prism 0.17.1 → 0.18.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (50) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +30 -1
  3. data/Makefile +5 -5
  4. data/README.md +2 -2
  5. data/config.yml +26 -13
  6. data/docs/build_system.md +6 -6
  7. data/docs/building.md +1 -1
  8. data/docs/configuration.md +1 -0
  9. data/docs/encoding.md +68 -32
  10. data/docs/heredocs.md +1 -1
  11. data/docs/javascript.md +29 -1
  12. data/docs/ruby_api.md +14 -0
  13. data/ext/prism/api_node.c +74 -45
  14. data/ext/prism/extconf.rb +91 -127
  15. data/ext/prism/extension.c +1 -1
  16. data/ext/prism/extension.h +1 -1
  17. data/include/prism/ast.h +148 -133
  18. data/include/prism/diagnostic.h +27 -1
  19. data/include/prism/enc/pm_encoding.h +42 -1
  20. data/include/prism/parser.h +6 -0
  21. data/include/prism/version.h +3 -3
  22. data/lib/prism/compiler.rb +3 -3
  23. data/lib/prism/debug.rb +4 -0
  24. data/lib/prism/desugar_compiler.rb +1 -0
  25. data/lib/prism/dispatcher.rb +14 -14
  26. data/lib/prism/dot_visitor.rb +4334 -0
  27. data/lib/prism/dsl.rb +11 -11
  28. data/lib/prism/ffi.rb +3 -3
  29. data/lib/prism/mutation_compiler.rb +6 -6
  30. data/lib/prism/node.rb +182 -113
  31. data/lib/prism/node_ext.rb +61 -3
  32. data/lib/prism/parse_result.rb +46 -12
  33. data/lib/prism/serialize.rb +125 -131
  34. data/lib/prism/visitor.rb +3 -3
  35. data/lib/prism.rb +1 -0
  36. data/prism.gemspec +5 -1
  37. data/rbi/prism.rbi +83 -54
  38. data/sig/prism.rbs +47 -32
  39. data/src/diagnostic.c +61 -3
  40. data/src/enc/pm_big5.c +63 -0
  41. data/src/enc/pm_cp51932.c +57 -0
  42. data/src/enc/pm_euc_jp.c +10 -0
  43. data/src/enc/pm_gbk.c +5 -2
  44. data/src/enc/pm_tables.c +1478 -148
  45. data/src/node.c +33 -21
  46. data/src/prettyprint.c +1027 -925
  47. data/src/prism.c +925 -374
  48. data/src/regexp.c +12 -12
  49. data/src/serialize.c +36 -9
  50. metadata +6 -2
data/lib/prism/dsl.rb CHANGED
@@ -152,6 +152,11 @@ module Prism
152
152
  CapturePatternNode.new(value, target, operator_loc, location)
153
153
  end
154
154
 
155
+ # Create a new CaseMatchNode node
156
+ def CaseMatchNode(predicate, conditions, consequent, case_keyword_loc, end_keyword_loc, location = Location())
157
+ CaseMatchNode.new(predicate, conditions, consequent, case_keyword_loc, end_keyword_loc, location)
158
+ end
159
+
155
160
  # Create a new CaseNode node
156
161
  def CaseNode(predicate, conditions, consequent, case_keyword_loc, end_keyword_loc, location = Location())
157
162
  CaseNode.new(predicate, conditions, consequent, case_keyword_loc, end_keyword_loc, location)
@@ -363,8 +368,8 @@ module Prism
363
368
  end
364
369
 
365
370
  # Create a new IfNode node
366
- def IfNode(if_keyword_loc, predicate, statements, consequent, end_keyword_loc, location = Location())
367
- IfNode.new(if_keyword_loc, predicate, statements, consequent, end_keyword_loc, location)
371
+ def IfNode(if_keyword_loc, predicate, then_keyword_loc, statements, consequent, end_keyword_loc, location = Location())
372
+ IfNode.new(if_keyword_loc, predicate, then_keyword_loc, statements, consequent, end_keyword_loc, location)
368
373
  end
369
374
 
370
375
  # Create a new ImaginaryNode node
@@ -518,8 +523,8 @@ module Prism
518
523
  end
519
524
 
520
525
  # Create a new MatchWriteNode node
