git.iko.soy/typeable/pandoc
on master help sign in
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'

commitb70079fbfa73dff4a3d94de1aa3d8b804fc9520b authorAlbert Krewinkel <albert@zeitkraut.de> dateSat 6 Jan 2018, 23:25 (9 years ago) parenta2e327f
1 file changed+83−51
data/pandoc.lua+83−51
data/pandoc.lua+83−51
@@ -29,39 +29,54 @@ local M = {
2929local List = require 'pandoc.List'
3030
3131------------------------------------------------------------------------
32−-- The base class for pandoc's AST elements.
33−-- @type AstElement
32+-- The base class for types
33+-- @type Type
3434-- @local
35−local AstElement = {}
36−AstElement.__index = AstElement
35+local Type = {}
36+Type.name = 'Type'
37+Type.__index = Type
38+Type.behavior = {
39+ __type = Type,
40+ new = function (obj)
41+ obj = obj or {}
42+ setmetatable(obj, self)
43+ return obj
44+ end
45+}
46+Type.behavior.__index = Type.behavior
3747
38−--- Create a new element subtype
48+--- Set a new behavior for the type, inheriting that of the parent type if none
49+--- is specified explicitely
50+-- @param behavior the behavior object for this type.
3951-- @local
40−function AstElement:make_subtype(o)
41− o = o or {}
42− setmetatable(o, self)
43− -- Make subtype usable as a metatable
44− o.__index = o
45− return o
52+function Type:set_behavior (behavior)
53+ behavior = behavior or {}
54+ behavior.__index = rawget(behavior, '__index') or behavior
55+ behavior.__type = self
56+ if not getmetatable(behavior) and getmetatable(self) then
57+ setmetatable(behavior, getmetatable(self).behavior)
58+ end
59+ self.behavior = behavior
4660end
4761
48−--- Create a new element given its tag and arguments
62+--- Create a new subtype, using the given table as base.
63+-- @param obj type object
4964-- @local
50−function AstElement:new(tag, ...)
51− local element = { t = tag }
52− local content = {...}
53− -- special case for unary constructors
54− if #content == 1 then
55− element.c = content[1]
56− -- Don't set 'c' field if no further arguments were given. This is important
57− -- for nullary constructors like `Space` and `HorizontalRule`.
58− elseif #content > 0 then
59− element.c = content
60− end
61− setmetatable(element, self)
62− return element
65+function Type:make_subtype(name, behavior)
66+ local newtype = setmetatable({}, self)
67+ newtype.name = name
68+ newtype.__index = newtype
69+ newtype:set_behavior(behavior)
70+ return newtype
6371end
6472
73+
74+------------------------------------------------------------------------
75+-- The base class for pandoc's AST elements.
76+-- @type AstElement
77+-- @local
78+local AstElement = Type:make_subtype 'AstElement'
79+
6580--- Create a new constructor
6681-- @local
6782-- @param tag Tag used to identify the constructor
@@ -69,31 +84,48 @@ end
6984-- @param accessors names to use as accessors for numerical fields
7085-- @return function that constructs a new element
7186function AstElement:create_constructor(tag, fn, accessors)
72− local constr = self:make_subtype({tag = tag, getters = {}, setters = {}})
87+ local constr = self:make_subtype(tag, {tag = tag, getters = {}, setters = {}})
88+ behavior = constr.behavior
89+ behavior.__index = function(t, k)
90+ if getmetatable(t).getters[k] then
91+ return getmetatable(t).getters[k](t)
92+ elseif k == "t" then
93+ return getmetatable(t)["tag"]
94+ else
95+ return getmetatable(t)[k]
96+ end
97+ end
98+ behavior.__newindex = function(t, k, v)
99+ if getmetatable(t).setters[k] then
100+ getmetatable(t).setters[k](t, v)
101+ else
102+ rawset(t, k, v)
103+ end
104+ end
73105
74106 -- Add accessors to the metatable
75107 if type(accessors) == "string" then
76− constr.getters[accessors] = function(elem)
108+ behavior.getters[accessors] = function(elem)
77109 return elem.c
78110 end
79− constr.setters[accessors] = function(elem, v)
111+ behavior.setters[accessors] = function(elem, v)
80112 elem.c = v
81113 end
82114 else
83115 for i = 1, #(accessors or {}) do
84116 if type(accessors[i]) == "string" then
85− constr.getters[accessors[i]] = function(elem)
117+ behavior.getters[accessors[i]] = function(elem)
86118 return elem.c[i]
87119 end
88− constr.setters[accessors[i]] = function(elem, v)
120+ behavior.setters[accessors[i]] = function(elem, v)
89121 elem.c[i] = v
90122 end
91123 else -- only two levels of nesting are supported
92124 for k, v in ipairs(accessors[i]) do
93− constr.getters[v] = function(elem)
125+ behavior.getters[v] = function(elem)
94126 return elem.c[i][k]
95127 end
96− constr.setters[v] = function(elem, v)
128+ behavior.setters[v] = function(elem, v)
97129 elem.c[i][k] = v
98130 end
99131 end
@@ -102,31 +134,31 @@ function AstElement:create_constructor(tag, fn, accessors)
102134 end
103135
104136 function constr:new(...)
105− local obj = fn(...)
106− setmetatable(obj, self)
107− self.__index = function(t, k)
108− if getmetatable(t).getters[k] then
109− return getmetatable(t).getters[k](t)
110− elseif k == "t" then
111− return getmetatable(t)["tag"]
112− else
113− return getmetatable(t)[k]
114− end
115− end
116− self.__newindex = function(t, k, v)
117− if getmetatable(t).setters[k] then
118− getmetatable(t).setters[k](t, v)
119− else
120− rawset(t, k, v)
121− end
122− end
123− return obj
137+ return setmetatable(fn(...), self.behavior)
124138 end
125139 self.constructor = self.constructor or {}
126140 self.constructor[tag] = constr
127141 return constr
128142end
129143
144+--- Create a new element given its tag and arguments
145+-- @local
146+function AstElement.new(constr, ...)
147+ local element = { t = constr.__type.name }
148+ local content = {...}
149+ -- special case for unary constructors
150+ if #content == 1 then
151+ element.c = content[1]
152+ -- Don't set 'c' field if no further arguments were given. This is important
153+ -- for nullary constructors like `Space` and `HorizontalRule`.
154+ elseif #content > 0 then
155+ element.c = content
156+ end
157+ setmetatable(element, constr)
158+ element.__index = element
159+ return element
160+end
161+
130162------------------------------------------------------------------------
131163--- Pandoc Document
132164-- @section document