{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE OverloadedStrings #-}
{- |
Module      : Text.Pandoc.Lua.Filter
Copyright   : © 2012-2024 John MacFarlane,
              © 2017-2024 Albert Krewinkel
License     : GPL-2.0-or-later
Maintainer  : Albert Krewinkel <albert+pandoc@tarleb.com>

Types and functions for running Lua filters.
-}
module Text.Pandoc.Lua.Filter
  ( runFilterFile
  , runFilterFile'
  ) where
import Control.Monad ((>=>), (<$!>))
import HsLua as Lua
import Text.Pandoc.Definition (Pandoc)
import Text.Pandoc.Error (PandocError)
import Text.Pandoc.Lua.Marshal.AST
import Text.Pandoc.Lua.Marshal.Filter
import Text.Pandoc.Lua.PandocLua ()

-- | Transform document using the filter defined in the given file.
-- Runs the filter in the global environment.
runFilterFile :: FilePath -> Pandoc -> LuaE PandocError Pandoc
runFilterFile :: FilePath -> Pandoc -> LuaE PandocError Pandoc
runFilterFile FilePath
filterPath Pandoc
doc = do
  LuaE PandocError ()
forall e. LuaE e ()
Lua.pushglobaltable
  StackIndex -> FilePath -> Pandoc -> LuaE PandocError Pandoc
runFilterFile' StackIndex
Lua.top FilePath
filterPath Pandoc
doc LuaE PandocError Pandoc
-> LuaE PandocError () -> LuaE PandocError Pandoc
forall a b.
LuaE PandocError a -> LuaE PandocError b -> LuaE PandocError a
forall (f :: * -> *) a b. Applicative f => f a -> f b -> f a
<* Int -> LuaE PandocError ()
forall e. Int -> LuaE e ()
Lua.pop Int
1

-- | Like 'runFilterFile', but uses the table at the given index as the
-- environment in which the filter is run.
runFilterFile' :: StackIndex -> FilePath -> Pandoc
               -> LuaE PandocError Pandoc
runFilterFile' :: StackIndex -> FilePath -> Pandoc -> LuaE PandocError Pandoc
runFilterFile' StackIndex
envIdx FilePath
filterPath Pandoc
doc = do
  oldtop <- LuaE PandocError StackIndex
forall e. LuaE e StackIndex
gettop
  stat <- dofileTrace' envIdx (Just filterPath)
  if stat /= OK
    then throwErrorAsException
    else do
      newtop <- gettop
      -- Use the returned filters, or the implicitly defined global
      -- filter if nothing was returned.
      luaFilters <- forcePeek $
        if newtop - oldtop >= 1
        then liftLua (rawlen top) >>= \case
          -- explicitly returned filter, either a single one or a list
          Int
0 -> (Filter -> [Filter] -> [Filter]
forall a. a -> [a] -> [a]
:[]) (Filter -> [Filter])
-> Peek PandocError Filter -> Peek PandocError [Filter]
forall (m :: * -> *) a b. Monad m => (a -> b) -> m a -> m b
<$!> Peeker PandocError Filter
forall e. LuaError e => Peeker e Filter
peekFilter StackIndex
top  -- single filter
          Int
_ -> Peeker PandocError Filter -> Peeker PandocError [Filter]
forall a e. LuaError e => Peeker e a -> Peeker e [a]
peekList Peeker PandocError Filter
forall e. LuaError e => Peeker e Filter
peekFilter StackIndex
top    -- list of explicit filters
        else (:[]) <$!> peekFilter envIdx -- get the implicit filter in _ENV
      settop oldtop
      runAll luaFilters doc

-- | Apply Lua filters to a document
runAll :: [Filter] -> Pandoc -> LuaE PandocError Pandoc
runAll :: [Filter] -> Pandoc -> LuaE PandocError Pandoc
runAll = (Filter
 -> (Pandoc -> LuaE PandocError Pandoc)
 -> Pandoc
 -> LuaE PandocError Pandoc)
-> (Pandoc -> LuaE PandocError Pandoc)
-> [Filter]
-> Pandoc
-> LuaE PandocError Pandoc
forall a b. (a -> b -> b) -> b -> [a] -> b
forall (t :: * -> *) a b.
Foldable t =>
(a -> b -> b) -> b -> t a -> b
foldr ((Pandoc -> LuaE PandocError Pandoc)
-> (Pandoc -> LuaE PandocError Pandoc)
-> Pandoc
-> LuaE PandocError Pandoc
forall (m :: * -> *) a b c.
Monad m =>
(a -> m b) -> (b -> m c) -> a -> m c
(>=>) ((Pandoc -> LuaE PandocError Pandoc)
 -> (Pandoc -> LuaE PandocError Pandoc)
 -> Pandoc
 -> LuaE PandocError Pandoc)
-> (Filter -> Pandoc -> LuaE PandocError Pandoc)
-> Filter
-> (Pandoc -> LuaE PandocError Pandoc)
-> Pandoc
-> LuaE PandocError Pandoc
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Filter -> Pandoc -> LuaE PandocError Pandoc
forall e. LuaError e => Filter -> Pandoc -> LuaE e Pandoc
applyFully) Pandoc -> LuaE PandocError Pandoc
forall a. a -> LuaE PandocError a
forall (m :: * -> *) a. Monad m => a -> m a
return

-- | Like 'HsLua.Core.Trace.dofileTrace', but uses a local environment.
dofileTrace' :: LuaError e
             => StackIndex     -- ^ stack index of the environment table
             -> Maybe FilePath -- ^ file to load (or @Nothing@ for stdin)
             -> LuaE e Status
dofileTrace' :: forall e.
LuaError e =>
StackIndex -> Maybe FilePath -> LuaE e Status
dofileTrace' StackIndex
envIdx Maybe FilePath
fp = do
  absEnv <- StackIndex -> LuaE e StackIndex
forall e. StackIndex -> LuaE e StackIndex
Lua.absindex StackIndex
envIdx
  loadfile fp >>= \case
    Status
OK -> do
      StackIndex -> LuaE e ()
forall e. StackIndex -> LuaE e ()
Lua.pushvalue StackIndex
absEnv
      Just (Name "_ENV") <- StackIndex -> Int -> LuaE e (Maybe Name)
forall e. StackIndex -> Int -> LuaE e (Maybe Name)
Lua.setupvalue (CInt -> StackIndex
Lua.nth CInt
2) Int
1
      pcallTrace 0 multret
    Status
s  -> Status -> LuaE e Status
forall a. a -> LuaE e a
forall (f :: * -> *) a. Applicative f => a -> f a
pure Status
s