521
- def MatchWriteNode(call, locals, location = Location())
522
- MatchWriteNode.new(call, locals, location)
526
+ def MatchWriteNode(call, targets, location = Location())
527
+ MatchWriteNode.new(call, targets, location)
523
528
  end
524
529
 
525
530
  # Create a new MissingNode node
@@ -702,11 +707,6 @@ module Prism
702
707
  StatementsNode.new(body, location)
703
708
  end
704
709
 
705
- # Create a new StringConcatNode node
706
- def StringConcatNode(left, right, location = Location())
707
- StringConcatNode.new(left, right, location)
708
- end
709
-
710
710
  # Create a new StringNode node
711
711
  def StringNode(flags, opening_loc, content_loc, closing_loc, unescaped, location = Location())
712
712
  StringNode.new(flags, opening_loc, content_loc, closing_loc, unescaped, location)
@@ -733,8 +733,8 @@ module Prism
733
733
  end
734
734
 
735
735
  # Create a new UnlessNode node
736
- def UnlessNode(keyword_loc, predicate, statements, consequent, end_keyword_loc, location = Location())
737
- UnlessNode.new(keyword_loc, predicate, statements, consequent, end_keyword_loc, location)
736
+ def UnlessNode(keyword_loc, predicate, then_keyword_loc, statements, consequent, end_keyword_loc, location = Location())
737
+ UnlessNode.new(keyword_loc, predicate, then_keyword_loc, statements, consequent, end_keyword_loc, location)
738
738
  end
739
739
 
740
740
  # Create a new UntilNode node
data/lib/prism/ffi.rb CHANGED
@@ -14,7 +14,7 @@ module Prism
14
14
 
15
15
  # Define the library that we will be pulling functions from. Note that this
16
16
  # must align with the build shared library from make/rake.
17
- ffi_lib File.expand_path("../../build/librubyparser.#{RbConfig::CONFIG["SOEXT"]}", __dir__)
17
+ ffi_lib File.expand_path("../../build/libprism.#{RbConfig::CONFIG["SOEXT"]}", __dir__)
18
18
 
19
19
  # Convert a native C type declaration into a symbol that FFI understands.
20
20
  # For example:
@@ -230,7 +230,7 @@ module Prism
230
230
  loader = Serialize::Loader.new(source, buffer.read)
231
231
 
232
232
  loader.load_header
233
- loader.load_force_encoding
233
+ loader.load_encoding
234
234
  loader.load_start_line
235
235
  loader.load_comments
236
236
  end
@@ -299,7 +299,7 @@ module Prism
299
299
  values << (options.fetch(:frozen_string_literal, false) ? 1 : 0)
300
300
 
301
301
  template << "C"
302
- values << (options[:verbose] ? 0 : 1)
302
+ values << (options.fetch(:verbose, true) ? 0 : 1)
303
303
 
304
304
  template << "L"
305
305
  if (scopes = options[:scopes])
@@ -120,6 +120,11 @@ module Prism
120
120
  node.copy(value: visit(node.value), target: visit(node.target))
121
121
  end
122
122
 
123
+ # Copy a CaseMatchNode node
124
+ def visit_case_match_node(node)
125
+ node.copy(predicate: visit(node.predicate), conditions: visit_all(node.conditions), consequent: visit(node.consequent))
126
+ end
127
+
123
128
  # Copy a CaseNode node
124
129
  def visit_case_node(node)
125
130
  node.copy(predicate: visit(node.predicate), conditions: visit_all(node.conditions), consequent: visit(node.consequent))
@@ -487,7 +492,7 @@ module Prism
487
492
 
488
493
  # Copy a MatchWriteNode node
489
494
  def visit_match_write_node(node)
490
- node.copy(call: visit(node.call))
495
+ node.copy(call: visit(node.call), targets: visit_all(node.targets))
491
496
  end
492
497
 
493
498
  # Copy a MissingNode node
@@ -670,11 +675,6 @@ module Prism
670
675
  node.copy(body: visit_all(node.body))
671
676
  end
672
677
 
673
- # Copy a StringConcatNode node
674
- def visit_string_concat_node(node)
675
- node.copy(left: visit(node.left), right: visit(node.right))
676
- end
677
-
678
678
  # Copy a StringNode node
679
679
  def visit_string_node(node)
680
680
  node.copy
data/lib/prism/node.rb CHANGED
@@ -38,6 +38,11 @@ module Prism
38
38
  end
