@@ -2,8 +2,8 @@
22{-# LANGUAGE NoImplicitPrelude #-}
33{- |
44Module : Text.Pandoc.Lua.Filter
5−Copyright : © 2012–2019 John MacFarlane,
6− © 2017-2019 Albert Krewinkel
5+Copyright : © 2012–2020 John MacFarlane,
6+ © 2017-2020 Albert Krewinkel
77License : GNU GPL, version 2 or above
88Maintainer : Albert Krewinkel <tarleb+pandoc@moltkeplatz.de>
99Stability : alpha
@@ -13,25 +13,23 @@ Types and functions for running Lua filters.
1313module Text.Pandoc.Lua.Filter ( LuaFilterFunction
1414 , LuaFilter
1515 , runFilterFile
16− , tryFilter
17− , runFilterFunction
18− , walkMWithLuaFilter
1916 , walkInlines
2017 , walkBlocks
21− , blockElementNames
22− , inlineElementNames
2318 , module Text.Pandoc.Lua.Walk
2419 ) where
2520import Prelude
21+import Control.Applicative ((<|>))
2622import Control.Monad (mplus, (>=>))
2723import Control.Monad.Catch (finally)
2824import Data.Data (Data, DataType, dataTypeConstrs, dataTypeName, dataTypeOf,
2925 showConstr, toConstr, tyconUQname)
3026import Data.Foldable (foldrM)
3127import Data.Map (Map)
28+import Data.Maybe (fromMaybe)
3229import Foreign.Lua (Lua, Peekable, Pushable)
3330import Text.Pandoc.Definition
3431import Text.Pandoc.Lua.Marshaling ()
32+import Text.Pandoc.Lua.Marshaling.List (List (..))
3533import Text.Pandoc.Lua.Walk (SingletonsList (..))
3634import Text.Pandoc.Walk (Walkable (walkM))
3735
@@ -67,7 +65,9 @@ newtype LuaFilter = LuaFilter (Map String LuaFilterFunction)
6765
6866instance Peekable LuaFilter where
6967 peek idx = do
70− let constrs = metaFilterName
68+ let constrs = listOfInlinesFilterName
69+ : listOfBlocksFilterName
70+ : metaFilterName
7171 : pandocFilterNames
7272 ++ blockElementNames
7373 ++ inlineElementNames
@@ -109,22 +109,34 @@ elementOrList x = do
109109 Right res -> [res] <$ Lua.pop 1
110110 Left _ -> Lua.peekList topOfStack `finally` Lua.pop 1
111111
112−-- | Try running a filter for the given element
113−tryFilter :: (Data a, Peekable a, Pushable a)
114− => LuaFilter -> a -> Lua [a]
115−tryFilter (LuaFilter fnMap) x =
116− let filterFnName = showConstr (toConstr x)
117− catchAllName = tyconUQname $ dataTypeName (dataTypeOf x)
118− in
119− case Map.lookup filterFnName fnMap `mplus` Map.lookup catchAllName fnMap of
120− Just fn -> runFilterFunction fn x *> elementOrList x
121− Nothing -> return [x]
122−
123−-- | Apply filter on a sequence of AST elements.
112+-- | Pop and return a value from the stack; if the value at the top of
113+-- the stack is @nil@, return the fallback element.
114+popOption :: Peekable a => a -> Lua a
115+popOption fallback = fromMaybe fallback . Lua.fromOptional <$> Lua.popValue
116+
117+-- | Apply filter on a sequence of AST elements. Both lists and single
118+-- value are accepted as filter function return values.
124119runOnSequence :: (Data a, Peekable a, Pushable a)
125120 => LuaFilter -> SingletonsList a -> Lua (SingletonsList a)
126−runOnSequence lf (SingletonsList xs) =
127− SingletonsList <$> mconcatMapM (tryFilter lf) xs
121+runOnSequence (LuaFilter fnMap) (SingletonsList xs) =
122+ SingletonsList <$> mconcatMapM tryFilter xs
123+ where
124+ tryFilter :: (Data a, Peekable a, Pushable a) => a -> Lua [a]
125+ tryFilter x =
126+ let filterFnName = showConstr (toConstr x)
127+ catchAllName = tyconUQname $ dataTypeName (dataTypeOf x)
128+ in case Map.lookup filterFnName fnMap <|> Map.lookup catchAllName fnMap of
129+ Just fn -> runFilterFunction fn x *> elementOrList x
130+ Nothing -> return [x]
131+
132+-- | Try filtering the given value without type error corrections on
133+-- the return value.
134+runOnValue :: (Data a, Peekable a, Pushable a)
135+ => String -> LuaFilter -> a -> Lua a
136+runOnValue filterFnName (LuaFilter fnMap) x =
137+ case Map.lookup filterFnName fnMap of
138+ Just fn -> runFilterFunction fn x *> popOption x
139+ Nothing -> return x
128140
129141-- | Push a value to the stack via a lua filter function. The filter function is
130142-- called with given element as argument and is expected to return an element.
@@ -138,7 +150,12 @@ runFilterFunction lf x = do
138150
139151walkMWithLuaFilter :: LuaFilter -> Pandoc -> Lua Pandoc
140152walkMWithLuaFilter f =
141− walkInlines f >=> walkBlocks f >=> walkMeta f >=> walkPandoc f
153+ walkInlines f
154+ >=> walkInlineLists f
155+ >=> walkBlocks f
156+ >=> walkBlockLists f
157+ >=> walkMeta f
158+ >=> walkPandoc f
142159
143160mconcatMapM :: (Monad m) => (a -> m [a]) -> [a] -> m [a]
144161mconcatMapM f = fmap mconcat . mapM f
@@ -146,6 +163,9 @@ mconcatMapM f = fmap mconcat . mapM f
146163hasOneOf :: LuaFilter -> [String] -> Bool
147164hasOneOf (LuaFilter fnMap) = any (\k -> Map.member k fnMap)
148165
166+contains :: LuaFilter -> String -> Bool
167+contains (LuaFilter fnMap) = (`Map.member` fnMap)
168+
149169walkInlines :: Walkable (SingletonsList Inline) a => LuaFilter -> a -> Lua a
150170walkInlines lf =
151171 let f :: SingletonsList Inline -> Lua (SingletonsList Inline)
@@ -154,6 +174,14 @@ walkInlines lf =
154174 then walkM f
155175 else return
156176
177+walkInlineLists :: Walkable (List Inline) a => LuaFilter -> a -> Lua a
178+walkInlineLists lf =
179+ let f :: List Inline -> Lua (List Inline)
180+ f = runOnValue listOfInlinesFilterName lf
181+ in if lf `contains` listOfInlinesFilterName
182+ then walkM f
183+ else return
184+
157185walkBlocks :: Walkable (SingletonsList Block) a => LuaFilter -> a -> Lua a
158186walkBlocks lf =
159187 let f :: SingletonsList Block -> Lua (SingletonsList Block)
@@ -162,13 +190,18 @@ walkBlocks lf =
162190 then walkM f
163191 else return
164192
193+walkBlockLists :: Walkable (List Block) a => LuaFilter -> a -> Lua a
194+walkBlockLists lf =
195+ let f :: List Block -> Lua (List Block)
196+ f = runOnValue listOfBlocksFilterName lf
197+ in if lf `contains` listOfBlocksFilterName
198+ then walkM f
199+ else return
200+
165201walkMeta :: LuaFilter -> Pandoc -> Lua Pandoc
166−walkMeta (LuaFilter fnMap) =
167− case Map.lookup "Meta" fnMap of
168− Just fn -> walkM (\(Pandoc meta blocks) -> do
169− meta' <- runFilterFunction fn meta *> singleElement meta
170− return $ Pandoc meta' blocks)
171− Nothing -> return
202+walkMeta lf (Pandoc m bs) = do
203+ m' <- runOnValue "Meta" lf m
204+ return $ Pandoc m' bs
172205
173206walkPandoc :: LuaFilter -> Pandoc -> Lua Pandoc
174207walkPandoc (LuaFilter fnMap) =
@@ -185,6 +218,12 @@ inlineElementNames = "Inline" : constructorsFor (dataTypeOf (Str mempty))
185218blockElementNames :: [String]
186219blockElementNames = "Block" : constructorsFor (dataTypeOf (Para []))
187220
221+listOfInlinesFilterName :: String
222+listOfInlinesFilterName = "Inlines"
223+
224+listOfBlocksFilterName :: String
225+listOfBlocksFilterName = "Blocks"
226+
188227metaFilterName :: String
189228metaFilterName = "Meta"
190229