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.
@@ -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
- def initialize(binding, self_object)
14
- @binding, @self_object = binding, self_object
15
- @cache = { SELF => KatakataIrb::Types.type_from_object(self_object) }
16
- @local_variables = binding.local_variables.map(&:to_s).to_set
17
- @global_variables = global_variables.map(&:to_s).to_set
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(name)
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
- self[SELF]
76
+ KatakataIrb::Types.type_from_object @self_object
48
77
  end
49
78
 
50
- def local_variables
51
- @local_variables.to_a
52
- end
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, :jump_branches, :mergeable_changes, :level, :lvars
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 = {}, trace_cvar: true, trace_ivar: true, trace_lvar: true, passthrough: false)
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 + (passthrough ? 0 : 1)
106
- @trace_cvar = trace_cvar
117
+ @level = parent.level + 1
107
118
  @trace_ivar = trace_ivar
108
119
  @trace_lvar = trace_lvar
109
- @passthrough = passthrough
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
- @lvars = Set.new
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
- @changes = @mergeable_changes.dup
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 == :cvar ? @trace_cvar : type == :ivar ? @trace_ivar : type == :lvar ? @trace_lvar : true
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 level_of(name)
153
- variable_level, = @changes[name]
154
- variable_level || parent.level_of(name)
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
- level, value = @changes[name]
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] if trace? 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
- variable_level = level_of(name) || level
168
- @lvars << name if level == variable_level && BaseScope.type_by_name(name) == :lvar
169
- @changes[name] = [variable_level, value]
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
- self[SELF]
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 = @changes.keys.select do |name|
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
- @changes = @mergeable_changes
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, RAISE_BREAK => nil }, passthrough: true)
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, passthrough: true
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
- @changes.key? name
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