@@ -1,8 +1,9 @@
1{-# LANGUAGE FlexibleContexts #-}
2{-# LANGUAGE FlexibleContexts #-}
2{-# LANGUAGE FlexibleInstances #-}
3{-# LANGUAGE FlexibleInstances #-}
3{-# LANGUAGE MultiParamTypeClasses #-}
4{-# LANGUAGE MultiParamTypeClasses #-}
4−{-# LANGUAGE DeriveDataTypeable #-}
5{-# LANGUAGE ScopedTypeVariables #-}
5{-# LANGUAGE ScopedTypeVariables #-}
6+{-# OPTIONS_GHC -fno-warn-orphans #-}
7Copyright © 2017 Albert Krewinkel <tarleb+pandoc@moltkeplatz.de>
8Copyright © 2017 Albert Krewinkel <tarleb+pandoc@moltkeplatz.de>
@@ -33,6 +34,7 @@ Pandoc lua utils.
33module Text.Pandoc.Lua (LuaException (..), pushPandocModule, runLuaFilter) where
34module Text.Pandoc.Lua (LuaException (..), pushPandocModule, runLuaFilter) where
35import Control.Monad (mplus, unless, when, (>=>))
36import Control.Monad (mplus, unless, when, (>=>))
37+import Control.Monad.Identity (Identity)
36import Control.Monad.Trans (MonadIO (..))
38import Control.Monad.Trans (MonadIO (..))
37import Data.Data (DataType, Data, toConstr, showConstr, dataTypeOf,
39import Data.Data (DataType, Data, toConstr, showConstr, dataTypeOf,
38 dataTypeConstrs, dataTypeName, tyconUQname)
40 dataTypeConstrs, dataTypeName, tyconUQname)
@@ -40,10 +42,10 @@ import Data.Foldable (foldrM)
40import Data.Map (Map)
42import Data.Map (Map)
41import Data.Maybe (isJust)
43import Data.Maybe (isJust)
42import Foreign.Lua (Lua, FromLuaStack (peek), LuaException (..), StackIndex,
44import Foreign.Lua (Lua, FromLuaStack (peek), LuaException (..), StackIndex,
43− Status(OK), ToLuaStack (push))
45+ Status (OK), ToLuaStack (push))
44import Text.Pandoc.Definition
46import Text.Pandoc.Definition
45import Text.Pandoc.Lua.PandocModule (pushPandocModule)
47import Text.Pandoc.Lua.PandocModule (pushPandocModule)
46−import Text.Pandoc.Walk (Walkable (walkM))
48+import Text.Pandoc.Walk (walkM)
48import qualified Data.Map as Map
50import qualified Data.Map as Map
49import qualified Foreign.Lua as Lua
51import qualified Foreign.Lua as Lua
@@ -56,7 +58,7 @@ runLuaFilter datadir filterPath args pd = liftIO . Lua.runLua $ do
56 pushPandocModule datadir
58 pushPandocModule datadir
57 Lua.setglobal "pandoc"
59 Lua.setglobal "pandoc"
58 top <- Lua.gettop
60 top <- Lua.gettop
59− stat<- Lua.dofile filterPath
61+ stat <- Lua.dofile filterPath
60 if stat /= OK
62 if stat /= OK
62 luaErrMsg <- peek (-1) <* Lua.pop 1
64 luaErrMsg <- peek (-1) <* Lua.pop 1
@@ -64,7 +66,7 @@ runLuaFilter datadir filterPath args pd = liftIO . Lua.runLua $ do
65 newtop <- Lua.gettop
67 newtop <- Lua.gettop
66 -- Use the implicitly defined global filter if nothing was returned
68 -- Use the implicitly defined global filter if nothing was returned
67− when (newtop - top < 1) $ pushGlobalFilter
69+ when (newtop - top < 1) pushGlobalFilter
68 luaFilters <- peek (-1)
70 luaFilters <- peek (-1)
70 Lua.setglobal "PandocParameters"
72 Lua.setglobal "PandocParameters"
@@ -81,27 +83,36 @@ runAll :: [LuaFilter] -> Pandoc -> Lua Pandoc
81runAll = foldr ((>=>) . walkMWithLuaFilter) return
83runAll = foldr ((>=>) . walkMWithLuaFilter) return
83walkMWithLuaFilter :: LuaFilter -> Pandoc -> Lua Pandoc
85walkMWithLuaFilter :: LuaFilter -> Pandoc -> Lua Pandoc
84−walkMWithLuaFilter (LuaFilter fnMap) = walkLua
86+walkMWithLuaFilter (LuaFilter fnMap) =
87+ walkInlines >=> walkBlocks >=> walkMeta >=> walkPandoc
86− walkLua :: Pandoc -> Lua Pandoc
89+ walkInlines :: Pandoc -> Lua Pandoc
87− walkLua =
90+ walkInlines =
88− (if hasOneOf inlineFilterNames
91+ if hasOneOf inlineFilterNames
89− then walkM (tryFilter fnMap :: Inline -> Lua Inline)
92+ then walkM (mconcatMapM (tryFilter fnMap :: Inline -> Lua [Inline]))
90− else return)
93+ else return
95+ walkBlocks :: Pandoc -> Lua Pandoc
92− (if hasOneOf blockFilterNames
97+ if hasOneOf blockFilterNames
93− then walkM (tryFilter fnMap :: Block -> Lua Block)
98+ then walkM (mconcatMapM (tryFilter fnMap :: Block -> Lua [Block]))
94− else return)
99+ else return
101+ walkMeta :: Pandoc -> Lua Pandoc
96− (case Map.lookup "Meta" fnMap of
103+ case Map.lookup "Meta" fnMap of
97− Just fn -> walkM (\(Pandoc meta blocks) -> do
104+ Just fn -> walkM (\(Pandoc meta blocks) -> do
98− meta' <- runFilterFunction fn meta
105+ meta' <- runFilterFunction fn meta *> singleElement meta
99− return $ Pandoc meta' blocks)
106+ return $ Pandoc meta' blocks)
100− Nothing -> return)
107+ Nothing -> return
109+ walkPandoc :: Pandoc -> Lua Pandoc
102− (case foldl mplus Nothing (map (`Map.lookup` fnMap) pandocFilterNames) of
111+ case foldl mplus Nothing (map (`Map.lookup` fnMap) pandocFilterNames) of
103− Just fn -> runFilterFunction fn :: Pandoc -> Lua Pandoc
112+ Just fn -> \x -> runFilterFunction fn x *> singleElement x
104− Nothing -> return)
113+ Nothing -> return
115+ mconcatMapM f = fmap mconcat . mapM f
105 hasOneOf = any (\k -> isJust (Map.lookup k fnMap))
116 hasOneOf = any (\k -> isJust (Map.lookup k fnMap))
107constructorsFor :: DataType -> [String]
118constructorsFor :: DataType -> [String]
@@ -124,14 +135,15 @@ newtype LuaFilter = LuaFilter FunctionMap
124newtype LuaFilterFunction = LuaFilterFunction { functionIndex :: Int }
135newtype LuaFilterFunction = LuaFilterFunction { functionIndex :: Int }
126-- | Try running a filter for the given element
137-- | Try running a filter for the given element
127−tryFilter :: (Data a, FromLuaStack a, ToLuaStack a) => FunctionMap -> a -> Lua a
138+tryFilter :: (Data a, FromLuaStack a, ToLuaStack a)
139+ => FunctionMap -> a -> Lua [a]
128tryFilter fnMap x =
140tryFilter fnMap x =
129 let filterFnName = showConstr (toConstr x)
141 let filterFnName = showConstr (toConstr x)
130 catchAllName = tyconUQname $ dataTypeName (dataTypeOf x)
142 catchAllName = tyconUQname $ dataTypeName (dataTypeOf x)
132 case Map.lookup filterFnName fnMap `mplus` Map.lookup catchAllName fnMap of
144 case Map.lookup filterFnName fnMap `mplus` Map.lookup catchAllName fnMap of
133− Just fn -> runFilterFunction fn x
145+ Just fn -> runFilterFunction fn x *> elementOrList x
134− Nothing -> return x
146+ Nothing -> return [x]
136instance FromLuaStack LuaFilter where
148instance FromLuaStack LuaFilter where
137 peek idx =
149 peek idx =
@@ -151,28 +163,42 @@ instance FromLuaStack LuaFilter where
151-- called with given element as argument and is expected to return an element.
163-- called with given element as argument and is expected to return an element.
152-- Alternatively, the function can return nothing or nil, in which case the
164-- Alternatively, the function can return nothing or nil, in which case the
153-- element is left unchanged.
165-- element is left unchanged.
154−runFilterFunction :: (FromLuaStack a, ToLuaStack a)
166+runFilterFunction :: ToLuaStack a => LuaFilterFunction -> a -> Lua ()
155− => LuaFilterFunction -> a -> Lua a
156runFilterFunction lf x = do
167runFilterFunction lf x = do
157 pushFilterFunction lf
168 pushFilterFunction lf
159 z <- Lua.pcall 1 1 Nothing
170 z <- Lua.pcall 1 1 Nothing
160− if z /= OK
171+ when (z /= OK) $ do
162− msg <- peek (-1)
172+ msg <- Lua.peek (-1) <* Lua.pop 1
163− let prefix = "Error while running filter function: "
173+ let prefix = "Error while running filter function: "
164− Lua.throwLuaError $ prefix ++ msg
174+ Lua.throwLuaError $ prefix ++ msg
176+elementOrList :: FromLuaStack a => a -> Lua [a]
178+ let topOfStack = Lua.StackIndex (-1)
179+ elementUnchanged <- Lua.isnil topOfStack
181+ then [x] <$ Lua.pop 1
183+ mbres <- Lua.peekEither topOfStack
185+ Right res -> [res] <$ Lua.pop 1
186+ Left _ -> Lua.toList topOfStack <* Lua.pop 1
188+singleElement :: FromLuaStack a => a -> Lua a
190+ elementUnchanged <- Lua.isnil (-1)
166− noExplicitFilter <- Lua.isnil (-1)
168− then Lua.pop 1 *> return x
170− mbres <- Lua.peekEither (-1)
194+ mbres <- Lua.peekEither (-1)
171− case mbres of
195+ case mbres of
196+ Right res -> res <$ Lua.pop 1
172− Left err -> Lua.throwLuaError
199+ Lua.throwLuaError $
173− ("Error while trying to get a filter's return "
200+ "Error while trying to get a filter's return " ++
174− ++ "value from lua stack.\n" ++ err)
201+ "value from lua stack.\n" ++ err
175− Right res -> res <$ Lua.pop 1
177-- | Push the filter function to the top of the stack.
203-- | Push the filter function to the top of the stack.
178pushFilterFunction :: LuaFilterFunction -> Lua ()
204pushFilterFunction :: LuaFilterFunction -> Lua ()
@@ -188,6 +214,9 @@ registerFilterFunction idx = do
188 refIdx <- Lua.ref Lua.registryindex
214 refIdx <- Lua.ref Lua.registryindex
189 return $ LuaFilterFunction refIdx
215 return $ LuaFilterFunction refIdx
217+instance (FromLuaStack a) => FromLuaStack (Identity a) where
218+ peek = fmap return . peek
191instance ToLuaStack LuaFilterFunction where
220instance ToLuaStack LuaFilterFunction where
192 push = pushFilterFunction
221 push = pushFilterFunction