prism 0.17.1 → 0.18.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 (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