prism 1.6.0 → 1.7.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 (55) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +23 -1
  3. data/Makefile +3 -3
  4. data/README.md +1 -1
  5. data/config.yml +28 -3
  6. data/docs/build_system.md +2 -2
  7. data/docs/cruby_compilation.md +1 -1
  8. data/docs/releasing.md +2 -2
  9. data/ext/prism/api_node.c +7 -3
  10. data/ext/prism/extconf.rb +1 -1
  11. data/ext/prism/extension.c +2 -3
  12. data/ext/prism/extension.h +1 -1
  13. data/include/prism/ast.h +54 -20
  14. data/include/prism/diagnostic.h +2 -0
  15. data/include/prism/options.h +8 -2
  16. data/include/prism/parser.h +3 -0
  17. data/include/prism/version.h +2 -2
  18. data/include/prism.h +1 -1
  19. data/lib/prism/dot_visitor.rb +5 -0
  20. data/lib/prism/dsl.rb +2 -2
  21. data/lib/prism/ffi.rb +3 -1
  22. data/lib/prism/inspect_visitor.rb +1 -0
  23. data/lib/prism/node.rb +52 -13
  24. data/lib/prism/parse_result.rb +2 -15
  25. data/lib/prism/polyfill/scan_byte.rb +1 -1
  26. data/lib/prism/reflection.rb +1 -1
  27. data/lib/prism/serialize.rb +6 -4
  28. data/lib/prism/translation/parser/compiler.rb +16 -16
  29. data/lib/prism/translation/parser.rb +5 -3
  30. data/lib/prism/translation/parser35.rb +1 -6
  31. data/lib/prism/translation/parser40.rb +13 -0
  32. data/lib/prism/translation/parser41.rb +13 -0
  33. data/lib/prism/translation/parser_current.rb +4 -2
  34. data/lib/prism/translation/ripper.rb +2 -2
  35. data/lib/prism/translation/ruby_parser.rb +53 -18
  36. data/lib/prism/translation.rb +2 -0
  37. data/lib/prism.rb +4 -5
  38. data/prism.gemspec +5 -1
  39. data/rbi/prism/dsl.rbi +3 -3
  40. data/rbi/prism/node.rbi +21 -8
  41. data/rbi/prism/translation/parser35.rbi +0 -2
  42. data/rbi/prism/translation/parser40.rbi +6 -0
  43. data/rbi/prism/translation/parser41.rbi +6 -0
  44. data/sig/prism/dsl.rbs +2 -2
  45. data/sig/prism/node.rbs +18 -8
  46. data/src/diagnostic.c +5 -1
  47. data/src/encoding.c +172 -67
  48. data/src/node.c +9 -0
  49. data/src/options.c +17 -7
  50. data/src/prettyprint.c +16 -0
  51. data/src/prism.c +1192 -1895
  52. data/src/serialize.c +7 -1
  53. data/src/token_type.c +2 -2
  54. data/src/util/pm_constant_pool.c +1 -1
  55. metadata +5 -1
data/lib/prism/node.rb CHANGED
@@ -2605,7 +2605,7 @@ module Prism
2605
2605
  # ^^^^^^^^
2606
2606
  class CallNode < Node
2607
2607
  # Initialize a new CallNode node.
2608
- def initialize(source, node_id, location, flags, receiver, call_operator_loc, name, message_loc, opening_loc, arguments, closing_loc, block)
2608
+ def initialize(source, node_id, location, flags, receiver, call_operator_loc, name, message_loc, opening_loc, arguments, closing_loc, equal_loc, block)
2609
2609
  @source = source
2610
2610
  @node_id = node_id
2611
2611
  @location = location
@@ -2617,6 +2617,7 @@ module Prism
2617
2617
  @opening_loc = opening_loc
2618
2618
  @arguments = arguments
2619
2619
  @closing_loc = closing_loc
2620
+ @equal_loc = equal_loc
2620
2621
  @block = block
2621
2622
  end
2622
2623
 
@@ -2641,20 +2642,20 @@ module Prism
2641
2642
 
2642
2643
  # def comment_targets: () -> Array[Node | Location]
2643
2644
  def comment_targets
2644
- [*receiver, *call_operator_loc, *message_loc, *opening_loc, *arguments, *closing_loc, *block] #: Array[Prism::node | Location]
2645
+ [*receiver, *call_operator_loc, *message_loc, *opening_loc, *arguments, *closing_loc, *equal_loc, *block] #: Array[Prism::node | Location]
2645
2646
  end
2646
2647
 
