git.iko.soy/typeable/pandoc
on master help sign in
API change: use new type PandocLua for all pandoc Lua operations
62cf21cAlbert Krewinkel
pandoc.cabal+2
@@ -632,6 +632,7 @@ library
632 Text.Pandoc.Lua.Module.Types,
632 Text.Pandoc.Lua.Module.Types,
633 Text.Pandoc.Lua.Module.Utils,
633 Text.Pandoc.Lua.Module.Utils,
634 Text.Pandoc.Lua.Packages,
634 Text.Pandoc.Lua.Packages,
635+ Text.Pandoc.Lua.PandocLua,
635 Text.Pandoc.Lua.Util,
636 Text.Pandoc.Lua.Util,
636 Text.Pandoc.Lua.Walk,
637 Text.Pandoc.Lua.Walk,
637 Text.Pandoc.CSS,
638 Text.Pandoc.CSS,
@@ -736,6 +737,7 @@ test-suite test-pandoc
736 mtl >= 2.2 && < 2.3,
737 mtl >= 2.2 && < 2.3,
737 bytestring >= 0.9 && < 0.11,
738 bytestring >= 0.9 && < 0.11,
738 base64-bytestring >= 0.1 && < 1.1,
739 base64-bytestring >= 0.1 && < 1.1,
740+ exceptions >= 0.8 && < 0.11,
739 text >= 1.1.1.0 && < 1.3,
741 text >= 1.1.1.0 && < 1.3,
740 time >= 1.5 && < 1.10,
742 time >= 1.5 && < 1.10,
741 directory >= 1.2.3 && < 1.4,
743 directory >= 1.2.3 && < 1.4,
src/Text/Pandoc/Filter/JSON.hs+3−3
@@ -23,7 +23,6 @@ import System.Directory (executable, doesFileExist, findExecutable,
23import System.Environment (getEnvironment)
23import System.Environment (getEnvironment)
24import System.Exit (ExitCode (..))
24import System.Exit (ExitCode (..))
25import System.FilePath ((</>), takeExtension)
25import System.FilePath ((</>), takeExtension)
26−import Text.Pandoc.Class.PandocIO (PandocIO)
27import Text.Pandoc.Error (PandocError (PandocFilterError))
26import Text.Pandoc.Error (PandocError (PandocFilterError))
28import Text.Pandoc.Definition (Pandoc)
27import Text.Pandoc.Definition (Pandoc)
29import Text.Pandoc.Options (ReaderOptions)
28import Text.Pandoc.Options (ReaderOptions)
@@ -32,11 +31,12 @@ import Text.Pandoc.Shared (pandocVersion, tshow)
32import qualified Control.Exception as E
31import qualified Control.Exception as E
33import qualified Text.Pandoc.UTF8 as UTF8
32import qualified Text.Pandoc.UTF8 as UTF8
34
33
34+apply :: MonadIO m
35−apply :: ReaderOptions
35+ => ReaderOptions
36 -> [String]
36 -> [String]
37 -> FilePath
37 -> FilePath
38 -> Pandoc
38 -> Pandoc
39− -> PandocIO Pandoc
39+ -> m Pandoc
40apply ropts args f = liftIO . externalFilter ropts f args
40apply ropts args f = liftIO . externalFilter ropts f args
41
41
42externalFilter :: MonadIO m
42externalFilter :: MonadIO m
src/Text/Pandoc/Lua/Init.hs+23−60
@@ -9,9 +9,7 @@
9Functions to initialize the Lua interpreter.
9Functions to initialize the Lua interpreter.
10-}
10-}
11module Text.Pandoc.Lua.Init
11module Text.Pandoc.Lua.Init
12− ( LuaPackageParams (..)
13− , runLua
12+ ( runLua
14− , luaPackageParams
15 ) where
13 ) where
16
14
17import Control.Monad.Catch (try)
15import Control.Monad.Catch (try)
@@ -20,17 +18,12 @@ import Data.Data (Data, dataTypeConstrs, dataTypeOf, showConstr)
20import Foreign.Lua (Lua)
18import Foreign.Lua (Lua)
21import GHC.IO.Encoding (getForeignEncoding, setForeignEncoding, utf8)
19import GHC.IO.Encoding (getForeignEncoding, setForeignEncoding, utf8)
22import Text.Pandoc.Class.PandocIO (PandocIO)
20import Text.Pandoc.Class.PandocIO (PandocIO)
23−import Text.Pandoc.Class.PandocMonad (getCommonState, getUserDataDir,
24− putCommonState)
25import Text.Pandoc.Error (PandocError)
21import 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)
22+import Text.Pandoc.Lua.Packages (installPandocPackageSearcher)
23+import Text.Pandoc.Lua.PandocLua (PandocLua, liftPandocLua,
30−import Text.Pandoc.Lua.Util (loadScriptFromDataDir)
24+ loadScriptFromDataDir, runPandocLua)
31
25
32import qualified Foreign.Lua as Lua
26import qualified Foreign.Lua as Lua
33−import qualified Foreign.Lua.Module.Text as Lua
34import qualified Text.Pandoc.Definition as Pandoc
27import qualified Text.Pandoc.Definition as Pandoc
35import qualified Text.Pandoc.Lua.Module.Pandoc as ModulePandoc
28import qualified Text.Pandoc.Lua.Module.Pandoc as ModulePandoc
36
29
@@ -38,65 +31,35 @@ import qualified Text.Pandoc.Lua.Module.Pandoc as ModulePandoc
38-- initialization.
31-- initialization.
39runLua :: Lua a -> PandocIO (Either PandocError a)
32runLua :: Lua a -> PandocIO (Either PandocError a)
40runLua luaOp = do
33runLua luaOp = do
41− luaPkgParams <- luaPackageParams
42− globals <- defaultGlobals
43 enc <- liftIO $ getForeignEncoding <* setForeignEncoding utf8
34 enc <- liftIO $ getForeignEncoding <* setForeignEncoding utf8
44− res <- liftIO . try . Lua.run' errorConversion $ do
35+ res <- runPandocLua . try $ do
45− setGlobals globals
46− initLuaState luaPkgParams
36+ initLuaState
47− -- run the given Lua operation
37+ liftPandocLua luaOp
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)
55 liftIO $ setForeignEncoding enc
38 liftIO $ setForeignEncoding enc
56− case res of
39+ return res
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 }
77
40
78-- | Initialize the lua state with all required values
41-- | Initialize the lua state with all required values
79−initLuaState :: LuaPackageParams -> Lua ()
42+initLuaState :: PandocLua ()
80−initLuaState pkgParams = do
43+initLuaState = do
81− Lua.openlibs
44+ liftPandocLua Lua.openlibs
82− Lua.preloadTextModule "text"
83− installPandocPackageSearcher pkgParams
45+ installPandocPackageSearcher
84 initPandocModule
46 initPandocModule
85− loadScriptFromDataDir (luaPkgDataDir pkgParams) "init.lua"
47+ loadScriptFromDataDir "init.lua"
86 where
48 where
87− initPandocModule :: Lua ()
49+ initPandocModule :: PandocLua ()
88 initPandocModule = do
50 initPandocModule = do
89 -- Push module table
51 -- Push module table
90− ModulePandoc.pushModule (luaPkgDataDir pkgParams)
52+ ModulePandoc.pushModule
91 -- register as loaded module
53 -- register as loaded module
54+ liftPandocLua $ do
92− Lua.pushvalue Lua.stackTop
55+ Lua.pushvalue Lua.stackTop
93− Lua.getfield Lua.registryindex Lua.loadedTableRegistryField
56+ Lua.getfield Lua.registryindex Lua.loadedTableRegistryField
94− Lua.setfield (Lua.nthFromTop 2) "pandoc"
57+ Lua.setfield (Lua.nthFromTop 2) "pandoc"
95− Lua.pop 1
58+ Lua.pop 1
96 -- copy constructors into registry
59 -- copy constructors into registry
97 putConstructorsInRegistry
60 putConstructorsInRegistry
98 -- assign module to global variable
61 -- assign module to global variable
99− Lua.setglobal "pandoc"
62+ liftPandocLua $ Lua.setglobal "pandoc"
100
63
101-- | AST elements are marshaled via normal constructor functions in the
64-- | AST elements are marshaled via normal constructor functions in the
102-- @pandoc@ module. However, accessing Lua globals from Haskell is
65-- @pandoc@ module. However, accessing Lua globals from Haskell is
@@ -106,8 +69,8 @@ initLuaState pkgParams = do
106--
69--
107-- This function expects the @pandoc@ module to be at the top of the
70-- This function expects the @pandoc@ module to be at the top of the
108-- stack.
71-- stack.
109−putConstructorsInRegistry :: Lua ()
72+putConstructorsInRegistry :: PandocLua ()
110−putConstructorsInRegistry = do
73+putConstructorsInRegistry = liftPandocLua $ do
111 constrsToReg $ Pandoc.Pandoc mempty mempty
74 constrsToReg $ Pandoc.Pandoc mempty mempty
112 constrsToReg $ Pandoc.Str mempty
75 constrsToReg $ Pandoc.Str mempty
113 constrsToReg $ Pandoc.Para mempty
76 constrsToReg $ Pandoc.Para mempty
src/Text/Pandoc/Lua/Module/MediaBag.hs+26−51
@@ -14,13 +14,13 @@ module Text.Pandoc.Lua.Module.MediaBag
14 ) where
14 ) where
15
15
16import Control.Monad (zipWithM_)
16import Control.Monad (zipWithM_)
17−import Foreign.Lua (Lua, NumResults, Optional, liftIO)
17+import Foreign.Lua (Lua, NumResults, Optional)
18import Text.Pandoc.Class.CommonState (CommonState (..))
18import Text.Pandoc.Class.CommonState (CommonState (..))
19−import Text.Pandoc.Class.PandocIO (runIOorExplode)
19+import Text.Pandoc.Class.PandocMonad (fetchItem, getMediaBag, modifyCommonState,
20−import Text.Pandoc.Class.PandocMonad (fetchItem, putCommonState, setMediaBag)
20+ setMediaBag)
21import Text.Pandoc.Lua.Marshaling ()
21import Text.Pandoc.Lua.Marshaling ()
22import Text.Pandoc.Lua.Marshaling.MediaBag (pushIterator)
22import Text.Pandoc.Lua.Marshaling.MediaBag (pushIterator)
23−import Text.Pandoc.Lua.Util (addFunction)
23+import Text.Pandoc.Lua.PandocLua (PandocLua (..), liftPandocLua, addFunction)
24import Text.Pandoc.MIME (MimeType)
24import Text.Pandoc.MIME (MimeType)
25
25
26import qualified Data.ByteString.Lazy as BL
26import qualified Data.ByteString.Lazy as BL
@@ -31,9 +31,9 @@ import qualified Text.Pandoc.MediaBag as MB
31--
31--
32-- MediaBag submodule
32-- MediaBag submodule
33--
33--
34−pushModule :: Lua NumResults
34+pushModule :: PandocLua NumResults
35pushModule = do
35pushModule = do
36− Lua.newtable
36+ liftPandocLua Lua.newtable
37 addFunction "delete" delete
37 addFunction "delete" delete
38 addFunction "empty" empty
38 addFunction "empty" empty
39 addFunction "insert" insertMediaFn
39 addFunction "insert" insertMediaFn
@@ -43,66 +43,46 @@ pushModule = do
43 addFunction "fetch" fetch
43 addFunction "fetch" fetch
44 return 1
44 return 1
45
45
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−
65-- | Delete a single item from the media bag.
46-- | Delete a single item from the media bag.
66−delete :: FilePath -> Lua NumResults
47+delete :: FilePath -> PandocLua NumResults
67delete fp = 0 <$ modifyCommonState
48delete fp = 0 <$ modifyCommonState
68 (\st -> st { stMediaBag = MB.deleteMedia fp (stMediaBag st) })
49 (\st -> st { stMediaBag = MB.deleteMedia fp (stMediaBag st) })
69
50
70-- | Delete all items from the media bag.
51-- | Delete all items from the media bag.
71−empty :: Lua NumResults
52+empty :: PandocLua NumResults
72empty = 0 <$ modifyCommonState (\st -> st { stMediaBag = mempty })
53empty = 0 <$ modifyCommonState (\st -> st { stMediaBag = mempty })
73
54
74-- | Insert a new item into the media bag.
55-- | Insert a new item into the media bag.
75insertMediaFn :: FilePath
56insertMediaFn :: FilePath
76 -> Optional MimeType
57 -> Optional MimeType
77 -> BL.ByteString
58 -> BL.ByteString
78− -> Lua NumResults
59+ -> PandocLua NumResults
79insertMediaFn fp optionalMime contents = do
60insertMediaFn fp optionalMime contents = do
80− modifyCommonState $ \st ->
61+ mb <- getMediaBag
81− let mb = MB.insertMedia fp (Lua.fromOptional optionalMime) contents
62+ setMediaBag $ MB.insertMedia fp (Lua.fromOptional optionalMime) contents mb
82− (stMediaBag st)
83− in st { stMediaBag = mb }
84− return 0
63+ return (Lua.NumResults 0)
85
64
86-- | Returns iterator values to be used with a Lua @for@ loop.
65-- | Returns iterator values to be used with a Lua @for@ loop.
87−items :: Lua NumResults
66+items :: PandocLua NumResults
88−items = getCommonState >>= pushIterator . stMediaBag
67+items = getMediaBag >>= liftPandocLua . pushIterator
89
68
90lookupMediaFn :: FilePath
69lookupMediaFn :: FilePath
91− -> Lua NumResults
70+ -> PandocLua NumResults
92lookupMediaFn fp = do
71lookupMediaFn fp = do
93− res <- MB.lookupMedia fp . stMediaBag <$> getCommonState
72+ res <- MB.lookupMedia fp <$> getMediaBag
94− case res of
73+ liftPandocLua $ case res of
95 Nothing -> 1 <$ Lua.pushnil
74 Nothing -> 1 <$ Lua.pushnil
96 Just (mimeType, contents) -> do
75 Just (mimeType, contents) -> do
97 Lua.push mimeType
76 Lua.push mimeType
98 Lua.push contents
77 Lua.push contents
99 return 2
78 return 2
100
79
101−mediaDirectoryFn :: Lua NumResults
80+mediaDirectoryFn :: PandocLua NumResults
102mediaDirectoryFn = do
81mediaDirectoryFn = do
103− dirContents <- MB.mediaDirectory . stMediaBag <$> getCommonState
82+ dirContents <- MB.mediaDirectory <$> getMediaBag
83+ liftPandocLua $ do
104− Lua.newtable
84+ Lua.newtable
105− zipWithM_ addEntry [1..] dirContents
85+ zipWithM_ addEntry [1..] dirContents
106 return 1
86 return 1
107 where
87 where
108 addEntry :: Lua.Integer -> (FilePath, MimeType, Int) -> Lua ()
88 addEntry :: Lua.Integer -> (FilePath, MimeType, Int) -> Lua ()
@@ -114,14 +94,9 @@ mediaDirectoryFn = do
114 Lua.rawseti (-2) idx
94 Lua.rawseti (-2) idx
115
95
116fetch :: T.Text
96fetch :: T.Text
117− -> Lua NumResults
97+ -> PandocLua NumResults
118fetch src = do
98fetch 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
99+ (bs, mimeType) <- fetchItem src
125− Lua.push $ maybe "" T.unpack mimeType
100+ liftPandocLua . Lua.push $ maybe "" T.unpack mimeType
126− Lua.push bs
101+ liftPandocLua $ Lua.push bs
127 return 2 -- returns 2 values: contents, mimetype
102 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)
24import Text.Pandoc.Definition (Block, Inline)
24import Text.Pandoc.Definition (Block, Inline)
25import Text.Pandoc.Lua.Filter (walkInlines, walkBlocks, LuaFilter, SingletonsList (..))
25import Text.Pandoc.Lua.Filter (walkInlines, walkBlocks, LuaFilter, SingletonsList (..))
26import Text.Pandoc.Lua.Marshaling ()
26import Text.Pandoc.Lua.Marshaling ()
27+import Text.Pandoc.Lua.PandocLua (PandocLua, addFunction, liftPandocLua,
28+ loadScriptFromDataDir)
27import Text.Pandoc.Walk (Walkable)
29import Text.Pandoc.Walk (Walkable)
28import Text.Pandoc.Options (ReaderOptions (readerExtensions))
30import Text.Pandoc.Options (ReaderOptions (readerExtensions))
29import Text.Pandoc.Process (pipeProcess)
31import Text.Pandoc.Process (pipeProcess)
@@ -38,28 +40,28 @@ import Text.Pandoc.Error
38
40
39-- | Push the "pandoc" on the lua stack. Requires the `list` module to be
41-- | Push the "pandoc" on the lua stack. Requires the `list` module to be
40-- loaded.
42-- loaded.
41−pushModule :: Maybe FilePath -> Lua NumResults
43+pushModule :: PandocLua NumResults
42−pushModule datadir = do
44+pushModule = do
43− LuaUtil.loadScriptFromDataDir datadir "pandoc.lua"
45+ loadScriptFromDataDir "pandoc.lua"
44− LuaUtil.addFunction "read" readDoc
46+ addFunction "read" readDoc
45− LuaUtil.addFunction "pipe" pipeFn
47+ addFunction "pipe" pipeFn
46− LuaUtil.addFunction "walk_block" walkBlock
48+ addFunction "walk_block" walkBlock
47− LuaUtil.addFunction "walk_inline" walkInline
49+ addFunction "walk_inline" walkInline
48 return 1
50 return 1
49
51
50walkElement :: (Walkable (SingletonsList Inline) a,
52walkElement :: (Walkable (SingletonsList Inline) a,
51 Walkable (SingletonsList Block) a)
53 Walkable (SingletonsList Block) a)
52− => a -> LuaFilter -> Lua a
54+ => a -> LuaFilter -> PandocLua a
53−walkElement x f = walkInlines f x >>= walkBlocks f
55+walkElement x f = liftPandocLua $ walkInlines f x >>= walkBlocks f
54
56
55−walkInline :: Inline -> LuaFilter -> Lua Inline
57+walkInline :: Inline -> LuaFilter -> PandocLua Inline
56walkInline = walkElement
58walkInline = walkElement
57
59
58−walkBlock :: Block -> LuaFilter -> Lua Block
60+walkBlock :: Block -> LuaFilter -> PandocLua Block
59walkBlock = walkElement
61walkBlock = walkElement
60
62
61−readDoc :: T.Text -> Optional T.Text -> Lua NumResults
63+readDoc :: T.Text -> Optional T.Text -> PandocLua NumResults
62−readDoc content formatSpecOrNil = do
64+readDoc content formatSpecOrNil = liftPandocLua $ do
63 let formatSpec = fromMaybe "markdown" (Lua.fromOptional formatSpecOrNil)
65 let formatSpec = fromMaybe "markdown" (Lua.fromOptional formatSpecOrNil)
64 res <- Lua.liftIO . runIO $
66 res <- Lua.liftIO . runIO $
65 getReader formatSpec >>= \(rdr,es) ->
67 getReader formatSpec >>= \(rdr,es) ->
@@ -80,8 +82,8 @@ readDoc content formatSpecOrNil = do
80pipeFn :: String
82pipeFn :: String
81 -> [String]
83 -> [String]
82 -> BL.ByteString
84 -> BL.ByteString
83− -> Lua NumResults
85+ -> PandocLua NumResults
84−pipeFn command args input = do
86+pipeFn command args input = liftPandocLua $ do
85 (ec, output) <- Lua.liftIO $ pipeProcess Nothing command args input
87 (ec, output) <- Lua.liftIO $ pipeProcess Nothing command args input
86 case ec of
88 case ec of
87 ExitSuccess -> 1 <$ Lua.push output
89 ExitSuccess -> 1 <$ Lua.push output
src/Text/Pandoc/Lua/Module/Utils.hs+15−23
@@ -18,13 +18,11 @@ import Control.Monad.Catch (try)
18import Data.Default (def)
18import Data.Default (def)
19import Data.Version (Version)
19import Data.Version (Version)
20import Foreign.Lua (Peekable, Lua, NumResults)
20import Foreign.Lua (Peekable, Lua, NumResults)
21−import Text.Pandoc.Class.PandocIO (runIO)
22−import Text.Pandoc.Class.PandocMonad (setUserDataDir)
23import Text.Pandoc.Definition ( Pandoc, Meta, MetaValue (..), Block, Inline
21import Text.Pandoc.Definition ( Pandoc, Meta, MetaValue (..), Block, Inline
24 , Citation, Attr, ListAttributes)
22 , Citation, Attr, ListAttributes)
25import Text.Pandoc.Error (PandocError)
23import Text.Pandoc.Error (PandocError)
26import Text.Pandoc.Lua.Marshaling ()
24import Text.Pandoc.Lua.Marshaling ()
27−import Text.Pandoc.Lua.Util (addFunction)
25+import Text.Pandoc.Lua.PandocLua (PandocLua, addFunction, liftPandocLua)
28
26
29import qualified Data.Digest.Pure.SHA as SHA
27import qualified Data.Digest.Pure.SHA as SHA
30import qualified Data.ByteString.Lazy as BSL
28import qualified Data.ByteString.Lazy as BSL
@@ -35,14 +33,14 @@ import qualified Text.Pandoc.Filter.JSON as JSONFilter
35import qualified Text.Pandoc.Shared as Shared
33import qualified Text.Pandoc.Shared as Shared
36
34
37-- | Push the "pandoc.utils" module to the lua stack.
35-- | Push the "pandoc.utils" module to the lua stack.
38−pushModule :: Maybe FilePath -> Lua NumResults
36+pushModule :: PandocLua NumResults
39−pushModule mbDatadir = do
37+pushModule = do
40− Lua.newtable
38+ liftPandocLua Lua.newtable
41 addFunction "blocks_to_inlines" blocksToInlines
39 addFunction "blocks_to_inlines" blocksToInlines
42 addFunction "equals" equals
40 addFunction "equals" equals
43 addFunction "make_sections" makeSections
41 addFunction "make_sections" makeSections
44 addFunction "normalize_date" normalizeDate
42 addFunction "normalize_date" normalizeDate
45− addFunction "run_json_filter" (runJSONFilter mbDatadir)
43+ addFunction "run_json_filter" runJSONFilter
46 addFunction "sha1" sha1
44 addFunction "sha1" sha1
47 addFunction "stringify" stringify
45 addFunction "stringify" stringify
48 addFunction "to_roman_numeral" toRomanNumeral
46 addFunction "to_roman_numeral" toRomanNumeral
@@ -50,8 +48,8 @@ pushModule mbDatadir = do
50 return 1
48 return 1
51
49
52-- | Squashes a list of blocks into inlines.
50-- | Squashes a list of blocks into inlines.
53−blocksToInlines :: [Block] -> Lua.Optional [Inline] -> Lua [Inline]
51+blocksToInlines :: [Block] -> Lua.Optional [Inline] -> PandocLua [Inline]
54−blocksToInlines blks optSep = do
52+blocksToInlines blks optSep = liftPandocLua $ do
55 let sep = case Lua.fromOptional optSep of
53 let sep = case Lua.fromOptional optSep of
56 Just x -> B.fromList x
54 Just x -> B.fromList x
57 Nothing -> Shared.defaultBlocksSeparator
55 Nothing -> Shared.defaultBlocksSeparator
@@ -70,23 +68,17 @@ normalizeDate :: T.Text -> Lua (Lua.Optional T.Text)
70normalizeDate = return . Lua.Optional . Shared.normalizeDate
68normalizeDate = return . Lua.Optional . Shared.normalizeDate
71
69
72-- | Run a JSON filter on the given document.
70-- | Run a JSON filter on the given document.
73−runJSONFilter :: Maybe FilePath
71+runJSONFilter :: Pandoc
74− -> Pandoc
75 -> FilePath
72 -> FilePath
76 -> Lua.Optional [String]
73 -> Lua.Optional [String]
77− -> Lua NumResults
74+ -> PandocLua Pandoc
78−runJSONFilter mbDatadir doc filterFile optArgs = do
75+runJSONFilter doc filterFile optArgs = do
79 args <- case Lua.fromOptional optArgs of
76 args <- case Lua.fromOptional optArgs of
80 Just x -> return x
77 Just x -> return x
81− Nothing -> do
78+ Nothing -> liftPandocLua $ do
82 Lua.getglobal "FORMAT"
79 Lua.getglobal "FORMAT"
83 (:[]) <$> Lua.popValue
80 (:[]) <$> Lua.popValue
84− filterRes <- Lua.liftIO . runIO $ do
85− setUserDataDir mbDatadir
86− JSONFilter.apply def args filterFile doc
81+ 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
90
82
91-- | Calculate the hash of the given contents.
83-- | Calculate the hash of the given contents.
92sha1 :: BSL.ByteString
84sha1 :: BSL.ByteString
@@ -96,7 +88,7 @@ sha1 = return . T.pack . SHA.showDigest . SHA.sha1
96-- | Convert pandoc structure to a string with formatting removed.
88-- | Convert pandoc structure to a string with formatting removed.
97-- Footnotes are skipped (since we don't want their contents in link
89-- Footnotes are skipped (since we don't want their contents in link
98-- labels).
90-- labels).
99−stringify :: AstElement -> Lua T.Text
91+stringify :: AstElement -> PandocLua T.Text
100stringify el = return $ case el of
92stringify el = return $ case el of
101 PandocElement pd -> Shared.stringify pd
93 PandocElement pd -> Shared.stringify pd
102 InlineElement i -> Shared.stringify i
94 InlineElement i -> Shared.stringify i
@@ -112,7 +104,7 @@ stringifyMetaValue mv = case mv of
112 MetaString s -> s
104 MetaString s -> s
113 _ -> Shared.stringify mv
105 _ -> Shared.stringify mv
114
106
115−equals :: AstElement -> AstElement -> Lua Bool
107+equals :: AstElement -> AstElement -> PandocLua Bool
116equals e1 e2 = return (e1 == e2)
108equals e1 e2 = return (e1 == e2)
117
109
118data AstElement
110data AstElement
@@ -141,5 +133,5 @@ instance Peekable AstElement where
141 "Expected an AST element, but could not parse value as such."
133 "Expected an AST element, but could not parse value as such."
142
134
143-- | Convert a number < 4000 to uppercase roman numeral.
135-- | Convert a number < 4000 to uppercase roman numeral.
144−toRomanNumeral :: Lua.Integer -> Lua T.Text
136+toRomanNumeral :: Lua.Integer -> PandocLua T.Text
145toRomanNumeral = return . Shared.toRomanNumeral . fromIntegral
137toRomanNumeral = return . Shared.toRomanNumeral . fromIntegral
src/Text/Pandoc/Lua/Packages.hs+23−41
@@ -8,37 +8,32 @@
8 Maintainer : Albert Krewinkel <tarleb+pandoc@moltkeplatz.de>
8 Maintainer : Albert Krewinkel <tarleb+pandoc@moltkeplatz.de>
9 Stability : alpha
9 Stability : alpha
10
10
11−Pandoc module for lua.
11+Pandoc module for Lua.
12-}
12-}
13module Text.Pandoc.Lua.Packages
13module Text.Pandoc.Lua.Packages
14− ( LuaPackageParams (..)
15− , installPandocPackageSearcher
14+ ( installPandocPackageSearcher
16 ) where
15 ) where
17
16
18import Control.Monad (forM_)
17import Control.Monad (forM_)
19import Data.ByteString (ByteString)
18import Data.ByteString (ByteString)
20−import Foreign.Lua (Lua, NumResults, liftIO)
19+import Foreign.Lua (Lua, NumResults)
21−import Text.Pandoc.Class.PandocIO (runIO)
22−import Text.Pandoc.Class.PandocMonad (readDataFile, setUserDataDir)
20+import Text.Pandoc.Class.PandocMonad (readDataFile)
21+import Text.Pandoc.Lua.PandocLua (PandocLua, liftPandocLua)
23
22
24import qualified Foreign.Lua as Lua
23import qualified Foreign.Lua as Lua
24+import qualified Foreign.Lua.Module.Text as Text
25−import Text.Pandoc.Lua.Module.Pandoc as Pandoc
25+import qualified Text.Pandoc.Lua.Module.Pandoc as Pandoc
26−import Text.Pandoc.Lua.Module.MediaBag as MediaBag
26+import qualified Text.Pandoc.Lua.Module.MediaBag as MediaBag
27−import Text.Pandoc.Lua.Module.System as System
27+import qualified Text.Pandoc.Lua.Module.System as System
28−import Text.Pandoc.Lua.Module.Types as Types
28+import qualified Text.Pandoc.Lua.Module.Types as Types
29−import Text.Pandoc.Lua.Module.Utils as Utils
29+import qualified 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− }
35
30
36-- | Insert pandoc's package loader as the first loader, making it the default.
31-- | Insert pandoc's package loader as the first loader, making it the default.
37−installPandocPackageSearcher :: LuaPackageParams -> Lua ()
32+installPandocPackageSearcher :: PandocLua ()
38−installPandocPackageSearcher luaPkgParams = do
33+installPandocPackageSearcher = liftPandocLua $ do
39 Lua.getglobal' "package.searchers"
34 Lua.getglobal' "package.searchers"
40 shiftArray
35 shiftArray
41− Lua.pushHaskellFunction (pandocPackageSearcher luaPkgParams)
36+ Lua.pushHaskellFunction pandocPackageSearcher
42 Lua.rawseti (Lua.nthFromTop 2) 1
37 Lua.rawseti (Lua.nthFromTop 2) 1
43 Lua.pop 1 -- remove 'package.searchers' from stack
38 Lua.pop 1 -- remove 'package.searchers' from stack
44 where
39 where
@@ -47,29 +42,24 @@ installPandocPackageSearcher luaPkgParams = do
47 Lua.rawseti (-2) (i + 1)
42 Lua.rawseti (-2) (i + 1)
48
43
49-- | Load a pandoc module.
44-- | Load a pandoc module.
50−pandocPackageSearcher :: LuaPackageParams -> String -> Lua NumResults
45+pandocPackageSearcher :: String -> PandocLua NumResults
51−pandocPackageSearcher pkgParams pkgName =
46+pandocPackageSearcher pkgName =
52 case pkgName of
47 case pkgName of
53− "pandoc" -> let datadir = luaPkgDataDir pkgParams
54− in pushWrappedHsFun (Pandoc.pushModule datadir)
48+ "pandoc" -> pushWrappedHsFun Pandoc.pushModule
55 "pandoc.mediabag" -> pushWrappedHsFun MediaBag.pushModule
49 "pandoc.mediabag" -> pushWrappedHsFun MediaBag.pushModule
56 "pandoc.system" -> pushWrappedHsFun System.pushModule
50 "pandoc.system" -> pushWrappedHsFun System.pushModule
57 "pandoc.types" -> pushWrappedHsFun Types.pushModule
51 "pandoc.types" -> pushWrappedHsFun Types.pushModule
58− "pandoc.utils" -> let datadir = luaPkgDataDir pkgParams
59− in pushWrappedHsFun (Utils.pushModule datadir)
52+ "pandoc.utils" -> pushWrappedHsFun Utils.pushModule
53+ "text" -> pushWrappedHsFun Text.pushModule
60− _ -> searchPureLuaLoader
54+ _ -> searchPureLuaLoader
61 where
55 where
62− pushWrappedHsFun f = do
56+ pushWrappedHsFun f = liftPandocLua $ do
63 Lua.pushHaskellFunction f
57 Lua.pushHaskellFunction f
64 return 1
58 return 1
65 searchPureLuaLoader = do
59 searchPureLuaLoader = do
66 let filename = pkgName ++ ".lua"
60 let filename = pkgName ++ ".lua"
67− modScript <- liftIO (dataDirScript (luaPkgDataDir pkgParams) filename)
61+ script <- readDataFile filename
68− case modScript of
69− Just script -> pushWrappedHsFun (loadStringAsPackage pkgName script)
62+ pushWrappedHsFun (loadStringAsPackage pkgName script)
70− Nothing -> do
71− Lua.push ("\n\tno file '" ++ filename ++ "' in pandoc's datadir")
72− return 1
73
63
74loadStringAsPackage :: String -> ByteString -> Lua NumResults
64loadStringAsPackage :: String -> ByteString -> Lua NumResults
75loadStringAsPackage pkgName script = do
65loadStringAsPackage pkgName script = do
@@ -79,11 +69,3 @@ loadStringAsPackage pkgName script = do
79 else do
69 else do
80 msg <- Lua.popValue
70 msg <- Lua.popValue
81 Lua.raiseError ("Error while loading `" <> pkgName <> "`.\n" <> msg)
71 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
19 , addFunction
19 , addFunction
20 , addValue
20 , addValue
21 , pushViaConstructor
21 , pushViaConstructor
22− , loadScriptFromDataDir
23 , defineHowTo
22 , defineHowTo
24 , throwTopMessageAsError'
23 , throwTopMessageAsError'
25 , callWithTraceback
24 , callWithTraceback
@@ -27,13 +26,11 @@ module Text.Pandoc.Lua.Util
27 ) where
26 ) where
28
27
29import Control.Monad (unless, when)
28import Control.Monad (unless, when)
29+import Data.Text (Text)
30import Foreign.Lua ( Lua, NumArgs, NumResults, Peekable, Pushable, StackIndex
30import Foreign.Lua ( Lua, NumArgs, NumResults, Peekable, Pushable, StackIndex
31 , Status, ToHaskellFunction )
31 , Status, ToHaskellFunction )
32−import Text.Pandoc.Class.PandocIO (runIOorExplode)
33−import Text.Pandoc.Class.PandocMonad (readDataFile, setUserDataDir)
34import qualified Foreign.Lua as Lua
32import qualified Foreign.Lua as Lua
35import qualified Text.Pandoc.UTF8 as UTF8
33import qualified Text.Pandoc.UTF8 as UTF8
36−import Data.Text (Text)
37
34
38-- | Get value behind key from table at given index.
35-- | Get value behind key from table at given index.
39rawField :: Peekable a => StackIndex -> String -> Lua a
36rawField :: Peekable a => StackIndex -> String -> Lua a
@@ -87,15 +84,6 @@ pushViaCall fn = pushViaCall' fn (return ()) 0
87pushViaConstructor :: PushViaCall a => String -> a
84pushViaConstructor :: PushViaCall a => String -> a
88pushViaConstructor pandocFn = pushViaCall ("pandoc." ++ pandocFn)
85pushViaConstructor pandocFn = pushViaCall ("pandoc." ++ pandocFn)
89
86
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−
99-- | Get the tag of a value. This is an optimized and specialized version of
87-- | Get the tag of a value. This is an optimized and specialized version of
100-- @Lua.getfield idx "tag"@. It only checks for the field on the table at index
88-- @Lua.getfield idx "tag"@. It only checks for the field on the table at index
101-- @idx@ and on its metatable, also ignoring any @__index@ value on the
89-- @idx@ and on its metatable, also ignoring any @__index@ value on the
@@ -144,7 +132,8 @@ pcallWithTraceback nargs nresults = do
144callWithTraceback :: NumArgs -> NumResults -> Lua ()
132callWithTraceback :: NumArgs -> NumResults -> Lua ()
145callWithTraceback nargs nresults = do
133callWithTraceback nargs nresults = do
146 result <- pcallWithTraceback nargs nresults
134 result <- pcallWithTraceback nargs nresults
135+ when (result /= Lua.OK)
147− when (result /= Lua.OK) Lua.throwTopMessage
136+ Lua.throwTopMessage
148
137
149-- | Run the given string as a Lua program, while also adding a traceback to the
138-- | Run the given string as a Lua program, while also adding a traceback to the
150-- error message if an error occurs.
139-- error message if an error occurs.