summaryrefslogtreecommitdiff
path: root/library/lua/class.lua
diff options
context:
space:
mode:
Diffstat (limited to 'library/lua/class.lua')
-rw-r--r--library/lua/class.lua150
1 files changed, 150 insertions, 0 deletions
diff --git a/library/lua/class.lua b/library/lua/class.lua
new file mode 100644
index 00000000..7b142e49
--- /dev/null
+++ b/library/lua/class.lua
@@ -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