git.iko.soy/typeable/pandoc
on master help sign in
Avoid repeating StackValue instances definitions

The lua filters and custom lua writer system defined very similar StackValue instances for strings and tuples. These instance definitions are extracted to a separate module to enable sharing.

commit0add4253e6dc5c3cdca894c5bb312428fe3d31b3 authorAlbert Krewinkel <albert@zeitkraut.de> dateFri 14 Apr 2017, 19:07 (10 years ago) parentfeb1c1a
5 files changed+166−156
pandoc.cabal+1 src/Text/Pandoc/Lua/SharedInstances.hs+106 src/Text/Pandoc/Lua/StackInstances.hs+3−125 src/Text/Pandoc/Lua/Util.hs+55−1 src/Text/Pandoc/Writers/Custom.hs+1−30
pandoc.cabal+1
@@ -457,6 +457,7 @@ Library
457457 Text.Pandoc.Readers.Org.Shared,
458458 Text.Pandoc.Lua.Compat,
459459 Text.Pandoc.Lua.PandocModule,
460+ Text.Pandoc.Lua.SharedInstances,
460461 Text.Pandoc.Lua.StackInstances,
461462 Text.Pandoc.Lua.Util,
462463 Text.Pandoc.CSS,
src/Text/Pandoc/Lua/SharedInstances.hsnew file+106
@@ -0,0 +1,106 @@
1+{-
2+Copyright © 2012-2016 John MacFarlane <jgm@berkeley.edu>
3+ 2017 Albert Krewinkel <tarleb+pandoc@moltkeplatz.de>
4+
5+This program is free software; you can redistribute it and/or modify
6+it under the terms of the GNU General Public License as published by
7+the Free Software Foundation; either version 2 of the License, or
8+(at your option) any later version.
9+
10+This program is distributed in the hope that it will be useful,
11+but WITHOUT ANY WARRANTY; without even the implied warranty of
12+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+GNU General Public License for more details.
14+
15+You should have received a copy of the GNU General Public License
16+along with this program; if not, write to the Free Software
17+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18+-}
19+{-# LANGUAGE CPP #-}
20+{-# LANGUAGE FlexibleInstances #-}
21+{-# LANGUAGE LambdaCase #-}
22+#if !MIN_VERSION_base(4,8,0)
23+{-# LANGUAGE OverlappingInstances #-}
24+#endif
25+{-# OPTIONS_GHC -fno-warn-orphans #-}
26+{- |
27+ Module : Text.Pandoc.Lua.SharedInstances
28+ Copyright : © 2012–2016 John MacFarlane,
29+ © 2017 Albert Krewinkel
30+ License : GNU GPL, version 2 or above
31+
32+ Maintainer : Albert Krewinkel <tarleb+pandoc@moltkeplatz.de>
33+ Stability : alpha
34+
35+Shared StackValue instances for pandoc and generic types.
36+-}
37+module Text.Pandoc.Lua.SharedInstances () where
38+
39+import Scripting.Lua ( LTYPE(..), StackValue(..), newtable )
40+import Text.Pandoc.Lua.Util ( addRawInt, addValue, getRawInt, keyValuePairs )
41+
42+import qualified Data.Map as M
43+import qualified Text.Pandoc.UTF8 as UTF8
44+
45+#if MIN_VERSION_base(4,8,0)
46+instance {-# OVERLAPS #-} StackValue [Char] where
47+#else
48+instance StackValue [Char] where
49+#endif
50+ push lua cs = push lua (UTF8.fromString cs)
51+ peek lua i = fmap UTF8.toString <$> peek lua i
52+ valuetype _ = TSTRING
53+
54+instance (StackValue a, StackValue b) => StackValue (a, b) where
55+ push lua (a, b) = do
56+ newtable lua
57+ addRawInt lua 1 a
58+ addRawInt lua 2 b
59+ peek lua idx = do
60+ a <- getRawInt lua idx 1
61+ b <- getRawInt lua idx 2
62+ return $ (,) <$> a <*> b
63+ valuetype _ = TTABLE
64+
65+instance (StackValue a, StackValue b, StackValue c) =>
66+ StackValue (a, b, c)
67+ where
68+ push lua (a, b, c) = do
69+ newtable lua
70+ addRawInt lua 1 a
71+ addRawInt lua 2 b
72+ addRawInt lua 3 c
73+ peek lua idx = do
74+ a <- getRawInt lua idx 1
75+ b <- getRawInt lua idx 2
76+ c <- getRawInt lua idx 3
77+ return $ (,,) <$> a <*> b <*> c
78+ valuetype _ = TTABLE
79+
80+instance (StackValue a, StackValue b, StackValue c,
81+ StackValue d, StackValue e) =>
82+ StackValue (a, b, c, d, e)
83+ where
84+ push lua (a, b, c, d, e) = do
85+ newtable lua
86+ addRawInt lua 1 a
87+ addRawInt lua 2 b
88+ addRawInt lua 3 c
89+ addRawInt lua 4 d
90+ addRawInt lua 5 e
91+ peek lua idx = do
92+ a <- getRawInt lua idx 1
93+ b <- getRawInt lua idx 2
94+ c <- getRawInt lua idx 3
95+ d <- getRawInt lua idx 4
96+ e <- getRawInt lua idx 5
97+ return $ (,,,,) <$> a <*> b <*> c <*> d <*> e
98+ valuetype _ = TTABLE
99+
100+instance (Ord a, StackValue a, StackValue b) =>
101+ StackValue (M.Map a b) where
102+ push lua m = do
103+ newtable lua
104+ mapM_ (uncurry $ addValue lua) $ M.toList m
105+ peek lua idx = fmap M.fromList <$> keyValuePairs lua idx
106+ valuetype _ = TTABLE
src/Text/Pandoc/Lua/StackInstances.hs+3−125
@@ -16,12 +16,8 @@ You should have received a copy of the GNU General Public License
1616along with this program; if not, write to the Free Software
1717Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
1818-}
19−{-# LANGUAGE CPP #-}
2019{-# LANGUAGE FlexibleInstances #-}
2120{-# LANGUAGE LambdaCase #-}
22−#if !MIN_VERSION_base(4,8,0)
23−{-# LANGUAGE OverlappingInstances #-}
24−#endif
2521{-# OPTIONS_GHC -fno-warn-orphans #-}
2622{- |
2723 Module : Text.Pandoc.Lua.StackInstances
@@ -38,15 +34,10 @@ module Text.Pandoc.Lua.StackInstances () where
3834
3935import Control.Applicative ( (<|>) )
4036import Scripting.Lua
41− ( LTYPE(..), LuaState, StackValue(..)
42− , call, getglobal2, ltype, newtable, next, objlen, pop, pushnil
43− )
37+ ( LTYPE(..), LuaState, StackValue(..), getglobal2, ltype, newtable, objlen )
4438import Text.Pandoc.Definition
45−import Text.Pandoc.Lua.Util
46− ( adjustIndexBy, addValue, getTable, addRawInt, getRawInt )
47−
48−import qualified Data.Map as M
49−import qualified Text.Pandoc.UTF8 as UTF8
39+import Text.Pandoc.Lua.SharedInstances ()
40+import Text.Pandoc.Lua.Util ( addValue, getTable, pushViaConstructor )
5041
5142instance StackValue Pandoc where
5243 push lua (Pandoc meta blocks) = do
@@ -261,119 +252,6 @@ instance StackValue QuoteType where
261252 _ -> return Nothing
262253 valuetype _ = TTABLE
263254
264−#if MIN_VERSION_base(4,8,0)
265−instance {-# OVERLAPS #-} StackValue [Char] where
266−#else
267−instance StackValue [Char] where
268−#endif
269− push lua cs = push lua (UTF8.fromString cs)
270− peek lua i = fmap UTF8.toString <$> peek lua i
271− valuetype _ = TSTRING
272−
273−instance (StackValue a, StackValue b) => StackValue (a, b) where
274− push lua (a, b) = do
275− newtable lua
276− addRawInt lua 1 a
277− addRawInt lua 2 b
278− peek lua idx = do
279− a <- getRawInt lua idx 1
280− b <- getRawInt lua idx 2
281− return $ (,) <$> a <*> b
282− valuetype _ = TTABLE
283−
284−instance (StackValue a, StackValue b, StackValue c) =>
285− StackValue (a, b, c)
286− where
287− push lua (a, b, c) = do
288− newtable lua
289− addRawInt lua 1 a
290− addRawInt lua 2 b
291− addRawInt lua 3 c
292− peek lua idx = do
293− a <- getRawInt lua idx 1
294− b <- getRawInt lua idx 2
295− c <- getRawInt lua idx 3
296− return $ (,,) <$> a <*> b <*> c
297− valuetype _ = TTABLE
298−
299−instance (StackValue a, StackValue b, StackValue c,
300− StackValue d, StackValue e) =>
301− StackValue (a, b, c, d, e)
302− where
303− push lua (a, b, c, d, e) = do
304− newtable lua
305− addRawInt lua 1 a
306− addRawInt lua 2 b
307− addRawInt lua 3 c
308− addRawInt lua 4 d
309− addRawInt lua 5 e
310− peek lua idx = do
311− a <- getRawInt lua idx 1
312− b <- getRawInt lua idx 2
313− c <- getRawInt lua idx 3
314− d <- getRawInt lua idx 4
315− e <- getRawInt lua idx 5
316− return $ (,,,,) <$> a <*> b <*> c <*> d <*> e
317− valuetype _ = TTABLE
318−
319−instance (Ord a, StackValue a, StackValue b) =>
320− StackValue (M.Map a b) where
321− push lua m = do
322− newtable lua
323− mapM_ (uncurry $ addValue lua) $ M.toList m
324− peek lua idx = fmap M.fromList <$> keyValuePairs lua idx
325− valuetype _ = TTABLE
326−
327−-- | Try reading the value under the given index as a list of key-value pairs.
328−keyValuePairs :: (StackValue a, StackValue b)
329− => LuaState -> Int -> IO (Maybe [(a, b)])
330−keyValuePairs lua idx = do
331− pushnil lua
332− sequence <$> remainingPairs
333− where
334− remainingPairs = do
335− res <- nextPair
336− case res of
337− Nothing -> return []
338− Just a -> (a:) <$> remainingPairs
339− nextPair :: (StackValue a, StackValue b) => IO (Maybe (Maybe (a,b)))
340− nextPair = do
341− hasNext <- next lua (idx `adjustIndexBy` 1)
342− if hasNext
343− then do
344− val <- peek lua (-1)
345− key <- peek lua (-2)
346− pop lua 1 -- removes the value, keeps the key
347− return $ Just <$> ((,) <$> key <*> val)
348− else do
349− return Nothing
350−
351−
352−-- | Helper class for pushing a single value to the stack via a lua function.
353−-- See @pushViaCall@.
354−class PushViaCall a where
355− pushViaCall' :: LuaState -> String -> IO () -> Int -> a
356−
357−instance PushViaCall (IO ()) where
358− pushViaCall' lua fn pushArgs num = do
359− getglobal2 lua fn
360− pushArgs
361− call lua num 1
362−
363−instance (StackValue a, PushViaCall b) => PushViaCall (a -> b) where
364− pushViaCall' lua fn pushArgs num x =
365− pushViaCall' lua fn (pushArgs *> push lua x) (num + 1)
366−
367−-- | Push an value to the stack via a lua function. The lua function is called
368−-- with all arguments that are passed to this function and is expected to return
369−-- a single value.
370−pushViaCall :: PushViaCall a => LuaState -> String -> a
371−pushViaCall lua fn = pushViaCall' lua fn (return ()) 0
372−
373−-- | Call a pandoc element constructor within lua, passing all given arguments.
374−pushViaConstructor :: PushViaCall a => LuaState -> String -> a
375−pushViaConstructor lua pandocFn = pushViaCall lua ("pandoc." ++ pandocFn)
376−
377255-- | Return the value at the given index as inline if possible.
378256peekInline :: LuaState -> Int -> IO (Maybe Inline)
379257peekInline lua idx = do
src/Text/Pandoc/Lua/Util.hs+55−1
@@ -16,6 +16,7 @@ You should have received a copy of the GNU General Public License
1616along with this program; if not, write to the Free Software
1717Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
1818-}
19+{-# LANGUAGE FlexibleInstances #-}
1920{- |
2021 Module : Text.Pandoc.Lua.Util
2122 Copyright : © 2012–2016 John MacFarlane,
@@ -35,11 +36,15 @@ module Text.Pandoc.Lua.Util
3536 , getRawInt
3637 , setRawInt
3738 , addRawInt
39+ , keyValuePairs
40+ , PushViaCall
41+ , pushViaCall
42+ , pushViaConstructor
3843 ) where
3944
4045import Scripting.Lua
4146 ( LuaState, StackValue(..)
42− , gettable, pop, rawgeti, rawseti, settable
47+ , call, getglobal2, gettable, next, pop, pushnil, rawgeti, rawseti, settable
4348 )
4449
4550-- | Adjust the stack index, assuming that @n@ new elements have been pushed on
@@ -84,3 +89,52 @@ setRawInt lua idx key value = do
8489-- | Set numeric key/value in table at the top of the stack.
8590addRawInt :: StackValue a => LuaState -> Int -> a -> IO ()
8691addRawInt lua = setRawInt lua (-1)
92+
93+-- | Try reading the table under the given index as a list of key-value pairs.
94+keyValuePairs :: (StackValue a, StackValue b)
95+ => LuaState -> Int -> IO (Maybe [(a, b)])
96+keyValuePairs lua idx = do
97+ pushnil lua
98+ sequence <$> remainingPairs
99+ where
100+ remainingPairs = do
101+ res <- nextPair
102+ case res of
103+ Nothing -> return []
104+ Just a -> (a:) <$> remainingPairs
105+ nextPair :: (StackValue a, StackValue b) => IO (Maybe (Maybe (a,b)))
106+ nextPair = do
107+ hasNext <- next lua (idx `adjustIndexBy` 1)
108+ if hasNext
109+ then do
110+ val <- peek lua (-1)
111+ key <- peek lua (-2)
112+ pop lua 1 -- removes the value, keeps the key
113+ return $ Just <$> ((,) <$> key <*> val)
114+ else do
115+ return Nothing
116+
117+-- | Helper class for pushing a single value to the stack via a lua function.
118+-- See @pushViaCall@.
119+class PushViaCall a where
120+ pushViaCall' :: LuaState -> String -> IO () -> Int -> a
121+
122+instance PushViaCall (IO ()) where
123+ pushViaCall' lua fn pushArgs num = do
124+ getglobal2 lua fn
125+ pushArgs
126+ call lua num 1
127+
128+instance (StackValue a, PushViaCall b) => PushViaCall (a -> b) where
129+ pushViaCall' lua fn pushArgs num x =
130+ pushViaCall' lua fn (pushArgs *> push lua x) (num + 1)
131+
132+-- | Push an value to the stack via a lua function. The lua function is called
133+-- with all arguments that are passed to this function and is expected to return
134+-- a single value.
135+pushViaCall :: PushViaCall a => LuaState -> String -> a
136+pushViaCall lua fn = pushViaCall' lua fn (return ()) 0
137+
138+-- | Call a pandoc element constructor within lua, passing all given arguments.
139+pushViaConstructor :: PushViaCall a => LuaState -> String -> a
140+pushViaConstructor lua pandocFn = pushViaCall lua ("pandoc." ++ pandocFn)
src/Text/Pandoc/Writers/Custom.hs+1−30
@@ -47,6 +47,7 @@ import Scripting.Lua (LuaState, StackValue, callfunc)
4747import qualified Scripting.Lua as Lua
4848import Text.Pandoc.Lua.Compat ( loadstring )
4949import Text.Pandoc.Lua.Util ( addValue )
50+import Text.Pandoc.Lua.SharedInstances ()
5051import Text.Pandoc.Definition
5152import Text.Pandoc.Options
5253import Text.Pandoc.Templates
@@ -59,41 +60,11 @@ attrToMap (id',classes,keyvals) = M.fromList
5960 : ("class", unwords classes)
6061 : keyvals
6162
62−#if MIN_VERSION_base(4,8,0)
63−instance {-# OVERLAPS #-} StackValue [Char] where
64−#else
65−instance StackValue [Char] where
66−#endif
67− push lua cs = Lua.push lua (UTF8.fromString cs)
68− peek lua i = do
69− res <- Lua.peek lua i
70− return $ UTF8.toString `fmap` res
71− valuetype _ = Lua.TSTRING
72−
7363instance StackValue Format where
7464 push lua (Format f) = Lua.push lua (map toLower f)
7565 peek l n = fmap Format `fmap` Lua.peek l n
7666 valuetype _ = Lua.TSTRING
7767
78−instance (StackValue a, StackValue b) => StackValue (M.Map a b) where
79− push lua m = do
80− let xs = M.toList m
81− Lua.createtable lua (length xs + 1) 0
82− let addValue (k, v) = Lua.push lua k >> Lua.push lua v >>
83− Lua.rawset lua (-3)
84− mapM_ addValue xs
85− peek _ _ = undefined -- not needed for our purposes
86− valuetype _ = Lua.TTABLE
87−
88−instance (StackValue a, StackValue b) => StackValue (a,b) where
89− push lua (k,v) = do
90− Lua.createtable lua 2 0
91− Lua.push lua k
92− Lua.push lua v
93− Lua.rawset lua (-3)
94− peek _ _ = undefined -- not needed for our purposes
95− valuetype _ = Lua.TTABLE
96−
9768#if MIN_VERSION_base(4,8,0)
9869instance {-# OVERLAPS #-} StackValue [Inline] where
9970#else