class.lua 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412
  1. local u = {}
  2. function u.wrongarg(n,expected,got)
  3. return 'arg '..n..' expected to be '..expected..' (got '..tostring(got)..')'
  4. end
  5. local wrongarg = u.wrongarg
  6. function u.isname(name)
  7. return type(name)=='string' and string.find(name,'^[_%a][_%w]*$')
  8. end
  9. local isname = u.isname
  10. function u.assert(value,errmsg,...)
  11. if value then
  12. return value
  13. else
  14. if type(errmsg)=='nil' then
  15. error('assertion failed!',2)
  16. elseif type(errmsg)=='string' then
  17. error(errmsg,2)
  18. else
  19. error(errmsg(unpack(arg)),2)
  20. end
  21. end
  22. end
  23. local assert = u.assert
  24. function u.fwrongarg(...)
  25. return function()
  26. return wrongarg(unpack(arg))
  27. end
  28. end
  29. local fwrongarg = u.fwrongarg
  30. local INFO = '__info'
  31. function u.isobject(o)
  32. return type(o)=='table' and rawget(o,INFO)
  33. end
  34. local isobject = u.isobject
  35. local METAMETHODS = {
  36. '__tostring',
  37. '__add',
  38. '__sub',
  39. '__mul',
  40. '__div',
  41. '__pow',
  42. '__lt',
  43. '__le',
  44. '__eq',
  45. '__call',
  46. '__unm',
  47. '__concat',
  48. '__newindex',
  49. }
  50. local metatable = {}
  51. for _, name in ipairs(METAMETHODS) do
  52. local name = name
  53. metatable[name] = function(...)
  54. local a, b = unpack(arg)
  55. local f
  56. if isobject(a) then
  57. f = a[name]
  58. end
  59. if not f and isobject(b) then
  60. f = b[name]
  61. end
  62. if not f then
  63. local name = name..'__'
  64. if isobject(a) then
  65. f = a[name]
  66. end
  67. if not f and isobject(b) then
  68. f = b[name]
  69. end
  70. end
  71. assert(f, function()
  72. local class = rawget(a,INFO).__class
  73. local cname = rawget(class,INFO).__name
  74. return 'meta-method not found: '..cname..':'..name
  75. end)
  76. return f(unpack(arg))
  77. end
  78. end
  79. local
  80. function table2object(t)
  81. assert(type(t)=='table', fwrongarg(1,'table',t))
  82. local info = {}
  83. rawset(t,INFO,info)
  84. setmetatable(t,metatable)
  85. local p = newproxy(true)
  86. local mp = getmetatable(p)
  87. function mp:__gc()
  88. if rawget(t,INFO) == info then
  89. t:finalize()
  90. end
  91. end
  92. rawget(t,INFO).__proxy = p
  93. return t
  94. end
  95. local
  96. function object2table(o)
  97. assert(isobject(o), fwrongarg(1,'object',o))
  98. setmetatable(o,nil)
  99. rawset(o,INFO,nil)
  100. return o
  101. end
  102. local
  103. function givename(o,name)
  104. assert(isobject(o), fwrongarg(1,'object',o))
  105. assert(isname(name), fwrongarg(2,'name',name))
  106. rawget(o,INFO).__name = name
  107. getfenv(2)[name] = o
  108. end
  109. local
  110. function setclass(o,class)
  111. assert(isobject(o), fwrongarg(1,'object',o))
  112. assert(isobject(class), fwrongarg(2,'object',class))
  113. rawget(o,INFO).__class = class
  114. end
  115. local
  116. function setsuper(class,superclass)
  117. assert(isobject(class), fwrongarg(1,'object',class))
  118. assert(isobject(superclass), fwrongarg(2,'object',superclass))
  119. rawget(class,INFO).__super = superclass
  120. end
  121. local
  122. function object2class(o,name)
  123. assert(isobject(o), fwrongarg(1,'object',o))
  124. assert(isname(name), fwrongarg(2,'name',name))
  125. givename(o,name)
  126. rawget(o,INFO).__methods = {}
  127. rawget(o,INFO).__isclass = true
  128. rawget(o,INFO).__cmethods = {}
  129. end
  130. local
  131. function findmethod(class,name,iscmethod)
  132. local storage = iscmethod and '__cmethods' or '__methods'
  133. while class do
  134. local info = rawget(class,INFO)
  135. value = info[storage][name]
  136. if value ~= nil then
  137. return value
  138. end
  139. class = info.__super
  140. end
  141. end
  142. function metatable:__index(name)
  143. local value
  144. if rawget(self,INFO).__isclass then
  145. value = findmethod(self,name,true)
  146. if value ~= nil then
  147. return value
  148. end
  149. end
  150. local class = rawget(self,INFO).__class
  151. value = findmethod(class,name)
  152. if value ~= nil then
  153. return value
  154. end
  155. if name ~= '__index' and name ~= '__index__' then
  156. local index = self.__index or self.__index__
  157. if index then
  158. value = index(self,name)
  159. if value ~= nil then
  160. return value
  161. end
  162. end
  163. end
  164. end
  165. local _Object = table2object{}
  166. object2class(_Object,"Object")
  167. local _Class = table2object{}
  168. object2class(_Class,"Class")
  169. setclass(Object,Class)
  170. setclass(Class,Class)
  171. setsuper(Class,Object)
  172. local
  173. function makesupermethod(self,name,iscmethod)
  174. return function(...)
  175. local method
  176. local classinfo
  177. if iscmethod then
  178. classinfo = rawget(self,INFO)
  179. else
  180. local class = rawget(self,INFO).__class
  181. classinfo = rawget(class,INFO)
  182. end
  183. local super = classinfo.__super
  184. if super then
  185. method = findmethod(super,name,iscmethod)
  186. end
  187. assert(method, "no super method for "..classinfo.__name..":"..name)
  188. return method(self,unpack(arg))
  189. end
  190. end
  191. local methodsmeta = {}
  192. function methodsmeta:__call(object,...)
  193. local env = getfenv(self.__f)
  194. local metafenv = {
  195. __newindex = env,
  196. __index = env,
  197. }
  198. local fenv = {
  199. super = makesupermethod(object,self.__name,self.__iscmethod),
  200. }
  201. setmetatable(fenv,metafenv)
  202. setfenv(self.__f,fenv)
  203. local result = {self.__f(object,unpack(arg))}
  204. setfenv(self.__f,env)
  205. return unpack(result)
  206. end
  207. local
  208. function storemethod(storage,name,iscmethod,method)
  209. if type(method) == 'function' then
  210. local t = {
  211. __name = name,
  212. __f = method,
  213. __iscmethod = iscmethod,
  214. }
  215. setmetatable(t,methodsmeta)
  216. storage[name] = t
  217. else
  218. storage[name] = method
  219. end
  220. end
  221. rawget(Class,INFO).__methods.__newindex =
  222. function(self,name,method)
  223. storemethod(rawget(self,INFO).__methods,name,false,method)
  224. end
  225. function Class:__call__(...)
  226. local instance = self:new(unpack(arg))
  227. instance:initialize(unpack(arg))
  228. instance.__cbody = nil
  229. local constructor = instance[rawget(self,INFO).__name]
  230. if constructor ~= nil then
  231. constructor(instance, unpack(arg))
  232. constructor = nil
  233. end
  234. return instance
  235. end
  236. function Class:initialize(name,superclass)
  237. assert(isname(name), fwrongarg(1,'name',name))
  238. object2class(self,name)
  239. superclass = superclass or Object
  240. assert(isobject(superclass), fwrongarg(2,'object',superclass))
  241. setsuper(self,superclass or Object)
  242. end
  243. function Class:name()
  244. return rawget(self,INFO).__name
  245. end
  246. function Class:super()
  247. return rawget(self,INFO).__super
  248. end
  249. function Class:classtable()
  250. local t = {}
  251. local mt = {}
  252. function mt.__newindex(_,name,method)
  253. storemethod(rawget(self,INFO).__cmethods,name,true,method)
  254. end
  255. setmetatable(t,mt)
  256. return t
  257. end
  258. function Class:__tostring__()
  259. return self:name()
  260. end
  261. function Class:derives(class)
  262. local superclass = self:super()
  263. if superclass then
  264. return superclass == class or superclass:derives(class)
  265. end
  266. end
  267. function Class:adopt(t,initialize,...)
  268. assert(type(t)=='table', wrongarg(1,'table',t))
  269. local o = table2object(t)
  270. setclass(o,self)
  271. if initialize then
  272. o:initialize(unpack(arg))
  273. end
  274. return o
  275. end
  276. local Objectclass = Object:classtable()
  277. function Objectclass:new()
  278. local o = table2object{}
  279. setclass(o,self)
  280. return o
  281. end
  282. function Object:initialize()
  283. end
  284. function Object:finalize()
  285. end
  286. function Object:class()
  287. return rawget(self,INFO).__class
  288. end
  289. function Object:__eq__(other)
  290. return rawequal(self,other)
  291. end
  292. function Object:__newindex__(name,value)
  293. if self.__set_callback(self,name,value) == true then
  294. return
  295. end
  296. rawset(self,name,value)
  297. end
  298. function Object:instanceof(class)
  299. return self:class() == class
  300. end
  301. function Object:__set_callback(name,value)
  302. return false
  303. end
  304. function Object:inherits(class)
  305. local _class = self:class()
  306. return _class == class or _class:derives(class)
  307. end
  308. function Object:__tostring__()
  309. return 'instance of '..self:class():name()
  310. end
  311. function Object:__concat__(other)
  312. if isobject(self) then
  313. self = tostring(self)
  314. elseif isobject(other) then
  315. other = tostring(other)
  316. end
  317. return self..other
  318. end
  319. function Object:totable(finalize)
  320. if finalize then
  321. self:finalize()
  322. end
  323. setmetatable(self,nil)
  324. local info = rawget(self,INFO)
  325. rawset(self,INFO,nil)
  326. return self, info
  327. end
  328. function class(name)
  329. assert(isname(name), fwrongarg(1,'name',name))
  330. local _class = Class(name)
  331. return function(superclass)
  332. assert(isobject(superclass), fwrongarg(1,'object',superclass))
  333. setsuper(_class,superclass)
  334. end
  335. end
  336. classu = u