You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

166 lines
3.1 KiB
Lua

local format = string.format
local meta = {}
local names = {}
local classes = {}
local objects = setmetatable({}, {__mode = 'k'})
function meta:__call(...)
local obj = setmetatable({}, self)
objects[obj] = true
obj:__init(...)
return obj
end
function meta:__tostring()
return 'class ' .. self.__name
end
local default = {}
function default:__tostring()
return self.__name
end
function default:__hash()
return self
end
local function isClass(cls)
return classes[cls]
end
local function isObject(obj)
return objects[obj]
end
local function isSubclass(sub, cls)
if isClass(sub) and isClass(cls) then
if sub == cls then
return true
else
for _, base in ipairs(sub.__bases) do
if isSubclass(base, cls) then
return true
end
end
end
end
return false
end
local function isInstance(obj, cls)
return isObject(obj) and isSubclass(obj.__class, cls)
end
local function profile()
local ret = setmetatable({}, {__index = function() return 0 end})
for obj in pairs(objects) do
local name = obj.__name
ret[name] = ret[name] + 1
end
return ret
end
local types = {['string'] = true, ['number'] = true, ['boolean'] = true}
local function _getPrimitive(v)
return types[type(v)] and v or v ~= nil and tostring(v) or nil
end
local function serialize(obj)
if isObject(obj) then
local ret = {}
for k, v in pairs(obj.__getters) do
ret[k] = _getPrimitive(v(obj))
end
return ret
else
return _getPrimitive(obj)
end
end
local rawtype = type
local function type(obj)
return isObject(obj) and obj.__name or rawtype(obj)
end
return setmetatable({
classes = names,
isClass = isClass,
isObject = isObject,
isSubclass = isSubclass,
isInstance = isInstance,
type = type,
profile = profile,
serialize = serialize,
}, {__call = function(_, name, ...)
if names[name] then return error(format('Class %q already defined', name)) end
local class = setmetatable({}, meta)
classes[class] = true
for k, v in pairs(default) do
class[k] = v
end
local bases = {...}
local getters = {}
local setters = {}
for _, base in ipairs(bases) do
for k1, v1 in pairs(base) do
class[k1] = v1
for k2, v2 in pairs(base.__getters) do
getters[k2] = v2
end
for k2, v2 in pairs(base.__setters) do
setters[k2] = v2
end
end
end
class.__name = name
class.__class = class
class.__bases = bases
class.__getters = getters
class.__setters = setters
local pool = {}
local n = #pool
function class:__index(k)
if getters[k] then
return getters[k](self)
elseif pool[k] then
return rawget(self, pool[k])
else
return class[k]
end
end
function class:__newindex(k, v)
if setters[k] then
return setters[k](self, v)
elseif class[k] or getters[k] then
return error(format('Cannot overwrite protected property: %s.%s', name, k))
elseif k:find('_', 1, true) ~= 1 then
return error(format('Cannot write property to object without leading underscore: %s.%s', name, k))
else
if not pool[k] then
n = n + 1
pool[k] = n
end
return rawset(self, pool[k], v)
end
end
names[name] = class
return class, getters, setters
end})