katakata_irb 0.1.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.
@@ -0,0 +1,1108 @@
1
+ require_relative 'types'
2
+ require 'ripper'
3
+ require 'set'
4
+
5
+ class KatakataIrb::TypeSimulator
6
+ class DigTarget
7
+ def initialize(parents, receiver, &block)
8
+ @dig_ids = parents.to_h { [_1.__id__, true] }
9
+ @target_id = receiver.__id__
10
+ @block = block
11
+ end
12
+
13
+ def dig?(node) = @dig_ids[node.__id__]
14
+ def target?(node) = @target_id == node.__id__
15
+ def resolve(type, scope)
16
+ @block.call type, scope
17
+ end
18
+ end
19
+
20
+ class BaseScope
21
+ def initialize(binding, self_object)
22
+ @binding, @self_object = binding, self_object
23
+ @cache = { SELF => KatakataIrb::Types.type_from_object(self_object) }
24
+ @local_variables = binding.local_variables.map(&:to_s).to_set
25
+ end
26
+
27
+ def mutable?() = false
28
+
29
+ def [](name)
30
+ @cache[name] ||= (
31
+ fallback = KatakataIrb::Types::NIL
32
+ case BaseScope.type_by_name name
33
+ when :cvar
34
+ KatakataIrb::TypeSimulator.type_of(fallback:) { @self_object.class_variable_get name }
35
+ when :ivar
36
+ KatakataIrb::TypeSimulator.type_of(fallback:) { @self_object.instance_variable_get name }
37
+ when :lvar
38
+ KatakataIrb::TypeSimulator.type_of(fallback:) { @binding.local_variable_get(name) }
39
+ when :const
40
+ KatakataIrb::TypeSimulator.type_of(fallback:) { @binding.eval name }
41
+ end
42
+ )
43
+ end
44
+
45
+ def self_type
46
+ self[SELF]
47
+ end
48
+
49
+ def local_variables
50
+ @local_variables.to_a
51
+ end
52
+
53
+ def self.type_by_name(name)
54
+ if name.start_with? '@@'
55
+ :cvar
56
+ elsif name.start_with? '@'
57
+ :ivar
58
+ elsif name.start_with? '$'
59
+ :gvar
60
+ elsif name.start_with? '%'
61
+ :internal
62
+ elsif name[0].downcase != name[0]
63
+ :const
64
+ else
65
+ :lvar
66
+ end
67
+ end
68
+
69
+ def has?(name)
70
+ case BaseScope.type_by_name name
71
+ when :cvar
72
+ @self_object.class_variable_defined? name
73
+ when :ivar
74
+ @self_object.instance_variable_defined? name
75
+ when :lvar
76
+ @local_variables.include? name
77
+ when :const
78
+ @binding.eval("#{name};true") rescue false
79
+ when :internal
80
+ true
81
+ end
82
+ end
83
+ end
84
+
85
+ class Scope
86
+ attr_reader :parent, :jump_branches
87
+
88
+ def self.from_binding(binding) = new BaseScope.new(binding, binding.eval('self'))
89
+
90
+ def initialize(parent, table = {}, trace_cvar: true, trace_ivar: true, trace_lvar: true, passthrough: false)
91
+ @tables = [table]
92
+ @parent = parent
93
+ @trace_cvar = trace_cvar
94
+ @trace_ivar = trace_ivar
95
+ @trace_lvar = trace_lvar
96
+ @passthrough = passthrough
97
+ @terminated = false
98
+ @jump_branches = []
99
+ end
100
+
101
+ def mutable? = true
102
+
103
+ def terminated?
104
+ @terminated
105
+ end
106
+
107
+ def terminate_with(type)
108
+ scopes = ancestors.select(&:mutable?)
109
+ scope = scopes.find { _1.has_own? type } || scopes.last
110
+ index = scopes.index scope
111
+ scope.jump_branches << scopes.drop(index).map(&:branch_table_clone)
112
+ terminate
113
+ end
114
+
115
+ def terminate
116
+ @terminated = true
117
+ end
118
+
119
+ def branch_table_clone() = @tables.last.dup
120
+
121
+ def trace?(name)
122
+ return false unless @parent
123
+ type = BaseScope.type_by_name(name)
124
+ type == :cvar ? @trace_cvar : type == :ivar ? @trace_ivar : type == :lvar ? @trace_lvar : true
125
+ end
126
+
127
+ def [](name)
128
+ @tables.reverse_each do |table|
129
+ return table[name] if table.key? name
130
+ end
131
+ @parent[name] if trace? name
132
+ end
133
+
134
+ def []=(name, type)
135
+ # return if terminated?
136
+ if @passthrough && !BaseScope.type_by_name(name) == :internal
137
+ @parent[name] = type
138
+ elsif trace?(name) && @parent.mutable? && !@tables.any? { _1.key? name } && @parent.has?(name)
139
+ @parent[name] = type
140
+ else
141
+ @tables.last[name] = type
142
+ end
143
+ end
144
+
145
+ def self_type
146
+ self[SELF]
147
+ end
148
+
149
+ def local_variables
150
+ lvar_keys = @tables.flat_map(&:keys).select do |name|
151
+ BaseScope.type_by_name(name) == :lvar
152
+ end
153
+ lvar_keys |= @parent.local_variables if @trace_lvar
154
+ lvar_keys
155
+ end
156
+
157
+ def start_branch
158
+ @tables << {}
159
+ end
160
+
161
+ def end_branch
162
+ @tables.pop
163
+ end
164
+
165
+ def merge_jumps
166
+ if terminated?
167
+ merge @jump_branches
168
+ else
169
+ merge [*@jump_branches, [{}] * ancestors.size]
170
+ end
171
+ end
172
+
173
+ def merge_branch(tables)
174
+ target_table = @tables.last
175
+ keys = tables.flat_map(&:keys).uniq
176
+ keys.each do |key|
177
+ original_value = self[key] || KatakataIrb::Types::NIL
178
+ target_table[key] = KatakataIrb::Types::UnionType[*tables.map { _1[key] || original_value }.uniq]
179
+ end
180
+ end
181
+
182
+ def ancestors
183
+ scopes = [self]
184
+ scopes << scopes.last.parent while scopes.last.parent&.mutable?
185
+ scopes
186
+ end
187
+
188
+ def conditional(&block)
189
+ run_branches(block, -> {}).first || KatakataIrb::Types::NIL
190
+ end
191
+
192
+ def run_branches(*blocks)
193
+ results = blocks.map { branch(&_1) }.reject(&:last)
194
+ merge results.map { _2 }
195
+ if results.empty?
196
+ terminate
197
+ []
198
+ else
199
+ results.map(&:first)
200
+ end
201
+ end
202
+
203
+ def branch
204
+ scopes = ancestors
205
+ scopes.each(&:start_branch)
206
+ @terminated = false
207
+ result = yield
208
+ terminated = @terminated
209
+ @terminated = false
210
+ [result, scopes.map(&:end_branch), terminated]
211
+ end
212
+
213
+ def merge(branches)
214
+ scopes = ancestors
215
+ scopes.zip(*branches).each do |scope, *tables|
216
+ scope.merge_branch(tables)
217
+ end
218
+ end
219
+
220
+ def base_scope
221
+ @parent&.mutable? ? @parent.base_scope : @parent
222
+ end
223
+
224
+ def has_own?(name)
225
+ @tables.any? { _1.key? name }
226
+ end
227
+
228
+ def has?(name)
229
+ has_own?(name) || (trace?(name) && @parent.has?(name))
230
+ end
231
+ end
232
+
233
+ module LexerElemMatcher
234
+ refine Ripper::Lexer::Elem do
235
+ def deconstruct_keys(_keys)
236
+ {
237
+ tok:,
238
+ event:,
239
+ label: state.allbits?(Ripper::EXPR_LABEL),
240
+ beg: state.allbits?(Ripper::EXPR_BEG),
241
+ dot: state.allbits?(Ripper::EXPR_DOT)
242
+ }
243
+ end
244
+ end
245
+ end
246
+ using LexerElemMatcher
247
+
248
+ OBJECT_METHODS = {
249
+ to_s: KatakataIrb::Types::STRING,
250
+ to_str: KatakataIrb::Types::STRING,
251
+ to_a: KatakataIrb::Types::ARRAY,
252
+ to_ary: KatakataIrb::Types::ARRAY,
253
+ to_h: KatakataIrb::Types::HASH,
254
+ to_hash: KatakataIrb::Types::HASH,
255
+ to_i: KatakataIrb::Types::INTEGER,
256
+ to_int: KatakataIrb::Types::INTEGER,
257
+ to_f: KatakataIrb::Types::FLOAT,
258
+ to_c: KatakataIrb::Types::COMPLEX,
259
+ to_r: KatakataIrb::Types::RATIONAL
260
+ }
261
+
262
+ SELF = '%self'
263
+ BREAK_RESULT = '%break'
264
+ NEXT_RESULT = '%next'
265
+ RETURN_RESULT = '%return'
266
+
267
+ def initialize(dig_targets)
268
+ @dig_targets = dig_targets
269
+ end
270
+
271
+ def simulate_evaluate(sexp, scope, case_target: nil)
272
+ result = simulate_evaluate_inner(sexp, scope, case_target:)
273
+ @dig_targets.resolve result, scope if @dig_targets.target?(sexp)
274
+ result
275
+ end
276
+
277
+ def simulate_evaluate_inner(sexp, scope, case_target: nil)
278
+ case sexp
279
+ in [:program, statements]
280
+ statements.map { simulate_evaluate _1, scope }.last
281
+ in [:def | :defs,]
282
+ sexp in [:def, method_name_exp, params, body_stmt]
283
+ sexp in [:defs, receiver_exp, dot_exp, method_name_exp, params, body_stmt]
284
+ if receiver_exp
285
+ receiver_exp in [:paren, receiver_exp]
286
+ self_type = simulate_evaluate receiver_exp, scope
287
+ else
288
+ current_self_types = scope.self_type.types
289
+ self_types = current_self_types.map do |type|
290
+ if (type in KatakataIrb::Types::SingletonType) && type.module_or_class.is_a?(Class)
291
+ KatakataIrb::Types::InstanceType.new type.module_or_class
292
+ else
293
+ type
294
+ end
295
+ end
296
+ self_type = KatakataIrb::Types::UnionType[*self_types]
297
+ end
298
+ if @dig_targets.dig? sexp
299
+ params in [:paren, params]
300
+ params_table = extract_param_names(params).to_h { [_1, KatakataIrb::Types::NIL] }
301
+ method_scope = Scope.new scope, { **params_table, SELF => self_type, BREAK_RESULT => nil, NEXT_RESULT => nil, RETURN_RESULT => nil }, trace_lvar: false
302
+ evaluate_assign_params params, [], method_scope
303
+ method_scope.conditional { evaluate_param_defaults params, method_scope }
304
+ simulate_evaluate body_stmt, method_scope
305
+ end
306
+ KatakataIrb::Types::SYMBOL
307
+ in [:@int,]
308
+ KatakataIrb::Types::INTEGER
309
+ in [:@float,]
310
+ KatakataIrb::Types::FLOAT
311
+ in [:@rational,]
312
+ KatakataIrb::Types::RATIONAL
313
+ in [:@imaginary,]
314
+ KatakataIrb::Types::COMPLEX
315
+ in [:@tstring_content,]
316
+ KatakataIrb::Types::STRING
317
+ in [:symbol_literal,]
318
+ KatakataIrb::Types::SYMBOL
319
+ in [:dyna_symbol, [:string_content, *statements]]
320
+ statements.each { simulate_evaluate _1, scope }
321
+ KatakataIrb::Types::SYMBOL
322
+ in [:@CHAR,]
323
+ KatakataIrb::Types::STRING
324
+ in [:@backref,]
325
+ KatakataIrb::Types::UnionType[KatakataIrb::Types::STRING, KatakataIrb::Types::NIL]
326
+ in [:string_literal, [:string_content, *statements]]
327
+ statements.each { simulate_evaluate _1, scope }
328
+ KatakataIrb::Types::STRING
329
+ in [:xstring_literal, statements]
330
+ statements.each { simulate_evaluate _1, scope }
331
+ KatakataIrb::Types::STRING
332
+ in [:string_embexpr, statements]
333
+ statements.each { simulate_evaluate _1, scope }
334
+ KatakataIrb::Types::STRING
335
+ in [:string_dvar,]
336
+ KatakataIrb::Types::STRING
337
+ in [:regexp_literal, statements, _regexp_end]
338
+ statements.each { simulate_evaluate _1, scope }
339
+ KatakataIrb::Types::REGEXP
340
+ in [:array, [:args_add_star,] => star]
341
+ args, kwargs = retrieve_method_args star
342
+ types = args.flat_map do |elem|
343
+ if elem in KatakataIrb::Types::Splat
344
+ splat = simulate_evaluate elem.item, scope
345
+ array_value = to_array splat, :to_a
346
+ array_value ? (array_value.params[:Elem] || []) : splat
347
+ else
348
+ simulate_evaluate elem, scope
349
+ end
350
+ end
351
+ types << kwargs_type(kwargs, scope) if kwargs && kwargs.any?
352
+ KatakataIrb::Types::InstanceType.new Array, Elem: KatakataIrb::Types::UnionType[*types]
353
+ in [:array, statements]
354
+ if statements.nil? || statements.empty?
355
+ KatakataIrb::Types::ARRAY
356
+ elsif statements.all? { _1 in [Symbol,] }
357
+ # normal array
358
+ elem = statements ? KatakataIrb::Types::UnionType[*statements.map { simulate_evaluate _1, scope }] : KatakataIrb::Types::NIL
359
+ KatakataIrb::Types::InstanceType.new Array, Elem: elem
360
+ else
361
+ # %I[] or %W[]
362
+ statements.each do |sub_statements|
363
+ sub_statements.each { simulate_evaluate _1, scope }
364
+ end
365
+ # TODO: use AST because Ripper.sexp('%I[a]') == Ripper.sexp('%W[a]')
366
+ elem = KatakataIrb::Types::UnionType[KatakataIrb::Types::STRING, KatakataIrb::Types::SYMBOL]
367
+ KatakataIrb::Types::InstanceType.new Array, Elem: elem
368
+ end
369
+ in [:bare_assoc_hash, args]
370
+ simulate_evaluate [:hash, [:assoclist_from_args, args]], scope
371
+ in [:hash, [:assoclist_from_args, args]]
372
+ keys = []
373
+ values = []
374
+ args.each do |arg|
375
+ case arg
376
+ in [:assoc_new, key, value]
377
+ if key in [:@label, label, pos]
378
+ keys << KatakataIrb::Types::SYMBOL
379
+ name = label.delete ':'
380
+ value ||= [:__var_ref_or_call, [name =~ /\A[A-Z]/ ? :@const : :@ident, name, pos]]
381
+ else
382
+ keys << simulate_evaluate(key, scope)
383
+ end
384
+ values << simulate_evaluate(value, scope)
385
+ in [:assoc_splat, value]
386
+ hash = simulate_evaluate value, scope
387
+ unless (hash in KatakataIrb::Types::InstanceType) && hash.klass == Hash
388
+ hash = simulate_call hash, :to_hash, [], nil, nil
389
+ end
390
+ if (hash in KatakataIrb::Types::InstanceType) && hash.klass == Hash
391
+ keys << hash.params[:K] if hash.params[:K]
392
+ values << hash.params[:V] if hash.params[:V]
393
+ end
394
+ end
395
+ end
396
+ KatakataIrb::Types::InstanceType.new Hash, K: KatakataIrb::Types::UnionType[*keys], V: KatakataIrb::Types::UnionType[*values]
397
+ in [:hash, nil]
398
+ KatakataIrb::Types::InstanceType.new Hash
399
+ in [:paren | :ensure | :else, statements]
400
+ statements.map { simulate_evaluate _1, scope }.last
401
+ in [:const_path_ref, receiver, [:@const, name,]]
402
+ r = simulate_evaluate receiver, scope
403
+ (r in KatakataIrb::Types::SingletonType) ? self.class.type_of { r.module_or_class.const_get name } : KatakataIrb::Types::NIL
404
+ in [:__var_ref_or_call, [type, name, pos]]
405
+ sexp = scope.has?(name) ? [:var_ref, [type, name, pos]] : [:vcall, [:@ident, name, pos]]
406
+ simulate_evaluate sexp, scope
407
+ in [:var_ref, [:@kw, name,]]
408
+ case name
409
+ in 'self'
410
+ scope.self_type
411
+ in 'true'
412
+ KatakataIrb::Types::TRUE
413
+ in 'false'
414
+ KatakataIrb::Types::FALSE
415
+ in 'nil'
416
+ KatakataIrb::Types::NIL
417
+ in '__FILE__'
418
+ KatakataIrb::Types::STRING
419
+ in '__LINE__'
420
+ KatakataIrb::Types::INTEGER
421
+ in '__ENCODING__'
422
+ KatakataIrb::Types::InstanceType.new Encoding
423
+ end
424
+ in [:var_ref, [:@const | :@ivar | :@cvar | :@gvar | :@ident, name,]]
425
+ scope[name] || KatakataIrb::Types::NIL
426
+ in [:const_ref, [:@const, name,]]
427
+ scope[name] || KatakataIrb::Types::NIL
428
+ in [:aref, receiver, args]
429
+ receiver_type = simulate_evaluate receiver, scope if receiver
430
+ args, kwargs, _block = retrieve_method_args args
431
+ args_type = args.map do |arg|
432
+ if arg in KatakataIrb::Types::Splat
433
+ simulate_evaluate arg.item, scope
434
+ nil # TODO: splat
435
+ else
436
+ simulate_evaluate arg, scope
437
+ end
438
+ end
439
+ simulate_call receiver_type, :[], args_type, kwargs_type(kwargs, scope), nil
440
+ in [:call | :vcall | :command | :command_call | :method_add_arg | :method_add_block,]
441
+ if (sexp in [:vcall, [:@ident, name,]]) && scope.has?(name)
442
+ # workaround for https://bugs.ruby-lang.org/issues/19175
443
+ return scope[name]
444
+ end
445
+ receiver, method, args, kwargs, block, conditional = retrieve_method_call sexp
446
+ receiver_type = receiver ? simulate_evaluate(receiver, scope) : scope.self_type
447
+ evaluate_method = lambda do
448
+ args_type = args.map do |arg|
449
+ if arg in KatakataIrb::Types::Splat
450
+ simulate_evaluate arg.item, scope
451
+ nil # TODO: splat
452
+ else
453
+ simulate_evaluate arg, scope
454
+ end
455
+ end
456
+
457
+ if block
458
+ if block in [:symbol_literal, [:symbol, [:@ident, block_name,]]]
459
+ call_block_proc = ->(block_args) do
460
+ block_receiver, *rest = block_args
461
+ block_receiver ? simulate_call(block_receiver || KatakataIrb::Types::OBJECT, block_name, rest, nil, nil) : KatakataIrb::Types::OBJECT
462
+ end
463
+ elsif block in [:do_block | :brace_block => type, block_var, body]
464
+ block_var in [:block_var, params,]
465
+ call_block_proc = ->(block_args) do
466
+ result, breaks, nexts = scope.conditional do
467
+ if params
468
+ names = extract_param_names(params)
469
+ else
470
+ names = (1..max_numbered_params(body)).map { "_#{_1}" }
471
+ params = [:params, names.map { [:@ident, _1, [0, 0]] }, nil, nil, nil, nil, nil, nil]
472
+ end
473
+ params_table = names.zip(block_args).to_h { [_1, _2 || KatakataIrb::Types::NIL] }
474
+ block_scope = Scope.new scope, { **params_table, BREAK_RESULT => nil, NEXT_RESULT => nil }
475
+ evaluate_assign_params params, block_args, block_scope
476
+ block_scope.conditional { evaluate_param_defaults params, block_scope } if params
477
+ if type == :do_block
478
+ result = simulate_evaluate body, block_scope
479
+ else
480
+ result = body.map { simulate_evaluate _1, block_scope }.last
481
+ end
482
+ block_scope.merge_jumps
483
+ result = KatakataIrb::Types::NIL if block_scope.terminated?
484
+ [result, block_scope[BREAK_RESULT], block_scope[NEXT_RESULT]]
485
+ end
486
+ [KatakataIrb::Types::UnionType[result, *nexts], breaks]
487
+ end
488
+ else
489
+ simulate_evaluate block, scope
490
+ end
491
+ end
492
+ simulate_call receiver_type, method, args_type, kwargs_type(kwargs, scope), call_block_proc
493
+ end
494
+ if conditional
495
+ scope.conditional { evaluate_method.call }
496
+ else
497
+ evaluate_method.call
498
+ end
499
+ in [:binary, a, Symbol => op, b]
500
+ atype = simulate_evaluate a, scope
501
+ case op
502
+ when :'&&', :and
503
+ btype = scope.conditional { simulate_evaluate b, scope }
504
+ KatakataIrb::Types::UnionType[btype, KatakataIrb::Types::NIL, KatakataIrb::Types::FALSE]
505
+ when :'||', :or
506
+ btype = scope.conditional { simulate_evaluate b, scope }
507
+ KatakataIrb::Types::UnionType[atype, btype]
508
+ else
509
+ btype = simulate_evaluate b, scope
510
+ simulate_call atype, op, [btype], nil, nil
511
+ end
512
+ in [:unary, op, receiver]
513
+ simulate_call simulate_evaluate(receiver, scope), op, [], nil, nil
514
+ in [:lambda, params, statements]
515
+ params in [:paren, params] # ->{}, -> do end
516
+ statements in [:bodystmt, statements, _unknown, _unknown, _unknown] # -> do end
517
+ params in [:paren, params]
518
+ params_table = extract_param_names(params).to_h { [_1, KatakataIrb::Types::NIL] }
519
+ block_scope = Scope.new scope, { **params_table, BREAK_RESULT => nil, NEXT_RESULT => nil, RETURN_RESULT => nil }
520
+ evaluate_assign_params params, [], block_scope
521
+ block_scope.conditional { evaluate_param_defaults params, block_scope }
522
+ statements.each { simulate_evaluate _1, block_scope }
523
+ block_scope.merge_jumps
524
+ KatakataIrb::Types::ProcType.new
525
+ in [:assign, [:var_field, [:@gvar | :@ivar | :@cvar | :@ident | :@const, name,]], value]
526
+ res = simulate_evaluate value, scope
527
+ scope[name] = res
528
+ res
529
+ in [:assign, [:aref_field, receiver, key], value]
530
+ simulate_evaluate receiver, scope
531
+ args, kwargs, _block = retrieve_method_args key
532
+ args.each do |arg|
533
+ item = ((arg in KatakataIrb::Types::Splat) ? arg.item : arg)
534
+ simulate_evaluate item, scope
535
+ end
536
+ kwargs_type kwargs, scope
537
+ simulate_evaluate value, scope
538
+ in [:assign, [:field, receiver, period, [:@ident,]], value]
539
+ simulate_evaluate receiver, scope
540
+ if period in [:@op, '&.',]
541
+ scope.conditional { simulate_evaluate value, scope }
542
+ else
543
+ simulate_evaluate value, scope
544
+ end
545
+ in [:opassign, target, [:@op, op,], value]
546
+ op = op.to_s.delete('=').to_sym
547
+ if target in [:var_field, *field]
548
+ receiver = [:var_ref, *field]
549
+ elsif target in [:field, *field]
550
+ receiver = [:call, *field]
551
+ elsif target in [:aref_field, *field]
552
+ receiver = [:aref, *field]
553
+ else
554
+ receiver = target
555
+ end
556
+ simulate_evaluate [:assign, target, [:binary, receiver, op, value]], scope
557
+ in [:assign, target, value]
558
+ simulate_evaluate target, scope
559
+ simulate_evaluate value, scope
560
+ in [:massign, targets, value]
561
+ rhs = simulate_evaluate value, scope
562
+ evaluate_massign targets, rhs, scope
563
+ rhs
564
+ in [:mrhs_new_from_args | :mrhs_add_star,]
565
+ values, = evaluate_mrhs sexp, scope
566
+ KatakataIrb::Types::InstanceType.new Array, Elem: KatakataIrb::Types::UnionType[*values]
567
+ in [:ifop, cond, tval, fval]
568
+ simulate_evaluate cond, scope
569
+ KatakataIrb::Types::UnionType[*scope.run_branches(
570
+ -> { simulate_evaluate tval, scope },
571
+ -> { simulate_evaluate fval, scope }
572
+ )]
573
+ in [:if_mod | :unless_mod, cond, statement]
574
+ simulate_evaluate cond, scope
575
+ KatakataIrb::Types::UnionType[scope.conditional { simulate_evaluate statement, scope }, KatakataIrb::Types::NIL]
576
+ in [:if | :unless | :elsif, cond, statements, else_statement]
577
+ simulate_evaluate cond, scope
578
+ results = scope.run_branches(
579
+ -> { statements.map { simulate_evaluate _1, scope }.last },
580
+ -> { else_statement ? simulate_evaluate(else_statement, scope) : KatakataIrb::Types::NIL }
581
+ )
582
+ results.empty? ? KatakataIrb::Types::NIL : KatakataIrb::Types::UnionType[*results]
583
+ in [:while | :until, cond, statements]
584
+ inner_scope = Scope.new scope, { BREAK_RESULT => nil }, passthrough: true
585
+ simulate_evaluate cond, inner_scope
586
+ scope.conditional { statements.each { simulate_evaluate _1, inner_scope } }
587
+ breaks = inner_scope[BREAK_RESULT]
588
+ inner_scope.merge_jumps
589
+ breaks ? KatakataIrb::Types::UnionType[breaks, KatakataIrb::Types::NIL] : KatakataIrb::Types::NIL
590
+ in [:while_mod | :until_mod, cond, statement]
591
+ simulate_evaluate cond, scope
592
+ scope.conditional { simulate_evaluate statement, scope }
593
+ KatakataIrb::Types::NIL
594
+ in [:break | :next | :return => jump_type, value]
595
+ internal_key = jump_type == :break ? BREAK_RESULT : jump_type == :next ? NEXT_RESULT : RETURN_RESULT
596
+ if value.empty?
597
+ scope[internal_key] = KatakataIrb::Types::NIL
598
+ else
599
+ values, kw = evaluate_mrhs value, scope
600
+ values << kw if kw
601
+ scope[internal_key] = values.size == 1 ? values.first : KatakataIrb::Types::InstanceType.new(Array, Elem: KatakataIrb::Types::UnionType[*values])
602
+ end
603
+ scope.terminate_with internal_key
604
+ KatakataIrb::Types::NIL
605
+ in [:return0]
606
+ scope[RETURN_RESULT] = KatakataIrb::Types::NIL
607
+ scope.terminate_with RETURN_RESULT
608
+ KatakataIrb::Types::NIL
609
+ in [:yield, args]
610
+ evaluate_mrhs args, scope
611
+ KatakataIrb::Types::OBJECT
612
+ in [:yield0]
613
+ KatakataIrb::Types::OBJECT
614
+ in [:super, args]
615
+ args, kwargs, _block = retrieve_method_args args
616
+ args.each do |arg|
617
+ item = ((arg in KatakataIrb::Types::Splat) ? arg.item : arg)
618
+ simulate_evaluate item, scope
619
+ end
620
+ kwargs_type kwargs, scope
621
+ KatakataIrb::Types::OBJECT
622
+ in [:begin, body_stmt]
623
+ simulate_evaluate body_stmt, scope
624
+ in [:bodystmt, statements, rescue_stmt, _unknown, ensure_stmt]
625
+ statements = [statements] if statements in [Symbol,] # oneliner-def body
626
+ return_type = statements.map { simulate_evaluate _1, scope }.last
627
+ if rescue_stmt
628
+ return_type = KatakataIrb::Types::UnionType[return_type, scope.conditional { simulate_evaluate rescue_stmt, scope }]
629
+ end
630
+ simulate_evaluate ensure_stmt, scope if ensure_stmt
631
+ return_type
632
+ in [:rescue, error_class_stmts, error_var_stmt, statements, rescue_stmt]
633
+ return_type = scope.conditional do
634
+ if error_var_stmt in [:var_field, [:@ident, error_var,]]
635
+ if (error_class_stmts in [:mrhs_new_from_args, Array => stmts, stmt])
636
+ error_class_stmts = [*stmts, stmt]
637
+ end
638
+ error_classes = (error_class_stmts || []).flat_map { simulate_evaluate _1, scope }.uniq
639
+ error_types = error_classes.filter_map { KatakataIrb::Types::InstanceType.new _1.module_or_class if _1 in KatakataIrb::Types::SingletonType }
640
+ error_types << KatakataIrb::Types::InstanceType.new(StandardError) if error_types.empty?
641
+ scope[error_var] = KatakataIrb::Types::UnionType[*error_types]
642
+ end
643
+ statements.map { simulate_evaluate _1, scope }.last
644
+ end
645
+ if rescue_stmt
646
+ return_type = KatakataIrb::Types::UnionType[return_type, scope.conditional { simulate_evaluate rescue_stmt, scope }]
647
+ end
648
+ return_type
649
+ in [:rescue_mod, statement1, statement2]
650
+ a = simulate_evaluate statement1, scope
651
+ b = scope.conditional { simulate_evaluate statement2, scope }
652
+ KatakataIrb::Types::UnionType[a, b]
653
+ in [:module, module_stmt, body_stmt]
654
+ module_types = simulate_evaluate(module_stmt, scope).types.grep(KatakataIrb::Types::SingletonType)
655
+ module_types << KatakataIrb::Types::MODULE if module_types.empty?
656
+ simulate_evaluate body_stmt, Scope.new(scope, { SELF => KatakataIrb::Types::UnionType[*module_types], BREAK_RESULT => nil, NEXT_RESULT => nil, RETURN_RESULT => nil }, trace_cvar: false, trace_ivar: false, trace_lvar: false)
657
+ in [:sclass, klass_stmt, body_stmt]
658
+ klass_types = simulate_evaluate(klass_stmt, scope).types.filter_map do |type|
659
+ KatakataIrb::Types::SingletonType.new type.klass if type in KatakataIrb::Types::InstanceType
660
+ end
661
+ klass_types = [KatakataIrb::Types::CLASS] if klass_types.empty?
662
+ simulate_evaluate body_stmt, Scope.new(scope, { SELF => KatakataIrb::Types::UnionType[*klass_types], BREAK_RESULT => nil, NEXT_RESULT => nil, RETURN_RESULT => nil }, trace_cvar: false, trace_ivar: false, trace_lvar: false)
663
+ in [:class, klass_stmt, superclass_stmt, body_stmt]
664
+ klass_types = simulate_evaluate(klass_stmt, scope).types
665
+ klass_types += simulate_evaluate(superclass_stmt, scope).types if superclass_stmt
666
+ klass_types = klass_types.select do |type|
667
+ (type in KatakataIrb::Types::SingletonType) && type.module_or_class.is_a?(Class)
668
+ end
669
+ klass_types << KatakataIrb::Types::CLASS if klass_types.empty?
670
+ simulate_evaluate body_stmt, Scope.new(scope, { SELF => KatakataIrb::Types::UnionType[*klass_types], BREAK_RESULT => nil, NEXT_RESULT => nil, RETURN_RESULT => nil }, trace_cvar: false, trace_ivar: false, trace_lvar: false)
671
+ in [:for, fields, enum, statements]
672
+ fields = [fields] if fields in [:var_field,]
673
+ params = [:params, fields, nil, nil, nil, nil, nil, nil]
674
+ enum = simulate_evaluate enum, scope
675
+ extract_param_names(params).each { scope[_1] = KatakataIrb::Types::NIL }
676
+ response = simulate_call enum, :first, [], nil, nil
677
+ evaluate_assign_params params, [response], scope
678
+ scope.conditional do
679
+ statements.each { simulate_evaluate _1, scope }
680
+ end
681
+ enum
682
+ in [:in | :when => mode, pattern, if_statements, else_statement]
683
+ if mode == :in
684
+ if_match = -> { match_pattern case_target, pattern, scope }
685
+ else_match = -> { scope.conditional { if_match.call } }
686
+ else
687
+ eval_pattern = lambda do |pattern, *rest|
688
+ simulate_evaluate pattern, scope
689
+ scope.conditional { eval_pattern.call(*rest) } if rest.any?
690
+ end
691
+ if_match = -> { eval_pattern.call(*pattern) }
692
+ else_match = -> { pattern.each { simulate_evaluate _1, scope } }
693
+ end
694
+ if_branch = lambda do
695
+ if_match.call
696
+ if_statements.map { simulate_evaluate _1, scope }.last
697
+ end
698
+ else_branch = lambda do
699
+ else_match.call
700
+ simulate_evaluate(else_statement, scope, case_target:)
701
+ end
702
+ if if_statements && else_statement
703
+ KatakataIrb::Types::UnionType[*scope.run_branches(if_branch, else_branch)]
704
+ elsif if_statements
705
+ KatakataIrb::Types::UnionType[scope.conditional { if_branch.call }, KatakataIrb::Types::NIL]
706
+ elsif else_statement
707
+ KatakataIrb::Types::UnionType[scope.conditional { else_branch.call }, KatakataIrb::Types::NIL]
708
+ else
709
+ KatakataIrb::Types::NIL
710
+ end
711
+ in [:case, target_exp, match_exp]
712
+ target = simulate_evaluate target_exp, scope
713
+ simulate_evaluate match_exp, scope, case_target: target
714
+ in [:void_stmt]
715
+ KatakataIrb::Types::NIL
716
+ in [:dot2 | :dot3, range_beg, range_end]
717
+ simulate_evaluate range_beg, scope if range_beg
718
+ simulate_evaluate range_end, scope if range_end
719
+ KatakataIrb::Types::RANGE
720
+ in [:top_const_ref, [:@const, name,]]
721
+ self.class.type_of { Object.const_get name }
722
+ else
723
+ KatakataIrb.log_puts
724
+ KatakataIrb.log_puts :NOMATCH
725
+ KatakataIrb.log_puts sexp.inspect
726
+ KatakataIrb::Types::NIL
727
+ end
728
+ end
729
+
730
+ def match_pattern(target, pattern, scope)
731
+ types = target.types
732
+ case pattern
733
+ in [:var_field, [:@ident, name,]]
734
+ scope[name] = target
735
+ in [:var_ref,] # in Array, in ^a, in nil
736
+ in [:@int | :@float | :@rational | :@imaginary | :@CHAR | :symbol_literal | :string_literal | :regexp_literal,]
737
+ in [:begin, statement] # in (statement)
738
+ simulate_evaluate statement, scope
739
+ in [:binary, lpattern, :|, rpattern]
740
+ match_pattern target, lpattern, scope
741
+ match_pattern target, rpattern, scope
742
+ in [:binary, lpattern, :'=>', [:var_field, [:@ident, name,]] => rpattern]
743
+ if lpattern in [:var_ref, [:@const, const_name,]]
744
+ const_value = simulate_evaluate lpattern, scope
745
+ if (const_value in KatakataIrb::Types::SingletonType) && const_value.module_or_class.is_a?(Class)
746
+ scope[name] = KatakataIrb::Types::InstanceType.new const_value.module_or_class
747
+ else
748
+ scope[name] = KatakataIrb::Types::OBJECT
749
+ end
750
+ else
751
+ match_pattern target, lpattern, scope
752
+ match_pattern target, rpattern, scope
753
+ end
754
+ in [:aryptn, _unknown, items, splat, post_items]
755
+ # TODO: deconstruct keys
756
+ array_types = types.select { (_1 in KatakataIrb::Types::InstanceType) && _1.klass == Array }
757
+ elem = KatakataIrb::Types::UnionType[*array_types.filter_map { _1.params[:Elem] }]
758
+ items&.each do |item|
759
+ match_pattern elem, item, scope
760
+ end
761
+ if splat in [:var_field, [:@ident, name,]]
762
+ scope[name] = KatakataIrb::Types::InstanceType.new Array, Elem: elem
763
+ end
764
+ post_items&.each do |item|
765
+ match_pattern elem, item, scope
766
+ end
767
+ in [:hshptn, _unknown, items, splat]
768
+ # TODO: deconstruct keys
769
+ hash_types = types.select { (_1 in KatakataIrb::Types::InstanceType) && _1.klass == Hash }
770
+ key_type = KatakataIrb::Types::UnionType[*hash_types.filter_map { _1.params[:K] }]
771
+ value_type = KatakataIrb::Types::UnionType[*hash_types.filter_map { _1.params[:V] }]
772
+ items&.each do |key_pattern, value_pattern|
773
+ if key_pattern in [:@label, label,]
774
+ name = label.delete ':'
775
+ scope[name] = value_type unless value_pattern
776
+ end
777
+ match_pattern value_type, value_pattern, scope if value_pattern
778
+ end
779
+ if splat in [:var_field, [:@ident, name,]]
780
+ scope[name] = KatakataIrb::Types::InstanceType.new Hash, K: key_type, V: value_type
781
+ end
782
+ in [:if_mod, cond, ifpattern]
783
+ simulate_evaluate cond, scope
784
+ match_pattern target, ifpattern, scope
785
+ in [:dyna_symbol,]
786
+ in [:const_path_ref,]
787
+ else
788
+ KatakataIrb.log_puts "Unimplemented match pattern: #{pattern}"
789
+ end
790
+ end
791
+
792
+ def evaluate_mrhs(sexp, scope)
793
+ args, kwargs, = retrieve_method_args sexp
794
+ values = args.filter_map do |t|
795
+ if t in KatakataIrb::Types::Splat
796
+ simulate_evaluate t.item, scope
797
+ # TODO
798
+ nil
799
+ else
800
+ simulate_evaluate t, scope
801
+ end
802
+ end
803
+ unless kwargs.empty?
804
+ kvs = kwargs.map do |t|
805
+ case t
806
+ in KatakataIrb::Types::Splat
807
+ simulate_evaluate t.item, scope
808
+ # TODO
809
+ [KatakataIrb::Types::SYMBOL, KatakataIrb::Types::OBJECT]
810
+ in [key, value]
811
+ key_type = (key in [:@label,]) ? KatakataIrb::Types::SYMBOL : simulate_evaluate(key, scope)
812
+ [key_type, simulate_evaluate(value, scope)]
813
+ end
814
+ end
815
+ key_type = KatakataIrb::Types::UnionType[*kvs.map(&:first)]
816
+ value_type = KatakataIrb::Types::UnionType[*kvs.map(&:last)]
817
+ kw = KatakataIrb::Types::InstanceType.new(Hash, K: key_type, V: value_type)
818
+ end
819
+ [values, kw]
820
+ end
821
+
822
+ def to_array(value, method)
823
+ return value if (value in KatakataIrb::Types::InstanceType) && value.klass == Array
824
+ to_array_result = simulate_call value, method, [], nil, nil, name_match: false
825
+ return to_array_result if (to_array_result in KatakataIrb::Types::InstanceType) && to_array_result.klass == Array
826
+ end
827
+
828
+ def evaluate_massign(sexp, values, scope)
829
+ unless values in Array
830
+ array_value = to_array values, :to_ary
831
+ values = array_value ? [array_value.params[:Elem] || KatakataIrb::Types::OBJECT] * sexp.size : [values]
832
+ end
833
+
834
+ rest_index = sexp.find_index { _1 in [:rest_param, ]}
835
+ if rest_index
836
+ pre = rest_index ? sexp[0...rest_index] : sexp
837
+ post = rest_index ? sexp[rest_index + 1..] : []
838
+ sexp[rest_index] in [:rest_param, rest_field]
839
+ rest_values = values[pre.size...-post.size] || []
840
+ rest_type = KatakataIrb::Types::InstanceType.new Array, Elem: KatakataIrb::Types::UnionType[*rest_values]
841
+ pairs = pre.zip(values.first(pre.size)) + [[rest_field, rest_type]] + post.zip(values.last(post.size))
842
+ else
843
+ pairs = sexp.zip values
844
+ end
845
+ pairs.each do |field, value|
846
+ case field
847
+ in [:@ident, name,]
848
+ # block arg mlhs
849
+ scope[name] = value || KatakataIrb::Types::OBJECT
850
+ in [:var_field, [:@ident | :@ivar | :@cvar | :@gvar, name,]]
851
+ # massign
852
+ scope[name] = value || KatakataIrb::Types::OBJECT
853
+ in [:mlhs, *mlhs]
854
+ evaluate_massign mlhs, value || [], scope
855
+ end
856
+ end
857
+ end
858
+
859
+ def kwargs_type(kwargs, scope)
860
+ return if kwargs.empty?
861
+ keys = []
862
+ values = []
863
+ kwargs.each do |kv|
864
+ if kv in KatakataIrb::Types::Splat
865
+ hash = simulate_evaluate kv.item, scope
866
+ unless (hash in KatakataIrb::Types::InstanceType) && hash.klass == Hash
867
+ hash = simulate_call hash, :to_hash, [], nil, nil
868
+ end
869
+ if (hash in KatakataIrb::Types::InstanceType) && hash.klass == Hash
870
+ keys << hash.params[:K] if hash.params[:K]
871
+ values << hash.params[:V] if hash.params[:V]
872
+ end
873
+ else
874
+ key, value = kv
875
+ keys << ((key in [:@label,]) ? KatakataIrb::Types::SYMBOL : simulate_evaluate(key, scope))
876
+ values << simulate_evaluate(value, scope)
877
+ end
878
+ end
879
+ KatakataIrb::Types::InstanceType.new(Hash, K: KatakataIrb::Types::UnionType[*keys], V: KatakataIrb::Types::UnionType[*values])
880
+ end
881
+
882
+ def self.type_of(fallback: KatakataIrb::Types::OBJECT)
883
+ begin
884
+ KatakataIrb::Types.type_from_object yield
885
+ rescue
886
+ fallback
887
+ end
888
+ end
889
+
890
+ def retrieve_method_call(sexp)
891
+ conditional = -> { _1 in [:@op, '&.',] }
892
+ case sexp
893
+ in [:fcall | :vcall, [:@ident | :@const | :@kw | :@op, method,]] # hoge
894
+ [nil, method, [], [], nil, false]
895
+ in [:call, receiver, [:@period,] | [:@op, '&.',] | :'::' => dot, :call]
896
+ [receiver, :call, [], [], nil, conditional[dot]]
897
+ in [:call, receiver, [:@period,] | [:@op, '&.',] | :'::' => dot, method]
898
+ method => [:@ident | :@const | :@kw | :@op, method,] unless method == :call
899
+ [receiver, method, [], [], nil, conditional[dot]]
900
+ in [:command, [:@ident | :@const | :@kw | :@op, method,], args] # hoge 1, 2
901
+ args, kwargs, block = retrieve_method_args args
902
+ [nil, method, args, kwargs, block, false]
903
+ in [:command_call, receiver, [:@period,] | [:@op, '&.',] | :'::' => dot, [:@ident | :@const | :@kw | :@op, method,], args] # a.hoge 1; a.hoge 1, 2;
904
+ args, kwargs, block = retrieve_method_args args
905
+ [receiver, method, args, kwargs, block, conditional[dot]]
906
+ in [:method_add_arg, call, args]
907
+ receiver, method, _arg, _kwarg, _block, cond = retrieve_method_call call
908
+ args, kwargs, block = retrieve_method_args args
909
+ [receiver, method, args, kwargs, block, cond]
910
+ in [:method_add_block, call, block]
911
+ receiver, method, args, kwargs, cond = retrieve_method_call call
912
+ [receiver, method, args, kwargs, block, cond]
913
+ end
914
+ end
915
+
916
+ def retrieve_method_args(sexp)
917
+ case sexp
918
+ in [:mrhs_add_star, args, star]
919
+ args, = retrieve_method_args args
920
+ [[*args, KatakataIrb::Types::Splat.new(star)], [], nil]
921
+ in [:mrhs_new_from_args, [:args_add_star,] => args]
922
+ args, = retrieve_method_args args
923
+ [args, [], nil]
924
+ in [:mrhs_new_from_args, [:args_add_star,] => args, last_arg]
925
+ args, = retrieve_method_args args
926
+ [[*args, last_arg], [], nil]
927
+ in [:mrhs_new_from_args, args, last_arg]
928
+ [[*args, last_arg], [], nil]
929
+ in [:mrhs_new_from_args, args]
930
+ [args, [], nil]
931
+ in [:args_add_block, [:args_add_star,] => args, block_arg]
932
+ args, kwargs, = retrieve_method_args args
933
+ [args, kwargs, block_arg]
934
+ in [:args_add_block, [*args, [:bare_assoc_hash,] => kw], block_arg]
935
+ _, kwargs = retrieve_method_args kw
936
+ [args, kwargs, block_arg]
937
+ in [:args_add_block, [*args], block_arg]
938
+ [args, [], block_arg]
939
+ in [:bare_assoc_hash, kws]
940
+ kwargs = []
941
+ kws.each do |kw|
942
+ if kw in [:assoc_splat, value,]
943
+ kwargs << KatakataIrb::Types::Splat.new(value)
944
+ elsif kw in [:assoc_new, [:@label, label,] => key, nil]
945
+ name = label.delete ':'
946
+ kwargs << [key, [:__var_ref_or_call, [name =~ /\A[A-Z]/ ? :@const : :@ident, name, [0, 0]]]]
947
+ elsif kw in [:assoc_new, key, value]
948
+ kwargs << [key, value]
949
+ end
950
+ end
951
+ [[], kwargs, nil]
952
+ in [:args_add_star, *args, [:bare_assoc_hash,] => kwargs]
953
+ args, = retrieve_method_args [:args_add_star, *args]
954
+ _, kwargs = retrieve_method_args kwargs
955
+ [args, kwargs, nil]
956
+ in [:args_add_star, pre_args, star_arg, *post_args]
957
+ pre_args, = retrieve_method_args pre_args if pre_args in [:args_add_star,]
958
+ args = [*pre_args, KatakataIrb::Types::Splat.new(star_arg), *post_args]
959
+ [args, [], nil]
960
+ in [:arg_paren, args]
961
+ args ? retrieve_method_args(args) : [[], [], nil]
962
+ else
963
+ [[], [], nil]
964
+ end
965
+ end
966
+
967
+ def simulate_call(receiver, method_name, args, kwargs, block, name_match: true)
968
+ methods = KatakataIrb::Types.rbs_methods receiver, method_name.to_sym, args, kwargs, !!block
969
+ block_called = false
970
+ type_breaks = methods.map do |method, given_params, method_params|
971
+ receiver_vars = (receiver in KatakataIrb::Types::InstanceType) ? receiver.params : {}
972
+ free_vars = method.type.free_variables - receiver_vars.keys.to_set
973
+ vars = receiver_vars.merge KatakataIrb::Types.match_free_variables(free_vars, method_params, given_params)
974
+ if block && method.block
975
+ params_type = method.block.type.required_positionals.map do |func_param|
976
+ KatakataIrb::Types.from_rbs_type func_param.type, receiver, vars
977
+ end
978
+ block_response, breaks = block.call params_type
979
+ block_called = true
980
+ vars.merge! KatakataIrb::Types.match_free_variables(free_vars - vars.keys.to_set, [method.block.type.return_type], [block_response])
981
+ end
982
+ [KatakataIrb::Types.from_rbs_type(method.type.return_type, receiver, vars || {}), breaks]
983
+ end
984
+ block&.call [] unless block_called
985
+ types = type_breaks.map(&:first)
986
+ breaks = type_breaks.map(&:last).compact
987
+ types << OBJECT_METHODS[method_name.to_sym] if name_match && OBJECT_METHODS.has_key?(method_name.to_sym)
988
+ KatakataIrb::Types::UnionType[*types, *breaks]
989
+ end
990
+
991
+ def extract_param_names(params)
992
+ params => [:params, pre_required, optional, rest, post_required, keywords, keyrest, block]
993
+ names = []
994
+ extract_mlhs = ->(item) do
995
+ case item
996
+ in [:var_field, [:@ident, name,],]
997
+ names << name
998
+ in [:@ident, name,]
999
+ names << name
1000
+ in [:mlhs, *items]
1001
+ items.each(&extract_mlhs)
1002
+ in [:rest_param, item]
1003
+ extract_mlhs.call item if item
1004
+ in [:excessed_comma]
1005
+ end
1006
+ end
1007
+ [*pre_required, *post_required].each(&extract_mlhs)
1008
+ extract_mlhs.call rest if rest
1009
+ optional&.each do |key, _value|
1010
+ key => [:@ident, name,]
1011
+ names << name
1012
+ end
1013
+ keywords&.each do |key, _value|
1014
+ key => [:@label, label,]
1015
+ names << label.delete(':')
1016
+ end
1017
+ if keyrest in [:kwrest_params, [:@ident, name,]]
1018
+ names << name
1019
+ end
1020
+ if block in [:blockarg, [:@ident, name,]]
1021
+ names << name
1022
+ end
1023
+ names
1024
+ end
1025
+
1026
+ def evaluate_assign_params(params, values, scope)
1027
+ values = values.dup
1028
+ params => [:params, pre_required, optional, rest, post_required, keywords, keyrest, block]
1029
+ size = (pre_required&.size || 0) + (optional&.size || 0) + (post_required&.size || 0) + (rest ? 1 : 0)
1030
+ if values.size == 1 && size >= 2
1031
+ value = values.first
1032
+ array_value = to_array value, :to_ary if value
1033
+ values = [array_value.params[:Elem] || KatakataIrb::Types::OBJECT] * size if array_value
1034
+ end
1035
+ pre_values = values.shift pre_required.size if pre_required
1036
+ post_values = values.pop post_required.size if post_required
1037
+ opt_values = values.shift optional.size if optional
1038
+ rest_values = values
1039
+ evaluate_massign pre_required, pre_values, scope if pre_required
1040
+ evaluate_massign optional.map(&:first), opt_values, scope if optional
1041
+ if rest in [:rest_param, [:@ident, name,]]
1042
+ scope[name] = KatakataIrb::Types::InstanceType.new Array, Elem: KatakataIrb::Types::UnionType[*rest_values]
1043
+ end
1044
+ evaluate_massign post_required, post_values, scope if post_required
1045
+ if keyrest in [:kwrest_param, [:@ident, name,]]
1046
+ scope[name] = KatakataIrb::Types::InstanceType.new Hash, K: KatakataIrb::Types::SYMBOL, V: KatakataIrb::Types::OBJECT
1047
+ end
1048
+ if block in [:blockarg, [:@ident, name,]]
1049
+ scope[name] = KatakataIrb::Types::PROC
1050
+ end
1051
+ end
1052
+
1053
+ def evaluate_param_defaults(params, scope)
1054
+ params => [:params, _pre_required, optional, rest, _post_required, keywords, keyrest, block]
1055
+ optional&.each do |item, value|
1056
+ item => [:@ident, name,]
1057
+ scope[name] = simulate_evaluate value, scope
1058
+ end
1059
+ if rest in [:rest_param, [:@ident, name,]]
1060
+ scope[name] = KatakataIrb::Types::ARRAY
1061
+ end
1062
+ keywords&.each do |key, value|
1063
+ key => [:@label, label,]
1064
+ name = label.delete ':'
1065
+ scope[name] = value ? simulate_evaluate(value, scope) : KatakataIrb::Types::OBJECT
1066
+ end
1067
+ case keyrest
1068
+ in [:args_forward] | nil
1069
+ in [:kwrest_param, [:@ident, name,]]
1070
+ scope[name] = KatakataIrb::Types::HASH
1071
+ end
1072
+ case block
1073
+ in :& | nil
1074
+ in [:blockarg, [:@ident, name,]]
1075
+ scope[name] = KatakataIrb::Types::PROC
1076
+ end
1077
+ end
1078
+
1079
+ def max_numbered_params(sexp)
1080
+ case sexp
1081
+ in [:do_block | :brace_block | :def | :class | :module,]
1082
+ 0
1083
+ in [:var_ref, [:@ident, name,]]
1084
+ name.match?(/\A_[1-9]\z/) ? name[1..].to_i : 0
1085
+ else
1086
+ sexp.filter_map do |s|
1087
+ max_numbered_params s if s in Array
1088
+ end.max || 0
1089
+ end
1090
+ end
1091
+
1092
+ def self.calculate_binding_scope(binding, parents, target)
1093
+ dig_targets = DigTarget.new(parents, target) do |_types, scope|
1094
+ return scope
1095
+ end
1096
+ scope = Scope.from_binding(binding)
1097
+ new(dig_targets).simulate_evaluate parents[0], scope
1098
+ scope
1099
+ end
1100
+
1101
+ def self.calculate_receiver(binding, parents, receiver)
1102
+ dig_targets = DigTarget.new(parents, receiver) do |type, _scope|
1103
+ return type
1104
+ end
1105
+ new(dig_targets).simulate_evaluate parents[0], Scope.from_binding(binding)
1106
+ KatakataIrb::Types::NIL
1107
+ end
1108
+ end