prism 1.8.0 → 1.9.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.
@@ -6,7 +6,7 @@ require_relative "../ripper"
6
6
  module Prism
7
7
  module Translation
8
8
  class Ripper
9
- class Lexer # :nodoc:
9
+ class Lexer < Ripper # :nodoc:
10
10
  # :stopdoc:
11
11
  class State
12
12
 
@@ -38,7 +38,96 @@ module Prism
38
38
  def allbits?(i) to_int.allbits?(i) end
39
39
  def anybits?(i) to_int.anybits?(i) end
40
40
  def nobits?(i) to_int.nobits?(i) end
41
+
42
+ # Instances are frozen and there are only a handful of them so we cache them here.
43
+ STATES = Hash.new { |h,k| h[k] = State.new(k) }
44
+
45
+ def self.cached(i)
46
+ STATES[i]
47
+ end
41
48
  end
49
+
50
+ class Elem
51
+ attr_accessor :pos, :event, :tok, :state, :message
52
+
53
+ def initialize(pos, event, tok, state, message = nil)
54
+ @pos = pos
55
+ @event = event
56
+ @tok = tok
57
+ @state = State.cached(state)
58
+ @message = message
59
+ end
60
+
61
+ def [](index)
62
+ case index
63
+ when 0, :pos
64
+ @pos
65
+ when 1, :event
66
+ @event
67
+ when 2, :tok
68
+ @tok
69
+ when 3, :state
70
+ @state
71
+ when 4, :message
72
+ @message
73
+ else
74
+ nil
75
+ end
76
+ end
77
+
78
+ def inspect
79
+ "#<#{self.class}: #{event}@#{pos[0]}:#{pos[1]}:#{state}: #{tok.inspect}#{": " if message}#{message}>"
80
+ end
81
+
82
+ alias to_s inspect
83
+
84
+ def pretty_print(q)
85
+ q.group(2, "#<#{self.class}:", ">") {
86
+ q.breakable
87
+ q.text("#{event}@#{pos[0]}:#{pos[1]}")
88
+ q.breakable
89
+ state.pretty_print(q)
90
+ q.breakable
91
+ q.text("token: ")
92
+ tok.pretty_print(q)
93
+ if message
94
+ q.breakable
95
+ q.text("message: ")
96
+ q.text(message)
97
+ end
98
+ }
99
+ end
100
+
101
+ def to_a
102
+ if @message
103
+ [@pos, @event, @tok, @state, @message]
104
+ else
105
+ [@pos, @event, @tok, @state]
106
+ end
107
+ end
108
+ end
109
+
110
+ # Pretty much just the same as Prism.lex_compat.
111
+ def lex(raise_errors: false)
112
+ Ripper.lex(@source, filename, lineno, raise_errors: raise_errors)
113
+ end
114
+
115
+ # Returns the lex_compat result wrapped in `Elem`. Errors are omitted.
116
+ # Since ripper is a streaming parser, tokens are expected to be emitted in the order
117
+ # that the parser encounters them. This is not implemented.
118
+ def parse(...)
119
+ lex(...).map do |position, event, token, state|
120
+ Elem.new(position, event, token, state.to_int)
121
+ end
122
+ end
123
+
124
+ # Similar to parse but ripper sorts the elements by position in the source. Also
125
+ # includes errors. Since prism does error recovery, in cases of syntax errors
126
+ # the result may differ greatly compared to ripper.
127
+ def scan(...)
128
+ parse(...)
129
+ end
130
+
42
131
  # :startdoc:
43
132
  end
44
133
  end
@@ -78,6 +78,19 @@ module Prism
78
78
  end
79
79
  end
80
80
 
81
+ # Tokenizes the Ruby program and returns an array of strings.
82
+ # The +filename+ and +lineno+ arguments are mostly ignored, since the
83
+ # return value is just the tokenized input.
84
+ # By default, this method does not handle syntax errors in +src+,
85
+ # use the +raise_errors+ keyword to raise a SyntaxError for an error in +src+.
86
+ #
87
+ # p Ripper.tokenize("def m(a) nil end")
88
+ # # => ["def", " ", "m", "(", "a", ")", " ", "nil", " ", "end"]
89
+ #
90
+ def self.tokenize(...)
91
+ lex(...).map(&:value)
92
+ end
93
+
81
94
  # This contains a table of all of the parser events and their
