local u = {} function u.wrongarg(n,expected,got) return 'arg '..n..' expected to be '..expected..' (got '..tostring(got)..')' end local wrongarg = u.wrongarg function u.isname(name) return type(name)=='string' and string.find(name,'^[_%a][_%w]*$') end local isname = u.isname function u.assert(value,errmsg,...) if value then return value else if type(errmsg)=='nil' then error('assertion failed!',2) elseif type(errmsg)=='string' then error(errmsg,2) else error(errmsg(unpack(arg)),2) end end end local assert = u.assert function u.fwrongarg(...) return function() return wrongarg(unpack(arg)) end end local fwrongarg = u.fwrongarg local INFO = '__info' function u.isobject(o) return type(o)=='table' and rawget(o,INFO) end local isobject = u.isobject local METAMETHODS = { '__tostring', '__add', '__sub', '__mul', '__div', '__pow', '__lt', '__le', '__eq', '__call', '__unm', '__concat', '__newindex', } local metatable = {} for _, name in ipairs(METAMETHODS) do local name = name metatable[name] = function(...) local a, b = unpack(arg) local f if isobject(a) then f = a[name] end if not f and isobject(b) then f = b[name] end if not f then local name = name..'__' if isobject(a) then f = a[name] end if not f and isobject(b) then f = b[name] end end assert(f, function() local class = rawget(a,INFO).__class local cname = rawget(class,INFO).__name return 'meta-method not found: '..cname..':'..name end) return f(unpack(arg)) end end local function table2object(t) assert(type(t)=='table', fwrongarg(1,'table',t)) local info = {} rawset(t,INFO,info) setmetatable(t,metatable) local p = newproxy(true) local mp = getmetatable(p) function mp:__gc() if rawget(t,INFO) == info then t:finalize() end end rawget(t,INFO).__proxy = p return t end local function object2table(o) assert(isobject(o), fwrongarg(1,'object',o)) setmetatable(o,nil) rawset(o,INFO,nil) return o end local function givename(o,name) assert(isobject(o), fwrongarg(1,'object',o)) assert(isname(name), fwrongarg(2,'name',name)) rawget(o,INFO).__name = name getfenv(2)[name] = o end local function setclass(o,class) assert(isobject(o), fwrongarg(1,'object',o)) assert(isobject(class), fwrongarg(2,'object',class)) rawget(o,INFO).__class = class end local function setsuper(class,superclass) assert(isobject(class), fwrongarg(1,'object',class)) assert(isobject(superclass), fwrongarg(2,'object',superclass)) rawget(class,INFO).__super = superclass end local function object2class(o,name) assert(isobject(o), fwrongarg(1,'object',o)) assert(isname(name), fwrongarg(2,'name',name)) givename(o,name) rawget(o,INFO).__methods = {} rawget(o,INFO).__isclass = true rawget(o,INFO).__cmethods = {} end local function findmethod(class,name,iscmethod) local storage = iscmethod and '__cmethods' or '__methods' while class do local info = rawget(class,INFO) value = info[storage][name] if value ~= nil then return value end class = info.__super end end function metatable:__index(name) local value if rawget(self,INFO).__isclass then value = findmethod(self,name,true) if value ~= nil then return value end end local class = rawget(self,INFO).__class value = findmethod(class,name) if value ~= nil then return value end if name ~= '__index' and name ~= '__index__' then local index = self.__index or self.__index__ if index then value = index(self,name) if value ~= nil then return value end end end end local _Object = table2object{} object2class(_Object,"Object") local _Class = table2object{} object2class(_Class,"Class") setclass(Object,Class) setclass(Class,Class) setsuper(Class,Object) local function makesupermethod(self,name,iscmethod) return function(...) local method local classinfo if iscmethod then classinfo = rawget(self,INFO) else local class = rawget(self,INFO).__class classinfo = rawget(class,INFO) end local super = classinfo.__super if super then method = findmethod(super,name,iscmethod) end assert(method, "no super method for "..classinfo.__name..":"..name) return method(self,unpack(arg)) end end local methodsmeta = {} function methodsmeta:__call(object,...) local env = getfenv(self.__f) local metafenv = { __newindex = env, __index = env, } local fenv = { super = makesupermethod(object,self.__name,self.__iscmethod), } setmetatable(fenv,metafenv) setfenv(self.__f,fenv) local result = {self.__f(object,unpack(arg))} setfenv(self.__f,env) return unpack(result) end local function storemethod(storage,name,iscmethod,method) if type(method) == 'function' then local t = { __name = name, __f = method, __iscmethod = iscmethod, } setmetatable(t,methodsmeta) storage[name] = t else storage[name] = method end end rawget(Class,INFO).__methods.__newindex = function(self,name,method) storemethod(rawget(self,INFO).__methods,name,false,method) end function Class:__call__(...) local instance = self:new(unpack(arg)) instance:initialize(unpack(arg)) instance.__cbody = nil local constructor = instance[rawget(self,INFO).__name] if constructor ~= nil then constructor(instance, unpack(arg)) constructor = nil end return instance end function Class:initialize(name,superclass) assert(isname(name), fwrongarg(1,'name',name)) object2class(self,name) superclass = superclass or Object assert(isobject(superclass), fwrongarg(2,'object',superclass)) setsuper(self,superclass or Object) end function Class:name() return rawget(self,INFO).__name end function Class:super() return rawget(self,INFO).__super end function Class:classtable() local t = {} local mt = {} function mt.__newindex(_,name,method) storemethod(rawget(self,INFO).__cmethods,name,true,method) end setmetatable(t,mt) return t end function Class:__tostring__() return self:name() end function Class:derives(class) local superclass = self:super() if superclass then return superclass == class or superclass:derives(class) end end function Class:adopt(t,initialize,...) assert(type(t)=='table', wrongarg(1,'table',t)) local o = table2object(t) setclass(o,self) if initialize then o:initialize(unpack(arg)) end return o end local Objectclass = Object:classtable() function Objectclass:new() local o = table2object{} setclass(o,self) return o end function Object:initialize() end function Object:finalize() end function Object:class() return rawget(self,INFO).__class end function Object:__eq__(other) return rawequal(self,other) end function Object:__newindex__(name,value) if self.__set_callback(self,name,value) == true then return end rawset(self,name,value) end function Object:instanceof(class) return self:class() == class end function Object:__set_callback(name,value) return false end function Object:inherits(class) local _class = self:class() return _class == class or _class:derives(class) end function Object:__tostring__() return 'instance of '..self:class():name() end function Object:__concat__(other) if isobject(self) then self = tostring(self) elseif isobject(other) then other = tostring(other) end return self..other end function Object:totable(finalize) if finalize then self:finalize() end setmetatable(self,nil) local info = rawget(self,INFO) rawset(self,INFO,nil) return self, info end function class(name) assert(isname(name), fwrongarg(1,'name',name)) local _class = Class(name) return function(superclass) assert(isobject(superclass), fwrongarg(1,'object',superclass)) setsuper(_class,superclass) end end classu = u