{-# LANGUAGE LambdaCase        #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RankNTypes        #-}
{- |
   Module      : Text.Pandoc.Lua.Run
   Copyright   : Copyright © 2017-2024 Albert Krewinkel
   License     : GPL-2.0-or-later
   Maintainer  : Albert Krewinkel <tarleb+pandoc@moltkeplatz.de>

Run code in the Lua interpreter.
-}
module Text.Pandoc.Lua.Run
  ( runLua
  , runLuaNoEnv
  , runLuaWith
  ) where

import Control.Monad.Catch (try)
import Control.Monad.Trans (MonadIO (..))
import HsLua as Lua hiding (try)
import Text.Pandoc.Class (PandocMonad (..))
import Text.Pandoc.Error (PandocError)
import Text.Pandoc.Lua.Global (Global (..), setGlobals)
import Text.Pandoc.Lua.Init (initLua)
import Text.Pandoc.Lua.PandocLua (PandocLua (..), liftPandocLua)

-- | Run the Lua interpreter, using pandoc's default way of environment
-- initialization.
runLua :: (PandocMonad m, MonadIO m)
       => LuaE PandocError a -> m (Either PandocError a)
runLua :: forall (m :: * -> *) a.
(PandocMonad m, MonadIO m) =>
LuaE PandocError a -> m (Either PandocError a)
runLua LuaE PandocError a
action = do
  (forall b. LuaE PandocError b -> IO b)
-> PandocLua (Either PandocError a) -> m (Either PandocError a)
forall (m :: * -> *) a.
(PandocMonad m, MonadIO m) =>
(forall b. LuaE PandocError b -> IO b) -> PandocLua a -> m a
runPandocLuaWith LuaE PandocError b -> IO b
forall b. LuaE PandocError b -> IO b
forall e a. LuaE e a -> IO a
Lua.run (PandocLua (Either PandocError a) -> m (Either PandocError a))
-> (PandocLua a -> PandocLua (Either PandocError a))
-> PandocLua a
-> m (Either PandocError a)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. PandocLua a -> PandocLua (Either PandocError a)
forall (m :: * -> *) e a.
(HasCallStack, MonadCatch m, Exception e) =>
m a -> m (Either e a)
try (PandocLua a -> m (Either PandocError a))
-> PandocLua a -> m (Either PandocError a)
forall a b. (a -> b) -> a -> b
$ do
    PandocLua ()
initLua
    LuaE PandocError a -> PandocLua a
forall a. LuaE PandocError a -> PandocLua a
liftPandocLua LuaE PandocError a
action

runLuaWith :: (PandocMonad m, MonadIO m)
           => GCManagedState -> LuaE PandocError a -> m (Either PandocError a)
runLuaWith :: forall (m :: * -> *) a.
(PandocMonad m, MonadIO m) =>
GCManagedState -> LuaE PandocError a -> m (Either PandocError a)
runLuaWith GCManagedState
luaState LuaE PandocError a
action = do
  (forall b. LuaE PandocError b -> IO b)
-> PandocLua (Either PandocError a) -> m (Either PandocError a)
forall (m :: * -> *) a.
(PandocMonad m, MonadIO m) =>
(forall b. LuaE PandocError b -> IO b) -> PandocLua a -> m a
runPandocLuaWith (GCManagedState -> LuaE PandocError b -> IO b
forall e a. GCManagedState -> LuaE e a -> IO a
withGCManagedState GCManagedState
luaState) (PandocLua (Either PandocError a) -> m (Either PandocError a))
-> (PandocLua a -> PandocLua (Either PandocError a))
-> PandocLua a
-> m (Either PandocError a)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. PandocLua a -> PandocLua (Either PandocError a)
forall (m :: * -> *) e a.
(HasCallStack, MonadCatch m, Exception e) =>
m a -> m (Either e a)
try (PandocLua a -> m (Either PandocError a))
-> PandocLua a -> m (Either PandocError a)
forall a b. (a -> b) -> a -> b
$ do
    PandocLua ()
initLua
    LuaE PandocError a -> PandocLua a
forall a. LuaE PandocError a -> PandocLua a
liftPandocLua LuaE PandocError a
action

-- | Like 'runLua', but ignores all environment variables like @LUA_PATH@.
runLuaNoEnv :: (PandocMonad m, MonadIO m)
            => LuaE PandocError a -> m (Either PandocError a)
runLuaNoEnv :: forall (m :: * -> *) a.
(PandocMonad m, MonadIO m) =>
LuaE PandocError a -> m (Either PandocError a)
runLuaNoEnv LuaE PandocError a
action = do
  (forall b. LuaE PandocError b -> IO b)
-> PandocLua (Either PandocError a) -> m (Either PandocError a)
forall (m :: * -> *) a.
(PandocMonad m, MonadIO m) =>
(forall b. LuaE PandocError b -> IO b) -> PandocLua a -> m a
runPandocLuaWith LuaE PandocError b -> IO b
forall b. LuaE PandocError b -> IO b
forall e a. LuaE e a -> IO a
Lua.run (PandocLua (Either PandocError a) -> m (Either PandocError a))
-> (PandocLua a -> PandocLua (Either PandocError a))
-> PandocLua a
-> m (Either PandocError a)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. PandocLua a -> PandocLua (Either PandocError a)
forall (m :: * -> *) e a.
(HasCallStack, MonadCatch m, Exception e) =>
m a -> m (Either e a)
try (PandocLua a -> m (Either PandocError a))
-> PandocLua a -> m (Either PandocError a)
forall a b. (a -> b) -> a -> b
$ do
    LuaE PandocError () -> PandocLua ()
forall a. LuaE PandocError a -> PandocLua a
liftPandocLua (LuaE PandocError () -> PandocLua ())
-> LuaE PandocError () -> PandocLua ()
forall a b. (a -> b) -> a -> b
$ do
      -- This is undocumented, but works -- the code is adapted from the
      -- `lua.c` sources for the default interpreter.
      Bool -> LuaE PandocError ()
forall e. Bool -> LuaE e ()
Lua.pushboolean Bool
True
      StackIndex -> Name -> LuaE PandocError ()
forall e. LuaError e => StackIndex -> Name -> LuaE e ()
Lua.setfield StackIndex
Lua.registryindex Name
"LUA_NOENV"
    PandocLua ()
initLua
    LuaE PandocError a -> PandocLua a
forall a. LuaE PandocError a -> PandocLua a
liftPandocLua LuaE PandocError a
action

-- | Evaluate a @'PandocLua'@ computation, running all contained Lua
-- operations.
runPandocLuaWith :: (PandocMonad m, MonadIO m)
                 => (forall b. LuaE PandocError b -> IO b)
                 -> PandocLua a
                 -> m a
runPandocLuaWith :: forall (m :: * -> *) a.
(PandocMonad m, MonadIO m) =>
(forall b. LuaE PandocError b -> IO b) -> PandocLua a -> m a
runPandocLuaWith forall b. LuaE PandocError b -> IO b
runner PandocLua a
pLua = do
  CommonState
origState <- m CommonState
forall (m :: * -> *). PandocMonad m => m CommonState
getCommonState
  [Global]
globals <- m [Global]
forall (m :: * -> *). PandocMonad m => m [Global]
defaultGlobals
  (a
result, CommonState
newState) <- IO (a, CommonState) -> m (a, CommonState)
forall a. IO a -> m a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO (a, CommonState) -> m (a, CommonState))
-> (PandocLua (a, CommonState) -> IO (a, CommonState))
-> PandocLua (a, CommonState)
-> m (a, CommonState)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. LuaE PandocError (a, CommonState) -> IO (a, CommonState)
forall b. LuaE PandocError b -> IO b
runner (LuaE PandocError (a, CommonState) -> IO (a, CommonState))
-> (PandocLua (a, CommonState)
    -> LuaE PandocError (a, CommonState))
