#!/usr/bin/env lua5.0 -- @file obj.lua: Object/Class model local function getuid(t) local str = tostring(t) local _,_,cap = string.find(str, '[^:]*: 0x(.*)$') return string.rep('0', 8 - string.len(cap)) .. cap end local Factory do local function new(obj, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20) local t = {} obj:init(t, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20) return t end local function createnew(self, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20) local o = self.prototype return new(o, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20) end function Factory(obj, newobj, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20) local t = new(obj, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20) t.prototype = newobj t.new = createnew getmetatable(t).__call = t.new return t end end local function objtostring(obj) return obj.tostring and obj:tostring() end local Object do Object = {} function Object:init(newobject, class) local parent = class or self if not rawget(newobject, 'uid') then newobject.uid = getuid(newobject) end local mt = { __index = parent, __tostring = objtostring, } setmetatable(newobject, mt) end -- function Object:tostring() -- return 'Object' -- end -- Object:init(Object) Object.uid = getuid(Object) setmetatable(Object, { __tostring = function() return 'Object' end }) end local Class do Class = Factory(Object, setmetatable({}, {__index = Object}), Object) assert(Class ~= Object) assert(Class ~= Class.prototype) assert(getmetatable(Class).__index == Object) assert(getmetatable(Class.prototype).__index == Object) local function protostring(t) if t.tostring then return t:tostring() else return '<' .. tostring(t.class) .. ' prototype>' end end local function classobjectstring(t) if t.tostring then return t:tostring() else return '<' .. tostring(t.class) .. ' instance>' end end function Class:new(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20) local o = self.prototype local newobj = {} if o.class and o.class.instancemeta then setmetatable(newobj, o.class.instancemeta) else Object:init(newobj, o) end o:init(newobj, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20) return newobj end function Class:init(newclass, parent, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20) parent = parent or self -- Object:init(newclass, parent) newclass.protometa = { __index = parent.prototype, __tostring = protostring, } newclass.prototype = {class = newclass} setmetatable(newclass.prototype, newclass.protometa) -- Object:init(newclass.prototype, parent.prototype) newclass.instancemeta = { __index = newclass.prototype, __tostring = classobjectstring, } -- setmetatable(newclass, {__index = parent, __tostring = objtostring}) newclass.mixins = {a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20} newclass.mixinmethods = setmetatable({}, {__index = parent}) setmetatable(newclass, {__index = newclass.mixinmethods, __tostring = objtostring}) -- Fold in the mixins local err for k,v in ipairs(newclass.mixins) do local i = rawget(v, 'init') if i and type(i) == 'function' then i(newclass) end for _,k2,v2 in v:mixinpairs() do if newclass[k2] then err = true else newclass[k2] = v2 end end end if err == true then local mm = newclass.mixinmethods for k,v in pairs(mm) do mm[k] = nil end -- method conflict error('Method conflict in attempt to mix in.', 4) end end function Class:tostring() return 'Class' end function Class.prototype:init(newobj) end local testobj = Class:new() assert(testobj ~= Class) assert(getmetatable(testobj).__index == Class.prototype) end local ClassFactory do ClassFactory = Factory(Object, Class, Object) assert(ClassFactory ~= Object) assert(ClassFactory ~= Class) assert(ClassFactory.prototype == Class) assert(getmetatable(ClassFactory).__index == Object) assert(getmetatable(ClassFactory.prototype).__index == Object) end local Mixin do Mixin = ClassFactory:new() local function mixinpairs(self) return function(s, k) local n = next(self.interface, k) return n, self[n] end, self.interface, nil end local function mixinpairs(self) return function(s, k) local n = next(s, k) -- print(s,k,n,s[n],self[s[n]]) return n, s[n], self[s[n]] end, self.interface, nil end function Mixin.prototype:init(newobj, interface) Class.prototype:init(newobj) newobj.interface = interface newobj.mixinpairs = mixinpairs end end local testclass = ClassFactory:new() assert(testclass ~= ClassFactory) -- assert(getmetatable(testclass).__index == Class) assert(getmetatable(testclass.prototype).__index == Class.prototype) assert(testclass.init == Class.init) assert(testclass.prototype.class == testclass) function testclass:tostring() return 'testclass' end function testclass.prototype:tostring() return '' end local testobj = testclass:new() assert(testobj ~= testclass) assert(getmetatable(testobj).__index == testclass.prototype) Rectangle = ClassFactory:new() function Rectangle:tostring() return 'Rectangle' end function Rectangle.prototype:init(newobj, arg) Class.prototype:init(newobj) if arg then newobj.w = arg.w or arg[1] newobj.h = arg.h or arg[2] end end function Rectangle.prototype:tostring() return string.format('<%s h=%s w=%s>', tostring(self.class), tostring(self.h), tostring(self.w)) end local rect = Rectangle:new({w = 5, h = 6}) assert(Rectangle.protometa.__index == Class.prototype) assert(getmetatable(rect).__index == Rectangle.prototype) assert(tostring(rect) == '') Square = ClassFactory:new(Rectangle) -- assert(getmetatable(Square).__index == Rectangle) assert(getmetatable(Square.prototype).__index == Rectangle.prototype) function Square:tostring() return 'Square' end function Square.prototype:init(newobj, arg) Rectangle.prototype:init(newobj, {h=arg, w=arg}) end assert(Square.prototype.class == Square) assert(Square.prototype.tostring == Rectangle.prototype.tostring) local sq = Square:new(5) assert(Square.protometa.__index == Rectangle.prototype) assert(getmetatable(sq) == Square.instancemeta) assert(tostring(Square) == 'Square') assert(tostring(sq) == '') local a = Mixin:new({'a'}) function a:init() print('a:init()') end function a:a() end local b = Mixin:new({'b'}) function b:init() print('b:init()') end function b:b() end local c = Mixin:new({'c'}) function c:init() print('c:init()') end function c:c() end local e = ClassFactory:new() e.e = true local f = ClassFactory:new(e, a, b, c) assert(f.a == a.a) assert(f.b == b.b) assert(f.c == c.c) assert(f.e == true) local d = Mixin:new({'c'}) function d.c() end local g = ClassFactory:new(e, a, b, c, d)