katakata_irb 0.1.12 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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