Use hslua v1.0.0

This commit is contained in:
Albert Krewinkel 2018-09-24 20:11:00 +02:00
parent 0272e63527
commit 56fe5b559e
No known key found for this signature in database
GPG key ID: 388DC0B21F631124
14 changed files with 260 additions and 312 deletions

View file

@ -372,8 +372,8 @@ library
blaze-html >= 0.9 && < 0.10, blaze-html >= 0.9 && < 0.10,
blaze-markup >= 0.8 && < 0.9, blaze-markup >= 0.8 && < 0.9,
vector >= 0.10 && < 0.13, vector >= 0.10 && < 0.13,
hslua >= 0.9.5 && < 0.9.6, hslua >= 1.0 && < 1.1,
hslua-module-text >= 0.1.2 && < 0.2, hslua-module-text >= 0.2 && < 0.3,
binary >= 0.5 && < 0.10, binary >= 0.5 && < 0.10,
SHA >= 1.6 && < 1.7, SHA >= 1.6 && < 1.7,
haddock-library >= 1.6 && < 1.7, haddock-library >= 1.6 && < 1.7,
@ -615,7 +615,7 @@ test-suite test-pandoc
time >= 1.5 && < 1.10, time >= 1.5 && < 1.10,
directory >= 1 && < 1.4, directory >= 1 && < 1.4,
filepath >= 1.1 && < 1.5, filepath >= 1.1 && < 1.5,
hslua >= 0.9.5 && < 0.9.6, hslua >= 1.0 && < 1.1,
process >= 1.2.3 && < 1.7, process >= 1.2.3 && < 1.7,
temporary >= 1.1 && < 1.4, temporary >= 1.1 && < 1.4,
Diff >= 0.2 && < 0.4, Diff >= 0.2 && < 0.4,

View file