2647
- # def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?receiver: Prism::node?, ?call_operator_loc: Location?, ?name: Symbol, ?message_loc: Location?, ?opening_loc: Location?, ?arguments: ArgumentsNode?, ?closing_loc: Location?, ?block: BlockNode | BlockArgumentNode | nil) -> CallNode
2648
- def copy(node_id: self.node_id, location: self.location, flags: self.flags, receiver: self.receiver, call_operator_loc: self.call_operator_loc, name: self.name, message_loc: self.message_loc, opening_loc: self.opening_loc, arguments: self.arguments, closing_loc: self.closing_loc, block: self.block)
2649
- CallNode.new(source, node_id, location, flags, receiver, call_operator_loc, name, message_loc, opening_loc, arguments, closing_loc, block)
2648
+ # def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?receiver: Prism::node?, ?call_operator_loc: Location?, ?name: Symbol, ?message_loc: Location?, ?opening_loc: Location?, ?arguments: ArgumentsNode?, ?closing_loc: Location?, ?equal_loc: Location?, ?block: BlockNode | BlockArgumentNode | nil) -> CallNode
2649
+ def copy(node_id: self.node_id, location: self.location, flags: self.flags, receiver: self.receiver, call_operator_loc: self.call_operator_loc, name: self.name, message_loc: self.message_loc, opening_loc: self.opening_loc, arguments: self.arguments, closing_loc: self.closing_loc, equal_loc: self.equal_loc, block: self.block)
2650
+ CallNode.new(source, node_id, location, flags, receiver, call_operator_loc, name, message_loc, opening_loc, arguments, closing_loc, equal_loc, block)
2650
2651
  end
2651
2652
 
2652
2653
  # def deconstruct: () -> Array[Node?]
2653
2654
  alias deconstruct child_nodes
2654
2655
 
2655
- # def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, receiver: Prism::node?, call_operator_loc: Location?, name: Symbol, message_loc: Location?, opening_loc: Location?, arguments: ArgumentsNode?, closing_loc: Location?, block: BlockNode | BlockArgumentNode | nil }
2656
+ # def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, receiver: Prism::node?, call_operator_loc: Location?, name: Symbol, message_loc: Location?, opening_loc: Location?, arguments: ArgumentsNode?, closing_loc: Location?, equal_loc: Location?, block: BlockNode | BlockArgumentNode | nil }
2656
2657
  def deconstruct_keys(keys)
2657
- { node_id: node_id, location: location, receiver: receiver, call_operator_loc: call_operator_loc, name: name, message_loc: message_loc, opening_loc: opening_loc, arguments: arguments, closing_loc: closing_loc, block: block }
2658
+ { node_id: node_id, location: location, receiver: receiver, call_operator_loc: call_operator_loc, name: name, message_loc: message_loc, opening_loc: opening_loc, arguments: arguments, closing_loc: closing_loc, equal_loc: equal_loc, block: block }
2658
2659
  end
2659
2660
 
2660
2661
  # def safe_navigation?: () -> bool
@@ -2791,6 +2792,31 @@ module Prism
2791
2792
  repository.enter(node_id, :closing_loc) unless @closing_loc.nil?
2792
2793
  end
2793
2794
 
2795
+ # Represents the location of the equal sign, in the case that this is an attribute write.
2796
+ #
2797
+ # foo.bar = value
2798
+ # ^
2799
+ #
2800
+ # foo[bar] = value
2801
+ # ^
2802
+ def equal_loc
2803
+ location = @equal_loc
2804
+ case location
2805
+ when nil
2806
+ nil
2807
+ when Location
2808
+ location
2809
+ else
2810
+ @equal_loc = Location.new(source, location >> 32, location & 0xFFFFFFFF)
2811
+ end
2812
+ end
2813
+
2814
+ # Save the equal_loc location using the given saved source so that
2815
+ # it can be retrieved later.
2816
+ def save_equal_loc(repository)
2817
+ repository.enter(node_id, :equal_loc) unless @equal_loc.nil?
2818
+ end
2819
+
2794
2820
  # Represents the block that is being passed to the method.
2795
2821
  #
2796
2822
  # foo { |a| a }
@@ -2817,6 +2843,11 @@ module Prism
2817
2843
  closing_loc&.slice
2818
2844
  end
2819
2845
 
2846
+ # def equal: () -> String?
2847
+ def equal
2848
+ equal_loc&.slice
2849
+ end
2850
+
2820
2851
  # def inspect -> String
2821
2852
  def inspect
2822
2853
  InspectVisitor.compose(self)
@@ -2844,6 +2875,7 @@ module Prism
2844
2875
  (opening_loc.nil? == other.opening_loc.nil?) &&
2845
2876
  (arguments === other.arguments) &&
2846
2877
  (closing_loc.nil? == other.closing_loc.nil?) &&
2878
+ (equal_loc.nil? == other.equal_loc.nil?) &&
2847
2879
  (block === other.block)
2848
2880
  end
2849
2881
  end
@@ -7536,10 +7568,15 @@ module Prism
7536
7568
  end
7537
7569
  end
7538
7570
 
7539
- # Represents the use of the `super` keyword without parentheses or arguments.
7571
+ # Represents the use of the `super` keyword without parentheses or arguments, but which might have a block.
7540
7572
  #
7541
7573
  # super
7542
7574
  # ^^^^^
7575
+ #
7576
+ # super { 123 }
7577
+ # ^^^^^^^^^^^^^
7578
+ #
7579
+ # If it has any other arguments, it would be a `SuperNode` instead.
7543
7580
  class ForwardingSuperNode < Node