39
39
  q.current_group.break
40
40
  end
41
+
42
+ # Convert this node into a graphviz dot graph string.
43
+ def to_dot
44
+ DotVisitor.new.tap { |visitor| accept(visitor) }.to_dot
45
+ end
41
46
  end
42
47
 
43
48
  # Represents the use of the `alias` keyword to alias a global variable.
@@ -2734,6 +2739,142 @@ module Prism
2734
2739
  end
2735
2740
  end
2736
2741
 
2742
+ # Represents the use of a case statement for pattern matching.
2743
+ #
2744
+ # case true
2745
+ # in false
2746
+ # end
2747
+ # ^^^^^^^^^
2748
+ class CaseMatchNode < Node
2749
+ # attr_reader predicate: Node?
2750
+ attr_reader :predicate
2751
+
2752
+ # attr_reader conditions: Array[Node]
2753
+ attr_reader :conditions
2754
+
2755
+ # attr_reader consequent: ElseNode?
2756
+ attr_reader :consequent
2757
+
2758
+ # attr_reader case_keyword_loc: Location
2759
+ attr_reader :case_keyword_loc
2760
+
2761
+ # attr_reader end_keyword_loc: Location
2762
+ attr_reader :end_keyword_loc
2763
+
2764
+ # def initialize: (predicate: Node?, conditions: Array[Node], consequent: ElseNode?, case_keyword_loc: Location, end_keyword_loc: Location, location: Location) -> void
2765
+ def initialize(predicate, conditions, consequent, case_keyword_loc, end_keyword_loc, location)
2766
+ @predicate = predicate
2767
+ @conditions = conditions
2768
+ @consequent = consequent
2769
+ @case_keyword_loc = case_keyword_loc
2770
+ @end_keyword_loc = end_keyword_loc
2771
+ @location = location
2772
+ end
2773
+
2774
+ # def accept: (visitor: Visitor) -> void
2775
+ def accept(visitor)
2776
+ visitor.visit_case_match_node(self)
2777
+ end
2778
+
2779
+ # def child_nodes: () -> Array[nil | Node]
2780
+ def child_nodes
2781
+ [predicate, *conditions, consequent]
2782
+ end
2783
+
2784
+ # def compact_child_nodes: () -> Array[Node]
2785
+ def compact_child_nodes
2786
+ compact = []
2787
+ compact << predicate if predicate
2788
+ compact.concat(conditions)
2789
+ compact << consequent if consequent
2790
+ compact
2791
+ end
2792
+
2793
+ # def comment_targets: () -> Array[Node | Location]
2794
+ def comment_targets
2795
+ [*predicate, *conditions, *consequent, case_keyword_loc, end_keyword_loc]
2796
+ end
2797
+
2798
+ # def copy: (**params) -> CaseMatchNode
2799
+ def copy(**params)
2800
+ CaseMatchNode.new(
2801
+ params.fetch(:predicate) { predicate },
2802
+ params.fetch(:conditions) { conditions },
2803
+ params.fetch(:consequent) { consequent },
2804
+ params.fetch(:case_keyword_loc) { case_keyword_loc },
2805
+ params.fetch(:end_keyword_loc) { end_keyword_loc },
2806
+ params.fetch(:location) { location },
2807
+ )
2808
+ end
2809
+
2810
+ # def deconstruct: () -> Array[nil | Node]
2811
+ alias deconstruct child_nodes
2812
+
2813
+ # def deconstruct_keys: (keys: Array[Symbol]) -> Hash[Symbol, nil | Node | Array[Node] | String | Token | Array[Token] | Location]
2814
+ def deconstruct_keys(keys)
2815
+ { predicate: predicate, conditions: conditions, consequent: consequent, case_keyword_loc: case_keyword_loc, end_keyword_loc: end_keyword_loc, location: location }
2816
+ end
2817
+
2818
+ # def case_keyword: () -> String
2819
+ def case_keyword
2820
+ case_keyword_loc.slice
2821
+ end
2822
+
2823
+ # def end_keyword: () -> String
2824
+ def end_keyword
2825
+ end_keyword_loc.slice
2826
+ end
2827
+
2828
+ # def inspect(inspector: NodeInspector) -> String
2829
+ def inspect(inspector = NodeInspector.new)
2830
+ inspector << inspector.header(self)
2831
+ if (predicate = self.predicate).nil?
2832
+ inspector << "├── predicate: ∅\n"
2833
+ else
2834
+ inspector << "├── predicate:\n"
2835
+ inspector << predicate.inspect(inspector.child_inspector("│ ")).delete_prefix(inspector.prefix)
2836
+ end
2837
+ inspector << "├── conditions: #{inspector.list("#{inspector.prefix}│ ", conditions)}"
2838
+ if (consequent = self.consequent).nil?
2839
+ inspector << "├── consequent: ∅\n"
2840
+ else
2841
+ inspector << "├── consequent:\n"
2842
+ inspector << consequent.inspect(inspector.child_inspector("│ ")).delete_prefix(inspector.prefix)
2843
+ end
2844
+ inspector << "├── case_keyword_loc: #{inspector.location(case_keyword_loc)}\n"
2845
+ inspector << "└── end_keyword_loc: #{inspector.location(end_keyword_loc)}\n"
2846
+ inspector.to_str
2847
+ end
2848
+
2849
+ # Sometimes you want to check an instance of a node against a list of
2850
+ # classes to see what kind of behavior to perform. Usually this is done by
2851
+ # calling `[cls1, cls2].include?(node.class)` or putting the node into a
2852
+ # case statement and doing `case node; when cls1; when cls2; end`. Both of
2853
+ # these approaches are relatively slow because of the constant lookups,
2854
+ # method calls, and/or array allocations.
2855
+ #
2856
+ # Instead, you can call #type, which will return to you a symbol that you
2857
+ # can use for comparison. This is faster than the other approaches because
2858
+ # it uses a single integer comparison, but also because if you're on CRuby
2859
+ # you can take advantage of the fact that case statements with all symbol
2860
+ # keys will use a jump table.
2861
+ #
2862
+ # def type: () -> Symbol
2863
+ def type
2864
+ :case_match_node
2865
+ end
2866
+
2867
+ # Similar to #type, this method returns a symbol that you can use for
2868
+ # splitting on the type of the node without having to do a long === chain.
2869
+ # Note that like #type, it will still be slower than using == for a single
2870
+ # class, but should be faster in a case statement or an array comparison.
2871
+ #
2872
+ # def self.type: () -> Symbol
2873
+ def self.type
2874
+ :case_match_node
2875
+ end
2876
+ end
2877
+
2737
2878
  # Represents the use of a case statement.
