katakata_irb 0.1.12 → 0.2.1

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