katakata_irb 0.1.12 → 0.2.1
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 +166 -218
- data/lib/katakata_irb/scope.rb +224 -81
- data/lib/katakata_irb/type_analyzer.rb +1168 -0
- data/lib/katakata_irb/types.rb +25 -11
- data/lib/katakata_irb/version.rb +1 -1
- data/lib/katakata_irb.rb +2 -0
- metadata +17 -4
- data/lib/katakata_irb/nesting_parser.rb +0 -257
- data/lib/katakata_irb/type_simulator.rb +0 -995
data/lib/katakata_irb/scope.rb
CHANGED
@@ -1,42 +1,71 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'set'
|
2
4
|
require_relative 'types'
|
3
5
|
|
4
6
|
module KatakataIrb
|
5
7
|
class BaseScope
|
6
|
-
SELF = '%self'
|
7
8
|
BREAK_RESULT = '%break'
|
8
9
|
NEXT_RESULT = '%next'
|
9
10
|
RETURN_RESULT = '%return'
|
10
11
|
PATTERNMATCH_BREAK = '%match'
|
11
|
-
RAISE_BREAK = '%raise'
|
12
12
|
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
@
|
17
|
-
@
|
13
|
+
attr_reader :module_nesting, :self_object
|
14
|
+
|
15
|
+
def initialize(binding, self_object, local_variables)
|
16
|
+
@binding = binding
|
17
|
+
@self_object = self_object
|
18
|
+
@cache = {}
|
19
|
+
modules = [*binding.eval('Module.nesting'), Object]
|
20
|
+
@module_nesting = modules.map { [_1, []] }
|
21
|
+
binding_local_variables = binding.local_variables
|
22
|
+
uninitialized_locals = local_variables - binding_local_variables
|
23
|
+
uninitialized_locals.each { @cache[_1] = KatakataIrb::Types::NIL }
|
24
|
+
@local_variables = (local_variables | binding_local_variables).map(&:to_s).to_set
|
25
|
+
@global_variables = Kernel.global_variables.map(&:to_s).to_set
|
26
|
+
@owned_constants_cache = {}
|
18
27
|
end
|
19
28
|
|
20
29
|
def level() = 0
|
21
30
|
|
22
|
-
def level_of(
|
23
|
-
has?(name) ? 0 : nil
|
24
|
-
end
|
31
|
+
def level_of(_name, _var_type) = 0
|
25
32
|
|
26
33
|
def mutable?() = false
|
27
34
|
|
35
|
+
def module_own_constant?(mod, name)
|
36
|
+
set = (@owned_constants_cache[mod] ||= Set.new(mod.constants.map(&:to_s)))
|
37
|
+
set.include? name
|
38
|
+
end
|
39
|
+
|
40
|
+
def get_const(nesting, path, _key = nil)
|
41
|
+
return unless nesting
|
42
|
+
|
43
|
+
result = path.reduce nesting do |mod, name|
|
44
|
+
return nil unless mod.is_a?(Module) && module_own_constant?(mod, name)
|
45
|
+
mod.const_get name
|
46
|
+
end
|
47
|
+
KatakataIrb::Types.type_from_object result
|
48
|
+
end
|
49
|
+
|
50
|
+
def get_cvar(nesting, path, name, _key = nil)
|
51
|
+
return KatakataIrb::Types::NIL unless nesting
|
52
|
+
|
53
|
+
result = path.reduce nesting do |mod, n|
|
54
|
+
return KatakataIrb::Types::NIL unless mod.is_a?(Module) && module_own_constant?(mod, n)
|
55
|
+
mod.const_get n
|
56
|
+
end
|
57
|
+
value = result.class_variable_get name if result.is_a?(Module) && name.size >= 3 && result.class_variable_defined?(name)
|
58
|
+
Types.type_from_object value
|
59
|
+
end
|
60
|
+
|
28
61
|
def [](name)
|
29
62
|
@cache[name] ||= (
|
30
63
|
fallback = KatakataIrb::Types::NIL
|
31
64
|
case BaseScope.type_by_name name
|
32
|
-
when :cvar
|
33
|
-
BaseScope.type_of(fallback: fallback) { @self_object.class_variable_get name }
|
34
65
|
when :ivar
|
35
66
|
BaseScope.type_of(fallback: fallback) { @self_object.instance_variable_get name }
|
36
67
|
when :lvar
|
37
68
|
BaseScope.type_of(fallback: fallback) { @binding.local_variable_get(name) }
|
38
|
-
when :const
|
39
|
-
BaseScope.type_of(fallback: fallback) { @binding.eval name }
|
40
69
|
when :gvar
|
41
70
|
BaseScope.type_of(fallback: fallback) { @binding.eval name if @global_variables.include? name }
|
42
71
|
end
|
@@ -44,12 +73,12 @@ module KatakataIrb
|
|
44
73
|
end
|
45
74
|
|
46
75
|
def self_type
|
47
|
-
|
76
|
+
KatakataIrb::Types.type_from_object @self_object
|
48
77
|
end
|
49
78
|
|
50
|
-
def local_variables
|
51
|
-
|
52
|
-
|
79
|
+
def local_variables() = @local_variables.to_a
|
80
|
+
|
81
|
+
def global_variables() = @global_variables.to_a
|
53
82
|
|
54
83
|
def self.type_of(fallback: KatakataIrb::Types::OBJECT)
|
55
84
|
begin
|
@@ -61,6 +90,7 @@ module KatakataIrb
|
|
61
90
|
|
62
91
|
def self.type_by_name(name)
|
63
92
|
if name.start_with? '@@'
|
93
|
+
# "@@cvar" or "@@cvar::[module_id]::[module_path]"
|
64
94
|
:cvar
|
65
95
|
elsif name.start_with? '@'
|
66
96
|
:ivar
|
@@ -68,49 +98,30 @@ module KatakataIrb
|
|
68
98
|
:gvar
|
69
99
|
elsif name.start_with? '%'
|
70
100
|
:internal
|
71
|
-
elsif name[0].downcase != name[0]
|
101
|
+
elsif name[0].downcase != name[0] || name[0].match?(/\d/)
|
102
|
+
# "ConstName" or "[module_id]::[const_path]"
|
72
103
|
:const
|
73
104
|
else
|
74
105
|
:lvar
|
75
106
|
end
|
76
107
|
end
|
77
|
-
|
78
|
-
def has?(name)
|
79
|
-
case BaseScope.type_by_name name
|
80
|
-
when :lvar
|
81
|
-
@local_variables.include? name
|
82
|
-
when :const
|
83
|
-
begin
|
84
|
-
@binding.eval(name)
|
85
|
-
true
|
86
|
-
rescue
|
87
|
-
false
|
88
|
-
end
|
89
|
-
when :gvar, :cvar, :ivar
|
90
|
-
true
|
91
|
-
when :internal
|
92
|
-
true
|
93
|
-
end
|
94
|
-
end
|
95
108
|
end
|
96
109
|
|
97
110
|
class Scope < BaseScope
|
98
|
-
attr_reader :parent, :
|
111
|
+
attr_reader :parent, :mergeable_changes, :level, :module_nesting
|
99
112
|
|
100
|
-
def self.from_binding(binding) = new(BaseScope.new(binding, binding.eval('self')))
|
113
|
+
def self.from_binding(binding, locals) = new(BaseScope.new(binding, binding.eval('self'), locals))
|
101
114
|
|
102
|
-
def initialize(parent, table = {},
|
103
|
-
@table = table
|
115
|
+
def initialize(parent, table = {}, trace_ivar: true, trace_lvar: true, self_type: nil, nesting: nil)
|
104
116
|
@parent = parent
|
105
|
-
@level = parent.level +
|
106
|
-
@trace_cvar = trace_cvar
|
117
|
+
@level = parent.level + 1
|
107
118
|
@trace_ivar = trace_ivar
|
108
119
|
@trace_lvar = trace_lvar
|
109
|
-
@
|
120
|
+
@module_nesting = nesting ? [nesting, *parent.module_nesting] : parent.module_nesting
|
121
|
+
@self_type = self_type
|
110
122
|
@terminated = false
|
111
123
|
@jump_branches = []
|
112
|
-
@
|
113
|
-
@mergeable_changes = @changes = table.transform_values { [level, _1] }
|
124
|
+
@mergeable_changes = @table = table.transform_values { [level, _1] }
|
114
125
|
end
|
115
126
|
|
116
127
|
def mutable? = true
|
@@ -138,53 +149,202 @@ module KatakataIrb
|
|
138
149
|
def terminate
|
139
150
|
return if terminated?
|
140
151
|
@terminated = true
|
141
|
-
@
|
152
|
+
@table = @mergeable_changes.dup
|
142
153
|
end
|
143
154
|
|
144
|
-
def branch_table_clone() = @tables.last.dup
|
145
|
-
|
146
155
|
def trace?(name)
|
147
156
|
return false unless @parent
|
148
157
|
type = BaseScope.type_by_name(name)
|
149
|
-
type == :
|
158
|
+
type == :ivar ? @trace_ivar : type == :lvar ? @trace_lvar : true
|
159
|
+
end
|
160
|
+
|
161
|
+
def level_of(name, var_type)
|
162
|
+
case var_type
|
163
|
+
when :ivar
|
164
|
+
return level unless @trace_ivar
|
165
|
+
when :gvar
|
166
|
+
return 0
|
167
|
+
end
|
168
|
+
variable_level, = @table[name]
|
169
|
+
variable_level || parent.level_of(name, var_type)
|
150
170
|
end
|
151
171
|
|
152
|
-
def
|
153
|
-
|
154
|
-
|
172
|
+
def get_const(nesting, path, key = nil)
|
173
|
+
key ||= [nesting.__id__, path].join('::')
|
174
|
+
_l, value = @table[key]
|
175
|
+
value || @parent.get_const(nesting, path, key)
|
176
|
+
end
|
177
|
+
|
178
|
+
def get_cvar(nesting, path, name, key = nil)
|
179
|
+
key ||= [name, nesting.__id__, path].join('::')
|
180
|
+
_l, value = @table[key]
|
181
|
+
value || @parent.get_cvar(nesting, path, name, key)
|
155
182
|
end
|
156
183
|
|
157
184
|
def [](name)
|
158
|
-
|
185
|
+
type = BaseScope.type_by_name(name)
|
186
|
+
if type == :const
|
187
|
+
return get_const(nil, nil, name) || KatakataIrb::Types::NIL if name.include?('::')
|
188
|
+
|
189
|
+
module_nesting.each do |(nesting, path)|
|
190
|
+
value = get_const nesting, [*path, name]
|
191
|
+
return value if value
|
192
|
+
end
|
193
|
+
return KatakataIrb::Types::NIL
|
194
|
+
elsif type == :cvar
|
195
|
+
return get_cvar(nil, nil, nil, name) if name.include?('::')
|
196
|
+
|
197
|
+
nesting, path = module_nesting.first
|
198
|
+
return get_cvar(nesting, path, name)
|
199
|
+
end
|
200
|
+
level, value = @table[name]
|
159
201
|
if level
|
160
202
|
value
|
161
203
|
elsif trace? name
|
162
|
-
@parent[name]
|
204
|
+
@parent[name]
|
205
|
+
elsif type == :ivar
|
206
|
+
self_instance_variable_get name
|
163
207
|
end
|
164
208
|
end
|
165
209
|
|
210
|
+
def set_const(nesting, path, value)
|
211
|
+
key = [nesting.__id__, path].join('::')
|
212
|
+
@table[key] = [0, value]
|
213
|
+
end
|
214
|
+
|
215
|
+
def set_cvar(nesting, path, name, value)
|
216
|
+
key = [name, nesting.__id__, path].join('::')
|
217
|
+
@table[key] = [0, value]
|
218
|
+
end
|
219
|
+
|
166
220
|
def []=(name, value)
|
167
|
-
|
168
|
-
|
169
|
-
|
221
|
+
type = BaseScope.type_by_name(name)
|
222
|
+
if type == :const
|
223
|
+
if name.include?('::')
|
224
|
+
@table[name] = [0, value]
|
225
|
+
else
|
226
|
+
parent_module, parent_path = module_nesting.first
|
227
|
+
set_const parent_module, [*parent_path, name], value
|
228
|
+
end
|
229
|
+
return
|
230
|
+
elsif type == :cvar
|
231
|
+
if name.include?('::')
|
232
|
+
@table[name] = [0, value]
|
233
|
+
else
|
234
|
+
parent_module, parent_path = module_nesting.first
|
235
|
+
set_cvar parent_module, parent_path, name, value
|
236
|
+
end
|
237
|
+
return
|
238
|
+
end
|
239
|
+
variable_level = level_of name, type
|
240
|
+
@table[name] = [variable_level, value] if variable_level
|
170
241
|
end
|
171
242
|
|
172
243
|
def self_type
|
173
|
-
|
244
|
+
@self_type || @parent.self_type
|
245
|
+
end
|
246
|
+
|
247
|
+
def global_variables
|
248
|
+
gvar_keys = @table.keys.select do |name|
|
249
|
+
BaseScope.type_by_name(name) == :gvar
|
250
|
+
end
|
251
|
+
gvar_keys | @parent.global_variables
|
174
252
|
end
|
175
253
|
|
176
254
|
def local_variables
|
177
|
-
lvar_keys = @
|
255
|
+
lvar_keys = @table.keys.select do |name|
|
178
256
|
BaseScope.type_by_name(name) == :lvar
|
179
257
|
end
|
180
258
|
lvar_keys |= @parent.local_variables if @trace_lvar
|
181
259
|
lvar_keys
|
182
260
|
end
|
183
261
|
|
262
|
+
def table_constants
|
263
|
+
constants = module_nesting.flat_map do |mod, path|
|
264
|
+
prefix = [mod.__id__, *path].join('::') + '::'
|
265
|
+
@table.keys.select { _1.start_with? prefix }.map { _1.delete_prefix(prefix).split('::').first }
|
266
|
+
end.uniq
|
267
|
+
constants |= @parent.table_constants if @parent.mutable?
|
268
|
+
constants
|
269
|
+
end
|
270
|
+
|
271
|
+
def table_module_constants(mod)
|
272
|
+
prefix = "#{mod.__id__}::"
|
273
|
+
constants = @table.keys.select { _1.start_with? prefix }.map { _1.delete_prefix(prefix).split('::').first }
|
274
|
+
constants |= @parent.table_constants if @parent.mutable?
|
275
|
+
constants
|
276
|
+
end
|
277
|
+
|
278
|
+
def base_scope
|
279
|
+
@parent.mutable? ? @parent.base_scope : @parent
|
280
|
+
end
|
281
|
+
|
282
|
+
def table_instance_variables
|
283
|
+
ivars = @table.keys.select { BaseScope.type_by_name(_1) == :ivar }
|
284
|
+
ivars |= @parent.table_instance_variables if @parent.mutable? && @trace_ivar
|
285
|
+
ivars
|
286
|
+
end
|
287
|
+
|
288
|
+
def instance_variables
|
289
|
+
self_singleton_types = self_type.types.grep(KatakataIrb::Types::SingletonType)
|
290
|
+
singleton_classes = self_type.types.grep(KatakataIrb::Types::InstanceType).map(&:klass).select(&:singleton_class?)
|
291
|
+
base_self = base_scope.self_object
|
292
|
+
self_instance_variables = singleton_classes.flat_map do |singleton_class|
|
293
|
+
if singleton_class.respond_to? :attached_object
|
294
|
+
singleton_class.attached_object.instance_variables.map(&:to_s)
|
295
|
+
elsif singleton_class == base_self.singleton_class
|
296
|
+
base_self.instance_variables.map(&:to_s)
|
297
|
+
else
|
298
|
+
[]
|
299
|
+
end
|
300
|
+
end
|
301
|
+
[
|
302
|
+
self_singleton_types.flat_map { _1.module_or_class.instance_variables.map(&:to_s) },
|
303
|
+
self_instance_variables || [],
|
304
|
+
table_instance_variables
|
305
|
+
].inject(:|)
|
306
|
+
end
|
307
|
+
|
308
|
+
def self_instance_variable_get(name)
|
309
|
+
self_objects = self_type.types.grep(KatakataIrb::Types::SingletonType).map(&:module_or_class)
|
310
|
+
singleton_classes = self_type.types.grep(KatakataIrb::Types::InstanceType).map(&:klass).select(&:singleton_class?)
|
311
|
+
base_self = base_scope.self_object
|
312
|
+
singleton_classes.each do |singleton_class|
|
313
|
+
if singleton_class.respond_to? :attached_object
|
314
|
+
self_objects << singleton_class.attached_object
|
315
|
+
elsif singleton_class == base_self.singleton_class
|
316
|
+
self_objects << base_self
|
317
|
+
end
|
318
|
+
end
|
319
|
+
types = self_objects.map do |object|
|
320
|
+
BaseScope.type_of(fallback: KatakataIrb::Types::NIL) { object.instance_variable_get name }
|
321
|
+
end
|
322
|
+
KatakataIrb::Types::UnionType[*types]
|
323
|
+
end
|
324
|
+
|
325
|
+
def table_class_variables
|
326
|
+
cvars = @table.keys.filter_map { _1.split('::', 2).first if BaseScope.type_by_name(_1) == :cvar }
|
327
|
+
cvars |= @parent.table_class_variables if @parent.mutable?
|
328
|
+
cvars
|
329
|
+
end
|
330
|
+
|
331
|
+
def class_variables
|
332
|
+
cvars = table_class_variables
|
333
|
+
m, = module_nesting.first
|
334
|
+
cvars |= m.class_variables.map(&:to_s) if m.is_a? Module
|
335
|
+
cvars
|
336
|
+
end
|
337
|
+
|
338
|
+
def constants
|
339
|
+
module_nesting.flat_map do |nest,|
|
340
|
+
nest.constants
|
341
|
+
end.map(&:to_s) | table_constants
|
342
|
+
end
|
343
|
+
|
184
344
|
def merge_jumps
|
185
345
|
if terminated?
|
186
346
|
@terminated = false
|
187
|
-
@
|
347
|
+
@table = @mergeable_changes
|
188
348
|
merge @jump_branches
|
189
349
|
@terminated = true
|
190
350
|
else
|
@@ -197,27 +357,15 @@ module KatakataIrb
|
|
197
357
|
end
|
198
358
|
|
199
359
|
def never(&block)
|
200
|
-
block.call Scope.new(self, { BREAK_RESULT => nil, NEXT_RESULT => nil, PATTERNMATCH_BREAK => nil, RETURN_RESULT => nil
|
201
|
-
end
|
202
|
-
|
203
|
-
def run(*args, **option)
|
204
|
-
scope = Scope.new(self, *args, **option)
|
205
|
-
yield scope
|
206
|
-
merge_jumps
|
207
|
-
update scope
|
208
|
-
end
|
209
|
-
|
210
|
-
def touch_lvars(lvars)
|
211
|
-
lvars.each { self[_1] = self[_1] || KatakataIrb::Types::NIL }
|
360
|
+
block.call Scope.new(self, { BREAK_RESULT => nil, NEXT_RESULT => nil, PATTERNMATCH_BREAK => nil, RETURN_RESULT => nil })
|
212
361
|
end
|
213
362
|
|
214
363
|
def run_branches(*blocks)
|
215
364
|
results = []
|
216
365
|
branches = []
|
217
366
|
blocks.each do |block|
|
218
|
-
scope = Scope.new self
|
367
|
+
scope = Scope.new self
|
219
368
|
result = block.call scope
|
220
|
-
touch_lvars scope.lvars if scope.level == level
|
221
369
|
next if scope.terminated?
|
222
370
|
results << result
|
223
371
|
branches << scope.mergeable_changes
|
@@ -227,17 +375,12 @@ module KatakataIrb
|
|
227
375
|
results
|
228
376
|
end
|
229
377
|
|
230
|
-
def has?(name)
|
231
|
-
has_own?(name) || (trace?(name) && @parent.has?(name))
|
232
|
-
end
|
233
|
-
|
234
378
|
def has_own?(name)
|
235
|
-
@
|
379
|
+
@table.key? name
|
236
380
|
end
|
237
381
|
|
238
382
|
def update(child_scope)
|
239
383
|
current_level = level
|
240
|
-
touch_lvars child_scope.lvars if child_scope.level == current_level
|
241
384
|
child_scope.mergeable_changes.each do |name, (level, value)|
|
242
385
|
self[name] = value if level <= current_level
|
243
386
|
end
|