mirror of
https://github.com/jgm/pandoc.git
synced 2026-08-25 01:36:45 +00:00
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'
This commit is contained in:
+87
-55
@@ -28,39 +28,54 @@ local M = {
|
|||||||
|
|
||||||
local List = require 'pandoc.List'
|
local List = require 'pandoc.List'
|
||||||
|
|
||||||
|
------------------------------------------------------------------------
|
||||||
|
-- The base class for types
|
||||||
|
-- @type Type
|
||||||
|
-- @local
|
||||||
|
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
|
||||||
|
|
||||||
|
--- 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 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 subtype, using the given table as base.
|
||||||
|
-- @param obj type object
|
||||||
|
-- @local
|
||||||
|
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.
|
-- The base class for pandoc's AST elements.
|
||||||
-- @type AstElement
|
-- @type AstElement
|
||||||
-- @local
|
-- @local
|
||||||
local AstElement = {}
|
local AstElement = Type:make_subtype 'AstElement'
|
||||||
AstElement.__index = AstElement
|
|
||||||
|
|
||||||
--- Create a new element subtype
|
|
||||||
-- @local
|
|
||||||
function AstElement:make_subtype(o)
|
|
||||||
o = o or {}
|
|
||||||
setmetatable(o, self)
|
|
||||||
-- Make subtype usable as a metatable
|
|
||||||
o.__index = o
|
|
||||||
return o
|
|
||||||
end
|
|
||||||
|
|
||||||
--- Create a new element given its tag and arguments
|
|
||||||
-- @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
|
|
||||||
end
|
|
||||||
|
|
||||||
--- Create a new constructor
|
--- Create a new constructor
|
||||||
-- @local
|
-- @local
|
||||||
@@ -69,31 +84,48 @@ end
|
|||||||
-- @param accessors names to use as accessors for numerical fields
|
-- @param accessors names to use as accessors for numerical fields
|
||||||
-- @return function that constructs a new element
|
-- @return function that constructs a new element
|
||||||
function AstElement:create_constructor(tag, fn, accessors)
|
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
|
-- Add accessors to the metatable
|
||||||
if type(accessors) == "string" then
|
if type(accessors) == "string" then
|
||||||
constr.getters[accessors] = function(elem)
|
behavior.getters[accessors] = function(elem)
|
||||||
return elem.c
|
return elem.c
|
||||||
end
|
end
|
||||||
constr.setters[accessors] = function(elem, v)
|
behavior.setters[accessors] = function(elem, v)
|
||||||
elem.c = v
|
elem.c = v
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
for i = 1, #(accessors or {}) do
|
for i = 1, #(accessors or {}) do
|
||||||
if type(accessors[i]) == "string" then
|
if type(accessors[i]) == "string" then
|
||||||
constr.getters[accessors[i]] = function(elem)
|
behavior.getters[accessors[i]] = function(elem)
|
||||||
return elem.c[i]
|
return elem.c[i]
|
||||||
end
|
end
|
||||||
constr.setters[accessors[i]] = function(elem, v)
|
behavior.setters[accessors[i]] = function(elem, v)
|
||||||
elem.c[i] = v
|
elem.c[i] = v
|
||||||
end
|
end
|
||||||
else -- only two levels of nesting are supported
|
else -- only two levels of nesting are supported
|
||||||
for k, v in ipairs(accessors[i]) do
|
for k, v in ipairs(accessors[i]) do
|
||||||
constr.getters[v] = function(elem)
|
behavior.getters[v] = function(elem)
|
||||||
return elem.c[i][k]
|
return elem.c[i][k]
|
||||||
end
|
end
|
||||||
constr.setters[v] = function(elem, v)
|
behavior.setters[v] = function(elem, v)
|
||||||
elem.c[i][k] = v
|
elem.c[i][k] = v
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -102,31 +134,31 @@ function AstElement:create_constructor(tag, fn, accessors)
|
|||||||
end
|
end
|
||||||
|
|
||||||
function constr:new(...)
|
function constr:new(...)
|
||||||
local obj = fn(...)
|
return setmetatable(fn(...), self.behavior)
|
||||||
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
|
|
||||||
end
|
end
|
||||||
self.constructor = self.constructor or {}
|
self.constructor = self.constructor or {}
|
||||||
self.constructor[tag] = constr
|
self.constructor[tag] = constr
|
||||||
return constr
|
return constr
|
||||||
end
|
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
|
--- Pandoc Document
|
||||||
-- @section document
|
-- @section document
|
||||||
|
|||||||
Reference in New Issue
Block a user