147 lines
3.5 KiB
Lua
147 lines
3.5 KiB
Lua
local oop = {}
|
|
|
|
local function super_call(self, method_name, ...)
|
|
local class = getmetatable(self)
|
|
if not class then
|
|
error("super() called on non-instance")
|
|
end
|
|
local base = rawget(class, "__base")
|
|
if not base then
|
|
error("super() called but no base class")
|
|
end
|
|
local fn = base[method_name]
|
|
if not fn then
|
|
error("base class has no method '" .. tostring(method_name) .. "'")
|
|
end
|
|
return fn(self, ...)
|
|
end
|
|
|
|
local function_def_cache = setmetatable({}, { __mode = "k" })
|
|
|
|
function oop.setattr(target, key, value)
|
|
target[key] = value
|
|
end
|
|
|
|
function oop.new(class, ...)
|
|
local actual_class = class
|
|
if type(class) == "function" then
|
|
actual_class = function_def_cache[class]
|
|
if not actual_class then
|
|
actual_class = oop.class(class)
|
|
function_def_cache[class] = actual_class
|
|
end
|
|
end
|
|
local obj = setmetatable({}, actual_class)
|
|
if actual_class.__init then
|
|
actual_class.__init(obj, ...)
|
|
end
|
|
return obj
|
|
end
|
|
|
|
local function lookup_in_class(cls, key)
|
|
local cur = cls
|
|
while cur do
|
|
local val = rawget(cur, key)
|
|
if val ~= nil then
|
|
return val
|
|
end
|
|
cur = rawget(cur, "__base")
|
|
end
|
|
return nil
|
|
end
|
|
|
|
function oop.class(def, base)
|
|
local cls = {}
|
|
cls.__name = "Anonymous"
|
|
cls.__base = base
|
|
cls.__index = function(self, key)
|
|
local val = lookup_in_class(cls, key)
|
|
if type(val) == "function" then
|
|
return function(...)
|
|
return val(self, ...)
|
|
end
|
|
end
|
|
return val
|
|
end
|
|
|
|
function cls:super(method_name, ...)
|
|
return super_call(self, method_name, ...)
|
|
end
|
|
function cls.setattr(target, key, value)
|
|
return oop.setattr(target, key, value)
|
|
end
|
|
function cls.inherit(parent)
|
|
local actual_parent = parent
|
|
if type(parent) == "function" then
|
|
actual_parent = function_def_cache[parent]
|
|
if not actual_parent then
|
|
actual_parent = oop.class(parent)
|
|
function_def_cache[parent] = actual_parent
|
|
end
|
|
end
|
|
cls.__base = actual_parent
|
|
cls.super = actual_parent
|
|
local mt = getmetatable(cls) or {}
|
|
mt.__index = actual_parent
|
|
mt.__call = mt.__call or function(c, ...)
|
|
return oop.new(c, ...)
|
|
end
|
|
setmetatable(cls, mt)
|
|
end
|
|
|
|
setmetatable(cls, {
|
|
__index = base,
|
|
__call = function(c, ...)
|
|
return oop.new(c, ...)
|
|
end
|
|
})
|
|
|
|
if type(def) == "string" then
|
|
cls.__name = def
|
|
elseif type(def) == "function" then
|
|
def(cls)
|
|
elseif def ~= nil then
|
|
error("class definition must be a function or name string")
|
|
end
|
|
if base then
|
|
cls.inherit(base)
|
|
end
|
|
|
|
return cls
|
|
end
|
|
|
|
function oop.isinstance(obj, class)
|
|
local mt = getmetatable(obj)
|
|
while mt do
|
|
if mt == class then
|
|
return true
|
|
end
|
|
mt = rawget(mt, "__base")
|
|
end
|
|
return false
|
|
end
|
|
|
|
function oop.issubclass(class, base)
|
|
local mt = class
|
|
while mt do
|
|
if mt == base then
|
|
return true
|
|
end
|
|
mt = rawget(mt, "__base")
|
|
end
|
|
return false
|
|
end
|
|
|
|
function oop.install(env)
|
|
local target = env or _G
|
|
target.class = oop.class
|
|
target.new = oop.new
|
|
target.setattr = oop.setattr
|
|
target.isinstance = oop.isinstance
|
|
target.issubclass = oop.issubclass
|
|
end
|
|
|
|
oop.install()
|
|
|
|
return oop
|