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) cls.__base = parent cls.super = parent local mt = getmetatable(cls) or {} mt.__index = 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