@@ -33,25 +33,18 @@ Pandoc lua utils.
3333-}
3434module Text.Pandoc.Lua (LuaException (..), pushPandocModule, runLuaFilter) where
3535
36−import Control.Monad (mplus, unless, when, (>=>))
36+import Control.Monad (when, (>=>))
3737import Control.Monad.Identity (Identity)
3838import Control.Monad.Trans (MonadIO (..))
39−import Data.Data (Data, DataType, dataTypeConstrs, dataTypeName, dataTypeOf,
40− showConstr, toConstr, tyconUQname)
41−import Data.Foldable (foldrM)
4239import Data.IORef (IORef, newIORef, readIORef)
43−import Data.Map (Map)
44−import Data.Maybe (isJust)
45−import Foreign.Lua (FromLuaStack (peek), Lua, LuaException (..), StackIndex,
40+import Foreign.Lua (FromLuaStack (peek), Lua, LuaException (..),
4641 Status (OK), ToLuaStack (push))
4742import Text.Pandoc.Class (CommonState, PandocIO, getCommonState, getMediaBag,
4843 setMediaBag)
4944import Text.Pandoc.Definition
5045import Text.Pandoc.Lua.PandocModule (pushMediaBagModule, pushPandocModule)
46+import Text.Pandoc.Lua.Filter (LuaFilter, walkMWithLuaFilter)
5147import Text.Pandoc.MediaBag (MediaBag)
52−import Text.Pandoc.Walk (walkM)
53−
54−import qualified Data.Map as Map
5548import qualified Foreign.Lua as Lua
5649
5750runLuaFilter :: Maybe FilePath -> FilePath -> String
@@ -109,142 +102,5 @@ pushGlobalFilter = do
109102runAll :: [LuaFilter] -> Pandoc -> Lua Pandoc
110103runAll = foldr ((>=>) . walkMWithLuaFilter) return
111104
112−walkMWithLuaFilter :: LuaFilter -> Pandoc -> Lua Pandoc
113−walkMWithLuaFilter (LuaFilter fnMap) =
114− walkInlines >=> walkBlocks >=> walkMeta >=> walkPandoc
115− where
116− walkInlines :: Pandoc -> Lua Pandoc
117− walkInlines =
118− if hasOneOf inlineFilterNames
119− then walkM (mconcatMapM (tryFilter fnMap :: Inline -> Lua [Inline]))
120− else return
121−
122− walkBlocks :: Pandoc -> Lua Pandoc
123− walkBlocks =
124− if hasOneOf blockFilterNames
125− then walkM (mconcatMapM (tryFilter fnMap :: Block -> Lua [Block]))
126− else return
127−
128− walkMeta :: Pandoc -> Lua Pandoc
129− walkMeta =
130− case Map.lookup "Meta" fnMap of
131− Just fn -> walkM (\(Pandoc meta blocks) -> do
132− meta' <- runFilterFunction fn meta *> singleElement meta
133− return $ Pandoc meta' blocks)
134− Nothing -> return
135−
136− walkPandoc :: Pandoc -> Lua Pandoc
137− walkPandoc =
138− case foldl mplus Nothing (map (`Map.lookup` fnMap) pandocFilterNames) of
139− Just fn -> \x -> runFilterFunction fn x *> singleElement x
140− Nothing -> return
141−
142− mconcatMapM f = fmap mconcat . mapM f
143− hasOneOf = any (\k -> isJust (Map.lookup k fnMap))
144−
145−constructorsFor :: DataType -> [String]
146−constructorsFor x = map show (dataTypeConstrs x)
147−
148−inlineFilterNames :: [String]
149−inlineFilterNames = "Inline" : constructorsFor (dataTypeOf (Str []))
150−
151−blockFilterNames :: [String]
152−blockFilterNames = "Block" : constructorsFor (dataTypeOf (Para []))
153−
154−metaFilterName :: String
155−metaFilterName = "Meta"
156−
157−pandocFilterNames :: [String]
158−pandocFilterNames = ["Pandoc", "Doc"]
159−
160−type FunctionMap = Map String LuaFilterFunction
161−newtype LuaFilter = LuaFilter FunctionMap
162−newtype LuaFilterFunction = LuaFilterFunction { functionIndex :: Int }
163−
164−-- | Try running a filter for the given element
165−tryFilter :: (Data a, FromLuaStack a, ToLuaStack a)
166− => FunctionMap -> a -> Lua [a]
167−tryFilter fnMap x =
168− let filterFnName = showConstr (toConstr x)
169− catchAllName = tyconUQname $ dataTypeName (dataTypeOf x)
170− in
171− case Map.lookup filterFnName fnMap `mplus` Map.lookup catchAllName fnMap of
172− Just fn -> runFilterFunction fn x *> elementOrList x
173− Nothing -> return [x]
174−
175−instance FromLuaStack LuaFilter where
176− peek idx =
177− let constrs = metaFilterName : pandocFilterNames
178− ++ blockFilterNames
179− ++ inlineFilterNames
180− fn c acc = do
181− Lua.getfield idx c
182− filterFn <- Lua.tryLua (peek (-1))
183− Lua.pop 1
184− return $ case filterFn of
185− Left _ -> acc
186− Right f -> (c, f) : acc
187− in LuaFilter . Map.fromList <$> foldrM fn [] constrs
188−
189−-- | Push a value to the stack via a lua filter function. The filter function is
190−-- called with given element as argument and is expected to return an element.
191−-- Alternatively, the function can return nothing or nil, in which case the
192−-- element is left unchanged.
193−runFilterFunction :: ToLuaStack a => LuaFilterFunction -> a -> Lua ()
194−runFilterFunction lf x = do
195− pushFilterFunction lf
196− push x
197− z <- Lua.pcall 1 1 Nothing
198− when (z /= OK) $ do
199− let addPrefix = ("Error while running filter function: " ++)
200− Lua.throwTopMessageAsError' addPrefix
201−
202−elementOrList :: FromLuaStack a => a -> Lua [a]
203−elementOrList x = do
204− let topOfStack = Lua.StackIndex (-1)
205− elementUnchanged <- Lua.isnil topOfStack
206− if elementUnchanged
207− then [x] <$ Lua.pop 1
208− else do
209− mbres <- Lua.peekEither topOfStack
210− case mbres of
211− Right res -> [res] <$ Lua.pop 1
212− Left _ -> Lua.toList topOfStack <* Lua.pop 1
213−
214−singleElement :: FromLuaStack a => a -> Lua a
215−singleElement x = do
216− elementUnchanged <- Lua.isnil (-1)
217− if elementUnchanged
218− then x <$ Lua.pop 1
219− else do
220− mbres <- Lua.peekEither (-1)
221− case mbres of
222− Right res -> res <$ Lua.pop 1
223− Left err -> do
224− Lua.pop 1
225− Lua.throwLuaError $
226− "Error while trying to get a filter's return " ++
227− "value from lua stack.\n" ++ err
228−
229−-- | Push the filter function to the top of the stack.
230−pushFilterFunction :: LuaFilterFunction -> Lua ()
231−pushFilterFunction lf =
232− -- The function is stored in a lua registry table, retrieve it from there.
233− Lua.rawgeti Lua.registryindex (functionIndex lf)
234−
235−registerFilterFunction :: StackIndex -> Lua LuaFilterFunction
236−registerFilterFunction idx = do
237− isFn <- Lua.isfunction idx
238− unless isFn . Lua.throwLuaError $ "Not a function at index " ++ show idx
239− Lua.pushvalue idx
240− refIdx <- Lua.ref Lua.registryindex
241− return $ LuaFilterFunction refIdx
242−
243105instance (FromLuaStack a) => FromLuaStack (Identity a) where
244106 peek = fmap return . peek
245−
246−instance ToLuaStack LuaFilterFunction where
247− push = pushFilterFunction
248−
249−instance FromLuaStack LuaFilterFunction where
250− peek = registerFilterFunction