{-# LANGUAGE FlexibleContexts  #-}
{- |
Module      : Text.Pandoc.Lua.Filter
Copyright   : © 2012-2021 John MacFarlane,
              © 2017-2021 Albert Krewinkel
License     : GNU GPL, version 2 or above
Maintainer  : Albert Krewinkel <tarleb+pandoc@moltkeplatz.de>
Stability   : alpha

Types and functions for running Lua filters.
-}
module Text.Pandoc.Lua.Filter ( LuaFilterFunction
                              , LuaFilter
                              , runFilterFile
                              , walkInlines
                              , walkBlocks
                              , module Text.Pandoc.Lua.Walk
                              ) where
import Control.Applicative ((<|>))
import Control.Monad (mplus, (>=>))
import Control.Monad.Catch (finally, try)
import Data.Data (Data, DataType, dataTypeConstrs, dataTypeName, dataTypeOf,
                  showConstr, toConstr, tyconUQname)
import Data.Foldable (foldrM)
import Data.List (foldl')
import Data.Map (Map)
import Data.Maybe (fromMaybe)
import Foreign.Lua (Lua, Peekable, Pushable, StackIndex)
import Text.Pandoc.Definition
import Text.Pandoc.Error (PandocError)
import Text.Pandoc.Lua.Marshaling ()
import Text.Pandoc.Lua.Marshaling.List (List (..))
import Text.Pandoc.Lua.Walk (SingletonsList (..))
import Text.Pandoc.Walk (Walkable (walkM))

import qualified Data.Map.Strict as Map
import qualified Foreign.Lua as Lua
import qualified Text.Pandoc.Lua.Util as LuaUtil

-- | Transform document using the filter defined in the given file.
runFilterFile :: FilePath -> Pandoc -> Lua Pandoc
runFilterFile :: String -> Pandoc -> Lua Pandoc
runFilterFile String
filterPath Pandoc
doc = do
  StackIndex
top <- Lua StackIndex
Lua.gettop
  Status
stat <- String -> Lua Status
LuaUtil.dofileWithTraceback String
filterPath
  if Status
stat Status -> Status -> Bool
forall a. Eq a => a -> a -> Bool
/= Status
Lua.OK
    then Lua Pandoc
forall a. Lua a
Lua.throwTopMessage
    else do
      StackIndex
newtop <- Lua StackIndex
Lua.gettop
      -- Use the returned filters, or the implicitly defined global
      -- filter if nothing was returned.
      [LuaFilter]
luaFilters <- if StackIndex
newtop StackIndex -> StackIndex -> StackIndex
forall a. Num a => a -> a -> a
- StackIndex
top StackIndex -> StackIndex -> Bool
forall a. Ord a => a -> a -> Bool
>= StackIndex
1
                    then StackIndex -> Lua [LuaFilter]
forall a. Peekable a => StackIndex -> Lua a
Lua.peek StackIndex
Lua.stackTop
                    else Lua ()
Lua.pushglobaltable Lua () -> Lua [LuaFilter] -> Lua [LuaFilter]
forall (f :: * -> *) a b. Applicative f => f a -> f b -> f b
*> (LuaFilter -> [LuaFilter]) -> Lua LuaFilter -> Lua [LuaFilter]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (LuaFilter -> [LuaFilter] -> [LuaFilter]
forall a. a -> [a] -> [a]
:[]) Lua LuaFilter
forall a. Peekable a => Lua a
Lua.popValue
      [LuaFilter] -> Pandoc -> Lua Pandoc
runAll [LuaFilter]
luaFilters Pandoc
doc

runAll :: [LuaFilter] -> Pandoc -> Lua Pandoc
runAll :: [LuaFilter] -> Pandoc -> Lua Pandoc
runAll = (LuaFilter -> (Pandoc -> Lua Pandoc) -> Pandoc -> Lua Pandoc)
-> (Pandoc -> Lua Pandoc) -> [LuaFilter] -> Pandoc -> Lua Pandoc
forall (t :: * -> *) a b.
Foldable t =>
(a -> b -> b) -> b -> t a -> b
foldr ((Pandoc -> Lua Pandoc)
-> (Pandoc -> Lua Pandoc) -> Pandoc -> Lua Pandoc
forall (m :: * -> *) a b c.
Monad m =>
(a -> m b) -> (b -> m c) -> a -> m c
(>=>) ((Pandoc -> Lua Pandoc)
 -> (Pandoc -> Lua Pandoc) -> Pandoc -> Lua Pandoc)
-> (LuaFilter -> Pandoc -> Lua Pandoc)
-> LuaFilter
-> (Pandoc -> Lua Pandoc)
-> Pandoc
-> Lua Pandoc
forall b c a. (b -> c) -> (a -> b) -> a -> c
. LuaFilter -> Pandoc -> Lua Pandoc
walkMWithLuaFilter) Pandoc -> Lua Pandoc
forall (m :: * -> *) a. Monad m => a -> m a
return

-- | Filter function stored in the registry
newtype LuaFilterFunction = LuaFilterFunction Lua.Reference

-- | Collection of filter functions (at most one function per element
-- constructor)
newtype LuaFilter = LuaFilter (Map String LuaFilterFunction)

instance Peekable LuaFilter where
  peek :: StackIndex -> Lua LuaFilter
peek StackIndex
idx = do
    let constrs :: [String]
constrs = String
listOfInlinesFilterName
                String -> [String] -> [String]
forall a. a -> [a] -> [a]
: String
listOfBlocksFilterName
                String -> [String] -> [String]
forall a. a -> [a] -> [a]
: String
metaFilterName
                String -> [String] -> [String]
forall a. a -> [a] -> [a]
: [String]
pandocFilterNames
                [String] -> [String] -> [String]
forall a. [a] -> [a] -> [a]
++ [String]
blockElementNames
                [String] -> [String] -> [String]
forall a. [a] -> [a] -> [a]
++ [String]
inlineElementNames
    let go :: String
-> Map String LuaFilterFunction
-> Lua (Map String LuaFilterFunction)
go String
constr Map String LuaFilterFunction
acc = do
          StackIndex -> String -> Lua ()
Lua.getfield StackIndex
idx String
constr
          Maybe LuaFilterFunction
filterFn <- Lua (Maybe LuaFilterFunction)
registerFilterFunction
          Map String LuaFilterFunction -> Lua (Map String LuaFilterFunction)
forall (m :: * -> *) a. Monad m => a -> m a
return (Map String LuaFilterFunction
 -> Lua (Map String LuaFilterFunction))
-> Map String LuaFilterFunction
-> Lua (Map String LuaFilterFunction)
forall a b. (a -> b) -> a -> b
$ case Maybe LuaFilterFunction
filterFn of
            Maybe LuaFilterFunction
Nothing -> Map String LuaFilterFunction
acc
            Just LuaFilterFunction
fn -> String
-> LuaFilterFunction
-> Map String LuaFilterFunction
-> Map String LuaFilterFunction
forall k a. Ord k => k -> a -> Map k a -> Map k a
Map.insert String
constr LuaFilterFunction
fn Map String LuaFilterFunction
acc
    Map String LuaFilterFunction -> LuaFilter
LuaFilter (Map String LuaFilterFunction -> LuaFilter)
-> Lua (Map String LuaFilterFunction) -> Lua LuaFilter
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> (String
 -> Map String LuaFilterFunction
 -> Lua (Map String LuaFilterFunction))
-> Map String LuaFilterFunction
-> [String]
-> Lua (Map String LuaFilterFunction)
forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
(a -> b -> m b) -> b -> t a -> m b
foldrM String
-> Map String LuaFilterFunction
-> Lua (Map String LuaFilterFunction)
go Map String LuaFilterFunction
forall k a. Map k a
Map.empty [String]
constrs

-- | Register the function at the top of the stack as a filter function in the
-- registry.
registerFilterFunction :: Lua (Maybe LuaFilterFunction)
registerFilterFunction :: Lua (Maybe LuaFilterFunction)
registerFilterFunction = do
  Bool
isFn <- StackIndex -> Lua Bool
Lua.isfunction StackIndex
Lua.stackTop
  if Bool
isFn
    then LuaFilterFunction -> Maybe LuaFilterFunction
forall a. a -> Maybe a
Just (LuaFilterFunction -> Maybe LuaFilterFunction)
-> (Reference -> LuaFilterFunction)
-> Reference
-> Maybe LuaFilterFunction
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Reference -> LuaFilterFunction
LuaFilterFunction (Reference -> Maybe LuaFilterFunction)
-> Lua Reference -> Lua (Maybe LuaFilterFunction)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> StackIndex -> Lua Reference
Lua.ref StackIndex
Lua.registryindex
    else Maybe LuaFilterFunction
forall a. Maybe a
Nothing Maybe LuaFilterFunction -> Lua () -> Lua (Maybe LuaFilterFunction)
forall (f :: * -> *) a b. Functor f => a -> f b -> f a
<$ StackIndex -> Lua ()
Lua.pop StackIndex
1

-- | Retrieve filter function from registry and push it to the top of the stack.
pushFilterFunction :: LuaFilterFunction -> Lua ()
pushFilterFunction :: LuaFilterFunction -> Lua ()
pushFilterFunction (LuaFilterFunction Reference
fnRef) =
  StackIndex -> Reference -> Lua ()
Lua.getref StackIndex
Lua.registryindex Reference
fnRef

-- | Fetch either a list of elements from the stack. If there is a single
-- element instead of a list, fetch that element as a singleton list. If the top
-- of the stack is nil, return the default element that was passed to this
-- function. If none of these apply, raise an error.
elementOrList :: Peekable a => a -> Lua [a]
elementOrList :: forall a. Peekable a => a -> Lua [a]
elementOrList a
x = do
  let topOfStack :: StackIndex
topOfStack = StackIndex
Lua.stackTop
  Bool
elementUnchanged <- StackIndex -> Lua Bool
Lua.isnil StackIndex
topOfStack
  if Bool
elementUnchanged
    then [a
x] [a] -> Lua () -> Lua [a]
forall (f :: * -> *) a b. Functor f => a -> f b -> f a
<$ StackIndex -> Lua ()
Lua.pop StackIndex
1
    else do
       Either PandocError a
mbres <- StackIndex -> Lua (Either PandocError a)
forall a. Peekable a => StackIndex -> Lua (Either PandocError a)
peekEither StackIndex
topOfStack
       case Either PandocError a
mbres of
         Right a
res -> [a
res] [a] -> Lua () -> Lua [a]
forall (f :: * -> *) a b. Functor f => a -> f b -> f a
<$ StackIndex -> Lua ()
Lua.pop StackIndex
1
         Left PandocError
_    -> StackIndex -> Lua [a]
forall a. Peekable a => StackIndex -> Lua [a]
Lua.peekList StackIndex
topOfStack Lua [a] -> Lua () -> Lua [a]
forall (m :: * -> *) a b. MonadMask m => m a -> m b -> m a
`finally` StackIndex -> Lua ()
Lua.pop StackIndex
1

-- | Pop and return a value from the stack; if the value at the top of
-- the stack is @nil@, return the fallback element.
popOption :: Peekable a => a -> Lua a
popOption :: forall a. Peekable a => a -> Lua a
popOption a
fallback = a -> Maybe a -> a
forall a. a -> Maybe a -> a
fromMaybe a
fallback (Maybe a -> a) -> (Optional a -> Maybe a) -> Optional a -> a
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Optional a -> Maybe a
forall a. Optional a -> Maybe a
Lua.fromOptional (Optional a -> a) -> Lua (Optional a) -> Lua a
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Lua (Optional a)
forall a. Peekable a => Lua a
Lua.popValue

-- | Apply filter on a sequence of AST elements. Both lists and single
-- value are accepted as filter function return values.
runOnSequence :: (Data a, Peekable a, Pushable a)
              => LuaFilter -> SingletonsList a -> Lua (SingletonsList a)
runOnSequence :: forall a.
(Data a, Peekable a, Pushable a) =>
LuaFilter -> SingletonsList a -> Lua (SingletonsList a)
runOnSequence (LuaFilter Map String LuaFilterFunction
fnMap) (SingletonsList [a]
xs) =
  [a] -> SingletonsList a
forall a. [a] -> SingletonsList a
SingletonsList ([a] -> SingletonsList a) -> Lua [a] -> Lua (SingletonsList a)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> (a -> Lua [a]) -> [a] -> Lua [a]
forall (m :: * -> *) a. Monad m => (a -> m [a]) -> [a] -> m [a]
mconcatMapM a -> Lua [a]
forall a. (Data a, Peekable a, Pushable a) => a -> Lua [a]
tryFilter [a]
xs
 where
  tryFilter :: (Data a, Peekable a, Pushable a) => a -> Lua [a]
  tryFilter :: forall a. (Data a, Peekable a, Pushable a) => a -> Lua [a]
tryFilter a
x =
    let filterFnName :: String
filterFnName = Constr -> String
showConstr (a -> Constr
forall a. Data a => a -> Constr
toConstr a
x)
        catchAllName :: String
catchAllName = String -> String
tyconUQname (String -> String) -> String -> String
forall a b. (a -> b) -> a -> b
$ DataType -> String
dataTypeName (a -> DataType
forall a. Data a => a -> DataType
dataTypeOf a
x)
    in case String -> Map String LuaFilterFunction -> Maybe LuaFilterFunction
forall k a. Ord k => k -> Map k a -> Maybe a
Map.lookup String
filterFnName Map String LuaFilterFunction
fnMap Maybe LuaFilterFunction
-> Maybe LuaFilterFunction -> Maybe LuaFilterFunction
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> String -> Map String LuaFilterFunction -> Maybe LuaFilterFunction
forall k a. Ord k => k -> Map k a -> Maybe a
Map.lookup String
catchAllName Map String LuaFilterFunction
fnMap of
         Just LuaFilterFunction
fn -> LuaFilterFunction -> a -> Lua ()
forall a. Pushable a => LuaFilterFunction -> a -> Lua ()
runFilterFunction LuaFilterFunction
fn a
x Lua () -> Lua [a] -> Lua [a]
forall (f :: * -> *) a b. Applicative f => f a -> f b -> f b
*> a -> Lua [a]
forall a. Peekable a => a -> Lua [a]
elementOrList a
x
         Maybe LuaFilterFunction
Nothing -> [a] -> Lua [a]
forall (m :: * -> *) a. Monad m => a -> m a
return [a
x]

-- | Try filtering the given value without type error corrections on
-- the return value.
runOnValue :: (Data a, Peekable a, Pushable a)
           => String -> LuaFilter -> a -> Lua a
runOnValue :: forall a.
(Data a, Peekable a, Pushable a) =>
String -> LuaFilter -> a -> Lua a
runOnValue String
filterFnName (LuaFilter Map String LuaFilterFunction
fnMap) a
x =
  case String -> Map String LuaFilterFunction -> Maybe LuaFilterFunction
forall k a. Ord k => k -> Map k a -> Maybe a
Map.lookup String
filterFnName Map String LuaFilterFunction
fnMap of
    Just LuaFilterFunction
fn -> LuaFilterFunction -> a -> Lua ()
forall a. Pushable a => LuaFilterFunction -> a -> Lua ()
runFilterFunction LuaFilterFunction
fn a
x Lua () -> Lua a -> Lua a
forall (f :: * -> *) a b. Applicative f => f a -> f b -> f b
*> a -> Lua a
forall a. Peekable a => a -> Lua a
popOption a
x
    Maybe LuaFilterFunction
Nothing -> a -> Lua a
forall (m :: * -> *) a. Monad m => a -> m a
return a
x

-- | Push a value to the stack via a lua filter function. The filter function is
-- called with given element as argument and is expected to return an element.
-- Alternatively, the function can return nothing or nil, in which case the
-- element is left unchanged.
runFilterFunction :: Pushable a => LuaFilterFunction -> a -> Lua ()
runFilterFunction :: forall a. Pushable a => LuaFilterFunction -> a -> Lua ()
runFilterFunction LuaFilterFunction
lf a
x = do
  LuaFilterFunction -> Lua ()
pushFilterFunction LuaFilterFunction
lf
  a -> Lua ()
forall a. Pushable a => a -> Lua ()
Lua.push a
x
  NumArgs -> NumResults -> Lua ()
LuaUtil.callWithTraceback NumArgs
1 NumResults
1

walkMWithLuaFilter :: LuaFilter -> Pandoc -> Lua Pandoc
walkMWithLuaFilter :: LuaFilter -> Pandoc -> Lua Pandoc
walkMWithLuaFilter LuaFilter
f =
      LuaFilter -> Pandoc -> Lua Pandoc
forall a.
Walkable (SingletonsList Inline) a =>
LuaFilter -> a -> Lua a
walkInlines LuaFilter
f
  (Pandoc -> Lua Pandoc)
-> (Pandoc -> Lua Pandoc) -> Pandoc -> Lua Pandoc
forall (m :: * -> *) a b c.
Monad m =>
(a -> m b) -> (b -> m c) -> a -> m c
>=> LuaFilter -> Pandoc -> Lua Pandoc
forall a. Walkable (List Inline) a => LuaFilter -> a -> Lua a
walkInlineLists LuaFilter
f
  (Pandoc -> Lua Pandoc)
-> (Pandoc -> Lua Pandoc) -> Pandoc -> Lua Pandoc
forall (m :: * -> *) a b c.
Monad m =>
(a -> m b) -> (b -> m c) -> a -> m c
>=> LuaFilter -> Pandoc -> Lua Pandoc
forall a.
Walkable (SingletonsList Block) a =>
LuaFilter -> a -> Lua a
walkBlocks LuaFilter
f
  (Pandoc -> Lua Pandoc)
-> (Pandoc -> Lua Pandoc) -> Pandoc -> Lua Pandoc
forall (m :: * -> *) a b c.
Monad m =>
(a -> m b) -> (b -> m c) -> a -> m c
>=> LuaFilter -> Pandoc -> Lua Pandoc
forall a. Walkable (List Block) a => LuaFilter -> a -> Lua a
walkBlockLists LuaFilter
f
  (Pandoc -> Lua Pandoc)
-> (Pandoc -> Lua Pandoc) -> Pandoc -> Lua Pandoc
forall (m :: * -> *) a b c.
Monad m =>
(a -> m b) -> (b -> m c) -> a -> m c
>=> LuaFilter -> Pandoc -> Lua Pandoc
walkMeta LuaFilter
f
  (Pandoc -> Lua Pandoc)
-> (Pandoc -> Lua Pandoc) -> Pandoc -> Lua Pandoc
forall (m :: * -> *) a b c.
Monad m =>
(a -> m b) -> (b -> m c) -> a -> m c
>=> LuaFilter -> Pandoc -> Lua Pandoc
walkPandoc LuaFilter
f

mconcatMapM :: (Monad m) => (a -> m [a]) -> [a] -> m [a]
mconcatMapM :: forall (m :: * -> *) a. Monad m => (a -> m [a]) -> [a] -> m [a]
mconcatMapM a -> m [a]
f = ([[a]] -> [a]) -> m [[a]] -> m [a]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap [[a]] -> [a]
forall a. Monoid a => [a] -> a
mconcat (m [[a]] -> m [a]) -> ([a] -> m [[a]]) -> [a] -> m [a]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (a -> m [a]) -> [a] -> m [[a]]
forall (t :: * -> *) (m :: * -> *) a b.
(Traversable t, Monad m) =>
(a -> m b) -> t a -> m (t b)
mapM a -> m [a]
f

hasOneOf :: LuaFilter -> [String] -> Bool
hasOneOf :: LuaFilter -> [String] -> Bool
hasOneOf (LuaFilter Map String LuaFilterFunction
fnMap) = (String -> Bool) -> [String] -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
any (String -> Map String LuaFilterFunction -> Bool
forall k a. Ord k => k -> Map k a -> Bool
`Map.member` Map String LuaFilterFunction
fnMap)

contains :: LuaFilter -> String -> Bool
contains :: LuaFilter -> String -> Bool
contains (LuaFilter Map String LuaFilterFunction
fnMap) = (String -> Map String LuaFilterFunction -> Bool
forall k a. Ord k => k -> Map k a -> Bool
`Map.member` Map String LuaFilterFunction
fnMap)

walkInlines :: Walkable (SingletonsList Inline) a => LuaFilter -> a -> Lua a
walkInlines :: forall a.
Walkable (SingletonsList Inline) a =>
LuaFilter -> a -> Lua a
walkInlines LuaFilter
lf =
  let f :: SingletonsList Inline -> Lua (SingletonsList Inline)
      f :: SingletonsList Inline -> Lua (SingletonsList Inline)
f = LuaFilter -> SingletonsList Inline -> Lua (SingletonsList Inline)
forall a.
(Data a, Peekable a, Pushable a) =>
LuaFilter -> SingletonsList a -> Lua (SingletonsList a)
runOnSequence LuaFilter
lf
  in if LuaFilter
lf LuaFilter -> [String] -> Bool
`hasOneOf` [String]
inlineElementNames
     then (SingletonsList Inline -> Lua (SingletonsList Inline))
-> a -> Lua a
forall a b (m :: * -> *).
(Walkable a b, Monad m, Applicative m, Functor m) =>
(a -> m a) -> b -> m b
walkM SingletonsList Inline -> Lua (SingletonsList Inline)
f
     else a -> Lua a
forall (m :: * -> *) a. Monad m => a -> m a
return

walkInlineLists :: Walkable (List Inline) a => LuaFilter -> a -> Lua a
walkInlineLists :: forall a. Walkable (List Inline) a => LuaFilter -> a -> Lua a
walkInlineLists LuaFilter
lf =
  let f :: List Inline -> Lua (List Inline)
      f :: List Inline -> Lua (List Inline)
f = String -> LuaFilter -> List Inline -> Lua (List Inline)
forall a.
(Data a, Peekable a, Pushable a) =>
String -> LuaFilter -> a -> Lua a
runOnValue String
listOfInlinesFilterName LuaFilter
lf
  in if LuaFilter
lf LuaFilter -> String -> Bool
`contains` String
listOfInlinesFilterName
     then (List Inline -> Lua (List Inline)) -> a -> Lua a
forall a b (m :: * -> *).
(Walkable a b, Monad m, Applicative m, Functor m) =>
(a -> m a) -> b -> m b
walkM List Inline -> Lua (List Inline)
f
     else a -> Lua a
forall (m :: * -> *) a. Monad m => a -> m a
return

walkBlocks :: Walkable (SingletonsList Block) a => LuaFilter -> a -> Lua a
walkBlocks :: forall a.
Walkable (SingletonsList Block) a =>
LuaFilter -> a -> Lua a
walkBlocks LuaFilter
lf =
  let f :: SingletonsList Block -> Lua (SingletonsList Block)
      f :: SingletonsList Block -> Lua (SingletonsList Block)
f = LuaFilter -> SingletonsList Block -> Lua (SingletonsList Block)
forall a.
(Data a, Peekable a, Pushable a) =>
LuaFilter -> SingletonsList a -> Lua (SingletonsList a)
runOnSequence LuaFilter
lf
  in if LuaFilter
lf LuaFilter -> [String] -> Bool
`hasOneOf` [String]
blockElementNames
     then (SingletonsList Block -> Lua (SingletonsList Block)) -> a -> Lua a
forall a b (m :: * -> *).
(Walkable a b, Monad m, Applicative m, Functor m) =>
(a -> m a) -> b -> m b
walkM SingletonsList Block -> Lua (SingletonsList Block)
f
     else a -> Lua a
forall (m :: * -> *) a. Monad m => a -> m a
return

walkBlockLists :: Walkable (List Block) a => LuaFilter -> a -> Lua a
walkBlockLists :: forall a. Walkable (List Block) a => LuaFilter -> a -> Lua a
walkBlockLists LuaFilter
lf =
  let f :: List Block -> Lua (List Block)
      f :: List Block -> Lua (List Block)
f = String -> LuaFilter -> List Block -> Lua (List Block)
forall a.
(Data a, Peekable a, Pushable a) =>
String -> LuaFilter -> a -> Lua a
runOnValue String
listOfBlocksFilterName LuaFilter
lf
  in if LuaFilter
lf LuaFilter -> String -> Bool
`contains` String
listOfBlocksFilterName
     then (List Block -> Lua (List Block)) -> a -> Lua a
forall a b (m :: * -> *).
(Walkable a b, Monad m, Applicative m, Functor m) =>
(a -> m a) -> b -> m b
walkM List Block -> Lua (List Block)
f
     else a -> Lua a
forall (m :: * -> *) a. Monad m => a -> m a
return

walkMeta :: LuaFilter -> Pandoc -> Lua Pandoc
walkMeta :: LuaFilter -> Pandoc -> Lua Pandoc
walkMeta LuaFilter
lf (Pandoc Meta
m [Block]
bs) = do
  Meta
m' <- String -> LuaFilter -> Meta -> Lua Meta
forall a.
(Data a, Peekable a, Pushable a) =>
String -> LuaFilter -> a -> Lua a
runOnValue String
"Meta" LuaFilter
lf Meta
m
  Pandoc -> Lua Pandoc
forall (m :: * -> *) a. Monad m => a -> m a
return (Pandoc -> Lua Pandoc) -> Pandoc -> Lua Pandoc
forall a b. (a -> b) -> a -> b
$ Meta -> [Block] -> Pandoc
Pandoc Meta
m' [Block]
bs

walkPandoc :: LuaFilter -> Pandoc -> Lua Pandoc
walkPandoc :: LuaFilter -> Pandoc -> Lua Pandoc
walkPandoc (LuaFilter Map String LuaFilterFunction
fnMap) =
  case (Maybe LuaFilterFunction
 -> Maybe LuaFilterFunction -> Maybe LuaFilterFunction)
-> Maybe LuaFilterFunction
-> [Maybe LuaFilterFunction]
-> Maybe LuaFilterFunction
forall (t :: * -> *) b a.
Foldable t =>
(b -> a -> b) -> b -> t a -> b
foldl' Maybe LuaFilterFunction
-> Maybe LuaFilterFunction -> Maybe LuaFilterFunction
forall (m :: * -> *) a. MonadPlus m => m a -> m a -> m a
mplus Maybe LuaFilterFunction
forall a. Maybe a
Nothing ((String -> Maybe LuaFilterFunction)
-> [String] -> [Maybe LuaFilterFunction]
forall a b. (a -> b) -> [a] -> [b]
map (String -> Map String LuaFilterFunction -> Maybe LuaFilterFunction
forall k a. Ord k => k -> Map k a -> Maybe a
`Map.lookup` Map String LuaFilterFunction
fnMap) [String]
pandocFilterNames) of
    Just LuaFilterFunction
fn -> \Pandoc
x -> LuaFilterFunction -> Pandoc -> Lua ()
forall a. Pushable a => LuaFilterFunction -> a -> Lua ()
runFilterFunction LuaFilterFunction
fn Pandoc
x Lua () -> Lua Pandoc -> Lua Pandoc
forall (f :: * -> *) a b. Applicative f => f a -> f b -> f b
*> Pandoc -> Lua Pandoc
forall a. Peekable a => a -> Lua a
singleElement Pandoc
x
    Maybe LuaFilterFunction
Nothing -> Pandoc -> Lua Pandoc
forall (m :: * -> *) a. Monad m => a -> m a
return

constructorsFor :: DataType -> [String]
constructorsFor :: DataType -> [String]
constructorsFor DataType
x = (Constr -> String) -> [Constr] -> [String]
forall a b. (a -> b) -> [a] -> [b]
map Constr -> String
forall a. Show a => a -> String
show (DataType -> [Constr]
dataTypeConstrs DataType
x)

inlineElementNames :: [String]
inlineElementNames :: [String]
inlineElementNames = String
"Inline" String -> [String] -> [String]
forall a. a -> [a] -> [a]
: DataType -> [String]
constructorsFor (Inline -> DataType
forall a. Data a => a -> DataType
dataTypeOf (Text -> Inline
Str Text
forall a. Monoid a => a
mempty))

blockElementNames :: [String]
blockElementNames :: [String]
blockElementNames = String
"Block" String -> [String] -> [String]
forall a. a -> [a] -> [a]
: DataType -> [String]
constructorsFor (Block -> DataType
forall a. Data a => a -> DataType
dataTypeOf ([Inline] -> Block
Para []))

listOfInlinesFilterName :: String
listOfInlinesFilterName :: String
listOfInlinesFilterName = String
"Inlines"

listOfBlocksFilterName :: String
listOfBlocksFilterName :: String
listOfBlocksFilterName = String
"Blocks"

metaFilterName :: String
metaFilterName :: String
metaFilterName = String
"Meta"

pandocFilterNames :: [String]
pandocFilterNames :: [String]
pandocFilterNames = [String
"Pandoc", String
"Doc"]

singleElement :: Peekable a => a -> Lua a
singleElement :: forall a. Peekable a => a -> Lua a
singleElement a
x = do
  Bool
elementUnchanged <- StackIndex -> Lua Bool
Lua.isnil (-StackIndex
1)
  if Bool
elementUnchanged
    then a
x a -> Lua () -> Lua a
forall (f :: * -> *) a b. Functor f => a -> f b -> f a
<$ StackIndex -> Lua ()
Lua.pop StackIndex
1
    else do
    Either PandocError a
mbres <- StackIndex -> Lua (Either PandocError a)
forall a. Peekable a => StackIndex -> Lua (Either PandocError a)
peekEither (-StackIndex
1)
    case Either PandocError a
mbres of
      Right a
res -> a
res a -> Lua () -> Lua a
forall (f :: * -> *) a b. Functor f => a -> f b -> f a
<$ StackIndex -> Lua ()
Lua.pop StackIndex
1
      Left PandocError
err  -> do
        StackIndex -> Lua ()
Lua.pop StackIndex
1
        String -> Lua a
forall a. String -> Lua a
Lua.throwMessage
          (String
"Error while trying to get a filter's return " String -> String -> String
forall a. Semigroup a => a -> a -> a
<>
           String
"value from Lua stack.\n" String -> String -> String
forall a. Semigroup a => a -> a -> a
<> PandocError -> String
forall a. Show a => a -> String
show PandocError
err)

-- | Try to convert the value at the given stack index to a Haskell value.
-- Returns @Left@ with an error message on failure.
peekEither :: Peekable a => StackIndex -> Lua (Either PandocError a)
peekEither :: forall a. Peekable a => StackIndex -> Lua (Either PandocError a)
peekEither = Lua a -> Lua (Either PandocError a)
forall (m :: * -> *) e a.
(MonadCatch m, Exception e) =>
m a -> m (Either e a)
try (Lua a -> Lua (Either PandocError a))
-> (StackIndex -> Lua a)
-> StackIndex
-> Lua (Either PandocError a)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. StackIndex -> Lua a
forall a. Peekable a => StackIndex -> Lua a
Lua.peek