Class: ReplTypeCompletor::Scope

Inherits:
Object
  • Object
show all
Defined in:
lib/repl_type_completor/scope.rb

Constant Summary collapse

BREAK_RESULT =
'%break'
NEXT_RESULT =
'%next'
RETURN_RESULT =
'%return'
PATTERNMATCH_BREAK =
'%match'

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(parent, table = {}, trace_ivar: true, trace_lvar: true, self_type: nil, nesting: nil) ⇒ Scope

Returns a new instance of Scope.



113
114
115
116
117
118
119
120
121
122
123
# File 'lib/repl_type_completor/scope.rb', line 113

def initialize(parent, table = {}, trace_ivar: true, trace_lvar: true, self_type: nil, nesting: nil)
  @parent = parent
  @level = parent.level + 1
  @trace_ivar = trace_ivar
  @trace_lvar = trace_lvar
  @module_nesting = nesting ? [nesting, *parent.module_nesting] : parent.module_nesting
  @self_type = self_type
  @terminated = false
  @jump_branches = []
  @mergeable_changes = @table = table.transform_values { [level, _1] }
end

Instance Attribute Details

#levelObject (readonly)

Returns the value of attribute level.



109
110
111
# File 'lib/repl_type_completor/scope.rb', line 109

def level
  @level
end

#mergeable_changesObject (readonly)

Returns the value of attribute mergeable_changes.



109
110
111
# File 'lib/repl_type_completor/scope.rb', line 109

def mergeable_changes
  @mergeable_changes
end

#module_nestingObject (readonly)

Returns the value of attribute module_nesting.



109
110
111
# File 'lib/repl_type_completor/scope.rb', line 109

def module_nesting
  @module_nesting
end

#parentObject (readonly)

Returns the value of attribute parent.



109
110
111
# File 'lib/repl_type_completor/scope.rb', line 109

def parent
  @parent
end

Class Method Details

.from_binding(binding, locals) ⇒ Object



111
# File 'lib/repl_type_completor/scope.rb', line 111

def self.from_binding(binding, locals) = new(RootScope.new(binding, binding.receiver, locals))

Instance Method Details

#[](name) ⇒ Object



182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
# File 'lib/repl_type_completor/scope.rb', line 182

def [](name)
  type = RootScope.type_by_name(name)
  if type == :const
    return get_const(nil, nil, name) || Types::NIL if name.include?('::')

    module_nesting.each do |(nesting, path)|
      value = get_const nesting, [*path, name]
      return value if value
    end
    return Types::NIL
  elsif type == :cvar
    return get_cvar(nil, nil, nil, name) if name.include?('::')

    nesting, path = module_nesting.first
    return get_cvar(nesting, path, name)
  end
  level, value = @table[name]
  if level
    value
  elsif trace? name
    @parent[name]
  elsif type == :ivar
    self_instance_variable_get name
  end
end

#[]=(name, value) ⇒ Object



218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
# File 'lib/repl_type_completor/scope.rb', line 218

def []=(name, value)
  type = RootScope.type_by_name(name)
  if type == :const
    if name.include?('::')
      @table[name] = [0, value]
    else
      parent_module, parent_path = module_nesting.first
      set_const parent_module, [*parent_path, name], value
    end
    return
  elsif type == :cvar
    if name.include?('::')
      @table[name] = [0, value]
    else
      parent_module, parent_path = module_nesting.first
      set_cvar parent_module, parent_path, name, value
    end
    return
  end
  variable_level = level_of name, type
  @table[name] = [variable_level, value] if variable_level
end

#base_scopeObject



276
277
278
# File 'lib/repl_type_completor/scope.rb', line 276

def base_scope
  @parent.mutable? ? @parent.base_scope : @parent
end

#class_variablesObject



316
317
318
319
320
321
# File 'lib/repl_type_completor/scope.rb', line 316

def class_variables
  cvars = table_class_variables
  m, = module_nesting.first
  cvars |= m.class_variables.map(&:to_s) if m.is_a? Module
  cvars
end

#conditional(&block) ⇒ Object



340
341
342
# File 'lib/repl_type_completor/scope.rb', line 340

def conditional(&block)
  run_branches(block, ->(_s) {}).first || Types::NIL
end

#constantsObject



