katakata_irb 0.1.11 → 0.2.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.
@@ -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