-- Class library -- (c) Er2 2022 -- Zlib License -- (Not) Virtual classes table local vtable = {} -- Metamethods local cls = {} cls.__index = cls -- :__tostring() - Calls when using Lua tostring() or error occurs. function cls:__tostring() local str = 'Class <'.. self.__name ..'>' while self.__super do str = str.. ' inherits '.. self.__super.__name self = self.__super end return str end -- :__call(table) - Calls when defining class body function cls:__call(t) for k, v in pairs(t) do self[k] = v end if self.__init then self:__init() end end -- :new(...) - Creates new instance of class. Please use new(...) function instead. -- Can throw error if no constructor defined in class or subclasses. function cls:new(...) local cl = self local cons repeat cons = rawget(cl, 1) or rawget(cl, 'new') cl = cl.__super until cons or not cl assert(cons, 'No constructor found in class') --self.__index = self.__index or cls.__index self = setmetatable({}, self) cons(self, ...) return self end -- :super(...) - Call parent constructor. function cls:super(...) return cls.superM(self, 1, ...) or cls.superM(self, 'new', ...) end -- :superM(method, ...) - Call method from parent class. -- Can throw error if class is not inherited. function cls:superM(meth, ...) local cl = self.__super local fn while not fn and cl do fn = cl[meth] if cl.__sup then fn = nil end cl = cl.__super end if not fn then return nil end if cl then cl.__sup = true end local v = {fn(self, ...)} if cl then cl.__sup = nil end return table.unpack(v) end -- :clone() - Make clone of class. function cls:clone() local cl = {} for k, v in pairs(cls) do cl[k] = v end for k, v in pairs(self) do cl[k] = v end cl.__index = cl cl.__super = self return setmetatable(cl, self) end -- :inherits(className) - Make this class inherits from another. -- Can throw error if class is already inherited. function cls:inherits(name) -- assert(self.__super, 'no') local cl = vtable[name] assert(cl, 'Class is not exists') return setmetatable(self, cl:clone()) end -- :extends(className) - Same as :inherits(className). cls.extends = cls.inherits -- class(className) - Make new class. -- Can throw error if class already exists with this name. function class(name) assert(not vtable[name], 'Cannot override defined class') local cl = setmetatable({__name = name, __tostring = cls.__tostring}, cls) cl.__index = cl vtable[name] = cl return cl end -- new(className) - Make new instance of class. Use this instead of :new(class). -- Can throw error if class by name was not found. function new(name) local cl = vtable[name] assert(cl, 'No class found') return function(...) return cls.new(cl, ...) end end