7544
7581
  # Initialize a new ForwardingSuperNode node.
7545
7582
  def initialize(source, node_id, location, flags, block)
@@ -7585,7 +7622,7 @@ module Prism
7585
7622
  { node_id: node_id, location: location, block: block }
7586
7623
  end
7587
7624
 
7588
- # attr_reader block: BlockNode?
7625
+ # All other arguments are forwarded as normal, except the original block is replaced with the new block.
7589
7626
  attr_reader :block
7590
7627
 
7591
7628
  # def inspect -> String
@@ -10951,7 +10988,7 @@ module Prism
10951
10988
  [*opening_loc, *parts, *closing_loc] #: Array[Prism::node | Location]
10952
10989
  end
10953
10990
 
10954
- # def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?opening_loc: Location?, ?parts: Array[StringNode | EmbeddedStatementsNode | EmbeddedVariableNode | InterpolatedStringNode | XStringNode], ?closing_loc: Location?) -> InterpolatedStringNode
10991
+ # def copy: (?node_id: Integer, ?location: Location, ?flags: Integer, ?opening_loc: Location?, ?parts: Array[StringNode | EmbeddedStatementsNode | EmbeddedVariableNode | InterpolatedStringNode | XStringNode | InterpolatedXStringNode | SymbolNode | InterpolatedSymbolNode], ?closing_loc: Location?) -> InterpolatedStringNode
10955
10992
  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)
10956
10993
  InterpolatedStringNode.new(source, node_id, location, flags, opening_loc, parts, closing_loc)
10957
10994
  end
@@ -10959,7 +10996,7 @@ module Prism
10959
10996
  # def deconstruct: () -> Array[Node?]
10960
10997
  alias deconstruct child_nodes
10961
10998
 
10962
- # def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, opening_loc: Location?, parts: Array[StringNode | EmbeddedStatementsNode | EmbeddedVariableNode | InterpolatedStringNode | XStringNode], closing_loc: Location? }
10999
+ # def deconstruct_keys: (Array[Symbol] keys) -> { node_id: Integer, location: Location, opening_loc: Location?, parts: Array[StringNode | EmbeddedStatementsNode | EmbeddedVariableNode | InterpolatedStringNode | XStringNode | InterpolatedXStringNode | SymbolNode | InterpolatedSymbolNode], closing_loc: Location? }
10963
11000
  def deconstruct_keys(keys)
10964
11001
  { node_id: node_id, location: location, opening_loc: opening_loc, parts: parts, closing_loc: closing_loc }
10965
11002
  end
@@ -10993,7 +11030,7 @@ module Prism
10993
11030
  repository.enter(node_id, :opening_loc) unless @opening_loc.nil?
10994
11031
  end
10995
11032
 
10996
- # attr_reader parts: Array[StringNode | EmbeddedStatementsNode | EmbeddedVariableNode | InterpolatedStringNode | XStringNode]
11033
+ # attr_reader parts: Array[StringNode | EmbeddedStatementsNode | EmbeddedVariableNode | InterpolatedStringNode | XStringNode | InterpolatedXStringNode | SymbolNode | InterpolatedSymbolNode]
10997
11034
  attr_reader :parts
10998
11035
 
10999
11036
  # attr_reader closing_loc: Location?
@@ -17213,6 +17250,8 @@ module Prism
17213
17250
  #
17214
17251
  # super foo, bar
17215
17252
  # ^^^^^^^^^^^^^^
17253
+ #
17254
+ # If no arguments are provided (except for a block), it would be a `ForwardingSuperNode` instead.
17216
17255
  class SuperNode < Node
17217
17256
  # Initialize a new SuperNode node.
17218
17257
  def initialize(source, node_id, location, flags, keyword_loc, lparen_loc, arguments, rparen_loc, block)
@@ -17295,7 +17334,7 @@ module Prism
17295
17334
  repository.enter(node_id, :lparen_loc) unless @lparen_loc.nil?
17296
17335
  end
17297
17336
 
17298
- # attr_reader arguments: ArgumentsNode?
17337
+ # Can be only `nil` when there are empty parentheses, like `super()`.
17299
17338
  attr_reader :arguments
17300
17339
 
17301
17340
  # attr_reader rparen_loc: Location?
@@ -155,21 +155,8 @@ module Prism
155
155
  # Binary search through the offsets to find the line number for the given
156
156
  # byte offset.
157
157
  def find_line(byte_offset)
158
- left = 0
159
- right = offsets.length - 1
160
-
161
- while left <= right
162
- mid = left + (right - left) / 2
163
- return mid if (offset = offsets[mid]) == byte_offset
164
-
165
- if offset < byte_offset
166
- left = mid + 1
167
- else
168
- right = mid - 1
169
- end
170
- end
171
-
172
- left - 1
158
+ index = offsets.bsearch_index { |offset| offset > byte_offset } || offsets.length
159
+ index - 1
173
160
  end
174
161
  end
