git.iko.soy/typeable/pandoc
on master help sign in
API change: use PandocError for exceptions in Lua subsystem

The PandocError type is used throughout the Lua subsystem, all Lua functions throw an exception of this type if an error occurs. The `LuaException` type is removed and no longer exported from `Text.Pandoc.Lua`. In its place, a new constructor `PandocLuaError` is added to PandocError.

commitfb54f3d6792d2f8e7b05e458b59142f8ae6bb3e2 authorAlbert Krewinkel <albert@zeitkraut.de> dateWed 25 Mar 2020, 22:16 (7 years ago) parent2877ca7
17 files changed+209−74
pandoc.cabal+6−3 src/Text/Pandoc/Error.hs+2 src/Text/Pandoc/Filter/Lua.hs+4−5 src/Text/Pandoc/Lua.hs+1−2 src/Text/Pandoc/Lua/ErrorConversion.hs+61 src/Text/Pandoc/Lua/Filter.hs+13−7 src/Text/Pandoc/Lua/Init.hs+7−9 src/Text/Pandoc/Lua/Marshaling.hs+2−1 src/Text/Pandoc/Lua/Marshaling/AST.hs+10−5 src/Text/Pandoc/Lua/Marshaling/PandocError.hs+65 src/Text/Pandoc/Lua/Marshaling/Version.hs+2−2 src/Text/Pandoc/Lua/Module/Utils.hs+11−8 src/Text/Pandoc/Lua/Util.hs+4−3 src/Text/Pandoc/Writers/Custom.hs+9−18 stack.yaml+1 test/Tests/Lua.hs+8−7 test/lua/module/pandoc-types.lua+3−4
pandoc.cabal+6−3
@@ -412,9 +412,9 @@ library
412412 blaze-markup >= 0.8 && < 0.9,
413413 vector >= 0.10 && < 0.13,
414414 jira-wiki-markup >= 1.3 && < 1.4,
415− hslua >= 1.0.1 && < 1.2,
415+ hslua >= 1.1 && < 1.2,
416416 hslua-module-system >= 0.2 && < 0.3,
417− hslua-module-text >= 0.2 && < 0.3,
417+ hslua-module-text >= 0.2.1 && < 0.3,
418418 binary >= 0.5 && < 0.11,
419419 SHA >= 1.6 && < 1.7,
420420 haddock-library >= 1.8 && < 1.10,
@@ -611,6 +611,7 @@ library
611611 Text.Pandoc.Writers.Roff,
612612 Text.Pandoc.Writers.Powerpoint.Presentation,
613613 Text.Pandoc.Writers.Powerpoint.Output,
614+ Text.Pandoc.Lua.ErrorConversion,
614615 Text.Pandoc.Lua.Filter,
615616 Text.Pandoc.Lua.Global,
616617 Text.Pandoc.Lua.Init,
@@ -621,6 +622,7 @@ library
621622 Text.Pandoc.Lua.Marshaling.Context,
622623 Text.Pandoc.Lua.Marshaling.List,
623624 Text.Pandoc.Lua.Marshaling.MediaBag,
625+ Text.Pandoc.Lua.Marshaling.PandocError,
624626 Text.Pandoc.Lua.Marshaling.ReaderOptions,
625627 Text.Pandoc.Lua.Marshaling.Version,
626628 Text.Pandoc.Lua.Module.MediaBag,
@@ -736,8 +738,9 @@ test-suite test-pandoc
736738 text >= 1.1.1.0 && < 1.3,
737739 time >= 1.5 && < 1.10,
738740 directory >= 1.2.3 && < 1.4,
741+ exceptions >= 0.8 && < 0.11,
739742 filepath >= 1.1 && < 1.5,
740− hslua >= 1.0 && < 1.2,
743+ hslua >= 1.1 && < 1.2,
741744 process >= 1.2.3 && < 1.7,
742745 temporary >= 1.1 && < 1.4,
743746 Diff >= 0.2 && < 0.5,
src/Text/Pandoc/Error.hs+2
@@ -47,6 +47,7 @@ data PandocError = PandocIOError Text IOError
4747 | PandocPDFProgramNotFoundError Text
4848 | PandocPDFError Text
4949 | PandocFilterError Text Text
50+ | PandocLuaError Text
5051 | PandocCouldNotFindDataFileError Text
5152 | PandocResourceNotFound Text
5253 | PandocTemplateError Text
@@ -100,6 +101,7 @@ handleError (Left e) =
100101 PandocPDFError logmsg -> err 43 $ "Error producing PDF.\n" <> logmsg
101102 PandocFilterError filtername msg -> err 83 $ "Error running filter " <>
102103 filtername <> ":\n" <> msg
104+ PandocLuaError msg -> err 84 $ "Error running Lua:\n" <> msg
103105 PandocCouldNotFindDataFileError fn -> err 97 $
104106 "Could not find data file " <> fn
105107 PandocResourceNotFound fn -> err 99 $
src/Text/Pandoc/Filter/Lua.hs+4−5
@@ -17,8 +17,7 @@ import qualified Data.Text as T
1717import Text.Pandoc.Class.PandocIO (PandocIO)
1818import Text.Pandoc.Definition (Pandoc)
1919import Text.Pandoc.Error (PandocError (PandocFilterError))
20−import Text.Pandoc.Lua (Global (..), LuaException (..),
21− runLua, runFilterFile, setGlobals)
20+import Text.Pandoc.Lua (Global (..), runLua, runFilterFile, setGlobals)
2221import Text.Pandoc.Options (ReaderOptions)
2322
2423-- | Run the Lua filter in @filterPath@ for a transformation to the
@@ -40,7 +39,7 @@ apply ropts args fp doc = do
4039 ]
4140 runFilterFile fp doc
4241
43−forceResult :: FilePath -> Either LuaException Pandoc -> PandocIO Pandoc
42+forceResult :: FilePath -> Either PandocError Pandoc -> PandocIO Pandoc
4443forceResult fp eitherResult = case eitherResult of
45− Right x -> return x
46− Left (LuaException s) -> throw (PandocFilterError (T.pack fp) s)
44+ Right x -> return x
45+ Left err -> throw (PandocFilterError (T.pack fp) (T.pack $ show err))
src/Text/Pandoc/Lua.hs+1−2
@@ -10,7 +10,6 @@ Running pandoc Lua filters.
1010-}
1111module Text.Pandoc.Lua
1212 ( runLua
13− , LuaException (..)
1413 -- * Lua globals
1514 , Global (..)
1615 , setGlobals
@@ -20,5 +19,5 @@ module Text.Pandoc.Lua
2019
2120import Text.Pandoc.Lua.Filter (runFilterFile)
2221import Text.Pandoc.Lua.Global (Global (..), setGlobals)
23−import Text.Pandoc.Lua.Init (LuaException (..), runLua)
22+import Text.Pandoc.Lua.Init (runLua)
2423import Text.Pandoc.Lua.Marshaling ()
src/Text/Pandoc/Lua/ErrorConversion.hsnew file+61
@@ -0,0 +1,61 @@
1+{-# LANGUAGE LambdaCase #-}
2+{-# LANGUAGE RankNTypes #-}
3+{-# LANGUAGE ScopedTypeVariables #-}
4+{- |
5+ Module : Text.Pandoc.Lua.ErrorConversion
6+ Copyright : © 2020 Albert Krewinkel
7+ License : GNU GPL, version 2 or above
8+
9+ Maintainer : Albert Krewinkel <tarleb+pandoc@moltkeplatz.de>
10+ Stability : alpha
11+
12+Define how Lua errors are converted into @'PandocError'@ Haskell
13+exceptions, and /vice versa/.
14+-}
15+module Text.Pandoc.Lua.ErrorConversion
16+ ( errorConversion
17+ ) where
18+
19+import Foreign.Lua (Lua (..), NumResults)
20+import Text.Pandoc.Error (PandocError (PandocLuaError))
21+import Text.Pandoc.Lua.Marshaling.PandocError (pushPandocError, peekPandocError)
22+
23+import qualified Control.Monad.Catch as Catch
24+import qualified Data.Text as T
25+import qualified Foreign.Lua as Lua
26+
27+-- | Conversions between Lua errors and Haskell exceptions, assuming
28+-- that all exceptions are of type @'PandocError'@.
29+errorConversion :: Lua.ErrorConversion
30+errorConversion = Lua.ErrorConversion
31+ { Lua.addContextToException = addContextToException
32+ , Lua.alternative = alternative
33+ , Lua.errorToException = errorToException
34+ , Lua.exceptionToError = exceptionToError
35+ }
36+
37+-- | Convert a Lua error, which must be at the top of the stack, into a
38+-- @'PandocError'@, popping the value from the stack.
39+errorToException :: forall a . Lua.State -> IO a
40+errorToException l = Lua.unsafeRunWith l $ do
41+ err <- peekPandocError Lua.stackTop
42+ Lua.pop 1
43+ Catch.throwM err
44+
45+-- | Try the first op -- if it doesn't succeed, run the second.
46+alternative :: forall a . Lua a -> Lua a -> Lua a
47+alternative x y = Catch.try x >>= \case
48+ Left (_ :: PandocError) -> y
49+ Right x' -> return x'
50+
51+-- | Add more context to an error
52+addContextToException :: forall a . String -> Lua a -> Lua a
53+addContextToException ctx op = op `Catch.catch` \case
54+ PandocLuaError msg -> Catch.throwM $ PandocLuaError (T.pack ctx <> msg)
55+ e -> Catch.throwM e
56+
57+-- | Catch a @'PandocError'@ exception and raise it as a Lua error.
58+exceptionToError :: Lua NumResults -> Lua NumResults
59+exceptionToError op = op `Catch.catch` \e -> do
60+ pushPandocError e
61+ Lua.error
src/Text/Pandoc/Lua/Filter.hs+13−7
@@ -18,14 +18,15 @@ module Text.Pandoc.Lua.Filter ( LuaFilterFunction
1818 ) where
1919import Control.Applicative ((<|>))
2020import Control.Monad (mplus, (>=>))
21−import Control.Monad.Catch (finally)
21+import Control.Monad.Catch (finally, try)
2222import Data.Data (Data, DataType, dataTypeConstrs, dataTypeName, dataTypeOf,
2323 showConstr, toConstr, tyconUQname)
2424import Data.Foldable (foldrM)
2525import Data.Map (Map)
2626import Data.Maybe (fromMaybe)
27−import Foreign.Lua (Lua, Peekable, Pushable)
27+import Foreign.Lua (Lua, Peekable, Pushable, StackIndex)
2828import Text.Pandoc.Definition
29+import Text.Pandoc.Error (PandocError)
2930import Text.Pandoc.Lua.Marshaling ()
3031import Text.Pandoc.Lua.Marshaling.List (List (..))
3132import Text.Pandoc.Lua.Walk (SingletonsList (..))
@@ -102,7 +103,7 @@ elementOrList x = do
102103 if elementUnchanged
103104 then [x] <$ Lua.pop 1
104105 else do
105− mbres <- Lua.peekEither topOfStack
106+ mbres <- peekEither topOfStack
106107 case mbres of
107108 Right res -> [res] <$ Lua.pop 1
108109 Left _ -> Lua.peekList topOfStack `finally` Lua.pop 1
@@ -234,11 +235,16 @@ singleElement x = do
234235 if elementUnchanged
235236 then x <$ Lua.pop 1
236237 else do
237− mbres <- Lua.peekEither (-1)
238+ mbres <- peekEither (-1)
238239 case mbres of
239240 Right res -> res <$ Lua.pop 1
240241 Left err -> do
241242 Lua.pop 1
242− Lua.throwException $
243− "Error while trying to get a filter's return " ++
244− "value from lua stack.\n" ++ err
243+ Lua.throwMessage
244+ ("Error while trying to get a filter's return " <>
245+ "value from Lua stack.\n" <> show err)
246+
247+-- | Try to convert the value at the given stack index to a Haskell value.
248+-- Returns @Left@ with an error message on failure.
249+peekEither :: Peekable a => StackIndex -> Lua (Either PandocError a)
250+peekEither = try . Lua.peek
src/Text/Pandoc/Lua/Init.hs+7−9
@@ -9,12 +9,12 @@
99Functions to initialize the Lua interpreter.
1010-}
1111module Text.Pandoc.Lua.Init
12− ( LuaException (..)
13− , LuaPackageParams (..)
12+ ( LuaPackageParams (..)
1413 , runLua
1514 , luaPackageParams
1615 ) where
1716
17+import Control.Monad.Catch (try)
1818import Control.Monad.Trans (MonadIO (..))
1919import Data.Data (Data, dataTypeConstrs, dataTypeOf, showConstr)
2020import Foreign.Lua (Lua)
@@ -22,28 +22,26 @@ import GHC.IO.Encoding (getForeignEncoding, setForeignEncoding, utf8)
2222import Text.Pandoc.Class.PandocIO (PandocIO)
2323import Text.Pandoc.Class.PandocMonad (getCommonState, getUserDataDir,
2424 putCommonState)
25+import Text.Pandoc.Error (PandocError)
26+import Text.Pandoc.Lua.ErrorConversion (errorConversion)
2527import Text.Pandoc.Lua.Global (Global (..), setGlobals)
2628import Text.Pandoc.Lua.Packages (LuaPackageParams (..),
2729 installPandocPackageSearcher)
2830import Text.Pandoc.Lua.Util (loadScriptFromDataDir)
2931
30−import qualified Data.Text as Text
3132import qualified Foreign.Lua as Lua
3233import qualified Foreign.Lua.Module.Text as Lua
3334import qualified Text.Pandoc.Definition as Pandoc
3435import qualified Text.Pandoc.Lua.Module.Pandoc as ModulePandoc
3536
36−-- | Lua error message
37−newtype LuaException = LuaException Text.Text deriving (Show)
38−
3937-- | Run the lua interpreter, using pandoc's default way of environment
4038-- initialization.
41−runLua :: Lua a -> PandocIO (Either LuaException a)
39+runLua :: Lua a -> PandocIO (Either PandocError a)
4240runLua luaOp = do
4341 luaPkgParams <- luaPackageParams
4442 globals <- defaultGlobals
4543 enc <- liftIO $ getForeignEncoding <* setForeignEncoding utf8
46− res <- liftIO . Lua.runEither $ do
44+ res <- liftIO . try . Lua.run' errorConversion $ do
4745 setGlobals globals
4846 initLuaState luaPkgParams
4947 -- run the given Lua operation
@@ -56,7 +54,7 @@ runLua luaOp = do
5654 return (opResult, st)
5755 liftIO $ setForeignEncoding enc
5856 case res of
59− Left (Lua.Exception msg) -> return $ Left (LuaException $ Text.pack msg)
57+ Left err -> return $ Left err
6058 Right (x, newState) -> do
6159 putCommonState newState
6260 return $ Right x
src/Text/Pandoc/Lua/Marshaling.hs+2−1
@@ -13,6 +13,7 @@ module Text.Pandoc.Lua.Marshaling () where
1313
1414import Text.Pandoc.Lua.Marshaling.AST ()
1515import Text.Pandoc.Lua.Marshaling.CommonState ()
16−import Text.Pandoc.Lua.Marshaling.ReaderOptions ()
1716import Text.Pandoc.Lua.Marshaling.Context ()
17+import Text.Pandoc.Lua.Marshaling.PandocError()
18+import Text.Pandoc.Lua.Marshaling.ReaderOptions ()
1819import Text.Pandoc.Lua.Marshaling.Version ()
src/Text/Pandoc/Lua/Marshaling/AST.hs+10−5
@@ -19,9 +19,11 @@ module Text.Pandoc.Lua.Marshaling.AST
1919import Control.Applicative ((<|>))
2020import Foreign.Lua (Lua, Peekable, Pushable, StackIndex)
2121import Text.Pandoc.Definition
22+import Text.Pandoc.Error (PandocError)
2223import Text.Pandoc.Lua.Util (defineHowTo, pushViaConstructor)
2324import Text.Pandoc.Lua.Marshaling.CommonState ()
2425
26+import qualified Control.Monad.Catch as Catch
2527import qualified Foreign.Lua as Lua
2628import qualified Text.Pandoc.Lua.Util as LuaUtil
2729
@@ -131,7 +133,7 @@ peekMetaValue idx = defineHowTo "get MetaValue" $ do
131133 Lua.TypeBoolean -> MetaBool <$> Lua.peek idx
132134 Lua.TypeString -> MetaString <$> Lua.peek idx
133135 Lua.TypeTable -> do
134− tag <- Lua.try $ LuaUtil.getTag idx
136+ tag <- try $ LuaUtil.getTag idx
135137 case tag of
136138 Right "MetaBlocks" -> MetaBlocks <$> elementContent
137139 Right "MetaBool" -> MetaBool <$> elementContent
@@ -139,7 +141,7 @@ peekMetaValue idx = defineHowTo "get MetaValue" $ do
139141 Right "MetaInlines" -> MetaInlines <$> elementContent
140142 Right "MetaList" -> MetaList <$> elementContent
141143 Right "MetaString" -> MetaString <$> elementContent
142− Right t -> Lua.throwException ("Unknown meta tag: " <> t)
144+ Right t -> Lua.throwMessage ("Unknown meta tag: " <> t)
143145 Left _ -> do
144146 -- no meta value tag given, try to guess.
145147 len <- Lua.rawlen idx
@@ -148,7 +150,7 @@ peekMetaValue idx = defineHowTo "get MetaValue" $ do
148150 else (MetaInlines <$> Lua.peek idx)
149151 <|> (MetaBlocks <$> Lua.peek idx)
150152 <|> (MetaList <$> Lua.peek idx)
151− _ -> Lua.throwException "could not get meta value"
153+ _ -> Lua.throwMessage "could not get meta value"
152154
153155-- | Push a block element to the top of the Lua stack.
154156pushBlock :: Block -> Lua ()
@@ -199,7 +201,7 @@ peekBlock idx = defineHowTo "get Block value" $ do
199201 tbodies
200202 tfoot)
201203 <$> elementContent
202− _ -> Lua.throwException ("Unknown block type: " <> tag)
204+ _ -> Lua.throwMessage ("Unknown block type: " <> tag)
203205 where
204206 -- Get the contents of an AST element.
205207 elementContent :: Peekable a => Lua a
@@ -344,12 +346,15 @@ peekInline idx = defineHowTo "get Inline value" $ do
344346 "Strong" -> Strong <$> elementContent
345347 "Subscript" -> Subscript <$> elementContent
346348 "Superscript"-> Superscript <$> elementContent
347− _ -> Lua.throwException ("Unknown inline type: " <> tag)
349+ _ -> Lua.throwMessage ("Unknown inline type: " <> tag)
348350 where
349351 -- Get the contents of an AST element.
350352 elementContent :: Peekable a => Lua a
351353 elementContent = LuaUtil.rawField idx "c"
352354
355+try :: Lua a -> Lua (Either PandocError a)
356+try = Catch.try
357+
353358withAttr :: (Attr -> a -> b) -> (LuaAttr, a) -> b
354359withAttr f (attributes, x) = f (fromLuaAttr attributes) x
355360
src/Text/Pandoc/Lua/Marshaling/PandocError.hsnew file+65
@@ -0,0 +1,65 @@
1+{-# OPTIONS_GHC -fno-warn-orphans #-}
2+{-# LANGUAGE LambdaCase #-}
3+{-# LANGUAGE OverloadedStrings #-}
4+{-# LANGUAGE ScopedTypeVariables #-}
5+{- |
6+ Module : Text.Pandoc.Lua.Marshaling.PandocError
7+ Copyright : © 2020 Albert Krewinkel
8+ License : GNU GPL, version 2 or above
9+
10+ Maintainer : Albert Krewinkel <tarleb+pandoc@moltkeplatz.de>
11+ Stability : alpha
12+
13+Marshaling of @'PandocError'@ values.
14+-}
15+module Text.Pandoc.Lua.Marshaling.PandocError
16+ ( peekPandocError
17+ , pushPandocError
18+ )
19+ where
20+
21+import Foreign.Lua (Lua, Peekable, Pushable, StackIndex)
22+import Text.Pandoc.Error (PandocError (PandocLuaError))
23+
24+import qualified Foreign.Lua as Lua
25+import qualified Foreign.Lua.Userdata as Lua
26+import qualified Text.Pandoc.Lua.Util as LuaUtil
27+import qualified Text.Pandoc.UTF8 as UTF8
28+
29+-- | Userdata name used by Lua for the @PandocError@ type.
30+pandocErrorName :: String
31+pandocErrorName = "pandoc error"
32+
33+-- | Peek a @'PandocError'@ element to the Lua stack.
34+pushPandocError :: PandocError -> Lua ()
35+pushPandocError = Lua.pushAnyWithMetatable pushPandocErrorMT
36+ where
37+ pushPandocErrorMT = Lua.ensureUserdataMetatable pandocErrorName $
38+ LuaUtil.addFunction "__tostring" __tostring
39+
40+-- | Retrieve a @'PandocError'@ from the Lua stack.
41+peekPandocError :: StackIndex -> Lua PandocError
42+peekPandocError idx = Lua.ltype idx >>= \case
43+ Lua.TypeUserdata -> do
44+ errMb <- Lua.toAnyWithName idx pandocErrorName
45+ return $ case errMb of
46+ Just err -> err
47+ Nothing -> PandocLuaError "could not retrieve original error"
48+ _ -> do
49+ Lua.pushvalue idx
50+ msg <- Lua.state >>= \l -> Lua.liftIO (Lua.errorMessage l)
51+ return $ PandocLuaError (UTF8.toText msg)
52+
53+-- | Convert to string.
54+__tostring :: PandocError -> Lua String
55+__tostring = return . show
56+
57+--
58+-- Instances
59+--
60+
61+instance Pushable PandocError where
62+ push = pushPandocError
63+
64+instance Peekable PandocError where
65+ peek = peekPandocError
src/Text/Pandoc/Lua/Marshaling/Version.hs+2−2
@@ -57,7 +57,7 @@ peekVersion idx = Lua.ltype idx >>= \case
5757 let parses = readP_to_S parseVersion versionStr
5858 case lastMay parses of
5959 Just (v, "") -> return v
60− _ -> Lua.throwException $ "could not parse as Version: " ++ versionStr
60+ _ -> Lua.throwMessage $ "could not parse as Version: " ++ versionStr
6161
6262 Lua.TypeUserdata ->
6363 reportValueOnFailure versionTypeName
@@ -71,7 +71,7 @@ peekVersion idx = Lua.ltype idx >>= \case
7171 makeVersion <$> Lua.peek idx
7272
7373 _ ->
74− Lua.throwException "could not peek Version"
74+ Lua.throwMessage "could not peek Version"
7575
7676instance Peekable Version where
7777 peek = peekVersion
src/Text/Pandoc/Lua/Module/Utils.hs+11−8
@@ -1,3 +1,4 @@
1+{-# LANGUAGE ScopedTypeVariables #-}
12{- |
23 Module : Text.Pandoc.Lua.Module.Utils
34 Copyright : Copyright © 2017-2020 Albert Krewinkel
@@ -13,6 +14,7 @@ module Text.Pandoc.Lua.Module.Utils
1314 ) where
1415
1516import Control.Applicative ((<|>))
17+import Control.Monad.Catch (try)
1618import Data.Default (def)
1719import Data.Version (Version)
1820import Foreign.Lua (Peekable, Lua, NumResults)
@@ -20,6 +22,7 @@ import Text.Pandoc.Class.PandocIO (runIO)
2022import Text.Pandoc.Class.PandocMonad (setUserDataDir)
2123import Text.Pandoc.Definition ( Pandoc, Meta, MetaValue (..), Block, Inline
2224 , Citation, Attr, ListAttributes)
25+import Text.Pandoc.Error (PandocError)
2326import Text.Pandoc.Lua.Marshaling ()
2427import Text.Pandoc.Lua.Util (addFunction)
2528
@@ -125,16 +128,16 @@ data AstElement
125128
126129instance Peekable AstElement where
127130 peek idx = do
128− res <- Lua.try $ (PandocElement <$> Lua.peek idx)
129− <|> (InlineElement <$> Lua.peek idx)
130− <|> (BlockElement <$> Lua.peek idx)
131− <|> (AttrElement <$> Lua.peek idx)
132− <|> (ListAttributesElement <$> Lua.peek idx)
133− <|> (MetaElement <$> Lua.peek idx)
134− <|> (MetaValueElement <$> Lua.peek idx)
131+ res <- try $ (PandocElement <$> Lua.peek idx)
132+ <|> (InlineElement <$> Lua.peek idx)
133+ <|> (BlockElement <$> Lua.peek idx)
134+ <|> (AttrElement <$> Lua.peek idx)
135+ <|> (ListAttributesElement <$> Lua.peek idx)
136+ <|> (MetaElement <$> Lua.peek idx)
137+ <|> (MetaValueElement <$> Lua.peek idx)
135138 case res of
136139 Right x -> return x
137− Left _ -> Lua.throwException
140+ Left (_ :: PandocError) -> Lua.throwMessage
138141 "Expected an AST element, but could not parse value as such."
139142
140143-- | Convert a number < 4000 to uppercase roman numeral.
src/Text/Pandoc/Lua/Util.hs+4−3
@@ -107,7 +107,7 @@ getTag idx = do
107107 Lua.push ("tag" :: Text)
108108 Lua.rawget (Lua.nthFromTop 2)
109109 Lua.tostring Lua.stackTop <* Lua.pop 2 >>= \case
110− Nothing -> Lua.throwException "untagged value"
110+ Nothing -> Lua.throwMessage "untagged value"
111111 Just x -> return (UTF8.toString x)
112112
113113-- | Modify the message at the top of the stack before throwing it as an
@@ -116,11 +116,12 @@ throwTopMessageAsError' :: (String -> String) -> Lua a
116116throwTopMessageAsError' modifier = do
117117 msg <- Lua.tostring' Lua.stackTop
118118 Lua.pop 2 -- remove error and error string pushed by tostring'
119− Lua.throwException (modifier (UTF8.toString msg))
119+ Lua.throwMessage (modifier (UTF8.toString msg))
120120
121121-- | Mark the context of a Lua computation for better error reporting.
122122defineHowTo :: String -> Lua a -> Lua a
123−defineHowTo ctx = Lua.withExceptionMessage (("Could not " <> ctx <> ": ") <>)
123+defineHowTo ctx op = Lua.errorConversion >>= \ec ->
124+ Lua.addContextToException ec ("Could not " <> ctx <> ": ") op
124125
125126-- | Like @'Lua.pcall'@, but uses a predefined error handler which adds a
126127-- traceback on error.
src/Text/Pandoc/Writers/Custom.hs+9−18
@@ -20,17 +20,14 @@ import Data.List (intersperse)
2020import qualified Data.Map as M
2121import qualified Data.Text as T
2222import Data.Text (Text, pack)
23−import Data.Typeable
2423import Foreign.Lua (Lua, Pushable)
2524import Text.DocLayout (render, literal)
2625import Text.Pandoc.Class.PandocIO (PandocIO)
2726import Text.Pandoc.Definition
28−import Text.Pandoc.Lua (Global (..), LuaException (LuaException),
29− runLua, setGlobals)
27+import Text.Pandoc.Lua (Global (..), runLua, setGlobals)
3028import Text.Pandoc.Lua.Util (addField, dofileWithTraceback)
3129import Text.Pandoc.Options
3230import Text.Pandoc.Templates (renderTemplate)
33−import qualified Text.Pandoc.UTF8 as UTF8
3431import Text.Pandoc.Writers.Shared
3532
3633import qualified Foreign.Lua as Lua
@@ -81,11 +78,6 @@ instance (Pushable a, Pushable b) => Pushable (KeyValue a b) where
8178 Lua.push v
8279 Lua.rawset (Lua.nthFromTop 3)
8380
84−data PandocLuaException = PandocLuaException Text
85− deriving (Show, Typeable)
86−
87−instance Exception PandocLuaException
88−
8981-- | Convert Pandoc to custom markup.
9082writeCustom :: FilePath -> WriterOptions -> Pandoc -> PandocIO Text
9183writeCustom luaFile opts doc@(Pandoc meta _) = do
@@ -97,21 +89,20 @@ writeCustom luaFile opts doc@(Pandoc meta _) = do
9789 stat <- dofileWithTraceback luaFile
9890 -- check for error in lua script (later we'll change the return type
9991 -- to handle this more gracefully):
100− when (stat /= Lua.OK) $
101− Lua.tostring' (-1) >>= throw . PandocLuaException . UTF8.toText
92+ when (stat /= Lua.OK)
93+ Lua.throwTopMessage
10294 rendered <- docToCustom opts doc
10395 context <- metaToContext opts
10496 (fmap (literal . pack) . blockListToCustom)
10597 (fmap (literal . pack) . inlineListToCustom)
10698 meta
10799 return (pack rendered, context)
108− let (body, context) = case res of
109− Left (LuaException msg) -> throw (PandocLuaException msg)
110− Right x -> x
111− return $
112− case writerTemplate opts of
113− Nothing -> body
114− Just tpl -> render Nothing $
100+ case res of
101+ Left msg -> throw msg
102+ Right (body, context) -> return $
103+ case writerTemplate opts of
104+ Nothing -> body
105+ Just tpl -> render Nothing $
115106 renderTemplate tpl $ setField "body" body context
116107
117108docToCustom :: WriterOptions -> Pandoc -> Lua String
stack.yaml+1
@@ -23,6 +23,7 @@ extra-deps:
2323- regex-pcre-builtin-0.95.0.8.8.35
2424- doclayout-0.3
2525- emojis-0.1
26+- hslua-1.1.0
2627- jira-wiki-markup-1.3.0
2728- HsYAML-0.2.0.0
2829- HsYAML-aeson-0.2.0.0
test/Tests/Lua.hs+8−7
@@ -1,5 +1,6 @@
11{-# LANGUAGE NoImplicitPrelude #-}
22{-# LANGUAGE OverloadedStrings #-}
3+{-# LANGUAGE ScopedTypeVariables #-}
34{- |
45 Module : Tests.Lua
56 Copyright : © 2017-2020 Albert Krewinkel
@@ -28,11 +29,13 @@ import Text.Pandoc.Builder (bulletList, definitionList, displayMath, divWith,
2829import Text.Pandoc.Class (runIOorExplode, setUserDataDir)
2930import Text.Pandoc.Definition (Block (BlockQuote, Div, Para), Inline (Emph, Str),
3031 Attr, Meta, Pandoc, pandocTypesVersion)
32+import Text.Pandoc.Error (PandocError (PandocLuaError))
3133import Text.Pandoc.Filter (Filter (LuaFilter), applyFilters)
3234import Text.Pandoc.Lua (runLua)
3335import Text.Pandoc.Options (def)
3436import Text.Pandoc.Shared (pandocVersion)
3537
38+import qualified Control.Monad.Catch as Catch
3639import qualified Foreign.Lua as Lua
3740import qualified Data.Text as T
3841import qualified Data.Text.Encoding as TE
@@ -197,12 +200,13 @@ tests = map (localOption (QuickCheckTests 20))
197200
198201 , testCase "informative error messages" . runLuaTest $ do
199202 Lua.pushboolean True
200− err <- Lua.peekEither Lua.stackTop
201− case (err :: Either String Pandoc) of
202− Left msg -> do
203+ eitherPandoc <- Catch.try (Lua.peek Lua.stackTop :: Lua.Lua Pandoc)
204+ case eitherPandoc of
205+ Left (PandocLuaError msg) -> do
203206 let expectedMsg = "Could not get Pandoc value: "
204207 <> "table expected, got boolean"
205208 Lua.liftIO $ assertEqual "unexpected error message" expectedMsg msg
209+ Left e -> error ("Expected a Lua error, but got " <> show e)
206210 Right _ -> error "Getting a Pandoc element from a bool should fail."
207211 ]
208212
@@ -223,10 +227,7 @@ roundtripEqual x = (x ==) <$> roundtripped
223227 size <- Lua.gettop
224228 when (size - oldSize /= 1) $
225229 error ("not exactly one additional element on the stack: " ++ show size)
226− res <- Lua.peekEither (-1)
227− case res of
228− Left e -> error (show e)
229− Right y -> return y
230+ Lua.peek (-1)
230231
231232runLuaTest :: Lua.Lua a -> IO a
232233runLuaTest op = runIOorExplode $ do
test/lua/module/pandoc-types.lua+3−4
@@ -26,10 +26,9 @@ return {
2626 )
2727 end),
2828 test('non-version string is rejected', function ()
29− assert.error_matches(
30− function () Version '11friends' end,
31− '11friends'
32− )
29+ local success, msg = pcall(function () Version '11friends' end)
30+ assert.is_falsy(success)
31+ assert.is_truthy(tostring(msg):match('11friends'))
3332 end)
3433 },
3534