summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlbert Krewinkel <albert@zeitkraut.de>2018-01-06 23:25:08 +0100
committerAlbert Krewinkel <albert@zeitkraut.de>2018-01-06 23:25:08 +0100
commitb70079fbfa73dff4a3d94de1aa3d8b804fc9520b (patch)
treeca0fbdf2083f670c673ea2c42eea700fb14716d3
parenta2e327f0db6be6999d575a3562007202b72153cf (diff)
data/pandoc.lua: split type and behavior tables
Clearly distinguish between a type and the behavioral properties of an instance of that type. The behavior of a type (and all its subtypes) can now be amended by adding methods to that types `behavior` object, without exposing the type objects internals. E.g.: pandoc.Inline.behavior.frob = function () print'42' end local str = pandoc.Str'hello' str.frob() -- outputs '42'
-rw-r--r--data/pandoc.lua134
1 files changed, 83 insertions, 51 deletions
diff --git a/data/pandoc.lua b/data/pandoc.lua
index f6484f05e..ebe14cb0c 100644
--- a/data/pandoc.lua
+++ b/data/pandoc.lua
@@ -29,39 +29,54 @@ local M = {
local List = require 'pandoc.List'
------------------------------------------------------------------------
--- The base class for pandoc's AST elements.
--- @type AstElement
+-- The base class for types
+-- @type Type
-- @local
-local AstElement = {}
-AstElement.__index = AstElement
+local Type = {}
+Type.name = 'Type'
+Type.__index = Type
+Type.behavior = {
+ __type = Type,
+ new = function (obj)
+ obj = obj or {}
+ setmetatable(obj, self)
+ return obj
+ end
+}
+Type.behavior.__index = Type.behavior
---- Create a new element subtype
+--- Set a new behavior for the type, inheriting that of the parent type if none
+--- is specified explicitely
+-- @param behavior the behavior object for this type.
-- @local
-function AstElement:make_subtype(o)
- o = o or {}
- setmetatable(o, self)
- -- Make subtype usable as a metatable
- o.__index = o
- return o
+function Type:set_behavior (behavior)
+ behavior = behavior or {}
+ behavior.__index = rawget(behavior, '__index') or behavior
+ behavior.__type = self
+ if not getmetatable(behavior) and getmetatable(self) then
+ setmetatable(behavior, getmetatable(self).behavior)
+ end
+ self.behavior = behavior
end
---- Create a new element given its tag and arguments
+--- Create a new subtype, using the given table as base.
+-- @param obj type object
-- @local
-function AstElement:new(tag, ...)
- local element = { t = tag }
- local content = {...}
- -- special case for unary constructors
- if #content == 1 then
- element.c = content[1]
- -- Don't set 'c' field if no further arguments were given. This is important
- -- for nullary constructors like `Space` and `HorizontalRule`.
- elseif #content > 0 then
- element.c = content
- end
- setmetatable(element, self)
- return element
+function Type:make_subtype(name, behavior)
+ local newtype = setmetatable({}, self)
+ newtype.name = name
+ newtype.__index = newtype
+ newtype:set_behavior(behavior)
+ return newtype
end
+
+------------------------------------------------------------------------
+-- The base class for pandoc's AST elements.
+-- @type AstElement
+-- @local
+local AstElement = Type:make_subtype 'AstElement'
+
--- Create a new constructor
-- @local
-- @param tag Tag used to identify the constructor
@@ -69,31 +84,48 @@ end
-- @param accessors names to use as accessors for numerical fields
-- @return function that constructs a new element
function AstElement:create_constructor(tag, fn, accessors)
- local constr = self:make_subtype({tag = tag, getters = {}, setters = {}})
+ local constr = self:make_subtype(tag, {tag = tag, getters = {}, setters = {}})
+ behavior = constr.behavior
+ behavior.__index = function(t, k)
+ if getmetatable(t).getters[k] then
+ return getmetatable(t).getters[k](t)
+ elseif k == "t" then
+ return getmetatable(t)["tag"]
+ else
+ return getmetatable(t)[k]
+ end
+ end
+ behavior.__newindex = function(t, k, v)
+ if getmetatable(t).setters[k] then
+ getmetatable(t).setters[k](t, v)
+ else
+ rawset(t, k, v)
+ end
+ end
-- Add accessors to the metatable
if type(accessors) == "string" then
- constr.getters[accessors] = function(elem)
+ behavior.getters[accessors] = function(elem)
return elem.c
end
- constr.setters[accessors] = function(elem, v)
+ behavior.setters[accessors] = function(elem, v)
elem.c = v
end
else
for i = 1, #(accessors or {}) do
if type(accessors[i]) == "string" then
- constr.getters[accessors[i]] = function(elem)
+ behavior.getters[accessors[i]] = function(elem)
return elem.c[i]
end
- constr.setters[accessors[i]] = function(elem, v)
+ behavior.setters[accessors[i]] = function(elem, v)
elem.c[i] = v
end
else -- only two levels of nesting are supported
for k, v in ipairs(accessors[i]) do
- constr.getters[v] = function(elem)
+ behavior.getters[v] = function(elem)
return elem.c[i][k]
end
- constr.setters[v] = function(elem, v)
+ behavior.setters[v] = function(elem, v)
elem.c[i][k] = v
end
end
@@ -102,31 +134,31 @@ function AstElement:create_constructor(tag, fn, accessors)
end
function constr:new(...)
- local obj = fn(...)
- setmetatable(obj, self)
- self.__index = function(t, k)
- if getmetatable(t).getters[k] then
- return getmetatable(t).getters[k](t)
- elseif k == "t" then
- return getmetatable(t)["tag"]
- else
- return getmetatable(t)[k]
- end
- end
- self.__newindex = function(t, k, v)
- if getmetatable(t).setters[k] then
- getmetatable(t).setters[k](t, v)
- else
- rawset(t, k, v)
- end
- end
- return obj
+ return setmetatable(fn(...), self.behavior)
end
self.constructor = self.constructor or {}
self.constructor[tag] = constr
return constr
end
+--- Create a new element given its tag and arguments
+-- @local
+function AstElement.new(constr, ...)
+ local element = { t = constr.__type.name }
+ local content = {...}
+ -- special case for unary constructors
+ if #content == 1 then
+ element.c = content[1]
+ -- Don't set 'c' field if no further arguments were given. This is important
+ -- for nullary constructors like `Space` and `HorizontalRule`.
+ elseif #content > 0 then
+ element.c = content
+ end
+ setmetatable(element, constr)
+ element.__index = element
+ return element
+end
+
------------------------------------------------------------------------
--- Pandoc Document
-- @section document