katakata_irb 0.1.12 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,7 +1,9 @@
1
- require 'ripper'
1
+ # frozen_string_literal: true
2
+
2
3
  require 'set'
3
4
  require_relative 'types'
4
5
  require_relative 'scope'
6
+ require 'prism'
5
7
 
6
8
  class KatakataIrb::TypeSimulator
7
9
  class DigTarget
@@ -18,21 +20,6 @@ class KatakataIrb::TypeSimulator
18
20
  end
19
21
  end
20
22
 
21
- module LexerElemMatcher
22
- refine Ripper::Lexer::Elem do
23
- def deconstruct_keys(_keys)
24
- {
25
- tok: tok,
26
- event: event,
27
- label: state.allbits?(Ripper::EXPR_LABEL),
28
- beg: state.allbits?(Ripper::EXPR_BEG),
29
- dot: state.allbits?(Ripper::EXPR_DOT)
30
- }
31
- end
32
- end
33
- end
34
- using LexerElemMatcher
35
-
36
23
  OBJECT_METHODS = {
37
24
  to_s: KatakataIrb::Types::STRING,
38
25
  to_str: KatakataIrb::Types::STRING,
@@ -51,22 +38,25 @@ class KatakataIrb::TypeSimulator
51
38
  @dig_targets = dig_targets
52
39
  end
53
40
 
54
- def simulate_evaluate(sexp, scope, case_target: nil)
55
- result = simulate_evaluate_inner(sexp, scope, case_target: case_target)
56
- @dig_targets.resolve result, scope if @dig_targets.target?(sexp)
41
+ def evaluate(node, scope)
42
+ result = evaluate_inner(node, scope)
43
+ @dig_targets.resolve result, scope if @dig_targets.target?(node)
57
44
  result
58
45
  end
59
46
 
60
- def simulate_evaluate_inner(sexp, scope, case_target: nil)
61
- case sexp
62
- in [:program, statements]
63
- statements.map { simulate_evaluate _1, scope }.last
64
- in [:def | :defs,]
65
- sexp in [:def, _method_name_exp, params, body_stmt]
66
- sexp in [:defs, receiver_exp, _dot_exp, _method_name_exp, params, body_stmt]
67
- if receiver_exp
68
- receiver_exp in [:paren, receiver_exp]
69
- self_type = simulate_evaluate receiver_exp, scope
47
+ def evaluate_inner(node, scope)
48
+ case node
49
+ when Prism::ProgramNode
50
+ evaluate node.statements, scope
51
+ when Prism::StatementsNode
52
+ if node.body.empty?
53
+ KatakataIrb::Types::NIL
54
+ else
55
+ node.body.map { evaluate _1, scope }.last
56
+ end
57
+ when Prism::DefNode
58
+ if node.receiver
59
+ self_type = evaluate node.receiver, scope
70
60
  else
71
61
  current_self_types = scope.self_type.types
72
62
  self_types = current_self_types.map do |type|
@@ -78,104 +68,81 @@ class KatakataIrb::TypeSimulator
78
68
  end
79
69
  self_type = KatakataIrb::Types::UnionType[*self_types]
80
70
  end
81
- if @dig_targets.dig? sexp
82
- params in [:paren, params]
83
- params ||= [:params, nil, nil, nil, nil, nil, nil, nil] # params might be nil in ruby 3.0
84
- params_table = extract_param_names(params).to_h { [_1, KatakataIrb::Types::NIL] }
71
+ if @dig_targets.dig?(node.body) || @dig_targets.dig?(node.parameters)
72
+ params_table = node.locals.to_h { [_1.to_s, KatakataIrb::Types::NIL] }
85
73
  method_scope = KatakataIrb::Scope.new(
86
74
  scope,
87
- { **params_table, KatakataIrb::Scope::SELF => self_type, KatakataIrb::Scope::BREAK_RESULT => nil, KatakataIrb::Scope::NEXT_RESULT => nil, KatakataIrb::Scope::RETURN_RESULT => nil },
88
- trace_lvar: false
75
+ { **params_table, KatakataIrb::Scope::BREAK_RESULT => nil, KatakataIrb::Scope::NEXT_RESULT => nil, KatakataIrb::Scope::RETURN_RESULT => nil },
76
+ self_type: self_type,
77
+ trace_lvar: false,
78
+ trace_ivar: false
89
79
  )
90
- evaluate_assign_params params, [], method_scope
91
- method_scope.conditional { evaluate_param_defaults params, _1 }
92
- simulate_evaluate body_stmt, method_scope
80
+ if node.parameters
81
+ # node.parameters is Prism::ParametersNode
82
+ assign_parameters node.parameters, method_scope, [], {}
83
+ end
84
+
85
+ if @dig_targets.dig?(node.body)
86
+ method_scope.conditional do |s|
87
+ evaluate node.body, s
88
+ end
89
+ end
93
90
  method_scope.merge_jumps
94
91
  scope.update method_scope
95
92
  end
96
93
  KatakataIrb::Types::SYMBOL
97
- in [:@int,]
94
+ when Prism::IntegerNode
98
95
  KatakataIrb::Types::INTEGER
99
- in [:@float,]
96
+ when Prism::FloatNode
100
97
  KatakataIrb::Types::FLOAT
101
- in [:@rational,]
98
+ when Prism::RationalNode
102
99
  KatakataIrb::Types::RATIONAL
103
- in [:@imaginary,]
100
+ when Prism::ImaginaryNode
104
101
  KatakataIrb::Types::COMPLEX
105
- in [:@tstring_content,]
102
+ when Prism::StringNode
106
103
  KatakataIrb::Types::STRING
107
- in [:symbol_literal,]
108
- KatakataIrb::Types::SYMBOL
109
- in [:dyna_symbol, [:string_content, *statements]]
110
- statements.each { simulate_evaluate _1, scope }
104
+ when Prism::XStringNode
105
+ KatakataIrb::Types::UnionType[KatakataIrb::Types::STRING, KatakataIrb::Types::NIL]
106
+ when Prism::SymbolNode
111
107
  KatakataIrb::Types::SYMBOL
112
- in [:@CHAR,]
108
+ when Prism::RegularExpressionNode
109
+ KatakataIrb::Types::REGEXP
110
+ when Prism::StringConcatNode
111
+ evaluate node.left, scope
112
+ evaluate node.right, scope
113
113
  KatakataIrb::Types::STRING
114
- in [:@backref,]
115
- KatakataIrb::Types::UnionType[KatakataIrb::Types::STRING, KatakataIrb::Types::NIL]
116
- in [:string_literal, [:string_content, *statements]]
117
- statements.each { simulate_evaluate _1, scope }
114
+ when Prism::InterpolatedStringNode
115
+ node.parts.each { evaluate _1, scope }
118
116
  KatakataIrb::Types::STRING
119
- in [:xstring_literal, statements]
120
- statements.each { simulate_evaluate _1, scope }
117
+ when Prism::InterpolatedXStringNode
118
+ node.parts.each { evaluate _1, scope }
121
119
  KatakataIrb::Types::STRING
122
- in [:string_embexpr, statements]
123
- statements.each { simulate_evaluate _1, scope }
120
+ when Prism::InterpolatedSymbolNode
121
+ node.parts.each { evaluate _1, scope }
122
+ KatakataIrb::Types::SYMBOL
123
+ when Prism::InterpolatedRegularExpressionNode
124
+ node.parts.each { evaluate _1, scope }
125
+ KatakataIrb::Types::REGEXP
126
+ when Prism::EmbeddedStatementsNode
127
+ node.statements ? evaluate(node.statements, scope) : KatakataIrb::Types::NIL
124
128
  KatakataIrb::Types::STRING
125
- in [:string_dvar,]
129
+ when Prism::EmbeddedVariableNode
130
+ evaluate node.variable, scope
126
131
  KatakataIrb::Types::STRING
127
- in [:regexp_literal, statements, _regexp_end]
128
- statements.each { simulate_evaluate _1, scope }
129
- KatakataIrb::Types::REGEXP
130
- in [:array, [:args_add_star,] => star]
131
- args, kwargs = retrieve_method_args star
132
- types = args.flat_map do |elem|
133
- if elem.is_a? KatakataIrb::Types::Splat
134
- splat = simulate_evaluate elem.item, scope
135
- array_elem, non_array = partition_to_array splat.nonnillable, :to_a
136
- KatakataIrb::Types::UnionType[*array_elem, *non_array]
137
- else
138
- simulate_evaluate elem, scope
139
- end
140
- end
141
- types << kwargs_type(kwargs, scope) if kwargs && kwargs.any?
142
- KatakataIrb::Types::InstanceType.new Array, Elem: KatakataIrb::Types::UnionType[*types]
143
- in [:array, statements]
144
- if statements.nil? || statements.empty?
145
- KatakataIrb::Types::ARRAY
146
- elsif statements.all? { _1 in [Symbol,] }
147
- # normal array
148
- elem = statements ? KatakataIrb::Types::UnionType[*statements.map { simulate_evaluate _1, scope }] : KatakataIrb::Types::NIL
149
- KatakataIrb::Types::InstanceType.new Array, Elem: elem
150
- else
151
- # %I[] or %W[]
152
- statements.each do |sub_statements|
153
- sub_statements.each { simulate_evaluate _1, scope }
154
- end
155
- # TODO: use AST because Ripper.sexp('%I[a]') == Ripper.sexp('%W[a]')
156
- elem = KatakataIrb::Types::UnionType[KatakataIrb::Types::STRING, KatakataIrb::Types::SYMBOL]
157
- KatakataIrb::Types::InstanceType.new Array, Elem: elem
158
- end
159
- in [:bare_assoc_hash, args]
160
- simulate_evaluate [:hash, [:assoclist_from_args, args]], scope
161
- in [:hash, [:assoclist_from_args, args]]
132
+ when Prism::ArrayNode
133
+ KatakataIrb::Types.array_of evaluate_list_splat_items(node.elements, scope)
134
+ when Prism::HashNode, Prism::KeywordHashNode
162
135
  keys = []
163
136
  values = []
164
- args.each do |arg|
165
- case arg
166
- in [:assoc_new, key, value]
167
- if key in [:@label, label, pos]
168
- keys << KatakataIrb::Types::SYMBOL
169
- name = label.delete ':'
170
- value ||= [:__var_ref_or_call, [name =~ /\A[A-Z]/ ? :@const : :@ident, name, pos]]
171
- else
172
- keys << simulate_evaluate(key, scope)
173
- end
174
- values << simulate_evaluate(value, scope)
175
- in [:assoc_splat, value]
176
- hash = simulate_evaluate value, scope
137
+ node.elements.each do |assoc|
138
+ case assoc
139
+ when Prism::AssocNode
140
+ keys << evaluate(assoc.key, scope)
141
+ values << evaluate(assoc.value, scope)
142
+ when Prism::AssocSplatNode
143
+ hash = evaluate assoc.value, scope
177
144
  unless hash.is_a?(KatakataIrb::Types::InstanceType) && hash.klass == Hash
178
- hash = simulate_call hash, :to_hash, [], nil, nil
145
+ hash = simulate_call hash, :to_hash, [], nil, nil, scope
179
146
  end
180
147
  if hash.is_a?(KatakataIrb::Types::InstanceType) && hash.klass == Hash
181
148
  keys << hash.params[:K] if hash.params[:K]
@@ -183,120 +150,89 @@ class KatakataIrb::TypeSimulator
183
150
  end
184
151
  end
185
152
  end
186
- KatakataIrb::Types::InstanceType.new Hash, K: KatakataIrb::Types::UnionType[*keys], V: KatakataIrb::Types::UnionType[*values]
187
- in [:hash, nil]
188
- KatakataIrb::Types::InstanceType.new Hash
189
- in [:paren, [Symbol,] | false => statement]
190
- # workaround for `p ()` and `p (foo)`
191
- simulate_evaluate statement, scope if statement
192
- in [:paren | :ensure | :else, statements]
193
- statements.map { simulate_evaluate _1, scope }.last
194
- in [:const_path_ref, receiver, [:@const, name,]]
195
- r = simulate_evaluate receiver, scope
196
- r.is_a?(KatakataIrb::Types::SingletonType) ? KatakataIrb::BaseScope.type_of { r.module_or_class.const_get name } : KatakataIrb::Types::NIL
197
- in [:__var_ref_or_call, [type, name, pos]]
198
- sexp = scope.has?(name) ? [:var_ref, [type, name, pos]] : [:vcall, [:@ident, name, pos]]
199
- simulate_evaluate sexp, scope
200
- in [:var_ref, [:@kw, name,]]
201
- case name
202
- in 'self'
203
- scope.self_type
204
- in 'true'
205
- KatakataIrb::Types::TRUE
206
- in 'false'
207
- KatakataIrb::Types::FALSE
208
- in 'nil'
209
- KatakataIrb::Types::NIL
210
- in '__FILE__'
153
+ if keys.empty? && values.empty?
154
+ KatakataIrb::Types::InstanceType.new Hash
155
+ else
156
+ KatakataIrb::Types::InstanceType.new Hash, K: KatakataIrb::Types::UnionType[*keys], V: KatakataIrb::Types::UnionType[*values]
157
+ end
158
+ when Prism::ParenthesesNode
159
+ node.body ? evaluate(node.body, scope) : KatakataIrb::Types::NIL
160
+ when Prism::ConstantPathNode
161
+ type, = evaluate_constant_node node, scope
162
+ type
163
+ when Prism::SelfNode
164
+ scope.self_type
165
+ when Prism::TrueNode
166
+ KatakataIrb::Types::TRUE
167
+ when Prism::FalseNode
168
+ KatakataIrb::Types::FALSE
169
+ when Prism::NilNode
170
+ KatakataIrb::Types::NIL
171
+ when Prism::SourceFileNode
211
172
  KatakataIrb::Types::STRING
212
- in '__LINE__'
173
+ when Prism::SourceLineNode
213
174
  KatakataIrb::Types::INTEGER
214
- in '__ENCODING__'
215
- KatakataIrb::Types::InstanceType.new Encoding
216
- end
217
- in [:var_ref, [:@const | :@ivar | :@cvar | :@gvar | :@ident, name,]]
218
- scope[name] || KatakataIrb::Types::NIL
219
- in [:const_ref, [:@const, name,]]
220
- scope[name] || KatakataIrb::Types::NIL
221
- in [:aref, receiver, args]
222
- receiver_type = simulate_evaluate receiver, scope if receiver
223
- args, kwargs, _block = retrieve_method_args args
224
- args_type = args.map do |arg|
225
- if arg.is_a? KatakataIrb::Types::Splat
226
- simulate_evaluate arg.item, scope
227
- nil # TODO: splat
228
- else
229
- simulate_evaluate arg, scope
230
- end
231
- end
232
- simulate_call receiver_type, :[], args_type, kwargs_type(kwargs, scope), nil
233
- in [:call | :vcall | :command | :command_call | :method_add_arg | :method_add_block,]
234
- if (sexp in [:vcall, [:@ident, name,]]) && scope.has?(name)
235
- # workaround for https://bugs.ruby-lang.org/issues/19175
236
- return scope[name]
237
- end
238
- receiver, method, args, kwargs, block, optional_chain = retrieve_method_call sexp
239
- if receiver.nil? && method == 'raise'
240
- scope.terminate_with KatakataIrb::Scope::RAISE_BREAK, KatakataIrb::Types::TRUE
241
- return KatakataIrb::Types::NIL
242
- end
243
- receiver_type = receiver ? simulate_evaluate(receiver, scope) : scope.self_type
175
+ when Prism::SourceEncodingNode
176
+ KatakataIrb::Types::InstanceType.new Encoding
177
+ when Prism::NumberedReferenceReadNode, Prism::BackReferenceReadNode
178
+ KatakataIrb::Types::UnionType[KatakataIrb::Types::STRING, KatakataIrb::Types::NIL]
179
+ when Prism::LocalVariableReadNode
180
+ scope[node.name.to_s] || KatakataIrb::Types::NIL
181
+ when Prism::ConstantReadNode, Prism::GlobalVariableReadNode, Prism::InstanceVariableReadNode, Prism::ClassVariableReadNode
182
+ scope[node.name.to_s] || KatakataIrb::Types::NIL
183
+ when Prism::CallNode
184
+ # TODO: return type of []=, field= when operator_loc.nil?
185
+ receiver_type = node.receiver ? evaluate(node.receiver, scope) : scope.self_type
244
186
  evaluate_method = lambda do |scope|
245
- args_type = args.map do |arg|
246
- if arg.is_a? KatakataIrb::Types::Splat
247
- simulate_evaluate arg.item, scope
248
- nil # TODO: splat
249
- else
250
- simulate_evaluate arg, scope
251
- end
252
- end
187
+ args_types, kwargs_types, block_sym_node, has_block = evaluate_call_node_arguments node, scope
253
188
 
254
- if block
255
- if block in [:symbol_literal, [:symbol, [:@ident, block_name,]]]
189
+ if block_sym_node
190
+ block_sym = block_sym_node.value
191
+ if @dig_targets.target? block_sym_node
192
+ # method(args, &:completion_target)
193
+ call_block_proc = ->(block_args, _self_type) do
194
+ block_receiver = block_args.first || KatakataIrb::Types::OBJECT
195
+ @dig_targets.resolve block_receiver, scope
196
+ KatakataIrb::Types::OBJECT
197
+ end
198
+ else
256
199
  call_block_proc = ->(block_args, _self_type) do
257
200
  block_receiver, *rest = block_args
258
- block_receiver ? simulate_call(block_receiver || KatakataIrb::Types::OBJECT, block_name, rest, nil, nil) : KatakataIrb::Types::OBJECT
201
+ block_receiver ? simulate_call(block_receiver || KatakataIrb::Types::OBJECT, block_sym, rest, nil, nil, scope) : KatakataIrb::Types::OBJECT
259
202
  end
260
- elsif block in [:do_block | :brace_block => type, block_var, body]
261
- block_var in [:block_var, params,]
262
- call_block_proc = ->(block_args, block_self_type) do
263
- scope.conditional do |s|
264
- if params
265
- names = extract_param_names(params)
266
- else
267
- names = (1..max_numbered_params(body)).map { "_#{_1}" }
268
- params = [:params, names.map { [:@ident, _1, [0, 0]] }, nil, nil, nil, nil, nil, nil]
269
- end
270
- params_table = names.zip(block_args).to_h { [_1, _2 || KatakataIrb::Types::NIL] }
271
- table = { **params_table, KatakataIrb::Scope::BREAK_RESULT => nil, KatakataIrb::Scope::NEXT_RESULT => nil }
272
- table[KatakataIrb::Scope::SELF] = block_self_type if block_self_type
273
- block_scope = KatakataIrb::Scope.new s, table
274
- evaluate_assign_params params, block_args, block_scope
275
- block_scope.conditional { evaluate_param_defaults params, _1 } if params
276
- if type == :do_block
277
- result = simulate_evaluate body, block_scope
278
- else
279
- result = body.map { simulate_evaluate _1, block_scope }.last
280
- end
281
- block_scope.merge_jumps
282
- s.update block_scope
283
- nexts = block_scope[KatakataIrb::Scope::NEXT_RESULT]
284
- breaks = block_scope[KatakataIrb::Scope::BREAK_RESULT]
285
- if block_scope.terminated?
286
- [KatakataIrb::Types::UnionType[*nexts], breaks]
287
- else
288
- [KatakataIrb::Types::UnionType[result, *nexts], breaks]
289
- end
203
+ end
204
+ elsif node.block.is_a? Prism::BlockNode
205
+ call_block_proc = ->(block_args, block_self_type) do
206
+ scope.conditional do |s|
207
+ numbered_parameters = node.block.locals.grep(/\A_[1-9]/).map(&:to_s)
208
+ params_table = node.block.locals.to_h { [_1.to_s, KatakataIrb::Types::NIL] }
209
+ table = { **params_table, KatakataIrb::Scope::BREAK_RESULT => nil, KatakataIrb::Scope::NEXT_RESULT => nil }
210
+ block_scope = KatakataIrb::Scope.new s, table, self_type: block_self_type, trace_ivar: !block_self_type
211
+ # TODO kwargs
212
+ if node.block.parameters&.parameters
213
+ # node.block.parameters is Prism::BlockParametersNode
214
+ assign_parameters node.block.parameters.parameters, block_scope, block_args, {}
215
+ elsif !numbered_parameters.empty?
216
+ assign_numbered_parameters numbered_parameters, block_scope, block_args, {}
217
+ end
218
+ result = node.block.body ? evaluate(node.block.body, block_scope) : KatakataIrb::Types::NIL
219
+ block_scope.merge_jumps
220
+ s.update block_scope
221
+ nexts = block_scope[KatakataIrb::Scope::NEXT_RESULT]
222
+ breaks = block_scope[KatakataIrb::Scope::BREAK_RESULT]
223
+ if block_scope.terminated?
224
+ [KatakataIrb::Types::UnionType[*nexts], breaks]
225
+ else
226
+ [KatakataIrb::Types::UnionType[result, *nexts], breaks]
290
227
  end
291
228
  end
292
- else
293
- call_block_proc = ->(_block_args, _self_type) { KatakataIrb::Types::OBJECT }
294
- simulate_evaluate block, scope
295
229
  end
230
+ elsif has_block
231
+ call_block_proc = ->(_block_args, _self_type) { KatakataIrb::Types::OBJECT }
296
232
  end
297
- simulate_call receiver_type, method, args_type, kwargs_type(kwargs, scope), call_block_proc
233
+ simulate_call receiver_type, node.name, args_types, kwargs_types, call_block_proc, scope
298
234
  end
299
- if optional_chain
235
+ if node.call_operator == '&.'
300
236
  result = scope.conditional { evaluate_method.call _1 }
301
237
  if receiver_type.nillable?
302
238
  KatakataIrb::Types::UnionType[result, KatakataIrb::Types::NIL]
@@ -306,381 +242,671 @@ class KatakataIrb::TypeSimulator
306
242
  else
307
243
  evaluate_method.call scope
308
244
  end
309
- in [:binary, a, Symbol => op, b]
310
- atype = simulate_evaluate a, scope
311
- case op
312
- when :'&&', :and
313
- btype = scope.conditional { simulate_evaluate b, _1 }
314
- KatakataIrb::Types::UnionType[btype, KatakataIrb::Types::NIL, KatakataIrb::Types::FALSE]
315
- when :'||', :or
316
- btype = scope.conditional { simulate_evaluate b, _1 }
317
- KatakataIrb::Types::UnionType[atype, btype]
245
+ when Prism::AndNode, Prism::OrNode
246
+ left = evaluate node.left, scope
247
+ right = scope.conditional { evaluate node.right, _1 }
248
+ if node.operator == '&&'
249
+ KatakataIrb::Types::UnionType[right, KatakataIrb::Types::NIL, KatakataIrb::Types::FALSE]
250
+ else
251
+ KatakataIrb::Types::UnionType[left, right]
252
+ end
253
+ when Prism::CallOperatorWriteNode, Prism::CallAndWriteNode, Prism::CallOrWriteNode
254
+ receiver_type = evaluate node.receiver, scope
255
+ args_types, kwargs_types, block_sym_node, has_block = evaluate_call_node_arguments node, scope
256
+ if block_sym_node
257
+ block_sym = block_sym_node.value
258
+ call_block_proc = ->(block_args, _self_type) do
259
+ block_receiver, *rest = block_args
260
+ block_receiver ? simulate_call(block_receiver || KatakataIrb::Types::OBJECT, block_sym, rest, nil, nil, scope) : KatakataIrb::Types::OBJECT
261
+ end
262
+ elsif has_block
263
+ call_block_proc = ->(_block_args, _self_type) { KatakataIrb::Types::OBJECT }
264
+ end
265
+ method = node.write_name.to_s.delete_suffix('=')
266
+ left = simulate_call receiver_type, method, args_types, kwargs_types, call_block_proc, scope
267
+ if node.operator == '&&='
268
+ right = scope.conditional { evaluate node.value, _1 }
269
+ KatakataIrb::Types::UnionType[right, KatakataIrb::Types::NIL, KatakataIrb::Types::FALSE]
270
+ elsif node.operator == '||='
271
+ right = scope.conditional { evaluate node.value, _1 }
272
+ KatakataIrb::Types::UnionType[left, right]
318
273
  else
319
- btype = simulate_evaluate b, scope
320
- simulate_call atype, op, [btype], nil, nil
321
- end
322
- in [:unary, op, receiver]
323
- simulate_call simulate_evaluate(receiver, scope), op, [], nil, nil
324
- in [:lambda, params, statements]
325
- params in [:paren, params] # ->{}, -> do end
326
- statements in [:bodystmt, statements, _unknown, _unknown, _unknown] # -> do end
327
- params in [:paren, params]
328
- params_table = extract_param_names(params).to_h { [_1, KatakataIrb::Types::NIL] }
329
- block_scope = KatakataIrb::Scope.new scope, { **params_table, KatakataIrb::Scope::BREAK_RESULT => nil, KatakataIrb::Scope::NEXT_RESULT => nil, KatakataIrb::Scope::RETURN_RESULT => nil }
274
+ right = evaluate node.value, scope
275
+ simulate_call left, node.operator, [right], nil, nil, scope, name_match: false
276
+ end
277
+ when Prism::ClassVariableOperatorWriteNode, Prism::InstanceVariableOperatorWriteNode, Prism::LocalVariableOperatorWriteNode, Prism::GlobalVariableOperatorWriteNode
278
+ left = scope[node.name.to_s] || KatakataIrb::Types::OBJECT
279
+ right = evaluate node.value, scope
280
+ scope[node.name.to_s] = simulate_call left, node.operator, [right], nil, nil, scope, name_match: false
281
+ when Prism::ClassVariableAndWriteNode, Prism::InstanceVariableAndWriteNode, Prism::LocalVariableAndWriteNode, Prism::GlobalVariableAndWriteNode
282
+ right = scope.conditional { evaluate node.value, scope }
283
+ scope[node.name.to_s] = KatakataIrb::Types::UnionType[right, KatakataIrb::Types::NIL, KatakataIrb::Types::FALSE]
284
+ when Prism::ClassVariableOrWriteNode, Prism::InstanceVariableOrWriteNode, Prism::LocalVariableOrWriteNode, Prism::GlobalVariableOrWriteNode
285
+ left = scope[node.name.to_s] || KatakataIrb::Types::OBJECT
286
+ right = scope.conditional { evaluate node.value, scope }
287
+ scope[node.name.to_s] = KatakataIrb::Types::UnionType[left, right]
288
+ when Prism::ConstantOperatorWriteNode
289
+ left = scope[node.name.to_s] || KatakataIrb::Types::OBJECT
290
+ right = evaluate node.value, scope
291
+ scope[node.name.to_s] = simulate_call left, node.operator, [right], nil, nil, scope, name_match: false
292
+ when Prism::ConstantAndWriteNode
293
+ right = scope.conditional { evaluate node.value, scope }
294
+ scope[node.name.to_s] = KatakataIrb::Types::UnionType[right, KatakataIrb::Types::NIL, KatakataIrb::Types::FALSE]
295
+ when Prism::ConstantOrWriteNode
296
+ left = scope[node.name.to_s] || KatakataIrb::Types::OBJECT
297
+ right = scope.conditional { evaluate node.value, scope }
298
+ scope[node.name.to_s] = KatakataIrb::Types::UnionType[left, right]
299
+ when Prism::ConstantPathOperatorWriteNode
300
+ left, receiver, _parent_module, name = evaluate_constant_node node.target, scope
301
+ right = evaluate node.value, scope
302
+ value = simulate_call left, node.operator, [right], nil, nil, scope, name_match: false
303
+ const_path_write receiver, name, value, scope
304
+ value
305
+ when Prism::ConstantPathAndWriteNode
306
+ _left, receiver, _parent_module, name = evaluate_constant_node node.target, scope
307
+ right = scope.conditional { evaluate node.value, scope }
308
+ value = KatakataIrb::Types::UnionType[right, KatakataIrb::Types::NIL, KatakataIrb::Types::FALSE]
309
+ const_path_write receiver, name, value, scope
310
+ value
311
+ when Prism::ConstantPathOrWriteNode
312
+ left, receiver, _parent_module, name = evaluate_constant_node node.target, scope
313
+ right = scope.conditional { evaluate node.value, scope }
314
+ value = KatakataIrb::Types::UnionType[left, right]
315
+ const_path_write receiver, name, value, scope
316
+ value
317
+ when Prism::ConstantPathWriteNode
318
+ receiver = evaluate node.target.parent, scope if node.target.parent
319
+ value = evaluate node.value, scope
320
+ const_path_write receiver, node.target.child.name.to_s, value, scope
321
+ value
322
+ when Prism::LambdaNode
323
+ local_table = node.locals.to_h { [_1.to_s, KatakataIrb::Types::OBJECT] }
324
+ block_scope = KatakataIrb::Scope.new scope, { **local_table, KatakataIrb::Scope::BREAK_RESULT => nil, KatakataIrb::Scope::NEXT_RESULT => nil, KatakataIrb::Scope::RETURN_RESULT => nil }
330
325
  block_scope.conditional do |s|
331
- evaluate_assign_params params, [], s
332
- s.conditional { evaluate_param_defaults params, _1 }
333
- statements.each { simulate_evaluate _1, s }
326
+ assign_parameters node.parameters.parameters, s, [], {} if node.parameters&.parameters
327
+ evaluate node.body, s if node.body
334
328
  end
335
329
  block_scope.merge_jumps
336
330
  scope.update block_scope
337
331
  KatakataIrb::Types::ProcType.new
338
- in [:assign, [:var_field, [:@gvar | :@ivar | :@cvar | :@ident | :@const, name,]], value]
339
- res = simulate_evaluate value, scope
340
- scope[name] = res
341
- res
342
- in [:assign, [:aref_field, receiver, key], value]
343
- simulate_evaluate receiver, scope
344
- args, kwargs, _block = retrieve_method_args key
345
- args.each do |arg|
346
- item = arg.is_a?(KatakataIrb::Types::Splat) ? arg.item : arg
347
- simulate_evaluate item, scope
348
- end
349
- kwargs_type kwargs, scope
350
- simulate_evaluate value, scope
351
- in [:assign, [:field, receiver, period, [:@ident,]], value]
352
- simulate_evaluate receiver, scope
353
- if period in [:@op, '&.',]
354
- scope.conditional { simulate_evaluate value, scope }
355
- else
356
- simulate_evaluate value, scope
357
- end
358
- in [:opassign, target, [:@op, op,], value]
359
- op = op.to_s.delete('=').to_sym
360
- if target in [:var_field, *field]
361
- receiver = [:var_ref, *field]
362
- elsif target in [:field, *field]
363
- receiver = [:call, *field]
364
- elsif target in [:aref_field, *field]
365
- receiver = [:aref, *field]
366
- else
367
- receiver = target
368
- end
369
- simulate_evaluate [:assign, target, [:binary, receiver, op, value]], scope
370
- in [:assign, target, value]
371
- simulate_evaluate target, scope
372
- simulate_evaluate value, scope
373
- in [:massign, targets, value]
374
- targets in [:mlhs, *targets] # (a,b) = value
375
- rhs = simulate_evaluate value, scope
376
- evaluate_massign targets, rhs, scope
377
- rhs
378
- in [:mrhs_new_from_args | :mrhs_add_star,]
379
- values, = evaluate_mrhs sexp, scope
380
- KatakataIrb::Types::InstanceType.new Array, Elem: KatakataIrb::Types::UnionType[*values]
381
- in [:ifop, cond, tval, fval]
382
- simulate_evaluate cond, scope
332
+ when Prism::LocalVariableWriteNode, Prism::GlobalVariableWriteNode, Prism::InstanceVariableWriteNode, Prism::ClassVariableWriteNode, Prism::ConstantWriteNode
333
+ scope[node.name.to_s] = evaluate node.value, scope
334
+ when Prism::MultiWriteNode
335
+ evaluated_receivers = {}
336
+ evaluate_multi_write_receiver node, scope, evaluated_receivers
337
+ value = (
338
+ if node.value.is_a? Prism::ArrayNode
339
+ if node.value.elements.any?(Prism::SplatNode)
340
+ evaluate node.value, scope
341
+ else
342
+ node.value.elements.map do |n|
343
+ evaluate n, scope
344
+ end
345
+ end
346
+ elsif node.value
347
+ evaluate node.value, scope
348
+ else
349
+ # For syntax invalid code like `(*a).b`
350
+ KatakataIrb::Types::NIL
351
+ end
352
+ )
353
+ evaluate_multi_write node, value, scope, evaluated_receivers
354
+ when Prism::IfNode, Prism::UnlessNode
355
+ evaluate node.predicate, scope
383
356
  KatakataIrb::Types::UnionType[*scope.run_branches(
384
- -> { simulate_evaluate tval, _1 },
385
- -> { simulate_evaluate fval, _1 }
357
+ -> { node.statements ? evaluate(node.statements, _1) : KatakataIrb::Types::NIL },
358
+ -> { node.consequent ? evaluate(node.consequent, _1) : KatakataIrb::Types::NIL }
386
359
  )]
387
- in [:if_mod | :unless_mod, cond, statement]
388
- simulate_evaluate cond, scope
389
- KatakataIrb::Types::UnionType[scope.conditional { simulate_evaluate statement, _1 }, KatakataIrb::Types::NIL]
390
- in [:if | :unless | :elsif, cond, statements, else_statement]
391
- simulate_evaluate cond, scope
392
- results = scope.run_branches(
393
- ->(s) { statements.map { simulate_evaluate _1, s }.last },
394
- ->(s) { else_statement ? simulate_evaluate(else_statement, s) : KatakataIrb::Types::NIL }
395
- )
396
- results.empty? ? KatakataIrb::Types::NIL : KatakataIrb::Types::UnionType[*results]
397
- in [:while | :until, cond, statements]
398
- inner_scope = KatakataIrb::Scope.new scope, { KatakataIrb::Scope::BREAK_RESULT => nil }, passthrough: true
399
- simulate_evaluate cond, inner_scope
400
- inner_scope.conditional { |s| statements.each { simulate_evaluate _1, s } }
401
- inner_scope.merge_jumps
402
- scope.update inner_scope
403
- breaks = inner_scope[KatakataIrb::Scope::BREAK_RESULT]
404
- breaks ? KatakataIrb::Types::UnionType[breaks, KatakataIrb::Types::NIL] : KatakataIrb::Types::NIL
405
- in [:while_mod | :until_mod, cond, statement]
406
- inner_scope = KatakataIrb::Scope.new scope, { KatakataIrb::Scope::BREAK_RESULT => nil }, passthrough: true
407
- simulate_evaluate cond, inner_scope
408
- inner_scope.conditional { |s| simulate_evaluate statement, s }
360
+ when Prism::ElseNode
361
+ node.statements ? evaluate(node.statements, scope) : KatakataIrb::Types::NIL
362
+ when Prism::WhileNode, Prism::UntilNode
363
+ inner_scope = KatakataIrb::Scope.new scope, { KatakataIrb::Scope::BREAK_RESULT => nil }
364
+ evaluate node.predicate, inner_scope
365
+ if node.statements
366
+ inner_scope.conditional do |s|
367
+ evaluate node.statements, s
368
+ end
369
+ end
409
370
  inner_scope.merge_jumps
410
371
  scope.update inner_scope
411
372
  breaks = inner_scope[KatakataIrb::Scope::BREAK_RESULT]
412
373
  breaks ? KatakataIrb::Types::UnionType[breaks, KatakataIrb::Types::NIL] : KatakataIrb::Types::NIL
413
- in [:break | :next | :return => jump_type, value]
414
- internal_key = jump_type == :break ? KatakataIrb::Scope::BREAK_RESULT : jump_type == :next ? KatakataIrb::Scope::NEXT_RESULT : KatakataIrb::Scope::RETURN_RESULT
415
- if value.empty?
416
- jump_value = KatakataIrb::Types::NIL
417
- else
418
- values, kw = evaluate_mrhs value, scope
419
- values << kw if kw
420
- jump_value = values.size == 1 ? values.first : KatakataIrb::Types::InstanceType.new(Array, Elem: KatakataIrb::Types::UnionType[*values])
421
- end
374
+ when Prism::BreakNode, Prism::NextNode, Prism::ReturnNode
375
+ internal_key = (
376
+ case node
377
+ when Prism::BreakNode
378
+ KatakataIrb::Scope::BREAK_RESULT
379
+ when Prism::NextNode
380
+ KatakataIrb::Scope::NEXT_RESULT
381
+ when Prism::ReturnNode
382
+ KatakataIrb::Scope::RETURN_RESULT
383
+ end
384
+ )
385
+ jump_value = (
386
+ arguments = node.arguments&.arguments
387
+ if arguments.nil? || arguments.empty?
388
+ KatakataIrb::Types::NIL
389
+ elsif arguments.size == 1 && !arguments.first.is_a?(Prism::SplatNode)
390
+ evaluate arguments.first, scope
391
+ else
392
+ KatakataIrb::Types.array_of evaluate_list_splat_items(arguments, scope)
393
+ end
394
+ )
422
395
  scope.terminate_with internal_key, jump_value
423
396
  KatakataIrb::Types::NIL
424
- in [:return0]
425
- scope.terminate_with KatakataIrb::Scope::RETURN_RESULT, KatakataIrb::Types::NIL
426
- KatakataIrb::Types::NIL
427
- in [:yield, args]
428
- evaluate_mrhs args, scope
429
- KatakataIrb::Types::OBJECT
430
- in [:yield0]
397
+ when Prism::YieldNode
398
+ evaluate_list_splat_items node.arguments.arguments, scope if node.arguments
431
399
  KatakataIrb::Types::OBJECT
432
- in [:redo | :retry]
400
+ when Prism::RedoNode, Prism::RetryNode
433
401
  scope.terminate
434
- in [:zsuper]
402
+ when Prism::ForwardingSuperNode
435
403
  KatakataIrb::Types::OBJECT
436
- in [:super, args]
437
- args, kwargs, _block = retrieve_method_args args
438
- args.each do |arg|
439
- item = arg.is_a?(KatakataIrb::Types::Splat) ? arg.item : arg
440
- simulate_evaluate item, scope
441
- end
442
- kwargs_type kwargs, scope
404
+ when Prism::SuperNode
405
+ evaluate_list_splat_items node.arguments.arguments, scope if node.arguments
443
406
  KatakataIrb::Types::OBJECT
444
- in [:begin, body_stmt]
445
- simulate_evaluate body_stmt, scope
446
- in [:bodystmt, statements, rescue_stmt, _unknown, ensure_stmt]
447
- statements = [statements] if statements in [Symbol,] # oneliner-def body
448
- rescue_scope = KatakataIrb::Scope.new scope, { KatakataIrb::Scope::RAISE_BREAK => nil }, passthrough: true if rescue_stmt
449
- return_type = statements.map { simulate_evaluate _1, rescue_scope || scope }.last
450
- if rescue_stmt
451
- rescue_scope.merge_jumps
452
- scope.update rescue_scope
453
- return_type = KatakataIrb::Types::UnionType[return_type, scope.conditional { simulate_evaluate rescue_stmt, _1 }]
454
- end
455
- simulate_evaluate ensure_stmt, scope if ensure_stmt
407
+ when Prism::BeginNode
408
+ return_type = node.statements ? evaluate(node.statements, scope) : KatakataIrb::Types::NIL
409
+ if node.rescue_clause
410
+ if node.else_clause
411
+ return_types = scope.run_branches(
412
+ ->{ evaluate node.rescue_clause, _1 },
413
+ ->{ evaluate node.else_clause, _1 }
414
+ )
415
+ else
416
+ return_types = [
417
+ return_type,
418
+ scope.conditional { evaluate node.rescue_clause, _1 }
419
+ ]
420
+ end
421
+ return_type = KatakataIrb::Types::UnionType[*return_types]
422
+ end
423
+ if node.ensure_clause&.statements
424
+ # ensure_clause is Prism::EnsureNode
425
+ evaluate node.ensure_clause.statements, scope
426
+ end
456
427
  return_type
457
- in [:rescue, error_class_stmts, error_var_stmt, statements, rescue_stmt]
458
- return_type = scope.conditional do |s|
459
- if error_var_stmt in [:var_field, [:@ident, error_var,]]
460
- if (error_class_stmts in [:mrhs_new_from_args, Array => stmts, stmt])
461
- error_class_stmts = [*stmts, stmt]
428
+ when Prism::RescueNode
429
+ run_rescue = lambda do |s|
430
+ if node.reference
431
+ error_classes_type = evaluate_list_splat_items node.exceptions, s
432
+ error_types = error_classes_type.types.filter_map do
433
+ KatakataIrb::Types::InstanceType.new _1.module_or_class if _1.is_a?(KatakataIrb::Types::SingletonType)
462
434
  end
463
- error_classes = (error_class_stmts || []).flat_map { simulate_evaluate _1, s }.uniq
464
- error_types = error_classes.filter_map { KatakataIrb::Types::InstanceType.new _1.module_or_class if _1.is_a?(KatakataIrb::Types::SingletonType) }
465
435
  error_types << KatakataIrb::Types::InstanceType.new(StandardError) if error_types.empty?
466
- s[error_var] = KatakataIrb::Types::UnionType[*error_types]
436
+ error_type = KatakataIrb::Types::UnionType[*error_types]
437
+ case node.reference
438
+ when Prism::LocalVariableTargetNode, Prism::InstanceVariableTargetNode, Prism::ClassVariableTargetNode, Prism::GlobalVariableTargetNode, Prism::ConstantTargetNode
439
+ s[node.reference.name.to_s] = error_type
440
+ when Prism::CallNode
441
+ evaluate node.reference, s
442
+ end
467
443
  end
468
- statements.map { simulate_evaluate _1, s }.last
444
+ node.statements ? evaluate(node.statements, s) : KatakataIrb::Types::NIL
469
445
  end
470
- if rescue_stmt
471
- return_type = KatakataIrb::Types::UnionType[return_type, scope.conditional { simulate_evaluate rescue_stmt, _1 }]
446
+ if node.consequent # begin; rescue A; rescue B; end
447
+ types = scope.run_branches(
448
+ run_rescue,
449
+ -> { evaluate node.consequent, _1 }
450
+ )
451
+ KatakataIrb::Types::UnionType[*types]
452
+ else
453
+ run_rescue.call scope
472
454
  end
473
- return_type
474
- in [:rescue_mod, statement1, statement2]
475
- rescue_scope = KatakataIrb::Scope.new scope, { KatakataIrb::Scope::RAISE_BREAK => nil }, passthrough: true
476
- a = simulate_evaluate statement1, rescue_scope
477
- rescue_scope.merge_jumps
478
- scope.update rescue_scope
479
- b = scope.conditional { simulate_evaluate statement2, _1 }
455
+ when Prism::RescueModifierNode
456
+ a = evaluate node.expression, scope
457
+ b = scope.conditional { evaluate node.rescue_expression, _1 }
480
458
  KatakataIrb::Types::UnionType[a, b]
481
- in [:module, module_stmt, body_stmt]
482
- module_types = simulate_evaluate(module_stmt, scope).types.grep(KatakataIrb::Types::SingletonType)
483
- module_types << KatakataIrb::Types::MODULE if module_types.empty?
484
- module_scope = KatakataIrb::Scope.new(scope, { KatakataIrb::Scope::SELF => KatakataIrb::Types::UnionType[*module_types], KatakataIrb::Scope::BREAK_RESULT => nil, KatakataIrb::Scope::NEXT_RESULT => nil, KatakataIrb::Scope::RETURN_RESULT => nil }, trace_cvar: false, trace_ivar: false, trace_lvar: false)
485
- result = simulate_evaluate body_stmt, module_scope
486
- scope.update module_scope
487
- result
488
- in [:sclass, klass_stmt, body_stmt]
489
- klass_types = simulate_evaluate(klass_stmt, scope).types.filter_map do |type|
459
+ when Prism::SingletonClassNode
460
+ klass_types = evaluate(node.expression, scope).types.filter_map do |type|
490
461
  KatakataIrb::Types::SingletonType.new type.klass if type.is_a? KatakataIrb::Types::InstanceType
491
462
  end
492
463
  klass_types = [KatakataIrb::Types::CLASS] if klass_types.empty?
493
- sclass_scope = KatakataIrb::Scope.new(scope, { KatakataIrb::Scope::SELF => KatakataIrb::Types::UnionType[*klass_types], KatakataIrb::Scope::BREAK_RESULT => nil, KatakataIrb::Scope::NEXT_RESULT => nil, KatakataIrb::Scope::RETURN_RESULT => nil }, trace_cvar: false, trace_ivar: false, trace_lvar: false)
494
- result = simulate_evaluate body_stmt, sclass_scope
464
+ table = node.locals.to_h { [_1.to_s, KatakataIrb::Types::NIL] }
465
+ sclass_scope = KatakataIrb::Scope.new(
466
+ scope,
467
+ { **table, KatakataIrb::Scope::BREAK_RESULT => nil, KatakataIrb::Scope::NEXT_RESULT => nil, KatakataIrb::Scope::RETURN_RESULT => nil },
468
+ trace_ivar: false,
469
+ trace_lvar: false,
470
+ self_type: KatakataIrb::Types::UnionType[*klass_types]
471
+ )
472
+ result = node.body ? evaluate(node.body, sclass_scope) : KatakataIrb::Types::NIL
495
473
  scope.update sclass_scope
496
474
  result
497
- in [:class, klass_stmt, superclass_stmt, body_stmt]
498
- klass_types = simulate_evaluate(klass_stmt, scope).types
499
- klass_types += simulate_evaluate(superclass_stmt, scope).types if superclass_stmt
500
- klass_types = klass_types.select do |type|
501
- type.is_a?(KatakataIrb::Types::SingletonType) && type.module_or_class.is_a?(Class)
502
- end
503
- klass_types << KatakataIrb::Types::CLASS if klass_types.empty?
504
- klass_scope = KatakataIrb::Scope.new(scope, { KatakataIrb::Scope::SELF => KatakataIrb::Types::UnionType[*klass_types], KatakataIrb::Scope::BREAK_RESULT => nil, KatakataIrb::Scope::NEXT_RESULT => nil, KatakataIrb::Scope::RETURN_RESULT => nil }, trace_cvar: false, trace_ivar: false, trace_lvar: false)
505
- result = simulate_evaluate body_stmt, klass_scope
506
- scope.update klass_scope
475
+ when Prism::ModuleNode, Prism::ClassNode
476
+ unless node.constant_path.is_a?(Prism::ConstantReadNode) || node.constant_path.is_a?(Prism::ConstantPathNode)
477
+ # Syntax error code, example: `module a.b; end`
478
+ return KatakataIrb::Types::NIL
479
+ end
480
+ const_type, _receiver, parent_module, name = evaluate_constant_node node.constant_path, scope
481
+ if node.is_a? Prism::ModuleNode
482
+ module_types = const_type.types.select { _1.is_a?(KatakataIrb::Types::SingletonType) && !_1.module_or_class.is_a?(Class) }
483
+ module_types << KatakataIrb::Types::MODULE if module_types.empty?
484
+ else
485
+ select_class_type = -> { _1.is_a?(KatakataIrb::Types::SingletonType) && _1.module_or_class.is_a?(Class) }
486
+ module_types = const_type.types.select(&select_class_type)
487
+ module_types += evaluate(node.superclass, scope).types.select(&select_class_type) if node.superclass
488
+ module_types << KatakataIrb::Types::CLASS if module_types.empty?
489
+ end
490
+ return KatakataIrb::Types::NIL unless node.body
491
+
492
+ table = node.locals.to_h { [_1.to_s, KatakataIrb::Types::NIL] }
493
+ if parent_module.is_a?(Module) || parent_module.nil?
494
+ value = parent_module.const_get name if parent_module&.const_defined?(name)
495
+ unless value
496
+ value_type = scope[name]
497
+ value = value_type.module_or_class if value_type.is_a? KatakataIrb::Types::SingletonType
498
+ end
499
+
500
+ if value.is_a? Module
501
+ nesting = [value, []]
502
+ else
503
+ if parent_module
504
+ nesting = [parent_module, [name]]
505
+ else
506
+ parent_nesting, parent_path = scope.module_nesting.first
507
+ nesting = [parent_nesting, parent_path + [name]]
508
+ end
509
+ nesting_key = [nesting[0].__id__, nesting[1]].join('::')
510
+ nesting_value = node.is_a?(Prism::ModuleNode) ? KatakataIrb::Types::MODULE : KatakataIrb::Types::CLASS
511
+ end
512
+ else
513
+ # parent_module == :unknown
514
+ # TODO: dummy module
515
+ end
516
+ module_scope = KatakataIrb::Scope.new(
517
+ scope,
518
+ { **table, KatakataIrb::Scope::BREAK_RESULT => nil, KatakataIrb::Scope::NEXT_RESULT => nil, KatakataIrb::Scope::RETURN_RESULT => nil },
519
+ trace_cvar: false,
520
+ trace_ivar: false,
521
+ trace_lvar: false,
522
+ self_type: KatakataIrb::Types::UnionType[*module_types],
523
+ nesting: nesting
524
+ )
525
+ module_scope[nesting_key] = nesting_value if nesting_value
526
+ result = evaluate(node.body, module_scope)
527
+ scope.update module_scope
507
528
  result
508
- in [:for, fields, enum, statements]
509
- fields = [fields] if fields in [:var_field | :field | :aref_field,]
510
- params = [:params, fields, nil, nil, nil, nil, nil, nil]
511
- enum = simulate_evaluate enum, scope
512
- extract_param_names(params).each { scope[_1] = KatakataIrb::Types::NIL }
513
- response = simulate_call enum, :first, [], nil, nil
514
- evaluate_assign_params params, [response], scope
515
- inner_scope = KatakataIrb::Scope.new scope, { KatakataIrb::Scope::BREAK_RESULT => nil }, passthrough: true
516
- scope.conditional do |s|
517
- statements.each { simulate_evaluate _1, s }
529
+ when Prism::ForNode
530
+ node.statements
531
+ collection = evaluate node.collection, scope
532
+ inner_scope = KatakataIrb::Scope.new scope, { KatakataIrb::Scope::BREAK_RESULT => nil }
533
+ ary_type = simulate_call collection, :to_ary, [], nil, nil, nil, name_match: false
534
+ element_types = ary_type.types.filter_map do |ary|
535
+ ary.params[:Elem] if ary.is_a?(KatakataIrb::Types::InstanceType) && ary.klass == Array
536
+ end
537
+ element_type = KatakataIrb::Types::UnionType[*element_types]
538
+ inner_scope.conditional do |s|
539
+ evaluate_write node.index, element_type, s, nil
540
+ evaluate node.statements, s if node.statements
518
541
  end
519
542
  inner_scope.merge_jumps
520
543
  scope.update inner_scope
521
544
  breaks = inner_scope[KatakataIrb::Scope::BREAK_RESULT]
522
- breaks ? KatakataIrb::Types::UnionType[breaks, enum] : enum
523
- in [:when, pattern, if_statements, else_statement]
524
- eval_pattern = lambda do |s, pattern, *rest|
525
- simulate_evaluate pattern, s
526
- scope.conditional { eval_pattern.call(_1, *rest) } if rest.any?
527
- end
528
- if_branch = lambda do |s|
529
- eval_pattern.call(s, *pattern)
530
- if_statements.map { simulate_evaluate _1, s }.last
531
- end
532
- else_branch = lambda do |s|
533
- pattern.each { simulate_evaluate _1, s }
534
- simulate_evaluate(else_statement, s, case_target: case_target)
535
- end
536
- if if_statements && else_statement
537
- KatakataIrb::Types::UnionType[*scope.run_branches(if_branch, else_branch)]
538
- else
539
- KatakataIrb::Types::UnionType[scope.conditional { (if_branch || else_branch).call _1 }, KatakataIrb::Types::NIL]
540
- end
541
- in [:in, [:var_field, [:@ident, name,]], if_statements, else_statement]
542
- scope.never { simulate_evaluate else_statement, scope } if else_statement
543
- scope[name] = case_target || KatakataIrb::Types::OBJECT
544
- if_statements ? if_statements.map { simulate_evaluate _1, scope }.last : KatakataIrb::Types::NIL
545
- in [:in, pattern, if_statements, else_statement]
546
- pattern_scope = KatakataIrb::Scope.new(scope, { KatakataIrb::Scope::PATTERNMATCH_BREAK => nil }, passthrough: true)
547
- results = pattern_scope.run_branches(
548
- ->(s) {
549
- match_pattern case_target, pattern, s
550
- if_statements ? if_statements.map { simulate_evaluate _1, s }.last : KatakataIrb::Types::NIL
551
- },
552
- ->(s) {
553
- else_statement ? simulate_evaluate(else_statement, s, case_target: case_target) : KatakataIrb::Types::NIL
554
- }
555
- )
556
- pattern_scope.merge_jumps
557
- scope.update pattern_scope
558
- KatakataIrb::Types::UnionType[*results]
559
- in [:case, target_exp, match_exp]
560
- target = target_exp ? simulate_evaluate(target_exp, scope) : KatakataIrb::Types::NIL
561
- simulate_evaluate match_exp, scope, case_target: target
562
- in [:void_stmt]
563
- KatakataIrb::Types::NIL
564
- in [:dot2 | :dot3, range_beg, range_end]
565
- beg_type = simulate_evaluate range_beg, scope if range_beg
566
- end_type = simulate_evaluate range_end, scope if range_end
545
+ breaks ? KatakataIrb::Types::UnionType[breaks, collection] : collection
546
+ when Prism::CaseNode
547
+ target = evaluate(node.predicate, scope) if node.predicate
548
+ # TODO
549
+ branches = node.conditions.map do |condition|
550
+ ->(s) { evaluate_case_match target, condition, s }
551
+ end
552
+ if node.consequent
553
+ branches << ->(s) { evaluate node.consequent, s }
554
+ elsif node.conditions.any? { _1.is_a? Prism::WhenNode }
555
+ branches << ->(s) { KatakataIrb::Types::NIL }
556
+ end
557
+ KatakataIrb::Types::UnionType[*scope.run_branches(*branches)]
558
+ when Prism::MatchRequiredNode
559
+ value_type = evaluate node.value, scope
560
+ evaluate_match_pattern value_type, node.pattern, scope
561
+ KatakataIrb::Types::NIL # void value
562
+ when Prism::MatchPredicateNode
563
+ value_type = evaluate node.value, scope
564
+ scope.conditional { evaluate_match_pattern value_type, node.pattern, _1 }
565
+ KatakataIrb::Types::BOOLEAN
566
+ when Prism::RangeNode
567
+ beg_type = evaluate node.left, scope if node.left
568
+ end_type = evaluate node.right, scope if node.right
567
569
  elem = (KatakataIrb::Types::UnionType[*[beg_type, end_type].compact]).nonnillable
568
- KatakataIrb::Types::InstanceType.new Range, { Elem: elem }
569
- in [:top_const_ref, [:@const, name,]]
570
- KatakataIrb::BaseScope.type_of { Object.const_get name }
571
- in [:string_concat, a, b]
572
- simulate_evaluate a, scope
573
- simulate_evaluate b, scope
574
- KatakataIrb::Types::STRING
575
- in [:defined, expression]
576
- scope.conditional { simulate_evaluate expression, _1 }
570
+ KatakataIrb::Types::InstanceType.new Range, Elem: elem
571
+ when Prism::DefinedNode
572
+ scope.conditional { evaluate node.value, _1 }
577
573
  KatakataIrb::Types::UnionType[KatakataIrb::Types::STRING, KatakataIrb::Types::NIL]
574
+ when Prism::FlipFlopNode
575
+ scope.conditional { evaluate node.left, _1 } if node.left
576
+ scope.conditional { evaluate node.right, _1 } if node.right
577
+ KatakataIrb::Types::BOOLEAN
578
+ when Prism::MultiTargetNode
579
+ # Raw MultiTargetNode, incomplete code like `a,b`, `*a`.
580
+ evaluate_multi_write_receiver node, scope, nil
581
+ KatakataIrb::Types::NIL
582
+ when Prism::ImplicitNode
583
+ evaluate node.value, scope
584
+ when Prism::MatchWriteNode
585
+ # /(?<a>)(?<b>)/ =~ string
586
+ evaluate node.call, scope
587
+ node.locals.each { scope[_1.to_s] = KatakataIrb::Types::UnionType[KatakataIrb::Types::STRING, KatakataIrb::Types::NIL] }
588
+ KatakataIrb::Types::BOOLEAN
589
+ when Prism::MatchLastLineNode
590
+ KatakataIrb::Types::BOOLEAN
591
+ when Prism::InterpolatedMatchLastLineNode
592
+ node.parts.each { evaluate _1, scope }
593
+ KatakataIrb::Types::BOOLEAN
594
+ when Prism::PreExecutionNode, Prism::PostExecutionNode
595
+ node.statements ? evaluate(node.statements, scope) : KatakataIrb::Types::NIL
596
+ when Prism::AliasMethodNode, Prism::AliasGlobalVariableNode, Prism::UndefNode, Prism::MissingNode
597
+ # do nothing
598
+ KatakataIrb::Types::NIL
578
599
  else
579
600
  KatakataIrb.log_puts
580
601
  KatakataIrb.log_puts :NOMATCH
581
- KatakataIrb.log_puts sexp.inspect
602
+ KatakataIrb.log_puts node.inspect
582
603
  KatakataIrb::Types::NIL
583
604
  end
584
605
  end
585
606
 
586
- def match_pattern(target, pattern, scope)
587
- breakable = -> { scope.terminate_with KatakataIrb::Scope::PATTERNMATCH_BREAK, KatakataIrb::Types::NIL }
588
- types = target.types
589
- case pattern
590
- in [:var_field, [:@ident, name,]]
591
- scope[name] = target
592
- in [:var_ref,] # in Array, in ^a, in nil
593
- in [:@int | :@float | :@rational | :@imaginary | :@CHAR | :symbol_literal | :string_literal | :regexp_literal,]
594
- in [:begin, statement] # in (statement)
595
- simulate_evaluate statement, scope
596
- breakable.call
597
- in [:binary, lpattern, :|, rpattern]
598
- match_pattern target, lpattern, scope
599
- scope.conditional { match_pattern target, rpattern, _1 }
600
- breakable.call
601
- in [:binary, lpattern, :'=>', [:var_field, [:@ident, name,]] => rpattern]
602
- if lpattern in [:var_ref, [:@const, _const_name,]]
603
- const_value = simulate_evaluate lpattern, scope
604
- if const_value.is_a?(KatakataIrb::Types::SingletonType) && const_value.module_or_class.is_a?(Class)
605
- scope[name] = KatakataIrb::Types::InstanceType.new const_value.module_or_class
606
- else
607
- scope[name] = KatakataIrb::Types::OBJECT
608
- end
609
- breakable.call
607
+ def evaluate_call_node_arguments(call_node, scope)
608
+ # call_node.arguments is Prism::ArgumentsNode
609
+ arguments = call_node.arguments&.arguments&.dup || []
610
+ block_arg = call_node.block.expression if call_node.respond_to?(:block) && call_node.block.is_a?(Prism::BlockArgumentNode)
611
+ kwargs = arguments.pop.elements if arguments.last.is_a?(Prism::KeywordHashNode)
612
+ args_types = arguments.map do |arg|
613
+ case arg
614
+ when Prism::ForwardingArgumentsNode
615
+ # `f(a, ...)` treat like splat
616
+ nil
617
+ when Prism::SplatNode
618
+ evaluate arg.expression, scope
619
+ nil # TODO: splat
610
620
  else
611
- match_pattern target, lpattern, scope
612
- match_pattern target, rpattern, scope
613
- end
614
- in [:aryptn, _unknown, items, splat, post_items]
615
- # TODO: deconstruct keys
616
- array_types = types.select { _1.is_a?(KatakataIrb::Types::InstanceType) && _1.klass == Array }
617
- elem = KatakataIrb::Types::UnionType[*array_types.filter_map { _1.params[:Elem] }]
618
- items&.each do |item|
619
- match_pattern elem, item, scope
620
- end
621
- if splat in [:var_field, [:@ident, name,]]
622
- scope[name] = KatakataIrb::Types::InstanceType.new Array, Elem: elem
623
- breakable.call
624
- end
625
- post_items&.each do |item|
626
- match_pattern elem, item, scope
627
- end
628
- in [:hshptn, _unknown, items, splat]
629
- # TODO: deconstruct keys
630
- hash_types = types.select { _1.is_a?(KatakataIrb::Types::InstanceType) && _1.klass == Hash }
631
- key_type = KatakataIrb::Types::UnionType[*hash_types.filter_map { _1.params[:K] }]
632
- value_type = KatakataIrb::Types::UnionType[*hash_types.filter_map { _1.params[:V] }]
633
- items&.each do |key_pattern, value_pattern|
634
- if (key_pattern in [:@label, label,]) && !value_pattern
635
- name = label.delete ':'
636
- scope[name] = value_type
637
- breakable.call
621
+ evaluate arg, scope
622
+ end
623
+ end
624
+ if kwargs
625
+ kwargs_types = kwargs.map do |arg|
626
+ case arg
627
+ when Prism::AssocNode
628
+ if arg.key.is_a?(Prism::SymbolNode)
629
+ [arg.key.value, evaluate(arg.value, scope)]
630
+ else
631
+ evaluate arg.key, scope
632
+ evaluate arg.value, scope
633
+ nil
634
+ end
635
+ when Prism::AssocSplatNode
636
+ evaluate arg.value, scope
637
+ nil
638
+ end
639
+ end.compact.to_h
640
+ end
641
+ if block_arg.is_a? Prism::SymbolNode
642
+ block_sym_node = block_arg
643
+ elsif block_arg
644
+ evaluate block_arg, scope
645
+ end
646
+ [args_types, kwargs_types, block_sym_node, !!block_arg]
647
+ end
648
+
649
+ def const_path_write(receiver, name, value, scope)
650
+ if receiver # receiver::A = value
651
+ singleton_type = receiver.types.find { _1.is_a? KatakataIrb::Types::SingletonType }
652
+ scope.set_const singleton_type.module_or_class, name, value if singleton_type
653
+ else # ::A = value
654
+ scope.set_const Object, name, value
655
+ end
656
+ end
657
+
658
+ def assign_required_parameter(node, value, scope)
659
+ case node
660
+ when Prism::RequiredParameterNode
661
+ scope[node.name.to_s] = value || KatakataIrb::Types::OBJECT
662
+ when Prism::RequiredDestructuredParameterNode
663
+ values = value ? sized_splat(value, :to_ary, node.parameters.size) : []
664
+ node.parameters.zip values do |n, v|
665
+ assign_required_parameter n, v, scope
666
+ end
667
+ when Prism::SplatNode
668
+ splat_value = value ? KatakataIrb::Types.array_of(value) : KatakataIrb::Types::ARRAY
669
+ assign_required_parameter node.expression, splat_value, scope
670
+ end
671
+ end
672
+
673
+ def evaluate_constant_node(node, scope)
674
+ case node
675
+ when Prism::ConstantPathNode
676
+ name = node.child.name.to_s
677
+ if node.parent
678
+ receiver = evaluate node.parent, scope
679
+ if receiver.is_a? KatakataIrb::Types::SingletonType
680
+ parent_module = receiver.module_or_class
638
681
  end
639
- match_pattern value_type, value_pattern, scope if value_pattern
640
- end
641
- if splat in [:var_field, [:@ident, name,]]
642
- scope[name] = KatakataIrb::Types::InstanceType.new Hash, K: key_type, V: value_type
643
- breakable.call
644
- end
645
- in [:if_mod, cond, ifpattern]
646
- match_pattern target, ifpattern, scope
647
- simulate_evaluate cond, scope
648
- breakable.call
649
- in [:dyna_symbol,]
650
- in [:const_path_ref,]
682
+ else
683
+ parent_module = Object
684
+ end
685
+ if parent_module
686
+ type = scope.get_const(parent_module, [name]) || KatakataIrb::Types::NIL
687
+ else
688
+ parent_module = :unknown
689
+ type = KatakataIrb::Types::NIL
690
+ end
691
+ when Prism::ConstantReadNode
692
+ name = node.name.to_s
693
+ type = scope[name]
694
+ end
695
+ [type, receiver, parent_module, name]
696
+ end
697
+
698
+
699
+ def assign_parameters(node, scope, args, kwargs)
700
+ args = args.dup
701
+ kwargs = kwargs.dup
702
+ size = node.requireds.size + node.optionals.size + (node.rest ? 1 : 0) + node.posts.size
703
+ args = sized_splat(args.first, :to_ary, size) if size >= 2 && args.size == 1
704
+ reqs = args.shift node.requireds.size
705
+ if node.rest
706
+ # node.rest.class is Prism::RestParameterNode
707
+ posts = []
708
+ opts = args.shift node.optionals.size
709
+ rest = args
651
710
  else
652
- KatakataIrb.log_puts "Unimplemented match pattern: #{pattern}"
711
+ posts = args.pop node.posts.size
712
+ opts = args
713
+ rest = []
714
+ end
715
+ node.requireds.zip reqs do |n, v|
716
+ assign_required_parameter n, v, scope
717
+ end
718
+ node.optionals.zip opts do |n, v|
719
+ # n is Prism::OptionalParameterNode
720
+ values = [v]
721
+ values << evaluate(n.value, scope) if n.value
722
+ scope[n.name.to_s] = KatakataIrb::Types::UnionType[*values.compact]
723
+ end
724
+ node.posts.zip posts do |n, v|
725
+ assign_required_parameter n, v, scope
726
+ end
727
+ if node.rest&.name
728
+ # node.rest is Prism::RestParameterNode
729
+ scope[node.rest.name.to_s] = KatakataIrb::Types.array_of(*rest)
730
+ end
731
+ node.keywords.each do |n|
732
+ # n is Prism::KeywordParameterNode
733
+ name = n.name.to_s.delete(':')
734
+ values = [kwargs.delete(name)]
735
+ values << evaluate(n.value, scope) if n.value
736
+ scope[name] = KatakataIrb::Types::UnionType[*values.compact]
737
+ end
738
+ # node.keyword_rest is Prism::KeywordRestParameterNode or Prism::ForwardingParameterNode or Prism::NoKeywordsParameterNode
739
+ if node.keyword_rest.is_a?(Prism::KeywordRestParameterNode) && node.keyword_rest.name
740
+ scope[node.keyword_rest.name.to_s] = KatakataIrb::Types::InstanceType.new(Hash, K: KatakataIrb::Types::SYMBOL, V: KatakataIrb::Types::UnionType[*kwargs.values])
741
+ end
742
+ if node.block&.name
743
+ # node.block is Prism::BlockParameterNode
744
+ scope[node.block.name.to_s] = KatakataIrb::Types::PROC
653
745
  end
654
746
  end
655
747
 
656
- def evaluate_mrhs(sexp, scope)
657
- args, kwargs, = retrieve_method_args sexp
658
- values = args.filter_map do |t|
659
- if t.is_a? KatakataIrb::Types::Splat
660
- simulate_evaluate t.item, scope
661
- # TODO
662
- nil
748
+ def assign_numbered_parameters(numbered_parameters, scope, args, _kwargs)
749
+ return if numbered_parameters.empty?
750
+ max_num = numbered_parameters.map { _1[1].to_i }.max
751
+ if max_num == 1
752
+ if args.size == 0
753
+ scope['_1'] = KatakataIrb::Types::NIL
754
+ elsif args.size == 1
755
+ scope['_1'] = args.first
663
756
  else
664
- simulate_evaluate t, scope
757
+ scope['_1'] = KatakataIrb::Types.array_of(*args)
665
758
  end
759
+ else
760
+ args = sized_splat(args.first, :to_ary, max_num) if args.size == 1
761
+ numbered_parameters.each do |name|
762
+ index = name[1].to_i - 1
763
+ scope[name] = args[index] || KatakataIrb::Types::NIL
764
+ end
765
+ end
766
+ end
767
+
768
+ def evaluate_case_match(target, node, scope)
769
+ case node
770
+ when Prism::WhenNode
771
+ node.conditions.each { evaluate _1, scope }
772
+ node.statements ? evaluate(node.statements, scope) : KatakataIrb::Types::NIL
773
+ when Prism::InNode
774
+ pattern = node.pattern
775
+ if pattern in Prism::IfNode | Prism::UnlessNode
776
+ cond_node = pattern.predicate
777
+ pattern = pattern.statements.body.first
778
+ end
779
+ evaluate_match_pattern(target, pattern, scope)
780
+ evaluate cond_node, scope if cond_node # TODO: conditional branch
781
+ node.statements ? evaluate(node.statements, scope) : KatakataIrb::Types::NIL
666
782
  end
667
- unless kwargs.empty?
668
- kvs = kwargs.map do |t|
669
- case t
670
- in KatakataIrb::Types::Splat
671
- simulate_evaluate t.item, scope
672
- # TODO
673
- [KatakataIrb::Types::SYMBOL, KatakataIrb::Types::OBJECT]
674
- in [key, value]
675
- key_type = (key in [:@label,]) ? KatakataIrb::Types::SYMBOL : simulate_evaluate(key, scope)
676
- [key_type, simulate_evaluate(value, scope)]
783
+ end
784
+
785
+ def evaluate_match_pattern(value, pattern, scope)
786
+ # TODO: scope.terminate_with KatakataIrb::Scope::PATTERNMATCH_BREAK, KatakataIrb::Types::NIL
787
+ case pattern
788
+ when Prism::FindPatternNode
789
+ # TODO
790
+ evaluate_match_pattern KatakataIrb::Types::OBJECT, pattern.left, scope
791
+ pattern.requireds.each { evaluate_match_pattern KatakataIrb::Types::OBJECT, _1, scope }
792
+ evaluate_match_pattern KatakataIrb::Types::OBJECT, pattern.right, scope
793
+ when Prism::ArrayPatternNode
794
+ # TODO
795
+ pattern.requireds.each { evaluate_match_pattern KatakataIrb::Types::OBJECT, _1, scope }
796
+ evaluate_match_pattern KatakataIrb::Types::OBJECT, pattern.rest, scope if pattern.rest
797
+ pattern.posts.each { evaluate_match_pattern KatakataIrb::Types::OBJECT, _1, scope }
798
+ KatakataIrb::Types::ARRAY
799
+ when Prism::HashPatternNode
800
+ # TODO
801
+ pattern.assocs.each { evaluate_match_pattern KatakataIrb::Types::OBJECT, _1, scope }
802
+ KatakataIrb::Types::HASH
803
+ when Prism::AssocNode
804
+ evaluate_match_pattern value, pattern.value, scope if pattern.value
805
+ KatakataIrb::Types::OBJECT
806
+ when Prism::AssocSplatNode
807
+ # TODO
808
+ evaluate_match_pattern KatakataIrb::Types::HASH, pattern.value, scope
809
+ KatakataIrb::Types::OBJECT
810
+ when Prism::PinnedVariableNode
811
+ evaluate pattern.variable, scope
812
+ when Prism::PinnedExpressionNode
813
+ evaluate pattern.expression, scope
814
+ when Prism::LocalVariableTargetNode
815
+ scope[pattern.name.to_s] = value
816
+ when Prism::AlternationPatternNode
817
+ KatakataIrb::Types::UnionType[evaluate_match_pattern(value, pattern.left, scope), evaluate_match_pattern(value, pattern.right, scope)]
818
+ when Prism::CapturePatternNode
819
+ capture_type = class_or_value_to_instance evaluate_match_pattern(value, pattern.value, scope)
820
+ value = capture_type unless capture_type.types.empty? || capture_type.types == [KatakataIrb::Types::OBJECT]
821
+ evaluate_match_pattern value, pattern.target, scope
822
+ when Prism::SplatNode
823
+ value = KatakataIrb::Types.array_of value
824
+ evaluate_match_pattern value, pattern.expression, scope if pattern.expression
825
+ value
826
+ else
827
+ # literal node
828
+ type = evaluate(pattern, scope)
829
+ class_or_value_to_instance(type)
830
+ end
831
+ end
832
+
833
+ def class_or_value_to_instance(type)
834
+ instance_types = type.types.map do |t|
835
+ t.is_a?(KatakataIrb::Types::SingletonType) ? KatakataIrb::Types::InstanceType.new(t.module_or_class) : t
836
+ end
837
+ KatakataIrb::Types::UnionType[*instance_types]
838
+ end
839
+
840
+ def evaluate_write(node, value, scope, evaluated_receivers)
841
+ case node
842
+ when Prism::MultiTargetNode
843
+ evaluate_multi_write node, value, scope, evaluated_receivers
844
+ when Prism::CallNode
845
+ evaluated_receivers&.[](node.receiver) || evaluate(node.receiver, scope) if node.receiver
846
+ when Prism::SplatNode
847
+ evaluate_write node.expression, KatakataIrb::Types.array_of(value), scope, evaluated_receivers
848
+ when Prism::LocalVariableTargetNode, Prism::GlobalVariableTargetNode, Prism::InstanceVariableTargetNode, Prism::ClassVariableTargetNode, Prism::ConstantTargetNode
849
+ scope[node.name.to_s] = value
850
+ when Prism::ConstantPathTargetNode
851
+ receiver = evaluated_receivers&.[](node.parent) || evaluate(node.parent, scope) if node.parent
852
+ const_path_write receiver, node.child.name.to_s, value, scope
853
+ value
854
+ end
855
+ end
856
+
857
+ def evaluate_multi_write(node, values, scope, evaluated_receivers)
858
+ values = sized_splat values, :to_ary, node.targets.size unless values.is_a? Array
859
+ splat_index = node.targets.find_index { _1.is_a? Prism::SplatNode }
860
+ if splat_index
861
+ pre_targets = node.targets[0...splat_index]
862
+ splat_target = node.targets[splat_index]
863
+ post_targets = node.targets[splat_index + 1..]
864
+ pre_values = values.shift pre_targets.size
865
+ post_values = values.pop post_targets.size
866
+ splat_value = KatakataIrb::Types::UnionType[*values]
867
+ zips = pre_targets.zip(pre_values) + [[splat_target, splat_value]] + post_targets.zip(post_values)
868
+ else
869
+ zips = node.targets.zip(values)
870
+ end
871
+ zips.each do |target, value|
872
+ evaluate_write target, value || KatakataIrb::Types::NIL, scope, evaluated_receivers
873
+ end
874
+ end
875
+
876
+ def evaluate_multi_write_receiver(node, scope, evaluated_receivers)
877
+ case node
878
+ when Prism::MultiWriteNode, Prism::MultiTargetNode
879
+ node.targets.each { evaluate_multi_write_receiver _1, scope, evaluated_receivers }
880
+ when Prism::CallNode
881
+ if node.receiver
882
+ receiver = evaluate(node.receiver, scope)
883
+ evaluated_receivers[node.receiver] = receiver if evaluated_receivers
884
+ end
885
+ if node.arguments
886
+ node.arguments.arguments&.each do |arg|
887
+ if arg.is_a? Prism::SplatNode
888
+ evaluate arg.expression, scope
889
+ else
890
+ evaluate arg, scope
891
+ end
677
892
  end
678
893
  end
679
- key_type = KatakataIrb::Types::UnionType[*kvs.map(&:first)]
680
- value_type = KatakataIrb::Types::UnionType[*kvs.map(&:last)]
681
- kw = KatakataIrb::Types::InstanceType.new(Hash, K: key_type, V: value_type)
894
+ when Prism::SplatNode
895
+ evaluate_multi_write_receiver node.expression, scope, evaluated_receivers if node.expression
682
896
  end
683
- [values, kw]
897
+ end
898
+
899
+ def evaluate_list_splat_items(list, scope)
900
+ items = list.flat_map do |node|
901
+ if node.is_a? Prism::SplatNode
902
+ splat = evaluate node.expression, scope
903
+ array_elem, non_array = partition_to_array splat.nonnillable, :to_a
904
+ [*array_elem, *non_array]
905
+ else
906
+ evaluate node, scope
907
+ end
908
+ end.uniq
909
+ KatakataIrb::Types::UnionType[*items]
684
910
  end
685
911
 
686
912
  def sized_splat(value, method, size)
@@ -693,7 +919,7 @@ class KatakataIrb::TypeSimulator
693
919
  def partition_to_array(value, method)
694
920
  arrays, non_arrays = value.types.partition { _1.is_a?(KatakataIrb::Types::InstanceType) && _1.klass == Array }
695
921
  non_arrays.select! do |type|
696
- to_array_result = simulate_call type, method, [], nil, nil, name_match: false
922
+ to_array_result = simulate_call type, method, [], nil, nil, nil, name_match: false
697
923
  if to_array_result.is_a?(KatakataIrb::Types::InstanceType) && to_array_result.klass == Array
698
924
  arrays << to_array_result
699
925
  false
@@ -706,147 +932,7 @@ class KatakataIrb::TypeSimulator
706
932
  [array_elem, non_array]
707
933
  end
708
934
 
709
- def evaluate_massign(sexp, values, scope)
710
- values = sized_splat values, :to_ary, sexp.size unless values.is_a? Array
711
- rest_index = sexp.find_index { _1 in [:rest_param, ]}
712
- if rest_index
713
- pre = rest_index ? sexp[0...rest_index] : sexp
714
- post = rest_index ? sexp[rest_index + 1..] : []
715
- sexp[rest_index] in [:rest_param, rest_field]
716
- rest_values = values[pre.size...values.size - post.size] || []
717
- rest_type = KatakataIrb::Types::InstanceType.new Array, Elem: KatakataIrb::Types::UnionType[*rest_values]
718
- pairs = pre.zip(values.first(pre.size)) + [[rest_field, rest_type]] + post.zip(values.last(post.size))
719
- else
720
- pairs = sexp.zip values
721
- end
722
- pairs.each do |field, value|
723
- case field
724
- in [:@ident, name,]
725
- # block arg mlhs
726
- scope[name] = value || KatakataIrb::Types::OBJECT
727
- in [:var_field, [:@gvar | :@ivar | :@cvar | :@ident | :@const, name,]]
728
- # massign
729
- scope[name] = value || KatakataIrb::Types::OBJECT
730
- in [:mlhs, *mlhs]
731
- evaluate_massign mlhs, value || [], scope
732
- in [:field, receiver,]
733
- # (a=x).b, c = value
734
- simulate_evaluate receiver, scope
735
- in [:aref_field, *field]
736
- # (a=x)[i=y, j=z], b = value
737
- simulate_evaluate [:aref, *field], scope
738
- in nil
739
- # a, *, b = value
740
- end
741
- end
742
- end
743
-
744
- def kwargs_type(kwargs, scope)
745
- return if kwargs.empty?
746
- keys = []
747
- values = []
748
- kwargs.each do |kv|
749
- if kv.is_a? KatakataIrb::Types::Splat
750
- hash = simulate_evaluate kv.item, scope
751
- unless hash.is_a?(KatakataIrb::Types::InstanceType) && hash.klass == Hash
752
- hash = simulate_call hash, :to_hash, [], nil, nil
753
- end
754
- if hash.is_a?(KatakataIrb::Types::InstanceType) && hash.klass == Hash
755
- keys << hash.params[:K] if hash.params[:K]
756
- values << hash.params[:V] if hash.params[:V]
757
- end
758
- else
759
- key, value = kv
760
- keys << ((key in [:@label,]) ? KatakataIrb::Types::SYMBOL : simulate_evaluate(key, scope))
761
- values << simulate_evaluate(value, scope)
762
- end
763
- end
764
- KatakataIrb::Types::InstanceType.new(Hash, K: KatakataIrb::Types::UnionType[*keys], V: KatakataIrb::Types::UnionType[*values])
765
- end
766
-
767
- def retrieve_method_call(sexp)
768
- optional = -> { _1 in [:@op, '&.',] }
769
- case sexp
770
- in [:fcall | :vcall, [:@ident | :@const | :@kw | :@op, method,]] # hoge
771
- [nil, method, [], [], nil, false]
772
- in [:call, receiver, [:@period,] | [:@op, '&.',] | [:@op, '::',] | :'::' => dot, :call]
773
- [receiver, :call, [], [], nil, optional[dot]]
774
- in [:call, receiver, [:@period,] | [:@op, '&.',] | [:@op, '::',] | :'::' => dot, method]
775
- method => [:@ident | :@const | :@kw | :@op, method,] unless method == :call
776
- [receiver, method, [], [], nil, optional[dot]]
777
- in [:command, [:@ident | :@const | :@kw | :@op, method,], args] # hoge 1, 2
778
- args, kwargs, block = retrieve_method_args args
779
- [nil, method, args, kwargs, block, false]
780
- in [:command_call, receiver, [:@period,] | [:@op, '&.',] | :'::' => dot, [:@ident | :@const | :@kw | :@op, method,], args] # a.hoge 1; a.hoge 1, 2;
781
- args, kwargs, block = retrieve_method_args args
782
- [receiver, method, args, kwargs, block, optional[dot]]
783
- in [:method_add_arg, call, args]
784
- receiver, method, _arg, _kwarg, _block, opt = retrieve_method_call call
785
- args, kwargs, block = retrieve_method_args args
786
- [receiver, method, args, kwargs, block, opt]
787
- in [:method_add_block, call, block]
788
- receiver, method, args, kwargs, opt = retrieve_method_call call
789
- [receiver, method, args, kwargs, block, opt]
790
- end
791
- end
792
-
793
- def retrieve_method_args(sexp)
794
- case sexp
795
- in [:mrhs_add_star, args, star]
796
- args, = retrieve_method_args args
797
- [[*args, KatakataIrb::Types::Splat.new(star)], [], nil]
798
- in [:mrhs_new_from_args, [:args_add_star,] => args]
799
- args, = retrieve_method_args args
800
- [args, [], nil]
801
- in [:mrhs_new_from_args, [:args_add_star,] => args, last_arg]
802
- args, = retrieve_method_args args
803
- [[*args, last_arg], [], nil]
804
- in [:mrhs_new_from_args, args, last_arg]
805
- [[*args, last_arg], [], nil]
806
- in [:mrhs_new_from_args, args]
807
- [args, [], nil]
808
- in [:args_add_block, [:args_add_star,] => args, block_arg]
809
- args, kwargs, = retrieve_method_args args
810
- block_arg = [:void_stmt] if block_arg.nil? # method(*splat, &)
811
- [args, kwargs, block_arg]
812
- in [:args_add_block, [*args, [:bare_assoc_hash,] => kw], block_arg]
813
- block_arg = [:void_stmt] if block_arg.nil? # method(**splat, &)
814
- _, kwargs = retrieve_method_args kw
815
- [args, kwargs, block_arg]
816
- in [:args_add_block, [*args], block_arg]
817
- block_arg = [:void_stmt] if block_arg.nil? # method(arg, &)
818
- [args, [], block_arg]
819
- in [:bare_assoc_hash, kws]
820
- kwargs = []
821
- kws.each do |kw|
822
- if kw in [:assoc_splat, value,]
823
- kwargs << KatakataIrb::Types::Splat.new(value) if value
824
- elsif kw in [:assoc_new, [:@label, label,] => key, nil]
825
- name = label.delete ':'
826
- kwargs << [key, [:__var_ref_or_call, [name =~ /\A[A-Z]/ ? :@const : :@ident, name, [0, 0]]]]
827
- elsif kw in [:assoc_new, key, value]
828
- kwargs << [key, value]
829
- end
830
- end
831
- [[], kwargs, nil]
832
- in [:args_add_star, *args, [:bare_assoc_hash,] => kwargs]
833
- args, = retrieve_method_args [:args_add_star, *args]
834
- _, kwargs = retrieve_method_args kwargs
835
- [args, kwargs, nil]
836
- in [:args_add_star, pre_args, star_arg, *post_args]
837
- pre_args, = retrieve_method_args pre_args if pre_args in [:args_add_star,]
838
- args = star_arg ? [*pre_args, KatakataIrb::Types::Splat.new(star_arg), *post_args] : pre_args + post_args
839
- [args, [], nil]
840
- in [:arg_paren, args]
841
- args ? retrieve_method_args(args) : [[], [], nil]
842
- in [[:command | :command_call, ] => command_arg] # method(a b, c), method(a.b c, d)
843
- [[command_arg], [], nil]
844
- else
845
- [[], [], nil]
846
- end
847
- end
848
-
849
- def simulate_call(receiver, method_name, args, kwargs, block, name_match: true)
935
+ def simulate_call(receiver, method_name, args, kwargs, block, scope, name_match: true)
850
936
  methods = KatakataIrb::Types.rbs_methods receiver, method_name.to_sym, args, kwargs, !!block
851
937
  block_called = false
852
938
  type_breaks = methods.map do |method, given_params, method_params|
@@ -862,10 +948,15 @@ class KatakataIrb::TypeSimulator
862
948
  block_called = true
863
949
  vars.merge! KatakataIrb::Types.match_free_variables(free_vars - vars.keys.to_set, [method.block.type.return_type], [block_response])
864
950
  end
865
- [KatakataIrb::Types.from_rbs_type(method.type.return_type, receiver, vars || {}), breaks]
951
+ if KatakataIrb::Types.method_return_bottom?(method)
952
+ [nil, breaks]
953
+ else
954
+ [KatakataIrb::Types.from_rbs_type(method.type.return_type, receiver, vars || {}), breaks]
955
+ end
866
956
  end
867
957
  block&.call [], nil unless block_called
868
- types = type_breaks.map(&:first)
958
+ terminates = !type_breaks.empty? && type_breaks.map(&:first).all?(&:nil?)
959
+ types = type_breaks.map(&:first).compact
869
960
  breaks = type_breaks.map(&:last).compact
870
961
  types << OBJECT_METHODS[method_name.to_sym] if name_match && OBJECT_METHODS.has_key?(method_name.to_sym)
871
962
 
@@ -876,120 +967,24 @@ class KatakataIrb::TypeSimulator
876
967
  end
877
968
  end
878
969
  end
970
+ scope&.terminate if terminates && breaks.empty?
879
971
  KatakataIrb::Types::UnionType[*types, *breaks]
880
972
  end
881
973
 
882
- def extract_param_names(params)
883
- params => [:params, pre_required, optional, rest, post_required, keywords, keyrest, block]
884
- names = []
885
- extract_mlhs = ->(item) do
886
- case item
887
- in [:var_field, [:@ident, name,],]
888
- names << name
889
- in [:@ident, name,]
890
- names << name
891
- in [:mlhs, *items]
892
- items.each(&extract_mlhs)
893
- in [:rest_param, item]
894
- extract_mlhs.call item if item
895
- in [:field | :aref_field,]
896
- # a.b, c[i] = value
897
- in [:excessed_comma]
898
- in [:args_forward]
899
- end
900
- end
901
- [*pre_required, *post_required].each(&extract_mlhs)
902
- extract_mlhs.call rest if rest
903
- optional&.each do |key, _value|
904
- key => [:@ident, name,]
905
- names << name
906
- end
907
- keywords&.each do |key, _value|
908
- key => [:@label, label,]
909
- names << label.delete(':')
910
- end
911
- if keyrest in [:kwrest_params, [:@ident, name,]]
912
- names << name
913
- end
914
- if block in [:blockarg, [:@ident, name,]]
915
- names << name
916
- end
917
- names
918
- end
919
-
920
- def evaluate_assign_params(params, values, scope)
921
- values = values.dup
922
- params => [:params, pre_required, optional, rest, post_required, _keywords, keyrest, block]
923
- size = (pre_required&.size || 0) + (optional&.size || 0) + (post_required&.size || 0) + (rest ? 1 : 0)
924
- values = sized_splat values.first, :to_ary, size if values.size == 1 && size >= 2
925
- pre_values = values.shift pre_required.size if pre_required
926
- post_values = values.pop post_required.size if post_required
927
- opt_values = values.shift optional.size if optional
928
- rest_values = values
929
- evaluate_massign pre_required, pre_values, scope if pre_required
930
- evaluate_massign optional.map(&:first), opt_values, scope if optional
931
- if rest in [:rest_param, [:@ident, name,]]
932
- scope[name] = KatakataIrb::Types::InstanceType.new Array, Elem: KatakataIrb::Types::UnionType[*rest_values]
933
- end
934
- evaluate_massign post_required, post_values, scope if post_required
935
- # TODO: assign keywords
936
- if keyrest in [:kwrest_param, [:@ident, name,]]
937
- scope[name] = KatakataIrb::Types::InstanceType.new Hash, K: KatakataIrb::Types::SYMBOL, V: KatakataIrb::Types::OBJECT
938
- end
939
- if block in [:blockarg, [:@ident, name,]]
940
- scope[name] = KatakataIrb::Types::PROC
941
- end
942
- end
943
-
944
- def evaluate_param_defaults(params, scope)
945
- params => [:params, _pre_required, optional, rest, _post_required, keywords, keyrest, block]
946
- optional&.each do |item, value|
947
- item => [:@ident, name,]
948
- scope[name] = simulate_evaluate value, scope
949
- end
950
- if rest in [:rest_param, [:@ident, name,]]
951
- scope[name] = KatakataIrb::Types::ARRAY
952
- end
953
- keywords&.each do |key, value|
954
- key => [:@label, label,]
955
- name = label.delete ':'
956
- scope[name] = value ? simulate_evaluate(value, scope) : KatakataIrb::Types::OBJECT
957
- end
958
- if keyrest in [:kwrest_param, [:@ident, name,]]
959
- scope[name] = KatakataIrb::Types::HASH
960
- end
961
- if block in [:blockarg, [:@ident, name,]]
962
- scope[name] = KatakataIrb::Types::PROC
963
- end
964
- end
965
-
966
- def max_numbered_params(sexp)
967
- case sexp
968
- in [:do_block | :brace_block | :def | :class | :module,]
969
- 0
970
- in [:var_ref, [:@ident, name,]]
971
- name.match?(/\A_[1-9]\z/) ? name[1..].to_i : 0
972
- else
973
- sexp.filter_map do |s|
974
- max_numbered_params s if s.is_a? Array
975
- end.max || 0
976
- end
977
- end
978
-
979
- def self.calculate_binding_scope(binding, parents, target)
980
- dig_targets = DigTarget.new(parents, target) do |_types, scope|
981
- return scope
974
+ def evaluate_program(program, scope)
975
+ # statements.body[0] is local variable assign code
976
+ program.statements.body[1..].each do |statement|
977
+ evaluate statement, scope
982
978
  end
983
- scope = KatakataIrb::Scope.from_binding(binding)
984
- new(dig_targets).simulate_evaluate parents[0], scope
985
- scope
986
979
  end
987
980
 
988
- def self.calculate_receiver(binding, parents, receiver)
989
- dig_targets = DigTarget.new(parents, receiver) do |type, _scope|
990
- return type
981
+ def self.calculate_target_type_scope(binding, parents, target)
982
+ dig_targets = DigTarget.new(parents, target) do |type, scope|
983
+ return type, scope
991
984
  end
992
- new(dig_targets).simulate_evaluate parents[0], KatakataIrb::Scope.from_binding(binding)
993
- KatakataIrb::Types::NIL
985
+ program = parents.first
986
+ scope = KatakataIrb::Scope.from_binding(binding, program.locals)
987
+ new(dig_targets).evaluate_program program, scope
988
+ [KatakataIrb::Types::NIL, scope]
994
989
  end
995
990
  end