lua_oop/oop/oop.lua

126 lines
2.8 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
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
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