git.iko.soy/typeable/pandoc
on master help sign in
API change: use new type PandocLua for all pandoc Lua operations

The new type `PandocLua` is an instance of the `PandocMonad` typeclass and can thus be used in a way similar to `PandocIO`.

commit62cf21cbaa9ac3fbc2ba7218a3037208364c80a4 authorAlbert Krewinkel <albert@zeitkraut.de> dateMon 3 Dec 2018, 08:24 (8 years ago) parenteceb8ea
9 files changed+246−207
pandoc.cabal+2 src/Text/Pandoc/Filter/JSON.hs+3−3 src/Text/Pandoc/Lua/Init.hs+23−60 src/Text/Pandoc/Lua/Module/MediaBag.hs+26−51 src/Text/Pandoc/Lua/Module/Pandoc.hs+17−15 src/Text/Pandoc/Lua/Module/Utils.hs+15−23 src/Text/Pandoc/Lua/Packages.hs+23−41 src/Text/Pandoc/Lua/PandocLua.hs+134 src/Text/Pandoc/Lua/Util.hs+3−14
pandoc.cabal+2
@@ -632,6 +632,7 @@ library
632632 Text.Pandoc.Lua.Module.Types,
633633 Text.Pandoc.Lua.Module.Utils,
634634 Text.Pandoc.Lua.Packages,
635+ Text.Pandoc.Lua.PandocLua,
635636 Text.Pandoc.Lua.Util,
636637 Text.Pandoc.Lua.Walk,
637638 Text.Pandoc.CSS,
@@ -736,6 +737,7 @@ test-suite test-pandoc
736737 mtl >= 2.2 && < 2.3,
737738 bytestring >= 0.9 && < 0.11,
738739 base64-bytestring >= 0.1 && < 1.1,
740+ exceptions >= 0.8 && < 0.11,
739741 text >= 1.1.1.0 && < 1.3,
740742 time >= 1.5 && < 1.10,
741743 directory >= 1.2.3 && < 1.4,
src/Text/Pandoc/Filter/JSON.hs+3−3
@@ -23,7 +23,6 @@ import System.Directory (executable, doesFileExist, findExecutable,
2323import System.Environment (getEnvironment)
2424import System.Exit (ExitCode (..))
2525import System.FilePath ((</>), takeExtension)
26−import Text.Pandoc.Class.PandocIO (PandocIO)
2726import Text.Pandoc.Error (PandocError (PandocFilterError))
2827import Text.Pandoc.Definition (Pandoc)
2928import Text.Pandoc.Options (ReaderOptions)
@@ -32,11 +31,12 @@ import Text.Pandoc.Shared (pandocVersion, tshow)
3231import qualified Control.Exception as E
3332import qualified Text.Pandoc.UTF8 as UTF8
3433
35−apply :: ReaderOptions
34+apply :: MonadIO m
35+ => ReaderOptions
3636 -> [String]
3737 -> FilePath
3838 -> Pandoc
39− -> PandocIO Pandoc
39+ -> m Pandoc
4040apply ropts args f = liftIO . externalFilter ropts f args
4141
4242externalFilter :: MonadIO m
src/Text/Pandoc/Lua/Init.hs+23−60
@@ -9,9 +9,7 @@
99Functions to initialize the Lua interpreter.
1010-}
1111module Text.Pandoc.Lua.Init
12− ( LuaPackageParams (..)
13− , runLua
14− , luaPackageParams
12+ ( runLua
1513 ) where
1614
1715import Control.Monad.Catch (try)
@@ -20,17 +18,12 @@ import Data.Data (Data, dataTypeConstrs, dataTypeOf, showConstr)
2018import Foreign.Lua (Lua)
2119import GHC.IO.Encoding (getForeignEncoding, setForeignEncoding, utf8)
2220import Text.Pandoc.Class.PandocIO (PandocIO)
23−import Text.Pandoc.Class.PandocMonad (getCommonState, getUserDataDir,
24− putCommonState)
2521import Text.Pandoc.Error (PandocError)
26−import Text.Pandoc.Lua.ErrorConversion (errorConversion)
27−import Text.Pandoc.Lua.Global (Global (..), setGlobals)
28−import Text.Pandoc.Lua.Packages (LuaPackageParams (..),
29− installPandocPackageSearcher)
30−import Text.Pandoc.Lua.Util (loadScriptFromDataDir)
22+import Text.Pandoc.Lua.Packages (installPandocPackageSearcher)
23+import Text.Pandoc.Lua.PandocLua (PandocLua, liftPandocLua,
24+ loadScriptFromDataDir, runPandocLua)
3125
3226import qualified Foreign.Lua as Lua
33−import qualified Foreign.Lua.Module.Text as Lua
3427import qualified Text.Pandoc.Definition as Pandoc
3528import qualified Text.Pandoc.Lua.Module.Pandoc as ModulePandoc
3629
@@ -38,65 +31,35 @@ import qualified Text.Pandoc.Lua.Module.Pandoc as ModulePandoc
3831-- initialization.
3932runLua :: Lua a -> PandocIO (Either PandocError a)
4033runLua luaOp = do
41− luaPkgParams <- luaPackageParams
42− globals <- defaultGlobals
4334 enc <- liftIO $ getForeignEncoding <* setForeignEncoding utf8
44− res <- liftIO . try . Lua.run' errorConversion $ do
45− setGlobals globals
46− initLuaState luaPkgParams
47− -- run the given Lua operation
48− opResult <- luaOp
49− -- get the (possibly modified) state back
50− Lua.getglobal "PANDOC_STATE"
51− st <- Lua.peek Lua.stackTop
52− Lua.pop 1
53− -- done
54− return (opResult, st)
35+ res <- runPandocLua . try $ do
36+ initLuaState
37+ liftPandocLua luaOp
5538 liftIO $ setForeignEncoding enc
56− case res of
57− Left err -> return $ Left err
58− Right (x, newState) -> do
59− putCommonState newState
60− return $ Right x
61−
62−-- | Global variables which should always be set.
63−defaultGlobals :: PandocIO [Global]
64−defaultGlobals = do
65− commonState <- getCommonState
66− return
67− [ PANDOC_API_VERSION
68− , PANDOC_STATE commonState
69− , PANDOC_VERSION
70− ]
71−
72−-- | Generate parameters required to setup pandoc's lua environment.
73−luaPackageParams :: PandocIO LuaPackageParams
74−luaPackageParams = do
75− datadir <- getUserDataDir
76− return LuaPackageParams { luaPkgDataDir = datadir }
39+ return res
7740
7841-- | Initialize the lua state with all required values
79−initLuaState :: LuaPackageParams -> Lua ()
80−initLuaState pkgParams = do
81− Lua.openlibs
82− Lua.preloadTextModule "text"
83− installPandocPackageSearcher pkgParams
42+initLuaState :: PandocLua ()
43+initLuaState = do
44+ liftPandocLua Lua.openlibs
45+ installPandocPackageSearcher
8446 initPandocModule
85− loadScriptFromDataDir (luaPkgDataDir pkgParams) "init.lua"
47+ loadScriptFromDataDir "init.lua"
8648 where
87− initPandocModule :: Lua ()
49+ initPandocModule :: PandocLua ()
8850 initPandocModule = do
8951 -- Push module table
90− ModulePandoc.pushModule (luaPkgDataDir pkgParams)
52+ ModulePandoc.pushModule
9153 -- register as loaded module
92− Lua.pushvalue Lua.stackTop
93− Lua.getfield Lua.registryindex Lua.loadedTableRegistryField
94− Lua.setfield (Lua.nthFromTop 2) "pandoc"
95− Lua.pop 1
54+ liftPandocLua $ do
55+ Lua.pushvalue Lua.stackTop
56+ Lua.getfield Lua.registryindex Lua.loadedTableRegistryField
57+ Lua.setfield (Lua.nthFromTop 2) "pandoc"
58+ Lua.pop 1
9659 -- copy constructors into registry
9760 putConstructorsInRegistry
9861 -- assign module to global variable
99− Lua.setglobal "pandoc"
62+ liftPandocLua $ Lua.setglobal "pandoc"
10063
10164-- | AST elements are marshaled via normal constructor functions in the
10265-- @pandoc@ module. However, accessing Lua globals from Haskell is
@@ -106,8 +69,8 @@ initLuaState pkgParams = do
10669--
10770-- This function expects the @pandoc@ module to be at the top of the
10871-- stack.
109−putConstructorsInRegistry :: Lua ()
110−putConstructorsInRegistry = do
72+putConstructorsInRegistry :: PandocLua ()
73+putConstructorsInRegistry = liftPandocLua $ do
11174 constrsToReg $ Pandoc.Pandoc mempty mempty
11275 constrsToReg $ Pandoc.Str mempty
11376 constrsToReg $ Pandoc.Para mempty
src/Text/Pandoc/Lua/Module/MediaBag.hs+26−51
@@ -14,13 +14,13 @@ module Text.Pandoc.Lua.Module.MediaBag
1414 ) where
1515
1616import Control.Monad (zipWithM_)
17−import Foreign.Lua (Lua, NumResults, Optional, liftIO)
17+import Foreign.Lua (Lua, NumResults, Optional)
1818import Text.Pandoc.Class.CommonState (CommonState (..))
19−import Text.Pandoc.Class.PandocIO (runIOorExplode)
20−import Text.Pandoc.Class.PandocMonad (fetchItem, putCommonState, setMediaBag)
19+import Text.Pandoc.Class.PandocMonad (fetchItem, getMediaBag, modifyCommonState,
20+ setMediaBag)
2121import Text.Pandoc.Lua.Marshaling ()
2222import Text.Pandoc.Lua.Marshaling.MediaBag (pushIterator)
23−import Text.Pandoc.Lua.Util (addFunction)
23+import Text.Pandoc.Lua.PandocLua (PandocLua (..), liftPandocLua, addFunction)
2424import Text.Pandoc.MIME (MimeType)
2525
2626import qualified Data.ByteString.Lazy as BL
@@ -31,9 +31,9 @@ import qualified Text.Pandoc.MediaBag as MB
3131--
3232-- MediaBag submodule
3333--
34−pushModule :: Lua NumResults
34+pushModule :: PandocLua NumResults
3535pushModule = do
36− Lua.newtable
36+ liftPandocLua Lua.newtable
3737 addFunction "delete" delete
3838 addFunction "empty" empty
3939 addFunction "insert" insertMediaFn
@@ -43,66 +43,46 @@ pushModule = do
4343 addFunction "fetch" fetch
4444 return 1
4545
46−--
47−-- Port functions from Text.Pandoc.Class to the Lua monad.
48−-- TODO: reuse existing functions.
49−
50−-- Get the current CommonState.
51−getCommonState :: Lua CommonState
52−getCommonState = do
53− Lua.getglobal "PANDOC_STATE"
54− Lua.peek Lua.stackTop
55−
56−-- Replace MediaBag in CommonState.
57−setCommonState :: CommonState -> Lua ()
58−setCommonState st = do
59− Lua.push st
60− Lua.setglobal "PANDOC_STATE"
61−
62−modifyCommonState :: (CommonState -> CommonState) -> Lua ()
63−modifyCommonState f = getCommonState >>= setCommonState . f
64−
6546-- | Delete a single item from the media bag.
66−delete :: FilePath -> Lua NumResults
47+delete :: FilePath -> PandocLua NumResults
6748delete fp = 0 <$ modifyCommonState
6849 (\st -> st { stMediaBag = MB.deleteMedia fp (stMediaBag st) })
6950
7051-- | Delete all items from the media bag.
71−empty :: Lua NumResults
52+empty :: PandocLua NumResults
7253empty = 0 <$ modifyCommonState (\st -> st { stMediaBag = mempty })
7354
7455-- | Insert a new item into the media bag.
7556insertMediaFn :: FilePath
7657 -> Optional MimeType
7758 -> BL.ByteString
78− -> Lua NumResults
59+ -> PandocLua NumResults
7960insertMediaFn fp optionalMime contents = do
80− modifyCommonState $ \st ->
81− let mb = MB.insertMedia fp (Lua.fromOptional optionalMime) contents
82− (stMediaBag st)
83− in st { stMediaBag = mb }
84− return 0
61+ mb <- getMediaBag
62+ setMediaBag $ MB.insertMedia fp (Lua.fromOptional optionalMime) contents mb
63+ return (Lua.NumResults 0)
8564
8665-- | Returns iterator values to be used with a Lua @for@ loop.
87−items :: Lua NumResults
88−items = getCommonState >>= pushIterator . stMediaBag
66+items :: PandocLua NumResults
67+items = getMediaBag >>= liftPandocLua . pushIterator
8968
9069lookupMediaFn :: FilePath
91− -> Lua NumResults
70+ -> PandocLua NumResults
9271lookupMediaFn fp = do
93− res <- MB.lookupMedia fp . stMediaBag <$> getCommonState
94− case res of
72+ res <- MB.lookupMedia fp <$> getMediaBag
73+ liftPandocLua $ case res of
9574 Nothing -> 1 <$ Lua.pushnil
9675 Just (mimeType, contents) -> do
9776 Lua.push mimeType
9877 Lua.push contents
9978 return 2
10079
101−mediaDirectoryFn :: Lua NumResults
80+mediaDirectoryFn :: PandocLua NumResults
10281mediaDirectoryFn = do
103− dirContents <- MB.mediaDirectory . stMediaBag <$> getCommonState
104− Lua.newtable
105− zipWithM_ addEntry [1..] dirContents
82+ dirContents <- MB.mediaDirectory <$> getMediaBag
83+ liftPandocLua $ do
84+ Lua.newtable
85+ zipWithM_ addEntry [1..] dirContents
10686 return 1
10787 where
10888 addEntry :: Lua.Integer -> (FilePath, MimeType, Int) -> Lua ()
@@ -114,14 +94,9 @@ mediaDirectoryFn = do
11494 Lua.rawseti (-2) idx
11595
11696fetch :: T.Text
117− -> Lua NumResults
97+ -> PandocLua NumResults
11898fetch src = do
119− commonState <- getCommonState
120− let mediaBag = stMediaBag commonState
121− (bs, mimeType) <- liftIO . runIOorExplode $ do
122− putCommonState commonState
123− setMediaBag mediaBag
124− fetchItem src
125− Lua.push $ maybe "" T.unpack mimeType
126− Lua.push bs
99+ (bs, mimeType) <- fetchItem src
100+ liftPandocLua . Lua.push $ maybe "" T.unpack mimeType
101+ liftPandocLua $ Lua.push bs
127102 return 2 -- returns 2 values: contents, mimetype
src/Text/Pandoc/Lua/Module/Pandoc.hs+17−15
@@ -24,6 +24,8 @@ import Text.Pandoc.Class.PandocIO (runIO)
2424import Text.Pandoc.Definition (Block, Inline)
2525import Text.Pandoc.Lua.Filter (walkInlines, walkBlocks, LuaFilter, SingletonsList (..))
2626import Text.Pandoc.Lua.Marshaling ()
27+import Text.Pandoc.Lua.PandocLua (PandocLua, addFunction, liftPandocLua,
28+ loadScriptFromDataDir)
2729import Text.Pandoc.Walk (Walkable)
2830import Text.Pandoc.Options (ReaderOptions (readerExtensions))
2931import Text.Pandoc.Process (pipeProcess)
@@ -38,28 +40,28 @@ import Text.Pandoc.Error
3840
3941-- | Push the "pandoc" on the lua stack. Requires the `list` module to be
4042-- loaded.
41−pushModule :: Maybe FilePath -> Lua NumResults
42−pushModule datadir = do
43− LuaUtil.loadScriptFromDataDir datadir "pandoc.lua"
44− LuaUtil.addFunction "read" readDoc
45− LuaUtil.addFunction "pipe" pipeFn
46− LuaUtil.addFunction "walk_block" walkBlock
47− LuaUtil.addFunction "walk_inline" walkInline
43+pushModule :: PandocLua NumResults
44+pushModule = do
45+ loadScriptFromDataDir "pandoc.lua"
46+ addFunction "read" readDoc
47+ addFunction "pipe" pipeFn
48+ addFunction "walk_block" walkBlock
49+ addFunction "walk_inline" walkInline
4850 return 1
4951
5052walkElement :: (Walkable (SingletonsList Inline) a,
5153 Walkable (SingletonsList Block) a)
52− => a -> LuaFilter -> Lua a
53−walkElement x f = walkInlines f x >>= walkBlocks f
54+ => a -> LuaFilter -> PandocLua a
55+walkElement x f = liftPandocLua $ walkInlines f x >>= walkBlocks f
5456
55−walkInline :: Inline -> LuaFilter -> Lua Inline
57+walkInline :: Inline -> LuaFilter -> PandocLua Inline
5658walkInline = walkElement
5759
58−walkBlock :: Block -> LuaFilter -> Lua Block
60+walkBlock :: Block -> LuaFilter -> PandocLua Block
5961walkBlock = walkElement
6062
61−readDoc :: T.Text -> Optional T.Text -> Lua NumResults
62−readDoc content formatSpecOrNil = do
63+readDoc :: T.Text -> Optional T.Text -> PandocLua NumResults
64+readDoc content formatSpecOrNil = liftPandocLua $ do
6365 let formatSpec = fromMaybe "markdown" (Lua.fromOptional formatSpecOrNil)
6466 res <- Lua.liftIO . runIO $
6567 getReader formatSpec >>= \(rdr,es) ->
@@ -80,8 +82,8 @@ readDoc content formatSpecOrNil = do
8082pipeFn :: String
8183 -> [String]
8284 -> BL.ByteString
83− -> Lua NumResults
84−pipeFn command args input = do
85+ -> PandocLua NumResults
86+pipeFn command args input = liftPandocLua $ do
8587 (ec, output) <- Lua.liftIO $ pipeProcess Nothing command args input
8688 case ec of
8789 ExitSuccess -> 1 <$ Lua.push output
src/Text/Pandoc/Lua/Module/Utils.hs+15−23
@@ -18,13 +18,11 @@ import Control.Monad.Catch (try)
1818import Data.Default (def)
1919import Data.Version (Version)
2020import Foreign.Lua (Peekable, Lua, NumResults)
21−import Text.Pandoc.Class.PandocIO (runIO)
22−import Text.Pandoc.Class.PandocMonad (setUserDataDir)
2321import Text.Pandoc.Definition ( Pandoc, Meta, MetaValue (..), Block, Inline
2422 , Citation, Attr, ListAttributes)
2523import Text.Pandoc.Error (PandocError)
2624import Text.Pandoc.Lua.Marshaling ()
27−import Text.Pandoc.Lua.Util (addFunction)
25+import Text.Pandoc.Lua.PandocLua (PandocLua, addFunction, liftPandocLua)
2826
2927import qualified Data.Digest.Pure.SHA as SHA
3028import qualified Data.ByteString.Lazy as BSL
@@ -35,14 +33,14 @@ import qualified Text.Pandoc.Filter.JSON as JSONFilter
3533import qualified Text.Pandoc.Shared as Shared
3634
3735-- | Push the "pandoc.utils" module to the lua stack.
38−pushModule :: Maybe FilePath -> Lua NumResults
39−pushModule mbDatadir = do
40− Lua.newtable
36+pushModule :: PandocLua NumResults
37+pushModule = do
38+ liftPandocLua Lua.newtable
4139 addFunction "blocks_to_inlines" blocksToInlines
4240 addFunction "equals" equals
4341 addFunction "make_sections" makeSections
4442 addFunction "normalize_date" normalizeDate
45− addFunction "run_json_filter" (runJSONFilter mbDatadir)
43+ addFunction "run_json_filter" runJSONFilter
4644 addFunction "sha1" sha1
4745 addFunction "stringify" stringify
4846 addFunction "to_roman_numeral" toRomanNumeral
@@ -50,8 +48,8 @@ pushModule mbDatadir = do
5048 return 1
5149
5250-- | Squashes a list of blocks into inlines.
53−blocksToInlines :: [Block] -> Lua.Optional [Inline] -> Lua [Inline]
54−blocksToInlines blks optSep = do
51+blocksToInlines :: [Block] -> Lua.Optional [Inline] -> PandocLua [Inline]
52+blocksToInlines blks optSep = liftPandocLua $ do
5553 let sep = case Lua.fromOptional optSep of
5654 Just x -> B.fromList x
5755 Nothing -> Shared.defaultBlocksSeparator
@@ -70,23 +68,17 @@ normalizeDate :: T.Text -> Lua (Lua.Optional T.Text)
7068normalizeDate = return . Lua.Optional . Shared.normalizeDate
7169
7270-- | Run a JSON filter on the given document.
73−runJSONFilter :: Maybe FilePath
74− -> Pandoc
71+runJSONFilter :: Pandoc
7572 -> FilePath
7673 -> Lua.Optional [String]
77− -> Lua NumResults
78−runJSONFilter mbDatadir doc filterFile optArgs = do
74+ -> PandocLua Pandoc
75+runJSONFilter doc filterFile optArgs = do
7976 args <- case Lua.fromOptional optArgs of
8077 Just x -> return x
81− Nothing -> do
78+ Nothing -> liftPandocLua $ do
8279 Lua.getglobal "FORMAT"
8380 (:[]) <$> Lua.popValue
84− filterRes <- Lua.liftIO . runIO $ do
85− setUserDataDir mbDatadir
86− JSONFilter.apply def args filterFile doc
87− case filterRes of
88− Left err -> Lua.raiseError (show err)
89− Right d -> (1 :: NumResults) <$ Lua.push d
81+ JSONFilter.apply def args filterFile doc
9082
9183-- | Calculate the hash of the given contents.
9284sha1 :: BSL.ByteString
@@ -96,7 +88,7 @@ sha1 = return . T.pack . SHA.showDigest . SHA.sha1
9688-- | Convert pandoc structure to a string with formatting removed.
9789-- Footnotes are skipped (since we don't want their contents in link
9890-- labels).
99−stringify :: AstElement -> Lua T.Text
91+stringify :: AstElement -> PandocLua T.Text
10092stringify el = return $ case el of
10193 PandocElement pd -> Shared.stringify pd
10294 InlineElement i -> Shared.stringify i
@@ -112,7 +104,7 @@ stringifyMetaValue mv = case mv of
112104 MetaString s -> s
113105 _ -> Shared.stringify mv
114106
115−equals :: AstElement -> AstElement -> Lua Bool
107+equals :: AstElement -> AstElement -> PandocLua Bool
116108equals e1 e2 = return (e1 == e2)
117109
118110data AstElement
@@ -141,5 +133,5 @@ instance Peekable AstElement where
141133 "Expected an AST element, but could not parse value as such."
142134
143135-- | Convert a number < 4000 to uppercase roman numeral.
144−toRomanNumeral :: Lua.Integer -> Lua T.Text
136+toRomanNumeral :: Lua.Integer -> PandocLua T.Text
145137toRomanNumeral = return . Shared.toRomanNumeral . fromIntegral
src/Text/Pandoc/Lua/Packages.hs+23−41
@@ -8,37 +8,32 @@
88 Maintainer : Albert Krewinkel <tarleb+pandoc@moltkeplatz.de>
99 Stability : alpha
1010
11−Pandoc module for lua.
11+Pandoc module for Lua.
1212-}
1313module Text.Pandoc.Lua.Packages
14− ( LuaPackageParams (..)
15− , installPandocPackageSearcher
14+ ( installPandocPackageSearcher
1615 ) where
1716
1817import Control.Monad (forM_)
1918import Data.ByteString (ByteString)
20−import Foreign.Lua (Lua, NumResults, liftIO)
21−import Text.Pandoc.Class.PandocIO (runIO)
22−import Text.Pandoc.Class.PandocMonad (readDataFile, setUserDataDir)
19+import Foreign.Lua (Lua, NumResults)
20+import Text.Pandoc.Class.PandocMonad (readDataFile)
21+import Text.Pandoc.Lua.PandocLua (PandocLua, liftPandocLua)
2322
2423import qualified Foreign.Lua as Lua
25−import Text.Pandoc.Lua.Module.Pandoc as Pandoc
26−import Text.Pandoc.Lua.Module.MediaBag as MediaBag
27−import Text.Pandoc.Lua.Module.System as System
28−import Text.Pandoc.Lua.Module.Types as Types
29−import Text.Pandoc.Lua.Module.Utils as Utils
30−
31−-- | Parameters used to create lua packages/modules.
32−data LuaPackageParams = LuaPackageParams
33− { luaPkgDataDir :: Maybe FilePath
34− }
24+import qualified Foreign.Lua.Module.Text as Text
25+import qualified Text.Pandoc.Lua.Module.Pandoc as Pandoc
26+import qualified Text.Pandoc.Lua.Module.MediaBag as MediaBag
27+import qualified Text.Pandoc.Lua.Module.System as System
28+import qualified Text.Pandoc.Lua.Module.Types as Types
29+import qualified Text.Pandoc.Lua.Module.Utils as Utils
3530
3631-- | Insert pandoc's package loader as the first loader, making it the default.
37−installPandocPackageSearcher :: LuaPackageParams -> Lua ()
38−installPandocPackageSearcher luaPkgParams = do
32+installPandocPackageSearcher :: PandocLua ()
33+installPandocPackageSearcher = liftPandocLua $ do
3934 Lua.getglobal' "package.searchers"
4035 shiftArray
41− Lua.pushHaskellFunction (pandocPackageSearcher luaPkgParams)
36+ Lua.pushHaskellFunction pandocPackageSearcher
4237 Lua.rawseti (Lua.nthFromTop 2) 1
4338 Lua.pop 1 -- remove 'package.searchers' from stack
4439 where
@@ -47,29 +42,24 @@ installPandocPackageSearcher luaPkgParams = do
4742 Lua.rawseti (-2) (i + 1)
4843
4944-- | Load a pandoc module.
50−pandocPackageSearcher :: LuaPackageParams -> String -> Lua NumResults
51−pandocPackageSearcher pkgParams pkgName =
45+pandocPackageSearcher :: String -> PandocLua NumResults
46+pandocPackageSearcher pkgName =
5247 case pkgName of
53− "pandoc" -> let datadir = luaPkgDataDir pkgParams
54− in pushWrappedHsFun (Pandoc.pushModule datadir)
48+ "pandoc" -> pushWrappedHsFun Pandoc.pushModule
5549 "pandoc.mediabag" -> pushWrappedHsFun MediaBag.pushModule
5650 "pandoc.system" -> pushWrappedHsFun System.pushModule
5751 "pandoc.types" -> pushWrappedHsFun Types.pushModule
58− "pandoc.utils" -> let datadir = luaPkgDataDir pkgParams
59− in pushWrappedHsFun (Utils.pushModule datadir)
60− _ -> searchPureLuaLoader
52+ "pandoc.utils" -> pushWrappedHsFun Utils.pushModule
53+ "text" -> pushWrappedHsFun Text.pushModule
54+ _ -> searchPureLuaLoader
6155 where
62− pushWrappedHsFun f = do
56+ pushWrappedHsFun f = liftPandocLua $ do
6357 Lua.pushHaskellFunction f
6458 return 1
6559 searchPureLuaLoader = do
6660 let filename = pkgName ++ ".lua"
67− modScript <- liftIO (dataDirScript (luaPkgDataDir pkgParams) filename)
68− case modScript of
69− Just script -> pushWrappedHsFun (loadStringAsPackage pkgName script)
70− Nothing -> do
71− Lua.push ("\n\tno file '" ++ filename ++ "' in pandoc's datadir")
72− return 1
61+ script <- readDataFile filename
62+ pushWrappedHsFun (loadStringAsPackage pkgName script)
7363
7464loadStringAsPackage :: String -> ByteString -> Lua NumResults
7565loadStringAsPackage pkgName script = do
@@ -79,11 +69,3 @@ loadStringAsPackage pkgName script = do
7969 else do
8070 msg <- Lua.popValue
8171 Lua.raiseError ("Error while loading `" <> pkgName <> "`.\n" <> msg)
82−
83−-- | Get the ByteString representation of the pandoc module.
84−dataDirScript :: Maybe FilePath -> FilePath -> IO (Maybe ByteString)
85−dataDirScript datadir moduleFile = do
86− res <- runIO $ setUserDataDir datadir >> readDataFile moduleFile
87− return $ case res of
88− Left _ -> Nothing
89− Right s -> Just s
src/Text/Pandoc/Lua/PandocLua.hsnew file+134
@@ -0,0 +1,134 @@
1+{-# LANGUAGE DeriveFunctor #-}
2+{-# LANGUAGE FlexibleContexts #-}
3+{-# LANGUAGE FlexibleInstances #-}
4+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
5+{-# LANGUAGE MultiParamTypeClasses #-}
6+{-# LANGUAGE OverloadedStrings #-}
7+{-# LANGUAGE RankNTypes #-}
8+{-# LANGUAGE ScopedTypeVariables #-}
9+{-# OPTIONS_GHC -fno-warn-orphans #-}
10+{- |
11+ Module : Text.Pandoc.Lua.PandocLua
12+ Copyright : Copyright © 2020 Albert Krewinkel
13+ License : GNU GPL, version 2 or above
14+
15+ Maintainer : Albert Krewinkel <tarleb+pandoc@moltkeplatz.de>
16+ Stability : alpha
17+
18+PandocMonad instance which allows execution of Lua operations and which
19+uses Lua to handle state.
20+-}
21+module Text.Pandoc.Lua.PandocLua
22+ ( PandocLua (..)
23+ , runPandocLua
24+ , liftPandocLua
25+ , addFunction
26+ , loadScriptFromDataDir
27+ ) where
28+
29+import Control.Monad (when)
30+import Control.Monad.Catch (MonadCatch, MonadMask, MonadThrow)
31+import Control.Monad.Except (MonadError (catchError, throwError))
32+import Control.Monad.IO.Class (MonadIO (liftIO))
33+import Foreign.Lua (Lua (..), NumResults, Pushable, ToHaskellFunction)
34+import Text.Pandoc.Class.PandocIO (PandocIO)
35+import Text.Pandoc.Class.PandocMonad (PandocMonad (..), readDataFile)
36+import Text.Pandoc.Error (PandocError)
37+import Text.Pandoc.Lua.Global (Global (..), setGlobals)
38+import Text.Pandoc.Lua.ErrorConversion (errorConversion)
39+
40+import qualified Control.Monad.Catch as Catch
41+import qualified Foreign.Lua as Lua
42+import qualified Text.Pandoc.Class.IO as IO
43+import qualified Text.Pandoc.Lua.Util as LuaUtil
44+
45+-- | Type providing access to both, pandoc and Lua operations.
46+newtype PandocLua a = PandocLua { unPandocLua :: Lua a }
47+ deriving
48+ ( Applicative
49+ , Functor
50+ , Monad
51+ , MonadCatch
52+ , MonadIO
53+ , MonadMask
54+ , MonadThrow
55+ )
56+
57+-- | Lift a @'Lua'@ operation into the @'PandocLua'@ type.
58+liftPandocLua :: Lua a -> PandocLua a
59+liftPandocLua = PandocLua
60+
61+-- | Evaluate a @'PandocLua'@ computation, running all contained Lua
62+-- operations..
63+runPandocLua :: PandocLua a -> PandocIO a
64+runPandocLua pLua = do
65+ origState <- getCommonState
66+ globals <- defaultGlobals
67+ (result, newState) <- liftIO . Lua.run' errorConversion . unPandocLua $ do
68+ putCommonState origState
69+ liftPandocLua $ setGlobals globals
70+ r <- pLua
71+ c <- getCommonState
72+ return (r, c)
73+ putCommonState newState
74+ return result
75+
76+instance {-# OVERLAPPING #-} ToHaskellFunction (PandocLua NumResults) where
77+ toHsFun _narg = unPandocLua
78+
79+instance Pushable a => ToHaskellFunction (PandocLua a) where
80+ toHsFun _narg x = 1 <$ (unPandocLua x >>= Lua.push)
81+
82+-- | Add a function to the table at the top of the stack, using the given name.
83+addFunction :: ToHaskellFunction a => String -> a -> PandocLua ()
84+addFunction name fn = liftPandocLua $ do
85+ Lua.push name
86+ Lua.pushHaskellFunction fn
87+ Lua.rawset (-3)
88+
89+-- | Load a file from pandoc's data directory.
90+loadScriptFromDataDir :: FilePath -> PandocLua ()
91+loadScriptFromDataDir scriptFile = do
92+ script <- readDataFile scriptFile
93+ status <- liftPandocLua $ Lua.dostring script
94+ when (status /= Lua.OK) . liftPandocLua $
95+ LuaUtil.throwTopMessageAsError'
96+ (("Couldn't load '" ++ scriptFile ++ "'.\n") ++)
97+
98+-- | Global variables which should always be set.
99+defaultGlobals :: PandocIO [Global]
100+defaultGlobals = do
101+ commonState <- getCommonState
102+ return
103+ [ PANDOC_API_VERSION
104+ , PANDOC_STATE commonState
105+ , PANDOC_VERSION
106+ ]
107+
108+instance MonadError PandocError PandocLua where
109+ catchError = Catch.catch
110+ throwError = Catch.throwM
111+
112+instance PandocMonad PandocLua where
113+ lookupEnv = IO.lookupEnv
114+ getCurrentTime = IO.getCurrentTime
115+ getCurrentTimeZone = IO.getCurrentTimeZone
116+ newStdGen = IO.newStdGen
117+ newUniqueHash = IO.newUniqueHash
118+
119+ openURL = IO.openURL
120+
121+ readFileLazy = IO.readFileLazy
122+ readFileStrict = IO.readFileStrict
123+
124+ glob = IO.glob
125+ fileExists = IO.fileExists
126+ getDataFileName = IO.getDataFileName
127+ getModificationTime = IO.getModificationTime
128+
129+ getCommonState = PandocLua $ do
130+ Lua.getglobal "PANDOC_STATE"
131+ Lua.peek Lua.stackTop
132+ putCommonState = PandocLua . setGlobals . (:[]) . PANDOC_STATE
133+
134+ logOutput = IO.logOutput
src/Text/Pandoc/Lua/Util.hs+3−14
@@ -19,7 +19,6 @@ module Text.Pandoc.Lua.Util
1919 , addFunction
2020 , addValue
2121 , pushViaConstructor
22− , loadScriptFromDataDir
2322 , defineHowTo
2423 , throwTopMessageAsError'
2524 , callWithTraceback
@@ -27,13 +26,11 @@ module Text.Pandoc.Lua.Util
2726 ) where
2827
2928import Control.Monad (unless, when)
29+import Data.Text (Text)
3030import Foreign.Lua ( Lua, NumArgs, NumResults, Peekable, Pushable, StackIndex
3131 , Status, ToHaskellFunction )
32−import Text.Pandoc.Class.PandocIO (runIOorExplode)
33−import Text.Pandoc.Class.PandocMonad (readDataFile, setUserDataDir)
3432import qualified Foreign.Lua as Lua
3533import qualified Text.Pandoc.UTF8 as UTF8
36−import Data.Text (Text)
3734
3835-- | Get value behind key from table at given index.
3936rawField :: Peekable a => StackIndex -> String -> Lua a
@@ -87,15 +84,6 @@ pushViaCall fn = pushViaCall' fn (return ()) 0
8784pushViaConstructor :: PushViaCall a => String -> a
8885pushViaConstructor pandocFn = pushViaCall ("pandoc." ++ pandocFn)
8986
90−-- | Load a file from pandoc's data directory.
91−loadScriptFromDataDir :: Maybe FilePath -> FilePath -> Lua ()
92−loadScriptFromDataDir datadir scriptFile = do
93− script <- Lua.liftIO . runIOorExplode $
94− setUserDataDir datadir >> readDataFile scriptFile
95− status <- Lua.dostring script
96− when (status /= Lua.OK) $
97− throwTopMessageAsError' (("Couldn't load '" ++ scriptFile ++ "'.\n") ++)
98−
9987-- | Get the tag of a value. This is an optimized and specialized version of
10088-- @Lua.getfield idx "tag"@. It only checks for the field on the table at index
10189-- @idx@ and on its metatable, also ignoring any @__index@ value on the
@@ -144,7 +132,8 @@ pcallWithTraceback nargs nresults = do
144132callWithTraceback :: NumArgs -> NumResults -> Lua ()
145133callWithTraceback nargs nresults = do
146134 result <- pcallWithTraceback nargs nresults
147− when (result /= Lua.OK) Lua.throwTopMessage
135+ when (result /= Lua.OK)
136+ Lua.throwTopMessage
148137
149138-- | Run the given string as a Lua program, while also adding a traceback to the
150139-- error message if an error occurs.