175
162
 
@@ -3,7 +3,7 @@
3
3
  require "strscan"
4
4
 
5
5
  # Polyfill for StringScanner#scan_byte, which didn't exist until Ruby 3.4.
6
- if !(StringScanner.instance_methods.include?(:scan_byte))
6
+ if !(StringScanner.method_defined?(:scan_byte))
7
7
  StringScanner.include(
8
8
  Module.new {
9
9
  def scan_byte # :nodoc:
@@ -143,7 +143,7 @@ module Prism
143
143
  when :call_and_write_node
144
144
  [FlagsField.new(:flags, [:safe_navigation?, :variable_call?, :attribute_write?, :ignore_visibility?]), OptionalNodeField.new(:receiver), OptionalLocationField.new(:call_operator_loc), OptionalLocationField.new(:message_loc), ConstantField.new(:read_name), ConstantField.new(:write_name), LocationField.new(:operator_loc), NodeField.new(:value)]
145
145
  when :call_node
146
- [FlagsField.new(:flags, [:safe_navigation?, :variable_call?, :attribute_write?, :ignore_visibility?]), OptionalNodeField.new(:receiver), OptionalLocationField.new(:call_operator_loc), ConstantField.new(:name), OptionalLocationField.new(:message_loc), OptionalLocationField.new(:opening_loc), OptionalNodeField.new(:arguments), OptionalLocationField.new(:closing_loc), OptionalNodeField.new(:block)]
146
+ [FlagsField.new(:flags, [:safe_navigation?, :variable_call?, :attribute_write?, :ignore_visibility?]), OptionalNodeField.new(:receiver), OptionalLocationField.new(:call_operator_loc), ConstantField.new(:name), OptionalLocationField.new(:message_loc), OptionalLocationField.new(:opening_loc), OptionalNodeField.new(:arguments), OptionalLocationField.new(:closing_loc), OptionalLocationField.new(:equal_loc), OptionalNodeField.new(:block)]
147
147
  when :call_operator_write_node
148
148
  [FlagsField.new(:flags, [:safe_navigation?, :variable_call?, :attribute_write?, :ignore_visibility?]), OptionalNodeField.new(:receiver), OptionalLocationField.new(:call_operator_loc), OptionalLocationField.new(:message_loc), ConstantField.new(:read_name), ConstantField.new(:write_name), ConstantField.new(:binary_operator), LocationField.new(:binary_operator_loc), NodeField.new(:value)]
149
149
  when :call_or_write_node
@@ -21,7 +21,7 @@ module Prism
21
21
 
22
22
  # The minor version of prism that we are expecting to find in the serialized
23
23
  # strings.
24
- MINOR_VERSION = 6
24
+ MINOR_VERSION = 7
25
25
 
26
26
  # The patch version of prism that we are expecting to find in the serialized
27
27
  # strings.
@@ -556,6 +556,7 @@ module Prism
556
556
  :parameter_wild_loose_comma,
557
557
  :pattern_array_multiple_rests,
558
558
  :pattern_capture_duplicate,
559
+ :pattern_capture_in_alternative,
559
560
  :pattern_expression_after_bracket,
560
561
  :pattern_expression_after_comma,
561
562
  :pattern_expression_after_hrocket,
@@ -617,6 +618,7 @@ module Prism
617
618
  :unexpected_index_keywords,
618
619
  :unexpected_label,
619
620
  :unexpected_multi_write,
621
+ :unexpected_parameter_default_value,
620
622
  :unexpected_range_operator,
621
623
  :unexpected_safe_navigation,
622
624
  :unexpected_token_close_context,
@@ -879,7 +881,7 @@ module Prism
879
881
  when 18 then
880
882
  CallAndWriteNode.new(source, node_id, location, load_varuint, load_optional_node(constant_pool, encoding, freeze), load_optional_location(freeze), load_optional_location(freeze), load_constant(constant_pool, encoding), load_constant(constant_pool, encoding), load_location(freeze), load_node(constant_pool, encoding, freeze))
881
883
  when 19 then
882
- CallNode.new(source, node_id, location, load_varuint, load_optional_node(constant_pool, encoding, freeze), load_optional_location(freeze), load_constant(constant_pool, encoding), load_optional_location(freeze), load_optional_location(freeze), load_optional_node(constant_pool, encoding, freeze), load_optional_location(freeze), load_optional_node(constant_pool, encoding, freeze))
884
+ CallNode.new(source, node_id, location, load_varuint, load_optional_node(constant_pool, encoding, freeze), load_optional_location(freeze), load_constant(constant_pool, encoding), load_optional_location(freeze), load_optional_location(freeze), load_optional_node(constant_pool, encoding, freeze), load_optional_location(freeze), load_optional_location(freeze), load_optional_node(constant_pool, encoding, freeze))
883
885
  when 20 then
884
886
  CallOperatorWriteNode.new(source, node_id, location, load_varuint, load_optional_node(constant_pool, encoding, freeze), load_optional_location(freeze), load_optional_location(freeze), load_constant(constant_pool, encoding), load_constant(constant_pool, encoding), load_constant(constant_pool, encoding), load_location(freeze), load_node(constant_pool, encoding, freeze))
885
887
  when 21 then
@@ -1287,7 +1289,7 @@ module Prism
1287
1289
  -> (constant_pool, encoding, freeze) {
1288
1290
  node_id = load_varuint
1289
1291
  location = load_location(freeze)
1290
- value = CallNode.new(source, node_id, location, load_varuint, load_optional_node(constant_pool, encoding, freeze), load_optional_location(freeze), load_constant(constant_pool, encoding), load_optional_location(freeze), load_optional_location(freeze), load_optional_node(constant_pool, encoding, freeze), load_optional_location(freeze), load_optional_node(constant_pool, encoding, freeze))
1292
+ value = CallNode.new(source, node_id, location, load_varuint, load_optional_node(constant_pool, encoding, freeze), load_optional_location(freeze), load_constant(constant_pool, encoding), load_optional_location(freeze), load_optional_location(freeze), load_optional_node(constant_pool, encoding, freeze), load_optional_location(freeze), load_optional_location(freeze), load_optional_node(constant_pool, encoding, freeze))
1291
1293
  value.freeze if freeze
1292
1294
  value
1293
1295
  },
@@ -2239,6 +2241,7 @@ module Prism
2239
2241
  :KEYWORD_WHEN,
2240
2242
  :NEWLINE,
2241
2243
  :PARENTHESIS_RIGHT,
2244
+ :PIPE,
2242
2245
  :SEMICOLON,
2243
2246
  :AMPERSAND,
2244
2247
  :AMPERSAND_AMPERSAND,
@@ -2355,7 +2358,6 @@ module Prism
2355
2358
  :PERCENT_LOWER_X,
2356
2359
  :PERCENT_UPPER_I,
2357
2360
  :PERCENT_UPPER_W,
2358
- :PIPE,
2359
2361
  :PIPE_EQUAL,
2360
2362
  :PIPE_PIPE,
2361
2363
  :PIPE_PIPE_EQUAL,
@@ -217,7 +217,7 @@ module Prism
217
217
  rescue_clause.exceptions.any? ? builder.array(nil, visit_all(rescue_clause.exceptions), nil) : nil,
218
218
  token(rescue_clause.operator_loc),
219
219
  visit(rescue_clause.reference),
220
- srange_find(find_start_offset, find_end_offset, ";"),
220
+ srange_semicolon(find_start_offset, find_end_offset),
221
221
  visit(rescue_clause.statements)
222
222
  )
223
223
  end until (rescue_clause = rescue_clause.subsequent).nil?
@@ -323,7 +323,7 @@ module Prism
323
323
  visit_all(arguments),
324
324
  token(node.closing_loc),
325
325
  ),