2738
2879
  #
2739
2880
  # case true
@@ -7387,6 +7528,9 @@ module Prism
7387
7528
  # attr_reader predicate: Node
7388
7529
  attr_reader :predicate
7389
7530
 
7531
+ # attr_reader then_keyword_loc: Location?
7532
+ attr_reader :then_keyword_loc
7533
+
7390
7534
  # attr_reader statements: StatementsNode?
7391
7535
  attr_reader :statements
7392
7536
 
@@ -7396,10 +7540,11 @@ module Prism
7396
7540
  # attr_reader end_keyword_loc: Location?
7397
7541
  attr_reader :end_keyword_loc
7398
7542
 
7399
- # def initialize: (if_keyword_loc: Location?, predicate: Node, statements: StatementsNode?, consequent: Node?, end_keyword_loc: Location?, location: Location) -> void
7400
- def initialize(if_keyword_loc, predicate, statements, consequent, end_keyword_loc, location)
7543
+ # def initialize: (if_keyword_loc: Location?, predicate: Node, then_keyword_loc: Location?, statements: StatementsNode?, consequent: Node?, end_keyword_loc: Location?, location: Location) -> void
7544
+ def initialize(if_keyword_loc, predicate, then_keyword_loc, statements, consequent, end_keyword_loc, location)
7401
7545
  @if_keyword_loc = if_keyword_loc
7402
7546
  @predicate = predicate
7547
+ @then_keyword_loc = then_keyword_loc
7403
7548
  @statements = statements
7404
7549
  @consequent = consequent
7405
7550
  @end_keyword_loc = end_keyword_loc
@@ -7431,7 +7576,7 @@ module Prism
7431
7576
 
7432
7577
  # def comment_targets: () -> Array[Node | Location]
7433
7578
  def comment_targets
