git.iko.soy/typeable/pandoc
on ab792fa help sign in
typeable/pandoc/data/pandoc.lua
1075 lines 32 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.system = require 'pandoc.system'
31M.types = require 'pandoc.types'
32M.utils = require 'pandoc.utils'
33M.text = require 'text'
35-- Local names for modules which this module depends on.
36local List = M.List
37local utils = M.utils
40------------------------------------------------------------------------
41-- Accessor objects
42--
43-- Create metatables which allow to access numerical indices via accessor
44-- methods.
45-- @section
46-- @local
48--- Create a new indexing function.
49-- @param template function template
50-- @param indices list of indices, starting with the most deeply nested
51-- @return newly created function
52-- @local
53function make_indexing_function(template, indices)
54 local loadstring = loadstring or load
55 local bracketed = {}
56 for i = 1, #indices do
57 bracketed[i] = string.format('[%d]', indices[#indices - i + 1])
58 end
59 local fnstr = string.format('return ' .. template, table.concat(bracketed))
60 return assert(loadstring(fnstr))()
61end
63--- Create accessor functions using a function template.
64-- @param fn_template function template in which '%s' is replacd with indices
65-- @param accessors list of accessors
66-- @return mapping from accessor names to accessor functions
67-- @local
68local function create_accessor_functions (fn_template, accessors)
69 local res = {}
70 function add_accessors(acc, ...)
71 if type(acc) == 'string' then
72 res[acc] = make_indexing_function(fn_template, {...})
73 elseif type(acc) == 'table' and #acc == 0 and next(acc) then
74 local name, substructure = next(acc)
75 res[name] = make_indexing_function(fn_template, {...})
76 add_accessors(substructure, ...)
77 else
78 for i = 1, #(acc or {}) do
79 add_accessors(acc[i], i, ...)
80 end
81 end
82 end
83 add_accessors(accessors)
84 return res
85end
87--- Get list of top-level fields from field descriptor table.
88-- E.g.: `top_level_fields{'foo', {bar='baz'}, {'qux', 'quux'}}`
89-- gives {'foo, 'bar', 'qux', 'quux'}
90-- @local
91local function top_level_fields (fields)
92 local result = List:new{}
93 for _, v in ipairs(fields) do
94 if type(v) == 'string' then
95 table.insert(result, v)
96 elseif type(v) == 'table' and #v == 0 and next(v) then
97 table.insert(result, (next(v)))
98 else
99 result:extend(top_level_fields(v))
100 end
101 end
102 return result
103end
105--- Creates a function which behaves like next, but respects field names.
106-- @local
107local function make_next_function (fields)
108 local field_indices = {}
109 for i, f in ipairs(fields) do
110 field_indices[f] = i
111 end
113 return function (t, field)
114 local raw_idx = field == nil and 0 or field_indices[field]
115 local next_field = fields[raw_idx + 1]
116 return next_field, t[next_field]
117 end
118end
120--- Create a new table which allows to access numerical indices via accessor
121-- functions.
122-- @local
123local function create_accessor_behavior (tag, accessors)
124 local behavior = {tag = tag}
125 behavior.getters = create_accessor_functions(
126 'function (x) return x.c%s end',
127 accessors
128 )
129 behavior.setters = create_accessor_functions(
130 'function (x, v) x.c%s = v end',
131 accessors
132 )
133 behavior.__eq = utils.equals
134 behavior.__index = function(t, k)
135 if getmetatable(t).getters[k] then
136 return getmetatable(t).getters[k](t)
137 elseif k == "t" then
138 return getmetatable(t)["tag"]
139 else
140 return getmetatable(t)[k]
141 end
142 end
143 behavior.__newindex = function(t, k, v)
144 if getmetatable(t).setters[k] then
145 getmetatable(t).setters[k](t, v)
146 else
147 rawset(t, k, v)
148 end
149 end
150 behavior.__pairs = function (t)
151 if accessors == nil then
152 return next, t
153 end
154 local iterable_fields = type(accessors) == 'string'
155 and {accessors}
156 or top_level_fields(accessors)
157 return make_next_function(iterable_fields), t
158 end
159 return behavior
160end
163------------------------------------------------------------------------
164-- The base class for types
165-- @type Type
166-- @local
167local Type = {}
168Type.name = 'Type'
169Type.__index = Type
170Type.behavior = {
171 __type = Type,
172 new = function (obj)
173 obj = obj or {}
174 setmetatable(obj, self)
175 return obj
176 end
178Type.behavior.__index = Type.behavior
180--- Set a new behavior for the type, inheriting that of the parent type if none
181--- is specified explicitly
182-- @param behavior the behavior object for this type.
183-- @local
184function Type:set_behavior (behavior)
185 behavior = behavior or {}
186 behavior.__index = rawget(behavior, '__index') or behavior
187 behavior.__type = self
188 if not getmetatable(behavior) and getmetatable(self) then
189 setmetatable(behavior, getmetatable(self).behavior)
190 end
191 self.behavior = behavior
192end
194--- Create a new subtype, using the given table as base.
195-- @param name name of the new type
196-- @param[opt] behavior behavioral object for the new type.
197-- @return a new type
198-- @local
199function Type:make_subtype(name, behavior)
200 local newtype = setmetatable({}, self)
201 newtype.name = name
202 newtype.__index = newtype
203 newtype:set_behavior(behavior)
204 return newtype
205end
208------------------------------------------------------------------------
209-- The base class for pandoc's AST elements.
210-- @type AstElement
211-- @local
212local AstElement = Type:make_subtype 'AstElement'
213AstElement.__call = function(t, ...)
214 local success, ret = pcall(t.new, t, ...)
215 if success then
216 return setmetatable(ret, t.behavior)
217 else
218 error(string.format('Constructor for %s failed: %s\n', t.name, ret))
219 end
220end
222--- Make a new subtype which constructs a new value when called.
223-- @local
224function AstElement:make_subtype(...)
225 local newtype = Type.make_subtype(self, ...)
226 newtype.__call = self.__call
227 return newtype
228end
230--- Create a new constructor
231-- @local
232-- @param tag Tag used to identify the constructor
233-- @param fn Function to be called when constructing a new element
234-- @param accessors names to use as accessors for numerical fields
235-- @return function that constructs a new element
236function AstElement:create_constructor(tag, fn, accessors)
237 local constr = self:make_subtype(tag, create_accessor_behavior(tag, accessors))
238 function constr:new(...)
239 return setmetatable(fn(...), self.behavior)
240 end
241 self.constructor = self.constructor or {}
242 self.constructor[tag] = constr
243 return constr
244end
246--- Convert AstElement input into a list if necessary.
247-- @local
248local function ensureList (x)
249 if x.tag then
250 -- Lists are not tagged, but all elements are
251 return List:new{x}
252 else
253 return List:new(x)
254 end
255end
257--- Ensure a given object is an Inline element, or convert it into one.
258-- @local
259local function ensureInlineList (x)
260 if type(x) == 'string' then
261 return List:new{M.Str(x)}
262 else
263 return ensureList(x)
264 end
265end
267--- Ensure that the given object is a definition pair, convert if necessary.
268-- @local
269local function ensureDefinitionPairs (pair)
270 local inlines = ensureInlineList(pair[1] or {})
271 local blocks = ensureList(pair[2] or {}):map(ensureList)
272 return {inlines, blocks}
273end
275------------------------------------------------------------------------
276--- Pandoc Document
277-- @section document
279--- A complete pandoc document
280-- @function Pandoc
281-- @tparam {Block,...} blocks document content
282-- @tparam[opt] Meta meta document meta data
283M.Pandoc = AstElement:make_subtype'Pandoc'
284M.Pandoc.behavior.clone = M.types.clone.Pandoc
285function M.Pandoc:new (blocks, meta)
286 return {
287 blocks = ensureList(blocks),
288 meta = meta or {},
289 }
290end
292-- DEPRECATED synonym:
293M.Doc = M.Pandoc
295------------------------------------------------------------------------
296-- Meta
297-- @section Meta
299--- Create a new Meta object. It sets the metatable of the given table to
300--- `Meta`.
301-- @function Meta
302-- @tparam meta table table containing document meta information
303M.Meta = AstElement:make_subtype'Meta'
304M.Meta.behavior.clone = M.types.clone.Meta
305function M.Meta:new (meta) return meta end
308------------------------------------------------------------------------
309-- MetaValue
310-- @section MetaValue
311M.MetaValue = AstElement:make_subtype('MetaValue')
312M.MetaValue.behavior.clone = M.types.clone.MetaValue
314--- Meta blocks
315-- @function MetaBlocks
316-- @tparam {Block,...} blocks blocks
317M.MetaBlocks = M.MetaValue:create_constructor(
318 'MetaBlocks',
319 function (content) return ensureList(content) end
322--- Meta inlines
323-- @function MetaInlines
324-- @tparam {Inline,...} inlines inlines
325M.MetaInlines = M.MetaValue:create_constructor(
326 'MetaInlines',
327 function (content) return ensureInlineList(content) end
330--- Meta list
331-- @function MetaList
332-- @tparam {MetaValue,...} meta_values list of meta values
333M.MetaList = M.MetaValue:create_constructor(
334 'MetaList',
335 function (content)
336 if content.tag == 'MetaList' then
337 return content
338 end
339 return ensureList(content)
340 end
342for k, v in pairs(List) do
343 M.MetaList.behavior[k] = v
344end
346--- Meta map
347-- @function MetaMap
348-- @tparam table key_value_map a string-indexed map of meta values
349M.MetaMap = M.MetaValue:create_constructor(
350 "MetaMap",
351 function (mm) return mm end
354--- Creates string to be used in meta data.
355-- Does nothing, lua strings are meta strings.
356-- @function MetaString
357-- @tparam string str string value
358function M.MetaString(str)
359 return str
360end
362--- Creates boolean to be used in meta data.
363-- Does nothing, lua booleans are meta booleans.
364-- @function MetaBool
365-- @tparam boolean bool boolean value
366function M.MetaBool(bool)
367 return bool
368end
370------------------------------------------------------------------------
371-- Blocks
372-- @section Block
374--- Block elements
375M.Block = AstElement:make_subtype'Block'
376M.Block.behavior.clone = M.types.clone.Block
378--- Creates a block quote element
379-- @function BlockQuote
380-- @tparam {Block,...} content block content
381-- @treturn Block block quote element
382M.BlockQuote = M.Block:create_constructor(
383 "BlockQuote",
384 function(content) return {c = ensureList(content)} end,
385 "content"
388--- Creates a bullet (i.e. unordered) list.
389-- @function BulletList
390-- @tparam {{Block,...},...} content list of items
391-- @treturn Block bullet list element
392M.BulletList = M.Block:create_constructor(
393 "BulletList",
394 function(content) return {c = ensureList(content):map(ensureList)} end,
395 "content"
398--- Creates a code block element
399-- @function CodeBlock
400-- @tparam string text code string
401-- @tparam[opt] Attr attr element attributes
402-- @treturn Block code block element
403M.CodeBlock = M.Block:create_constructor(
404 "CodeBlock",
405 function(text, attr) return {c = {attr or M.Attr(), text}} end,
406 {{attr = {"identifier", "classes", "attributes"}}, "text"}
409--- Creates a definition list, containing terms and their explanation.
410-- @function DefinitionList
411-- @tparam {{{Inline,...},{{Block,...}}},...} content list of items
412-- @treturn Block definition list element
413M.DefinitionList = M.Block:create_constructor(
414 "DefinitionList",
415 function(content)
416 return {c = ensureList(content):map(ensureDefinitionPairs)}
417 end,
418 "content"
421--- Creates a div element
422-- @function Div
423-- @tparam {Block,...} content block content
424-- @tparam[opt] Attr attr element attributes
425-- @treturn Block div element
426M.Div = M.Block:create_constructor(
427 "Div",
428 function(content, attr)
429 return {c = {attr or M.Attr(), ensureList(content)}}
430 end,
431 {{attr = {"identifier", "classes", "attributes"}}, "content"}
434--- Creates a header element.
435-- @function Header
436-- @tparam int level header level
437-- @tparam {Inline,...} content inline content
438-- @tparam[opt] Attr attr element attributes
439-- @treturn Block header element
440M.Header = M.Block:create_constructor(
441 "Header",
442 function(level, content, attr)
443 return {c = {level, attr or M.Attr(), ensureInlineList(content)}}
444 end,
445 {"level", {attr = {"identifier", "classes", "attributes"}}, "content"}
448--- Creates a horizontal rule.
449-- @function HorizontalRule
450-- @treturn Block horizontal rule
451M.HorizontalRule = M.Block:create_constructor(
452 "HorizontalRule",
453 function() return {} end
456--- Creates a line block element.
457-- @function LineBlock
458-- @tparam {{Inline,...},...} content inline content
459-- @treturn Block line block element
460M.LineBlock = M.Block:create_constructor(
461 "LineBlock",
462 function(content) return {c = ensureList(content):map(ensureInlineList)} end,
463 "content"
466--- Creates a null element.
467-- @function Null
468-- @treturn Block null element
469M.Null = M.Block:create_constructor(
470 "Null",
471 function() return {} end
474--- Creates an ordered list.
475-- @function OrderedList
476-- @tparam {{Block,...},...} items list items
477-- @param[opt] listAttributes list parameters
478-- @treturn Block ordered list element
479M.OrderedList = M.Block:create_constructor(
480 "OrderedList",
481 function(items, listAttributes)
482 listAttributes = listAttributes or M.ListAttributes()
483 return {c = {listAttributes, ensureList(items):map(ensureList)}}
484 end,
485 {{listAttributes = {"start", "style", "delimiter"}}, "content"}
488--- Creates a para element.
489-- @function Para
490-- @tparam {Inline,...} content inline content
491-- @treturn Block paragraph element
492M.Para = M.Block:create_constructor(
493 "Para",
494 function(content) return {c = ensureInlineList(content)} end,
495 "content"
498--- Creates a plain element.
499-- @function Plain
500-- @tparam {Inline,...} content inline content
501-- @treturn Block plain element
502M.Plain = M.Block:create_constructor(
503 "Plain",
504 function(content) return {c = ensureInlineList(content)} end,
505 "content"
508--- Creates a raw content block of the specified format.
509-- @function RawBlock
510-- @tparam string format format of content
511-- @tparam string text string content
512-- @treturn Block raw block element
513M.RawBlock = M.Block:create_constructor(
514 "RawBlock",
515 function(format, text) return {c = {format, text}} end,
516 {"format", "text"}
519--- Creates a table element.
520-- @function Table
521-- @tparam {Inline,...} caption table caption
522-- @tparam {AlignDefault|AlignLeft|AlignRight|AlignCenter,...} aligns alignments
523-- @tparam {int,...} widths column widths
524-- @tparam {Block,...} headers header row
525-- @tparam {{Block,...}} rows table rows
526-- @treturn Block table element
527M.Table = M.Block:create_constructor(
528 "Table",
529 function(caption, aligns, widths, headers, rows)
530 return {
531 c = {
532 ensureInlineList(caption),
533 List:new(aligns),
534 List:new(widths),
535 List:new(headers),
536 List:new(rows)
537 }
538 }
539 end,
540 {"caption", "aligns", "widths", "headers", "rows"}
544------------------------------------------------------------------------
545-- Inline
546-- @section Inline
548--- Inline element class
549M.Inline = AstElement:make_subtype'Inline'
550M.Inline.behavior.clone = M.types.clone.Inline
552--- Creates a Cite inline element
553-- @function Cite
554-- @tparam {Inline,...} content List of inlines
555-- @tparam {Citation,...} citations List of citations
556-- @treturn Inline citations element
557M.Cite = M.Inline:create_constructor(
558 "Cite",
559 function(content, citations)
560 return {c = {ensureList(citations), ensureInlineList(content)}}
561 end,
562 {"citations", "content"}
565--- Creates a Code inline element
566-- @function Code
567-- @tparam string text code string
568-- @tparam[opt] Attr attr additional attributes
569-- @treturn Inline code element
570M.Code = M.Inline:create_constructor(
571 "Code",
572 function(text, attr) return {c = {attr or M.Attr(), text}} end,
573 {{attr = {"identifier", "classes", "attributes"}}, "text"}
576--- Creates an inline element representing emphasised text.
577-- @function Emph
578-- @tparam {Inline,..} content inline content
579-- @treturn Inline emphasis element
580M.Emph = M.Inline:create_constructor(
581 "Emph",
582 function(content) return {c = ensureInlineList(content)} end,
583 "content"
586--- Creates a Image inline element
587-- @function Image
588-- @tparam {Inline,..} caption text used to describe the image
589-- @tparam string src path to the image file
590-- @tparam[opt] string title brief image description
591-- @tparam[opt] Attr attr additional attributes
592-- @treturn Inline image element
593M.Image = M.Inline:create_constructor(
594 "Image",
595 function(caption, src, title, attr)
596 title = title or ""
597 attr = attr or M.Attr()
598 return {c = {attr, ensureInlineList(caption), {src, title}}}
599 end,
600 {{attr = {"identifier", "classes", "attributes"}}, "caption", {"src", "title"}}
603--- Create a LineBreak inline element
604-- @function LineBreak
605-- @treturn Inline linebreak element
606M.LineBreak = M.Inline:create_constructor(
607 "LineBreak",
608 function() return {} end
611--- Creates a link inline element, usually a hyperlink.
612-- @function Link
613-- @tparam {Inline,..} content text for this link
614-- @tparam string target the link target
615-- @tparam[opt] string title brief link description
616-- @tparam[opt] Attr attr additional attributes
617-- @treturn Inline image element
618M.Link = M.Inline:create_constructor(
619 "Link",
620 function(content, target, title, attr)
621 title = title or ""
622 attr = attr or M.Attr()
623 return {c = {attr, ensureInlineList(content), {target, title}}}
624 end,
625 {{attr = {"identifier", "classes", "attributes"}}, "content", {"target", "title"}}
628--- Creates a Math element, either inline or displayed.
629-- @function Math
630-- @tparam "InlineMath"|"DisplayMath" mathtype rendering specifier
631-- @tparam string text Math content
632-- @treturn Inline Math element
633M.Math = M.Inline:create_constructor(
634 "Math",
635 function(mathtype, text)
636 return {c = {mathtype, text}}
637 end,
638 {"mathtype", "text"}
640--- Creates a DisplayMath element (DEPRECATED).
641-- @function DisplayMath
642-- @tparam string text Math content
643-- @treturn Inline Math element
644M.DisplayMath = M.Inline:create_constructor(
645 "DisplayMath",
646 function(text) return M.Math("DisplayMath", text) end,
647 {"mathtype", "text"}
649--- Creates an InlineMath inline element (DEPRECATED).
650-- @function InlineMath
651-- @tparam string text Math content
652-- @treturn Inline Math element
653M.InlineMath = M.Inline:create_constructor(
654 "InlineMath",
655 function(text) return M.Math("InlineMath", text) end,
656 {"mathtype", "text"}
659--- Creates a Note inline element
660-- @function Note
661-- @tparam {Block,...} content footnote block content
662M.Note = M.Inline:create_constructor(
663 "Note",
664 function(content) return {c = ensureList(content)} end,
665 "content"
668--- Creates a Quoted inline element given the quote type and quoted content.
669-- @function Quoted
670-- @tparam "DoubleQuote"|"SingleQuote" quotetype type of quotes to be used
671-- @tparam {Inline,..} content inline content
672-- @treturn Inline quoted element
673M.Quoted = M.Inline:create_constructor(
674 "Quoted",
675 function(quotetype, content)
676 return {c = {quotetype, ensureInlineList(content)}}
677 end,
678 {"quotetype", "content"}
680--- Creates a single-quoted inline element (DEPRECATED).
681-- @function SingleQuoted
682-- @tparam {Inline,..} content inline content
683-- @treturn Inline quoted element
684-- @see Quoted
685M.SingleQuoted = M.Inline:create_constructor(
686 "SingleQuoted",
687 function(content) return M.Quoted(M.SingleQuote, content) end,
688 {"quotetype", "content"}
690--- Creates a single-quoted inline element (DEPRECATED).
691-- @function DoubleQuoted
692-- @tparam {Inline,..} content inline content
693-- @treturn Inline quoted element
694-- @see Quoted
695M.DoubleQuoted = M.Inline:create_constructor(
696 "DoubleQuoted",
697 function(content) return M.Quoted("DoubleQuote", content) end,
698 {"quotetype", "content"}
701--- Creates a RawInline inline element
702-- @function RawInline
703-- @tparam string format format of the contents
704-- @tparam string text string content
705-- @treturn Inline raw inline element
706M.RawInline = M.Inline:create_constructor(
707 "RawInline",
708 function(format, text) return {c = {format, text}} end,
709 {"format", "text"}
712--- Creates text rendered in small caps
713-- @function SmallCaps
714-- @tparam {Inline,..} content inline content
715-- @treturn Inline smallcaps element
716M.SmallCaps = M.Inline:create_constructor(
717 "SmallCaps",
718 function(content) return {c = ensureInlineList(content)} end,
719 "content"
722--- Creates a SoftBreak inline element.
723-- @function SoftBreak
724-- @treturn Inline softbreak element
725M.SoftBreak = M.Inline:create_constructor(
726 "SoftBreak",
727 function() return {} end
730--- Create a Space inline element
731-- @function Space
732-- @treturn Inline space element
733M.Space = M.Inline:create_constructor(
734 "Space",
735 function() return {} end
738--- Creates a Span inline element
739-- @function Span
740-- @tparam {Inline,..} content inline content
741-- @tparam[opt] Attr attr additional attributes
742-- @treturn Inline span element
743M.Span = M.Inline:create_constructor(
744 "Span",
745 function(content, attr)
746 return {c = {attr or M.Attr(), ensureInlineList(content)}}
747 end,
748 {{attr = {"identifier", "classes", "attributes"}}, "content"}
751--- Creates a Str inline element
752-- @function Str
753-- @tparam string text content
754-- @treturn Inline string element
755M.Str = M.Inline:create_constructor(
756 "Str",
757 function(text) return {c = text} end,
758 "text"
761--- Creates text which is striked out.
762-- @function Strikeout
763-- @tparam {Inline,..} content inline content
764-- @treturn Inline strikeout element
765M.Strikeout = M.Inline:create_constructor(
766 "Strikeout",
767 function(content) return {c = ensureInlineList(content)} end,
768 "content"
771--- Creates a Strong element, whose text is usually displayed in a bold font.
772-- @function Strong
773-- @tparam {Inline,..} content inline content
774-- @treturn Inline strong element
775M.Strong = M.Inline:create_constructor(
776 "Strong",
777 function(content) return {c = ensureInlineList(content)} end,
778 "content"
781--- Creates a Subscript inline element
782-- @function Subscript
783-- @tparam {Inline,..} content inline content
784-- @treturn Inline subscript element
785M.Subscript = M.Inline:create_constructor(
786 "Subscript",
787 function(content) return {c = ensureInlineList(content)} end,
788 "content"
791--- Creates a Superscript inline element
792-- @function Superscript
793-- @tparam {Inline,..} content inline content
794-- @treturn Inline strong element
795M.Superscript = M.Inline:create_constructor(
796 "Superscript",
797 function(content) return {c = ensureInlineList(content)} end,
798 "content"
802------------------------------------------------------------------------
803-- Element components
804-- @section components
806--- Check if the first element of a pair matches the given value.
807-- @param x key value to be checked
808-- @return function returning true iff first element of its argument matches x
809-- @local
810local function assoc_key_equals (x)
811 return function (y) return y[1] == x end
812end
814--- Lookup a value in an associative list
815-- @function lookup
816-- @local
817-- @tparam {{key, value},...} alist associative list
818-- @param key key for which the associated value is to be looked up
819local function lookup(alist, key)
820 return (List.find_if(alist, assoc_key_equals(key)) or {})[2]
821end
823--- Return an iterator which returns key-value pairs of an associative list.
824-- @function apairs
825-- @local
826-- @tparam {{key, value},...} alist associative list
827local apairs = function (alist)
828 local i = 1
829 local cur
830 function nxt ()
831 cur = rawget(alist, i)
832 if cur then
833 i = i + 1
834 return cur[1], cur[2]
835 end
836 return nil
837 end
838 return nxt, nil, nil
839end
841--- AttributeList, a metatable to allow table-like access to attribute lists
842-- represented by associative lists.
843-- @local
844local AttributeList = {
845 __index = function (t, k)
846 if type(k) == "number" then
847 return rawget(t, k)
848 else
849 return lookup(t, k)
850 end
851 end,
853 __newindex = function (t, k, v)
854 local cur, idx = List.find_if(t, assoc_key_equals(k))
855 if v == nil and not cur then
856 -- deleted key does not exists in list
857 return
858 elseif v == nil then
859 table.remove(t, idx)
860 elseif cur then
861 cur[2] = v
862 elseif type(k) == "number" then
863 rawset(t, k, v)
864 else
865 rawset(t, #t + 1, {k, v})
866 end
867 end,
869 __pairs = apairs
872--- Convert a table to an associative list. The order of key-value pairs in the
873-- alist is undefined. The table should either contain no numeric keys or
874-- already be an associative list.
875-- @local
876-- @tparam table tbl associative list or table without numeric keys.
877-- @treturn table associative list
878local to_alist = function (tbl)
879 if #tbl ~= 0 or next(tbl) == nil then
880 -- probably already an alist
881 return tbl
882 end
883 local alist = {}
884 local i = 1
885 for k, v in pairs(tbl) do
886 alist[i] = {k, v}
887 i = i + 1
888 end
889 return alist
890end
892-- Attr
894--- Create a new set of attributes (Attr).
895-- @function Attr
896-- @tparam[opt] string identifier element identifier
897-- @tparam[opt] {string,...} classes element classes
898-- @tparam[opt] table attributes table containing string keys and values
899-- @return element attributes
900M.Attr = AstElement:make_subtype'Attr'
901function M.Attr:new (identifier, classes, attributes)
902 identifier = identifier or ''
903 classes = ensureList(classes or {})
904 attributes = setmetatable(to_alist(attributes or {}), AttributeList)
905 return {identifier, classes, attributes}
906end
907M.Attr.behavior.clone = M.types.clone.Attr
908M.Attr.behavior._field_names = {identifier = 1, classes = 2, attributes = 3}
909M.Attr.behavior.__eq = utils.equals
910M.Attr.behavior.__index = function(t, k)
911 return rawget(t, getmetatable(t)._field_names[k]) or
912 getmetatable(t)[k]
913end
914M.Attr.behavior.__newindex = function(t, k, v)
915 if getmetatable(t)._field_names[k] then
916 rawset(t, getmetatable(t)._field_names[k], v)
917 else
918 rawset(t, k, v)
919 end
920end
921M.Attr.behavior.__pairs = function(t)
922 local field_names = M.Attr.behavior._field_names
923 local fields = {}
924 for name, i in pairs(field_names) do
925 fields[i] = name
926 end
927 return make_next_function(fields), t, nil
928end
930-- Citation
931M.Citation = AstElement:make_subtype'Citation'
932M.Citation.behavior.clone = M.types.clone.Citation
934--- Creates a single citation.
935-- @function Citation
936-- @tparam string id citation identifier (like a bibtex key)
937-- @tparam AuthorInText|SuppressAuthor|NormalCitation mode citation mode
938-- @tparam[opt] {Inline,...} prefix citation prefix
939-- @tparam[opt] {Inline,...} suffix citation suffix
940-- @tparam[opt] int note_num note number
941-- @tparam[opt] int hash hash number
942function M.Citation:new (id, mode, prefix, suffix, note_num, hash)
943 return {
944 id = id,
945 mode = mode,
946 prefix = ensureList(prefix or {}),
947 suffix = ensureList(suffix or {}),
948 note_num = note_num or 0,
949 hash = hash or 0,
950 }
951end
953-- ListAttributes
954M.ListAttributes = AstElement:make_subtype 'ListAttributes'
955M.ListAttributes.behavior.clone = M.types.clone.ListAttributes
957--- Creates a set of list attributes.
958-- @function ListAttributes
959-- @tparam[opt] integer start number of the first list item
960-- @tparam[opt] string style style used for list numbering
961-- @tparam[opt] DefaultDelim|Period|OneParen|TwoParens delimiter delimiter of list numbers
962-- @treturn table list attributes table
963function M.ListAttributes:new (start, style, delimiter)
964 start = start or 1
965 style = style or 'DefaultStyle'
966 delimiter = delimiter or 'DefaultDelim'
967 return {start, style, delimiter}
968end
969M.ListAttributes.behavior._field_names = {start = 1, style = 2, delimiter = 3}
970M.ListAttributes.behavior.__eq = utils.equals
971M.ListAttributes.behavior.__index = function (t, k)
972 return rawget(t, getmetatable(t)._field_names[k]) or
973 getmetatable(t)[k]
974end
975M.ListAttributes.behavior.__newindex = function (t, k, v)
976 if getmetatable(t)._field_names[k] then
977 rawset(t, getmetatable(t)._field_names[k], v)
978 else
979 rawset(t, k, v)
980 end
981end
982M.ListAttributes.behavior.__pairs = function(t)
983 local field_names = M.ListAttributes.behavior._field_names
984 local fields = {}
985 for name, i in pairs(field_names) do
986 fields[i] = name
987 end
988 return make_next_function(fields), t, nil
989end
992------------------------------------------------------------------------
993-- Constants
994-- @section constants
996--- Author name is mentioned in the text.
997-- @see Citation
998-- @see Cite
999M.AuthorInText = "AuthorInText"
1001--- Author name is suppressed.
1002-- @see Citation
1003-- @see Cite
1004M.SuppressAuthor = "SuppressAuthor"
1006--- Default citation style is used.
1007-- @see Citation
1008-- @see Cite
1009M.NormalCitation = "NormalCitation"
1011--- Table cells aligned left.
1012-- @see Table
1013M.AlignLeft = "AlignLeft"
1015--- Table cells right-aligned.
1016-- @see Table
1017M.AlignRight = "AlignRight"
1019--- Table cell content is centered.
1020-- @see Table
1021M.AlignCenter = "AlignCenter"
1023--- Table cells are alignment is unaltered.
1024-- @see Table
1025M.AlignDefault = "AlignDefault"
1027--- Default list number delimiters are used.
1028-- @see OrderedList
1029M.DefaultDelim = "DefaultDelim"
1031--- List numbers are delimited by a period.
1032-- @see OrderedList
1033M.Period = "Period"
1035--- List numbers are delimited by a single parenthesis.
1036-- @see OrderedList
1037M.OneParen = "OneParen"
1039--- List numbers are delimited by a double parentheses.
1040-- @see OrderedList
1041M.TwoParens = "TwoParens"
1043--- List are numbered in the default style
1044-- @see OrderedList
1045M.DefaultStyle = "DefaultStyle"
1047--- List items are numbered as examples.
1048-- @see OrderedList
1049M.Example = "Example"
1051--- List are numbered using decimal integers.
1052-- @see OrderedList
1053M.Decimal = "Decimal"
1055--- List are numbered using lower-case roman numerals.
1056-- @see OrderedList
1057M.LowerRoman = "LowerRoman"
1059--- List are numbered using upper-case roman numerals
1060-- @see OrderedList
1061M.UpperRoman = "UpperRoman"
1063--- List are numbered using lower-case alphabetic characters.
1064-- @see OrderedList
1065M.LowerAlpha = "LowerAlpha"
1067--- List are numbered using upper-case alphabetic characters.
1068-- @see OrderedList
1069M.UpperAlpha = "UpperAlpha"
1071------------------------------------------------------------------------
1072-- Functions which have moved to different modules
1073M.sha1 = utils.sha1
1075return M