@ -1,4 +1,3 @@
{-# LANGUAGE NoImplicitPrelude #-}
{- {-
Copyright © 20172018 Albert Krewinkel <tarleb+pandoc@moltkeplatz.de> Copyright © 20172018 Albert Krewinkel <tarleb+pandoc@moltkeplatz.de>
@ -16,6 +15,7 @@ You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-} -}
{-# LANGUAGE NoImplicitPrelude #-}
{- | {- |
Module : Text.Pandoc.Lua Module : Text.Pandoc.Lua
Copyright : Copyright © 20172018 Albert Krewinkel Copyright : Copyright © 20172018 Albert Krewinkel
@ -34,12 +34,11 @@ module Text.Pandoc.Lua
import Prelude import Prelude
import Control.Monad ((>=>)) import Control.Monad ((>=>))
import Foreign.Lua (Lua, LuaException (..)) import Foreign.Lua (Lua)
import Text.Pandoc.Class (PandocIO) import Text.Pandoc.Class (PandocIO)
import Text.Pandoc.Definition (Pandoc) import Text.Pandoc.Definition (Pandoc)
import Text.Pandoc.Lua.Filter (LuaFilter, walkMWithLuaFilter) import Text.Pandoc.Lua.Filter (LuaFilter, walkMWithLuaFilter)
import Text.Pandoc.Lua.Init (runPandocLua, registerScriptPath) import Text.Pandoc.Lua.Init (LuaException (..), runPandocLua, registerScriptPath)
import Text.Pandoc.Lua.Util (popValue)
import Text.Pandoc.Options (ReaderOptions) import Text.Pandoc.Options (ReaderOptions)
import qualified Foreign.Lua as Lua import qualified Foreign.Lua as Lua
@ -61,14 +60,14 @@ runLuaFilter' ropts filterPath format pd = do
top <- Lua.gettop top <- Lua.gettop
stat <- Lua.dofile filterPath stat <- Lua.dofile filterPath
if stat /= Lua.OK if stat /= Lua.OK
then Lua.throwTopMessageAsError then Lua.throwTopMessage
else do else do
newtop <- Lua.gettop newtop <- Lua.gettop
-- Use the returned filters, or the implicitly defined global filter if -- Use the returned filters, or the implicitly defined global filter if
-- nothing was returned. -- nothing was returned.
luaFilters <- if newtop - top >= 1 luaFilters <- if newtop - top >= 1
then Lua.peek Lua.stackTop then Lua.peek Lua.stackTop
else Lua.getglobal "_G" *> fmap (:[]) popValue else Lua.pushglobaltable *> fmap (:[]) Lua.popValue
runAll luaFilters pd runAll luaFilters pd
where where
registerFormat = do registerFormat = do

View file

@ -45,23 +45,22 @@ import Data.Data (Data, DataType, dataTypeConstrs, dataTypeName, dataTypeOf,
showConstr, toConstr, tyconUQname) showConstr, toConstr, tyconUQname)
import Data.Foldable (foldrM) import Data.Foldable (foldrM)
import Data.Map (Map) import Data.Map (Map)
import Foreign.Lua (Lua, FromLuaStack, ToLuaStack) import Foreign.Lua (Lua, Peekable, Pushable)
import Text.Pandoc.Definition import Text.Pandoc.Definition
import Text.Pandoc.Lua.StackInstances () import Text.Pandoc.Lua.StackInstances ()
import Text.Pandoc.Lua.Util (typeCheck)
import Text.Pandoc.Walk (walkM, Walkable) import Text.Pandoc.Walk (walkM, Walkable)
import qualified Data.Map.Strict as Map import qualified Data.Map.Strict as Map
import qualified Foreign.Lua as Lua import qualified Foreign.Lua as Lua
-- | Filter function stored at the given index in the registry -- | Filter function stored in the registry
newtype LuaFilterFunction = LuaFilterFunction Int newtype LuaFilterFunction = LuaFilterFunction Lua.Reference
-- | Collection of filter functions (at most one function per element -- | Collection of filter functions (at most one function per element
-- constructor) -- constructor)
newtype LuaFilter = LuaFilter (Map String LuaFilterFunction) newtype LuaFilter = LuaFilter (Map String LuaFilterFunction)
instance FromLuaStack LuaFilter where instance Peekable LuaFilter where
peek idx = do peek idx = do
let constrs = metaFilterName let constrs = metaFilterName
: pandocFilterNames : pandocFilterNames
@ -87,10 +86,10 @@ registerFilterFunction = do
-- | Retrieve filter function from registry and push it to the top of the stack. -- | Retrieve filter function from registry and push it to the top of the stack.
pushFilterFunction :: LuaFilterFunction -> Lua () pushFilterFunction :: LuaFilterFunction -> Lua ()
pushFilterFunction (LuaFilterFunction fnRef) = pushFilterFunction (LuaFilterFunction fnRef) =
Lua.rawgeti Lua.registryindex fnRef Lua.getref Lua.registryindex fnRef
elementOrList :: FromLuaStack a => a -> Lua [a] elementOrList :: Peekable a => a -> Lua [a]
elementOrList x = do elementOrList x = do
let topOfStack = Lua.stackTop let topOfStack = Lua.stackTop
elementUnchanged <- Lua.isnil topOfStack elementUnchanged <- Lua.isnil topOfStack
@ -100,12 +99,10 @@ elementOrList x = do
mbres <- Lua.peekEither topOfStack mbres <- Lua.peekEither topOfStack
case mbres of case mbres of
Right res -> [res] <$ Lua.pop 1 Right res -> [res] <$ Lua.pop 1
Left _ -> do Left _ -> Lua.peekList topOfStack `finally` Lua.pop 1
typeCheck Lua.stackTop Lua.TypeTable
Lua.toList topOfStack `finally` Lua.pop 1
-- | Try running a filter for the given element -- | Try running a filter for the given element
tryFilter :: (Data a, FromLuaStack a, ToLuaStack a) tryFilter :: (Data a, Peekable a, Pushable a)
=> LuaFilter -> a -> Lua [a] => LuaFilter -> a -> Lua [a]
tryFilter (LuaFilter fnMap) x = tryFilter (LuaFilter fnMap) x =
let filterFnName = showConstr (toConstr x) let filterFnName = showConstr (toConstr x)
@ -119,10 +116,10 @@ tryFilter (LuaFilter fnMap) x =
-- called with given element as argument and is expected to return an element. -- 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 -- Alternatively, the function can return nothing or nil, in which case the
-- element is left unchanged. -- element is left unchanged.
runFilterFunction :: ToLuaStack a => LuaFilterFunction -> a -> Lua () runFilterFunction :: Pushable a => LuaFilterFunction -> a -> Lua ()
runFilterFunction lf x = do runFilterFunction lf x = do
let errorPrefix = "Error while running filter function:\n" let errorPrefix = "Error while running filter function:\n"
(`Lua.modifyLuaError` (errorPrefix <>)) $ do Lua.withExceptionMessage (errorPrefix <>) $ do
pushFilterFunction lf pushFilterFunction lf
Lua.push x Lua.push x
Lua.call 1 1 Lua.call 1 1
@ -178,7 +175,7 @@ metaFilterName = "Meta"
pandocFilterNames :: [String] pandocFilterNames :: [String]
pandocFilterNames = ["Pandoc", "Doc"] pandocFilterNames = ["Pandoc", "Doc"]
singleElement :: FromLuaStack a => a -> Lua a singleElement :: Peekable a => a -> Lua a
singleElement x = do singleElement x = do
elementUnchanged <- Lua.isnil (-1) elementUnchanged <- Lua.isnil (-1)
if elementUnchanged if elementUnchanged
@ -189,6 +186,6 @@ singleElement x = do
Right res -> res <$ Lua.pop 1 Right res -> res <$ Lua.pop 1
Left err -> do Left err -> do
Lua.pop 1 Lua.pop 1
Lua.throwLuaError $ Lua.throwException $
"Error while trying to get a filter's return " ++ "Error while trying to get a filter's return " ++
"value from lua stack.\n" ++ err "value from lua stack.\n" ++ err

View file

@ -1,4 +1,3 @@
{-# LANGUAGE NoImplicitPrelude #-}
{- {-
Copyright © 2017-2018 Albert Krewinkel <tarleb+pandoc@moltkeplatz.de> Copyright © 2017-2018 Albert Krewinkel <tarleb+pandoc@moltkeplatz.de>
@ -16,6 +15,7 @@ You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-} -}
{-# LANGUAGE NoImplicitPrelude #-}
{- | {- |
Module : Text.Pandoc.Lua Module : Text.Pandoc.Lua
Copyright : Copyright © 2017-2018 Albert Krewinkel Copyright : Copyright © 2017-2018 Albert Krewinkel
@ -40,7 +40,7 @@ import Control.Monad.Trans (MonadIO (..))
import Data.Data (Data, dataTypeConstrs, dataTypeOf, showConstr) import Data.Data (Data, dataTypeConstrs, dataTypeOf, showConstr)
import Data.IORef (newIORef, readIORef) import Data.IORef (newIORef, readIORef)
import Data.Version (Version (versionBranch)) import Data.Version (Version (versionBranch))
import Foreign.Lua (Lua, LuaException (..)) import Foreign.Lua (Lua)
import GHC.IO.Encoding (getForeignEncoding, setForeignEncoding, utf8) import GHC.IO.Encoding (getForeignEncoding, setForeignEncoding, utf8)
import Paths_pandoc (version) import Paths_pandoc (version)
import Text.Pandoc.Class (PandocIO, getCommonState, getUserDataDir, getMediaBag, import Text.Pandoc.Class (PandocIO, getCommonState, getUserDataDir, getMediaBag,
@ -54,17 +54,22 @@ import qualified Foreign.Lua as Lua
import qualified Foreign.Lua.Module.Text as Lua import qualified Foreign.Lua.Module.Text as Lua
import qualified Text.Pandoc.Definition as Pandoc import qualified Text.Pandoc.Definition as Pandoc
-- | Lua error message
newtype LuaException = LuaException String deriving (Show)
-- | Run the lua interpreter, using pandoc's default way of environment -- | Run the lua interpreter, using pandoc's default way of environment
-- initialization. -- initialization.
runPandocLua :: Lua a -> PandocIO (Either LuaException a) runPandocLua :: Lua a -> PandocIO (Either LuaException a)
runPandocLua luaOp = do runPandocLua luaOp = do
luaPkgParams <- luaPackageParams luaPkgParams <- luaPackageParams
enc <- liftIO $ getForeignEncoding <* setForeignEncoding utf8 enc <- liftIO $ getForeignEncoding <* setForeignEncoding utf8
res <- liftIO $ Lua.runLuaEither (initLuaState luaPkgParams *> luaOp) res <- liftIO $ Lua.runEither (initLuaState luaPkgParams *> luaOp)
liftIO $ setForeignEncoding enc liftIO $ setForeignEncoding enc
newMediaBag <- liftIO (readIORef (luaPkgMediaBag luaPkgParams)) newMediaBag <- liftIO (readIORef (luaPkgMediaBag luaPkgParams))
setMediaBag newMediaBag setMediaBag newMediaBag
return res return $ case res of
Left (Lua.Exception msg) -> Left (LuaException msg)
Right x -> Right x
-- | Generate parameters required to setup pandoc's lua environment. -- | Generate parameters required to setup pandoc's lua environment.
luaPackageParams :: PandocIO LuaPackageParams luaPackageParams :: PandocIO LuaPackageParams

View file

@ -87,7 +87,7 @@ mediaDirectoryFn mbRef = do
zipWithM_ addEntry [1..] dirContents zipWithM_ addEntry [1..] dirContents
return 1 return 1
where where
addEntry :: Int -> (FilePath, MimeType, Int) -> Lua () addEntry :: Lua.Integer -> (FilePath, MimeType, Int) -> Lua ()
addEntry idx (fp, mimeType, contentLength) = do addEntry idx (fp, mimeType, contentLength) = do
Lua.newtable Lua.newtable
Lua.push "path" *> Lua.push fp *> Lua.rawset (-3) Lua.push "path" *> Lua.push fp *> Lua.rawset (-3)

View file

@ -1,4 +1,3 @@
{-# LANGUAGE NoImplicitPrelude #-}
{- {-
Copyright © 2017-2018 Albert Krewinkel <tarleb+pandoc@moltkeplatz.de> Copyright © 2017-2018 Albert Krewinkel <tarleb+pandoc@moltkeplatz.de>
@ -17,6 +16,7 @@ along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-} -}
{-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE NoImplicitPrelude #-}
{- | {- |
Module : Text.Pandoc.Lua.Module.Pandoc Module : Text.Pandoc.Lua.Module.Pandoc
Copyright : Copyright © 2017-2018 Albert Krewinkel Copyright : Copyright © 2017-2018 Albert Krewinkel
@ -36,13 +36,12 @@ import Control.Monad (when)
import Data.Default (Default (..)) import Data.Default (Default (..))
import Data.Maybe (fromMaybe) import Data.Maybe (fromMaybe)
import Data.Text (pack) import Data.Text (pack)
import Foreign.Lua (ToLuaStack, FromLuaStack, Lua, NumResults, Optional, liftIO) import Foreign.Lua (Lua, NumResults, Optional, Peekable, Pushable)
import System.Exit (ExitCode (..)) import System.Exit (ExitCode (..))
import Text.Pandoc.Class (runIO) import Text.Pandoc.Class (runIO)
import Text.Pandoc.Definition (Block, Inline) import Text.Pandoc.Definition (Block, Inline)
import Text.Pandoc.Lua.Filter (walkInlines, walkBlocks, LuaFilter) import Text.Pandoc.Lua.Filter (walkInlines, walkBlocks, LuaFilter)
import Text.Pandoc.Lua.StackInstances () import Text.Pandoc.Lua.StackInstances ()
import Text.Pandoc.Lua.Util (addFunction, loadScriptFromDataDir)
import Text.Pandoc.Walk (Walkable) import Text.Pandoc.Walk (Walkable)
import Text.Pandoc.Options (ReaderOptions (readerExtensions)) import Text.Pandoc.Options (ReaderOptions (readerExtensions))
import Text.Pandoc.Process (pipeProcess) import Text.Pandoc.Process (pipeProcess)
@ -57,14 +56,14 @@ import qualified Text.Pandoc.Lua.Util as LuaUtil
-- loaded. -- loaded.
pushModule :: Maybe FilePath -> Lua NumResults pushModule :: Maybe FilePath -> Lua NumResults
pushModule datadir = do pushModule datadir = do
loadScriptFromDataDir datadir "pandoc.lua" LuaUtil.loadScriptFromDataDir datadir "pandoc.lua"
addFunction "read" readDoc LuaUtil.addFunction "read" readDoc
addFunction "pipe" pipeFn LuaUtil.addFunction "pipe" pipeFn
addFunction "walk_block" walkBlock LuaUtil.addFunction "walk_block" walkBlock
addFunction "walk_inline" walkInline LuaUtil.addFunction "walk_inline" walkInline
return 1 return 1
walkElement :: (ToLuaStack a, Walkable [Inline] a, Walkable [Block] a) walkElement :: (Pushable a, Walkable [Inline] a, Walkable [Block] a)
=> a -> LuaFilter -> Lua a => a -> LuaFilter -> Lua a
walkElement x f = walkInlines f x >>= walkBlocks f walkElement x f = walkInlines f x >>= walkBlocks f
@ -82,7 +81,8 @@ readDoc content formatSpecOrNil = do
Right (reader, es) -> Right (reader, es) ->
case reader of case reader of
TextReader r -> do TextReader r -> do
res <- liftIO $ runIO $ r def{ readerExtensions = es } (pack content) res <- Lua.liftIO . runIO $
r def{ readerExtensions = es } (pack content)
case res of case res of
Right pd -> (1 :: NumResults) <$ Lua.push pd -- success, push Pandoc Right pd -> (1 :: NumResults) <$ Lua.push pd -- success, push Pandoc
Left s -> Lua.raiseError (show s) -- error while reading Left s -> Lua.raiseError (show s) -- error while reading
@ -94,7 +94,7 @@ pipeFn :: String
-> BL.ByteString -> BL.ByteString
-> Lua NumResults -> Lua NumResults
pipeFn command args input = do pipeFn command args input = do
(ec, output) <- liftIO $ pipeProcess Nothing command args input (ec, output) <- Lua.liftIO $ pipeProcess Nothing command args input
case ec of case ec of
ExitSuccess -> 1 <$ Lua.push output ExitSuccess -> 1 <$ Lua.push output
ExitFailure n -> Lua.raiseError (PipeError command n output) ExitFailure n -> Lua.raiseError (PipeError command n output)
@ -105,14 +105,14 @@ data PipeError = PipeError
, pipeErrorOutput :: BL.ByteString , pipeErrorOutput :: BL.ByteString
} }
instance FromLuaStack PipeError where instance Peekable PipeError where
peek idx = peek idx =
PipeError PipeError
<$> (Lua.getfield idx "command" *> Lua.peek (-1) <* Lua.pop 1) <$> (Lua.getfield idx "command" *> Lua.peek (-1) <* Lua.pop 1)
<*> (Lua.getfield idx "error_code" *> Lua.peek (-1) <* Lua.pop 1) <*> (Lua.getfield idx "error_code" *> Lua.peek (-1) <* Lua.pop 1)
<*> (Lua.getfield idx "output" *> Lua.peek (-1) <* Lua.pop 1) <*> (Lua.getfield idx "output" *> Lua.peek (-1) <* Lua.pop 1)
instance ToLuaStack PipeError where instance Pushable PipeError where
push pipeErr = do push pipeErr = do
Lua.newtable Lua.newtable
LuaUtil.addField "command" (pipeErrorCommand pipeErr) LuaUtil.addField "command" (pipeErrorCommand pipeErr)
@ -124,7 +124,7 @@ instance ToLuaStack PipeError where
pushPipeErrorMetaTable :: Lua () pushPipeErrorMetaTable :: Lua ()
pushPipeErrorMetaTable = do pushPipeErrorMetaTable = do
v <- Lua.newmetatable "pandoc pipe error" v <- Lua.newmetatable "pandoc pipe error"
when v $ addFunction "__tostring" pipeErrorMessage when v $ LuaUtil.addFunction "__tostring" pipeErrorMessage
pipeErrorMessage :: PipeError -> Lua BL.ByteString pipeErrorMessage :: PipeError -> Lua BL.ByteString
pipeErrorMessage (PipeError cmd errorCode output) = return $ mconcat pipeErrorMessage (PipeError cmd errorCode output) = return $ mconcat

View file

@ -1,4 +1,3 @@
{-# LANGUAGE NoImplicitPrelude #-}
{- {-
Copyright © 2017-2018 Albert Krewinkel <tarleb+pandoc@moltkeplatz.de> Copyright © 2017-2018 Albert Krewinkel <tarleb+pandoc@moltkeplatz.de>
@ -16,6 +15,7 @@ You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-} -}
{-# LANGUAGE NoImplicitPrelude #-}
{- | {- |
Module : Text.Pandoc.Lua.Module.Utils Module : Text.Pandoc.Lua.Module.Utils
Copyright : Copyright © 2017-2018 Albert Krewinkel Copyright : Copyright © 2017-2018 Albert Krewinkel
@ -33,11 +33,11 @@ module Text.Pandoc.Lua.Module.Utils
import Prelude import Prelude
import Control.Applicative ((<|>)) import Control.Applicative ((<|>))
import Data.Default (def) import Data.Default (def)
import Foreign.Lua (FromLuaStack, Lua, LuaInteger, NumResults) import Foreign.Lua (Peekable, Lua, NumResults)
import Text.Pandoc.Class (runIO, setUserDataDir) import Text.Pandoc.Class (runIO, setUserDataDir)
import Text.Pandoc.Definition (Pandoc, Meta, MetaValue, Block, Inline) import Text.Pandoc.Definition (Pandoc, Meta, MetaValue, Block, Inline)
import Text.Pandoc.Lua.StackInstances () import Text.Pandoc.Lua.StackInstances ()
import Text.Pandoc.Lua.Util (addFunction, popValue) import Text.Pandoc.Lua.Util (addFunction)
import qualified Data.Digest.Pure.SHA as SHA import qualified Data.Digest.Pure.SHA as SHA
import qualified Data.ByteString.Lazy as BSL import qualified Data.ByteString.Lazy as BSL
@ -89,7 +89,7 @@ runJSONFilter mbDatadir doc filterFile optArgs = do
Just x -> return x Just x -> return x
Nothing -> do Nothing -> do
Lua.getglobal "FORMAT" Lua.getglobal "FORMAT"
(:[]) <$> popValue (:[]) <$> Lua.popValue
filterRes <- Lua.liftIO . runIO $ do filterRes <- Lua.liftIO . runIO $ do
setUserDataDir mbDatadir setUserDataDir mbDatadir
JSONFilter.apply def args filterFile doc JSONFilter.apply def args filterFile doc
@ -121,18 +121,18 @@ data AstElement
| MetaValueElement MetaValue | MetaValueElement MetaValue
deriving (Show) deriving (Show)
instance FromLuaStack AstElement where instance Peekable AstElement where
peek idx = do peek idx = do
res <- Lua.tryLua $ (PandocElement <$> Lua.peek idx) res <- Lua.try $ (PandocElement <$> Lua.peek idx)
<|> (InlineElement <$> Lua.peek idx) <|> (InlineElement <$> Lua.peek idx)
<|> (BlockElement <$> Lua.peek idx) <|> (BlockElement <$> Lua.peek idx)
<|> (MetaElement <$> Lua.peek idx) <|> (MetaElement <$> Lua.peek idx)
<|> (MetaValueElement <$> Lua.peek idx) <|> (MetaValueElement <$> Lua.peek idx)
case res of case res of
Right x -> return x Right x -> return x
Left _ -> Lua.throwLuaError Left _ -> Lua.throwException
"Expected an AST element, but could not parse value as such." "Expected an AST element, but could not parse value as such."
-- | Convert a number < 4000 to uppercase roman numeral. -- | Convert a number < 4000 to uppercase roman numeral.
toRomanNumeral :: LuaInteger -> Lua String toRomanNumeral :: Lua.Integer -> Lua String
toRomanNumeral = return . Shared.toRomanNumeral . fromIntegral toRomanNumeral = return . Shared.toRomanNumeral . fromIntegral

View file

@ -1,4 +1,3 @@
{-# LANGUAGE NoImplicitPrelude #-}
{- {-
Copyright © 2017-2018 Albert Krewinkel <tarleb+pandoc@moltkeplatz.de> Copyright © 2017-2018 Albert Krewinkel <tarleb+pandoc@moltkeplatz.de>
@ -16,8 +15,9 @@ You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-} -}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE NoImplicitPrelude #-}
{-# LANGUAGE ScopedTypeVariables #-}
{- | {- |
Module : Text.Pandoc.Lua.Packages Module : Text.Pandoc.Lua.Packages
Copyright : Copyright © 2017-2018 Albert Krewinkel Copyright : Copyright © 2017-2018 Albert Krewinkel
@ -35,12 +35,11 @@ module Text.Pandoc.Lua.Packages
import Prelude import Prelude
import Control.Monad (forM_) import Control.Monad (forM_)
import Data.ByteString.Char8 (unpack) import Data.ByteString (ByteString)
import Data.IORef (IORef) import Data.IORef (IORef)
import Foreign.Lua (Lua, NumResults, liftIO) import Foreign.Lua (Lua, NumResults, liftIO)
import Text.Pandoc.Class (CommonState, readDataFile, runIO, setUserDataDir) import Text.Pandoc.Class (CommonState, readDataFile, runIO, setUserDataDir)
import Text.Pandoc.MediaBag (MediaBag) import Text.Pandoc.MediaBag (MediaBag)
import Text.Pandoc.Lua.Util (dostring')
import qualified Foreign.Lua as Lua import qualified Foreign.Lua as Lua
import Text.Pandoc.Lua.Module.Pandoc as Pandoc import Text.Pandoc.Lua.Module.Pandoc as Pandoc
@ -57,14 +56,10 @@ data LuaPackageParams = LuaPackageParams
-- | Insert pandoc's package loader as the first loader, making it the default. -- | Insert pandoc's package loader as the first loader, making it the default.
installPandocPackageSearcher :: LuaPackageParams -> Lua () installPandocPackageSearcher :: LuaPackageParams -> Lua ()
installPandocPackageSearcher luaPkgParams = do installPandocPackageSearcher luaPkgParams = do
luaVersion <- Lua.getglobal "_VERSION" *> Lua.peek (-1) Lua.getglobal' "package.searchers"
if luaVersion == "Lua 5.1"
then Lua.getglobal' "package.loaders"
else Lua.getglobal' "package.searchers"
shiftArray shiftArray
Lua.pushHaskellFunction (pandocPackageSearcher luaPkgParams) Lua.pushHaskellFunction (pandocPackageSearcher luaPkgParams)
Lua.wrapHaskellFunction Lua.rawseti (Lua.nthFromTop 2) 1
Lua.rawseti (-2) 1
Lua.pop 1 -- remove 'package.searchers' from stack Lua.pop 1 -- remove 'package.searchers' from stack
where where
shiftArray = forM_ [4, 3, 2, 1] $ \i -> do shiftArray = forM_ [4, 3, 2, 1] $ \i -> do
@ -86,7 +81,6 @@ pandocPackageSearcher luaPkgParams pkgName =
where where
pushWrappedHsFun f = do pushWrappedHsFun f = do
Lua.pushHaskellFunction f Lua.pushHaskellFunction f
Lua.wrapHaskellFunction
return 1 return 1
searchPureLuaLoader = do searchPureLuaLoader = do
let filename = pkgName ++ ".lua" let filename = pkgName ++ ".lua"
@ -97,21 +91,19 @@ pandocPackageSearcher luaPkgParams pkgName =
Lua.push ("no file '" ++ filename ++ "' in pandoc's datadir") Lua.push ("no file '" ++ filename ++ "' in pandoc's datadir")
return 1 return 1
loadStringAsPackage :: String -> String -> Lua NumResults loadStringAsPackage :: String -> ByteString -> Lua NumResults
loadStringAsPackage pkgName script = do loadStringAsPackage pkgName script = do
status <- dostring' script status <- Lua.dostring script
if status == Lua.OK if status == Lua.OK
then return (1 :: NumResults) then return (1 :: NumResults)
else do else do
msg <- Lua.peek (-1) <* Lua.pop 1 msg <- Lua.popValue
Lua.push ("Error while loading ``" ++ pkgName ++ "`.\n" ++ msg) Lua.raiseError ("Error while loading `" <> pkgName <> "`.\n" <> msg)
Lua.lerror
return (2 :: NumResults)
-- | Get the string representation of the pandoc module -- | Get the ByteString representation of the pandoc module.
dataDirScript :: Maybe FilePath -> FilePath -> IO (Maybe String) dataDirScript :: Maybe FilePath -> FilePath -> IO (Maybe ByteString)
dataDirScript datadir moduleFile = do dataDirScript datadir moduleFile = do
res <- runIO $ setUserDataDir datadir >> readDataFile moduleFile res <- runIO $ setUserDataDir datadir >> readDataFile moduleFile
return $ case res of return $ case res of
Left _ -> Nothing Left _ -> Nothing
Right s -> Just (unpack s) Right s -> Just s

View file

@ -1,4 +1,3 @@
{-# LANGUAGE NoImplicitPrelude #-}
{- {-
Copyright © 2012-2018 John MacFarlane <jgm@berkeley.edu> Copyright © 2012-2018 John MacFarlane <jgm@berkeley.edu>
2017-2018 Albert Krewinkel <tarleb+pandoc@moltkeplatz.de> 2017-2018 Albert Krewinkel <tarleb+pandoc@moltkeplatz.de>
@ -19,6 +18,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-} -}
{-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE LambdaCase #-} {-# LANGUAGE LambdaCase #-}
{-# LANGUAGE NoImplicitPrelude #-}
{-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE ScopedTypeVariables #-}
{-# OPTIONS_GHC -fno-warn-orphans #-} {-# OPTIONS_GHC -fno-warn-orphans #-}
{- | {- |
@ -37,67 +37,59 @@ module Text.Pandoc.Lua.StackInstances () where
import Prelude import Prelude
import Control.Applicative ((<|>)) import Control.Applicative ((<|>))
import Control.Monad (when) import Control.Monad (when)
import Control.Monad.Catch (finally)
import Data.Data (showConstr, toConstr) import Data.Data (showConstr, toConstr)
import Data.Foldable (forM_) import Foreign.Lua (Lua, Peekable, Pushable, StackIndex)
import Foreign.Lua (FromLuaStack (peek), Lua, LuaInteger, LuaNumber, StackIndex,
ToLuaStack (push), Type (..), throwLuaError, tryLua)
import Text.Pandoc.Definition import Text.Pandoc.Definition
import Text.Pandoc.Extensions (Extensions) import Text.Pandoc.Extensions (Extensions)
import Text.Pandoc.Lua.Util (pushViaConstructor, typeCheck) import Text.Pandoc.Lua.Util (defineHowTo, pushViaConstructor)
import Text.Pandoc.Options (ReaderOptions (..), TrackChanges) import Text.Pandoc.Options (ReaderOptions (..), TrackChanges)
import Text.Pandoc.Shared (Element (Blk, Sec), safeRead) import Text.Pandoc.Shared (Element (Blk, Sec))
import qualified Foreign.Lua as Lua
import qualified Data.Set as Set import qualified Data.Set as Set
import qualified Foreign.Lua as Lua
import qualified Text.Pandoc.Lua.Util as LuaUtil import qualified Text.Pandoc.Lua.Util as LuaUtil
defineHowTo :: String -> Lua a -> Lua a instance Pushable Pandoc where
defineHowTo ctx op = op `Lua.modifyLuaError` (("Could not " ++ ctx ++ ": ") ++)
instance ToLuaStack Pandoc where
push (Pandoc meta blocks) = push (Pandoc meta blocks) =
pushViaConstructor "Pandoc" blocks meta pushViaConstructor "Pandoc" blocks meta
instance FromLuaStack Pandoc where instance Peekable Pandoc where
peek idx = defineHowTo "get Pandoc value" $ do peek idx = defineHowTo "get Pandoc value" $ do
typeCheck idx Lua.TypeTable
blocks <- LuaUtil.rawField idx "blocks" blocks <- LuaUtil.rawField idx "blocks"
meta <- Lua.getfield idx "meta" *> (Lua.peek Lua.stackTop `finally` Lua.pop 1) meta <- LuaUtil.rawField idx "meta"
return $ Pandoc meta blocks return $ Pandoc meta blocks
instance ToLuaStack Meta where instance Pushable Meta where
push (Meta mmap) = push (Meta mmap) =
pushViaConstructor "Meta" mmap pushViaConstructor "Meta" mmap
instance FromLuaStack Meta where instance Peekable Meta where
peek idx = defineHowTo "get Meta value" $ do peek idx = defineHowTo "get Meta value" $
typeCheck idx Lua.TypeTable Meta <$> Lua.peek idx
Meta <$> peek idx
instance ToLuaStack MetaValue where instance Pushable MetaValue where
push = pushMetaValue push = pushMetaValue
instance FromLuaStack MetaValue where instance Peekable MetaValue where
peek = peekMetaValue peek = peekMetaValue
instance ToLuaStack Block where instance Pushable Block where
push = pushBlock push = pushBlock
instance FromLuaStack Block where instance Peekable Block where
peek = peekBlock peek = peekBlock
-- Inline -- Inline
instance ToLuaStack Inline where instance Pushable Inline where
push = pushInline push = pushInline
instance FromLuaStack Inline where instance Peekable Inline where
peek = peekInline peek = peekInline
-- Citation -- Citation
instance ToLuaStack Citation where instance Pushable Citation where
push (Citation cid prefix suffix mode noteNum hash) = push (Citation cid prefix suffix mode noteNum hash) =
pushViaConstructor "Citation" cid mode prefix suffix noteNum hash pushViaConstructor "Citation" cid mode prefix suffix noteNum hash
instance FromLuaStack Citation where instance Peekable Citation where
peek idx = do peek idx = do
id' <- LuaUtil.rawField idx "id" id' <- LuaUtil.rawField idx "id"
prefix <- LuaUtil.rawField idx "prefix" prefix <- LuaUtil.rawField idx "prefix"
@ -107,78 +99,63 @@ instance FromLuaStack Citation where
hash <- LuaUtil.rawField idx "hash" hash <- LuaUtil.rawField idx "hash"
return $ Citation id' prefix suffix mode num hash return $ Citation id' prefix suffix mode num hash
instance ToLuaStack Alignment where instance Pushable Alignment where
push = push . show push = Lua.push . show
instance FromLuaStack Alignment where instance Peekable Alignment where
peek idx = safeRead' =<< peek idx peek = Lua.peekRead
instance ToLuaStack CitationMode where instance Pushable CitationMode where
push = push . show push = Lua.push . show
instance FromLuaStack CitationMode where instance Peekable CitationMode where
peek idx = safeRead' =<< peek idx peek = Lua.peekRead
instance ToLuaStack Format where instance Pushable Format where
push (Format f) = push f push (Format f) = Lua.push f
instance FromLuaStack Format where instance Peekable Format where
peek idx = Format <$> peek idx peek idx = Format <$> Lua.peek idx
instance ToLuaStack ListNumberDelim where instance Pushable ListNumberDelim where
push = push . show push = Lua.push . show
instance FromLuaStack ListNumberDelim where instance Peekable ListNumberDelim where
peek idx = safeRead' =<< peek idx peek = Lua.peekRead
instance ToLuaStack ListNumberStyle where instance Pushable ListNumberStyle where
push = push . show push = Lua.push . show
instance FromLuaStack ListNumberStyle where instance Peekable ListNumberStyle where
peek idx = safeRead' =<< peek idx peek = Lua.peekRead
instance ToLuaStack MathType where instance Pushable MathType where
push = push . show push = Lua.push . show
instance FromLuaStack MathType where instance Peekable MathType where
peek idx = safeRead' =<< peek idx peek = Lua.peekRead
instance ToLuaStack QuoteType where instance Pushable QuoteType where
push = push . show push = Lua.push . show
instance FromLuaStack QuoteType where instance Peekable QuoteType where
peek idx = safeRead' =<< peek idx peek = Lua.peekRead
instance ToLuaStack Double where
push = push . (realToFrac :: Double -> LuaNumber)
instance FromLuaStack Double where
peek = fmap (realToFrac :: LuaNumber -> Double) . peek
instance ToLuaStack Int where
push = push . (fromIntegral :: Int -> LuaInteger)
instance FromLuaStack Int where
peek = fmap (fromIntegral :: LuaInteger-> Int) . peek
safeRead' :: Read a => String -> Lua a
safeRead' s = case safeRead s of
Nothing -> throwLuaError ("Could not read: " ++ s)
Just x -> return x
-- | Push an meta value element to the top of the lua stack. -- | Push an meta value element to the top of the lua stack.
pushMetaValue :: MetaValue -> Lua () pushMetaValue :: MetaValue -> Lua ()
pushMetaValue = \case pushMetaValue = \case
MetaBlocks blcks -> pushViaConstructor "MetaBlocks" blcks MetaBlocks blcks -> pushViaConstructor "MetaBlocks" blcks
MetaBool bool -> push bool MetaBool bool -> Lua.push bool
MetaInlines inlns -> pushViaConstructor "MetaInlines" inlns MetaInlines inlns -> pushViaConstructor "MetaInlines" inlns
MetaList metalist -> pushViaConstructor "MetaList" metalist MetaList metalist -> pushViaConstructor "MetaList" metalist
MetaMap metamap -> pushViaConstructor "MetaMap" metamap MetaMap metamap -> pushViaConstructor "MetaMap" metamap
MetaString str -> push str MetaString str -> Lua.push str
-- | Interpret the value at the given stack index as meta value. -- | Interpret the value at the given stack index as meta value.
peekMetaValue :: StackIndex -> Lua MetaValue peekMetaValue :: StackIndex -> Lua MetaValue
peekMetaValue idx = defineHowTo "get MetaValue" $ do peekMetaValue idx = defineHowTo "get MetaValue" $ do
-- Get the contents of an AST element. -- Get the contents of an AST element.
let elementContent :: FromLuaStack a => Lua a let elementContent :: Peekable a => Lua a
elementContent = peek idx elementContent = Lua.peek idx
luatype <- Lua.ltype idx luatype <- Lua.ltype idx
case luatype of case luatype of
TypeBoolean -> MetaBool <$> peek idx Lua.TypeBoolean -> MetaBool <$> Lua.peek idx
TypeString -> MetaString <$> peek idx Lua.TypeString -> MetaString <$> Lua.peek idx
TypeTable -> do Lua.TypeTable -> do
tag <- tryLua $ LuaUtil.getTag idx tag <- Lua.try $ LuaUtil.getTag idx
case tag of case tag of
Right "MetaBlocks" -> MetaBlocks <$> elementContent Right "MetaBlocks" -> MetaBlocks <$> elementContent
Right "MetaBool" -> MetaBool <$> elementContent Right "MetaBool" -> MetaBool <$> elementContent
@ -186,16 +163,16 @@ peekMetaValue idx = defineHowTo "get MetaValue" $ do
Right "MetaInlines" -> MetaInlines <$> elementContent Right "MetaInlines" -> MetaInlines <$> elementContent
Right "MetaList" -> MetaList <$> elementContent Right "MetaList" -> MetaList <$> elementContent
Right "MetaString" -> MetaString <$> elementContent Right "MetaString" -> MetaString <$> elementContent
Right t -> throwLuaError ("Unknown meta tag: " ++ t) Right t -> Lua.throwException ("Unknown meta tag: " <> t)
Left _ -> do Left _ -> do
-- no meta value tag given, try to guess. -- no meta value tag given, try to guess.
len <- Lua.rawlen idx len <- Lua.rawlen idx
if len <= 0 if len <= 0
then MetaMap <$> peek idx then MetaMap <$> Lua.peek idx
else (MetaInlines <$> peek idx) else (MetaInlines <$> Lua.peek idx)
<|> (MetaBlocks <$> peek idx) <|> (MetaBlocks <$> Lua.peek idx)
<|> (MetaList <$> peek idx) <|> (MetaList <$> Lua.peek idx)
_ -> throwLuaError "could not get meta value" _ -> Lua.throwException "could not get meta value"
-- | Push an block element to the top of the lua stack. -- | Push an block element to the top of the lua stack.
pushBlock :: Block -> Lua () pushBlock :: Block -> Lua ()
@ -219,7 +196,6 @@ pushBlock = \case
-- | Return the value at the given index as block if possible. -- | Return the value at the given index as block if possible.
peekBlock :: StackIndex -> Lua Block peekBlock :: StackIndex -> Lua Block
peekBlock idx = defineHowTo "get Block value" $ do peekBlock idx = defineHowTo "get Block value" $ do
typeCheck idx Lua.TypeTable
tag <- LuaUtil.getTag idx tag <- LuaUtil.getTag idx
case tag of case tag of
"BlockQuote" -> BlockQuote <$> elementContent "BlockQuote" -> BlockQuote <$> elementContent
@ -239,10 +215,10 @@ peekBlock idx = defineHowTo "get Block value" $ do
"Table" -> (\(capt, aligns, widths, headers, body) -> "Table" -> (\(capt, aligns, widths, headers, body) ->
Table capt aligns widths headers body) Table capt aligns widths headers body)
<$> elementContent <$> elementContent
_ -> throwLuaError ("Unknown block type: " ++ tag) _ -> Lua.throwException ("Unknown block type: " <> tag)
where where
-- Get the contents of an AST element. -- Get the contents of an AST element.
elementContent :: FromLuaStack a => Lua a elementContent :: Peekable a => Lua a
elementContent = LuaUtil.rawField idx "c" elementContent = LuaUtil.rawField idx "c"
-- | Push an inline element to the top of the lua stack. -- | Push an inline element to the top of the lua stack.
@ -271,7 +247,6 @@ pushInline = \case
-- | Return the value at the given index as inline if possible. -- | Return the value at the given index as inline if possible.
peekInline :: StackIndex -> Lua Inline peekInline :: StackIndex -> Lua Inline
peekInline idx = defineHowTo "get Inline value" $ do peekInline idx = defineHowTo "get Inline value" $ do
typeCheck idx Lua.TypeTable
tag <- LuaUtil.getTag idx tag <- LuaUtil.getTag idx
case tag of case tag of
"Cite" -> uncurry Cite <$> elementContent "Cite" -> uncurry Cite <$> elementContent
@ -295,10 +270,10 @@ peekInline idx = defineHowTo "get Inline value" $ do
"Strong" -> Strong <$> elementContent "Strong" -> Strong <$> elementContent
"Subscript" -> Subscript <$> elementContent "Subscript" -> Subscript <$> elementContent
"Superscript"-> Superscript <$> elementContent "Superscript"-> Superscript <$> elementContent
_ -> throwLuaError ("Unknown inline type: " ++ tag) _ -> Lua.throwException ("Unknown inline type: " <> tag)
where where
-- Get the contents of an AST element. -- Get the contents of an AST element.
elementContent :: FromLuaStack a => Lua a elementContent :: Peekable a => Lua a
elementContent = LuaUtil.rawField idx "c" elementContent = LuaUtil.rawField idx "c"
withAttr :: (Attr -> a -> b) -> (LuaAttr, a) -> b withAttr :: (Attr -> a -> b) -> (LuaAttr, a) -> b
@ -307,18 +282,18 @@ withAttr f (attributes, x) = f (fromLuaAttr attributes) x
-- | Wrapper for Attr -- | Wrapper for Attr
newtype LuaAttr = LuaAttr { fromLuaAttr :: Attr } newtype LuaAttr = LuaAttr { fromLuaAttr :: Attr }
instance ToLuaStack LuaAttr where instance Pushable LuaAttr where
push (LuaAttr (id', classes, kv)) = push (LuaAttr (id', classes, kv)) =
pushViaConstructor "Attr" id' classes kv pushViaConstructor "Attr" id' classes kv
instance FromLuaStack LuaAttr where instance Peekable LuaAttr where
peek idx = defineHowTo "get Attr value" (LuaAttr <$> peek idx) peek idx = defineHowTo "get Attr value" (LuaAttr <$> Lua.peek idx)
-- --
-- Hierarchical elements -- Hierarchical elements
-- --
instance ToLuaStack Element where instance Pushable Element where
push (Blk blk) = push blk push (Blk blk) = Lua.push blk
push (Sec lvl num attr label contents) = do push (Sec lvl num attr label contents) = do
Lua.newtable Lua.newtable
LuaUtil.addField "level" lvl LuaUtil.addField "level" lvl
@ -342,18 +317,13 @@ instance ToLuaStack Element where
-- --
-- Reader Options -- Reader Options
-- --
instance ToLuaStack Extensions where instance Pushable Extensions where
push exts = push (show exts) push exts = Lua.push (show exts)
instance ToLuaStack TrackChanges where instance Pushable TrackChanges where
push = push . showConstr . toConstr push = Lua.push . showConstr . toConstr
instance ToLuaStack a => ToLuaStack (Set.Set a) where instance Pushable ReaderOptions where
push set = do
Lua.newtable
forM_ set (`LuaUtil.addValue` True)
instance ToLuaStack ReaderOptions where
push ro = do push ro = do
let ReaderOptions let ReaderOptions
(extensions :: Extensions) (extensions :: Extensions)

View file

@ -1,4 +1,3 @@
{-# LANGUAGE NoImplicitPrelude #-}
{- {-
Copyright © 2012-2018 John MacFarlane <jgm@berkeley.edu> Copyright © 2012-2018 John MacFarlane <jgm@berkeley.edu>
2017-2018 Albert Krewinkel <tarleb+pandoc@moltkeplatz.de> 2017-2018 Albert Krewinkel <tarleb+pandoc@moltkeplatz.de>
@ -18,6 +17,8 @@ along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-} -}
{-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE NoImplicitPrelude #-}
{- | {- |
Module : Text.Pandoc.Lua.Util Module : Text.Pandoc.Lua.Util
Copyright : © 20122018 John MacFarlane, Copyright : © 20122018 John MacFarlane,
@ -35,39 +36,35 @@ module Text.Pandoc.Lua.Util
, addField , addField
, addFunction , addFunction
, addValue , addValue
, typeCheck
, popValue
, PushViaCall
, pushViaCall
, pushViaConstructor , pushViaConstructor
, loadScriptFromDataDir , loadScriptFromDataDir
, dostring' , defineHowTo
, throwTopMessageAsError'
) where ) where
import Prelude import Prelude
import Control.Monad (when) import Control.Monad (unless, when)
import Control.Monad.Catch (finally) import Foreign.Lua ( Lua, NumArgs, Peekable, Pushable, StackIndex
import Data.ByteString.Char8 (unpack) , ToHaskellFunction )
import Foreign.Lua (FromLuaStack, Lua, NumArgs, StackIndex, Status,
ToLuaStack, ToHaskellFunction)
import Text.Pandoc.Class (readDataFile, runIOorExplode, setUserDataDir) import Text.Pandoc.Class (readDataFile, runIOorExplode, setUserDataDir)
import qualified Foreign.Lua as Lua import qualified Foreign.Lua as Lua
import qualified Text.Pandoc.UTF8 as UTF8
-- | Get value behind key from table at given index. -- | Get value behind key from table at given index.
rawField :: FromLuaStack a => StackIndex -> String -> Lua a rawField :: Peekable a => StackIndex -> String -> Lua a
rawField idx key = do rawField idx key = do
absidx <- Lua.absindex idx absidx <- Lua.absindex idx
Lua.push key Lua.push key
Lua.rawget absidx Lua.rawget absidx
popValue Lua.popValue
-- | Add a value to the table at the top of the stack at a string-index. -- | Add a value to the table at the top of the stack at a string-index.
addField :: ToLuaStack a => String -> a -> Lua () addField :: Pushable a => String -> a -> Lua ()
addField = addValue addField = addValue
-- | Add a key-value pair to the table at the top of the stack. -- | Add a key-value pair to the table at the top of the stack.
addValue :: (ToLuaStack a, ToLuaStack b) => a -> b -> Lua () addValue :: (Pushable a, Pushable b) => a -> b -> Lua ()
addValue key value = do addValue key value = do
Lua.push key Lua.push key
Lua.push value Lua.push value
@ -78,26 +75,8 @@ addFunction :: ToHaskellFunction a => String -> a -> Lua ()
addFunction name fn = do addFunction name fn = do
Lua.push name Lua.push name
Lua.pushHaskellFunction fn Lua.pushHaskellFunction fn
Lua.wrapHaskellFunction
Lua.rawset (-3) Lua.rawset (-3)
typeCheck :: StackIndex -> Lua.Type -> Lua ()
typeCheck idx expected = do
actual <- Lua.ltype idx
when (actual /= expected) $ do
expName <- Lua.typename expected
actName <- Lua.typename actual
Lua.throwLuaError $ "expected " ++ expName ++ " but got " ++ actName ++ "."
-- | Get, then pop the value at the top of the stack.
popValue :: FromLuaStack a => Lua a
popValue = do
resOrError <- Lua.peekEither (-1)
Lua.pop 1
case resOrError of
Left err -> Lua.throwLuaError err
Right x -> return x
-- | Helper class for pushing a single value to the stack via a lua function. -- | Helper class for pushing a single value to the stack via a lua function.
-- See @pushViaCall@. -- See @pushViaCall@.
class PushViaCall a where class PushViaCall a where
@ -110,7 +89,7 @@ instance PushViaCall (Lua ()) where
pushArgs pushArgs
Lua.call num 1 Lua.call num 1
instance (ToLuaStack a, PushViaCall b) => PushViaCall (a -> b) where instance (Pushable a, PushViaCall b) => PushViaCall (a -> b) where
pushViaCall' fn pushArgs num x = pushViaCall' fn pushArgs num x =
pushViaCall' fn (pushArgs *> Lua.push x) (num + 1) pushViaCall' fn (pushArgs *> Lua.push x) (num + 1)
@ -127,26 +106,11 @@ pushViaConstructor pandocFn = pushViaCall ("pandoc." ++ pandocFn)
-- | Load a file from pandoc's data directory. -- | Load a file from pandoc's data directory.
loadScriptFromDataDir :: Maybe FilePath -> FilePath -> Lua () loadScriptFromDataDir :: Maybe FilePath -> FilePath -> Lua ()
loadScriptFromDataDir datadir scriptFile = do loadScriptFromDataDir datadir scriptFile = do
script <- fmap unpack . Lua.liftIO . runIOorExplode $ script <- Lua.liftIO . runIOorExplode $
setUserDataDir datadir >> readDataFile scriptFile setUserDataDir datadir >> readDataFile scriptFile
status <- dostring' script status <- Lua.dostring script
when (status /= Lua.OK) . when (status /= Lua.OK) $
Lua.throwTopMessageAsError' $ \msg -> throwTopMessageAsError' (("Couldn't load '" ++ scriptFile ++ "'.\n") ++)
"Couldn't load '" ++ scriptFile ++ "'.\n" ++ msg
-- | Load a string and immediately perform a full garbage collection. This is
-- important to keep the program from hanging: If the program containes a call
-- to @require@, then a new loader function is created which then becomes
-- garbage. If that function is collected at an inopportune time, i.e. when the
-- Lua API is called via a function that doesn't allow calling back into Haskell
-- (getraw, setraw, …), then the function's finalizer, and the full program,
-- will hang.
dostring' :: String -> Lua Status
dostring' script = do
loadRes <- Lua.loadstring script
if loadRes == Lua.OK
then Lua.pcall 0 1 Nothing <* Lua.gc Lua.GCCOLLECT 0
else return loadRes
-- | Get the tag of a value. This is an optimized and specialized version of -- | Get the tag of a value. This is an optimized and specialized version of
-- @Lua.getfield idx "tag"@. It only checks for the field on the table at index -- @Lua.getfield idx "tag"@. It only checks for the field on the table at index
@ -155,7 +119,21 @@ dostring' script = do
getTag :: StackIndex -> Lua String getTag :: StackIndex -> Lua String
getTag idx = do getTag idx = do
-- push metatable or just the table -- push metatable or just the table
Lua.getmetatable idx >>= \hasMT -> when (not hasMT) (Lua.pushvalue idx) Lua.getmetatable idx >>= \hasMT -> unless hasMT (Lua.pushvalue idx)
Lua.push "tag" Lua.push "tag"
Lua.rawget (Lua.nthFromTop 2) Lua.rawget (Lua.nthFromTop 2)
Lua.peek Lua.stackTop `finally` Lua.pop 2 Lua.tostring Lua.stackTop <* Lua.pop 2 >>= \case
Nothing -> Lua.throwException "untagged value"
Just x -> return (UTF8.toString x)
-- | Modify the message at the top of the stack before throwing it as an
-- Exception.
throwTopMessageAsError' :: (String -> String) -> Lua a
throwTopMessageAsError' modifier = do
msg <- Lua.tostring' Lua.stackTop
Lua.pop 2 -- remove error and error string pushed by tostring'
Lua.throwException (modifier (UTF8.toString msg))
defineHowTo :: String -> Lua a -> Lua a
defineHowTo ctx = Lua.withExceptionMessage (("Could not " <> ctx <> ": ") <>)

View file

@ -1,6 +1,6 @@
{-# LANGUAGE NoImplicitPrelude #-} {-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE DeriveDataTypeable #-} {-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE NoImplicitPrelude #-}
{- Copyright (C) 2012-2018 John MacFarlane <jgm@berkeley.edu> {- Copyright (C) 2012-2018 John MacFarlane <jgm@berkeley.edu>
This program is free software; you can redistribute it and/or modify This program is free software; you can redistribute it and/or modify
@ -35,25 +35,26 @@ import Prelude
import Control.Arrow ((***)) import Control.Arrow ((***))
import Control.Exception import Control.Exception
import Control.Monad (when) import Control.Monad (when)
import Control.Monad.Trans (MonadIO (liftIO))
import Data.Char (toLower) import Data.Char (toLower)
import Data.List (intersperse) import Data.List (intersperse)
import qualified Data.Map as M import qualified Data.Map as M
import Data.Text (Text, pack) import Data.Text (Text, pack)
import Data.Typeable import Data.Typeable
import Foreign.Lua (Lua, ToLuaStack (..), callFunc) import Foreign.Lua (Lua, Pushable)
import Foreign.Lua.Api
import Text.Pandoc.Class (PandocIO) import Text.Pandoc.Class (PandocIO)
import Text.Pandoc.Definition import Text.Pandoc.Definition
import Text.Pandoc.Error import Text.Pandoc.Error
import Text.Pandoc.Lua.Init (runPandocLua, registerScriptPath) import Text.Pandoc.Lua.Init (LuaException (LuaException), runPandocLua,
registerScriptPath)
import Text.Pandoc.Lua.StackInstances () import Text.Pandoc.Lua.StackInstances ()
import Text.Pandoc.Lua.Util (addField, addValue, dostring') import Text.Pandoc.Lua.Util (addField)
import Text.Pandoc.Options import Text.Pandoc.Options
import Text.Pandoc.Templates import Text.Pandoc.Templates
import qualified Text.Pandoc.UTF8 as UTF8 import qualified Text.Pandoc.UTF8 as UTF8
import Text.Pandoc.Writers.Shared import Text.Pandoc.Writers.Shared
import qualified Foreign.Lua as Lua
attrToMap :: Attr -> M.Map String String attrToMap :: Attr -> M.Map String String
attrToMap (id',classes,keyvals) = M.fromList attrToMap (id',classes,keyvals) = M.fromList
$ ("id", id') $ ("id", id')
@ -62,26 +63,26 @@ attrToMap (id',classes,keyvals) = M.fromList
newtype Stringify a = Stringify a newtype Stringify a = Stringify a
instance ToLuaStack (Stringify Format) where instance Pushable (Stringify Format) where
push (Stringify (Format f)) = push (map toLower f) push (Stringify (Format f)) = Lua.push (map toLower f)
instance ToLuaStack (Stringify [Inline]) where instance Pushable (Stringify [Inline]) where
push (Stringify ils) = push =<< inlineListToCustom ils push (Stringify ils) = Lua.push =<< inlineListToCustom ils
instance ToLuaStack (Stringify [Block]) where instance Pushable (Stringify [Block]) where
push (Stringify blks) = push =<< blockListToCustom blks push (Stringify blks) = Lua.push =<< blockListToCustom blks
instance ToLuaStack (Stringify MetaValue) where instance Pushable (Stringify MetaValue) where
push (Stringify (MetaMap m)) = push (fmap Stringify m) push (Stringify (MetaMap m)) = Lua.push (fmap Stringify m)
push (Stringify (MetaList xs)) = push (map Stringify xs) push (Stringify (MetaList xs)) = Lua.push (map Stringify xs)
push (Stringify (MetaBool x)) = push x push (Stringify (MetaBool x)) = Lua.push x
push (Stringify (MetaString s)) = push s push (Stringify (MetaString s)) = Lua.push s
push (Stringify (MetaInlines ils)) = push (Stringify ils) push (Stringify (MetaInlines ils)) = Lua.push (Stringify ils)
push (Stringify (MetaBlocks bs)) = push (Stringify bs) push (Stringify (MetaBlocks bs)) = Lua.push (Stringify bs)
instance ToLuaStack (Stringify Citation) where instance Pushable (Stringify Citation) where
push (Stringify cit) = do push (Stringify cit) = do
createtable 6 0 Lua.createtable 6 0
addField "citationId" $ citationId cit addField "citationId" $ citationId cit
addField "citationPrefix" . Stringify $ citationPrefix cit addField "citationPrefix" . Stringify $ citationPrefix cit
addField "citationSuffix" . Stringify $ citationSuffix cit addField "citationSuffix" . Stringify $ citationSuffix cit
@ -93,10 +94,12 @@ instance ToLuaStack (Stringify Citation) where
-- associated value. -- associated value.
newtype KeyValue a b = KeyValue (a, b) newtype KeyValue a b = KeyValue (a, b)
instance (ToLuaStack a, ToLuaStack b) => ToLuaStack (KeyValue a b) where instance (Pushable a, Pushable b) => Pushable (KeyValue a b) where
push (KeyValue (k, v)) = do push (KeyValue (k, v)) = do
newtable Lua.newtable
addValue k v Lua.push k
Lua.push v
Lua.rawset (Lua.nthFromTop 3)
data PandocLuaException = PandocLuaException String data PandocLuaException = PandocLuaException String
deriving (Show, Typeable) deriving (Show, Typeable)
@ -106,14 +109,13 @@ instance Exception PandocLuaException
-- | Convert Pandoc to custom markup. -- | Convert Pandoc to custom markup.
writeCustom :: FilePath -> WriterOptions -> Pandoc -> PandocIO Text writeCustom :: FilePath -> WriterOptions -> Pandoc -> PandocIO Text
writeCustom luaFile opts doc@(Pandoc meta _) = do writeCustom luaFile opts doc@(Pandoc meta _) = do
luaScript <- liftIO $ UTF8.readFile luaFile
res <- runPandocLua $ do res <- runPandocLua $ do
registerScriptPath luaFile registerScriptPath luaFile
stat <- dostring' luaScript stat <- Lua.dofile luaFile
-- check for error in lua script (later we'll change the return type -- check for error in lua script (later we'll change the return type
-- to handle this more gracefully): -- to handle this more gracefully):
when (stat /= OK) $ when (stat /= Lua.OK) $
tostring (-1) >>= throw . PandocLuaException . UTF8.toString Lua.tostring' (-1) >>= throw . PandocLuaException . UTF8.toString
-- TODO - call hierarchicalize, so we have that info -- TODO - call hierarchicalize, so we have that info
rendered <- docToCustom opts doc rendered <- docToCustom opts doc
context <- metaToJSON opts context <- metaToJSON opts
@ -122,7 +124,7 @@ writeCustom luaFile opts doc@(Pandoc meta _) = do
meta meta
return (rendered, context) return (rendered, context)
let (body, context) = case res of let (body, context) = case res of
Left e -> throw (PandocLuaException (show e)) Left (LuaException msg) -> throw (PandocLuaException msg)
Right x -> x Right x -> x
case writerTemplate opts of case writerTemplate opts of
Nothing -> return $ pack body Nothing -> return $ pack body
@ -134,7 +136,7 @@ writeCustom luaFile opts doc@(Pandoc meta _) = do
docToCustom :: WriterOptions -> Pandoc -> Lua String docToCustom :: WriterOptions -> Pandoc -> Lua String
docToCustom opts (Pandoc (Meta metamap) blocks) = do docToCustom opts (Pandoc (Meta metamap) blocks) = do
body <- blockListToCustom blocks body <- blockListToCustom blocks
callFunc "Doc" body (fmap Stringify metamap) (writerVariables opts) Lua.callFunc "Doc" body (fmap Stringify metamap) (writerVariables opts)
-- | Convert Pandoc block element to Custom. -- | Convert Pandoc block element to Custom.
blockToCustom :: Block -- ^ Block element blockToCustom :: Block -- ^ Block element
@ -142,52 +144,55 @@ blockToCustom :: Block -- ^ Block element
blockToCustom Null = return "" blockToCustom Null = return ""
blockToCustom (Plain inlines) = callFunc "Plain" (Stringify inlines) blockToCustom (Plain inlines) = Lua.callFunc "Plain" (Stringify inlines)
blockToCustom (Para [Image attr txt (src,tit)]) = blockToCustom (Para [Image attr txt (src,tit)]) =
callFunc "CaptionedImage" src tit (Stringify txt) (attrToMap attr) Lua.callFunc "CaptionedImage" src tit (Stringify txt) (attrToMap attr)
blockToCustom (Para inlines) = callFunc "Para" (Stringify inlines) blockToCustom (Para inlines) = Lua.callFunc "Para" (Stringify inlines)
blockToCustom (LineBlock linesList) = callFunc "LineBlock" (map Stringify linesList) blockToCustom (LineBlock linesList) =
Lua.callFunc "LineBlock" (map Stringify linesList)
blockToCustom (RawBlock format str) = blockToCustom (RawBlock format str) =
callFunc "RawBlock" (Stringify format) str Lua.callFunc "RawBlock" (Stringify format) str
blockToCustom HorizontalRule = callFunc "HorizontalRule" blockToCustom HorizontalRule = Lua.callFunc "HorizontalRule"
blockToCustom (Header level attr inlines) = blockToCustom (Header level attr inlines) =
callFunc "Header" level (Stringify inlines) (attrToMap attr) Lua.callFunc "Header" level (Stringify inlines) (attrToMap attr)
blockToCustom (CodeBlock attr str) = blockToCustom (CodeBlock attr str) =
callFunc "CodeBlock" str (attrToMap attr) Lua.callFunc "CodeBlock" str (attrToMap attr)
blockToCustom (BlockQuote blocks) = callFunc "BlockQuote" (Stringify blocks) blockToCustom (BlockQuote blocks) =
Lua.callFunc "BlockQuote" (Stringify blocks)
blockToCustom (Table capt aligns widths headers rows) = blockToCustom (Table capt aligns widths headers rows) =
let aligns' = map show aligns let aligns' = map show aligns
capt' = Stringify capt capt' = Stringify capt
headers' = map Stringify headers headers' = map Stringify headers
rows' = map (map Stringify) rows rows' = map (map Stringify) rows
in callFunc "Table" capt' aligns' widths headers' rows' in Lua.callFunc "Table" capt' aligns' widths headers' rows'
blockToCustom (BulletList items) = callFunc "BulletList" (map Stringify items) blockToCustom (BulletList items) =
Lua.callFunc "BulletList" (map Stringify items)
blockToCustom (OrderedList (num,sty,delim) items) = blockToCustom (OrderedList (num,sty,delim) items) =
callFunc "OrderedList" (map Stringify items) num (show sty) (show delim) Lua.callFunc "OrderedList" (map Stringify items) num (show sty) (show delim)
blockToCustom (DefinitionList items) = blockToCustom (DefinitionList items) =
callFunc "DefinitionList" Lua.callFunc "DefinitionList"
(map (KeyValue . (Stringify *** map Stringify)) items) (map (KeyValue . (Stringify *** map Stringify)) items)
blockToCustom (Div attr items) = blockToCustom (Div attr items) =
callFunc "Div" (Stringify items) (attrToMap attr) Lua.callFunc "Div" (Stringify items) (attrToMap attr)
-- | Convert list of Pandoc block elements to Custom. -- | Convert list of Pandoc block elements to Custom.
blockListToCustom :: [Block] -- ^ List of block elements blockListToCustom :: [Block] -- ^ List of block elements
-> Lua String -> Lua String
blockListToCustom xs = do blockListToCustom xs = do
blocksep <- callFunc "Blocksep" blocksep <- Lua.callFunc "Blocksep"
bs <- mapM blockToCustom xs bs <- mapM blockToCustom xs
return $ mconcat $ intersperse blocksep bs return $ mconcat $ intersperse blocksep bs
@ -200,51 +205,51 @@ inlineListToCustom lst = do
-- | Convert Pandoc inline element to Custom. -- | Convert Pandoc inline element to Custom.
inlineToCustom :: Inline -> Lua String inlineToCustom :: Inline -> Lua String
inlineToCustom (Str str) = callFunc "Str" str inlineToCustom (Str str) = Lua.callFunc "Str" str
inlineToCustom Space = callFunc "Space" inlineToCustom Space = Lua.callFunc "Space"
inlineToCustom SoftBreak = callFunc "SoftBreak" inlineToCustom SoftBreak = Lua.callFunc "SoftBreak"
inlineToCustom (Emph lst) = callFunc "Emph" (Stringify lst) inlineToCustom (Emph lst) = Lua.callFunc "Emph" (Stringify lst)
inlineToCustom (Strong lst) = callFunc "Strong" (Stringify lst) inlineToCustom (Strong lst) = Lua.callFunc "Strong" (Stringify lst)
inlineToCustom (Strikeout lst) = callFunc "Strikeout" (Stringify lst) inlineToCustom (Strikeout lst) = Lua.callFunc "Strikeout" (Stringify lst)
inlineToCustom (Superscript lst) = callFunc "Superscript" (Stringify lst) inlineToCustom (Superscript lst) = Lua.callFunc "Superscript" (Stringify lst)
inlineToCustom (Subscript lst) = callFunc "Subscript" (Stringify lst) inlineToCustom (Subscript lst) = Lua.callFunc "Subscript" (Stringify lst)
inlineToCustom (SmallCaps lst) = callFunc "SmallCaps" (Stringify lst) inlineToCustom (SmallCaps lst) = Lua.callFunc "SmallCaps" (Stringify lst)
inlineToCustom (Quoted SingleQuote lst) = callFunc "SingleQuoted" (Stringify lst) inlineToCustom (Quoted SingleQuote lst) = Lua.callFunc "SingleQuoted" (Stringify lst)
inlineToCustom (Quoted DoubleQuote lst) = callFunc "DoubleQuoted" (Stringify lst) inlineToCustom (Quoted DoubleQuote lst) = Lua.callFunc "DoubleQuoted" (Stringify lst)
inlineToCustom (Cite cs lst) = callFunc "Cite" (Stringify lst) (map Stringify cs) inlineToCustom (Cite cs lst) = Lua.callFunc "Cite" (Stringify lst) (map Stringify cs)
inlineToCustom (Code attr str) = inlineToCustom (Code attr str) =
callFunc "Code" str (attrToMap attr) Lua.callFunc "Code" str (attrToMap attr)
inlineToCustom (Math DisplayMath str) = inlineToCustom (Math DisplayMath str) =
callFunc "DisplayMath" str Lua.callFunc "DisplayMath" str
inlineToCustom (Math InlineMath str) = inlineToCustom (Math InlineMath str) =
callFunc "InlineMath" str Lua.callFunc "InlineMath" str
inlineToCustom (RawInline format str) = inlineToCustom (RawInline format str) =
callFunc "RawInline" (Stringify format) str Lua.callFunc "RawInline" (Stringify format) str
inlineToCustom LineBreak = callFunc "LineBreak" inlineToCustom LineBreak = Lua.callFunc "LineBreak"
inlineToCustom (Link attr txt (src,tit)) = inlineToCustom (Link attr txt (src,tit)) =
callFunc "Link" (Stringify txt) src tit (attrToMap attr) Lua.callFunc "Link" (Stringify txt) src tit (attrToMap attr)
inlineToCustom (Image attr alt (src,tit)) = inlineToCustom (Image attr alt (src,tit)) =
callFunc "Image" (Stringify alt) src tit (attrToMap attr) Lua.callFunc "Image" (Stringify alt) src tit (attrToMap attr)
inlineToCustom (Note contents) = callFunc "Note" (Stringify contents) inlineToCustom (Note contents) = Lua.callFunc "Note" (Stringify contents)
inlineToCustom (Span attr items) = inlineToCustom (Span attr items) =
callFunc "Span" (Stringify items) (attrToMap attr) Lua.callFunc "Span" (Stringify items) (attrToMap attr)

View file

@ -12,8 +12,8 @@ packages:
- '.' - '.'
extra-deps: extra-deps:
- pandoc-citeproc-0.14.4 - pandoc-citeproc-0.14.4
- hslua-0.9.5.1 - hslua-1.0.0
- hslua-module-text-0.1.2.1 - hslua-module-text-0.2.0
- ansi-terminal-0.8.0.2 - ansi-terminal-0.8.0.2
- cmark-gfm-0.1.3 - cmark-gfm-0.1.3
- QuickCheck-2.11.3 - QuickCheck-2.11.3

View file

@ -24,6 +24,8 @@ extra-deps:
- HsYAML-0.1.1.1 - HsYAML-0.1.1.1
- texmath-0.11.1 - texmath-0.11.1
- yaml-0.9.0 - yaml-0.9.0
- hslua-1.0.0
- hslua-module-text-0.2.0
ghc-options: ghc-options:
"$locals": -fhide-source-paths -XNoImplicitPrelude "$locals": -fhide-source-paths -XNoImplicitPrelude
resolver: lts-12.6 resolver: lts-12.6

View file

@ -164,11 +164,11 @@ tests = map (localOption (QuickCheckTests 20))
, testCase "informative error messages" . runPandocLua' $ do , testCase "informative error messages" . runPandocLua' $ do
Lua.pushboolean True Lua.pushboolean True
err <- Lua.peekEither Lua.stackTop :: Lua.Lua (Either String Pandoc) err <- Lua.peekEither Lua.stackTop
case err of case (err :: Either String Pandoc) of
Left msg -> do Left msg -> do
let expectedMsg = "Could not get Pandoc value: " let expectedMsg = "Could not get Pandoc value: "
++ "expected table but got boolean." <> "table expected, got boolean"
Lua.liftIO $ assertEqual "unexpected error message" expectedMsg msg Lua.liftIO $ assertEqual "unexpected error message" expectedMsg msg
Right _ -> error "Getting a Pandoc element from a bool should fail." Right _ -> error "Getting a Pandoc element from a bool should fail."
] ]
@ -182,10 +182,10 @@ assertFilterConversion msg filterPath docIn docExpected = do
Left exception -> assertFailure (show exception) Left exception -> assertFailure (show exception)
Right docRes -> assertEqual msg docExpected docRes Right docRes -> assertEqual msg docExpected docRes
roundtripEqual :: (Eq a, Lua.FromLuaStack a, Lua.ToLuaStack a) => a -> IO Bool roundtripEqual :: (Eq a, Lua.Peekable a, Lua.Pushable a) => a -> IO Bool
roundtripEqual x = (x ==) <$> roundtripped roundtripEqual x = (x ==) <$> roundtripped
where where
roundtripped :: (Lua.FromLuaStack a, Lua.ToLuaStack a) => IO a roundtripped :: (Lua.Peekable a, Lua.Pushable a) => IO a
roundtripped = runPandocLua' $ do roundtripped = runPandocLua' $ do
oldSize <- Lua.gettop oldSize <- Lua.gettop
Lua.push x Lua.push x