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