{-# 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
                              , walkInlineLists
                              , walkBlocks
                              , walkBlockLists
                              , 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 :: FilePath -> Pandoc -> Lua Pandoc
runFilterFile FilePath
filterPath Pandoc
doc = do
  StackIndex
top <- Lua StackIndex
Lua.gettop
  Status
stat <- FilePath -> Lua Status
LuaUtil.dofileWithTraceback FilePath
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 :: [FilePath]
constrs = FilePath
listOfInlinesFilterName
                FilePath -> [FilePath] -> [FilePath]
forall a. a -> [a] -> [a]
: FilePath
listOfBlocksFilterName
                FilePath -> [FilePath] -> [FilePath]
forall a. a -> [a] -> [a]
: FilePath
metaFilterName
                FilePath -> [FilePath] -> [FilePath]
forall a. a -> [a] -> [a]
: [FilePath]
pandocFilterNames
                [FilePath] -> [FilePath] -> [FilePath]
forall a. [a] -> [a] -> [a]
++ [FilePath]
blockElementNames
                [FilePath] -> [FilePath] -> [FilePath]
forall a. [a] -> [a] -> [a]
++ [FilePath]
inlineElementNames
    let go :: FilePath
-> Map FilePath LuaFilterFunction
-> Lua (Map FilePath LuaFilterFunction)
go FilePath
constr Map FilePath LuaFilterFunction
acc = do
          StackIndex -> FilePath -> Lua ()
Lua.getfield StackIndex
idx FilePath
constr
          Maybe LuaFilterFunction
filterFn <- Lua (Maybe LuaFilterFunction)
registerFilterFunction
          Map FilePath LuaFilterFunction
-> Lua (Map FilePath LuaFilterFunction)
forall (m :: * -> *) a. Monad m => a -> m a
return (Map FilePath LuaFilterFunction
 -> Lua (Map FilePath LuaFilterFunction))
-> Map FilePath LuaFilterFunction
-> Lua (Map FilePath LuaFilterFunction)
forall a b. (a -> b) -> a -> b
$ case Maybe LuaFilterFunction
filterFn of
            Maybe LuaFilterFunction
Nothing -> Map FilePath LuaFilterFunction
acc
            Just LuaFilterFunction
fn -> FilePath
-> LuaFilterFunction
-> Map FilePath LuaFilterFunction
-> Map FilePath LuaFilterFunction
forall k a. Ord k => k -> a -> Map k a -> Map k a
Map.insert FilePath
constr LuaFilterFunction
fn Map FilePath LuaFilterFunction
acc
    Map FilePath LuaFilterFunction -> LuaFilter
LuaFilter (Map FilePath LuaFilterFunction -> LuaFilter)
-> Lua (Map FilePath LuaFilterFunction) -> Lua LuaFilter
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> (FilePath
 -> Map FilePath LuaFilterFunction
 -> Lua (Map FilePath LuaFilterFunction))
-> Map FilePath LuaFilterFunction
-> [FilePath]
-> Lua (Map FilePath LuaFilterFunction)
forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
(a -> b -> m b) -> b -> t a -> m b
foldrM FilePath
-> Map FilePath LuaFilterFunction
-> Lua (Map FilePath LuaFilterFunction)
go Map FilePath LuaFilterFunction
forall k a. Map k a
Map.empty [FilePath]
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 :: 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 :: 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 :: LuaFilter -> SingletonsList a -> Lua (SingletonsList a)
runOnSequence (LuaFilter Map FilePath 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 :: a -> Lua [a]
tryFilter a
x =
    let filterFnName :: FilePath
filterFnName = Constr -> FilePath
showConstr (a -> Constr
forall a. Data a => a -> Constr
toConstr a
x)
        catchAllName :: FilePath
catchAllName = FilePath -> FilePath
tyconUQname (FilePath -> FilePath) -> FilePath -> FilePath
forall a b. (a -> b) -> a -> b
$ DataType -> FilePath
dataTypeName (a -> DataType
forall a. Data a => a -> DataType
dataTypeOf a
x)
    in case FilePath
-> Map FilePath LuaFilterFunction -> Maybe LuaFilterFunction
forall k a. Ord k => k -> Map k a -> Maybe a
Map.lookup FilePath
filterFnName Map FilePath LuaFilterFunction
fnMap Maybe LuaFilterFunction
-> Maybe LuaFilterFunction -> Maybe LuaFilterFunction
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> FilePath
-> Map FilePath LuaFilterFunction -> Maybe LuaFilterFunction
forall k a. Ord k => k -> Map k a -> Maybe a
Map.lookup FilePath
catchAllName Map FilePath 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 :: FilePath -> LuaFilter -> a -> Lua a
runOnValue FilePath
filterFnName (LuaFilter Map FilePath LuaFilterFunction
fnMap) a
x =
  case FilePath
-> Map FilePath LuaFilterFunction -> Maybe LuaFilterFunction
forall k a. Ord k => k -> Map k a -> Maybe a
Map.lookup FilePath
filterFnName Map FilePath 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 :: 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 :: (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 -> [FilePath] -> Bool
hasOneOf (LuaFilter Map FilePath LuaFilterFunction
fnMap) = (FilePath -> Bool) -> [FilePath] -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
any (FilePath -> Map FilePath LuaFilterFunction -> Bool
forall k a. Ord k => k -> Map k a -> Bool
`Map.member` Map FilePath LuaFilterFunction
fnMap)

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

walkInlines :: Walkable (SingletonsList Inline) a => LuaFilter -> a -> Lua a
walkInlines :: 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 -> [FilePath] -> Bool
`hasOneOf` [FilePath]
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 :: LuaFilter -> a -> Lua a
walkInlineLists LuaFilter
lf =
  let f :: List Inline -> Lua (List Inline)
      f :: List Inline -> Lua (List Inline)
f = FilePath -> LuaFilter -> List Inline -> Lua (List Inline)
forall a.
(Data a, Peekable a, Pushable a) =>
FilePath -> LuaFilter -> a -> Lua a
runOnValue FilePath
listOfInlinesFilterName LuaFilter
lf
  in if LuaFilter
lf LuaFilter -> FilePath -> Bool
`contains` FilePath
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 :: 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 -> [FilePath] -> Bool
`hasOneOf` [FilePath]
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 :: LuaFilter -> a -> Lua a
walkBlockLists LuaFilter
lf =
  let f :: List Block -> Lua (List Block)
      f :: List Block -> Lua (List Block)
f = FilePath -> LuaFilter -> List Block -> Lua (List Block)
forall a.
(Data a, Peekable a, Pushable a) =>
FilePath -> LuaFilter -> a -> Lua a
runOnValue FilePath
listOfBlocksFilterName LuaFilter
lf
  in if LuaFilter
lf LuaFilter -> FilePath -> Bool
`contains` FilePath
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' <- FilePath -> LuaFilter -> Meta -> Lua Meta
forall a.
(Data a, Peekable a, Pushable a) =>
FilePath -> LuaFilter -> a -> Lua a
runOnValue FilePath
"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 FilePath 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 ((FilePath -> Maybe LuaFilterFunction)
-> [FilePath] -> [Maybe LuaFilterFunction]
forall a b. (a -> b) -> [a] -> [b]
map (FilePath
-> Map FilePath LuaFilterFunction -> Maybe LuaFilterFunction
forall k a. Ord k => k -> Map k a -> Maybe a
`Map.lookup` Map FilePath LuaFilterFunction
fnMap) [FilePath]
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 -> [FilePath]
constructorsFor DataType
x = (Constr -> FilePath) -> [Constr] -> [FilePath]
forall a b. (a -> b) -> [a] -> [b]
map Constr -> FilePath
forall a. Show a => a -> FilePath
show (DataType -> [Constr]
dataTypeConstrs DataType
x)

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

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

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

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

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

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

singleElement :: Peekable a => a -> Lua a
singleElement :: 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
        FilePath -> Lua a
forall a. FilePath -> Lua a
Lua.throwMessage
          (FilePath
"Error while trying to get a filter's return " FilePath -> FilePath -> FilePath
forall a. Semigroup a => a -> a -> a
<>
           FilePath
"value from Lua stack.\n" FilePath -> FilePath -> FilePath
forall a. Semigroup a => a -> a -> a
<> PandocError -> FilePath
forall a. Show a => a -> FilePath
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 :: 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