git.iko.soy/typeable/pandoc
on 2.7.1 help sign in
typeable/pandoc/data/pandoc.lua
1062 lines 31 kB history raw
1--[[
2pandoc.lua
4Copyright © 2017–2019 Albert Krewinkel
6Permission to use, copy, modify, and/or distribute this software for any purpose
7with or without fee is hereby granted, provided that the above copyright notice
8and this permission notice appear in all copies.
10THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
11REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
12FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
13INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
14OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
15TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
16THIS SOFTWARE.
17]]
19---
20-- Lua functions for pandoc scripts.
21--
22-- @author Albert Krewinkel
23-- @copyright © 2017–2019 Albert Krewinkel
24-- @license MIT
25local M = {}
27-- Re-export bundled modules
28M.List = require 'pandoc.List'
29M.mediabag = require 'pandoc.mediabag'
30M.utils = require 'pandoc.utils'
31M.text = require 'text'
33-- Local names for modules which this module depends on.
34local List = M.List
35local utils = M.utils
38------------------------------------------------------------------------
39-- Accessor objects
40--
41-- Create metatables which allow to access numerical indices via accessor
42-- methods.
43-- @section
44-- @local
46--- Create a new indexing function.
47-- @param template function template
48-- @param indices list of indices, starting with the most deeply nested
49-- @return newly created function
50-- @local
51function make_indexing_function(template, indices)
52 local loadstring = loadstring or load
53 local bracketed = {}
54 for i = 1, #indices do
55 bracketed[i] = string.format('[%d]', indices[#indices - i + 1])
56 end
57 local fnstr = string.format('return ' .. template, table.concat(bracketed))
58 return assert(loadstring(fnstr))()
59end
61--- Create accessor functions using a function template.
62-- @param fn_template function template in which '%s' is replacd with indices
63-- @param accessors list of accessors
64-- @return mapping from accessor names to accessor functions
65-- @local
66local function create_accessor_functions (fn_template, accessors)
67 local res = {}
68 function add_accessors(acc, ...)
69 if type(acc) == 'string' then
70 res[acc] = make_indexing_function(fn_template, {...})
71 elseif type(acc) == 'table' and #acc == 0 and next(acc) then
72 local name, substructure = next(acc)
73 res[name] = make_indexing_function(fn_template, {...})
74 add_accessors(substructure, ...)
75 else
76 for i = 1, #(acc or {}) do
77 add_accessors(acc[i], i, ...)
78 end
79 end
80 end
81 add_accessors(accessors)
82 return res
83end
85--- Get list of top-level fields from field descriptor table.
86-- E.g.: `top_level_fields{'foo', {bar='baz'}, {'qux', 'quux'}}`
87-- gives {'foo, 'bar', 'qux', 'quux'}
88-- @local
89local function top_level_fields (fields)
90 local result = List:new{}
91 for _, v in ipairs(fields) do
92 if type(v) == 'string' then
93 table.insert(result, v)
94 elseif type(v) == 'table' and #v == 0 and next(v) then
95 table.insert(result, (next(v)))
96 else
97 result:extend(top_level_fields(v))
98 end
99 end
100 return result
101end
103--- Creates a function which behaves like next, but respects field names.
104-- @local
105local function make_next_function (fields)
106 local field_indices = {}
107 for i, f in ipairs(fields) do
108 field_indices[f] = i
109 end
111 return function (t, field)
112 local raw_idx = field == nil and 0 or field_indices[field]
113 local next_field = fields[raw_idx + 1]
114 return next_field, t[next_field]
115 end
116end
118--- Create a new table which allows to access numerical indices via accessor
119-- functions.
120-- @local
121local function create_accessor_behavior (tag, accessors)
122 local behavior = {tag = tag}
123 behavior.getters = create_accessor_functions(
124 'function (x) return x.c%s end',
125 accessors
126 )
127 behavior.setters = create_accessor_functions(
128 'function (x, v) x.c%s = v end',
129 accessors
130 )
131 behavior.__eq = utils.equals
132 behavior.__index = function(t, k)
133 if getmetatable(t).getters[k] then
134 return getmetatable(t).getters[k](t)
135 elseif k == "t" then
136 return getmetatable(t)["tag"]
137 else
138 return getmetatable(t)[k]
139 end
140 end
141 behavior.__newindex = function(t, k, v)
142 if getmetatable(t).setters[k] then
143 getmetatable(t).setters[k](t, v)
144 else
145 rawset(t, k, v)
146 end
147 end
148 behavior.__pairs = function (t)
149 if accessors == nil then
150 return next, t
151 end
152 local iterable_fields = type(accessors) == 'string'
153 and {accessors}
154 or top_level_fields(accessors)
155 return make_next_function(iterable_fields), t
156 end
157 return behavior
158end
161------------------------------------------------------------------------
162-- The base class for types
163-- @type Type
164-- @local
165local Type = {}
166Type.name = 'Type'
167Type.__index = Type
168Type.behavior = {
169 __type = Type,
170 new = function (obj)
171 obj = obj or {}
172 setmetatable(obj, self)
173 return obj
174 end
176Type.behavior.__index = Type.behavior
178--- Set a new behavior for the type, inheriting that of the parent type if none
179--- is specified explicitly
180-- @param behavior the behavior object for this type.
181-- @local
182function Type:set_behavior (behavior)
183 behavior = behavior or {}
184 behavior.__index = rawget(behavior, '__index') or behavior
185 behavior.__type = self
186 if not getmetatable(behavior) and getmetatable(self) then
187 setmetatable(behavior, getmetatable(self).behavior)
188 end
189 self.behavior = behavior
190end
192--- Create a new subtype, using the given table as base.
193-- @param name name of the new type
194-- @param[opt] behavior behavioral object for the new type.
195-- @return a new type
196-- @local
197function Type:make_subtype(name, behavior)
198 local newtype = setmetatable({}, self)
199 newtype.name = name
200 newtype.__index = newtype
201 newtype:set_behavior(behavior)
202 return newtype
203end
206------------------------------------------------------------------------
207-- The base class for pandoc's AST elements.
208-- @type AstElement
209-- @local
210local AstElement = Type:make_subtype 'AstElement'
211AstElement.__call = function(t, ...)
212 local success, ret = pcall(t.new, t, ...)
213 if success then
214 return setmetatable(ret, t.behavior)
215 else
216 error(string.format('Constructor for %s failed: %s\n', t.name, ret))
217 end
218end
220--- Make a new subtype which constructs a new value when called.
221-- @local
222function AstElement:make_subtype(...)
223 local newtype = Type.make_subtype(self, ...)
224 newtype.__call = self.__call
225 return newtype
226end
228--- Create a new constructor
229-- @local
230-- @param tag Tag used to identify the constructor
231-- @param fn Function to be called when constructing a new element
232-- @param accessors names to use as accessors for numerical fields
233-- @return function that constructs a new element
234function AstElement:create_constructor(tag, fn, accessors)
235 local constr = self:make_subtype(tag, create_accessor_behavior(tag, accessors))
236 function constr:new(...)
237 return setmetatable(fn(...), self.behavior)
238 end
239 self.constructor = self.constructor or {}
240 self.constructor[tag] = constr
241 return constr
242end
244--- Convert AstElement input into a list if necessary.
245-- @local
246local function ensureList (x)
247 if x.tag then
248 -- Lists are not tagged, but all elements are
249 return List:new{x}
250 else
251 return List:new(x)
252 end
253end
255--- Ensure a given object is an Inline element, or convert it into one.
256-- @local
257local function ensureInlineList (x)
258 if type(x) == 'string' then
259 return List:new{M.Str(x)}
260 else
261 return ensureList(x)
262 end
263end
265--- Ensure that the given object is a definition pair, convert if necessary.
266-- @local
267local function ensureDefinitionPairs (pair)
268 local inlines = ensureInlineList(pair[1] or {})
269 local blocks = ensureList(pair[2] or {}):map(ensureList)
270 return {inlines, blocks}
271end
273------------------------------------------------------------------------
274--- Pandoc Document
275-- @section document
277--- A complete pandoc document
278-- @function Pandoc
279-- @tparam {Block,...} blocks document content
280-- @tparam[opt] Meta meta document meta data
281M.Pandoc = AstElement:make_subtype'Pandoc'
282function M.Pandoc:new (blocks, meta)
283 return {
284 blocks = ensureList(blocks),
285 meta = meta or {},
286 }
287end
289-- DEPRECATED synonym:
290M.Doc = M.Pandoc
292------------------------------------------------------------------------
293-- Meta
294-- @section Meta
296--- Create a new Meta object. It sets the metatable of the given table to
297--- `Meta`.
298-- @function Meta
299-- @tparam meta table table containing document meta information
300M.Meta = AstElement:make_subtype'Meta'
301function M.Meta:new (meta) return meta end
304------------------------------------------------------------------------
305-- MetaValue
306-- @section MetaValue
307M.MetaValue = AstElement:make_subtype('MetaValue')
309--- Meta blocks
310-- @function MetaBlocks
311-- @tparam {Block,...} blocks blocks
312M.MetaBlocks = M.MetaValue:create_constructor(
313 'MetaBlocks',
314 function (content) return ensureList(content) end
317--- Meta inlines
318-- @function MetaInlines
319-- @tparam {Inline,...} inlines inlines
320M.MetaInlines = M.MetaValue:create_constructor(
321 'MetaInlines',
322 function (content) return ensureInlineList(content) end
325--- Meta list
326-- @function MetaList
327-- @tparam {MetaValue,...} meta_values list of meta values
328M.MetaList = M.MetaValue:create_constructor(
329 'MetaList',
330 function (content)
331 if content.tag == 'MetaList' then
332 return content
333 end
334 return ensureList(content)
335 end
337for k, v in pairs(List) do
338 M.MetaList.behavior[k] = v
339end
341--- Meta map
342-- @function MetaMap
343-- @tparam table key_value_map a string-indexed map of meta values
344M.MetaMap = M.MetaValue:create_constructor(
345 "MetaMap",
346 function (mm) return mm end
349--- Creates string to be used in meta data.
350-- Does nothing, lua strings are meta strings.
351-- @function MetaString
352-- @tparam string str string value
353function M.MetaString(str)
354 return str
355end
357--- Creates boolean to be used in meta data.
358-- Does nothing, lua booleans are meta booleans.
359-- @function MetaBool
360-- @tparam boolean bool boolean value
361function M.MetaBool(bool)
362 return bool
363end
365------------------------------------------------------------------------
366-- Blocks
367-- @section Block
369--- Block elements
370M.Block = AstElement:make_subtype'Block'
372--- Creates a block quote element
373-- @function BlockQuote
374-- @tparam {Block,...} content block content
375-- @treturn Block block quote element
376M.BlockQuote = M.Block:create_constructor(
377 "BlockQuote",
378 function(content) return {c = ensureList(content)} end,
379 "content"
382--- Creates a bullet (i.e. unordered) list.
383-- @function BulletList
384-- @tparam {{Block,...},...} content list of items
385-- @treturn Block bullet list element
386M.BulletList = M.Block:create_constructor(
387 "BulletList",
388 function(content) return {c = ensureList(content):map(ensureList)} end,
389 "content"
392--- Creates a code block element
393-- @function CodeBlock
394-- @tparam string text code string
395-- @tparam[opt] Attr attr element attributes
396-- @treturn Block code block element
397M.CodeBlock = M.Block:create_constructor(
398 "CodeBlock",
399 function(text, attr) return {c = {attr or M.Attr(), text}} end,
400 {{attr = {"identifier", "classes", "attributes"}}, "text"}
403--- Creates a definition list, containing terms and their explanation.
404-- @function DefinitionList
405-- @tparam {{{Inline,...},{{Block,...}}},...} content list of items
406-- @treturn Block definition list element
407M.DefinitionList = M.Block:create_constructor(
408 "DefinitionList",
409 function(content)
410 return {c = ensureList(content):map(ensureDefinitionPairs)}
411 end,
412 "content"
415--- Creates a div element
416-- @function Div
417-- @tparam {Block,...} content block content
418-- @tparam[opt] Attr attr element attributes
419-- @treturn Block div element
420M.Div = M.Block:create_constructor(
421 "Div",
422 function(content, attr)
423 return {c = {attr or M.Attr(), ensureList(content)}}
424 end,
425 {{attr = {"identifier", "classes", "attributes"}}, "content"}
428--- Creates a header element.
429-- @function Header
430-- @tparam int level header level
431-- @tparam {Inline,...} content inline content
432-- @tparam[opt] Attr attr element attributes
433-- @treturn Block header element
434M.Header = M.Block:create_constructor(
435 "Header",
436 function(level, content, attr)
437 return {c = {level, attr or M.Attr(), ensureInlineList(content)}}
438 end,
439 {"level", {attr = {"identifier", "classes", "attributes"}}, "content"}
442--- Creates a horizontal rule.
443-- @function HorizontalRule
444-- @treturn Block horizontal rule
445M.HorizontalRule = M.Block:create_constructor(
446 "HorizontalRule",
447 function() return {} end
450--- Creates a line block element.
451-- @function LineBlock
452-- @tparam {{Inline,...},...} content inline content
453-- @treturn Block line block element
454M.LineBlock = M.Block:create_constructor(
455 "LineBlock",
456 function(content) return {c = ensureList(content):map(ensureInlineList)} end,
457 "content"
460--- Creates a null element.
461-- @function Null
462-- @treturn Block null element
463M.Null = M.Block:create_constructor(
464 "Null",
465 function() return {} end
468--- Creates an ordered list.
469-- @function OrderedList
470-- @tparam {{Block,...},...} items list items
471-- @param[opt] listAttributes list parameters
472-- @treturn Block ordered list element
473M.OrderedList = M.Block:create_constructor(
474 "OrderedList",
475 function(items, listAttributes)
476 listAttributes = listAttributes or M.ListAttributes()
477 return {c = {listAttributes, ensureList(items):map(ensureList)}}
478 end,
479 {{listAttributes = {"start", "style", "delimiter"}}, "content"}
482--- Creates a para element.
483-- @function Para
484-- @tparam {Inline,...} content inline content
485-- @treturn Block paragraph element
486M.Para = M.Block:create_constructor(
487 "Para",
488 function(content) return {c = ensureInlineList(content)} end,
489 "content"
492--- Creates a plain element.
493-- @function Plain
494-- @tparam {Inline,...} content inline content
495-- @treturn Block plain element
496M.Plain = M.Block:create_constructor(
497 "Plain",
498 function(content) return {c = ensureInlineList(content)} end,
499 "content"
502--- Creates a raw content block of the specified format.
503-- @function RawBlock
504-- @tparam string format format of content
505-- @tparam string text string content
506-- @treturn Block raw block element
507M.RawBlock = M.Block:create_constructor(
508 "RawBlock",
509 function(format, text) return {c = {format, text}} end,
510 {"format", "text"}
513--- Creates a table element.
514-- @function Table
515-- @tparam {Inline,...} caption table caption
516-- @tparam {AlignDefault|AlignLeft|AlignRight|AlignCenter,...} aligns alignments
517-- @tparam {int,...} widths column widths
518-- @tparam {Block,...} headers header row
519-- @tparam {{Block,...}} rows table rows
520-- @treturn Block table element
521M.Table = M.Block:create_constructor(
522 "Table",
523 function(caption, aligns, widths, headers, rows)
524 return {
525 c = {
526 ensureInlineList(caption),
527 List:new(aligns),
528 List:new(widths),
529 List:new(headers),
530 List:new(rows)
531 }
532 }
533 end,
534 {"caption", "aligns", "widths", "headers", "rows"}
538------------------------------------------------------------------------
539-- Inline
540-- @section Inline
542--- Inline element class
543M.Inline = AstElement:make_subtype'Inline'
545--- Creates a Cite inline element
546-- @function Cite
547-- @tparam {Inline,...} content List of inlines
548-- @tparam {Citation,...} citations List of citations
549-- @treturn Inline citations element
550M.Cite = M.Inline:create_constructor(
551 "Cite",
552 function(content, citations)
553 return {c = {ensureList(citations), ensureInlineList(content)}}
554 end,
555 {"citations", "content"}
558--- Creates a Code inline element
559-- @function Code
560-- @tparam string text code string
561-- @tparam[opt] Attr attr additional attributes
562-- @treturn Inline code element
563M.Code = M.Inline:create_constructor(
564 "Code",
565 function(text, attr) return {c = {attr or M.Attr(), text}} end,
566 {{attr = {"identifier", "classes", "attributes"}}, "text"}
569--- Creates an inline element representing emphasised text.
570-- @function Emph
571-- @tparam {Inline,..} content inline content
572-- @treturn Inline emphasis element
573M.Emph = M.Inline:create_constructor(
574 "Emph",
575 function(content) return {c = ensureInlineList(content)} end,
576 "content"
579--- Creates a Image inline element
580-- @function Image
581-- @tparam {Inline,..} caption text used to describe the image
582-- @tparam string src path to the image file
583-- @tparam[opt] string title brief image description
584-- @tparam[opt] Attr attr additional attributes
585-- @treturn Inline image element
586M.Image = M.Inline:create_constructor(
587 "Image",
588 function(caption, src, title, attr)
589 title = title or ""
590 attr = attr or M.Attr()
591 return {c = {attr, ensureInlineList(caption), {src, title}}}
592 end,
593 {{attr = {"identifier", "classes", "attributes"}}, "caption", {"src", "title"}}
596--- Create a LineBreak inline element
597-- @function LineBreak
598-- @treturn Inline linebreak element
599M.LineBreak = M.Inline:create_constructor(
600 "LineBreak",
601 function() return {} end
604--- Creates a link inline element, usually a hyperlink.
605-- @function Link
606-- @tparam {Inline,..} content text for this link
607-- @tparam string target the link target
608-- @tparam[opt] string title brief link description
609-- @tparam[opt] Attr attr additional attributes
610-- @treturn Inline image element
611M.Link = M.Inline:create_constructor(
612 "Link",
613 function(content, target, title, attr)
614 title = title or ""
615 attr = attr or M.Attr()
616 return {c = {attr, ensureInlineList(content), {target, title}}}
617 end,
618 {{attr = {"identifier", "classes", "attributes"}}, "content", {"target", "title"}}
621--- Creates a Math element, either inline or displayed.
622-- @function Math
623-- @tparam "InlineMath"|"DisplayMath" mathtype rendering specifier
624-- @tparam string text Math content
625-- @treturn Inline Math element
626M.Math = M.Inline:create_constructor(
627 "Math",
628 function(mathtype, text)
629 return {c = {mathtype, text}}
630 end,
631 {"mathtype", "text"}
633--- Creates a DisplayMath element (DEPRECATED).
634-- @function DisplayMath
635-- @tparam string text Math content
636-- @treturn Inline Math element
637M.DisplayMath = M.Inline:create_constructor(
638 "DisplayMath",
639 function(text) return M.Math("DisplayMath", text) end,
640 {"mathtype", "text"}
642--- Creates an InlineMath inline element (DEPRECATED).
643-- @function InlineMath
644-- @tparam string text Math content
645-- @treturn Inline Math element
646M.InlineMath = M.Inline:create_constructor(
647 "InlineMath",
648 function(text) return M.Math("InlineMath", text) end,
649 {"mathtype", "text"}
652--- Creates a Note inline element
653-- @function Note
654-- @tparam {Block,...} content footnote block content
655M.Note = M.Inline:create_constructor(
656 "Note",
657 function(content) return {c = ensureList(content)} end,
658 "content"
661--- Creates a Quoted inline element given the quote type and quoted content.
662-- @function Quoted
663-- @tparam "DoubleQuote"|"SingleQuote" quotetype type of quotes to be used
664-- @tparam {Inline,..} content inline content
665-- @treturn Inline quoted element
666M.Quoted = M.Inline:create_constructor(
667 "Quoted",
668 function(quotetype, content)
669 return {c = {quotetype, ensureInlineList(content)}}
670 end,
671 {"quotetype", "content"}
673--- Creates a single-quoted inline element (DEPRECATED).
674-- @function SingleQuoted
675-- @tparam {Inline,..} content inline content
676-- @treturn Inline quoted element
677-- @see Quoted
678M.SingleQuoted = M.Inline:create_constructor(
679 "SingleQuoted",
680 function(content) return M.Quoted(M.SingleQuote, content) end,
681 {"quotetype", "content"}
683--- Creates a single-quoted inline element (DEPRECATED).
684-- @function DoubleQuoted
685-- @tparam {Inline,..} content inline content
686-- @treturn Inline quoted element
687-- @see Quoted
688M.DoubleQuoted = M.Inline:create_constructor(
689 "DoubleQuoted",
690 function(content) return M.Quoted("DoubleQuote", content) end,
691 {"quotetype", "content"}
694--- Creates a RawInline inline element
695-- @function RawInline
696-- @tparam string format format of the contents
697-- @tparam string text string content
698-- @treturn Inline raw inline element
699M.RawInline = M.Inline:create_constructor(
700 "RawInline",
701 function(format, text) return {c = {format, text}} end,
702 {"format", "text"}
705--- Creates text rendered in small caps
706-- @function SmallCaps
707-- @tparam {Inline,..} content inline content
708-- @treturn Inline smallcaps element
709M.SmallCaps = M.Inline:create_constructor(
710 "SmallCaps",
711 function(content) return {c = ensureInlineList(content)} end,
712 "content"
715--- Creates a SoftBreak inline element.
716-- @function SoftBreak
717-- @treturn Inline softbreak element
718M.SoftBreak = M.Inline:create_constructor(
719 "SoftBreak",
720 function() return {} end
723--- Create a Space inline element
724-- @function Space
725-- @treturn Inline space element
726M.Space = M.Inline:create_constructor(
727 "Space",
728 function() return {} end
731--- Creates a Span inline element
732-- @function Span
733-- @tparam {Inline,..} content inline content
734-- @tparam[opt] Attr attr additional attributes
735-- @treturn Inline span element
736M.Span = M.Inline:create_constructor(
737 "Span",
738 function(content, attr)
739 return {c = {attr or M.Attr(), ensureInlineList(content)}}
740 end,
741 {{attr = {"identifier", "classes", "attributes"}}, "content"}
744--- Creates a Str inline element
745-- @function Str
746-- @tparam string text content
747-- @treturn Inline string element
748M.Str = M.Inline:create_constructor(
749 "Str",
750 function(text) return {c = text} end,
751 "text"
754--- Creates text which is striked out.
755-- @function Strikeout
756-- @tparam {Inline,..} content inline content
757-- @treturn Inline strikeout element
758M.Strikeout = M.Inline:create_constructor(
759 "Strikeout",
760 function(content) return {c = ensureInlineList(content)} end,
761 "content"
764--- Creates a Strong element, whose text is usually displayed in a bold font.
765-- @function Strong
766-- @tparam {Inline,..} content inline content
767-- @treturn Inline strong element
768M.Strong = M.Inline:create_constructor(
769 "Strong",
770 function(content) return {c = ensureInlineList(content)} end,
771 "content"
774--- Creates a Subscript inline element
775-- @function Subscript
776-- @tparam {Inline,..} content inline content
777-- @treturn Inline subscript element
778M.Subscript = M.Inline:create_constructor(
779 "Subscript",
780 function(content) return {c = ensureInlineList(content)} end,
781 "content"
784--- Creates a Superscript inline element
785-- @function Superscript
786-- @tparam {Inline,..} content inline content
787-- @treturn Inline strong element
788M.Superscript = M.Inline:create_constructor(
789 "Superscript",
790 function(content) return {c = ensureInlineList(content)} end,
791 "content"
795------------------------------------------------------------------------
796-- Element components
797-- @section components
799--- Check if the first element of a pair matches the given value.
800-- @param x key value to be checked
801-- @return function returning true iff first element of its argument matches x
802-- @local
803local function assoc_key_equals (x)
804 return function (y) return y[1] == x end
805end
807--- Lookup a value in an associative list
808-- @function lookup
809-- @local
810-- @tparam {{key, value},...} alist associative list
811-- @param key key for which the associated value is to be looked up
812local function lookup(alist, key)
813 return (List.find_if(alist, assoc_key_equals(key)) or {})[2]
814end
816--- Return an iterator which returns key-value pairs of an associative list.
817-- @function apairs
818-- @local
819-- @tparam {{key, value},...} alist associative list
820local apairs = function (alist)
821 local i = 1
822 local cur
823 function nxt ()
824 cur = rawget(alist, i)
825 if cur then
826 i = i + 1
827 return cur[1], cur[2]
828 end
829 return nil
830 end
831 return nxt, nil, nil
832end
834--- AttributeList, a metatable to allow table-like access to attribute lists
835-- represented by associative lists.
836-- @local
837local AttributeList = {
838 __index = function (t, k)
839 if type(k) == "number" then
840 return rawget(t, k)
841 else
842 return lookup(t, k)
843 end
844 end,
846 __newindex = function (t, k, v)
847 local cur, idx = List.find_if(t, assoc_key_equals(k))
848 if v == nil then
849 table.remove(t, idx)
850 elseif cur then
851 cur[2] = v
852 elseif type(k) == "number" then
853 rawset(t, k, v)
854 else
855 rawset(t, #t + 1, {k, v})
856 end
857 end,
859 __pairs = apairs
862--- Convert a table to an associative list. The order of key-value pairs in the
863-- alist is undefined. The table should either contain no numeric keys or
864-- already be an associative list.
865-- @local
866-- @tparam table tbl associative list or table without numeric keys.
867-- @treturn table associative list
868local to_alist = function (tbl)
869 if #tbl ~= 0 or next(tbl) == nil then
870 -- probably already an alist
871 return tbl
872 end
873 local alist = {}
874 local i = 1
875 for k, v in pairs(tbl) do
876 alist[i] = {k, v}
877 i = i + 1
878 end
879 return alist
880end
882-- Attr
884--- Create a new set of attributes (Attr).
885-- @function Attr
886-- @tparam[opt] string identifier element identifier
887-- @tparam[opt] {string,...} classes element classes
888-- @tparam[opt] table attributes table containing string keys and values
889-- @return element attributes
890M.Attr = AstElement:make_subtype'Attr'
891function M.Attr:new (identifier, classes, attributes)
892 identifier = identifier or ''
893 classes = ensureList(classes or {})
894 attributes = setmetatable(to_alist(attributes or {}), AttributeList)
895 return {identifier, classes, attributes}
896end
897M.Attr.behavior._field_names = {identifier = 1, classes = 2, attributes = 3}
898M.Attr.behavior.__eq = utils.equals
899M.Attr.behavior.__index = function(t, k)
900 return rawget(t, getmetatable(t)._field_names[k]) or
901 getmetatable(t)[k]
902end
903M.Attr.behavior.__newindex = function(t, k, v)
904 if getmetatable(t)._field_names[k] then
905 rawset(t, getmetatable(t)._field_names[k], v)
906 else
907 rawset(t, k, v)
908 end
909end
910M.Attr.behavior.__pairs = function(t)
911 local field_names = M.Attr.behavior._field_names
912 local fields = {}
913 for name, i in pairs(field_names) do
914 fields[i] = name
915 end
916 return make_next_function(fields), t, nil
917end
919-- Citation
920M.Citation = AstElement:make_subtype'Citation'
922--- Creates a single citation.
923-- @function Citation
924-- @tparam string id citation identifier (like a bibtex key)
925-- @tparam AuthorInText|SuppressAuthor|NormalCitation mode citation mode
926-- @tparam[opt] {Inline,...} prefix citation prefix
927-- @tparam[opt] {Inline,...} suffix citation suffix
928-- @tparam[opt] int note_num note number
929-- @tparam[opt] int hash hash number
930function M.Citation:new (id, mode, prefix, suffix, note_num, hash)
931 return {
932 id = id,
933 mode = mode,
934 prefix = ensureList(prefix or {}),
935 suffix = ensureList(suffix or {}),
936 note_num = note_num or 0,
937 hash = hash or 0,
938 }
939end
941-- ListAttributes
942M.ListAttributes = AstElement:make_subtype 'ListAttributes'
944--- Creates a set of list attributes.
945-- @function ListAttributes
946-- @tparam[opt] integer start number of the first list item
947-- @tparam[opt] string style style used for list numbering
948-- @tparam[opt] DefaultDelim|Period|OneParen|TwoParens delimiter delimiter of list numbers
949-- @treturn table list attributes table
950function M.ListAttributes:new (start, style, delimiter)
951 start = start or 1
952 style = style or 'DefaultStyle'
953 delimiter = delimiter or 'DefaultDelim'
954 return {start, style, delimiter}
955end
956M.ListAttributes.behavior._field_names = {start = 1, style = 2, delimiter = 3}
957M.ListAttributes.behavior.__eq = utils.equals
958M.ListAttributes.behavior.__index = function (t, k)
959 return rawget(t, getmetatable(t)._field_names[k]) or
960 getmetatable(t)[k]
961end
962M.ListAttributes.behavior.__newindex = function (t, k, v)
963 if getmetatable(t)._field_names[k] then
964 rawset(t, getmetatable(t)._field_names[k], v)
965 else
966 rawset(t, k, v)
967 end
968end
969M.ListAttributes.behavior.__pairs = function(t)
970 local field_names = M.ListAttributes.behavior._field_names
971 local fields = {}
972 for name, i in pairs(field_names) do
973 fields[i] = name
974 end
975 return make_next_function(fields), t, nil
976end
979------------------------------------------------------------------------
980-- Constants
981-- @section constants
983--- Author name is mentioned in the text.
984-- @see Citation
985-- @see Cite
986M.AuthorInText = "AuthorInText"
988--- Author name is suppressed.
989-- @see Citation
990-- @see Cite
991M.SuppressAuthor = "SuppressAuthor"
993--- Default citation style is used.
994-- @see Citation
995-- @see Cite
996M.NormalCitation = "NormalCitation"
998--- Table cells aligned left.
999-- @see Table
1000M.AlignLeft = "AlignLeft"
1002--- Table cells right-aligned.
1003-- @see Table
1004M.AlignRight = "AlignRight"
1006--- Table cell content is centered.
1007-- @see Table
1008M.AlignCenter = "AlignCenter"
1010--- Table cells are alignment is unaltered.
1011-- @see Table
1012M.AlignDefault = "AlignDefault"
1014--- Default list number delimiters are used.
1015-- @see OrderedList
1016M.DefaultDelim = "DefaultDelim"
1018--- List numbers are delimited by a period.
1019-- @see OrderedList
1020M.Period = "Period"
1022--- List numbers are delimited by a single parenthesis.
1023-- @see OrderedList
1024M.OneParen = "OneParen"
1026--- List numbers are delimited by a double parentheses.
1027-- @see OrderedList
1028M.TwoParens = "TwoParens"
1030--- List are numbered in the default style
1031-- @see OrderedList
1032M.DefaultStyle = "DefaultStyle"
1034--- List items are numbered as examples.
1035-- @see OrderedList
1036M.Example = "Example"
1038--- List are numbered using decimal integers.
1039-- @see OrderedList
1040M.Decimal = "Decimal"
1042--- List are numbered using lower-case roman numerals.
1043-- @see OrderedList
1044M.LowerRoman = "LowerRoman"
1046--- List are numbered using upper-case roman numerals
1047-- @see OrderedList
1048M.UpperRoman = "UpperRoman"
1050--- List are numbered using lower-case alphabetic characters.
1051-- @see OrderedList
1052M.LowerAlpha = "LowerAlpha"
1054--- List are numbered using upper-case alphabetic characters.
1055-- @see OrderedList
1056M.UpperAlpha = "UpperAlpha"
1058------------------------------------------------------------------------
1059-- Functions which have moved to different modules
1060M.sha1 = utils.sha1
1062return M