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.
- checksums.yaml +4 -4
- data/Gemfile +1 -0
- data/katakata_irb.gemspec +1 -0
- data/lib/katakata_irb/completor.rb +143 -219
- data/lib/katakata_irb/scope.rb +179 -74
- data/lib/katakata_irb/type_simulator.rb +774 -779
- data/lib/katakata_irb/types.rb +17 -3
- data/lib/katakata_irb/version.rb +1 -1
- data/lib/katakata_irb.rb +2 -0
- metadata +16 -3
- data/lib/katakata_irb/nesting_parser.rb +0 -257
@@ -1,7 +1,9 @@
|
|
1
|
-
|
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
|
55
|
-
result =
|
56
|
-
@dig_targets.resolve result, scope if @dig_targets.target?(
|
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
|
61
|
-
case
|
62
|
-
|
63
|
-
statements
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
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?
|
82
|
-
|
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::
|
88
|
-
|
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
|
-
|
91
|
-
|
92
|
-
|
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
|
-
|
94
|
+
when Prism::IntegerNode
|
98
95
|
KatakataIrb::Types::INTEGER
|
99
|
-
|
96
|
+
when Prism::FloatNode
|
100
97
|
KatakataIrb::Types::FLOAT
|
101
|
-
|
98
|
+
when Prism::RationalNode
|
102
99
|
KatakataIrb::Types::RATIONAL
|
103
|
-
|
100
|
+
when Prism::ImaginaryNode
|
104
101
|
KatakataIrb::Types::COMPLEX
|
105
|
-
|
102
|
+
when Prism::StringNode
|
106
103
|
KatakataIrb::Types::STRING
|
107
|
-
|
108
|
-
KatakataIrb::Types::
|
109
|
-
|
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
|
-
|
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
|
-
|
115
|
-
|
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
|
-
|
120
|
-
|
117
|
+
when Prism::InterpolatedXStringNode
|
118
|
+
node.parts.each { evaluate _1, scope }
|
121
119
|
KatakataIrb::Types::STRING
|
122
|
-
|
123
|
-
|
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
|
-
|
129
|
+
when Prism::EmbeddedVariableNode
|
130
|
+
evaluate node.variable, scope
|
126
131
|
KatakataIrb::Types::STRING
|
127
|
-
|
128
|
-
|
129
|
-
|
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
|
-
|
165
|
-
case
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
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
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
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
|
-
|
173
|
+
when Prism::SourceLineNode
|
213
174
|
KatakataIrb::Types::INTEGER
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
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
|
-
|
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
|
255
|
-
|
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,
|
201
|
+
block_receiver ? simulate_call(block_receiver || KatakataIrb::Types::OBJECT, block_sym, rest, nil, nil, scope) : KatakataIrb::Types::OBJECT
|
259
202
|
end
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
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,
|
233
|
+
simulate_call receiver_type, node.name, args_types, kwargs_types, call_block_proc, scope
|
298
234
|
end
|
299
|
-
if
|
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
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
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
|
-
|
320
|
-
simulate_call
|
321
|
-
end
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
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
|
-
|
332
|
-
|
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
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
|
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
|
-
-> {
|
385
|
-
-> {
|
357
|
+
-> { node.statements ? evaluate(node.statements, _1) : KatakataIrb::Types::NIL },
|
358
|
+
-> { node.consequent ? evaluate(node.consequent, _1) : KatakataIrb::Types::NIL }
|
386
359
|
)]
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
|
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
|
-
|
414
|
-
internal_key =
|
415
|
-
|
416
|
-
|
417
|
-
|
418
|
-
|
419
|
-
|
420
|
-
|
421
|
-
|
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
|
-
|
425
|
-
|
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
|
-
|
400
|
+
when Prism::RedoNode, Prism::RetryNode
|
433
401
|
scope.terminate
|
434
|
-
|
402
|
+
when Prism::ForwardingSuperNode
|
435
403
|
KatakataIrb::Types::OBJECT
|
436
|
-
|
437
|
-
|
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
|
-
|
445
|
-
|
446
|
-
|
447
|
-
|
448
|
-
|
449
|
-
|
450
|
-
|
451
|
-
|
452
|
-
|
453
|
-
|
454
|
-
|
455
|
-
|
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
|
-
|
458
|
-
|
459
|
-
if
|
460
|
-
|
461
|
-
|
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
|
-
|
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
|
444
|
+
node.statements ? evaluate(node.statements, s) : KatakataIrb::Types::NIL
|
469
445
|
end
|
470
|
-
if
|
471
|
-
|
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
|
-
|
474
|
-
|
475
|
-
|
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
|
-
|
482
|
-
|
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
|
-
|
494
|
-
|
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
|
-
|
498
|
-
|
499
|
-
|
500
|
-
|
501
|
-
|
502
|
-
|
503
|
-
|
504
|
-
|
505
|
-
|
506
|
-
|
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
|
-
|
509
|
-
|
510
|
-
|
511
|
-
|
512
|
-
|
513
|
-
|
514
|
-
|
515
|
-
|
516
|
-
|
517
|
-
|
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,
|
523
|
-
|
524
|
-
|
525
|
-
|
526
|
-
|
527
|
-
|
528
|
-
|
529
|
-
|
530
|
-
|
531
|
-
|
532
|
-
|
533
|
-
|
534
|
-
|
535
|
-
|
536
|
-
|
537
|
-
|
538
|
-
|
539
|
-
|
540
|
-
|
541
|
-
|
542
|
-
|
543
|
-
|
544
|
-
|
545
|
-
|
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,
|
569
|
-
|
570
|
-
|
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
|
602
|
+
KatakataIrb.log_puts node.inspect
|
582
603
|
KatakataIrb::Types::NIL
|
583
604
|
end
|
584
605
|
end
|
585
606
|
|
586
|
-
def
|
587
|
-
|
588
|
-
|
589
|
-
|
590
|
-
|
591
|
-
|
592
|
-
|
593
|
-
|
594
|
-
|
595
|
-
|
596
|
-
|
597
|
-
|
598
|
-
|
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
|
-
|
612
|
-
|
613
|
-
|
614
|
-
|
615
|
-
|
616
|
-
|
617
|
-
|
618
|
-
|
619
|
-
|
620
|
-
|
621
|
-
|
622
|
-
|
623
|
-
|
624
|
-
|
625
|
-
|
626
|
-
|
627
|
-
|
628
|
-
|
629
|
-
|
630
|
-
|
631
|
-
|
632
|
-
|
633
|
-
|
634
|
-
|
635
|
-
|
636
|
-
|
637
|
-
|
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
|
-
|
640
|
-
|
641
|
-
|
642
|
-
|
643
|
-
|
644
|
-
|
645
|
-
|
646
|
-
|
647
|
-
|
648
|
-
|
649
|
-
|
650
|
-
|
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
|
-
|
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
|
657
|
-
|
658
|
-
|
659
|
-
|
660
|
-
|
661
|
-
|
662
|
-
|
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
|
-
|
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
|
-
|
668
|
-
|
669
|
-
|
670
|
-
|
671
|
-
|
672
|
-
|
673
|
-
|
674
|
-
|
675
|
-
|
676
|
-
|
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
|
-
|
680
|
-
|
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
|
-
|
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
|
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
|
-
|
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
|
-
|
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
|
883
|
-
|
884
|
-
|
885
|
-
|
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.
|
989
|
-
dig_targets = DigTarget.new(parents,
|
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
|
-
|
993
|
-
KatakataIrb::
|
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
|