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.
@@ -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
- 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
+ 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
- self[SELF]
67
+ KatakataIrb::Types.type_from_object @self_object
48
68
  end
49
69
 
50
- def local_variables
51
- @local_variables.to_a
52
- end
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, :jump_branches, :mergeable_changes, :level, :lvars
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, passthrough: false)
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 + (passthrough ? 0 : 1)
106
+ @level = parent.level + 1
106
107
  @trace_cvar = trace_cvar
107
108
  @trace_ivar = trace_ivar
108
109
  @trace_lvar = trace_lvar
109
- @passthrough = passthrough
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
- @lvars = Set.new
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
- @changes = @mergeable_changes.dup
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
- variable_level, = @changes[name]
154
- variable_level || parent.level_of(name)
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
- level, value = @changes[name]
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] if trace? 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
- 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]
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
- self[SELF]
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 = @changes.keys.select do |name|
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
- @changes = @mergeable_changes
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, 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 }
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, passthrough: true
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
- @changes.key? name
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