7434
- [*if_keyword_loc, predicate, *statements, *consequent, *end_keyword_loc]
7579
+ [*if_keyword_loc, predicate, *then_keyword_loc, *statements, *consequent, *end_keyword_loc]
7435
7580
  end
7436
7581
 
7437
7582
  # def copy: (**params) -> IfNode
@@ -7439,6 +7584,7 @@ module Prism
7439
7584
  IfNode.new(
7440
7585
  params.fetch(:if_keyword_loc) { if_keyword_loc },
7441
7586
  params.fetch(:predicate) { predicate },
7587
+ params.fetch(:then_keyword_loc) { then_keyword_loc },
7442
7588
  params.fetch(:statements) { statements },
7443
7589
  params.fetch(:consequent) { consequent },
7444
7590
  params.fetch(:end_keyword_loc) { end_keyword_loc },
@@ -7451,7 +7597,7 @@ module Prism
7451
7597
 
7452
7598
  # def deconstruct_keys: (keys: Array[Symbol]) -> Hash[Symbol, nil | Node | Array[Node] | String | Token | Array[Token] | Location]
7453
7599
  def deconstruct_keys(keys)
7454
- { if_keyword_loc: if_keyword_loc, predicate: predicate, statements: statements, consequent: consequent, end_keyword_loc: end_keyword_loc, location: location }
7600
+ { if_keyword_loc: if_keyword_loc, predicate: predicate, then_keyword_loc: then_keyword_loc, statements: statements, consequent: consequent, end_keyword_loc: end_keyword_loc, location: location }
7455
7601
  end
7456
7602
 
7457
7603
  # def if_keyword: () -> String?
@@ -7459,6 +7605,11 @@ module Prism
7459
7605
  if_keyword_loc&.slice
7460
7606
  end
7461
7607
 
7608
+ # def then_keyword: () -> String?
7609
+ def then_keyword
7610
+ then_keyword_loc&.slice
7611
+ end
7612
+
7462
7613
  # def end_keyword: () -> String?
7463
7614
  def end_keyword
7464
7615
  end_keyword_loc&.slice
@@ -7470,6 +7621,7 @@ module Prism
7470
7621
  inspector << "├── if_keyword_loc: #{inspector.location(if_keyword_loc)}\n"
7471
7622
  inspector << "├── predicate:\n"
7472
7623
  inspector << inspector.child_node(predicate, "│ ")
7624
+ inspector << "├── then_keyword_loc: #{inspector.location(then_keyword_loc)}\n"
7473
7625
  if (statements = self.statements).nil?
7474
7626
  inspector << "├── statements: ∅\n"
7475
7627
  else
@@ -11132,13 +11284,13 @@ module Prism
11132
11284
  # attr_reader call: CallNode
11133
11285
  attr_reader :call
11134
11286
 
11135
- # attr_reader locals: Array[Symbol]
11136
- attr_reader :locals
11287
+ # attr_reader targets: Array[Node]
11288
+ attr_reader :targets
11137
11289
 
11138
- # def initialize: (call: CallNode, locals: Array[Symbol], location: Location) -> void
11139
- def initialize(call, locals, location)
11290
+ # def initialize: (call: CallNode, targets: Array[Node], location: Location) -> void
11291
+ def initialize(call, targets, location)
11140
11292
  @call = call
11141
- @locals = locals
11293
+ @targets = targets
11142
11294
  @location = location
11143
11295
  end
11144
11296
 
@@ -11149,24 +11301,24 @@ module Prism
11149
11301
 
11150
11302
  # def child_nodes: () -> Array[nil | Node]
11151
11303
  def child_nodes
11152
- [call]
11304
+ [call, *targets]
11153
11305
  end
11154
11306
 
11155
11307
  # def compact_child_nodes: () -> Array[Node]
11156
11308
  def compact_child_nodes
11157
- [call]
11309
+ [call, *targets]
11158
11310
  end
11159
11311
 
11160
11312
  # def comment_targets: () -> Array[Node | Location]
11161
11313
  def comment_targets
11162
- [call]
11314
+ [call, *targets]
11163
11315
  end
11164
11316
 
11165
11317
  # def copy: (**params) -> MatchWriteNode
11166
11318
  def copy(**params)
11167
11319
  MatchWriteNode.new(
11168
11320
  params.fetch(:call) { call },
11169
- params.fetch(:locals) { locals },
11321
+ params.fetch(:targets) { targets },
11170
11322
  params.fetch(:location) { location },
11171
11323
  )
11172
11324
  end
@@ -11176,7 +11328,7 @@ module Prism
11176
11328
 
11177
11329
  # def deconstruct_keys: (keys: Array[Symbol]) -> Hash[Symbol, nil | Node | Array[Node] | String | Token | Array[Token] | Location]
11178
11330
  def deconstruct_keys(keys)
11179
- { call: call, locals: locals, location: location }
11331
+ { call: call, targets: targets, location: location }
11180
11332
  end
11181
11333
 
11182
11334
  # def inspect(inspector: NodeInspector) -> String
@@ -11184,7 +11336,7 @@ module Prism
11184
11336
  inspector << inspector.header(self)
11185
11337
  inspector << "├── call:\n"
11186
11338
  inspector << inspector.child_node(call, "│ ")
11187
- inspector << "└── locals: #{locals.inspect}\n"
11339
+ inspector << "└── targets: #{inspector.list("#{inspector.prefix} ", targets)}"
11188
11340
  inspector.to_str
11189
11341
  end
11190
11342
 
@@ -15076,100 +15228,6 @@ module Prism
15076
15228
  end
15077
15229
  end
15078
15230
 
15079
- # Represents the use of compile-time string concatenation.
15080
- #
15081
- # "foo" "bar"
15082
- # ^^^^^^^^^^^
15083
- class StringConcatNode < Node
15084
- # attr_reader left: Node
15085
- attr_reader :left
15086
-
15087
- # attr_reader right: Node
15088
- attr_reader :right
15089
-
15090
- # def initialize: (left: Node, right: Node, location: Location) -> void
15091
- def initialize(left, right, location)
15092
- @left = left
15093
- @right = right
15094
- @location = location
15095
- end
15096
-
15097
- # def accept: (visitor: Visitor) -> void
15098
- def accept(visitor)
15099
- visitor.visit_string_concat_node(self)
15100
- end
15101
-
15102
- # def child_nodes: () -> Array[nil | Node]
15103
- def child_nodes
15104
- [left, right]
15105
- end
15106
-
15107
- # def compact_child_nodes: () -> Array[Node]
15108
- def compact_child_nodes
15109
- [left, right]
15110
- end
15111
-
15112
- # def comment_targets: () -> Array[Node | Location]
15113
- def comment_targets
15114
- [left, right]
15115
- end
15116
-
15117
- # def copy: (**params) -> StringConcatNode
15118
- def copy(**params)
15119
- StringConcatNode.new(
15120
- params.fetch(:left) { left },
15121
- params.fetch(:right) { right },
15122
- params.fetch(:location) { location },
15123
- )
15124
- end
15125
-
15126
- # def deconstruct: () -> Array[nil | Node]
15127
- alias deconstruct child_nodes
15128
-
15129
- # def deconstruct_keys: (keys: Array[Symbol]) -> Hash[Symbol, nil | Node | Array[Node] | String | Token | Array[Token] | Location]
15130
- def deconstruct_keys(keys)
15131
- { left: left, right: right, location: location }
15132
- end
15133
-
15134
- # def inspect(inspector: NodeInspector) -> String
15135
- def inspect(inspector = NodeInspector.new)
15136
- inspector << inspector.header(self)
15137
- inspector << "├── left:\n"
15138
- inspector << inspector.child_node(left, "│ ")
15139
- inspector << "└── right:\n"
15140
- inspector << inspector.child_node(right, " ")
15141
- inspector.to_str
15142
- end
15143
-
15144
- # Sometimes you want to check an instance of a node against a list of
15145
- # classes to see what kind of behavior to perform. Usually this is done by
15146
- # calling `[cls1, cls2].include?(node.class)` or putting the node into a
15147
- # case statement and doing `case node; when cls1; when cls2; end`. Both of
15148
- # these approaches are relatively slow because of the constant lookups,
15149
- # method calls, and/or array allocations.
15150
- #
15151
- # Instead, you can call #type, which will return to you a symbol that you
15152
- # can use for comparison. This is faster than the other approaches because
15153
- # it uses a single integer comparison, but also because if you're on CRuby
15154
- # you can take advantage of the fact that case statements with all symbol
15155
- # keys will use a jump table.
15156
- #
15157
- # def type: () -> Symbol
15158
- def type
15159
- :string_concat_node
15160
- end
15161
-
15162
- # Similar to #type, this method returns a symbol that you can use for
15163
- # splitting on the type of the node without having to do a long === chain.
15164
- # Note that like #type, it will still be slower than using == for a single
15165
- # class, but should be faster in a case statement or an array comparison.
15166
- #
15167
- # def self.type: () -> Symbol
15168
- def self.type
15169
- :string_concat_node
15170
- end
15171
- end
15172
-
15173
15231
  # Represents a string literal, a string contained within a `%w` list, or
15174
15232
  # plain string content within an interpolated string.
15175
15233
  #
@@ -15762,6 +15820,9 @@ module Prism
15762
15820
  # attr_reader predicate: Node
15763
15821
  attr_reader :predicate
15764
15822
 
15823
+ # attr_reader then_keyword_loc: Location?
15824
+ attr_reader :then_keyword_loc
15825
+
15765
15826
  # attr_reader statements: StatementsNode?
15766
15827
  attr_reader :statements
15767
15828
 
@@ -15771,10 +15832,11 @@ module Prism
15771
15832
  # attr_reader end_keyword_loc: Location?
15772
15833
  attr_reader :end_keyword_loc
15773
15834
 
15774
- # def initialize: (keyword_loc: Location, predicate: Node, statements: StatementsNode?, consequent: ElseNode?, end_keyword_loc: Location?, location: Location) -> void
15775
- def initialize(keyword_loc, predicate, statements, consequent, end_keyword_loc, location)
15835
+ # def initialize: (keyword_loc: Location, predicate: Node, then_keyword_loc: Location?, statements: StatementsNode?, consequent: ElseNode?, end_keyword_loc: Location?, location: Location) -> void
15836
+ def initialize(keyword_loc, predicate, then_keyword_loc, statements, consequent, end_keyword_loc, location)
15776
15837
  @keyword_loc = keyword_loc
15777
15838
  @predicate = predicate
15839
+ @then_keyword_loc = then_keyword_loc
15778
15840
  @statements = statements
15779
15841
  @consequent = consequent
15780
15842
  @end_keyword_loc = end_keyword_loc
@@ -15806,7 +15868,7 @@ module Prism
15806
15868
 
15807
15869
  # def comment_targets: () -> Array[Node | Location]
15808
15870
  def comment_targets
15809
- [keyword_loc, predicate, *statements, *consequent, *end_keyword_loc]
15871
+ [keyword_loc, predicate, *then_keyword_loc, *statements, *consequent, *end_keyword_loc]
15810
15872
  end
15811
15873
 
15812
15874
  # def copy: (**params) -> UnlessNode
@@ -15814,6 +15876,7 @@ module Prism
15814
15876
  UnlessNode.new(
15815
15877
  params.fetch(:keyword_loc) { keyword_loc },
15816
15878
  params.fetch(:predicate) { predicate },
15879
+ params.fetch(:then_keyword_loc) { then_keyword_loc },
15817
15880
  params.fetch(:statements) { statements },
15818
15881
  params.fetch(:consequent) { consequent },
15819
15882
  params.fetch(:end_keyword_loc) { end_keyword_loc },
@@ -15826,7 +15889,7 @@ module Prism
15826
15889
 
15827
15890
  # def deconstruct_keys: (keys: Array[Symbol]) -> Hash[Symbol, nil | Node | Array[Node] | String | Token | Array[Token] | Location]
15828
15891
  def deconstruct_keys(keys)
15829
- { keyword_loc: keyword_loc, predicate: predicate, statements: statements, consequent: consequent, end_keyword_loc: end_keyword_loc, location: location }
15892
+ { keyword_loc: keyword_loc, predicate: predicate, then_keyword_loc: then_keyword_loc, statements: statements, consequent: consequent, end_keyword_loc: end_keyword_loc, location: location }
15830
15893
  end
15831
15894
 
15832
15895
  # def keyword: () -> String
@@ -15834,6 +15897,11 @@ module Prism
15834
15897
  keyword_loc.slice
15835
15898
  end
15836
15899
 
15900
+ # def then_keyword: () -> String?
15901
+ def then_keyword
15902
+ then_keyword_loc&.slice
15903
+ end
15904
+
15837
15905
  # def end_keyword: () -> String?
15838
15906
  def end_keyword
15839
15907
  end_keyword_loc&.slice
@@ -15845,6 +15913,7 @@ module Prism
15845
15913
  inspector << "├── keyword_loc: #{inspector.location(keyword_loc)}\n"
15846
15914
  inspector << "├── predicate:\n"
15847
15915
  inspector << inspector.child_node(predicate, "│ ")
15916
+ inspector << "├── then_keyword_loc: #{inspector.location(then_keyword_loc)}\n"
15848
15917
  if (statements = self.statements).nil?
15849
15918
  inspector << "├── statements: ∅\n"
15850
15919
  else
@@ -61,7 +61,8 @@ module Prism
61
61
  end
62
62
 
63
63
  class ConstantReadNode < Node
64
- # Returns the list of parts for the full name of this constant. For example: [:Foo]
64
+ # Returns the list of parts for the full name of this constant.
65
+ # For example: [:Foo]
65
66
  def full_name_parts
66
67
  [name]
67
68
  end
@@ -73,7 +74,16 @@ module Prism
73
74
  end
74
75
 
75
76
  class ConstantPathNode < Node
76
- # Returns the list of parts for the full name of this constant path. For example: [:Foo, :Bar]
77
+ # An error class raised when dynamic parts are found while computing a
78
+ # constant path's full name. For example:
79
+ # Foo::Bar::Baz -> does not raise because all parts of the constant path are
80
+ # simple constants
81
+ # var::Bar::Baz -> raises because the first part of the constant path is a
82
+ # local variable
83
+ class DynamicPartsInConstantPathError < StandardError; end
84
+
85
+ # Returns the list of parts for the full name of this constant path.
86
+ # For example: [:Foo, :Bar]
77
87
  def full_name_parts
78
88
  parts = [child.name]
79
89
  current = parent
@@ -83,6 +93,10 @@ module Prism
83
93
  current = current.parent
84
94
  end
85
95
 
96
+ unless current.is_a?(ConstantReadNode)
97
+ raise DynamicPartsInConstantPathError, "Constant path contains dynamic parts. Cannot compute full name"
98
+ end
99
+
86
100
  parts.unshift(current&.name || :"")
87
101
  end
88
102
 
@@ -93,7 +107,8 @@ module Prism
93
107
  end
94
108
 
95
109
  class ConstantPathTargetNode < Node
96
- # Returns the list of parts for the full name of this constant path. For example: [:Foo, :Bar]
110
+ # Returns the list of parts for the full name of this constant path.
111
+ # For example: [:Foo, :Bar]
97
112
  def full_name_parts
98
113
  (parent&.full_name_parts || [:""]).push(child.name)
99
114
  end
@@ -103,4 +118,47 @@ module Prism
103
118
  full_name_parts.join("::")
104
119
  end
105
120
  end
121
+
122
+ class ParametersNode < Node
123
+ # Mirrors the Method#parameters method.
124
+ def signature
125
+ names = []
126
+
127
+ requireds.each do |param|
128
+ names << (param.is_a?(MultiTargetNode) ? [:req] : [:req, param.name])
129
+ end
130
+
131
+ optionals.each { |param| names << [:opt, param.name] }
132
+ names << [:rest, rest.name || :*] if rest
133
+
134
+ posts.each do |param|
135
+ names << (param.is_a?(MultiTargetNode) ? [:req] : [:req, param.name])
136
+ end
137
+
138
+ # Regardless of the order in which the keywords were defined, the required
139
+ # keywords always come first followed by the optional keywords.
140
+ keyopt = []
141
+ keywords.each do |param|
142
+ if param.is_a?(OptionalKeywordParameterNode)
143
+ keyopt << param
144
+ else
145
+ names << [:keyreq, param.name]
146
+ end
147
+ end
148
+
149
+ keyopt.each { |param| names << [:key, param.name] }
150
+
151
+ case keyword_rest
152
+ when ForwardingParameterNode
153
+ names.concat([[:rest, :*], [:keyrest, :**], [:block, :&]])
154
+ when KeywordRestParameterNode
155
+ names << [:keyrest, keyword_rest.name || :**]
156
+ when NoKeywordsParameterNode
157
+ names << [:nokey]
158
+ end
159
+
160
+ names << [:block, block.name || :&] if block
161
+ names
162
+ end
163
+ end
106
164
  end