323
324
325
326
327
# File 'lib/repl_type_completor/scope.rb', line 323

def constants
  module_nesting.flat_map do |nest,|
    nest.constants
  end.map(&:to_s) | table_constants
end

#get_const(nesting, path, key = nil) ⇒ Object



170
171
172
173
174
# File 'lib/repl_type_completor/scope.rb', line 170

def get_const(nesting, path, key = nil)
  key ||= [nesting.__id__, path].join('::')
  _l, value = @table[key]
  value || @parent.get_const(nesting, path, key)
end

#get_cvar(nesting, path, name, key = nil) ⇒ Object



176
177
178
179
180
# File 'lib/repl_type_completor/scope.rb', line 176

def get_cvar(nesting, path, name, key = nil)
  key ||= [name, nesting.__id__, path].join('::')
  _l, value = @table[key]
  value || @parent.get_cvar(nesting, path, name, key)
end

#global_variablesObject



245
246
247
248
249
250
# File 'lib/repl_type_completor/scope.rb', line 245

def global_variables
  gvar_keys = @table.keys.select do |name|
    RootScope.type_by_name(name) == :gvar
  end
  gvar_keys | @parent.global_variables
end

#has_own?(name) ⇒ Boolean

Returns:

  • (Boolean)


363
364
365
# File 'lib/repl_type_completor/scope.rb', line 363

def has_own?(name)
  @table.key? name
end

#instance_variablesObject



286
287
288
289
290
291
292
293
294
# File 'lib/repl_type_completor/scope.rb', line 286

def instance_variables
  modules = self_type.types.grep(Types::SingletonType).map(&:module_or_class)
  instances = self_type.types.grep(Types::InstanceType).filter_map(&:instances).flatten(1)
  self_objects = modules + instances
  [
    self_objects.flat_map { Methods::OBJECT_INSTANCE_VARIABLES_METHOD.bind_call(_1).map(&:to_s) },
    table_instance_variables
  ].inject([], :|)
end

#level_of(name, var_type) ⇒ Object



159
160
161
162
163
164
165
166
167
168
# File 'lib/repl_type_completor/scope.rb', line 159

def level_of(name, var_type)
  case var_type
  when :ivar
    return level unless @trace_ivar
  when :gvar
    return 0
  end
  variable_level, = @table[name]
  variable_level || parent.level_of(name, var_type)
end

#local_variablesObject



252
253
254
255
256
257
258
# File 'lib/repl_type_completor/scope.rb', line 252

def local_variables
  lvar_keys = @table.keys.select do |name|
    RootScope.type_by_name(name) == :lvar
  end
  lvar_keys |= @parent.local_variables if @trace_lvar
  lvar_keys
end

#merge_jumpsObject



329
330
331
332
333
334
335
336
337
338
# File 'lib/repl_type_completor/scope.rb', line 329

def merge_jumps
  if terminated?
    @terminated = false
    @table = @mergeable_changes
    merge @jump_branches
    @terminated = true
  else
    merge [*@jump_branches, {}]
  end
end

#mutable?Boolean

Returns:

  • (Boolean)


125
# File 'lib/repl_type_completor/scope.rb', line 125

def mutable? = true

#never(&block) ⇒ Object



344
345
346
# File 'lib/repl_type_completor/scope.rb', line 344

def never(&block)
  block.call Scope.new(self, { BREAK_RESULT => nil, NEXT_RESULT => nil, PATTERNMATCH_BREAK => nil, RETURN_RESULT => nil })
end

#run_branches(*blocks) ⇒ Object



348
349
350
351
352
353
354
355
356
357
358
359
360
361
# File 'lib/repl_type_completor/scope.rb', line 348

def run_branches(*blocks)
  results = []
  branches = []
  blocks.each do |block|
    scope = Scope.new self
    result = block.call scope
    next if scope.terminated?
    results << result
    branches << scope.mergeable_changes
  end
  terminate if branches.empty?
  merge branches
  results
end

#self_instance_variable_get(name) ⇒ Object



296
297
298
299
300
301
302
303
304
305
306
307
308
# File 'lib/repl_type_completor/scope.rb', line 296