326
- srange_find(node.message_loc.end_offset, node.arguments.arguments.last.location.start_offset, "="),
326
+ token(node.equal_loc),
327
327
  visit(node.arguments.arguments.last)
328
328
  ),
329
329
  block
@@ -340,7 +340,7 @@ module Prism
340
340
  if name.end_with?("=") && !message_loc.slice.end_with?("=") && node.arguments && block.nil?
341
341
  builder.assign(
342
342
  builder.attr_asgn(visit(node.receiver), call_operator, token(message_loc)),
343
- srange_find(message_loc.end_offset, node.arguments.location.start_offset, "="),
343
+ token(node.equal_loc),
344
344
  visit(node.arguments.arguments.last)
345
345
  )
346
346
  else
@@ -789,7 +789,7 @@ module Prism
789
789
  if (do_keyword_loc = node.do_keyword_loc)
790
790
  token(do_keyword_loc)
791
791
  else
792
- srange_find(node.collection.location.end_offset, (node.statements&.location || node.end_keyword_loc).start_offset, ";")
792
+ srange_semicolon(node.collection.location.end_offset, (node.statements&.location || node.end_keyword_loc).start_offset)
793
793
  end,
794
794
  visit(node.statements),
795
795
  token(node.end_keyword_loc)
@@ -921,7 +921,7 @@ module Prism
921
921
  if (then_keyword_loc = node.then_keyword_loc)
922
922
  token(then_keyword_loc)
923
923
  else
924
- srange_find(node.predicate.location.end_offset, (node.statements&.location || node.subsequent&.location || node.end_keyword_loc).start_offset, ";")
924
+ srange_semicolon(node.predicate.location.end_offset, (node.statements&.location || node.subsequent&.location || node.end_keyword_loc).start_offset)
925
925
  end,
926
926
  visit(node.statements),
927
927
  case node.subsequent
@@ -987,7 +987,7 @@ module Prism
987
987
  if (then_loc = node.then_loc)
988
988
  token(then_loc)
989
989
  else
990
- srange_find(node.pattern.location.end_offset, node.statements&.location&.start_offset, ";")
990
+ srange_semicolon(node.pattern.location.end_offset, node.statements&.location&.start_offset)
991
991
  end,
