git.iko.soy/typeable/pandoc
on master help sign in
Custom writer: use init file to setup Lua interpreter

The same init file (`data/init`) that is used to setup the Lua interpreter for Lua filters is also used to setup the interpreter of custom writers.lua.

commit4c64af4407776e6ceb2fcc8a803b83568b4c1964 authorAlbert Krewinkel <albert@zeitkraut.de> dateWed 13 Dec 2017, 21:15 (9 years ago) parentf9d0e1c
6 files changed+126−69
pandoc.cabal+2−1 src/Text/Pandoc/App.hs+2−2 src/Text/Pandoc/Lua.hs+13−42 src/Text/Pandoc/Lua/Init.hs+79 src/Text/Pandoc/Writers/Custom.hs+12−11 test/Tests/Lua.hs+18−13
pandoc.cabal+2−1
@@ -523,9 +523,10 @@ library
523523 Text.Pandoc.Readers.Org.ParserState,
524524 Text.Pandoc.Readers.Org.Parsing,
525525 Text.Pandoc.Readers.Org.Shared,
526+ Text.Pandoc.Lua.Filter,
527+ Text.Pandoc.Lua.Init,
526528 Text.Pandoc.Lua.Packages,
527529 Text.Pandoc.Lua.PandocModule,
528− Text.Pandoc.Lua.Filter,
529530 Text.Pandoc.Lua.StackInstances,
530531 Text.Pandoc.Lua.Util,
531532 Text.Pandoc.CSS,
src/Text/Pandoc/App.hs+2−2
@@ -223,7 +223,7 @@ convertWithOpts opts = do
223223 if ".lua" `isSuffixOf` format
224224 -- note: use non-lowercased version writerName
225225 then return (TextWriter
226− (\o d -> liftIO $ writeCustom writerName o d)
226+ (\o d -> writeCustom writerName o d)
227227 :: Writer PandocIO, mempty)
228228 else case getWriter writerName of
229229 Left e -> E.throwIO $ PandocAppError $
@@ -846,7 +846,7 @@ applyLuaFilters :: Maybe FilePath -> [FilePath] -> String -> Pandoc
846846applyLuaFilters mbDatadir filters format d = do
847847 expandedFilters <- mapM (expandFilterPath mbDatadir) filters
848848 let go f d' = do
849− res <- runLuaFilter mbDatadir f format d'
849+ res <- runLuaFilter f format d'
850850 case res of
851851 Right x -> return x
852852 Left (LuaException s) -> E.throw (PandocFilterError f s)
src/Text/Pandoc/Lua.hs+13−42
@@ -27,43 +27,32 @@ Running pandoc Lua filters.
2727-}
2828module Text.Pandoc.Lua
2929 ( LuaException (..)
30− , LuaPackageParams (..)
31− , pushPandocModule
3230 , runLuaFilter
33− , initLuaState
34− , luaPackageParams
31+ , runPandocLua
32+ , pushPandocModule
3533 ) where
3634
3735import Control.Monad (when, (>=>))
38−import Control.Monad.Trans (MonadIO (..))
39−import Data.IORef (newIORef, readIORef)
4036import Foreign.Lua (FromLuaStack (peek), Lua, LuaException (..),
4137 Status (OK), ToLuaStack (push))
42−import Text.Pandoc.Class (PandocIO, getCommonState, getMediaBag, setMediaBag)
43−import Text.Pandoc.Definition
38+import Text.Pandoc.Class (PandocIO)
39+import Text.Pandoc.Definition (Pandoc)
4440import Text.Pandoc.Lua.Filter (LuaFilter, walkMWithLuaFilter)
45−import Text.Pandoc.Lua.Packages (LuaPackageParams (..),
46− installPandocPackageSearcher)
41+import Text.Pandoc.Lua.Init (runPandocLua)
4742import Text.Pandoc.Lua.PandocModule (pushPandocModule) -- TODO: remove
48−import Text.Pandoc.Lua.Util (loadScriptFromDataDir)
4943import qualified Foreign.Lua as Lua
50−import qualified Foreign.Lua.Module.Text as Lua
5144
52−runLuaFilter :: Maybe FilePath -> FilePath -> String
45+-- | Run the Lua filter in @filterPath@ for a transformation to target
46+-- format @format@. Pandoc uses Lua init files to setup the Lua
47+-- interpreter.
48+runLuaFilter :: FilePath -> String
5349 -> Pandoc -> PandocIO (Either LuaException Pandoc)
54−runLuaFilter datadir filterPath format pd = do
55− luaPkgParams <- luaPackageParams datadir
56− res <- liftIO . Lua.runLuaEither $
57− runLuaFilter' luaPkgParams filterPath format pd
58− newMediaBag <- liftIO (readIORef (luaPkgMediaBag luaPkgParams))
59− setMediaBag newMediaBag
60− return res
50+runLuaFilter filterPath format doc =
51+ runPandocLua (runLuaFilter' filterPath format doc)
6152
62−runLuaFilter' :: LuaPackageParams
63− -> FilePath -> String
53+runLuaFilter' :: FilePath -> String
6454 -> Pandoc -> Lua Pandoc
65−runLuaFilter' luaPkgOpts filterPath format pd = do
66− initLuaState luaPkgOpts
55+runLuaFilter' filterPath format pd = do
6756 -- store module in global "pandoc"
6857 registerFormat
6958 top <- Lua.gettop
@@ -83,24 +72,6 @@ runLuaFilter' luaPkgOpts filterPath format pd = do
8372 push format
8473 Lua.setglobal "FORMAT"
8574
86−luaPackageParams :: Maybe FilePath -> PandocIO LuaPackageParams
87−luaPackageParams datadir = do
88− commonState <- getCommonState
89− mbRef <- liftIO . newIORef =<< getMediaBag
90− return LuaPackageParams
91− { luaPkgCommonState = commonState
92− , luaPkgDataDir = datadir
93− , luaPkgMediaBag = mbRef
94− }
95−
96−-- Initialize the lua state with all required values
97−initLuaState :: LuaPackageParams -> Lua ()
98−initLuaState luaPkgParams = do
99− Lua.openlibs
100− Lua.preloadTextModule "text"
101− installPandocPackageSearcher luaPkgParams
102− loadScriptFromDataDir (luaPkgDataDir luaPkgParams) "init.lua"
103−
10475pushGlobalFilter :: Lua ()
10576pushGlobalFilter = do
10677 Lua.newtable
src/Text/Pandoc/Lua/Init.hsnew file+79
@@ -0,0 +1,79 @@
1+{-
2+Copyright © 2017 Albert Krewinkel <tarleb+pandoc@moltkeplatz.de>
3+
4+This program is free software; you can redistribute it and/or modify
5+it under the terms of the GNU General Public License as published by
6+the Free Software Foundation; either version 2 of the License, or
7+(at your option) any later version.
8+
9+This program is distributed in the hope that it will be useful,
10+but WITHOUT ANY WARRANTY; without even the implied warranty of
11+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+GNU General Public License for more details.
13+
14+You should have received a copy of the GNU General Public License
15+along with this program; if not, write to the Free Software
16+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17+-}
18+{- |
19+ Module : Text.Pandoc.Lua
20+ Copyright : Copyright © 2017 Albert Krewinkel
21+ License : GNU GPL, version 2 or above
22+
23+ Maintainer : Albert Krewinkel <tarleb+pandoc@moltkeplatz.de>
24+ Stability : alpha
25+
26+Functions to initialize the Lua interpreter.
27+-}
28+module Text.Pandoc.Lua.Init
29+ ( LuaException (..)
30+ , LuaPackageParams (..)
31+ , runPandocLua
32+ , initLuaState
33+ , luaPackageParams
34+ ) where
35+
36+import Control.Monad.Trans (MonadIO (..))
37+import Data.IORef (newIORef, readIORef)
38+import Foreign.Lua (Lua, LuaException (..))
39+import GHC.IO.Encoding (getForeignEncoding, setForeignEncoding, utf8)
40+import Text.Pandoc.Class (PandocIO, getCommonState, getUserDataDir, getMediaBag,
41+ setMediaBag)
42+import Text.Pandoc.Lua.Packages (LuaPackageParams (..),
43+ installPandocPackageSearcher)
44+import Text.Pandoc.Lua.Util (loadScriptFromDataDir)
45+
46+import qualified Foreign.Lua as Lua
47+import qualified Foreign.Lua.Module.Text as Lua
48+
49+-- | Run the lua interpreter, using pandoc's default way of environment
50+-- initalization.
51+runPandocLua :: Lua a -> PandocIO (Either LuaException a)
52+runPandocLua luaOp = do
53+ datadir <- getUserDataDir
54+ luaPkgParams <- luaPackageParams datadir
55+ enc <- liftIO $ getForeignEncoding <* setForeignEncoding utf8
56+ res <- liftIO $ Lua.runLuaEither (initLuaState luaPkgParams *> luaOp)
57+ liftIO $ setForeignEncoding enc
58+ newMediaBag <- liftIO (readIORef (luaPkgMediaBag luaPkgParams))
59+ setMediaBag newMediaBag
60+ return res
61+
62+-- | Generate parameters required to setup pandoc's lua environment.
63+luaPackageParams :: Maybe FilePath -> PandocIO LuaPackageParams
64+luaPackageParams datadir = do
65+ commonState <- getCommonState
66+ mbRef <- liftIO . newIORef =<< getMediaBag
67+ return LuaPackageParams
68+ { luaPkgCommonState = commonState
69+ , luaPkgDataDir = datadir
70+ , luaPkgMediaBag = mbRef
71+ }
72+
73+-- Initialize the lua state with all required values
74+initLuaState :: LuaPackageParams -> Lua ()
75+initLuaState luaPkgParams = do
76+ Lua.openlibs
77+ Lua.preloadTextModule "text"
78+ installPandocPackageSearcher luaPkgParams
79+ loadScriptFromDataDir (luaPkgDataDir luaPkgParams) "init.lua"
src/Text/Pandoc/Writers/Custom.hs+12−11
@@ -33,18 +33,20 @@ module Text.Pandoc.Writers.Custom ( writeCustom ) where
3333import Control.Arrow ((***))
3434import Control.Exception
3535import Control.Monad (when)
36+import Control.Monad.Trans (MonadIO (liftIO))
3637import Data.Char (toLower)
3738import Data.List (intersperse)
3839import qualified Data.Map as M
3940import Data.Text (Text, pack)
4041import Data.Typeable
41−import Foreign.Lua (Lua, ToLuaStack (..), callFunc, runLua)
42+import Foreign.Lua (Lua, ToLuaStack (..), callFunc)
4243import Foreign.Lua.Api
43−import GHC.IO.Encoding (getForeignEncoding, setForeignEncoding, utf8)
44+import Text.Pandoc.Class (PandocIO)
4445import Text.Pandoc.Definition
4546import Text.Pandoc.Error
47+import Text.Pandoc.Lua.Init (runPandocLua)
4648import Text.Pandoc.Lua.StackInstances ()
47−import Text.Pandoc.Lua.Util (addValue)
49+import Text.Pandoc.Lua.Util (addValue, dostring')
4850import Text.Pandoc.Options
4951import Text.Pandoc.Templates
5052import qualified Text.Pandoc.UTF8 as UTF8
@@ -91,14 +93,11 @@ data PandocLuaException = PandocLuaException String
9193instance Exception PandocLuaException
9294
9395-- | Convert Pandoc to custom markup.
94−writeCustom :: FilePath -> WriterOptions -> Pandoc -> IO Text
96+writeCustom :: FilePath -> WriterOptions -> Pandoc -> PandocIO Text
9597writeCustom luaFile opts doc@(Pandoc meta _) = do
96− luaScript <- UTF8.readFile luaFile
97− enc <- getForeignEncoding
98− setForeignEncoding utf8
99− (body, context) <- runLua $ do
100− openlibs
101− stat <- loadstring luaScript
98+ luaScript <- liftIO $ UTF8.readFile luaFile
99+ res <- runPandocLua $ do
100+ stat <- dostring' luaScript
102101 -- check for error in lua script (later we'll change the return type
103102 -- to handle this more gracefully):
104103 when (stat /= OK) $
@@ -111,7 +110,9 @@ writeCustom luaFile opts doc@(Pandoc meta _) = do
111110 inlineListToCustom
112111 meta
113112 return (rendered, context)
114− setForeignEncoding enc
113+ let (body, context) = case res of
114+ Left e -> throw (PandocLuaException (show e))
115+ Right x -> x
115116 case writerTemplate opts of
116117 Nothing -> return $ pack body
117118 Just tpl ->
test/Tests/Lua.hs+18−13
@@ -10,9 +10,9 @@ import Text.Pandoc.Arbitrary ()
1010import Text.Pandoc.Builder (bulletList, divWith, doc, doubleQuoted, emph,
1111 header, linebreak, para, plain, rawBlock,
1212 singleQuoted, space, str, strong, (<>))
13−import Text.Pandoc.Class (runIOorExplode)
13+import Text.Pandoc.Class (runIOorExplode, setUserDataDir)
1414import Text.Pandoc.Definition (Block, Inline, Meta, Pandoc)
15−import Text.Pandoc.Lua (initLuaState, runLuaFilter, luaPackageParams)
15+import Text.Pandoc.Lua (runLuaFilter, runPandocLua)
1616
1717import qualified Foreign.Lua as Lua
1818
@@ -95,8 +95,9 @@ tests = map (localOption (QuickCheckTests 20))
9595
9696assertFilterConversion :: String -> FilePath -> Pandoc -> Pandoc -> Assertion
9797assertFilterConversion msg filterPath docIn docExpected = do
98− docEither <- runIOorExplode $
99− runLuaFilter (Just "../data") ("lua" </> filterPath) [] docIn
98+ docEither <- runIOorExplode $ do
99+ setUserDataDir (Just "../data")
100+ runLuaFilter ("lua" </> filterPath) [] docIn
100101 case docEither of
101102 Left _ -> fail "lua filter failed"
102103 Right docRes -> assertEqual msg docExpected docRes
@@ -105,14 +106,18 @@ roundtripEqual :: (Eq a, Lua.FromLuaStack a, Lua.ToLuaStack a) => a -> IO Bool
105106roundtripEqual x = (x ==) <$> roundtripped
106107 where
107108 roundtripped :: (Lua.FromLuaStack a, Lua.ToLuaStack a) => IO a
108− roundtripped = Lua.runLua $ do
109− initLuaState =<< Lua.liftIO (runIOorExplode (luaPackageParams (Just "../data")))
110− oldSize <- Lua.gettop
111− Lua.push x
112− size <- Lua.gettop
113− when (size - oldSize /= 1) $
114− error ("not exactly one additional element on the stack: " ++ show size)
115− res <- Lua.peekEither (-1)
109+ roundtripped = runIOorExplode $ do
110+ setUserDataDir (Just "../data")
111+ res <- runPandocLua $ do
112+ oldSize <- Lua.gettop
113+ Lua.push x
114+ size <- Lua.gettop
115+ when (size - oldSize /= 1) $
116+ error ("not exactly one additional element on the stack: " ++ show size)
117+ res <- Lua.peekEither (-1)
118+ case res of
119+ Left _ -> error "could not read from stack"
120+ Right y -> return y
116121 case res of
117− Left _ -> error "could not read from stack"
122+ Left e -> error (show e)
118123 Right y -> return y