82
95
  # corresponding arity.
83
96
  PARSER_EVENT_TABLE = {
@@ -424,6 +437,7 @@ module Prism
424
437
  end
425
438
  end
426
439
 
440
+ autoload :Filter, "prism/translation/ripper/filter"
427
441
  autoload :Lexer, "prism/translation/ripper/lexer"
428
442
  autoload :SexpBuilder, "prism/translation/ripper/sexp"
429
443
  autoload :SexpBuilderPP, "prism/translation/ripper/sexp"
@@ -818,7 +832,7 @@ module Prism
818
832
  # foo(bar)
819
833
  # ^^^
820
834
  def visit_arguments_node(node)
821
- arguments, _ = visit_call_node_arguments(node, nil, false)
835
+ arguments, _, _ = visit_call_node_arguments(node, nil, false)
822
836
  arguments
823
837
  end
824
838
 
@@ -1028,16 +1042,16 @@ module Prism
1028
1042
  case node.name
1029
1043
  when :[]
1030
1044
  receiver = visit(node.receiver)
1031
- arguments, block = visit_call_node_arguments(node.arguments, node.block, trailing_comma?(node.arguments&.location || node.location, node.closing_loc))
1045
+ arguments, block, has_ripper_block = visit_call_node_arguments(node.arguments, node.block, trailing_comma?(node.arguments&.location || node.location, node.closing_loc))
1032
1046
 
1033
1047
  bounds(node.location)
1034
1048
  call = on_aref(receiver, arguments)
1035
1049
 
1036
- if block.nil?
1037
- call
1038
- else
1050
+ if has_ripper_block
1039
1051
  bounds(node.location)
1040
1052
  on_method_add_block(call, block)
1053
+ else
1054
+ call
1041
1055
  end
1042
1056
  when :[]=
1043
1057
  receiver = visit(node.receiver)
@@ -1096,9 +1110,9 @@ module Prism
1096
1110
  if node.variable_call?
1097
1111
  on_vcall(message)
1098
1112
  else
1099
- arguments, block = visit_call_node_arguments(node.arguments, node.block, trailing_comma?(node.arguments&.location || node.location, node.closing_loc || node.location))
1113
+ arguments, block, has_ripper_block = visit_call_node_arguments(node.arguments, node.block, trailing_comma?(node.arguments&.location || node.location, node.closing_loc || node.location))
1100
1114
  call =
1101
- if node.opening_loc.nil? && arguments&.any?
1115
+ if node.opening_loc.nil? && get_arguments_and_block(node.arguments, node.block).first.any?
1102
1116
  bounds(node.location)
1103
1117
  on_command(message, arguments)
1104
1118
  elsif !node.opening_loc.nil?
@@ -1109,11 +1123,11 @@ module Prism
1109
1123
  on_method_add_arg(on_fcall(message), on_args_new)
1110
1124
  end
1111
1125
 
1112
- if block.nil?
1113
- call
1114
- else
1126
+ if has_ripper_block
1115
1127
  bounds(node.block.location)
1116
1128
  on_method_add_block(call, block)
1129
+ else
1130
+ call
1117
1131
  end
1118
1132
  end
1119
1133
  end
@@ -1137,7 +1151,7 @@ module Prism
1137
1151
  bounds(node.location)
1138
1152
  on_assign(on_field(receiver, call_operator, message), value)
1139
1153
  else
1140
- arguments, block = visit_call_node_arguments(node.arguments, node.block, trailing_comma?(node.arguments&.location || node.location, node.closing_loc || node.location))
1154
+ arguments, block, has_ripper_block = visit_call_node_arguments(node.arguments, node.block, trailing_comma?(node.arguments&.location || node.location, node.closing_loc || node.location))
1141
1155
  call =
1142
1156
  if node.opening_loc.nil?
1143
1157
  bounds(node.location)
@@ -1155,27 +1169,35 @@ module Prism
1155
1169
  on_method_add_arg(on_call(receiver, call_operator, message), arguments)
1156
1170
  end
1157
1171
 
1158
- if block.nil?
1159
- call
1160
- else
1172
+ if has_ripper_block
1161
1173
  bounds(node.block.location)
1162
1174
  on_method_add_block(call, block)
1175
+ else
1176
+ call
1163
1177
  end
1164
1178
  end
1165
1179
  end
1166
1180
  end
1167
1181
 
1168
- # Visit the arguments and block of a call node and return the arguments
1169
- # and block as they should be used.
1170
- private def visit_call_node_arguments(arguments_node, block_node, trailing_comma)
1182
+ # Extract the arguments and block Ripper-style, which means if the block
1183
+ # is like `&b` then it's moved to arguments.
1184
+ private def get_arguments_and_block(arguments_node, block_node)
1171
1185
  arguments = arguments_node&.arguments || []
1172
1186
  block = block_node
1173
1187
 
1174
1188
  if block.is_a?(BlockArgumentNode)
1175
- arguments << block
1189
+ arguments += [block]
1176
1190
  block = nil
1177
1191
  end
1178
1192
 
1193
+ [arguments, block]
1194
+ end
1195
+
1196
+ # Visit the arguments and block of a call node and return the arguments
1197
+ # and block as they should be used.
1198
+ private def visit_call_node_arguments(arguments_node, block_node, trailing_comma)
1199
+ arguments, block = get_arguments_and_block(arguments_node, block_node)
1200
+
1179
1201
  [
1180
1202
  if arguments.length == 1 && arguments.first.is_a?(ForwardingArgumentsNode)
1181
1203
  visit(arguments.first)
@@ -1189,7 +1211,8 @@ module Prism
1189
1211
  on_args_add_block(args, false)
1190
1212
  end
1191
1213
  end,
1192
- visit(block)
1214
+ visit(block),
1215
+ block != nil,
1193
1216
  ]
1194
1217
  end
1195
1218
 
@@ -1626,10 +1649,10 @@ module Prism
1626
1649
  end
1627
1650
 
1628
1651
  bounds(node.location)
1629
- if receiver.nil?
1630
- on_def(name, parameters, bodystmt)
1631
- else
1652
+ if receiver
1632
1653
  on_defs(receiver, operator, name, parameters, bodystmt)
1654
+ else
1655
+ on_def(name, parameters, bodystmt)
1633
1656
  end
1634
1657
  end
1635
1658
 
@@ -2027,7 +2050,7 @@ module Prism
2027
2050
  # ^^^^^^^^^^^^^^^
2028
2051
  def visit_index_operator_write_node(node)
2029
2052
  receiver = visit(node.receiver)
2030
- arguments, _ = visit_call_node_arguments(node.arguments, node.block, trailing_comma?(node.arguments&.location || node.location, node.closing_loc))
2053
+ arguments, _, _ = visit_call_node_arguments(node.arguments, node.block, trailing_comma?(node.arguments&.location || node.location, node.closing_loc))
2031
2054
 
2032
2055
  bounds(node.location)
2033
2056
  target = on_aref_field(receiver, arguments)
@@ -2044,7 +2067,7 @@ module Prism
2044
2067
  # ^^^^^^^^^^^^^^^^
2045
2068
  def visit_index_and_write_node(node)
2046
2069
  receiver = visit(node.receiver)
2047
- arguments, _ = visit_call_node_arguments(node.arguments, node.block, trailing_comma?(node.arguments&.location || node.location, node.closing_loc))
2070
+ arguments, _, _ = visit_call_node_arguments(node.arguments, node.block, trailing_comma?(node.arguments&.location || node.location, node.closing_loc))
2048
2071
 
2049
2072
  bounds(node.location)
2050
2073
  target = on_aref_field(receiver, arguments)
@@ -2061,7 +2084,7 @@ module Prism
2061
2084
  # ^^^^^^^^^^^^^^^^
2062
2085
  def visit_index_or_write_node(node)
2063
2086
  receiver = visit(node.receiver)
2064
- arguments, _ = visit_call_node_arguments(node.arguments, node.block, trailing_comma?(node.arguments&.location || node.location, node.closing_loc))
2087
+ arguments, _, _ = visit_call_node_arguments(node.arguments, node.block, trailing_comma?(node.arguments&.location || node.location, node.closing_loc))
2065
2088
 
2066
2089
  bounds(node.location)
2067
2090
  target = on_aref_field(receiver, arguments)
@@ -2078,7 +2101,7 @@ module Prism
2078
2101
  # ^^^^^^^^
2079
2102
  def visit_index_target_node(node)
2080
2103
  receiver = visit(node.receiver)
2081
- arguments, _ = visit_call_node_arguments(node.arguments, node.block, trailing_comma?(node.arguments&.location || node.location, node.closing_loc))
2104
+ arguments, _, _ = visit_call_node_arguments(node.arguments, node.block, trailing_comma?(node.arguments&.location || node.location, node.closing_loc))
2082
2105
 
2083
2106
  bounds(node.location)
2084
2107
  on_aref_field(receiver, arguments)
@@ -3108,7 +3131,7 @@ module Prism
3108
3131
  # super(foo)
3109
3132
  # ^^^^^^^^^^
3110
3133
  def visit_super_node(node)
3111
- arguments, block = visit_call_node_arguments(node.arguments, node.block, trailing_comma?(node.arguments&.location || node.location, node.rparen_loc || node.location))
3134
+ arguments, block, has_ripper_block = visit_call_node_arguments(node.arguments, node.block, trailing_comma?(node.arguments&.location || node.location, node.rparen_loc || node.location))
3112
3135
 
3113
3136
  if !node.lparen_loc.nil?
3114
3137
  bounds(node.lparen_loc)
@@ -3118,11 +3141,11 @@ module Prism
3118
3141
  bounds(node.location)
3119
3142
  call = on_super(arguments)
3120
3143
 
3121
- if block.nil?
3122
- call
3123
- else
3144
+ if has_ripper_block
3124
3145
  bounds(node.block.location)
3125
3146
  on_method_add_block(call, block)
3147
+ else
3148
+ call
3126
3149
  end
3127
3150
  end
3128
3151
 
@@ -3432,12 +3455,12 @@ module Prism
3432
3455
 
3433
3456
  # :stopdoc:
3434
3457
  def _dispatch_0; end
3435
- def _dispatch_1(_); end
3436
- def _dispatch_2(_, _); end
3437
- def _dispatch_3(_, _, _); end
3438
- def _dispatch_4(_, _, _, _); end
3439
- def _dispatch_5(_, _, _, _, _); end
3440
- def _dispatch_7(_, _, _, _, _, _, _); end
3458
+ def _dispatch_1(arg); arg end
3459
+ def _dispatch_2(arg, _); arg end
3460
+ def _dispatch_3(arg, _, _); arg end
3461
+ def _dispatch_4(arg, _, _, _); arg end
3462
+ def _dispatch_5(arg, _, _, _, _); arg end
3463
+ def _dispatch_7(arg, _, _, _, _, _, _); arg end
3441
3464
  # :startdoc:
3442
3465
 
3443
3466
  #
data/lib/prism.rb CHANGED
@@ -20,7 +20,6 @@ module Prism
20
20
  autoload :DSL, "prism/dsl"
21
21
  autoload :InspectVisitor, "prism/inspect_visitor"
22
22
  autoload :LexCompat, "prism/lex_compat"
23
- autoload :LexRipper, "prism/lex_ripper"
24
23
  autoload :MutationCompiler, "prism/mutation_compiler"
25
24
  autoload :Pack, "prism/pack"
26
25
  autoload :Pattern, "prism/pattern"
@@ -35,7 +34,6 @@ module Prism
35
34
  # private here.
36
35
 
37
36
  private_constant :LexCompat
38
- private_constant :LexRipper
39
37
 
40
38
  # Raised when requested to parse as the currently running Ruby version but Prism has no support for it.
41
39
  class CurrentVersionError < ArgumentError
@@ -61,24 +59,13 @@ module Prism
61
59
  # Prism::lex_compat(source, **options) -> LexCompat::Result
62
60
  #
63
61
  # Returns a parse result whose value is an array of tokens that closely
64
- # resembles the return value of Ripper::lex. The main difference is that the
65
- # `:on_sp` token is not emitted.
62
+ # resembles the return value of Ripper::lex.
66
63
  #
67
64
  # For supported options, see Prism::parse.
68
65
  def self.lex_compat(source, **options)
69
66
  LexCompat.new(source, **options).result # steep:ignore
70
67
  end
71
68
 
72
- # :call-seq:
73
- # Prism::lex_ripper(source) -> Array
74
- #
75
- # This lexes with the Ripper lex. It drops any space events but otherwise
76
- # returns the same tokens. Raises SyntaxError if the syntax in source is
77
- # invalid.
78
- def self.lex_ripper(source)
79
- LexRipper.new(source).result # steep:ignore
80
- end
81
-
82
69
  # :call-seq:
83
70
  # Prism::load(source, serialized, freeze) -> ParseResult
84
71
  #
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.8.0"
5
+ spec.version = "1.9.0"
6
6
  spec.authors = ["Shopify"]
7
7
  spec.email = ["ruby@shopify.com"]
8
8
 
@@ -77,7 +77,6 @@ Gem::Specification.new do |spec|
77
77
  "lib/prism/ffi.rb",
78
78
  "lib/prism/inspect_visitor.rb",
79
79
  "lib/prism/lex_compat.rb",
80
- "lib/prism/lex_ripper.rb",
81
80
  "lib/prism/mutation_compiler.rb",
82
81
  "lib/prism/node_ext.rb",
83
82
  "lib/prism/node.rb",
@@ -104,6 +103,7 @@ Gem::Specification.new do |spec|
104
103
  "lib/prism/translation/parser/compiler.rb",
105
104
  "lib/prism/translation/parser/lexer.rb",
106
105
  "lib/prism/translation/ripper.rb",
106
+ "lib/prism/translation/ripper/filter.rb",
107
107
  "lib/prism/translation/ripper/lexer.rb",
108
108
  "lib/prism/translation/ripper/sexp.rb",
109
109
  "lib/prism/translation/ripper/shim.rb",
data/rbi/prism/node.rbi CHANGED
@@ -57,6 +57,9 @@ class Prism::Node
57
57
  sig { params(block: T.proc.params(node: Prism::Node).returns(T::Boolean)).returns(T.nilable(Prism::Node)) }
58
58
  def breadth_first_search(&block); end
59
59
 
60
+ sig { params(block: T.proc.params(node: Prism::Node).returns(T::Boolean)).returns(T::Array[Prism::Node]) }
61
+ def breadth_first_search_all(&block); end
62
+
60
63
  sig { abstract.params(visitor: Prism::Visitor).returns(T.untyped) }
61
64
  def accept(visitor); end
62
65
 
data/rbi/prism.rbi CHANGED
@@ -16,9 +16,6 @@ module Prism
16
16
  sig { params(source: String, options: T::Hash[Symbol, T.untyped]).returns(Prism::LexCompat::Result) }
17
17
  def self.lex_compat(source, **options); end
18
18
 
19
- sig { params(source: String).returns(T::Array[T.untyped]) }
20
- def self.lex_ripper(source); end
21
-
22
19
  sig { params(source: String, serialized: String, freeze: T.nilable(T::Boolean)).returns(Prism::ParseResult) }
23
20
  def self.load(source, serialized, freeze = false); end
24
21
 
data/sig/prism/node.rbs CHANGED
@@ -29,6 +29,9 @@ module Prism
29
29
  def to_dot: () -> String
30
30
  def tunnel: (Integer line, Integer column) -> Array[Prism::node]
31
31
  def breadth_first_search: () { (Prism::node) -> bool } -> Prism::node?
32
+ alias find breadth_first_search
33
+ def breadth_first_search_all: () { (Prism::node) -> bool } -> Array[Prism::node]
34
+ alias find_all breadth_first_search_all
32
35
  def newline!: (Array[untyped]) -> void
33
36
 
34
37
  def save: (_Repository repository) -> void
@@ -14,6 +14,7 @@ module Prism
14
14
  def encoding: () -> Encoding
15
15
  def lines: () -> Array[String]
16
16
  def slice: (Integer byte_offset, Integer length) -> String
17
+ def byte_offset: (Integer line, Integer column) -> Integer
17
18
  def line: (Integer byte_offset) -> Integer
18
19
  def line_start: (Integer byte_offset) -> Integer
19
20
  def line_end: (Integer byte_offset) -> Integer