pandoc-lua-marshal 0.1.2 → 0.1.3
raw patch · 25 files changed
+941/−172 lines, 25 filesPVP: major bump suggested
API removals or changes: PVP suggests a major version bump
API changes (from Hackage documentation)
- Text.Pandoc.Lua.Marshal.Filter: newtype Filter
+ Text.Pandoc.Lua.Marshal.Filter: WalkForEachType :: WalkingOrder
+ Text.Pandoc.Lua.Marshal.Filter: WalkTopdown :: WalkingOrder
+ Text.Pandoc.Lua.Marshal.Filter: [filterMap] :: Filter -> Map Name FilterFunction
+ Text.Pandoc.Lua.Marshal.Filter: [filterWalkingOrder] :: Filter -> WalkingOrder
+ Text.Pandoc.Lua.Marshal.Filter: data Filter
+ Text.Pandoc.Lua.Marshal.Filter: data WalkingOrder
+ Text.Pandoc.Lua.Marshal.Row: mkRow :: LuaError e => DocumentedFunction e
+ Text.Pandoc.Lua.Marshal.Row: peekRow :: LuaError e => Peeker e Row
+ Text.Pandoc.Lua.Marshal.Row: peekRowFuzzy :: LuaError e => Peeker e Row
+ Text.Pandoc.Lua.Marshal.Row: pushRow :: LuaError e => Row -> LuaE e ()
+ Text.Pandoc.Lua.Marshal.Row: typeRow :: LuaError e => DocumentedType e Row
+ Text.Pandoc.Lua.Marshal.TableFoot: mkTableFoot :: LuaError e => DocumentedFunction e
+ Text.Pandoc.Lua.Marshal.TableFoot: peekTableFoot :: LuaError e => Peeker e TableFoot
+ Text.Pandoc.Lua.Marshal.TableFoot: pushTableFoot :: LuaError e => TableFoot -> LuaE e ()
+ Text.Pandoc.Lua.Marshal.TableFoot: typeTableFoot :: LuaError e => DocumentedType e TableFoot
+ Text.Pandoc.Lua.Marshal.TableHead: mkTableHead :: LuaError e => DocumentedFunction e
+ Text.Pandoc.Lua.Marshal.TableHead: peekTableHead :: LuaError e => Peeker e TableHead
+ Text.Pandoc.Lua.Marshal.TableHead: pushTableHead :: LuaError e => TableHead -> LuaE e ()
+ Text.Pandoc.Lua.Marshal.TableHead: typeTableHead :: LuaError e => DocumentedType e TableHead
+ Text.Pandoc.Lua.Marshal.TableParts: mkRow :: LuaError e => DocumentedFunction e
+ Text.Pandoc.Lua.Marshal.TableParts: mkTableFoot :: LuaError e => DocumentedFunction e
+ Text.Pandoc.Lua.Marshal.TableParts: mkTableHead :: LuaError e => DocumentedFunction e
+ Text.Pandoc.Lua.Marshal.TableParts: peekRowFuzzy :: LuaError e => Peeker e Row
- Text.Pandoc.Lua.Marshal.Filter: Filter :: Map Name FilterFunction -> Filter
+ Text.Pandoc.Lua.Marshal.Filter: Filter :: WalkingOrder -> Map Name FilterFunction -> Filter
- Text.Pandoc.Lua.Marshal.TableParts: pushRow :: LuaError e => Pusher e Row
+ Text.Pandoc.Lua.Marshal.TableParts: pushRow :: LuaError e => Row -> LuaE e ()
- Text.Pandoc.Lua.Marshal.TableParts: pushTableFoot :: LuaError e => Pusher e TableFoot
+ Text.Pandoc.Lua.Marshal.TableParts: pushTableFoot :: LuaError e => TableFoot -> LuaE e ()
- Text.Pandoc.Lua.Marshal.TableParts: pushTableHead :: LuaError e => Pusher e TableHead
+ Text.Pandoc.Lua.Marshal.TableParts: pushTableHead :: LuaError e => TableHead -> LuaE e ()
Files
- CHANGELOG.md +37/−0
- cbits/listmod.c +48/−4
- pandoc-lua-marshal.cabal +5/−1
- src/Text/Pandoc/Lua/Marshal/Attr.hs +3/−3
- src/Text/Pandoc/Lua/Marshal/Block.hs +4/−4
- src/Text/Pandoc/Lua/Marshal/Citation.hs +1/−1
- src/Text/Pandoc/Lua/Marshal/Filter.hs +24/−5
- src/Text/Pandoc/Lua/Marshal/Inline.hs +4/−3
- src/Text/Pandoc/Lua/Marshal/ListAttributes.hs +4/−3
- src/Text/Pandoc/Lua/Marshal/MetaValue.hs +7/−0
- src/Text/Pandoc/Lua/Marshal/Pandoc.hs +9/−6
- src/Text/Pandoc/Lua/Marshal/Row.hs +94/−0
- src/Text/Pandoc/Lua/Marshal/Shared.hs +59/−11
- src/Text/Pandoc/Lua/Marshal/SimpleTable.hs +5/−3
- src/Text/Pandoc/Lua/Marshal/TableFoot.hs +73/−0
- src/Text/Pandoc/Lua/Marshal/TableHead.hs +73/−0
- src/Text/Pandoc/Lua/Marshal/TableParts.hs +10/−38
- src/Text/Pandoc/Lua/Topdown.hs +138/−0
- src/Text/Pandoc/Lua/Walk.hs +30/−12
- test/test-block.lua +114/−18
- test/test-inline.lua +0/−1
- test/test-list.lua +75/−16
- test/test-metavalue.lua +4/−0
- test/test-pandoc-lua-marshal.hs +37/−43
- test/test-pandoc.lua +83/−0
CHANGELOG.md view
@@ -2,6 +2,43 @@ `pandoc-lua-marshal` uses [PVP Versioning][]. +## 0.1.3++Release pending.++### Lua changes++- The traversal order of filters can now be selected by setting+ the key `traverse` to either `'topdown'` or `'typewise'`; the+ default remains `'typewise'`.++ Topdown traversals can be cut short by returning `false` as a+ second value from the filter function. No child-element of+ the returned element is processed in that case.++- All types can be compared. Previously, comparing values of+ different types would lead to errors in a number of cases.++- Lists now have an `__eq` metamethod. List equality is checked+ by comparing both lists element-wise. Two lists are equal if+ they have the same type and have equal elements.++- If start indices in `List:find` and `List:find_if` are+ negative the start index is relative to the list length.++- TableFoot, TableHead, and Row values are marshaled as+ userdata objects.++### Haskell code++- Text.Pandoc.Lua.Marshal.Filter exports the new type+ `WalkingOrder`. The type `Filter` now contains the the+ traversal specifier as a field.++- New modules for TableFoot, TableHead, and Row, defining the+ usual marshaling methods and constructor functions for these+ types.+ ## 0.1.2 Released 2021-12-10.
cbits/listmod.c view
@@ -5,6 +5,13 @@ #define LIST_T "List" +/* compatibility with older Lua versions, which did not define this in the+ * header. */+#ifndef LUA_LOADED_TABLE+/* key, in the registry, for table of loaded modules */+#define LUA_LOADED_TABLE "_LOADED"+#endif+ /* ** Placeholder function. */@@ -14,7 +21,7 @@ ); } -/* translate a relative table position: negative means back from end */+/* Translate a relative table position: negative means back from end */ static lua_Integer posrelat (lua_Integer pos, size_t len) { if (pos >= 0) return pos; else if (0u - (size_t)pos > len) return 0;@@ -83,6 +90,42 @@ } /*+** Checks equality. Two lists are equal if and only if they have the same+** metatable and if all items are equal.+*/+static int list_eq (lua_State *L) {+ lua_settop(L, 2);+ /* compare meta tables */+ if (!(lua_getmetatable(L, 1) &&+ lua_getmetatable(L, 2) &&+ lua_rawequal(L, -1, -2))) {+ lua_pushboolean(L, 0);+ return 1;+ };+ lua_pop(L, 2); /* remove metatables */++ /* ensure both lists have the same length */+ lua_Integer len1 = luaL_len(L, 1);+ lua_Integer len2 = luaL_len(L, 2);+ if (len1 != len2) {+ lua_pushboolean(L, 0);+ return 1;+ }++ /* check element-wise equality */+ for (lua_Integer i = 1; i <= len1; i++) {+ lua_geti(L, 1, i);+ lua_geti(L, 2, i);+ if (!lua_compare(L, -1, -2, LUA_OPEQ)) {+ lua_pushboolean(L, 0);+ return 1;+ }+ }+ lua_pushboolean(L, 1);+ return 1;+}++/* ** Appends the second list to the first. */ static int list_extend (lua_State *L) {@@ -131,8 +174,8 @@ static int list_find (lua_State *L) { lua_settop(L, 3); luaL_checktype(L, 1, LUA_TTABLE);- lua_Integer start = luaL_optinteger(L, 3, 1); lua_Integer len = luaL_len(L, 1);+ lua_Integer start = posrelat(luaL_optinteger(L, 3, 1), len); for (lua_Integer i = start; i <= len; i++) { lua_geti(L, 1, i); if (lua_compare(L, 2, -1, LUA_OPEQ)) {@@ -153,8 +196,8 @@ lua_settop(L, 3); luaL_checktype(L, 1, LUA_TTABLE); luaL_checktype(L, 2, LUA_TFUNCTION);- lua_Integer start = luaL_optinteger(L, 3, 1); lua_Integer len = luaL_len(L, 1);+ lua_Integer start = posrelat(luaL_optinteger(L, 3, 1), len); for (lua_Integer i = start; i <= len; i++) { lua_pushvalue(L, 2); /* predicate function */ lua_geti(L, 1, i);@@ -194,7 +237,7 @@ luaL_checktype(L, 2, LUA_TFUNCTION); lua_Integer len = luaL_len(L, 1); lua_createtable(L, len, 0); /* create new table */- lua_getmetatable(L, 1);+ luaL_getmetatable(L, LIST_T); /* make result a generic list */ lua_setmetatable(L, 3); for (lua_Integer i = 1; i <= len; i++) { lua_pushvalue(L, 2); /* map function */@@ -250,6 +293,7 @@ static const luaL_Reg list_funcs[] = { {"__concat", list_concat},+ {"__eq", list_eq}, {"clone", list_clone}, {"extend", list_extend}, {"filter", list_filter},
pandoc-lua-marshal.cabal view
@@ -1,6 +1,6 @@ cabal-version: 2.4 name: pandoc-lua-marshal-version: 0.1.2+version: 0.1.3 synopsis: Use pandoc types in Lua description: This package provides functions to marshal and unmarshal pandoc document types to and from Lua.@@ -91,9 +91,13 @@ , Text.Pandoc.Lua.Marshal.MetaValue , Text.Pandoc.Lua.Marshal.Pandoc , Text.Pandoc.Lua.Marshal.QuoteType+ , Text.Pandoc.Lua.Marshal.Row , Text.Pandoc.Lua.Marshal.SimpleTable+ , Text.Pandoc.Lua.Marshal.TableFoot+ , Text.Pandoc.Lua.Marshal.TableHead , Text.Pandoc.Lua.Marshal.TableParts other-modules: Text.Pandoc.Lua.Marshal.Shared+ , Text.Pandoc.Lua.Topdown , Text.Pandoc.Lua.SpliceList , Text.Pandoc.Lua.Walk
src/Text/Pandoc/Lua/Marshal/Attr.hs view
@@ -38,9 +38,9 @@ typeAttr :: LuaError e => DocumentedType e Attr typeAttr = deftype "Attr" [ operation Eq $ lambda- ### liftPure2 (==)- <#> parameter peekAttr "a1" "Attr" ""- <#> parameter peekAttr "a2" "Attr" ""+ ### liftPure2 (\a b -> fromMaybe False ((==) <$> a <*> b))+ <#> parameter (optional . peekAttr) "a" "Attr" ""+ <#> parameter (optional . peekAttr) "b" "Attr" "" =#> functionResult pushBool "boolean" "whether the two are equal" , operation Tostring $ lambda ### liftPure show
src/Text/Pandoc/Lua/Marshal/Block.hs view
@@ -25,7 +25,7 @@ , walkBlocksStraight ) where -import Control.Applicative ((<|>))+import Control.Applicative ((<|>), optional) import Control.Monad.Catch (throwM) import Control.Monad ((<$!>)) import Data.Data (showConstr, toConstr)@@ -110,9 +110,9 @@ typeBlock :: forall e. LuaError e => DocumentedType e Block typeBlock = deftype "Block" [ operation Eq $ lambda- ### liftPure2 (==)- <#> parameter peekBlockFuzzy "Block" "a" ""- <#> parameter peekBlockFuzzy "Block" "b" ""+ ### liftPure2 (\a b -> fromMaybe False ((==) <$> a <*> b))+ <#> parameter (optional . peekBlockFuzzy) "Block" "a" ""+ <#> parameter (optional . peekBlockFuzzy) "Block" "b" "" =#> boolResult "whether the two values are equal" , operation Tostring $ lambda ### liftPure show
src/Text/Pandoc/Lua/Marshal/Citation.hs view
@@ -38,7 +38,7 @@ => DocumentedType e Citation typeCitation = deftype "Citation" [ operation Eq $ lambda- ### liftPure2 (==)+ ### liftPure2 (\a b -> fromMaybe False ((==) <$> a <*> b)) <#> parameter (optional . peekCitation) "Citation" "a" "" <#> parameter (optional . peekCitation) "Citation" "b" "" =#> functionResult pushBool "boolean" "true iff the citations are equal"
src/Text/Pandoc/Lua/Marshal/Filter.hs view
@@ -13,6 +13,7 @@ module Text.Pandoc.Lua.Marshal.Filter ( -- * Filters Filter (..)+ , WalkingOrder (..) , peekFilter , lookup , member@@ -28,7 +29,7 @@ ) where import Prelude hiding (lookup)-import Control.Applicative ((<|>))+import Control.Applicative ((<|>), optional) import Control.Monad ((<$!>)) import Data.Data ( Data, dataTypeConstrs, dataTypeName, dataTypeOf@@ -63,8 +64,18 @@ -- | Collection of filter functions (at most one function per element -- constructor)-newtype Filter = Filter (Map Name FilterFunction)+data Filter = Filter+ { filterWalkingOrder :: WalkingOrder+ , filterMap :: Map Name FilterFunction+ } +-- | Description of how an AST should be traversed.+data WalkingOrder+ = WalkForEachType -- ^ Process each type separately, traversing the+ -- tree bottom-up (leaves to root) for each type.+ | WalkTopdown -- ^ Traverse the tree top-down, from root to+ -- leaves and depth first, in a single traversal.+ -- | Retrieves a default `Filter` object from the stack, suitable for -- filtering a full document. peekFilter :: LuaError e => Peeker e Filter@@ -87,15 +98,21 @@ runPeek (peekFilterFunction top `lastly` pop 1) >>= \case Success fn -> pure $ Map.insert constr fn acc Failure {} -> pure acc- Filter <$!> foldrM go Map.empty fnNames+ walkingSequence <- do+ _ <- liftLua $ getfield idx "traverse"+ optional (peekText top) `lastly` pop 1 >>= \case+ Just "typewise" -> pure WalkForEachType+ Just "topdown" -> pure WalkTopdown+ _ -> pure WalkForEachType+ Filter walkingSequence <$!> foldrM go Map.empty fnNames -- | Looks up a filter function in a Lua 'Filter'. lookup :: Name -> Filter -> Maybe FilterFunction-lookup name (Filter filterMap) = name `Map.lookup` filterMap+lookup name = (name `Map.lookup`) . filterMap -- | Checks whether the 'Filter' contains a function of the given name. member :: Name -> Filter -> Bool-member name (Filter filterMap) = name `Map.member` filterMap+member name = (name `Map.member`) . filterMap -- | Filter function names for a given type. valueFunctionNames :: forall a. Data a => Proxy a -> [Name]@@ -115,6 +132,8 @@ fromString . (++ "s") . tyconUQname . dataTypeName . dataTypeOf $ (undefined :: a) +-- | Finds the best filter function for a given element; returns+-- 'Nothing' if no such function exists. getFunctionFor :: forall a. Data a => Filter -> a -> Maybe FilterFunction getFunctionFor filter' x = let constrName = fromString . showConstr . toConstr $ x
src/Text/Pandoc/Lua/Marshal/Inline.hs view
@@ -25,6 +25,7 @@ , walkInlinesStraight ) where +import Control.Applicative (optional) import Control.Monad.Catch (throwM) import Control.Monad ((<$!>)) import Data.Data (showConstr, toConstr)@@ -106,9 +107,9 @@ <#> parameter peekInline "inline" "Inline" "Object" =#> functionResult pushString "string" "stringified Inline" , operation Eq $ defun "__eq"- ### liftPure2 (==)- <#> parameter peekInline "a" "Inline" ""- <#> parameter peekInline "b" "Inline" ""+ ### liftPure2 (\a b -> fromMaybe False ((==) <$> a <*> b))+ <#> parameter (optional . peekInline) "a" "Inline" ""+ <#> parameter (optional . peekInline) "b" "Inline" "" =#> functionResult pushBool "boolean" "whether the two are equal" ] [ possibleProperty "attr" "element attributes"
src/Text/Pandoc/Lua/Marshal/ListAttributes.hs view
@@ -19,6 +19,7 @@ , pushListNumberStyle ) where +import Control.Applicative (optional) import Data.Maybe (fromMaybe) import HsLua import Text.Pandoc.Definition@@ -28,9 +29,9 @@ typeListAttributes :: LuaError e => DocumentedType e ListAttributes typeListAttributes = deftype "ListAttributes" [ operation Eq $ lambda- ### liftPure2 (==)- <#> parameter peekListAttributes "a" "ListAttributes" ""- <#> parameter peekListAttributes "b" "ListAttributes" ""+ ### liftPure2 (\a b -> fromMaybe False ((==) <$> a <*> b))+ <#> parameter (optional . peekListAttributes) "a" "ListAttributes" ""+ <#> parameter (optional . peekListAttributes) "b" "ListAttributes" "" =#> functionResult pushBool "boolean" "whether the two are equal" ] [ property "start" "number of the first list item"
src/Text/Pandoc/Lua/Marshal/MetaValue.hs view
@@ -1,6 +1,7 @@ {-# LANGUAGE LambdaCase #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TypeApplications #-} {- | Copyright : © 2021 Albert Krewinkel SPDX-License-Identifier : MIT@@ -23,6 +24,7 @@ ( peekInline, peekInlines, peekInlinesFuzzy, pushInlines ) import Text.Pandoc.Lua.Marshal.List (pushPandocList) import Text.Pandoc.Definition (MetaValue (..))+import qualified Data.Text as T -- | Push a 'MetaValue' element to the top of the Lua stack. pushMetaValue :: LuaError e => Pusher e MetaValue@@ -43,6 +45,11 @@ TypeBoolean -> MetaBool <$!> peekBool idx TypeString -> MetaString <$!> peekText idx++ TypeNumber -> MetaString . T.pack <$>+ (liftLua (isinteger idx) >>= \case+ False -> show <$!> peekRealFloat @Double idx+ True -> show <$!> peekIntegral @Prelude.Integer idx) TypeUserdata -> -- Allow singleton Inline or Block elements (MetaInlines . (:[]) <$!> peekInline idx) <|>
src/Text/Pandoc/Lua/Marshal/Pandoc.hs view
@@ -26,7 +26,7 @@ import Text.Pandoc.Lua.Marshal.Block (peekBlocksFuzzy, pushBlocks, walkBlockSplicing, walkBlocksStraight) import Text.Pandoc.Lua.Marshal.Inline (walkInlineSplicing, walkInlinesStraight)-import Text.Pandoc.Lua.Marshal.Filter (Filter, peekFilter)+import Text.Pandoc.Lua.Marshal.Filter import Text.Pandoc.Lua.Marshal.MetaValue (peekMetaValue, pushMetaValue) import Text.Pandoc.Lua.Marshal.Shared (walkBlocksAndInlines) import Text.Pandoc.Lua.Walk (applyStraight)@@ -44,7 +44,7 @@ typePandoc :: LuaError e => DocumentedType e Pandoc typePandoc = deftype "Pandoc" [ operation Eq $ defun "__eq"- ### liftPure2 (==)+ ### liftPure2 (\a b -> fromMaybe False ((==) <$> a <*> b)) <#> parameter (optional . peekPandoc) "doc1" "pandoc" "" <#> parameter (optional . peekPandoc) "doc2" "pandoc" "" =#> functionResult pushBool "boolean" "true iff the two values are equal"@@ -61,10 +61,13 @@ (peekMeta, \(Pandoc _ blks) meta -> Pandoc meta blks) , method $ defun "walk"- ### (\doc filter' ->- walkBlocksAndInlines filter' doc- >>= applyMetaFunction filter'- >>= applyPandocFunction filter')+ ### (\doc filter' -> case filterWalkingOrder filter' of+ WalkForEachType -> walkBlocksAndInlines filter' doc+ >>= applyMetaFunction filter'+ >>= applyPandocFunction filter'+ WalkTopdown -> applyPandocFunction filter' doc+ >>= applyMetaFunction filter'+ >>= walkBlocksAndInlines filter') <#> parameter peekPandoc "Pandoc" "self" "" <#> parameter peekFilter "Filter" "lua_filter" "table of filter functions" =#> functionResult pushPandoc "Pandoc" "modified element"
+ src/Text/Pandoc/Lua/Marshal/Row.hs view
@@ -0,0 +1,94 @@+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE LambdaCase #-}+{- |+Copyright : © 2021 Albert Krewinkel+SPDX-License-Identifier : MIT+Maintainer : Albert Krewinkel <tarleb+pandoc@moltkeplatz.de>++Marshaling/unmarshaling functions of 'Row' values.+-}+module Text.Pandoc.Lua.Marshal.Row+ ( peekRow+ , peekRowFuzzy+ , pushRow+ , typeRow+ , mkRow+ ) where++import Control.Applicative (optional)+import Control.Monad ((<$!>))+import Data.Maybe (fromMaybe)+import HsLua+import Text.Pandoc.Lua.Marshal.Attr (peekAttr, pushAttr)+import Text.Pandoc.Lua.Marshal.Cell (peekCellFuzzy, pushCell)+import Text.Pandoc.Lua.Marshal.Filter (peekFilter)+import Text.Pandoc.Lua.Marshal.List (pushPandocList)+import Text.Pandoc.Lua.Marshal.Shared (walkBlocksAndInlines)+import Text.Pandoc.Definition++-- | Push a table Row as a table with fields @attr@, @alignment@,+-- @row_span@, @col_span@, and @contents@.+pushRow :: LuaError e => Row -> LuaE e ()+pushRow = pushUD typeRow++-- | Retrieves a 'Cell' object from the stack.+peekRow :: LuaError e => Peeker e Row+peekRow = peekUD typeRow++-- | Retrieves a 'Cell' from the stack, accepting either a 'pandoc Cell'+-- userdata object or a table with fields @attr@, @alignment@, @row_span@,+-- @col_span@, and @contents@.+peekRowFuzzy :: LuaError e => Peeker e Row+peekRowFuzzy idx = liftLua (ltype idx) >>= \case+ TypeUserdata -> peekRow idx+ TypeTable -> uncurry Row <$!> peekPair peekAttr (peekList peekCellFuzzy) idx+ _ -> failPeek =<< typeMismatchMessage "Cell or table" idx++-- | Row object type.+typeRow :: LuaError e => DocumentedType e Row+typeRow = deftype "pandoc Row"+ [ operation Eq $ defun "__eq"+ ### liftPure2 (\a b -> fromMaybe False ((==) <$> a <*> b))+ <#> parameter (optional . peekRow) "Row" "self" ""+ <#> parameter (optional . peekRow) "any" "object" ""+ =#> functionResult pushBool "boolean" "true iff the two values are equal"+ , operation Tostring $ lambda+ ### liftPure show+ <#> parameter peekRow "Row" "self" ""+ =#> functionResult pushString "string" "native Haskell representation"+ ]+ [ property "attr" "row attributes"+ (pushAttr, \(Row attr _) -> attr)+ (peekAttr, \(Row _ cells) attr ->+ Row attr cells)+ , property "cells" "row cells"+ (pushPandocList pushCell, \(Row _ cells) -> cells)+ (peekList peekCellFuzzy, \(Row attr _) cells ->+ Row attr cells)++ , alias "identifier" "cell ID" ["attr", "identifier"]+ , alias "classes" "cell classes" ["attr", "classes"]+ , alias "attributes" "cell attributes" ["attr", "attributes"]++ , method $ defun "clone"+ ### return+ <#> parameter peekRow "Row" "self" ""+ =#> functionResult pushRow "Row" "cloned object"++ , method $ defun "walk"+ ### flip walkBlocksAndInlines+ <#> parameter peekRow "Row" "self" ""+ <#> parameter peekFilter "Filter" "lua_filter" "table of filter functions"+ =#> functionResult pushRow "Row" "modified cell"+ ]++-- | Constructor function for 'Row' values.+mkRow :: LuaError e => DocumentedFunction e+mkRow = defun "Row"+ ### liftPure2 (\mCells mAttr -> Row+ (fromMaybe nullAttr mAttr)+ (fromMaybe [] mCells))+ <#> optionalParameter (peekList peekCellFuzzy) "{Cell,...}" "cells"+ "row cells"+ <#> optionalParameter peekAttr "Attr" "attr" "cell attributes"+ =#> functionResult pushRow "Row" "new Row object"
@@ -1,4 +1,5 @@ {-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE OverloadedStrings #-} {- | Copyright : © 2021 Albert Krewinkel License : MIT@@ -11,26 +12,73 @@ walkBlocksAndInlines ) where +import Prelude hiding (lookup) import Control.Monad ((>=>))-import HsLua (LuaE, LuaError)+import HsLua import {-# SOURCE #-} Text.Pandoc.Lua.Marshal.Block- ( walkBlockSplicing, walkBlocksStraight )-import Text.Pandoc.Lua.Marshal.Filter (Filter) import {-# SOURCE #-} Text.Pandoc.Lua.Marshal.Inline- ( walkInlineSplicing, walkInlinesStraight )+import Text.Pandoc.Lua.Marshal.Filter import Text.Pandoc.Definition-import Text.Pandoc.Lua.Walk (SpliceList, Walkable)+import Text.Pandoc.Lua.Topdown+import Text.Pandoc.Lua.Walk+import Text.Pandoc.Walk -- | Walk blocks and inlines. walkBlocksAndInlines :: (LuaError e, Walkable (SpliceList Block) a, Walkable (SpliceList Inline) a, Walkable [Block] a,- Walkable [Inline] a)+ Walkable [Inline] a,+ Walkable Topdown a) => Filter -> a -> LuaE e a-walkBlocksAndInlines f =- walkInlineSplicing f- >=> walkInlinesStraight f- >=> walkBlockSplicing f- >=> walkBlocksStraight f+walkBlocksAndInlines filter' =+ case filterWalkingOrder filter' of+ WalkTopdown -> walkM (applyFilterTopdown filter')+ WalkForEachType -> walkInlineSplicing filter'+ >=> walkInlinesStraight filter'+ >=> walkBlockSplicing filter'+ >=> walkBlocksStraight filter'++-- | Applies a filter by processing the root node(s) first and descending+-- towards the leaves depth-first.+applyFilterTopdown :: LuaError e+ => Filter+ -> Topdown -> LuaE e Topdown+applyFilterTopdown filter' topdown@(Topdown _ node) =+ case node of+ TBlock x ->+ case filter' `getFunctionFor` x of+ Nothing ->+ pure topdown+ Just fn -> do+ (blocks, ctrl) <-+ applySplicingFunction fn pushBlock peekBlocksFuzzy x+ pure $ Topdown ctrl $ TBlocks blocks++ TBlocks xs ->+ case "Blocks" `lookup` filter' of+ Nothing ->+ pure topdown+ Just fn -> do+ (blocks, ctrl) <-+ applyStraightFunction fn pushBlocks peekBlocksFuzzy xs+ pure $ Topdown ctrl $ TBlocks blocks++ TInline x ->+ case filter' `getFunctionFor` x of+ Nothing ->+ pure topdown+ Just fn -> do+ (inlines, ctrl) <-+ applySplicingFunction fn pushInline peekInlinesFuzzy x+ pure $ Topdown ctrl $ TInlines inlines++ TInlines xs ->+ case "Inlines" `lookup` filter' of+ Nothing ->+ pure topdown+ Just fn -> do+ (inlines, ctrl) <-+ applyStraightFunction fn pushInlines peekInlinesFuzzy xs+ pure $ Topdown ctrl $ TInlines inlines
src/Text/Pandoc/Lua/Marshal/SimpleTable.hs view
@@ -17,6 +17,8 @@ ) where +import Control.Applicative (optional)+import Data.Maybe (fromMaybe) import HsLua as Lua import Text.Pandoc.Lua.Marshal.Alignment (peekAlignment, pushAlignment) import Text.Pandoc.Lua.Marshal.Block (peekBlocksFuzzy, pushBlocks)@@ -36,9 +38,9 @@ typeSimpleTable :: LuaError e => DocumentedType e SimpleTable typeSimpleTable = deftype "SimpleTable" [ operation Eq $ lambda- ### liftPure2 (==)- <#> udparam typeSimpleTable "a" ""- <#> udparam typeSimpleTable "b" ""+ ### liftPure2 (\a b -> fromMaybe False ((==) <$> a <*> b))+ <#> parameter (optional . peekSimpleTable) "value" "a" ""+ <#> parameter (optional . peekSimpleTable) "value" "b" "" =#> functionResult pushBool "boolean" "whether the two objects are equal" , operation Tostring $ lambda ### liftPure show
+ src/Text/Pandoc/Lua/Marshal/TableFoot.hs view
@@ -0,0 +1,73 @@+{-# LANGUAGE OverloadedStrings #-}+{- |+Copyright : © 2021 Albert Krewinkel+SPDX-License-Identifier : MIT+Maintainer : Albert Krewinkel <tarleb+pandoc@moltkeplatz.de>++Marshaling/unmarshaling functions of 'TableFoot' values.+-}+module Text.Pandoc.Lua.Marshal.TableFoot+ ( peekTableFoot+ , pushTableFoot+ , typeTableFoot+ , mkTableFoot+ ) where++import Control.Applicative (optional)+import Data.Maybe (fromMaybe)+import HsLua+import Text.Pandoc.Lua.Marshal.Attr (peekAttr, pushAttr)+import Text.Pandoc.Lua.Marshal.List (pushPandocList)+import Text.Pandoc.Lua.Marshal.Row (peekRowFuzzy, pushRow)+import Text.Pandoc.Definition++-- | Push a TableFoot as a userdata value.+pushTableFoot :: LuaError e => TableFoot -> LuaE e ()+pushTableFoot = pushUD typeTableFoot++-- | Retrieves a 'Cell' from the stack.+peekTableFoot :: LuaError e => Peeker e TableFoot+peekTableFoot = peekUD typeTableFoot++-- | Row object type.+typeTableFoot :: LuaError e => DocumentedType e TableFoot+typeTableFoot = deftype "pandoc TableFoot"+ [ operation Eq $ defun "__eq"+ ### liftPure2 (\a b -> fromMaybe False ((==) <$> a <*> b))+ <#> parameter (optional . peekTableFoot) "TableFoot" "self" ""+ <#> parameter (optional . peekTableFoot) "any" "object" ""+ =#> functionResult pushBool "boolean" "true iff the two values are equal"+ , operation Tostring $ lambda+ ### liftPure show+ <#> parameter peekTableFoot "TableFoot" "self" ""+ =#> functionResult pushString "string" "native Haskell representation"+ ]+ [ property "attr" "table foot attributes"+ (pushAttr, \(TableFoot attr _) -> attr)+ (peekAttr, \(TableFoot _ cells) attr ->+ TableFoot attr cells)+ , property "rows" "footer rows"+ (pushPandocList pushRow, \(TableFoot _ rows) -> rows)+ (peekList peekRowFuzzy, \(TableFoot attr _) rows ->+ TableFoot attr rows)++ , alias "identifier" "cell ID" ["attr", "identifier"]+ , alias "classes" "cell classes" ["attr", "classes"]+ , alias "attributes" "cell attributes" ["attr", "attributes"]++ , method $ defun "clone"+ ### return+ <#> parameter peekTableFoot "TableFoot" "self" ""+ =#> functionResult pushTableFoot "TableFoot" "cloned object"+ ]++-- | Constructor function for 'Row' values.+mkTableFoot :: LuaError e => DocumentedFunction e+mkTableFoot = defun "TableFoot"+ ### liftPure2 (\mCells mAttr -> TableFoot+ (fromMaybe nullAttr mAttr)+ (fromMaybe [] mCells))+ <#> optionalParameter (peekList peekRowFuzzy) "{Row,...}" "rows"+ "footer rows"+ <#> optionalParameter peekAttr "Attr" "attr" "table foot attributes"+ =#> functionResult pushTableFoot "TableFoot" "new TableFoot object"
+ src/Text/Pandoc/Lua/Marshal/TableHead.hs view
@@ -0,0 +1,73 @@+{-# LANGUAGE OverloadedStrings #-}+{- |+Copyright : © 2021 Albert Krewinkel+SPDX-License-Identifier : MIT+Maintainer : Albert Krewinkel <tarleb+pandoc@moltkeplatz.de>++Marshaling/unmarshaling functions of 'TableHead' values.+-}+module Text.Pandoc.Lua.Marshal.TableHead+ ( peekTableHead+ , pushTableHead+ , typeTableHead+ , mkTableHead+ ) where++import Control.Applicative (optional)+import Data.Maybe (fromMaybe)+import HsLua+import Text.Pandoc.Lua.Marshal.Attr (peekAttr, pushAttr)+import Text.Pandoc.Lua.Marshal.List (pushPandocList)+import Text.Pandoc.Lua.Marshal.Row (peekRowFuzzy, pushRow)+import Text.Pandoc.Definition++-- | Push a TableHead as a userdata value.+pushTableHead :: LuaError e => TableHead -> LuaE e ()+pushTableHead = pushUD typeTableHead++-- | Retrieves a 'Cell' from the stack.+peekTableHead :: LuaError e => Peeker e TableHead+peekTableHead = peekUD typeTableHead++-- | Row object type.+typeTableHead :: LuaError e => DocumentedType e TableHead+typeTableHead = deftype "pandoc TableHead"+ [ operation Eq $ defun "__eq"+ ### liftPure2 (\a b -> fromMaybe False ((==) <$> a <*> b))+ <#> parameter (optional . peekTableHead) "TableHead" "self" ""+ <#> parameter (optional . peekTableHead) "any" "object" ""+ =#> functionResult pushBool "boolean" "true iff the two values are equal"+ , operation Tostring $ lambda+ ### liftPure show+ <#> parameter peekTableHead "TableHead" "self" ""+ =#> functionResult pushString "string" "native Haskell representation"+ ]+ [ property "attr" "table head attributes"+ (pushAttr, \(TableHead attr _) -> attr)+ (peekAttr, \(TableHead _ cells) attr ->+ TableHead attr cells)+ , property "rows" "header rows"+ (pushPandocList pushRow, \(TableHead _ rows) -> rows)+ (peekList peekRowFuzzy, \(TableHead attr _) rows ->+ TableHead attr rows)++ , alias "identifier" "cell ID" ["attr", "identifier"]+ , alias "classes" "cell classes" ["attr", "classes"]+ , alias "attributes" "cell attributes" ["attr", "attributes"]++ , method $ defun "clone"+ ### return+ <#> parameter peekTableHead "TableHead" "self" ""+ =#> functionResult pushTableHead "TableHead" "cloned object"+ ]++-- | Constructor function for 'Row' values.+mkTableHead :: LuaError e => DocumentedFunction e+mkTableHead = defun "TableHead"+ ### liftPure2 (\mRows mAttr -> TableHead+ (fromMaybe nullAttr mAttr)+ (fromMaybe [] mRows))+ <#> optionalParameter (peekList peekRowFuzzy) "{Row,...}" "rows"+ "header rows"+ <#> optionalParameter peekAttr "Attr" "attr" "table head attributes"+ =#> functionResult pushTableHead "TableHead" "new TableHead object"
src/Text/Pandoc/Lua/Marshal/TableParts.hs view
@@ -14,6 +14,7 @@ , peekColSpec , pushColSpec , peekRow+ , peekRowFuzzy , pushRow , peekTableBody , pushTableBody@@ -21,6 +22,10 @@ , pushTableFoot , peekTableHead , pushTableHead+ -- * Constructors+ , mkRow+ , mkTableFoot+ , mkTableHead ) where import Control.Applicative (optional)@@ -30,10 +35,12 @@ import Text.Pandoc.Lua.Marshal.Attr (peekAttr, pushAttr) import {-# SOURCE #-} Text.Pandoc.Lua.Marshal.Block ( peekBlocksFuzzy, pushBlocks )-import Text.Pandoc.Lua.Marshal.Cell (peekCellFuzzy, pushCell) import {-# SOURCE #-} Text.Pandoc.Lua.Marshal.Inline ( peekInlinesFuzzy, pushInlines ) import Text.Pandoc.Lua.Marshal.List (pushPandocList)+import Text.Pandoc.Lua.Marshal.Row+import Text.Pandoc.Lua.Marshal.TableFoot+import Text.Pandoc.Lua.Marshal.TableHead import Text.Pandoc.Definition -- | Push Caption element@@ -69,17 +76,6 @@ (ColWidth w) -> push w ColWidthDefault -> pushnil --- | Push a table row as a pair of attr and the list of cells.-pushRow :: LuaError e => Pusher e Row-pushRow (Row attr cells) =- pushPair pushAttr (pushPandocList pushCell) (attr, cells)---- | Push a table row from a pair of attr and the list of cells.-peekRow :: LuaError e => Peeker e Row-peekRow = (uncurry Row <$!>)- . retrieving "Row"- . peekPair peekAttr (peekList peekCellFuzzy)- -- | Pushes a 'TableBody' value as a Lua table with fields @attr@, -- @row_head_columns@, @head@, and @body@. pushTableBody :: LuaError e => Pusher e TableBody@@ -98,32 +94,8 @@ $ \idx -> TableBody <$!> peekFieldRaw peekAttr "attr" idx <*> peekFieldRaw (fmap RowHeadColumns . peekIntegral) "row_head_columns" idx- <*> peekFieldRaw (peekList peekRow) "head" idx- <*> peekFieldRaw (peekList peekRow) "body" idx---- | Push a table head value as the pair of its Attr and rows.-pushTableHead :: LuaError e => Pusher e TableHead-pushTableHead (TableHead attr rows) =- pushPair pushAttr (pushPandocList pushRow) (attr, rows)---- | Peek a table head value from a pair of Attr and rows.-peekTableHead :: LuaError e => Peeker e TableHead-peekTableHead = (uncurry TableHead <$!>)- . retrieving "TableHead"- . peekPair peekAttr (peekList peekRow)---- | Pushes a 'TableFoot' value as a pair of the Attr value and the list--- of table rows.-pushTableFoot :: LuaError e => Pusher e TableFoot-pushTableFoot (TableFoot attr rows) =- pushPair pushAttr (pushPandocList pushRow) (attr, rows)---- | Retrieves a 'TableFoot' value from a pair containing an Attr value--- and a list of table rows.-peekTableFoot :: LuaError e => Peeker e TableFoot-peekTableFoot = (uncurry TableFoot <$!>)- . retrieving "TableFoot"- . peekPair peekAttr (peekList peekRow)+ <*> peekFieldRaw (peekList peekRowFuzzy) "head" idx+ <*> peekFieldRaw (peekList peekRowFuzzy) "body" idx -- | Add a value to the table at the top of the stack at a string-index. addField :: LuaError e => Name -> LuaE e () -> LuaE e ()
+ src/Text/Pandoc/Lua/Topdown.hs view
@@ -0,0 +1,138 @@+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{- |+Module : Text.Pandoc.Lua.Topdown+Copyright : © 2012-2021 John MacFarlane,+ © 2017-2021 Albert Krewinkel+License : GNU GPL, version 2 or above+Maintainer : Albert Krewinkel <tarleb+pandoc@moltkeplatz.de>++Walk documents in a filter-suitable way, descending from the root+towards the leaves.+-}+module Text.Pandoc.Lua.Topdown+ ( TraversalNode (..)+ , Topdown (..)+ , TraversalControl (..)+ )+where++import Control.Monad ((>=>))+import Text.Pandoc.Definition+import Text.Pandoc.Lua.Walk+import Text.Pandoc.Walk++-- | Helper type to do a preorder traversal of a subtree.+data TraversalNode+ = TBlock Block+ | TBlocks [Block]+ | TInline Inline+ | TInlines [Inline]++-- | Type used to traverse a 'Pandoc' AST from top to bottom, i.e.,+-- processing the root element first and then continue towards the+-- leaves depth-first. Aborts the descend if 'topdownControl' is 'Stop'.+data Topdown = Topdown+ { topdownControl :: TraversalControl+ , topdownNode :: TraversalNode+ }++-- | Extracts a list of 'Inline' elements from a 'TraversalNode'.+-- WARNING: This is a partial function and will throw an error if the+-- node contains a 'Block' or a list of 'Block's.+nodeInlines :: TraversalNode -> [Inline]+nodeInlines = \case+ TInlines xs -> xs+ TInline x -> [x]+ _ -> error $ "The 'impossible' has happened."+ ++ "Please report this as a bug"++-- | Extracts a list of 'Block' elements from a 'TraversalNode'.+nodeBlocks :: TraversalNode -> [Block]+nodeBlocks = \case+ TBlocks xs -> xs+ TBlock x -> [x]+ TInlines xs -> [Plain xs]+ TInline x -> [Plain [x]]++-- | Creates a topdown-walking function for a list of elements.+walkTopdownM :: (Monad m, Walkable Topdown a)+ => ([a] -> TraversalNode)+ -> (a -> TraversalNode)+ -> (TraversalNode -> [a])+ -> (Topdown -> m Topdown)+ -> [a] -> m [a]+walkTopdownM mkListNode mkElemNode nodeToList f =+ f . Topdown Continue . mkListNode >=> \case+ Topdown Stop node -> return $ nodeToList node+ Topdown Continue node -> mconcat <$>+ traverse (f . Topdown Continue . mkElemNode >=> \case+ Topdown Stop node' -> return $ nodeToList node'+ Topdown Continue node' -> traverse (walkM f) $+ nodeToList node')+ (nodeToList node)++-- | Creates a topdown-query function for a list of elements.+queryTopdown :: (Monoid a, Walkable Topdown b)+ => ([b] -> TraversalNode)+ -> (Topdown -> a) -> [b] -> a+queryTopdown mkListNode f xs =+ f (Topdown Continue $ mkListNode xs) <> mconcat (map (query f) xs)++instance {-# OVERLAPPING #-} Walkable Topdown [Block] where+ walkM = walkTopdownM TBlocks TBlock nodeBlocks+ query = queryTopdown TBlocks++instance {-# OVERLAPPING #-} Walkable Topdown [Inline] where+ walkM = walkTopdownM TInlines TInline nodeInlines+ query = queryTopdown TInlines++instance Walkable Topdown Block where+ walkM = walkBlockM+ query = queryBlock++instance Walkable Topdown Inline where+ walkM = walkInlineM+ query = queryInline++instance Walkable Topdown Pandoc where+ walkM = walkPandocM+ query = queryPandoc++instance Walkable Topdown Citation where+ walkM = walkCitationM+ query = queryCitation++instance Walkable Topdown Row where+ walkM = walkRowM+ query = queryRow++instance Walkable Topdown TableHead where+ walkM = walkTableHeadM+ query = queryTableHead++instance Walkable Topdown TableBody where+ walkM = walkTableBodyM+ query = queryTableBody++instance Walkable Topdown TableFoot where+ walkM = walkTableFootM+ query = queryTableFoot++instance Walkable Topdown Caption where+ walkM = walkCaptionM+ query = queryCaption++instance Walkable Topdown Cell where+ walkM = walkCellM+ query = queryCell++instance Walkable Topdown MetaValue where+ walkM = walkMetaValueM+ query = queryMetaValue++instance Walkable Topdown Meta where+ walkM f (Meta metamap) = Meta <$> walkM f metamap+ query f (Meta metamap) = query f metamap
src/Text/Pandoc/Lua/Walk.hs view
@@ -15,10 +15,13 @@ module Text.Pandoc.Lua.Walk ( SpliceList (..) , Walkable+ , TraversalControl (..) , walkSplicing , walkStraight , applyStraight , applySplicing+ , applyStraightFunction+ , applySplicingFunction ) where @@ -51,7 +54,7 @@ pure Just fn -> -- Walk the element with the filter function.- walkM $ applyStraightFunction fn pushElement peekElement+ walkM $ fmap fst . applyStraightFunction fn pushElement peekElement -- | Applies a filter on an element. The element is pushed to the stack -- via the given pusher and calls the filter function with that value,@@ -64,7 +67,7 @@ Nothing -> -- There is no filter function, do nothing. pure x- Just fn -> do+ Just fn -> fst <$> -- Apply the function applyStraightFunction fn pushElement peekElement x @@ -74,13 +77,15 @@ -- on the stack. applyStraightFunction :: LuaError e => FilterFunction -> Pusher e a -> Peeker e a- -> a -> LuaE e a+ -> a -> LuaE e (a, TraversalControl) applyStraightFunction fn pushElement peekElement x = do pushFilterFunction fn pushElement x- callWithTraceback 1 1- forcePeek . (`lastly` pop 1) $- (x <$ peekNil top) <|> peekElement top+ callWithTraceback 1 2+ forcePeek . (`lastly` pop 2) $+ (,)+ <$> ((x <$ peekNil (nth 2)) <|> peekElement (nth 2))+ <*> peekTraversalControl top -- -- Splicing@@ -118,7 +123,7 @@ Nothing -> -- There is no filter function, do nothing. pure [x]- Just fn -> do+ Just fn -> fst <$> -- Apply the function applySplicingFunction fn pushElement peekElements x @@ -128,14 +133,17 @@ -- on the stack. applySplicingFunction :: LuaError e => FilterFunction -> Pusher e a -> Peeker e [a]- -> a -> LuaE e [a]+ -> a -> LuaE e ([a], TraversalControl) applySplicingFunction fn pushElement peekElements x = do pushFilterFunction fn pushElement x- callWithTraceback 1 1- forcePeek . (`lastly` pop 1) $ liftLua (ltype top) >>= \case- TypeNil -> pure [x] -- function returned `nil`, keep original value- _ -> peekElements top+ callWithTraceback 1 2+ forcePeek . (`lastly` pop 2) $+ (,)+ <$> (liftLua (ltype (nth 2)) >>= \case+ TypeNil -> pure [x] -- function returned `nil`, keep original value+ _ -> peekElements (nth 2))+ <*> peekTraversalControl top -- -- Helper@@ -158,3 +166,13 @@ remove tracebackIdx when (result /= OK) throwErrorAsException++data TraversalControl = Continue | Stop++-- | Retrieves a Traversal control value: @nil@ or a truthy value+-- translate to 'Continue', @false@ is treated to mean 'Stop'.+peekTraversalControl :: Peeker e TraversalControl+peekTraversalControl idx = (Continue <$ peekNil idx)+ <|> (liftLua (toboolean top) >>= \case+ True -> pure Continue+ False -> pure Stop)
test/test-block.lua view
@@ -246,20 +246,20 @@ group 'Table' { test('access Attr via property `attr`', function () local caption = {long = {Plain 'cap'}}- local tbl = Table(caption, {}, {{}, {}}, {}, {{}, {}},+ local tbl = Table(caption, {}, TableHead(), {}, TableFoot(), {'my-tbl', {'a'}}) assert.are_equal(tbl.attr, Attr{'my-tbl', {'a'}}) tbl.attr = Attr{'my-other-tbl', {'b'}} assert.are_equal(- Table(caption, {}, {{}, {}}, {}, {{}, {}},+ Table(caption, {}, TableHead(), {}, TableFoot(), {'my-other-tbl', {'b'}}), tbl ) end), test('access caption via property `caption`', function () local caption = {long = {Plain 'cap'}}- local tbl = Table(caption, {}, {{}, {}}, {}, {{}, {}})+ local tbl = Table(caption, {}, TableHead(), {}, TableFoot()) assert.are_same(tbl.caption, {long = {Plain 'cap'}}) tbl.caption.short = 'brief'@@ -270,13 +270,13 @@ long = {Plain 'extended'} } assert.are_equal(- Table(new_caption, {}, {{}, {}}, {}, {{}, {}}),+ Table(new_caption, {}, TableHead(), {}, TableFoot()), tbl ) end), test('access column specifiers via property `colspecs`', function () local colspecs = {{AlignCenter, 1}}- local tbl = Table({long = {}}, colspecs, {{}, {}}, {}, {{}, {}})+ local tbl = Table({long = {}}, colspecs, TableHead(), {}, TableFoot()) assert.are_same(tbl.colspecs, colspecs) tbl.colspecs[1][1] = AlignRight@@ -284,33 +284,39 @@ local new_colspecs = {{AlignRight}} assert.are_equal(- Table({long = {}}, new_colspecs, {{}, {}}, {}, {{}, {}}),+ Table({long = {}}, new_colspecs, TableHead(), {}, TableFoot()), tbl ) end), test('access table head via property `head`', function ()- local head = {Attr{'tbl-head'}, {}}- local tbl = Table({long = {}}, {}, head, {}, {{}, {}})+ local head = TableHead({Row{Cell'a'}}, Attr('tbl-head'))+ local tbl = Table({long = {}}, {}, head, {}, TableFoot()) assert.are_same(tbl.head, head) - tbl.head[1] = Attr{'table-head'}+ local new_head = head:clone()+ new_head.attr = Attr{'table-head'}+ new_head.rows = {Row{Cell{'test'}}} - local new_head = {'table-head', {}}+ tbl.head = new_head+ assert.are_equal(- Table({long = {}}, {}, new_head, {}, {{}, {}}),+ Table({long = {}}, {}, new_head, {}, TableFoot()), tbl ) end),- test('access table head via property `head`', function ()- local foot = {{id = 'tbl-foot'}, {}}- local tbl = Table({long = {}}, {}, {{}, {}}, {}, foot)- assert.are_same(tbl.foot, {Attr('tbl-foot'), {}})+ test('access table foot via property `foot`', function ()+ local foot = TableFoot({Row{Cell{'test'}}}, {id = 'tbl-foot'})+ local tbl = Table({long = {}}, {}, TableHead(), {}, foot)+ assert.are_same(tbl.foot, foot) - tbl.foot[1] = Attr{'table-foot'}+ local new_foot = foot:clone()+ new_foot.attr = Attr{'table-foot'}+ new_foot.rows = {Row{Cell{'test'}}} - local new_foot = {'table-foot', {}}+ tbl.foot = new_foot+ assert.are_equal(- Table({long = {}}, {}, {{}, {}}, {}, new_foot),+ Table({long = {}}, {}, TableHead(), {}, new_foot), tbl ) end)@@ -448,6 +454,96 @@ } assert.are_same( {'Str', 'Inlines', 'Para', 'CodeBlock', 'Blocks'},+ names+ )+ end),+ test('topdown traversal works', function ()+ local names = List{}+ local tbl = Table(+ {long = {}},+ {{AlignCenter, 1}},+ TableHead{Row({Cell{'test', Para{'foo', Emph{'bar'}}}}, 'foo')},+ {},+ TableFoot()+ )+ tbl:walk{+ traverse = 'topdown',+ Blocks = function (_)+ names:insert('Blocks')+ end,+ Block = function (b)+ names:insert(b.t)+ end,+ Inline = function (i)+ names:insert(i.t)+ end,+ Inlines = function (_)+ names:insert('Inlines')+ end,+ }+ assert.are_same(+ -- Caption Cell+ {'Blocks', 'Blocks',+ 'Plain', 'Inlines', 'Str',+ 'Para', 'Inlines', 'Str',+ 'Emph', 'Inlines', 'Str'+ },+ names+ )+ end),+ test('truncating topdown traversal works', function ()+ local names = List{}+ local div = Div{+ Para{Emph 'a'},+ Plain{'b'},+ CodeBlock('c')+ }+ local filter+ filter = {+ traverse = 'topdown',+ Block = function (b)+ names:insert(b.t)+ if b.t == 'Para' then+ return b, false+ end+ end,+ Inline = function (i)+ names:insert(i.t)+ return i:walk(filter), false -- continue 'manually'+ end,+ }+ div:walk(filter)+ assert.are_same(+ {'Para', -- Emph is skipped!+ 'Plain', 'Str',+ 'CodeBlock',+ },+ names+ )+ end),+ test('truncating topdown traversal works in inlines', function ()+ local names = List{}+ local div = Div{+ Para{Emph 'a'},+ Plain{'b'},+ }+ div:walk {+ traverse = 'topdown',+ Block = function (b)+ names:insert(b.t)+ if b.t == 'Plain' then+ return nil, false+ end+ end,+ Emph = function (i)+ names:insert(i.t)+ return nil, false+ end,+ }+ assert.are_same(+ {'Para', 'Emph', -- Str is skipped+ 'Plain', -- Str is skipped here, too+ }, names ) end),
test/test-inline.lua view
@@ -412,7 +412,6 @@ names:insert('Inlines') end, }- print(names) assert.are_equal( 'Str, Space, Str, Space, Str, Inlines, Para, CodeBlock, Blocks', table.concat(names, ', ')
test/test-list.lua view
@@ -73,11 +73,19 @@ local list = List:new {19, 23, 29, 71} assert.are_equal(23, list:find(23, 1)) assert.are_equal(23, list:find(23, 2))+ assert.are_equal(23, list:find(23, -4)) assert.is_nil(list:find(23, 3))+ assert.is_nil(list:find(23, -2)) end), test('returns nil if element not found', function () assert.is_nil((List:new {18, 20, 22, 0, 24}):find('0')) end),+ test('fails if start index is not an integer', function ()+ assert.error_matches(+ function () List:new{}:find(0, 'NaN') end,+ 'number expected, got string'+ )+ end) }, group 'find_if' {@@ -99,6 +107,17 @@ local is_zero = function (n) return n == 0 end assert.is_nil((List:new {18, 20, 22, 24, 27}):find_if(is_zero)) end),+ test('respects start index', function ()+ local list = List:new {9, 29, 3, 71}+ assert.are_equal(71, list:find_if(function(n) return n > 10 end, 3))+ assert.are_equal(29, list:find_if(function(n) return n > 10 end, -3))+ end),+ test('fails if start index is not an integer', function ()+ assert.error_matches(+ function () List:new{}:find(0, 'NaN') end,+ 'number expected, got string'+ )+ end) }, group 'includes' {@@ -142,6 +161,14 @@ local primes = List:new {2, 3, 5, 7} local indices = primes:map(function (x, i) return i end) assert.are_same(List{1, 2, 3, 4}, indices)+ end),+ test('map returns a generic list', function ()+ local custom = CustomList{'α', 'β'}+ assert.are_equal(debug.getmetatable(custom).__name, 'CustomList')+ assert.are_same(+ debug.getmetatable(custom:map(tostring)).__name,+ 'List'+ ) end) }, @@ -194,22 +221,54 @@ }, group 'Operations' { group 'concatenation' {- test('yields a concatenated list', function ()- assert.are_same(List {3, 4, 5, 6}, List{3, 4} .. List {5, 6})- end),- test('does not modify its operands', function ()- local a = List {54, 74}- local b = List {90, 2014}- local result = a .. b- assert.are_same(a, List{54, 74})- assert.are_same(b, List{90, 2014})- end),- test('sets metatable of first operand on result', function ()- local result = {1, 4} .. List{9, 16}- assert.are_equal(nil, getmetatable(result))- result = List{1, 4} .. {9, 16}- assert.are_equal(List, getmetatable(result))- end),+ test('yields a concatenated list', function ()+ assert.are_same(List {3, 4, 5, 6}, List{3, 4} .. List {5, 6})+ end),+ test('does not modify its operands', function ()+ local a = List {54, 74}+ local b = List {90, 2014}+ local result = a .. b+ assert.are_same(a, List{54, 74})+ assert.are_same(b, List{90, 2014})+ end),+ test('sets metatable of first operand on result', function ()+ local result = {1, 4} .. List{9, 16}+ assert.are_equal(nil, getmetatable(result))+ result = List{1, 4} .. {9, 16}+ assert.are_equal(List, getmetatable(result))+ end),+ },+ group 'equality' {+ test('lists are equal if all elements are equal', function ()+ assert.are_equal(+ List {5, 6, 7, 8},+ List {5, 6, 7, 8}+ )+ end),+ test('lists are not equal if their metatables are different', function ()+ assert.is_truthy(+ List {18, 20, 2, 0, 24} ~=+ CustomList {18, 20, 2, 0, 24}+ )+ end),+ test('lists are not equal if one is a plain table', function ()+ assert.is_truthy(+ List {18, 20, 2, 0, 24} ~=+ {18, 20, 2, 0, 24}+ )+ end),+ test('lists are not equal if an element differs', function ()+ assert.is_truthy(+ List {18, 20, 22, 23, 24} ~=+ List {18, 20, 22, 0, 24}+ )+ end),+ test('can compare to a string', function ()+ assert.is_truthy(+ List {'a', 'b', 'c'} ~=+ "abc"+ )+ end), } }, }
test/test-metavalue.lua view
@@ -11,5 +11,9 @@ assert.are_equal(type(metalist.insert), 'function') assert.are_equal(type(metalist.remove), 'function') end),+ test('Numbers are treated as strings', function ()+ local metalist = MetaList{5, 23, 13.37}+ assert.are_same(metalist, MetaList{'5', '23', '13.37'})+ end) } }
test/test-pandoc-lua-marshal.hs view
@@ -28,6 +28,13 @@ listTests <- run @Lua.Exception $ do openlibs pushListModule *> setglobal "List"+ -- Create a custom List type with constructor "CustomList"+ pushHaskellFunction $ do+ settop 1+ newListMetatable "CustomList" (pure ())+ setmetatable (nthBottom 1)+ return 1+ setglobal "CustomList" translateResultsFromFile "test/test-list.lua" listAttributeTests <- run @Lua.Exception $ do@@ -53,52 +60,19 @@ translateResultsFromFile "test/test-citation.lua" inlineTests <- run @Lua.Exception $ do- openlibs- pushListModule *> setglobal "List"- register' mkAttr- register' mkCitation- register' mkInlines- registerConstants (Proxy @CitationMode)- registerConstants (Proxy @MathType)- registerConstants (Proxy @QuoteType)- forM_ inlineConstructors register'- forM_ blockConstructors register'+ registerDefault translateResultsFromFile "test/test-inline.lua" blockTests <- run @Lua.Exception $ do- openlibs- pushListModule *> setglobal "List"- register' mkAttr- register' mkBlocks- register' mkListAttributes- registerConstants (Proxy @Alignment)- registerConstants (Proxy @ListNumberStyle)- registerConstants (Proxy @ListNumberStyle)- forM_ inlineConstructors register'- forM_ blockConstructors register'+ registerDefault translateResultsFromFile "test/test-block.lua" cellTests <- run @Lua.Exception $ do- openlibs- pushListModule *> setglobal "List"- register' mkAttr- register' mkBlocks- register' mkCell- register' mkListAttributes- registerConstants (Proxy @Alignment)- forM_ inlineConstructors register'- forM_ blockConstructors register'+ registerDefault translateResultsFromFile "test/test-cell.lua" simpleTableTests <- run @Lua.Exception $ do- openlibs- pushListModule *> setglobal "List"- register' mkAttr- register' mkListAttributes- register' mkSimpleTable- registerConstants (Proxy @Alignment)- forM_ inlineConstructors register'- forM_ blockConstructors register'+ registerDefault translateResultsFromFile "test/test-simpletable.lua" metavalueTests <- run @Lua.Exception $ do@@ -108,12 +82,7 @@ translateResultsFromFile "test/test-metavalue.lua" pandocTests <- run @Lua.Exception $ do- openlibs- pushListModule *> setglobal "List"- register' mkMeta- register' mkPandoc- forM_ inlineConstructors register'- forM_ blockConstructors register'+ registerDefault translateResultsFromFile "test/test-pandoc.lua" defaultMain $ testGroup "pandoc-lua-marshal"@@ -129,6 +98,31 @@ , metavalueTests , pandocTests ]++-- | Registers all constructors and string constants in the global+-- environment.+registerDefault :: LuaError e => LuaE e ()+registerDefault = do+ openlibs+ pushListModule *> setglobal "List"+ register' mkAttr+ register' mkBlocks+ register' mkCell+ register' mkCitation+ register' mkInlines+ register' mkListAttributes+ register' mkPandoc+ register' mkRow+ register' mkSimpleTable+ register' mkTableHead+ register' mkTableFoot+ registerConstants (Proxy @Alignment)+ registerConstants (Proxy @ListNumberStyle)+ registerConstants (Proxy @ListNumberStyle)+ registerConstants (Proxy @MathType)+ registerConstants (Proxy @QuoteType)+ forM_ inlineConstructors register'+ forM_ blockConstructors register' register' :: LuaError e => DocumentedFunction e -> LuaE e () register' f = do
test/test-pandoc.lua view
@@ -54,5 +54,88 @@ } ) end),+ test('default traversal is typewise, bottom-up', function ()+ local names = List{}+ local doc = Pandoc(+ Blocks{+ Div{+ Plain{Emph 'a'},+ Para{'b'},+ CodeBlock('c')+ }+ },+ { test = Blocks 'foo' }+ )+ doc:walk {+ Block = function (b)+ names:insert(b.t)+ end,+ Inline = function (i)+ names:insert(i.t)+ end,+ Pandoc = function (_)+ names:insert('Pandoc')+ end,+ Meta = function (_)+ names:insert('Meta')+ end+ }+ assert.are_same(+ { 'Str', -- in meta value+ 'Str', -- in Emph+ 'Emph',+ 'Str', -- in Para,+ 'Plain', -- in meta value+ 'Plain',+ 'Para',+ 'CodeBlock',+ 'Div',+ 'Meta',+ 'Pandoc'+ },+ names+ )+ end),+ test('truncating topdown traversal works', function ()+ local names = List{}+ local doc = Pandoc(+ Blocks{+ Div{+ Plain{Emph 'a'},+ Para{'b'},+ CodeBlock('c')+ }+ },+ { test = Blocks 'foo' }+ )+ doc:walk {+ traverse = 'topdown',+ Block = function (b)+ names:insert(b.t)+ if b.t == 'Para' then+ return b, false+ end+ end,+ Inline = function (i)+ names:insert(i.t)+ end,+ Pandoc = function (_)+ names:insert('Pandoc')+ end,+ Meta = function (_)+ names:insert('Meta')+ end+ }+ assert.are_same(+ { 'Pandoc',+ 'Meta', 'Plain', 'Str', -- Meta and meta value+ 'Div',+ 'Plain', 'Emph', 'Str',+ 'Para', -- Str is skipped!+ 'CodeBlock'+ },+ names+ )+ end), } }