992
992
  visit(node.statements)
993
993
  )
@@ -1808,7 +1808,7 @@ module Prism
1808
1808
  if (then_keyword_loc = node.then_keyword_loc)
1809
1809
  token(then_keyword_loc)
1810
1810
  else
1811
- srange_find(node.predicate.location.end_offset, (node.statements&.location || node.else_clause&.location || node.end_keyword_loc).start_offset, ";")
1811
+ srange_semicolon(node.predicate.location.end_offset, (node.statements&.location || node.else_clause&.location || node.end_keyword_loc).start_offset)
1812
1812
  end,
1813
1813
  visit(node.else_clause),
1814
1814
  token(node.else_clause&.else_keyword_loc),
@@ -1839,7 +1839,7 @@ module Prism
1839
1839
  if (do_keyword_loc = node.do_keyword_loc)
1840
1840
  token(do_keyword_loc)
1841
1841
  else
1842
- srange_find(node.predicate.location.end_offset, (node.statements&.location || node.closing_loc).start_offset, ";")
1842
+ srange_semicolon(node.predicate.location.end_offset, (node.statements&.location || node.closing_loc).start_offset)
1843
1843
  end,
1844
1844
  visit(node.statements),
1845
1845
  token(node.closing_loc)
@@ -1863,7 +1863,7 @@ module Prism
1863
1863
  if (then_keyword_loc = node.then_keyword_loc)
1864
1864
  token(then_keyword_loc)
1865
1865
  else
1866
- srange_find(node.conditions.last.location.end_offset, node.statements&.location&.start_offset, ";")
1866
+ srange_semicolon(node.conditions.last.location.end_offset, node.statements&.location&.start_offset)
1867
1867
  end,
1868
1868
  visit(node.statements)
1869
1869
  )
@@ -1883,7 +1883,7 @@ module Prism
1883
1883
  if (do_keyword_loc = node.do_keyword_loc)
1884
1884
  token(do_keyword_loc)
1885
1885
  else
1886
- srange_find(node.predicate.location.end_offset, (node.statements&.location || node.closing_loc).start_offset, ";")
1886
+ srange_semicolon(node.predicate.location.end_offset, (node.statements&.location || node.closing_loc).start_offset)
1887
1887
  end,
1888
1888
  visit(node.statements),
1889
1889
  token(node.closing_loc)
@@ -2012,16 +2012,16 @@ module Prism
2012
2012
  Range.new(source_buffer, offset_cache[start_offset], offset_cache[end_offset])
2013
2013
  end
2014
2014
 
2015
- # Constructs a new source range by finding the given character between
2016
- # the given start offset and end offset. If the needle is not found, it
2017
- # returns nil. Importantly it does not search past newlines or comments.
2015
+ # Constructs a new source range by finding a semicolon between the given
2016
+ # start offset and end offset. If the semicolon is not found, it returns
2017
+ # nil. Importantly it does not search past newlines or comments.
2018
2018
  #
2019
2019
  # Note that end_offset is allowed to be nil, in which case this will
2020
2020
  # search until the end of the string.
