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
data/lib/katakata_irb/scope.rb
CHANGED
@@ -1,30 +1,50 @@
|
|
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
|
+
result = path.reduce nesting do |mod, name|
|
42
|
+
return nil unless mod.is_a?(Module) && module_own_constant?(mod, name)
|
43
|
+
mod.const_get name
|
44
|
+
end
|
45
|
+
KatakataIrb::Types.type_from_object result
|
46
|
+
end
|
47
|
+
|
28
48
|
def [](name)
|
29
49
|
@cache[name] ||= (
|
30
50
|
fallback = KatakataIrb::Types::NIL
|
@@ -44,12 +64,12 @@ module KatakataIrb
|
|
44
64
|
end
|
45
65
|
|
46
66
|
def self_type
|
47
|
-
|
67
|
+
KatakataIrb::Types.type_from_object @self_object
|
48
68
|
end
|
49
69
|
|
50
|
-
def local_variables
|
51
|
-
|
52
|
-
|
70
|
+
def local_variables() = @local_variables.to_a
|
71
|
+
|
72
|
+
def global_variables() = @global_variables.to_a
|
53
73
|
|
54
74
|
def self.type_of(fallback: KatakataIrb::Types::OBJECT)
|
55
75
|
begin
|
@@ -74,43 +94,24 @@ module KatakataIrb
|
|
74
94
|
:lvar
|
75
95
|
end
|
76
96
|
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
97
|
end
|
96
98
|
|
97
99
|
class Scope < BaseScope
|
98
|
-
attr_reader :parent, :
|
100
|
+
attr_reader :parent, :mergeable_changes, :level, :module_nesting
|
99
101
|
|
100
|
-
def self.from_binding(binding) = new(BaseScope.new(binding, binding.eval('self')))
|
102
|
+
def self.from_binding(binding, locals) = new(BaseScope.new(binding, binding.eval('self'), locals))
|
101
103
|
|
102
|
-
def initialize(parent, table = {}, trace_cvar: true, trace_ivar: true, trace_lvar: true,
|
103
|
-
@table = table
|
104
|
+
def initialize(parent, table = {}, trace_cvar: true, trace_ivar: true, trace_lvar: true, self_type: nil, nesting: nil)
|
104
105
|
@parent = parent
|
105
|
-
@level = parent.level +
|
106
|
+
@level = parent.level + 1
|
106
107
|
@trace_cvar = trace_cvar
|
107
108
|
@trace_ivar = trace_ivar
|
108
109
|
@trace_lvar = trace_lvar
|
109
|
-
@
|
110
|
+
@module_nesting = nesting ? [nesting, *parent.module_nesting] : parent.module_nesting
|
111
|
+
@self_type = self_type
|
110
112
|
@terminated = false
|
111
113
|
@jump_branches = []
|
112
|
-
@
|
113
|
-
@mergeable_changes = @changes = table.transform_values { [level, _1] }
|
114
|
+
@mergeable_changes = @table = table.transform_values { [level, _1] }
|
114
115
|
end
|
115
116
|
|
116
117
|
def mutable? = true
|
@@ -138,53 +139,174 @@ module KatakataIrb
|
|
138
139
|
def terminate
|
139
140
|
return if terminated?
|
140
141
|
@terminated = true
|
141
|
-
@
|
142
|
+
@table = @mergeable_changes.dup
|
142
143
|
end
|
143
144
|
|
144
|
-
def branch_table_clone() = @tables.last.dup
|
145
|
-
|
146
145
|
def trace?(name)
|
147
146
|
return false unless @parent
|
148
147
|
type = BaseScope.type_by_name(name)
|
149
148
|
type == :cvar ? @trace_cvar : type == :ivar ? @trace_ivar : type == :lvar ? @trace_lvar : true
|
150
149
|
end
|
151
150
|
|
152
|
-
def level_of(name)
|
153
|
-
|
154
|
-
|
151
|
+
def level_of(name, var_type)
|
152
|
+
case var_type
|
153
|
+
when :ivar
|
154
|
+
return level unless @trace_ivar
|
155
|
+
when :cvar
|
156
|
+
return level unless @trace_cvar
|
157
|
+
when :gvar
|
158
|
+
return 0
|
159
|
+
end
|
160
|
+
variable_level, = @table[name]
|
161
|
+
variable_level || parent.level_of(name, var_type)
|
162
|
+
end
|
163
|
+
|
164
|
+
def get_const(nesting, path, key = nil)
|
165
|
+
key ||= [nesting.__id__, path].join('::')
|
166
|
+
_l, value = @table[key]
|
167
|
+
value || @parent.get_const(nesting, path, key)
|
155
168
|
end
|
156
169
|
|
157
170
|
def [](name)
|
158
|
-
|
171
|
+
type = BaseScope.type_by_name(name)
|
172
|
+
if type == :const
|
173
|
+
module_nesting.each do |(nesting, path)|
|
174
|
+
value = get_const nesting, [*path, name]
|
175
|
+
return value if value
|
176
|
+
end
|
177
|
+
KatakataIrb::Types::NIL
|
178
|
+
end
|
179
|
+
level, value = @table[name]
|
159
180
|
if level
|
160
181
|
value
|
161
182
|
elsif trace? name
|
162
|
-
@parent[name]
|
183
|
+
@parent[name]
|
184
|
+
elsif type == :ivar
|
185
|
+
self_instance_variable_get name
|
163
186
|
end
|
164
187
|
end
|
165
188
|
|
189
|
+
def set_const(nesting, path, value)
|
190
|
+
key = [nesting.__id__, path].join('::')
|
191
|
+
@table[key] = [0, value]
|
192
|
+
end
|
193
|
+
|
166
194
|
def []=(name, value)
|
167
|
-
|
168
|
-
|
169
|
-
|
195
|
+
type = BaseScope.type_by_name(name)
|
196
|
+
if type == :const
|
197
|
+
parent_module, parent_path = module_nesting.first
|
198
|
+
set_const parent_module, [*parent_path, name], value
|
199
|
+
return
|
200
|
+
end
|
201
|
+
variable_level = level_of name, type
|
202
|
+
@table[name] = [variable_level, value] if variable_level
|
170
203
|
end
|
171
204
|
|
172
205
|
def self_type
|
173
|
-
|
206
|
+
@self_type || @parent.self_type
|
207
|
+
end
|
208
|
+
|
209
|
+
def global_variables
|
210
|
+
gvar_keys = @table.keys.select do |name|
|
211
|
+
BaseScope.type_by_name(name) == :gvar
|
212
|
+
end
|
213
|
+
gvar_keys | @parent.global_variables
|
174
214
|
end
|
175
215
|
|
176
216
|
def local_variables
|
177
|
-
lvar_keys = @
|
217
|
+
lvar_keys = @table.keys.select do |name|
|
178
218
|
BaseScope.type_by_name(name) == :lvar
|
179
219
|
end
|
180
220
|
lvar_keys |= @parent.local_variables if @trace_lvar
|
181
221
|
lvar_keys
|
182
222
|
end
|
183
223
|
|
224
|
+
def table_constants
|
225
|
+
constants = module_nesting.flat_map do |mod, path|
|
226
|
+
prefix = [mod.__id__, *path].join('::') + '::'
|
227
|
+
@table.keys.select { _1.start_with? prefix }.map { _1.delete_prefix(prefix).split('::').first }
|
228
|
+
end.uniq
|
229
|
+
constants |= @parent.table_constants if @parent.mutable?
|
230
|
+
constants
|
231
|
+
end
|
232
|
+
|
233
|
+
def table_module_constants(mod)
|
234
|
+
prefix = "#{mod.__id__}::"
|
235
|
+
constants = @table.keys.select { _1.start_with? prefix }.map { _1.delete_prefix(prefix).split('::').first }
|
236
|
+
constants |= @parent.table_constants if @parent.mutable?
|
237
|
+
constants
|
238
|
+
end
|
239
|
+
|
240
|
+
def base_scope
|
241
|
+
@parent.mutable? ? @parent.base_scope : @parent
|
242
|
+
end
|
243
|
+
|
244
|
+
def table_instance_variables
|
245
|
+
ivars = @table.keys.select { BaseScope.type_by_name(_1) == :ivar }
|
246
|
+
ivars |= @parent.table_instance_variables if @parent.mutable? && @trace_ivar
|
247
|
+
ivars
|
248
|
+
end
|
249
|
+
|
250
|
+
def instance_variables
|
251
|
+
self_singleton_types = self_type.types.grep(KatakataIrb::Types::SingletonType)
|
252
|
+
singleton_classes = self_type.types.grep(KatakataIrb::Types::InstanceType).map(&:klass).select(&:singleton_class?)
|
253
|
+
base_self = base_scope.self_object
|
254
|
+
self_instance_variables = singleton_classes.flat_map do |singleton_class|
|
255
|
+
if singleton_class.respond_to? :attached_object
|
256
|
+
singleton_class.attached_object.instance_variables.map(&:to_s)
|
257
|
+
elsif singleton_class == base_self.singleton_class
|
258
|
+
base_self.instance_variables.map(&:to_s)
|
259
|
+
else
|
260
|
+
[]
|
261
|
+
end
|
262
|
+
end
|
263
|
+
[
|
264
|
+
self_singleton_types.flat_map { _1.module_or_class.instance_variables.map(&:to_s) },
|
265
|
+
self_instance_variables || [],
|
266
|
+
table_instance_variables
|
267
|
+
].inject(:|)
|
268
|
+
end
|
269
|
+
|
270
|
+
def self_instance_variable_get(name)
|
271
|
+
self_objects = self_type.types.grep(KatakataIrb::Types::SingletonType).map(&:module_or_class)
|
272
|
+
singleton_classes = self_type.types.grep(KatakataIrb::Types::InstanceType).map(&:klass).select(&:singleton_class?)
|
273
|
+
base_self = base_scope.self_object
|
274
|
+
singleton_classes.each do |singleton_class|
|
275
|
+
if singleton_class.respond_to? :attached_object
|
276
|
+
self_objects << singleton_class.attached_object
|
277
|
+
elsif singleton_class == base_self.singleton_class
|
278
|
+
self_objects << base_self
|
279
|
+
end
|
280
|
+
end
|
281
|
+
types = self_objects.map do |object|
|
282
|
+
BaseScope.type_of(fallback: KatakataIrb::Types::NIL) { object.instance_variable_get name }
|
283
|
+
end
|
284
|
+
KatakataIrb::Types::UnionType[*types]
|
285
|
+
end
|
286
|
+
|
287
|
+
def table_class_variables
|
288
|
+
cvars = @table.keys.select { BaseScope.type_by_name(_1) == :cvar }
|
289
|
+
cvars |= @parent.table_class_variables if @parent.mutable? && @trace_cvar
|
290
|
+
cvars
|
291
|
+
end
|
292
|
+
|
293
|
+
def class_variables
|
294
|
+
cvars = table_class_variables
|
295
|
+
m, = module_nesting.first
|
296
|
+
cvars |= m.class_variables.map(&:to_s) if m.is_a? Module
|
297
|
+
cvars
|
298
|
+
end
|
299
|
+
|
300
|
+
def constants
|
301
|
+
module_nesting.flat_map do |nest,|
|
302
|
+
nest.constants
|
303
|
+
end.map(&:to_s) | table_constants
|
304
|
+
end
|
305
|
+
|
184
306
|
def merge_jumps
|
185
307
|
if terminated?
|
186
308
|
@terminated = false
|
187
|
-
@
|
309
|
+
@table = @mergeable_changes
|
188
310
|
merge @jump_branches
|
189
311
|
@terminated = true
|
190
312
|
else
|
@@ -197,27 +319,15 @@ module KatakataIrb
|
|
197
319
|
end
|
198
320
|
|
199
321
|
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 }
|
322
|
+
block.call Scope.new(self, { BREAK_RESULT => nil, NEXT_RESULT => nil, PATTERNMATCH_BREAK => nil, RETURN_RESULT => nil })
|
212
323
|
end
|
213
324
|
|
214
325
|
def run_branches(*blocks)
|
215
326
|
results = []
|
216
327
|
branches = []
|
217
328
|
blocks.each do |block|
|
218
|
-
scope = Scope.new self
|
329
|
+
scope = Scope.new self
|
219
330
|
result = block.call scope
|
220
|
-
touch_lvars scope.lvars if scope.level == level
|
221
331
|
next if scope.terminated?
|
222
332
|
results << result
|
223
333
|
branches << scope.mergeable_changes
|
@@ -227,17 +337,12 @@ module KatakataIrb
|
|
227
337
|
results
|
228
338
|
end
|
229
339
|
|
230
|
-
def has?(name)
|
231
|
-
has_own?(name) || (trace?(name) && @parent.has?(name))
|
232
|
-
end
|
233
|
-
|
234
340
|
def has_own?(name)
|
235
|
-
@
|
341
|
+
@table.key? name
|
236
342
|
end
|
237
343
|
|
238
344
|
def update(child_scope)
|
239
345
|
current_level = level
|
240
|
-
touch_lvars child_scope.lvars if child_scope.level == current_level
|
241
346
|
child_scope.mergeable_changes.each do |name, (level, value)|
|
242
347
|
self[name] = value if level <= current_level
|
243
348
|
end
|