git.iko.soy/typeable/pandoc
on master help sign in
Add basic lua List module (#4099)
0105a3cAlbert Krewinkel
data/List.luanew file+110
@@ -0,0 +1,110 @@
1+--[[
2+List.lua
3+
4+Copyright © 2017 Albert Krewinkel
5+
6+Permission to use, copy, modify, and/or distribute this software for any purpose
7+with or without fee is hereby granted, provided that the above copyright notice
8+and this permission notice appear in all copies.
9+
10+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
11+REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
12+FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
13+INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
14+OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
15+TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
16+THIS SOFTWARE.
17+]]
18+
19+---
20+-- Lua functions for pandoc scripts.
21+--
22+-- @author Albert Krewinkel
23+-- @copyright © 2017 Albert Krewinkel
24+-- @license MIT
25+local M = {
26+ _VERSION = "0.1.0"
27+}
28+
29+------------------------------------------------------------------------
30+-- Metatable for lists
31+-- @type List
32+local List = {}
33+
34+function List:new (o)
35+ o = o or {}
36+ setmetatable(o, self)
37+ self.__index = self
38+ return o
39+end
40+
41+function List:__call (o)
42+ return self:new(o)
43+end
44+
45+--- Concatenates two lists.
46+function List:__concat (list)
47+ local res = List.clone(self)
48+ List.extend(res, list)
49+ return res
50+end
51+
52+--- Returns a (shallow) copy of the list.
53+function List:clone ()
54+ local lst = setmetatable({}, getmetatable(self))
55+ List.extend(lst, self)
56+ return lst
57+end
58+
59+--- Appends the given list to the end of this list.
60+function List:includes (needle)
61+ for i = 1, #self do
62+ if self[i] == needle then
63+ return true
64+ end
65+ end
66+ return false
67+end
68+
69+--- Returns the value and index of the first occurrence of the given item.
70+-- @param needle item to search for
71+-- @return first item equal to the needle, or nil if no such item exists.
72+-- @return index of that element
73+function List:find (needle, init)
74+ return List.find_if(self, function(x) return x == needle end, init)
75+end
76+
77+--- Returns the value and index of the first element for which test returns true.
78+-- @param test the test function
79+-- @param init index at which the search is started
80+-- @return first item for which `test` succeeds, or nil if no such item exists.
81+-- @return index of that element
82+function List:find_if (test, init)
83+ init = (init == nil and 1) or (init < 0 and #self - init) or init
84+ for i = init, #self do
85+ if test(self[i], i) then
86+ return self[i], i
87+ end
88+ end
89+ return nil
90+end
91+
92+--- Add the given list to the end of this list.
93+-- @param list list to appended
94+function List:extend (list)
95+ for i = 1, #list do
96+ self[#self + 1] = list[i]
97+ end
98+end
99+
100+-- Returns a copy of the current list by applying the given function to all
101+-- elements.
102+function List:map (fn)
103+ local res = setmetatable({}, getmetatable(self))
104+ for i = 1, #self do
105+ res[i] = fn(self[i])
106+ end
107+ return res
108+end
109+
110+return List
data/pandoc.lua+39−41
@@ -26,6 +26,8 @@ local M = {
26 _VERSION = "0.3.0"
26 _VERSION = "0.3.0"
27}
27}
28
28
29+local List = require 'List'
30+
29------------------------------------------------------------------------
31------------------------------------------------------------------------
30-- The base class for pandoc's AST elements.
32-- The base class for pandoc's AST elements.
31-- @type Element
33-- @type Element
@@ -141,7 +143,7 @@ end
141function M.Pandoc(blocks, meta)
143function M.Pandoc(blocks, meta)
142 meta = meta or {}
144 meta = meta or {}
143 return {
145 return {
144− ["blocks"] = blocks,
146+ ["blocks"] = List:new(blocks),
145 ["meta"] = meta,
147 ["meta"] = meta,
146 ["pandoc-api-version"] = {1,17,0,5},
148 ["pandoc-api-version"] = {1,17,0,5},
147 }
149 }
@@ -250,7 +252,7 @@ M.CodeBlock = M.Block:create_constructor(
250-- @treturn Block block quote element
252-- @treturn Block block quote element
251M.DefinitionList = M.Block:create_constructor(
253M.DefinitionList = M.Block:create_constructor(
252 "DefinitionList",
254 "DefinitionList",
253− function(content) return {c = content} end,
255+ function(content) return {c = List:new(content)} end,
254 "content"
256 "content"
255)
257)
256
258
@@ -262,7 +264,7 @@ M.DefinitionList = M.Block:create_constructor(
262M.Div = M.Block:create_constructor(
264M.Div = M.Block:create_constructor(
263 "Div",
265 "Div",
264 function(content, attr)
266 function(content, attr)
265− return {c = {attr or M.Attr(), content}}
267+ return {c = {attr or M.Attr(), List:new(content)}}
266 end,
268 end,
267 {{"identifier", "classes", "attributes"}, "content"}
269 {{"identifier", "classes", "attributes"}, "content"}
268)
270)
@@ -295,7 +297,7 @@ M.HorizontalRule = M.Block:create_constructor(
295-- @treturn Block block quote element
297-- @treturn Block block quote element
296M.LineBlock = M.Block:create_constructor(
298M.LineBlock = M.Block:create_constructor(
297 "LineBlock",
299 "LineBlock",
298− function(content) return {c = content} end,
300+ function(content) return {c = List:new(content)} end,
299 "content"
301 "content"
300)
302)
301
303
@@ -316,7 +318,7 @@ M.OrderedList = M.Block:create_constructor(
316 "OrderedList",
318 "OrderedList",
317 function(items, listAttributes)
319 function(items, listAttributes)
318 listAttributes = listAttributes or {1, M.DefaultStyle, M.DefaultDelim}
320 listAttributes = listAttributes or {1, M.DefaultStyle, M.DefaultDelim}
319− return {c = {listAttributes, items}}
321+ return {c = {listAttributes, List:new(items)}}
320 end,
322 end,
321 {{"start", "style", "delimiter"}, "content"}
323 {{"start", "style", "delimiter"}, "content"}
322)
324)
@@ -327,7 +329,7 @@ M.OrderedList = M.Block:create_constructor(
327-- @treturn Block block quote element
329-- @treturn Block block quote element
328M.Para = M.Block:create_constructor(
330M.Para = M.Block:create_constructor(
329 "Para",
331 "Para",
330− function(content) return {c = content} end,
332+ function(content) return {c = List:new(content)} end,
331 "content"
333 "content"
332)
334)
333
335
@@ -337,7 +339,7 @@ M.Para = M.Block:create_constructor(
337-- @treturn Block block quote element
339-- @treturn Block block quote element
338M.Plain = M.Block:create_constructor(
340M.Plain = M.Block:create_constructor(
339 "Plain",
341 "Plain",
340− function(content) return {c = content} end,
342+ function(content) return {c = List:new(content)} end,
341 "content"
343 "content"
342)
344)
343
345
@@ -363,7 +365,15 @@ M.RawBlock = M.Block:create_constructor(
363M.Table = M.Block:create_constructor(
365M.Table = M.Block:create_constructor(
364 "Table",
366 "Table",
365 function(caption, aligns, widths, headers, rows)
367 function(caption, aligns, widths, headers, rows)
366− return {c = {caption, aligns, widths, headers, rows}}
368+ return {
369+ c = {
370+ List:new(caption),
371+ List:new(aligns),
372+ List:new(widths),
373+ List:new(headers),
374+ List:new(rows)
375+ }
376+ }
367 end,
377 end,
368 {"caption", "aligns", "widths", "headers", "rows"}
378 {"caption", "aligns", "widths", "headers", "rows"}
369)
379)
@@ -386,7 +396,9 @@ end
386-- @treturn Inline citations element
396-- @treturn Inline citations element
387M.Cite = M.Inline:create_constructor(
397M.Cite = M.Inline:create_constructor(
388 "Cite",
398 "Cite",
389− function(content, citations) return {c = {citations, content}} end,
399+ function(content, citations)
400+ return {c = {List:new(citations), List:new(content)}}
401+ end,
390 {"citations", "content"}
402 {"citations", "content"}
391)
403)
392
404
@@ -407,7 +419,7 @@ M.Code = M.Inline:create_constructor(
407-- @treturn Inline emphasis element
419-- @treturn Inline emphasis element
408M.Emph = M.Inline:create_constructor(
420M.Emph = M.Inline:create_constructor(
409 "Emph",
421 "Emph",
410− function(content) return {c = content} end,
422+ function(content) return {c = List:new(content)} end,
411 "content"
423 "content"
412)
424)
413
425
@@ -423,7 +435,7 @@ M.Image = M.Inline:create_constructor(
423 function(caption, src, title, attr)
435 function(caption, src, title, attr)
424 title = title or ""
436 title = title or ""
425 attr = attr or M.Attr()
437 attr = attr or M.Attr()
426− return {c = {attr, caption, {src, title}}}
438+ return {c = {attr, List:new(caption), {src, title}}}
427 end,
439 end,
428 {{"identifier", "classes", "attributes"}, "caption", {"src", "title"}}
440 {{"identifier", "classes", "attributes"}, "caption", {"src", "title"}}
429)
441)
@@ -448,7 +460,7 @@ M.Link = M.Inline:create_constructor(
448 function(content, target, title, attr)
460 function(content, target, title, attr)
449 title = title or ""
461 title = title or ""
450 attr = attr or M.Attr()
462 attr = attr or M.Attr()
451− return {c = {attr, content, {target, title}}}
463+ return {c = {attr, List:new(content), {target, title}}}
452 end,
464 end,
453 {{"identifier", "classes", "attributes"}, "content", {"target", "title"}}
465 {{"identifier", "classes", "attributes"}, "content", {"target", "title"}}
454)
466)
@@ -489,7 +501,7 @@ M.InlineMath = M.Inline:create_constructor(
489-- @tparam {Block,...} content footnote block content
501-- @tparam {Block,...} content footnote block content
490M.Note = M.Inline:create_constructor(
502M.Note = M.Inline:create_constructor(
491 "Note",
503 "Note",
492− function(content) return {c = content} end,
504+ function(content) return {c = List:new(content)} end,
493 "content"
505 "content"
494)
506)
495
507
@@ -500,7 +512,7 @@ M.Note = M.Inline:create_constructor(
500-- @treturn Inline quoted element
512-- @treturn Inline quoted element
501M.Quoted = M.Inline:create_constructor(
513M.Quoted = M.Inline:create_constructor(
502 "Quoted",
514 "Quoted",
503− function(quotetype, content) return {c = {quotetype, content}} end,
515+ function(quotetype, content) return {c = {quotetype, List:new(content)}} end,
504 {"quotetype", "content"}
516 {"quotetype", "content"}
505)
517)
506--- Creates a single-quoted inline element (DEPRECATED).
518--- Creates a single-quoted inline element (DEPRECATED).
@@ -541,7 +553,7 @@ M.RawInline = M.Inline:create_constructor(
541-- @treturn Inline smallcaps element
553-- @treturn Inline smallcaps element
542M.SmallCaps = M.Inline:create_constructor(
554M.SmallCaps = M.Inline:create_constructor(
543 "SmallCaps",
555 "SmallCaps",
544− function(content) return {c = content} end,
556+ function(content) return {c = List:new(content)} end,
545 "content"
557 "content"
546)
558)
547
559
@@ -568,7 +580,9 @@ M.Space = M.Inline:create_constructor(
568-- @treturn Inline span element
580-- @treturn Inline span element
569M.Span = M.Inline:create_constructor(
581M.Span = M.Inline:create_constructor(
570 "Span",
582 "Span",
583+ function(content, attr)
571− function(content, attr) return {c = {attr or M.Attr(), content}} end,
584+ return {c = {attr or M.Attr(), List:new(content)}}
585+ end,
572 {{"identifier", "classes", "attributes"}, "content"}
586 {{"identifier", "classes", "attributes"}, "content"}
573)
587)
574
588
@@ -588,7 +602,7 @@ M.Str = M.Inline:create_constructor(
588-- @treturn Inline strikeout element
602-- @treturn Inline strikeout element
589M.Strikeout = M.Inline:create_constructor(
603M.Strikeout = M.Inline:create_constructor(
590 "Strikeout",
604 "Strikeout",
591− function(content) return {c = content} end,
605+ function(content) return {c = List:new(content)} end,
592 "content"
606 "content"
593)
607)
594
608
@@ -598,7 +612,7 @@ M.Strikeout = M.Inline:create_constructor(
598-- @treturn Inline strong element
612-- @treturn Inline strong element
599M.Strong = M.Inline:create_constructor(
613M.Strong = M.Inline:create_constructor(
600 "Strong",
614 "Strong",
601− function(content) return {c = content} end,
615+ function(content) return {c = List:new(content)} end,
602 "content"
616 "content"
603)
617)
604
618
@@ -608,7 +622,7 @@ M.Strong = M.Inline:create_constructor(
608-- @treturn Inline subscript element
622-- @treturn Inline subscript element
609M.Subscript = M.Inline:create_constructor(
623M.Subscript = M.Inline:create_constructor(
610 "Subscript",
624 "Subscript",
611− function(content) return {c = content} end,
625+ function(content) return {c = List:new(content)} end,
612 "content"
626 "content"
613)
627)
614
628
@@ -618,7 +632,7 @@ M.Subscript = M.Inline:create_constructor(
618-- @treturn Inline strong element
632-- @treturn Inline strong element
619M.Superscript = M.Inline:create_constructor(
633M.Superscript = M.Inline:create_constructor(
620 "Superscript",
634 "Superscript",
621− function(content) return {c = content} end,
635+ function(content) return {c = List:new(content)} end,
622 "content"
636 "content"
623)
637)
624
638
@@ -627,24 +641,8 @@ M.Superscript = M.Inline:create_constructor(
627-- Helpers
641-- Helpers
628-- @section helpers
642-- @section helpers
629
643
630−-- Find a value pair in a list.
631−-- @function find
632−-- @tparam table list to be searched
633−-- @param needle element to search for
634−-- @param[opt] key when non-nil, compare on this field of each list element
635−local function find (alist, needle, key)
644+local function assoc_key_equals (x)
636− local test
645+ return function (y) return y[1] == x end
637− if key then
638− test = function(x) return x[key] == needle end
639− else
640− test = function(x) return x == needle end
641− end
642− for i, k in ipairs(alist) do
643− if test(k) then
644− return i, k
645− end
646− end
647− return nil
648end
646end
649
647
650-- Lookup a value in an associative list
648-- Lookup a value in an associative list
@@ -652,7 +650,7 @@ end
652-- @tparam {{key, value},...} alist associative list
650-- @tparam {{key, value},...} alist associative list
653-- @param key key for which the associated value is to be looked up
651-- @param key key for which the associated value is to be looked up
654local function lookup(alist, key)
652local function lookup(alist, key)
655− return (select(2, find(alist, key, 1)) or {})[2]
653+ return (List.find_if(alist, assoc_key_equals(key)) or {})[2]
656end
654end
657
655
658--- Return an iterator which returns key-value pairs of an associative list.
656--- Return an iterator which returns key-value pairs of an associative list.
@@ -684,7 +682,7 @@ local AttributeList = {
684 end,
682 end,
685
683
686 __newindex = function (t, k, v)
684 __newindex = function (t, k, v)
687− local idx, cur = find(t, k, 1)
685+ local cur, idx = List.find_if(t, assoc_key_equals(k))
688 if v == nil then
686 if v == nil then
689 table.remove(t, idx)
687 table.remove(t, idx)
690 elseif cur then
688 elseif cur then
@@ -729,7 +727,7 @@ M.Attr._field_names = {identifier = 1, classes = 2, attributes = 3}
729-- @return element attributes
727-- @return element attributes
730M.Attr.__call = function(t, identifier, classes, attributes)
728M.Attr.__call = function(t, identifier, classes, attributes)
731 identifier = identifier or ''
729 identifier = identifier or ''
732− classes = classes or {}
730+ classes = List:new(classes or {})
733 attributes = setmetatable(to_alist(attributes or {}), AttributeList)
731 attributes = setmetatable(to_alist(attributes or {}), AttributeList)
734 local attr = {identifier, classes, attributes}
732 local attr = {identifier, classes, attributes}
735 setmetatable(attr, t)
733 setmetatable(attr, t)
pandoc.cabal+2
@@ -110,6 +110,8 @@ data-files:
110 data/sample.lua
110 data/sample.lua
111 -- pandoc lua module
111 -- pandoc lua module
112 data/pandoc.lua
112 data/pandoc.lua
113+ -- lua List module
114+ data/List.lua
113 -- sample highlighting theme
115 -- sample highlighting theme
114 data/default.theme
116 data/default.theme
115 -- bash completion template
117 -- bash completion template
src/Text/Pandoc/Lua/PandocModule.hs+23−5
@@ -59,10 +59,12 @@ import qualified Foreign.Lua as Lua
59import qualified Text.Pandoc.MediaBag as MB
59import qualified Text.Pandoc.MediaBag as MB
60import Text.Pandoc.Lua.Filter (walkInlines, walkBlocks, LuaFilter)
60import Text.Pandoc.Lua.Filter (walkInlines, walkBlocks, LuaFilter)
61
61
62−-- | Push the "pandoc" on the lua stack.
62+-- | Push the "pandoc" on the lua stack. Requires the `list` module to be
63+-- loaded.
63pushPandocModule :: Maybe FilePath -> Lua ()
64pushPandocModule :: Maybe FilePath -> Lua ()
64pushPandocModule datadir = do
65pushPandocModule datadir = do
66+ loadListModule datadir
65− script <- liftIO (pandocModuleScript datadir)
67+ script <- liftIO (moduleScript datadir "pandoc.lua")
66 status <- Lua.loadstring script
68 status <- Lua.loadstring script
67 unless (status /= Lua.OK) $ Lua.call 0 1
69 unless (status /= Lua.OK) $ Lua.call 0 1
68 addFunction "_pipe" pipeFn
70 addFunction "_pipe" pipeFn
@@ -72,9 +74,25 @@ pushPandocModule datadir = do
72 addFunction "walk_inline" walkInline
74 addFunction "walk_inline" walkInline
73
75
74-- | Get the string representation of the pandoc module
76-- | Get the string representation of the pandoc module
75−pandocModuleScript :: Maybe FilePath -> IO String
77+moduleScript :: Maybe FilePath -> FilePath -> IO String
76−pandocModuleScript datadir = unpack <$>
78+moduleScript datadir moduleFile = unpack <$>
77− runIOorExplode (setUserDataDir datadir >> readDataFile "pandoc.lua")
79+ runIOorExplode (setUserDataDir datadir >> readDataFile moduleFile)
80+
81+-- Loads pandoc's list module without assigning it to a variable.
82+pushListModule :: Maybe FilePath -> Lua ()
83+pushListModule datadir = do
84+ script <- liftIO (moduleScript datadir "List.lua")
85+ status <- Lua.loadstring script
86+ if status == Lua.OK
87+ then Lua.call 0 1
88+ else Lua.throwTopMessageAsError' ("Error while loading module `list`\n" ++)
89+
90+loadListModule :: Maybe FilePath -> Lua ()
91+loadListModule datadir = do
92+ Lua.getglobal' "package.loaded"
93+ pushListModule datadir
94+ Lua.setfield (-2) "List"
95+ Lua.pop 1
78
96
79walkElement :: (ToLuaStack a, Walkable [Inline] a, Walkable [Block] a)
97walkElement :: (ToLuaStack a, Walkable [Inline] a, Walkable [Block] a)
80 => a -> LuaFilter -> Lua NumResults
98 => a -> LuaFilter -> Lua NumResults