| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412 |
- 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
|