|
class Obj |
|
attr_reader :vtable |
|
|
|
def set_vtable(vtable) |
|
@vtable = vtable |
|
end |
|
|
|
def send(name, *args) |
|
method = vtable.bind(name) |
|
method.call(self, *args) |
|
end |
|
end |
|
|
|
class Sym < Obj |
|
SYMBOLS = {} |
|
|
|
class << self |
|
private :new |
|
|
|
def intern(sym, name) |
|
if SYMBOLS.has_key?(name) |
|
SYMBOLS[name] |
|
else |
|
SYMBOLS[name] = new(name) |
|
end |
|
end |
|
end |
|
|
|
attr_reader :name, :vtable |
|
|
|
def initialize(name) |
|
@vtable = ::SymbolVT |
|
@name = name.freeze |
|
end |
|
|
|
def hash |
|
@name.hash |
|
end |
|
|
|
def eql(other) |
|
@name.eql(other) |
|
end |
|
|
|
def inspect |
|
"#<Sym #{name}>" |
|
end |
|
alias to_s inspect |
|
end |
|
|
|
class VTable < Obj |
|
attr_reader :parent, :methods |
|
|
|
class << self |
|
def add_method(vt, name, body) |
|
vt.methods[name] = body |
|
end |
|
|
|
def delegated(parent) |
|
vt = VTable.new(parent) |
|
vt.set_vtable(VTableVT) |
|
|
|
vt |
|
end |
|
|
|
def alloc(vt, klass) |
|
o = klass.allocate |
|
o.set_vtable(vt) |
|
|
|
o |
|
end |
|
|
|
def lookup(vt, name) |
|
if vt.methods.has_key?(name) |
|
vt.methods[name] |
|
elsif parent |
|
vt.parent.send(::Sym.intern(nil, "lookup"), name) |
|
end |
|
end |
|
|
|
end |
|
|
|
def initialize(parent = nil) |
|
@methods = {} |
|
@parent = parent |
|
end |
|
|
|
def set_parent(parent) |
|
@parent = parent |
|
end |
|
|
|
def bind(name) |
|
if self == ::VTableVT && name == ::Sym.intern(nil, "lookup") |
|
methods[name] |
|
else |
|
send(::Sym.intern(nil, "lookup"), name) |
|
end |
|
end |
|
end |
|
|
|
VTableVT = VTable.new |
|
|
|
# Analogy: Class.class == Class |
|
VTableVT.set_vtable(VTableVT) |
|
|
|
|
|
ObjectVT = VTable.new |
|
|
|
# Analogy: Object.class == Class |
|
ObjectVT.set_vtable(VTableVT) |
|
|
|
# Analogy: Class.is_a?(Object) == true |
|
VTableVT.set_parent(ObjectVT) |
|
|
|
SymbolVT = VTable.delegated(ObjectVT) |
|
|
|
s_lookup = Sym.intern(nil, "lookup") |
|
VTable.add_method(VTableVT, s_lookup, ->(vt, name) { |
|
VTable.lookup(vt, name) |
|
}) |
|
|
|
s_add_method = Sym.intern(nil, "add_method") |
|
|
|
VTable.add_method(VTableVT, s_add_method, ->(vt, name, body) { |
|
VTable.add_method(vt, name, body) |
|
}) |
|
|
|
s_allocate = Sym.intern(nil, "allocate") |
|
|
|
VTableVT.send(s_add_method, s_allocate, ->(vt, klass) { |
|
VTable.alloc(vt, klass) |
|
}) |
|
|
|
s_intern = Sym.intern(nil, "intern") |
|
SymbolVT.send(s_add_method, s_intern, ->(sym, name) { |
|
Sym.intern(sym, name) |
|
}) |
|
|
|
symbol = SymbolVT.send(s_allocate, Sym) |
|
|
|
s_delegated = symbol.send(s_intern, "delegated") |
|
VTableVT.send(s_add_method, s_delegated, ->(vt) { |
|
VTable.delegated(vt) |
|
}) |
|
|
|
begin |
|
require 'pry' |
|
rescue LoadError |
|
require 'irb' |
|
IRB.start |
|
else |
|
binding.pry |
|
end |