Overhaul the concept of lua 'class' initialization yet again.
parent
a7998f71a2
commit
57b72831ca
@ -0,0 +1,150 @@
|
||||
-- A trivial reloadable class system
|
||||
|
||||
local _ENV = mkmodule('class')
|
||||
|
||||
-- Metatable template for a class
|
||||
class_obj = {} or class_obj
|
||||
|
||||
-- Methods shared by all classes
|
||||
common_methods = {} or common_methods
|
||||
|
||||
-- Forbidden names for class fields and methods.
|
||||
reserved_names = { super = true, ATTRS = true }
|
||||
|
||||
-- Attribute table metatable
|
||||
attrs_meta = {} or attrs_meta
|
||||
|
||||
-- Create or updates a class; a class has metamethods and thus own metatable.
|
||||
function defclass(class,parent)
|
||||
class = class or {}
|
||||
|
||||
local meta = getmetatable(class)
|
||||
if not meta then
|
||||
meta = {}
|
||||
setmetatable(class, meta)
|
||||
end
|
||||
|
||||
for k,v in pairs(class_obj) do meta[k] = v end
|
||||
|
||||
meta.__index = parent or common_methods
|
||||
|
||||
local attrs = rawget(class, 'ATTRS') or {}
|
||||
setmetatable(attrs, attrs_meta)
|
||||
|
||||
rawset(class, 'super', parent)
|
||||
rawset(class, 'ATTRS', attrs)
|
||||
rawset(class, '__index', rawget(class, '__index') or class)
|
||||
|
||||
return class
|
||||
end
|
||||
|
||||
-- An instance uses the class as metatable
|
||||
function mkinstance(class,table)
|
||||
table = table or {}
|
||||
setmetatable(table, class)
|
||||
return table
|
||||
end
|
||||
|
||||
-- Patch the stubs in the global environment
|
||||
dfhack.BASE_G.defclass = _ENV.defclass
|
||||
dfhack.BASE_G.mkinstance = _ENV.mkinstance
|
||||
|
||||
-- Just verify the name, and then set.
|
||||
function class_obj:__newindex(name,val)
|
||||
if reserved_names[name] or common_methods[name] then
|
||||
error('Method name '..name..' is reserved.')
|
||||
end
|
||||
rawset(self, name, val)
|
||||
end
|
||||
|
||||
function attrs_meta:__call(attrs)
|
||||
for k,v in pairs(attrs) do
|
||||
self[k] = v
|
||||
end
|
||||
end
|
||||
|
||||
local function apply_attrs(obj, attrs, init_table)
|
||||
for k,v in pairs(attrs) do
|
||||
if v == DEFAULT_NIL then
|
||||
v = nil
|
||||
end
|
||||
obj[k] = init_table[k] or v
|
||||
end
|
||||
end
|
||||
|
||||
local function invoke_before_rec(self, class, method, ...)
|
||||
local meta = getmetatable(class)
|
||||
if meta then
|
||||
local fun = rawget(class, method)
|
||||
if fun then
|
||||
fun(self, ...)
|
||||
end
|
||||
|
||||
invoke_before_rec(self, meta.__index, method, ...)
|
||||
end
|
||||
end
|
||||
|
||||
local function invoke_after_rec(self, class, method, ...)
|
||||
local meta = getmetatable(class)
|
||||
if meta then
|
||||
invoke_after_rec(self, meta.__index, method, ...)
|
||||
|
||||
local fun = rawget(class, method)
|
||||
if fun then
|
||||
fun(self, ...)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function init_attrs_rec(obj, class, init_table)
|
||||
local meta = getmetatable(class)
|
||||
if meta then
|
||||
init_attrs_rec(obj, meta.__index, init_table)
|
||||
apply_attrs(obj, rawget(class, 'ATTRS'), init_table)
|
||||
end
|
||||
end
|
||||
|
||||
-- Call metamethod constructs the object
|
||||
function class_obj:__call(init_table)
|
||||
-- The table is assumed to be a scratch temporary.
|
||||
-- If it is not, copy it yourself before calling.
|
||||
init_table = init_table or {}
|
||||
|
||||
local obj = mkinstance(self)
|
||||
|
||||
-- This initialization sequence is broadly based on how the
|
||||
-- Common Lisp initialize-instance generic function works.
|
||||
|
||||
-- preinit screens input arguments in subclass to superclass order
|
||||
invoke_before_rec(obj, self, 'preinit', init_table)
|
||||
-- initialize the instance table from init table
|
||||
init_attrs_rec(obj, self, init_table)
|
||||
-- init in superclass -> subclass
|
||||
invoke_after_rec(obj, self, 'init', init_table)
|
||||
-- postinit in superclass -> subclass
|
||||
invoke_after_rec(obj, self, 'postinit', init_table)
|
||||
|
||||
return obj
|
||||
end
|
||||
|
||||
-- Common methods for all instances:
|
||||
|
||||
function common_methods:callback(method, ...)
|
||||
return dfhack.curry(self[method], self, ...)
|
||||
end
|
||||
|
||||
function common_methods:assign(data)
|
||||
for k,v in pairs(data) do
|
||||
self[k] = v
|
||||
end
|
||||
end
|
||||
|
||||
function common_methods:invoke_before(method, ...)
|
||||
return invoke_before_rec(self, getmetatable(self), method, ...)
|
||||
end
|
||||
|
||||
function common_methods:invoke_after(method, ...)
|
||||
return invoke_after_rec(self, getmetatable(self), method, ...)
|
||||
end
|
||||
|
||||
return _ENV
|
Loading…
Reference in New Issue