def self_instance_variable_get(name)
  modules = self_type.types.grep(Types::SingletonType).map(&:module_or_class)
  instances = self_type.types.grep(Types::InstanceType).filter_map(&:instances).flatten(1)
  self_objects = modules + instances
  types = self_objects.map do |object|
    value = begin
      Methods::OBJECT_INSTANCE_VARIABLE_GET_METHOD.bind_call(object, name)
    rescue NameError
    end
    Types.type_from_object value
  end
  Types::UnionType[*types]
end

#self_typeObject



241
242
243
# File 'lib/repl_type_completor/scope.rb', line 241

def self_type
  @self_type || @parent.self_type
end

#set_const(nesting, path, value) ⇒ Object



208
209
210
211
# File 'lib/repl_type_completor/scope.rb', line 208

def set_const(nesting, path, value)
  key = [nesting.__id__, path].join('::')
  @table[key] = [0, value]
end

#set_cvar(nesting, path, name, value) ⇒ Object



213
214
215
216
# File 'lib/repl_type_completor/scope.rb', line 213

def set_cvar(nesting, path, name, value)
  key = [name, nesting.__id__, path].join('::')
  @table[key] = [0, value]
end

#store_jump(type, value, changes) ⇒ Object



137
138
139
140
141
142
143
144
145
# File 'lib/repl_type_completor/scope.rb', line 137

def store_jump(type, value, changes)
  return if terminated?
  if has_own?(type)
    changes[type] = [level, value]
    @jump_branches << changes
  elsif @parent.mutable?
    @parent.store_jump(type, value, changes)
  end
end

#table_class_variablesObject



310
311
312
313
314
# File 'lib/repl_type_completor/scope.rb', line 310

def table_class_variables
  cvars = @table.keys.filter_map { _1.split('::', 2).first if RootScope.type_by_name(_1) == :cvar }
  cvars |= @parent.table_class_variables if @parent.mutable?
  cvars
end

#table_constantsObject



260
261
262
263
264
265
266
267
# File 'lib/repl_type_completor/scope.rb', line 260

def table_constants
  constants = module_nesting.flat_map do |mod, path|
    prefix = [mod.__id__, *path].join('::') + '::'
    @table.keys.select { _1.start_with? prefix }.map { _1.delete_prefix(prefix).split('::').first }
  end.uniq
  constants |= @parent.table_constants if @parent.mutable?
  constants
end

#table_instance_variablesObject



280
281
282
283
284
# File 'lib/repl_type_completor/scope.rb', line 280

def table_instance_variables
  ivars = @table.keys.select { RootScope.type_by_name(_1) == :ivar }
  ivars |= @parent.table_instance_variables if @parent.mutable? && @trace_ivar
  ivars
end

#table_module_constants(mod) ⇒ Object



269
270
271
272
273
274
# File 'lib/repl_type_completor/scope.rb', line 269

def table_module_constants(mod)
  prefix = "#{mod.__id__}::"
  constants = @table.keys.select { _1.start_with? prefix }.map { _1.delete_prefix(prefix).split('::').first }
  constants |= @parent.table_constants if @parent.mutable?
  constants
end

#terminateObject



147
148
149
150
151
# File 'lib/repl_type_completor/scope.rb', line 147

def terminate
  return if terminated?
  @terminated = true
  @table = @mergeable_changes.dup
end

#terminate_with(type, value) ⇒ Object



131
132
133
134
135
# File 'lib/repl_type_completor/scope.rb', line 131

def terminate_with(type, value)
  return if terminated?
  store_jump type, value, @mergeable_changes
  terminate
end

#terminated?Boolean

Returns:

  • (Boolean)


127
128
129
# File 'lib/repl_type_completor/scope.rb', line 127

def terminated?
  @terminated
end

#trace?(name) ⇒ Boolean

Returns:

  • (Boolean)


153
154
155
156
157
# File 'lib/repl_type_completor/scope.rb', line 153

def trace?(name)
  return false unless @parent
  type = RootScope.type_by_name(name)
  type == :ivar ? @trace_ivar : type == :lvar ? @trace_lvar : true
end

#update(child_scope) ⇒ Object



367
368
369
370
371
372
# File 'lib/repl_type_completor/scope.rb', line 367

def update(child_scope)
  current_level = level
  child_scope.mergeable_changes.each do |name, (level, value)|
    self[name] = value if level <= current_level
  end
end