2021
- def srange_find(start_offset, end_offset, character)
2022
- if (match = source_buffer.source.byteslice(start_offset...end_offset)[/\A\s*#{character}/])
2021
+ def srange_semicolon(start_offset, end_offset)
2022
+ if (match = source_buffer.source.byteslice(start_offset...end_offset)[/\A\s*;/])
2023
2023
  final_offset = start_offset + match.bytesize
2024
- [character, Range.new(source_buffer, offset_cache[final_offset - character.bytesize], offset_cache[final_offset])]
2024
+ [";", Range.new(source_buffer, offset_cache[final_offset - 1], offset_cache[final_offset])]
2025
2025
  end
2026
2026
  end
2027
2027
 
@@ -84,7 +84,7 @@ module Prism
84
84
  end
85
85
 
86
86
  def version # :nodoc:
87
- 35
87
+ 41
88
88
  end
89
89
 
90
90
  # The default encoding for Ruby files is UTF-8.
@@ -356,8 +356,10 @@ module Prism
356
356
  "3.3.1"
357
357
  when 34
358
358
  "3.4.0"
359
- when 35
360
- "3.5.0"
359
+ when 35, 40
360
+ "4.0.0"
361
+ when 41
362
+ "4.1.0"
361
363
  else
362
364
  "latest"
363
365
  end
@@ -3,11 +3,6 @@
3
3
 
4
4
  module Prism
5
5
  module Translation
6
- # This class is the entry-point for Ruby 3.5 of `Prism::Translation::Parser`.
7
- class Parser35 < Parser
8
- def version # :nodoc:
9
- 35
10
- end
11
- end
6
+ Parser35 = Parser40 # :nodoc:
12
7
  end
13
8
  end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+ # :markup: markdown
3
+
4
+ module Prism
5
+ module Translation
6
+ # This class is the entry-point for Ruby 4.0 of `Prism::Translation::Parser`.
7
+ class Parser40 < Parser
8
+ def version # :nodoc:
9
+ 40
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+ # :markup: markdown
3
+
4
+ module Prism
5
+ module Translation
6
+ # This class is the entry-point for Ruby 4.1 of `Prism::Translation::Parser`.
7
+ class Parser41 < Parser
8
+ def version # :nodoc:
9
+ 41
10
+ end
11
+ end
12
+ end
13
+ end
@@ -10,8 +10,10 @@ module Prism
10
10
  ParserCurrent = Parser33
11
11
  when /^3\.4\./
12
12
  ParserCurrent = Parser34
13
- when /^3\.5\./
14
- ParserCurrent = Parser35
13
+ when /^3\.5\./, /^4\.0\./
14
+ ParserCurrent = Parser40
15
+ when /^4\.1\./
16
+ ParserCurrent = Parser41
15
17
  else
16
18
  # Keep this in sync with released Ruby.
17
19
  parser = Parser34
@@ -71,7 +71,7 @@ module Prism
71
71
  # [[1, 13], :on_kw, "end", END ]]
72
72
  #
73
73
  def self.lex(src, filename = "-", lineno = 1, raise_errors: false)
74
- result = Prism.lex_compat(src, filepath: filename, line: lineno)
74
+ result = Prism.lex_compat(src, filepath: filename, line: lineno, version: "current")
75
75
 
76
76
  if result.failure? && raise_errors
77
77
  raise SyntaxError, result.errors.first.message
@@ -3295,7 +3295,7 @@ module Prism
3295
3295
 
3296
3296
  # Lazily initialize the parse result.
3297
3297
  def result
3298
- @result ||= Prism.parse(source, partial_script: true)
3298
+ @result ||= Prism.parse(source, partial_script: true, version: "current")
3299
3299
  end
3300
3300
 
3301
3301
  ##########################################################################
@@ -2,12 +2,17 @@
2
2
  # :markup: markdown
3
3
 
4
4
  begin
5
- require "ruby_parser"
5
+ require "sexp"
6
6
  rescue LoadError
7
- warn(%q{Error: Unable to load ruby_parser. Add `gem "ruby_parser"` to your Gemfile.})
7
+ warn(%q{Error: Unable to load sexp. Add `gem "sexp_processor"` to your Gemfile.})
8
8
  exit(1)
9
9
  end
10
10
 
11
+ class RubyParser # :nodoc:
12
+ class SyntaxError < RuntimeError # :nodoc:
13
+ end
14
+ end
15
+
11
16
  module Prism
12
17
  module Translation
13
18
  # This module is the entry-point for converting a prism syntax tree into the
@@ -415,14 +420,18 @@ module Prism
415
420
  visit(node.constant_path)
416
421
  end
417
422
 
418
- if node.body.nil?
419
- s(node, :class, name, visit(node.superclass))
420
- elsif node.body.is_a?(StatementsNode)
421
- compiler = copy_compiler(in_def: false)
422
- s(node, :class, name, visit(node.superclass)).concat(node.body.body.map { |child| child.accept(compiler) })
423
- else
424
- s(node, :class, name, visit(node.superclass), node.body.accept(copy_compiler(in_def: false)))
425
- end
423
+ result =
424
+ if node.body.nil?
425
+ s(node, :class, name, visit(node.superclass))
426
+ elsif node.body.is_a?(StatementsNode)
427
+ compiler = copy_compiler(in_def: false)
428
+ s(node, :class, name, visit(node.superclass)).concat(node.body.body.map { |child| child.accept(compiler) })
429
+ else
430
+ s(node, :class, name, visit(node.superclass), node.body.accept(copy_compiler(in_def: false)))
431
+ end
432
+
433
+ attach_comments(result, node)
434
+ result
426
435
  end
427
436
 
428
437
  # ```
@@ -611,7 +620,9 @@ module Prism
611
620
  s(node, :defs, visit(node.receiver), name)
612
621
  end
613
622
 
623
+ attach_comments(result, node)
614
624
  result.line(node.name_loc.start_line)
625
+
615
626
  if node.parameters.nil?
616
627
  result << s(node, :args).line(node.name_loc.start_line)
617
628
  else
@@ -1270,14 +1281,18 @@ module Prism
1270
1281
  visit(node.constant_path)
1271
1282
  end
1272
1283
 
1273
- if node.body.nil?
1274
- s(node, :module, name)
1275
- elsif node.body.is_a?(StatementsNode)
1276
- compiler = copy_compiler(in_def: false)
1277
- s(node, :module, name).concat(node.body.body.map { |child| child.accept(compiler) })
1278
- else
1279
- s(node, :module, name, node.body.accept(copy_compiler(in_def: false)))
1280
- end
1284
+ result =
1285
+ if node.body.nil?
1286
+ s(node, :module, name)
1287
+ elsif node.body.is_a?(StatementsNode)
1288
+ compiler = copy_compiler(in_def: false)
1289
+ s(node, :module, name).concat(node.body.body.map { |child| child.accept(compiler) })
1290
+ else
1291
+ s(node, :module, name, node.body.accept(copy_compiler(in_def: false)))
1292
+ end
1293
+
1294
+ attach_comments(result, node)
1295
+ result
1281
1296
  end
1282
1297
 
1283
1298
  # ```
@@ -1820,6 +1835,17 @@ module Prism
1820
1835
 
1821
1836
  private
1822
1837
 
1838
+ # Attach prism comments to the given sexp.
1839
+ def attach_comments(sexp, node)
1840
+ return unless node.comments
1841
+ return if node.comments.empty?
1842
+
1843
+ extra = node.location.start_line - node.comments.last.location.start_line
1844
+ comments = node.comments.map(&:slice)
1845
+ comments.concat([nil] * [0, extra].max)
1846
+ sexp.comments = comments.join("\n")
1847
+ end
1848
+
1823
1849
  # Create a new compiler with the given options.
1824
1850
  def copy_compiler(in_def: self.in_def, in_pattern: self.in_pattern)
1825
1851
  Compiler.new(file, in_def: in_def, in_pattern: in_pattern)
@@ -1898,6 +1924,14 @@ module Prism
1898
1924
  translate(Prism.parse_file(filepath, partial_script: true), filepath)
1899
1925
  end
1900
1926
 
1927
+ # Parse the give file and translate it into the
1928
+ # seattlerb/ruby_parser gem's Sexp format. This method is
1929
+ # provided for API compatibility to RubyParser and takes an
1930
+ # optional +timeout+ argument.
1931
+ def process(ruby, file = "(string)", timeout = nil)
1932
+ Timeout.timeout(timeout) { parse(ruby, file) }
1933
+ end
1934
+
1901
1935
  class << self
1902
1936
  # Parse the given source and translate it into the seattlerb/ruby_parser
1903
1937
  # gem's Sexp format.
@@ -1922,6 +1956,7 @@ module Prism
1922
1956
  raise ::RubyParser::SyntaxError, "#{filepath}:#{error.location.start_line} :: #{error.message}"
1923
1957
  end
1924
1958
 
1959
+ result.attach_comments!
1925
1960
  result.value.accept(Compiler.new(filepath))
1926
1961
  end
1927
1962
  end
@@ -10,6 +10,8 @@ module Prism
10
10
  autoload :Parser33, "prism/translation/parser33"
11
11
  autoload :Parser34, "prism/translation/parser34"
12
12
  autoload :Parser35, "prism/translation/parser35"
13
+ autoload :Parser40, "prism/translation/parser40"
14
+ autoload :Parser41, "prism/translation/parser41"
13
15
  autoload :Ripper, "prism/translation/ripper"
14
16
  autoload :RubyParser, "prism/translation/ruby_parser"
15
17
  end
data/lib/prism.rb CHANGED
@@ -42,13 +42,12 @@ module Prism
42
42
  # Initialize a new exception for the given ruby version string.
43
43
  def initialize(version)
44
44
  message = +"invalid version: Requested to parse as `version: 'current'`; "
45
- gem_version =
46
- begin
47
- Gem::Version.new(version)
48
- rescue ArgumentError
45
+ segments =
46
+ if version.match?(/\A\d+\.\d+.\d+\z/)
47
+ version.split(".").map(&:to_i)
49
48
  end
50
49
 
51
- if gem_version && gem_version < Gem::Version.new("3.3.0")
50
+ if segments && ((segments[0] < 3) || (segments[0] == 3 && segments[1] < 3))
52
51
  message << " #{version} is below the minimum supported syntax."
53
52
  else
54
53
  message << " #{version} is unknown. Please update the `prism` gem."
data/prism.gemspec CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  Gem::Specification.new do |spec|
4
4
  spec.name = "prism"
5
- spec.version = "1.6.0"
5
+ spec.version = "1.7.0"
6
6
  spec.authors = ["Shopify"]
7
7
  spec.email = ["ruby@shopify.com"]
8
8
 
@@ -101,6 +101,8 @@ Gem::Specification.new do |spec|
101
101
  "lib/prism/translation/parser33.rb",
102
102
  "lib/prism/translation/parser34.rb",
103
103
  "lib/prism/translation/parser35.rb",
104
+ "lib/prism/translation/parser40.rb",
105
+ "lib/prism/translation/parser41.rb",
104
106
  "lib/prism/translation/parser/builder.rb",
105
107
  "lib/prism/translation/parser/compiler.rb",
106
108
  "lib/prism/translation/parser/lexer.rb",
@@ -123,6 +125,8 @@ Gem::Specification.new do |spec|
123
125
  "rbi/prism/translation/parser33.rbi",
124
126
  "rbi/prism/translation/parser34.rbi",
125
127
  "rbi/prism/translation/parser35.rbi",
128
+ "rbi/prism/translation/parser40.rbi",
129
+ "rbi/prism/translation/parser41.rbi",
126
130
  "rbi/prism/translation/ripper.rbi",
127
131
  "rbi/prism/visitor.rbi",
128
132
  "sig/prism.rbs",