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.
- checksums.yaml +7 -0
- data/Gemfile +10 -0
- data/Gemfile.lock +23 -0
- data/LICENSE.txt +21 -0
- data/README.md +51 -0
- data/Rakefile +12 -0
- data/bin/console +10 -0
- data/bin/setup +8 -0
- data/exe/kirb +10 -0
- data/katakata_irb.gemspec +36 -0
- data/lib/katakata_irb/completor.rb +187 -0
- data/lib/katakata_irb/reline_patch.rb +40 -0
- data/lib/katakata_irb/reline_patches/escapeseq.patch +45 -0
- data/lib/katakata_irb/reline_patches/fullwidth.patch +200 -0
- data/lib/katakata_irb/reline_patches/indent.patch +25 -0
- data/lib/katakata_irb/reline_patches/raw.patch +95 -0
- data/lib/katakata_irb/reline_patches/scrollbar.patch +43 -0
- data/lib/katakata_irb/reline_patches/wholelines.patch +102 -0
- data/lib/katakata_irb/ruby_lex_patch.rb +197 -0
- data/lib/katakata_irb/trex.rb +207 -0
- data/lib/katakata_irb/type_simulator.rb +1108 -0
- data/lib/katakata_irb/types.rb +341 -0
- data/lib/katakata_irb/version.rb +5 -0
- data/lib/katakata_irb.rb +15 -0
- data/sig/katakata_irb.rbs +4 -0
- metadata +84 -0
@@ -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
|