-> PandocLua (a, CommonState)
-> IO (a, CommonState)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. PandocLua (a, CommonState) -> LuaE PandocError (a, CommonState)
forall a. PandocLua a -> LuaE PandocError a
unPandocLua (PandocLua (a, CommonState) -> m (a, CommonState))
-> PandocLua (a, CommonState) -> m (a, CommonState)
forall a b. (a -> b) -> a -> b
$ do
    CommonState -> PandocLua ()
forall (m :: * -> *). PandocMonad m => CommonState -> m ()
putCommonState CommonState
origState
    LuaE PandocError () -> PandocLua ()
forall a. LuaE PandocError a -> PandocLua a
liftPandocLua (LuaE PandocError () -> PandocLua ())
-> LuaE PandocError () -> PandocLua ()
forall a b. (a -> b) -> a -> b
$ [Global] -> LuaE PandocError ()
setGlobals [Global]
globals
    a
r <- PandocLua a
pLua
    CommonState
c <- PandocLua CommonState
forall (m :: * -> *). PandocMonad m => m CommonState
getCommonState
    (a, CommonState) -> PandocLua (a, CommonState)
forall a. a -> PandocLua a
forall (m :: * -> *) a. Monad m => a -> m a
return (a
r, CommonState
c)
  CommonState -> m ()
forall (m :: * -> *). PandocMonad m => CommonState -> m ()
putCommonState CommonState
newState
  a -> m a
forall a. a -> m a
forall (m :: * -> *) a. Monad m => a -> m a
return a
result

-- | Global variables which should always be set.
defaultGlobals :: PandocMonad m => m [Global]
defaultGlobals :: forall (m :: * -> *). PandocMonad m => m [Global]
defaultGlobals = do
  CommonState
commonState <- m CommonState
forall (m :: * -> *). PandocMonad m => m CommonState
getCommonState
  [Global] -> m [Global]
forall a. a -> m a
forall (m :: * -> *) a. Monad m => a -> m a
return
    [ Global
PANDOC_API_VERSION
    , CommonState -> Global
PANDOC_STATE CommonState
commonState
    , Global
PANDOC_VERSION
    ]