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