git.iko.soy/typeable/pandoc
on master help sign in
Lua filters: improve error messages
6528082Albert Krewinkel
pandoc.cabal+1
@@ -347,6 +347,7 @@ library
347 unordered-containers >= 0.2 && < 0.3,
347 unordered-containers >= 0.2 && < 0.3,
348 parsec >= 3.1 && < 3.2,
348 parsec >= 3.1 && < 3.2,
349 mtl >= 2.2 && < 2.3,
349 mtl >= 2.2 && < 2.3,
350+ exceptions >= 0.8 && < 0.9,
350 filepath >= 1.1 && < 1.5,
351 filepath >= 1.1 && < 1.5,
351 process >= 1.2.3 && < 1.7,
352 process >= 1.2.3 && < 1.7,
352 directory >= 1 && < 1.4,
353 directory >= 1 && < 1.4,
src/Text/Pandoc/Lua/Filter.hs+6−2
@@ -11,6 +11,7 @@ module Text.Pandoc.Lua.Filter ( LuaFilterFunction
11 , inlineElementNames
11 , inlineElementNames
12 ) where
12 ) where
13import Control.Monad (mplus, unless, when, (>=>))
13import Control.Monad (mplus, unless, when, (>=>))
14+import Control.Monad.Catch (finally)
14import Text.Pandoc.Definition
15import Text.Pandoc.Definition
15import Data.Foldable (foldrM)
16import Data.Foldable (foldrM)
16import Data.Map (Map)
17import Data.Map (Map)
@@ -22,6 +23,7 @@ import Text.Pandoc.Walk (walkM, Walkable)
22import Data.Data (Data, DataType, dataTypeConstrs, dataTypeName, dataTypeOf,
23import Data.Data (Data, DataType, dataTypeConstrs, dataTypeName, dataTypeOf,
23 showConstr, toConstr, tyconUQname)
24 showConstr, toConstr, tyconUQname)
24import Text.Pandoc.Lua.StackInstances()
25import Text.Pandoc.Lua.StackInstances()
26+import Text.Pandoc.Lua.Util (typeCheck)
25
27
26type FunctionMap = Map String LuaFilterFunction
28type FunctionMap = Map String LuaFilterFunction
27
29
@@ -65,7 +67,7 @@ registerFilterFunction idx = do
65
67
66elementOrList :: FromLuaStack a => a -> Lua [a]
68elementOrList :: FromLuaStack a => a -> Lua [a]
67elementOrList x = do
69elementOrList x = do
68− let topOfStack = Lua.StackIndex (-1)
70+ let topOfStack = Lua.stackTop
69 elementUnchanged <- Lua.isnil topOfStack
71 elementUnchanged <- Lua.isnil topOfStack
70 if elementUnchanged
72 if elementUnchanged
71 then [x] <$ Lua.pop 1
73 then [x] <$ Lua.pop 1
@@ -73,7 +75,9 @@ elementOrList x = do
73 mbres <- Lua.peekEither topOfStack
75 mbres <- Lua.peekEither topOfStack
74 case mbres of
76 case mbres of
75 Right res -> [res] <$ Lua.pop 1
77 Right res -> [res] <$ Lua.pop 1
78+ Left _ -> do
79+ typeCheck Lua.stackTop Lua.TypeTable
76− Left _ -> Lua.toList topOfStack <* Lua.pop 1
80+ Lua.toList topOfStack `finally` Lua.pop 1
77
81
78-- | Try running a filter for the given element
82-- | Try running a filter for the given element
79tryFilter :: (Data a, FromLuaStack a, ToLuaStack a)
83tryFilter :: (Data a, FromLuaStack a, ToLuaStack a)
src/Text/Pandoc/Lua/StackInstances.hs+19−13
@@ -35,13 +35,15 @@ module Text.Pandoc.Lua.StackInstances () where
35
35
36import Control.Applicative ((<|>))
36import Control.Applicative ((<|>))
37import Control.Monad (when)
37import Control.Monad (when)
38+import Control.Monad.Catch (finally)
38import Data.Data (showConstr, toConstr)
39import Data.Data (showConstr, toConstr)
39import Data.Foldable (forM_)
40import Data.Foldable (forM_)
40import Foreign.Lua (FromLuaStack (peek), Lua, LuaInteger, LuaNumber, StackIndex,
41import Foreign.Lua (FromLuaStack (peek), Lua, LuaInteger, LuaNumber, StackIndex,
41 ToLuaStack (push), Type (..), throwLuaError, tryLua)
42 ToLuaStack (push), Type (..), throwLuaError, tryLua)
42import Text.Pandoc.Definition
43import Text.Pandoc.Definition
43import Text.Pandoc.Extensions (Extensions)
44import Text.Pandoc.Extensions (Extensions)
44−import Text.Pandoc.Lua.Util (adjustIndexBy, getTable, pushViaConstructor)
45+import Text.Pandoc.Lua.Util (adjustIndexBy, getTable, pushViaConstructor,
46+ typeCheck)
45import Text.Pandoc.Options (ReaderOptions (..), TrackChanges)
47import Text.Pandoc.Options (ReaderOptions (..), TrackChanges)
46import Text.Pandoc.Shared (Element (Blk, Sec), safeRead)
48import Text.Pandoc.Shared (Element (Blk, Sec), safeRead)
47
49
@@ -49,21 +51,27 @@ import qualified Foreign.Lua as Lua
49import qualified Data.Set as Set
51import qualified Data.Set as Set
50import qualified Text.Pandoc.Lua.Util as LuaUtil
52import qualified Text.Pandoc.Lua.Util as LuaUtil
51
53
54+defineHowTo :: String -> Lua a -> Lua a
55+defineHowTo ctx op = op `Lua.modifyLuaError` (("Could not " ++ ctx ++ ": ") ++)
56+
52instance ToLuaStack Pandoc where
57instance ToLuaStack Pandoc where
53 push (Pandoc meta blocks) =
58 push (Pandoc meta blocks) =
54 pushViaConstructor "Pandoc" blocks meta
59 pushViaConstructor "Pandoc" blocks meta
55
60
56instance FromLuaStack Pandoc where
61instance FromLuaStack Pandoc where
57− peek idx = do
62+ peek idx = defineHowTo "get Pandoc value" $ do
63+ typeCheck idx Lua.TypeTable
58 blocks <- getTable idx "blocks"
64 blocks <- getTable idx "blocks"
59− meta <- getTable idx "meta"
65+ meta <- Lua.getfield idx "meta" *> (Lua.peek Lua.stackTop `finally` Lua.pop 1)
60 return $ Pandoc meta blocks
66 return $ Pandoc meta blocks
61
67
62instance ToLuaStack Meta where
68instance ToLuaStack Meta where
63 push (Meta mmap) =
69 push (Meta mmap) =
64 pushViaConstructor "Meta" mmap
70 pushViaConstructor "Meta" mmap
65instance FromLuaStack Meta where
71instance FromLuaStack Meta where
72+ peek idx = defineHowTo "get Meta value" $ do
73+ typeCheck idx Lua.TypeTable
66− peek idx = Meta <$> peek idx
74+ Meta <$> peek idx
67
75
68instance ToLuaStack MetaValue where
76instance ToLuaStack MetaValue where
69 push = pushMetaValue
77 push = pushMetaValue
@@ -160,7 +168,7 @@ pushMetaValue = \case
160
168
161-- | Interpret the value at the given stack index as meta value.
169-- | Interpret the value at the given stack index as meta value.
162peekMetaValue :: StackIndex -> Lua MetaValue
170peekMetaValue :: StackIndex -> Lua MetaValue
163−peekMetaValue idx = do
171+peekMetaValue idx = defineHowTo "get MetaValue" $ do
164 -- Get the contents of an AST element.
172 -- Get the contents of an AST element.
165 let elementContent :: FromLuaStack a => Lua a
173 let elementContent :: FromLuaStack a => Lua a
166 elementContent = peek idx
174 elementContent = peek idx
@@ -209,7 +217,8 @@ pushBlock = \case
209
217
210-- | Return the value at the given index as block if possible.
218-- | Return the value at the given index as block if possible.
211peekBlock :: StackIndex -> Lua Block
219peekBlock :: StackIndex -> Lua Block
212−peekBlock idx = do
220+peekBlock idx = defineHowTo "get Block value" $ do
221+ typeCheck idx Lua.TypeTable
213 tag <- getTag idx
222 tag <- getTag idx
214 case tag of
223 case tag of
215 "BlockQuote" -> BlockQuote <$> elementContent
224 "BlockQuote" -> BlockQuote <$> elementContent
@@ -260,7 +269,8 @@ pushInline = \case
260
269
261-- | Return the value at the given index as inline if possible.
270-- | Return the value at the given index as inline if possible.
262peekInline :: StackIndex -> Lua Inline
271peekInline :: StackIndex -> Lua Inline
263−peekInline idx = do
272+peekInline idx = defineHowTo "get Inline value" $ do
273+ typeCheck idx Lua.TypeTable
264 tag <- getTag idx
274 tag <- getTag idx
265 case tag of
275 case tag of
266 "Cite" -> uncurry Cite <$> elementContent
276 "Cite" -> uncurry Cite <$> elementContent
@@ -296,11 +306,7 @@ getTag idx = do
296 hasMT <- Lua.getmetatable idx
306 hasMT <- Lua.getmetatable idx
297 push "tag"
307 push "tag"
298 if hasMT then Lua.rawget (-2) else Lua.rawget (idx `adjustIndexBy` 1)
308 if hasMT then Lua.rawget (-2) else Lua.rawget (idx `adjustIndexBy` 1)
299− r <- tryLua (peek (-1))
300− Lua.settop top
309+ peek Lua.stackTop `finally` Lua.settop top
301− case r of
302− Left (Lua.LuaException err) -> throwLuaError err
303− Right res -> return res
304
310
305withAttr :: (Attr -> a -> b) -> (LuaAttr, a) -> b
311withAttr :: (Attr -> a -> b) -> (LuaAttr, a) -> b
306withAttr f (attributes, x) = f (fromLuaAttr attributes) x
312withAttr f (attributes, x) = f (fromLuaAttr attributes) x
@@ -313,7 +319,7 @@ instance ToLuaStack LuaAttr where
313 pushViaConstructor "Attr" id' classes kv
319 pushViaConstructor "Attr" id' classes kv
314
320
315instance FromLuaStack LuaAttr where
321instance FromLuaStack LuaAttr where
316− peek idx = LuaAttr <$> peek idx
322+ peek idx = defineHowTo "get Attr value" (LuaAttr <$> peek idx)
317
323
318--
324--
319-- Hierarchical elements
325-- Hierarchical elements
src/Text/Pandoc/Lua/Util.hs+9
@@ -36,6 +36,7 @@ module Text.Pandoc.Lua.Util
36 , getRawInt
36 , getRawInt
37 , setRawInt
37 , setRawInt
38 , addRawInt
38 , addRawInt
39+ , typeCheck
39 , raiseError
40 , raiseError
40 , popValue
41 , popValue
41 , PushViaCall
42 , PushViaCall
@@ -100,6 +101,14 @@ setRawInt idx key value = do
100addRawInt :: ToLuaStack a => Int -> a -> Lua ()
101addRawInt :: ToLuaStack a => Int -> a -> Lua ()
101addRawInt = setRawInt (-1)
102addRawInt = setRawInt (-1)
102
103
104+typeCheck :: StackIndex -> Lua.Type -> Lua ()
105+typeCheck idx expected = do
106+ actual <- Lua.ltype idx
107+ when (actual /= expected) $ do
108+ expName <- Lua.typename expected
109+ actName <- Lua.typename actual
110+ Lua.throwLuaError $ "expected " ++ expName ++ " but got " ++ actName ++ "."
111+
103raiseError :: ToLuaStack a => a -> Lua NumResults
112raiseError :: ToLuaStack a => a -> Lua NumResults
104raiseError e = do
113raiseError e = do
105 Lua.push e
114 Lua.push e
test/Tests/Lua.hs+10
@@ -123,6 +123,16 @@ tests = map (localOption (QuickCheckTests 20))
123 Lua.getglobal "PANDOC_API_VERSION"
123 Lua.getglobal "PANDOC_API_VERSION"
124 Lua.liftIO . assertEqual "pandoc-types version is wrong" versionNums
124 Lua.liftIO . assertEqual "pandoc-types version is wrong" versionNums
125 =<< Lua.peek Lua.stackTop
125 =<< Lua.peek Lua.stackTop
126+
127+ , testCase "informative error messages" . runPandocLua' $ do
128+ Lua.pushboolean True
129+ err <- Lua.peekEither Lua.stackTop :: Lua.Lua (Either String Pandoc)
130+ case err of
131+ Left msg -> do
132+ let expectedMsg = "Could not get Pandoc value: "
133+ ++ "expected table but got boolean."
134+ Lua.liftIO $ assertEqual "unexpected error message" expectedMsg msg
135+ Right _ -> error "Getting a Pandoc element from a bool should fail."
126 ]
136 ]
127
137
128assertFilterConversion :: String -> FilePath -> Pandoc -> Pandoc -> Assertion
138assertFilterConversion :: String -> FilePath -> Pandoc -> Pandoc -> Assertion