Switch to hslua-2.0
The new HsLua version takes a somewhat different approach to marshalling and unmarshalling, relying less on typeclasses and more on specialized types. This allows for better performance and improved error messages. Furthermore, new abstractions allow to document the code and exposed functions.
This commit is contained in:
parent
e10f495a01
commit
9e74826ba9
29 changed files with 1129 additions and 1226 deletions
|
@ -41,4 +41,3 @@ source-repository-package
|
||||||
-- type: git
|
-- type: git
|
||||||
-- location: https://github.com/jgm/ipynb.git
|
-- location: https://github.com/jgm/ipynb.git
|
||||||
-- tag: 1f1ddb29227335091a3a158b9aeeeb47a372c683
|
-- tag: 1f1ddb29227335091a3a158b9aeeeb47a372c683
|
||||||
|
|
||||||
|
|
15
pandoc.cabal
15
pandoc.cabal
|
@ -551,10 +551,11 @@ library
|
||||||
file-embed >= 0.0 && < 0.1,
|
file-embed >= 0.0 && < 0.1,
|
||||||
filepath >= 1.1 && < 1.5,
|
filepath >= 1.1 && < 1.5,
|
||||||
haddock-library >= 1.10 && < 1.11,
|
haddock-library >= 1.10 && < 1.11,
|
||||||
hslua >= 1.1 && < 1.4,
|
hslua >= 2.0 && < 2.1,
|
||||||
hslua-module-path >= 0.1.0 && < 0.2.0,
|
hslua-marshalling >= 2.0 && < 2.1,
|
||||||
hslua-module-system >= 0.2 && < 0.3,
|
hslua-module-path >= 1.0 && < 1.1,
|
||||||
hslua-module-text >= 0.2.1 && < 0.4,
|
hslua-module-system >= 1.0 && < 1.1,
|
||||||
|
hslua-module-text >= 1.0 && < 1.1,
|
||||||
http-client >= 0.4.30 && < 0.8,
|
http-client >= 0.4.30 && < 0.8,
|
||||||
http-client-tls >= 0.2.4 && < 0.4,
|
http-client-tls >= 0.2.4 && < 0.4,
|
||||||
http-types >= 0.8 && < 0.13,
|
http-types >= 0.8 && < 0.13,
|
||||||
|
@ -775,11 +776,9 @@ library
|
||||||
Text.Pandoc.Lua.Init,
|
Text.Pandoc.Lua.Init,
|
||||||
Text.Pandoc.Lua.Marshaling,
|
Text.Pandoc.Lua.Marshaling,
|
||||||
Text.Pandoc.Lua.Marshaling.AST,
|
Text.Pandoc.Lua.Marshaling.AST,
|
||||||
Text.Pandoc.Lua.Marshaling.AnyValue,
|
|
||||||
Text.Pandoc.Lua.Marshaling.CommonState,
|
Text.Pandoc.Lua.Marshaling.CommonState,
|
||||||
Text.Pandoc.Lua.Marshaling.Context,
|
Text.Pandoc.Lua.Marshaling.Context,
|
||||||
Text.Pandoc.Lua.Marshaling.List,
|
Text.Pandoc.Lua.Marshaling.List,
|
||||||
Text.Pandoc.Lua.Marshaling.MediaBag,
|
|
||||||
Text.Pandoc.Lua.Marshaling.PandocError,
|
Text.Pandoc.Lua.Marshaling.PandocError,
|
||||||
Text.Pandoc.Lua.Marshaling.ReaderOptions,
|
Text.Pandoc.Lua.Marshaling.ReaderOptions,
|
||||||
Text.Pandoc.Lua.Marshaling.SimpleTable,
|
Text.Pandoc.Lua.Marshaling.SimpleTable,
|
||||||
|
@ -847,14 +846,14 @@ test-suite test-pandoc
|
||||||
doctemplates >= 0.10 && < 0.11,
|
doctemplates >= 0.10 && < 0.11,
|
||||||
exceptions >= 0.8 && < 0.11,
|
exceptions >= 0.8 && < 0.11,
|
||||||
filepath >= 1.1 && < 1.5,
|
filepath >= 1.1 && < 1.5,
|
||||||
hslua >= 1.1 && < 1.4,
|
hslua >= 2.0 && < 2.1,
|
||||||
mtl >= 2.2 && < 2.3,
|
mtl >= 2.2 && < 2.3,
|
||||||
pandoc-types >= 1.22 && < 1.23,
|
pandoc-types >= 1.22 && < 1.23,
|
||||||
process >= 1.2.3 && < 1.7,
|
process >= 1.2.3 && < 1.7,
|
||||||
tasty >= 0.11 && < 1.5,
|
tasty >= 0.11 && < 1.5,
|
||||||
tasty-golden >= 2.3 && < 2.4,
|
tasty-golden >= 2.3 && < 2.4,
|
||||||
tasty-hunit >= 0.9 && < 0.11,
|
tasty-hunit >= 0.9 && < 0.11,
|
||||||
tasty-lua >= 0.2 && < 0.3,
|
tasty-lua >= 1.0 && < 1.1,
|
||||||
tasty-quickcheck >= 0.8 && < 0.11,
|
tasty-quickcheck >= 0.8 && < 0.11,
|
||||||
text >= 1.1.1.0 && < 1.3,
|
text >= 1.1.1.0 && < 1.3,
|
||||||
time >= 1.5 && < 1.13,
|
time >= 1.5 && < 1.13,
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
|
{-# OPTIONS_GHC -fno-warn-orphans #-}
|
||||||
{-# LANGUAGE LambdaCase #-}
|
{-# LANGUAGE LambdaCase #-}
|
||||||
{-# LANGUAGE RankNTypes #-}
|
|
||||||
{-# LANGUAGE ScopedTypeVariables #-}
|
|
||||||
{- |
|
{- |
|
||||||
Module : Text.Pandoc.Lua.ErrorConversion
|
Module : Text.Pandoc.Lua.ErrorConversion
|
||||||
Copyright : © 2020-2021 Albert Krewinkel
|
Copyright : © 2020-2021 Albert Krewinkel
|
||||||
|
@ -13,49 +12,37 @@ Define how Lua errors are converted into @'PandocError'@ Haskell
|
||||||
exceptions, and /vice versa/.
|
exceptions, and /vice versa/.
|
||||||
-}
|
-}
|
||||||
module Text.Pandoc.Lua.ErrorConversion
|
module Text.Pandoc.Lua.ErrorConversion
|
||||||
( errorConversion
|
( addContextToException
|
||||||
) where
|
) where
|
||||||
|
|
||||||
import Foreign.Lua (Lua (..), NumResults)
|
import HsLua (LuaError, LuaE, top)
|
||||||
|
import HsLua.Marshalling (resultToEither, runPeek)
|
||||||
|
import HsLua.Class.Peekable (PeekError (..))
|
||||||
import Text.Pandoc.Error (PandocError (PandocLuaError))
|
import Text.Pandoc.Error (PandocError (PandocLuaError))
|
||||||
import Text.Pandoc.Lua.Marshaling.PandocError (pushPandocError, peekPandocError)
|
import Text.Pandoc.Lua.Marshaling.PandocError (pushPandocError, peekPandocError)
|
||||||
|
|
||||||
import qualified Control.Monad.Catch as Catch
|
|
||||||
import qualified Data.Text as T
|
import qualified Data.Text as T
|
||||||
import qualified Foreign.Lua as Lua
|
import qualified HsLua as Lua
|
||||||
|
|
||||||
-- | Conversions between Lua errors and Haskell exceptions, assuming
|
addContextToException :: ()
|
||||||
-- that all exceptions are of type @'PandocError'@.
|
addContextToException = undefined
|
||||||
errorConversion :: Lua.ErrorConversion
|
|
||||||
errorConversion = Lua.ErrorConversion
|
|
||||||
{ Lua.addContextToException = addContextToException
|
|
||||||
, Lua.alternative = alternative
|
|
||||||
, Lua.errorToException = errorToException
|
|
||||||
, Lua.exceptionToError = exceptionToError
|
|
||||||
}
|
|
||||||
|
|
||||||
-- | Convert a Lua error, which must be at the top of the stack, into a
|
-- | Retrieve a @'PandocError'@ from the Lua stack.
|
||||||
-- @'PandocError'@, popping the value from the stack.
|
popPandocError :: LuaE PandocError PandocError
|
||||||
errorToException :: forall a . Lua.State -> IO a
|
popPandocError = do
|
||||||
errorToException l = Lua.unsafeRunWith l $ do
|
errResult <- runPeek $ peekPandocError top
|
||||||
err <- peekPandocError Lua.stackTop
|
case resultToEither errResult of
|
||||||
Lua.pop 1
|
Right x -> return x
|
||||||
Catch.throwM err
|
Left err -> return $ PandocLuaError (T.pack err)
|
||||||
|
|
||||||
-- | Try the first op -- if it doesn't succeed, run the second.
|
-- Ensure conversions between Lua errors and 'PandocError' exceptions
|
||||||
alternative :: forall a . Lua a -> Lua a -> Lua a
|
-- are possible.
|
||||||
alternative x y = Catch.try x >>= \case
|
instance LuaError PandocError where
|
||||||
Left (_ :: PandocError) -> y
|
popException = popPandocError
|
||||||
Right x' -> return x'
|
pushException = pushPandocError
|
||||||
|
luaException = PandocLuaError . T.pack
|
||||||
|
|
||||||
-- | Add more context to an error
|
instance PeekError PandocError where
|
||||||
addContextToException :: forall a . String -> Lua a -> Lua a
|
messageFromException = \case
|
||||||
addContextToException ctx op = op `Catch.catch` \case
|
PandocLuaError m -> T.unpack m
|
||||||
PandocLuaError msg -> Catch.throwM $ PandocLuaError (T.pack ctx <> msg)
|
err -> show err
|
||||||
e -> Catch.throwM e
|
|
||||||
|
|
||||||
-- | Catch a @'PandocError'@ exception and raise it as a Lua error.
|
|
||||||
exceptionToError :: Lua NumResults -> Lua NumResults
|
|
||||||
exceptionToError op = op `Catch.catch` \e -> do
|
|
||||||
pushPandocError e
|
|
||||||
Lua.error
|
|
||||||
|
|
|
@ -1,4 +1,7 @@
|
||||||
{-# LANGUAGE FlexibleContexts #-}
|
{-# LANGUAGE FlexibleContexts #-}
|
||||||
|
{-# LANGUAGE IncoherentInstances #-}
|
||||||
|
{-# LANGUAGE OverloadedStrings #-}
|
||||||
|
{-# LANGUAGE ScopedTypeVariables #-}
|
||||||
{- |
|
{- |
|
||||||
Module : Text.Pandoc.Lua.Filter
|
Module : Text.Pandoc.Lua.Filter
|
||||||
Copyright : © 2012-2021 John MacFarlane,
|
Copyright : © 2012-2021 John MacFarlane,
|
||||||
|
@ -19,43 +22,42 @@ module Text.Pandoc.Lua.Filter ( LuaFilterFunction
|
||||||
, module Text.Pandoc.Lua.Walk
|
, module Text.Pandoc.Lua.Walk
|
||||||
) where
|
) where
|
||||||
import Control.Applicative ((<|>))
|
import Control.Applicative ((<|>))
|
||||||
import Control.Monad (mplus, (>=>))
|
import Control.Monad (mplus, (>=>), (<$!>))
|
||||||
import Control.Monad.Catch (finally, try)
|
|
||||||
import Data.Data (Data, DataType, dataTypeConstrs, dataTypeName, dataTypeOf,
|
import Data.Data (Data, DataType, dataTypeConstrs, dataTypeName, dataTypeOf,
|
||||||
showConstr, toConstr, tyconUQname)
|
showConstr, toConstr, tyconUQname)
|
||||||
import Data.Foldable (foldrM)
|
import Data.Foldable (foldrM)
|
||||||
import Data.List (foldl')
|
import Data.List (foldl')
|
||||||
import Data.Map (Map)
|
import Data.Map (Map)
|
||||||
import Data.Maybe (fromMaybe)
|
import Data.String (IsString (fromString))
|
||||||
import Foreign.Lua (Lua, Peekable, Pushable, StackIndex)
|
import HsLua as Lua
|
||||||
import Text.Pandoc.Definition
|
import Text.Pandoc.Definition
|
||||||
import Text.Pandoc.Error (PandocError)
|
import Text.Pandoc.Error (PandocError)
|
||||||
import Text.Pandoc.Lua.Marshaling ()
|
import Text.Pandoc.Lua.Marshaling ()
|
||||||
import Text.Pandoc.Lua.Marshaling.List (List (..))
|
import Text.Pandoc.Lua.Marshaling.AST
|
||||||
|
import Text.Pandoc.Lua.Marshaling.List (List (..), peekList')
|
||||||
import Text.Pandoc.Lua.Walk (SingletonsList (..))
|
import Text.Pandoc.Lua.Walk (SingletonsList (..))
|
||||||
import Text.Pandoc.Walk (Walkable (walkM))
|
import Text.Pandoc.Walk (Walkable (walkM))
|
||||||
|
|
||||||
import qualified Data.Map.Strict as Map
|
import qualified Data.Map.Strict as Map
|
||||||
import qualified Foreign.Lua as Lua
|
|
||||||
import qualified Text.Pandoc.Lua.Util as LuaUtil
|
import qualified Text.Pandoc.Lua.Util as LuaUtil
|
||||||
|
|
||||||
-- | Transform document using the filter defined in the given file.
|
-- | Transform document using the filter defined in the given file.
|
||||||
runFilterFile :: FilePath -> Pandoc -> Lua Pandoc
|
runFilterFile :: FilePath -> Pandoc -> LuaE PandocError Pandoc
|
||||||
runFilterFile filterPath doc = do
|
runFilterFile filterPath doc = do
|
||||||
top <- Lua.gettop
|
oldtop <- Lua.gettop
|
||||||
stat <- LuaUtil.dofileWithTraceback filterPath
|
stat <- LuaUtil.dofileWithTraceback filterPath
|
||||||
if stat /= Lua.OK
|
if stat /= Lua.OK
|
||||||
then Lua.throwTopMessage
|
then Lua.throwErrorAsException
|
||||||
else do
|
else do
|
||||||
newtop <- Lua.gettop
|
newtop <- Lua.gettop
|
||||||
-- Use the returned filters, or the implicitly defined global
|
-- Use the returned filters, or the implicitly defined global
|
||||||
-- filter if nothing was returned.
|
-- filter if nothing was returned.
|
||||||
luaFilters <- if newtop - top >= 1
|
luaFilters <- if newtop - oldtop >= 1
|
||||||
then Lua.peek Lua.stackTop
|
then Lua.peek Lua.top
|
||||||
else Lua.pushglobaltable *> fmap (:[]) Lua.popValue
|
else Lua.pushglobaltable *> fmap (:[]) Lua.popValue
|
||||||
runAll luaFilters doc
|
runAll luaFilters doc
|
||||||
|
|
||||||
runAll :: [LuaFilter] -> Pandoc -> Lua Pandoc
|
runAll :: [LuaFilter] -> Pandoc -> LuaE PandocError Pandoc
|
||||||
runAll = foldr ((>=>) . walkMWithLuaFilter) return
|
runAll = foldr ((>=>) . walkMWithLuaFilter) return
|
||||||
|
|
||||||
-- | Filter function stored in the registry
|
-- | Filter function stored in the registry
|
||||||
|
@ -63,7 +65,7 @@ newtype LuaFilterFunction = LuaFilterFunction Lua.Reference
|
||||||
|
|
||||||
-- | Collection of filter functions (at most one function per element
|
-- | Collection of filter functions (at most one function per element
|
||||||
-- constructor)
|
-- constructor)
|
||||||
newtype LuaFilter = LuaFilter (Map String LuaFilterFunction)
|
newtype LuaFilter = LuaFilter (Map Name LuaFilterFunction)
|
||||||
|
|
||||||
instance Peekable LuaFilter where
|
instance Peekable LuaFilter where
|
||||||
peek idx = do
|
peek idx = do
|
||||||
|
@ -79,19 +81,19 @@ instance Peekable LuaFilter where
|
||||||
return $ case filterFn of
|
return $ case filterFn of
|
||||||
Nothing -> acc
|
Nothing -> acc
|
||||||
Just fn -> Map.insert constr fn acc
|
Just fn -> Map.insert constr fn acc
|
||||||
LuaFilter <$> foldrM go Map.empty constrs
|
LuaFilter <$!> foldrM go Map.empty constrs
|
||||||
|
|
||||||
-- | Register the function at the top of the stack as a filter function in the
|
-- | Register the function at the top of the stack as a filter function in the
|
||||||
-- registry.
|
-- registry.
|
||||||
registerFilterFunction :: Lua (Maybe LuaFilterFunction)
|
registerFilterFunction :: LuaError e => LuaE e (Maybe LuaFilterFunction)
|
||||||
registerFilterFunction = do
|
registerFilterFunction = do
|
||||||
isFn <- Lua.isfunction Lua.stackTop
|
isFn <- Lua.isfunction Lua.top
|
||||||
if isFn
|
if isFn
|
||||||
then Just . LuaFilterFunction <$> Lua.ref Lua.registryindex
|
then Just . LuaFilterFunction <$> Lua.ref Lua.registryindex
|
||||||
else Nothing <$ Lua.pop 1
|
else Nothing <$ Lua.pop 1
|
||||||
|
|
||||||
-- | Retrieve filter function from registry and push it to the top of the stack.
|
-- | Retrieve filter function from registry and push it to the top of the stack.
|
||||||
pushFilterFunction :: LuaFilterFunction -> Lua ()
|
pushFilterFunction :: LuaFilterFunction -> LuaE PandocError ()
|
||||||
pushFilterFunction (LuaFilterFunction fnRef) =
|
pushFilterFunction (LuaFilterFunction fnRef) =
|
||||||
Lua.getref Lua.registryindex fnRef
|
Lua.getref Lua.registryindex fnRef
|
||||||
|
|
||||||
|
@ -99,58 +101,66 @@ pushFilterFunction (LuaFilterFunction fnRef) =
|
||||||
-- element instead of a list, fetch that element as a singleton list. If the top
|
-- element instead of a list, fetch that element as a singleton list. If the top
|
||||||
-- of the stack is nil, return the default element that was passed to this
|
-- of the stack is nil, return the default element that was passed to this
|
||||||
-- function. If none of these apply, raise an error.
|
-- function. If none of these apply, raise an error.
|
||||||
elementOrList :: Peekable a => a -> Lua [a]
|
elementOrList :: Peeker PandocError a -> a -> LuaE PandocError [a]
|
||||||
elementOrList x = do
|
elementOrList p x = do
|
||||||
let topOfStack = Lua.stackTop
|
elementUnchanged <- Lua.isnil top
|
||||||
elementUnchanged <- Lua.isnil topOfStack
|
|
||||||
if elementUnchanged
|
if elementUnchanged
|
||||||
then [x] <$ Lua.pop 1
|
then [x] <$ pop 1
|
||||||
else do
|
else forcePeek . (`lastly` pop 1) $ (((:[]) <$!> p top) <|> peekList p top)
|
||||||
mbres <- peekEither topOfStack
|
|
||||||
case mbres of
|
-- | Fetches a single element; returns the fallback if the value is @nil@.
|
||||||
Right res -> [res] <$ Lua.pop 1
|
singleElement :: forall a e. (LuaError e) => Peeker e a -> a -> LuaE e a
|
||||||
Left _ -> Lua.peekList topOfStack `finally` Lua.pop 1
|
singleElement p x = do
|
||||||
|
elementUnchanged <- Lua.isnil top
|
||||||
|
if elementUnchanged
|
||||||
|
then x <$ Lua.pop 1
|
||||||
|
else forcePeek $ p top `lastly` pop 1
|
||||||
|
|
||||||
-- | Pop and return a value from the stack; if the value at the top of
|
-- | Pop and return a value from the stack; if the value at the top of
|
||||||
-- the stack is @nil@, return the fallback element.
|
-- the stack is @nil@, return the fallback element.
|
||||||
popOption :: Peekable a => a -> Lua a
|
popOption :: Peeker PandocError a -> a -> LuaE PandocError a
|
||||||
popOption fallback = fromMaybe fallback . Lua.fromOptional <$> Lua.popValue
|
popOption peeker fallback = forcePeek . (`lastly` pop 1) $
|
||||||
|
(fallback <$ peekNil top) <|> peeker top
|
||||||
|
|
||||||
-- | Apply filter on a sequence of AST elements. Both lists and single
|
-- | Apply filter on a sequence of AST elements. Both lists and single
|
||||||
-- value are accepted as filter function return values.
|
-- value are accepted as filter function return values.
|
||||||
runOnSequence :: (Data a, Peekable a, Pushable a)
|
runOnSequence :: forall a. (Data a, Pushable a)
|
||||||
=> LuaFilter -> SingletonsList a -> Lua (SingletonsList a)
|
=> Peeker PandocError a -> LuaFilter -> SingletonsList a
|
||||||
runOnSequence (LuaFilter fnMap) (SingletonsList xs) =
|
-> LuaE PandocError (SingletonsList a)
|
||||||
|
runOnSequence peeker (LuaFilter fnMap) (SingletonsList xs) =
|
||||||
SingletonsList <$> mconcatMapM tryFilter xs
|
SingletonsList <$> mconcatMapM tryFilter xs
|
||||||
where
|
where
|
||||||
tryFilter :: (Data a, Peekable a, Pushable a) => a -> Lua [a]
|
tryFilter :: a -> LuaE PandocError [a]
|
||||||
tryFilter x =
|
tryFilter x =
|
||||||
let filterFnName = showConstr (toConstr x)
|
let filterFnName = fromString $ showConstr (toConstr x)
|
||||||
catchAllName = tyconUQname $ dataTypeName (dataTypeOf x)
|
catchAllName = fromString . tyconUQname $ dataTypeName (dataTypeOf x)
|
||||||
in case Map.lookup filterFnName fnMap <|> Map.lookup catchAllName fnMap of
|
in case Map.lookup filterFnName fnMap <|> Map.lookup catchAllName fnMap of
|
||||||
Just fn -> runFilterFunction fn x *> elementOrList x
|
Just fn -> runFilterFunction fn x *> elementOrList peeker x
|
||||||
Nothing -> return [x]
|
Nothing -> return [x]
|
||||||
|
|
||||||
-- | Try filtering the given value without type error corrections on
|
-- | Try filtering the given value without type error corrections on
|
||||||
-- the return value.
|
-- the return value.
|
||||||
runOnValue :: (Data a, Peekable a, Pushable a)
|
runOnValue :: (Data a, Pushable a)
|
||||||
=> String -> LuaFilter -> a -> Lua a
|
=> Name -> Peeker PandocError a
|
||||||
runOnValue filterFnName (LuaFilter fnMap) x =
|
-> LuaFilter -> a
|
||||||
|
-> LuaE PandocError a
|
||||||
|
runOnValue filterFnName peeker (LuaFilter fnMap) x =
|
||||||
case Map.lookup filterFnName fnMap of
|
case Map.lookup filterFnName fnMap of
|
||||||
Just fn -> runFilterFunction fn x *> popOption x
|
Just fn -> runFilterFunction fn x *> popOption peeker x
|
||||||
Nothing -> return x
|
Nothing -> return x
|
||||||
|
|
||||||
-- | Push a value to the stack via a lua filter function. The filter function is
|
-- | Push a value to the stack via a Lua filter function. The filter
|
||||||
-- called with given element as argument and is expected to return an element.
|
-- function is called with the given element as argument and is expected
|
||||||
-- Alternatively, the function can return nothing or nil, in which case the
|
-- to return an element. Alternatively, the function can return nothing
|
||||||
-- element is left unchanged.
|
-- or nil, in which case the element is left unchanged.
|
||||||
runFilterFunction :: Pushable a => LuaFilterFunction -> a -> Lua ()
|
runFilterFunction :: Pushable a
|
||||||
|
=> LuaFilterFunction -> a -> LuaE PandocError ()
|
||||||
runFilterFunction lf x = do
|
runFilterFunction lf x = do
|
||||||
pushFilterFunction lf
|
pushFilterFunction lf
|
||||||
Lua.push x
|
Lua.push x
|
||||||
LuaUtil.callWithTraceback 1 1
|
LuaUtil.callWithTraceback 1 1
|
||||||
|
|
||||||
walkMWithLuaFilter :: LuaFilter -> Pandoc -> Lua Pandoc
|
walkMWithLuaFilter :: LuaFilter -> Pandoc -> LuaE PandocError Pandoc
|
||||||
walkMWithLuaFilter f =
|
walkMWithLuaFilter f =
|
||||||
walkInlines f
|
walkInlines f
|
||||||
>=> walkInlineLists f
|
>=> walkInlineLists f
|
||||||
|
@ -162,92 +172,76 @@ walkMWithLuaFilter f =
|
||||||
mconcatMapM :: (Monad m) => (a -> m [a]) -> [a] -> m [a]
|
mconcatMapM :: (Monad m) => (a -> m [a]) -> [a] -> m [a]
|
||||||
mconcatMapM f = fmap mconcat . mapM f
|
mconcatMapM f = fmap mconcat . mapM f
|
||||||
|
|
||||||
hasOneOf :: LuaFilter -> [String] -> Bool
|
hasOneOf :: LuaFilter -> [Name] -> Bool
|
||||||
hasOneOf (LuaFilter fnMap) = any (`Map.member` fnMap)
|
hasOneOf (LuaFilter fnMap) = any (`Map.member` fnMap)
|
||||||
|
|
||||||
contains :: LuaFilter -> String -> Bool
|
contains :: LuaFilter -> Name -> Bool
|
||||||
contains (LuaFilter fnMap) = (`Map.member` fnMap)
|
contains (LuaFilter fnMap) = (`Map.member` fnMap)
|
||||||
|
|
||||||
walkInlines :: Walkable (SingletonsList Inline) a => LuaFilter -> a -> Lua a
|
walkInlines :: Walkable (SingletonsList Inline) a
|
||||||
|
=> LuaFilter -> a -> LuaE PandocError a
|
||||||
walkInlines lf =
|
walkInlines lf =
|
||||||
let f :: SingletonsList Inline -> Lua (SingletonsList Inline)
|
let f :: SingletonsList Inline -> LuaE PandocError (SingletonsList Inline)
|
||||||
f = runOnSequence lf
|
f = runOnSequence peekInline lf
|
||||||
in if lf `hasOneOf` inlineElementNames
|
in if lf `hasOneOf` inlineElementNames
|
||||||
then walkM f
|
then walkM f
|
||||||
else return
|
else return
|
||||||
|
|
||||||
walkInlineLists :: Walkable (List Inline) a => LuaFilter -> a -> Lua a
|
walkInlineLists :: Walkable (List Inline) a
|
||||||
|
=> LuaFilter -> a -> LuaE PandocError a
|
||||||
walkInlineLists lf =
|
walkInlineLists lf =
|
||||||
let f :: List Inline -> Lua (List Inline)
|
let f :: List Inline -> LuaE PandocError (List Inline)
|
||||||
f = runOnValue listOfInlinesFilterName lf
|
f = runOnValue listOfInlinesFilterName (peekList' peekInline) lf
|
||||||
in if lf `contains` listOfInlinesFilterName
|
in if lf `contains` listOfInlinesFilterName
|
||||||
then walkM f
|
then walkM f
|
||||||
else return
|
else return
|
||||||
|
|
||||||
walkBlocks :: Walkable (SingletonsList Block) a => LuaFilter -> a -> Lua a
|
walkBlocks :: Walkable (SingletonsList Block) a
|
||||||
|
=> LuaFilter -> a -> LuaE PandocError a
|
||||||
walkBlocks lf =
|
walkBlocks lf =
|
||||||
let f :: SingletonsList Block -> Lua (SingletonsList Block)
|
let f :: SingletonsList Block -> LuaE PandocError (SingletonsList Block)
|
||||||
f = runOnSequence lf
|
f = runOnSequence peekBlock lf
|
||||||
in if lf `hasOneOf` blockElementNames
|
in if lf `hasOneOf` blockElementNames
|
||||||
then walkM f
|
then walkM f
|
||||||
else return
|
else return
|
||||||
|
|
||||||
walkBlockLists :: Walkable (List Block) a => LuaFilter -> a -> Lua a
|
walkBlockLists :: Walkable (List Block) a
|
||||||
|
=> LuaFilter -> a -> LuaE PandocError a
|
||||||
walkBlockLists lf =
|
walkBlockLists lf =
|
||||||
let f :: List Block -> Lua (List Block)
|
let f :: List Block -> LuaE PandocError (List Block)
|
||||||
f = runOnValue listOfBlocksFilterName lf
|
f = runOnValue listOfBlocksFilterName (peekList' peekBlock) lf
|
||||||
in if lf `contains` listOfBlocksFilterName
|
in if lf `contains` listOfBlocksFilterName
|
||||||
then walkM f
|
then walkM f
|
||||||
else return
|
else return
|
||||||
|
|
||||||
walkMeta :: LuaFilter -> Pandoc -> Lua Pandoc
|
walkMeta :: LuaFilter -> Pandoc -> LuaE PandocError Pandoc
|
||||||
walkMeta lf (Pandoc m bs) = do
|
walkMeta lf (Pandoc m bs) = do
|
||||||
m' <- runOnValue "Meta" lf m
|
m' <- runOnValue "Meta" peekMeta lf m
|
||||||
return $ Pandoc m' bs
|
return $ Pandoc m' bs
|
||||||
|
|
||||||
walkPandoc :: LuaFilter -> Pandoc -> Lua Pandoc
|
walkPandoc :: LuaFilter -> Pandoc -> LuaE PandocError Pandoc
|
||||||
walkPandoc (LuaFilter fnMap) =
|
walkPandoc (LuaFilter fnMap) =
|
||||||
case foldl' mplus Nothing (map (`Map.lookup` fnMap) pandocFilterNames) of
|
case foldl' mplus Nothing (map (`Map.lookup` fnMap) pandocFilterNames) of
|
||||||
Just fn -> \x -> runFilterFunction fn x *> singleElement x
|
Just fn -> \x -> runFilterFunction fn x *> singleElement peekPandoc x
|
||||||
Nothing -> return
|
Nothing -> return
|
||||||
|
|
||||||
constructorsFor :: DataType -> [String]
|
constructorsFor :: DataType -> [Name]
|
||||||
constructorsFor x = map show (dataTypeConstrs x)
|
constructorsFor x = map (fromString . show) (dataTypeConstrs x)
|
||||||
|
|
||||||
inlineElementNames :: [String]
|
inlineElementNames :: [Name]
|
||||||
inlineElementNames = "Inline" : constructorsFor (dataTypeOf (Str mempty))
|
inlineElementNames = "Inline" : constructorsFor (dataTypeOf (Str mempty))
|
||||||
|
|
||||||
blockElementNames :: [String]
|
blockElementNames :: [Name]
|
||||||
blockElementNames = "Block" : constructorsFor (dataTypeOf (Para []))
|
blockElementNames = "Block" : constructorsFor (dataTypeOf (Para []))
|
||||||
|
|
||||||
listOfInlinesFilterName :: String
|
listOfInlinesFilterName :: Name
|
||||||
listOfInlinesFilterName = "Inlines"
|
listOfInlinesFilterName = "Inlines"
|
||||||
|
|
||||||
listOfBlocksFilterName :: String
|
listOfBlocksFilterName :: Name
|
||||||
listOfBlocksFilterName = "Blocks"
|
listOfBlocksFilterName = "Blocks"
|
||||||
|
|
||||||
metaFilterName :: String
|
metaFilterName :: Name
|
||||||
metaFilterName = "Meta"
|
metaFilterName = "Meta"
|
||||||
|
|
||||||
pandocFilterNames :: [String]
|
pandocFilterNames :: [Name]
|
||||||
pandocFilterNames = ["Pandoc", "Doc"]
|
pandocFilterNames = ["Pandoc", "Doc"]
|
||||||
|
|
||||||
singleElement :: Peekable a => a -> Lua a
|
|
||||||
singleElement x = do
|
|
||||||
elementUnchanged <- Lua.isnil (-1)
|
|
||||||
if elementUnchanged
|
|
||||||
then x <$ Lua.pop 1
|
|
||||||
else do
|
|
||||||
mbres <- peekEither (-1)
|
|
||||||
case mbres of
|
|
||||||
Right res -> res <$ Lua.pop 1
|
|
||||||
Left err -> do
|
|
||||||
Lua.pop 1
|
|
||||||
Lua.throwMessage
|
|
||||||
("Error while trying to get a filter's return " <>
|
|
||||||
"value from Lua stack.\n" <> show err)
|
|
||||||
|
|
||||||
-- | Try to convert the value at the given stack index to a Haskell value.
|
|
||||||
-- Returns @Left@ with an error message on failure.
|
|
||||||
peekEither :: Peekable a => StackIndex -> Lua (Either PandocError a)
|
|
||||||
peekEither = try . Lua.peek
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
{-# LANGUAGE DeriveDataTypeable #-}
|
{-# LANGUAGE OverloadedStrings #-}
|
||||||
{- |
|
{- |
|
||||||
Module : Text.Pandoc.Lua
|
Module : Text.Pandoc.Lua
|
||||||
Copyright : Copyright © 2017-2021 Albert Krewinkel
|
Copyright : Copyright © 2017-2021 Albert Krewinkel
|
||||||
|
@ -14,19 +14,17 @@ module Text.Pandoc.Lua.Global
|
||||||
, setGlobals
|
, setGlobals
|
||||||
) where
|
) where
|
||||||
|
|
||||||
import Data.Data (Data)
|
import HsLua as Lua
|
||||||
import Foreign.Lua (Lua, Peekable, Pushable)
|
|
||||||
import Foreign.Lua.Userdata ( ensureUserdataMetatable, pushAnyWithMetatable
|
|
||||||
, metatableName)
|
|
||||||
import Paths_pandoc (version)
|
import Paths_pandoc (version)
|
||||||
import Text.Pandoc.Class.CommonState (CommonState)
|
import Text.Pandoc.Class.CommonState (CommonState)
|
||||||
import Text.Pandoc.Definition (Pandoc (Pandoc), pandocTypesVersion)
|
import Text.Pandoc.Definition (Pandoc (Pandoc), pandocTypesVersion)
|
||||||
|
import Text.Pandoc.Error (PandocError)
|
||||||
import Text.Pandoc.Lua.Marshaling ()
|
import Text.Pandoc.Lua.Marshaling ()
|
||||||
import Text.Pandoc.Lua.Util (addFunction)
|
import Text.Pandoc.Lua.Marshaling.CommonState (pushCommonState)
|
||||||
|
import Text.Pandoc.Lua.Marshaling.ReaderOptions (pushReaderOptions)
|
||||||
import Text.Pandoc.Options (ReaderOptions)
|
import Text.Pandoc.Options (ReaderOptions)
|
||||||
|
|
||||||
import qualified Data.Text as Text
|
import qualified Data.Text as Text
|
||||||
import qualified Foreign.Lua as Lua
|
|
||||||
|
|
||||||
-- | Permissible global Lua variables.
|
-- | Permissible global Lua variables.
|
||||||
data Global =
|
data Global =
|
||||||
|
@ -40,10 +38,10 @@ data Global =
|
||||||
-- Cannot derive instance of Data because of CommonState
|
-- Cannot derive instance of Data because of CommonState
|
||||||
|
|
||||||
-- | Set all given globals.
|
-- | Set all given globals.
|
||||||
setGlobals :: [Global] -> Lua ()
|
setGlobals :: [Global] -> LuaE PandocError ()
|
||||||
setGlobals = mapM_ setGlobal
|
setGlobals = mapM_ setGlobal
|
||||||
|
|
||||||
setGlobal :: Global -> Lua ()
|
setGlobal :: Global -> LuaE PandocError ()
|
||||||
setGlobal global = case global of
|
setGlobal global = case global of
|
||||||
-- This could be simplified if Global was an instance of Data.
|
-- This could be simplified if Global was an instance of Data.
|
||||||
FORMAT format -> do
|
FORMAT format -> do
|
||||||
|
@ -53,37 +51,24 @@ setGlobal global = case global of
|
||||||
Lua.push pandocTypesVersion
|
Lua.push pandocTypesVersion
|
||||||
Lua.setglobal "PANDOC_API_VERSION"
|
Lua.setglobal "PANDOC_API_VERSION"
|
||||||
PANDOC_DOCUMENT doc -> do
|
PANDOC_DOCUMENT doc -> do
|
||||||
Lua.push (LazyPandoc doc)
|
pushUD typePandocLazy doc
|
||||||
Lua.setglobal "PANDOC_DOCUMENT"
|
Lua.setglobal "PANDOC_DOCUMENT"
|
||||||
PANDOC_READER_OPTIONS ropts -> do
|
PANDOC_READER_OPTIONS ropts -> do
|
||||||
Lua.push ropts
|
pushReaderOptions ropts
|
||||||
Lua.setglobal "PANDOC_READER_OPTIONS"
|
Lua.setglobal "PANDOC_READER_OPTIONS"
|
||||||
PANDOC_SCRIPT_FILE filePath -> do
|
PANDOC_SCRIPT_FILE filePath -> do
|
||||||
Lua.push filePath
|
Lua.push filePath
|
||||||
Lua.setglobal "PANDOC_SCRIPT_FILE"
|
Lua.setglobal "PANDOC_SCRIPT_FILE"
|
||||||
PANDOC_STATE commonState -> do
|
PANDOC_STATE commonState -> do
|
||||||
Lua.push commonState
|
pushCommonState commonState
|
||||||
Lua.setglobal "PANDOC_STATE"
|
Lua.setglobal "PANDOC_STATE"
|
||||||
PANDOC_VERSION -> do
|
PANDOC_VERSION -> do
|
||||||
Lua.push version
|
Lua.push version
|
||||||
Lua.setglobal "PANDOC_VERSION"
|
Lua.setglobal "PANDOC_VERSION"
|
||||||
|
|
||||||
-- | Readonly and lazy pandoc objects.
|
-- | Readonly and lazy pandoc objects.
|
||||||
newtype LazyPandoc = LazyPandoc Pandoc
|
typePandocLazy :: LuaError e => DocumentedType e Pandoc
|
||||||
deriving (Data)
|
typePandocLazy = deftype "Pandoc (lazy)" []
|
||||||
|
[ readonly "meta" "document metadata" (push, \(Pandoc meta _) -> meta)
|
||||||
instance Pushable LazyPandoc where
|
, readonly "blocks" "content blocks" (push, \(Pandoc _ blocks) -> blocks)
|
||||||
push lazyDoc = pushAnyWithMetatable pushPandocMetatable lazyDoc
|
]
|
||||||
where
|
|
||||||
pushPandocMetatable = ensureUserdataMetatable (metatableName lazyDoc) $
|
|
||||||
addFunction "__index" indexLazyPandoc
|
|
||||||
|
|
||||||
instance Peekable LazyPandoc where
|
|
||||||
peek = Lua.peekAny
|
|
||||||
|
|
||||||
indexLazyPandoc :: LazyPandoc -> String -> Lua Lua.NumResults
|
|
||||||
indexLazyPandoc (LazyPandoc (Pandoc meta blks)) field = 1 <$
|
|
||||||
case field of
|
|
||||||
"blocks" -> Lua.push blks
|
|
||||||
"meta" -> Lua.push meta
|
|
||||||
_ -> Lua.pushnil
|
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
{-# LANGUAGE OverloadedStrings #-}
|
||||||
{- |
|
{- |
|
||||||
Module : Text.Pandoc.Lua
|
Module : Text.Pandoc.Lua
|
||||||
Copyright : Copyright © 2017-2021 Albert Krewinkel
|
Copyright : Copyright © 2017-2021 Albert Krewinkel
|
||||||
|
@ -13,23 +14,23 @@ module Text.Pandoc.Lua.Init
|
||||||
) where
|
) where
|
||||||
|
|
||||||
import Control.Monad (when)
|
import Control.Monad (when)
|
||||||
import Control.Monad.Catch (try)
|
import Control.Monad.Catch (throwM, try)
|
||||||
import Control.Monad.Trans (MonadIO (..))
|
import Control.Monad.Trans (MonadIO (..))
|
||||||
import Data.Data (Data, dataTypeConstrs, dataTypeOf, showConstr)
|
import Data.Data (Data, dataTypeConstrs, dataTypeOf, showConstr)
|
||||||
import Foreign.Lua (Lua)
|
import HsLua as Lua hiding (status, try)
|
||||||
import GHC.IO.Encoding (getForeignEncoding, setForeignEncoding, utf8)
|
import GHC.IO.Encoding (getForeignEncoding, setForeignEncoding, utf8)
|
||||||
import Text.Pandoc.Class.PandocMonad (readDataFile, PandocMonad)
|
import Text.Pandoc.Class.PandocMonad (PandocMonad, readDataFile)
|
||||||
import Text.Pandoc.Error (PandocError)
|
import Text.Pandoc.Error (PandocError (PandocLuaError))
|
||||||
import Text.Pandoc.Lua.Packages (installPandocPackageSearcher)
|
import Text.Pandoc.Lua.Packages (installPandocPackageSearcher)
|
||||||
import Text.Pandoc.Lua.PandocLua (PandocLua, liftPandocLua, runPandocLua)
|
import Text.Pandoc.Lua.PandocLua (PandocLua, liftPandocLua, runPandocLua)
|
||||||
import Text.Pandoc.Lua.Util (throwTopMessageAsError')
|
import qualified Data.Text as T
|
||||||
import qualified Foreign.Lua as Lua
|
|
||||||
import qualified Text.Pandoc.Definition as Pandoc
|
import qualified Text.Pandoc.Definition as Pandoc
|
||||||
import qualified Text.Pandoc.Lua.Module.Pandoc as ModulePandoc
|
import qualified Text.Pandoc.Lua.Module.Pandoc as ModulePandoc
|
||||||
|
|
||||||
-- | Run the lua interpreter, using pandoc's default way of environment
|
-- | Run the lua interpreter, using pandoc's default way of environment
|
||||||
-- initialization.
|
-- initialization.
|
||||||
runLua :: (PandocMonad m, MonadIO m) => Lua a -> m (Either PandocError a)
|
runLua :: (PandocMonad m, MonadIO m)
|
||||||
|
=> LuaE PandocError a -> m (Either PandocError a)
|
||||||
runLua luaOp = do
|
runLua luaOp = do
|
||||||
enc <- liftIO $ getForeignEncoding <* setForeignEncoding utf8
|
enc <- liftIO $ getForeignEncoding <* setForeignEncoding utf8
|
||||||
res <- runPandocLua . try $ do
|
res <- runPandocLua . try $ do
|
||||||
|
@ -52,9 +53,9 @@ initLuaState = do
|
||||||
ModulePandoc.pushModule
|
ModulePandoc.pushModule
|
||||||
-- register as loaded module
|
-- register as loaded module
|
||||||
liftPandocLua $ do
|
liftPandocLua $ do
|
||||||
Lua.pushvalue Lua.stackTop
|
Lua.pushvalue Lua.top
|
||||||
Lua.getfield Lua.registryindex Lua.loadedTableRegistryField
|
Lua.getfield Lua.registryindex Lua.loaded
|
||||||
Lua.setfield (Lua.nthFromTop 2) "pandoc"
|
Lua.setfield (Lua.nth 2) "pandoc"
|
||||||
Lua.pop 1
|
Lua.pop 1
|
||||||
-- copy constructors into registry
|
-- copy constructors into registry
|
||||||
putConstructorsInRegistry
|
putConstructorsInRegistry
|
||||||
|
@ -65,10 +66,12 @@ initLuaState = do
|
||||||
loadInitScript scriptFile = do
|
loadInitScript scriptFile = do
|
||||||
script <- readDataFile scriptFile
|
script <- readDataFile scriptFile
|
||||||
status <- liftPandocLua $ Lua.dostring script
|
status <- liftPandocLua $ Lua.dostring script
|
||||||
when (status /= Lua.OK) . liftPandocLua $
|
when (status /= Lua.OK) . liftPandocLua $ do
|
||||||
throwTopMessageAsError'
|
err <- popException
|
||||||
(("Couldn't load '" ++ scriptFile ++ "'.\n") ++)
|
let prefix = "Couldn't load '" <> T.pack scriptFile <> "':\n"
|
||||||
|
throwM . PandocLuaError . (prefix <>) $ case err of
|
||||||
|
PandocLuaError msg -> msg
|
||||||
|
_ -> T.pack $ show err
|
||||||
|
|
||||||
-- | AST elements are marshaled via normal constructor functions in the
|
-- | AST elements are marshaled via normal constructor functions in the
|
||||||
-- @pandoc@ module. However, accessing Lua globals from Haskell is
|
-- @pandoc@ module. However, accessing Lua globals from Haskell is
|
||||||
|
@ -91,12 +94,12 @@ putConstructorsInRegistry = liftPandocLua $ do
|
||||||
putInReg "List" -- pandoc.List
|
putInReg "List" -- pandoc.List
|
||||||
putInReg "SimpleTable" -- helper for backward-compatible table handling
|
putInReg "SimpleTable" -- helper for backward-compatible table handling
|
||||||
where
|
where
|
||||||
constrsToReg :: Data a => a -> Lua ()
|
constrsToReg :: Data a => a -> LuaE PandocError ()
|
||||||
constrsToReg = mapM_ (putInReg . showConstr) . dataTypeConstrs . dataTypeOf
|
constrsToReg = mapM_ (putInReg . showConstr) . dataTypeConstrs . dataTypeOf
|
||||||
|
|
||||||
putInReg :: String -> Lua ()
|
putInReg :: String -> LuaE PandocError ()
|
||||||
putInReg name = do
|
putInReg name = do
|
||||||
Lua.push ("pandoc." ++ name) -- name in registry
|
Lua.push ("pandoc." ++ name) -- name in registry
|
||||||
Lua.push name -- in pandoc module
|
Lua.push name -- in pandoc module
|
||||||
Lua.rawget (Lua.nthFromTop 3)
|
Lua.rawget (Lua.nth 3)
|
||||||
Lua.rawset Lua.registryindex
|
Lua.rawset Lua.registryindex
|
||||||
|
|
|
@ -17,3 +17,4 @@ import Text.Pandoc.Lua.Marshaling.Context ()
|
||||||
import Text.Pandoc.Lua.Marshaling.PandocError()
|
import Text.Pandoc.Lua.Marshaling.PandocError()
|
||||||
import Text.Pandoc.Lua.Marshaling.ReaderOptions ()
|
import Text.Pandoc.Lua.Marshaling.ReaderOptions ()
|
||||||
import Text.Pandoc.Lua.Marshaling.Version ()
|
import Text.Pandoc.Lua.Marshaling.Version ()
|
||||||
|
import Text.Pandoc.Lua.ErrorConversion ()
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
{-# OPTIONS_GHC -fno-warn-orphans #-}
|
{-# OPTIONS_GHC -fno-warn-orphans #-}
|
||||||
{-# LANGUAGE BangPatterns #-}
|
|
||||||
{-# LANGUAGE LambdaCase #-}
|
{-# LANGUAGE LambdaCase #-}
|
||||||
|
{-# LANGUAGE OverloadedStrings #-}
|
||||||
|
{-# LANGUAGE ScopedTypeVariables #-}
|
||||||
|
{-# LANGUAGE TypeApplications #-}
|
||||||
{- |
|
{- |
|
||||||
Module : Text.Pandoc.Lua.Marshaling.AST
|
Module : Text.Pandoc.Lua.Marshaling.AST
|
||||||
Copyright : © 2012-2021 John MacFarlane
|
Copyright : © 2012-2021 John MacFarlane
|
||||||
|
@ -13,223 +15,254 @@
|
||||||
Marshaling/unmarshaling instances for document AST elements.
|
Marshaling/unmarshaling instances for document AST elements.
|
||||||
-}
|
-}
|
||||||
module Text.Pandoc.Lua.Marshaling.AST
|
module Text.Pandoc.Lua.Marshaling.AST
|
||||||
( LuaAttr (..)
|
( peekAttr
|
||||||
, LuaListAttributes (..)
|
, peekBlock
|
||||||
|
, peekBlocks
|
||||||
|
, peekCaption
|
||||||
|
, peekCitation
|
||||||
|
, peekInline
|
||||||
|
, peekInlines
|
||||||
|
, peekListAttributes
|
||||||
|
, peekMeta
|
||||||
|
, peekMetaValue
|
||||||
|
, peekPandoc
|
||||||
|
|
||||||
|
, pushAttr
|
||||||
|
, pushBlock
|
||||||
|
, pushInline
|
||||||
|
, pushListAttributes
|
||||||
|
, pushMetaValue
|
||||||
|
, pushPandoc
|
||||||
) where
|
) where
|
||||||
|
|
||||||
import Control.Applicative ((<|>))
|
import Control.Applicative ((<|>), optional)
|
||||||
import Control.Monad ((<$!>))
|
import Control.Monad ((<$!>), (>=>))
|
||||||
import Foreign.Lua (Lua, Peekable, Pushable, StackIndex)
|
import HsLua hiding (Operation (Div))
|
||||||
import Text.Pandoc.Definition
|
import Text.Pandoc.Definition
|
||||||
import Text.Pandoc.Error (PandocError)
|
import Text.Pandoc.Lua.Util (pushViaConstr', pushViaConstructor)
|
||||||
import Text.Pandoc.Lua.Util (defineHowTo, pushViaConstructor)
|
|
||||||
import Text.Pandoc.Lua.Marshaling.CommonState ()
|
import Text.Pandoc.Lua.Marshaling.CommonState ()
|
||||||
|
|
||||||
import qualified Control.Monad.Catch as Catch
|
import qualified HsLua as Lua
|
||||||
import qualified Foreign.Lua as Lua
|
|
||||||
import qualified Text.Pandoc.Lua.Util as LuaUtil
|
import qualified Text.Pandoc.Lua.Util as LuaUtil
|
||||||
|
|
||||||
instance Pushable Pandoc where
|
instance Pushable Pandoc where
|
||||||
push (Pandoc meta blocks) =
|
push = pushPandoc
|
||||||
pushViaConstructor "Pandoc" blocks meta
|
|
||||||
|
|
||||||
instance Peekable Pandoc where
|
pushPandoc :: LuaError e => Pusher e Pandoc
|
||||||
peek idx = defineHowTo "get Pandoc value" $! Pandoc
|
pushPandoc (Pandoc meta blocks) =
|
||||||
<$!> LuaUtil.rawField idx "meta"
|
pushViaConstr' "Pandoc" [pushList pushBlock blocks, push meta]
|
||||||
<*> LuaUtil.rawField idx "blocks"
|
|
||||||
|
peekPandoc :: LuaError e => Peeker e Pandoc
|
||||||
|
peekPandoc = fmap (retrieving "Pandoc value")
|
||||||
|
. typeChecked "table" Lua.istable $ \idx -> do
|
||||||
|
meta <- peekFieldRaw peekMeta "meta" idx
|
||||||
|
blks <- peekFieldRaw peekBlocks "blocks" idx
|
||||||
|
return $ Pandoc meta blks
|
||||||
|
|
||||||
instance Pushable Meta where
|
instance Pushable Meta where
|
||||||
push (Meta mmap) =
|
push (Meta mmap) =
|
||||||
pushViaConstructor "Meta" mmap
|
pushViaConstr' "Meta" [push mmap]
|
||||||
instance Peekable Meta where
|
|
||||||
peek idx = defineHowTo "get Meta value" $!
|
peekMeta :: LuaError e => Peeker e Meta
|
||||||
Meta <$!> Lua.peek idx
|
peekMeta idx = retrieving "Meta" $
|
||||||
|
Meta <$!> peekMap peekText peekMetaValue idx
|
||||||
|
|
||||||
instance Pushable MetaValue where
|
instance Pushable MetaValue where
|
||||||
push = pushMetaValue
|
push = pushMetaValue
|
||||||
instance Peekable MetaValue where
|
|
||||||
peek = peekMetaValue
|
|
||||||
|
|
||||||
instance Pushable Block where
|
instance Pushable Block where
|
||||||
push = pushBlock
|
push = pushBlock
|
||||||
|
|
||||||
instance Peekable Block where
|
|
||||||
peek = peekBlock
|
|
||||||
|
|
||||||
-- Inline
|
-- Inline
|
||||||
instance Pushable Inline where
|
instance Pushable Inline where
|
||||||
push = pushInline
|
push = pushInline
|
||||||
|
|
||||||
instance Peekable Inline where
|
|
||||||
peek = peekInline
|
|
||||||
|
|
||||||
-- Citation
|
-- Citation
|
||||||
instance Pushable Citation where
|
instance Pushable Citation where
|
||||||
push (Citation cid prefix suffix mode noteNum hash) =
|
push (Citation cid prefix suffix mode noteNum hash) =
|
||||||
pushViaConstructor "Citation" cid mode prefix suffix noteNum hash
|
pushViaConstr' "Citation"
|
||||||
|
[ push cid, push mode, push prefix, push suffix, push noteNum, push hash
|
||||||
|
]
|
||||||
|
|
||||||
|
peekCitation :: LuaError e => Peeker e Citation
|
||||||
|
peekCitation = fmap (retrieving "Citation")
|
||||||
|
. typeChecked "table" Lua.istable $ \idx -> do
|
||||||
|
idx' <- liftLua $ absindex idx
|
||||||
|
Citation
|
||||||
|
<$!> peekFieldRaw peekText "id" idx'
|
||||||
|
<*> peekFieldRaw (peekList peekInline) "prefix" idx'
|
||||||
|
<*> peekFieldRaw (peekList peekInline) "suffix" idx'
|
||||||
|
<*> peekFieldRaw peekRead "mode" idx'
|
||||||
|
<*> peekFieldRaw peekIntegral "note_num" idx'
|
||||||
|
<*> peekFieldRaw peekIntegral "hash" idx'
|
||||||
|
|
||||||
instance Peekable Citation where
|
|
||||||
peek idx = Citation
|
|
||||||
<$!> LuaUtil.rawField idx "id"
|
|
||||||
<*> LuaUtil.rawField idx "prefix"
|
|
||||||
<*> LuaUtil.rawField idx "suffix"
|
|
||||||
<*> LuaUtil.rawField idx "mode"
|
|
||||||
<*> LuaUtil.rawField idx "note_num"
|
|
||||||
<*> LuaUtil.rawField idx "hash"
|
|
||||||
|
|
||||||
instance Pushable Alignment where
|
instance Pushable Alignment where
|
||||||
push = Lua.push . show
|
push = Lua.pushString . show
|
||||||
instance Peekable Alignment where
|
|
||||||
peek = Lua.peekRead
|
|
||||||
|
|
||||||
instance Pushable CitationMode where
|
instance Pushable CitationMode where
|
||||||
push = Lua.push . show
|
push = Lua.push . show
|
||||||
instance Peekable CitationMode where
|
|
||||||
peek = Lua.peekRead
|
|
||||||
|
|
||||||
instance Pushable Format where
|
instance Pushable Format where
|
||||||
push (Format f) = Lua.push f
|
push (Format f) = Lua.push f
|
||||||
instance Peekable Format where
|
|
||||||
peek idx = Format <$!> Lua.peek idx
|
peekFormat :: LuaError e => Peeker e Format
|
||||||
|
peekFormat idx = Format <$!> peekText idx
|
||||||
|
|
||||||
instance Pushable ListNumberDelim where
|
instance Pushable ListNumberDelim where
|
||||||
push = Lua.push . show
|
push = Lua.push . show
|
||||||
instance Peekable ListNumberDelim where
|
|
||||||
peek = Lua.peekRead
|
|
||||||
|
|
||||||
instance Pushable ListNumberStyle where
|
instance Pushable ListNumberStyle where
|
||||||
push = Lua.push . show
|
push = Lua.push . show
|
||||||
instance Peekable ListNumberStyle where
|
|
||||||
peek = Lua.peekRead
|
|
||||||
|
|
||||||
instance Pushable MathType where
|
instance Pushable MathType where
|
||||||
push = Lua.push . show
|
push = Lua.push . show
|
||||||
instance Peekable MathType where
|
|
||||||
peek = Lua.peekRead
|
|
||||||
|
|
||||||
instance Pushable QuoteType where
|
instance Pushable QuoteType where
|
||||||
push = Lua.push . show
|
push = Lua.push . show
|
||||||
instance Peekable QuoteType where
|
|
||||||
peek = Lua.peekRead
|
|
||||||
|
|
||||||
-- | Push an meta value element to the top of the lua stack.
|
-- | Push an meta value element to the top of the lua stack.
|
||||||
pushMetaValue :: MetaValue -> Lua ()
|
pushMetaValue :: LuaError e => MetaValue -> LuaE e ()
|
||||||
pushMetaValue = \case
|
pushMetaValue = \case
|
||||||
MetaBlocks blcks -> pushViaConstructor "MetaBlocks" blcks
|
MetaBlocks blcks -> pushViaConstr' "MetaBlocks" [pushList pushBlock blcks]
|
||||||
MetaBool bool -> Lua.push bool
|
MetaBool bool -> Lua.push bool
|
||||||
MetaInlines inlns -> pushViaConstructor "MetaInlines" inlns
|
MetaInlines inlns -> pushViaConstr' "MetaInlines"
|
||||||
MetaList metalist -> pushViaConstructor "MetaList" metalist
|
[pushList pushInline inlns]
|
||||||
MetaMap metamap -> pushViaConstructor "MetaMap" metamap
|
MetaList metalist -> pushViaConstr' "MetaList"
|
||||||
|
[pushList pushMetaValue metalist]
|
||||||
|
MetaMap metamap -> pushViaConstr' "MetaMap"
|
||||||
|
[pushMap pushText pushMetaValue metamap]
|
||||||
MetaString str -> Lua.push str
|
MetaString str -> Lua.push str
|
||||||
|
|
||||||
-- | Interpret the value at the given stack index as meta value.
|
-- | Interpret the value at the given stack index as meta value.
|
||||||
peekMetaValue :: StackIndex -> Lua MetaValue
|
peekMetaValue :: forall e. LuaError e => Peeker e MetaValue
|
||||||
peekMetaValue idx = defineHowTo "get MetaValue" $ do
|
peekMetaValue = retrieving "MetaValue $ " . \idx -> do
|
||||||
-- Get the contents of an AST element.
|
-- Get the contents of an AST element.
|
||||||
let elementContent :: Peekable a => Lua a
|
let mkMV :: (a -> MetaValue) -> Peeker e a -> Peek e MetaValue
|
||||||
elementContent = Lua.peek idx
|
mkMV f p = f <$!> p idx
|
||||||
luatype <- Lua.ltype idx
|
|
||||||
case luatype of
|
peekTagged = \case
|
||||||
Lua.TypeBoolean -> MetaBool <$!> Lua.peek idx
|
"MetaBlocks" -> mkMV MetaBlocks $
|
||||||
Lua.TypeString -> MetaString <$!> Lua.peek idx
|
retrieving "MetaBlocks" . peekBlocks
|
||||||
Lua.TypeTable -> do
|
"MetaBool" -> mkMV MetaBool $
|
||||||
tag <- try $ LuaUtil.getTag idx
|
retrieving "MetaBool" . peekBool
|
||||||
case tag of
|
"MetaMap" -> mkMV MetaMap $
|
||||||
Right "MetaBlocks" -> MetaBlocks <$!> elementContent
|
retrieving "MetaMap" . peekMap peekText peekMetaValue
|
||||||
Right "MetaBool" -> MetaBool <$!> elementContent
|
"MetaInlines" -> mkMV MetaInlines $
|
||||||
Right "MetaMap" -> MetaMap <$!> elementContent
|
retrieving "MetaInlines" . peekInlines
|
||||||
Right "MetaInlines" -> MetaInlines <$!> elementContent
|
"MetaList" -> mkMV MetaList $
|
||||||
Right "MetaList" -> MetaList <$!> elementContent
|
retrieving "MetaList" . peekList peekMetaValue
|
||||||
Right "MetaString" -> MetaString <$!> elementContent
|
"MetaString" -> mkMV MetaString $
|
||||||
Right t -> Lua.throwMessage ("Unknown meta tag: " <> t)
|
retrieving "MetaString" . peekText
|
||||||
Left _ -> do
|
(Name t) -> failPeek ("Unknown meta tag: " <> t)
|
||||||
|
|
||||||
|
peekUntagged = do
|
||||||
-- no meta value tag given, try to guess.
|
-- no meta value tag given, try to guess.
|
||||||
len <- Lua.rawlen idx
|
len <- liftLua $ Lua.rawlen idx
|
||||||
if len <= 0
|
if len <= 0
|
||||||
then MetaMap <$!> Lua.peek idx
|
then MetaMap <$!> peekMap peekText peekMetaValue idx
|
||||||
else (MetaInlines <$!> Lua.peek idx)
|
else (MetaInlines <$!> peekInlines idx)
|
||||||
<|> (MetaBlocks <$!> Lua.peek idx)
|
<|> (MetaBlocks <$!> peekBlocks idx)
|
||||||
<|> (MetaList <$!> Lua.peek idx)
|
<|> (MetaList <$!> peekList peekMetaValue idx)
|
||||||
_ -> Lua.throwMessage "could not get meta value"
|
luatype <- liftLua $ Lua.ltype idx
|
||||||
|
case luatype of
|
||||||
|
Lua.TypeBoolean -> MetaBool <$!> peekBool idx
|
||||||
|
Lua.TypeString -> MetaString <$!> peekText idx
|
||||||
|
Lua.TypeTable -> do
|
||||||
|
optional (LuaUtil.getTag idx) >>= \case
|
||||||
|
Just tag -> peekTagged tag
|
||||||
|
Nothing -> peekUntagged
|
||||||
|
_ -> failPeek "could not get meta value"
|
||||||
|
|
||||||
-- | Push a block element to the top of the Lua stack.
|
-- | Push a block element to the top of the Lua stack.
|
||||||
pushBlock :: Block -> Lua ()
|
pushBlock :: forall e. LuaError e => Block -> LuaE e ()
|
||||||
pushBlock = \case
|
pushBlock = \case
|
||||||
BlockQuote blcks -> pushViaConstructor "BlockQuote" blcks
|
BlockQuote blcks -> pushViaConstructor @e "BlockQuote" blcks
|
||||||
BulletList items -> pushViaConstructor "BulletList" items
|
BulletList items -> pushViaConstructor @e "BulletList" items
|
||||||
CodeBlock attr code -> pushViaConstructor "CodeBlock" code (LuaAttr attr)
|
CodeBlock attr code -> pushViaConstr' @e "CodeBlock"
|
||||||
DefinitionList items -> pushViaConstructor "DefinitionList" items
|
[ push code, pushAttr attr ]
|
||||||
Div attr blcks -> pushViaConstructor "Div" blcks (LuaAttr attr)
|
DefinitionList items -> pushViaConstructor @e "DefinitionList" items
|
||||||
Header lvl attr inlns -> pushViaConstructor "Header" lvl inlns (LuaAttr attr)
|
Div attr blcks -> pushViaConstr' @e "Div"
|
||||||
HorizontalRule -> pushViaConstructor "HorizontalRule"
|
[push blcks, pushAttr attr]
|
||||||
LineBlock blcks -> pushViaConstructor "LineBlock" blcks
|
Header lvl attr inlns -> pushViaConstr' @e "Header"
|
||||||
OrderedList lstAttr list -> pushViaConstructor "OrderedList" list
|
[push lvl, push inlns, pushAttr attr]
|
||||||
(LuaListAttributes lstAttr)
|
HorizontalRule -> pushViaConstructor @e "HorizontalRule"
|
||||||
Null -> pushViaConstructor "Null"
|
LineBlock blcks -> pushViaConstructor @e "LineBlock" blcks
|
||||||
Para blcks -> pushViaConstructor "Para" blcks
|
OrderedList lstAttr list -> pushViaConstr' @e "OrderedList"
|
||||||
Plain blcks -> pushViaConstructor "Plain" blcks
|
[ push list, pushListAttributes @e lstAttr ]
|
||||||
RawBlock f cs -> pushViaConstructor "RawBlock" f cs
|
Null -> pushViaConstructor @e "Null"
|
||||||
|
Para blcks -> pushViaConstructor @e "Para" blcks
|
||||||
|
Plain blcks -> pushViaConstructor @e "Plain" blcks
|
||||||
|
RawBlock f cs -> pushViaConstructor @e "RawBlock" f cs
|
||||||
Table attr blkCapt specs thead tbody tfoot ->
|
Table attr blkCapt specs thead tbody tfoot ->
|
||||||
pushViaConstructor "Table" blkCapt specs thead tbody tfoot attr
|
pushViaConstr' @e "Table"
|
||||||
|
[ pushCaption blkCapt, push specs, push thead, push tbody
|
||||||
|
, push tfoot, pushAttr attr]
|
||||||
|
|
||||||
-- | Return the value at the given index as block if possible.
|
-- | Return the value at the given index as block if possible.
|
||||||
peekBlock :: StackIndex -> Lua Block
|
peekBlock :: forall e. LuaError e => Peeker e Block
|
||||||
peekBlock idx = defineHowTo "get Block value" $! do
|
peekBlock = fmap (retrieving "Block")
|
||||||
tag <- LuaUtil.getTag idx
|
. typeChecked "table" Lua.istable
|
||||||
case tag of
|
$ \idx -> do
|
||||||
"BlockQuote" -> BlockQuote <$!> elementContent
|
|
||||||
"BulletList" -> BulletList <$!> elementContent
|
|
||||||
"CodeBlock" -> withAttr CodeBlock <$!> elementContent
|
|
||||||
"DefinitionList" -> DefinitionList <$!> elementContent
|
|
||||||
"Div" -> withAttr Div <$!> elementContent
|
|
||||||
"Header" -> (\(lvl, LuaAttr attr, lst) -> Header lvl attr lst)
|
|
||||||
<$!> elementContent
|
|
||||||
"HorizontalRule" -> return HorizontalRule
|
|
||||||
"LineBlock" -> LineBlock <$!> elementContent
|
|
||||||
"OrderedList" -> (\(LuaListAttributes lstAttr, lst) ->
|
|
||||||
OrderedList lstAttr lst)
|
|
||||||
<$!> elementContent
|
|
||||||
"Null" -> return Null
|
|
||||||
"Para" -> Para <$!> elementContent
|
|
||||||
"Plain" -> Plain <$!> elementContent
|
|
||||||
"RawBlock" -> uncurry RawBlock <$!> elementContent
|
|
||||||
"Table" -> (\(attr, capt, colSpecs, thead, tbodies, tfoot) ->
|
|
||||||
Table (fromLuaAttr attr)
|
|
||||||
capt
|
|
||||||
colSpecs
|
|
||||||
thead
|
|
||||||
tbodies
|
|
||||||
tfoot)
|
|
||||||
<$!> elementContent
|
|
||||||
_ -> Lua.throwMessage ("Unknown block type: " <> tag)
|
|
||||||
where
|
|
||||||
-- Get the contents of an AST element.
|
-- Get the contents of an AST element.
|
||||||
elementContent :: Peekable a => Lua a
|
let mkBlock :: (a -> Block) -> Peeker e a -> Peek e Block
|
||||||
elementContent = LuaUtil.rawField idx "c"
|
mkBlock f p = f <$!> peekFieldRaw p "c" idx
|
||||||
|
LuaUtil.getTag idx >>= \case
|
||||||
|
"BlockQuote" -> mkBlock BlockQuote peekBlocks
|
||||||
|
"BulletList" -> mkBlock BulletList (peekList peekBlocks)
|
||||||
|
"CodeBlock" -> mkBlock (uncurry CodeBlock)
|
||||||
|
(peekPair peekAttr peekText)
|
||||||
|
"DefinitionList" -> mkBlock DefinitionList
|
||||||
|
(peekList (peekPair peekInlines (peekList peekBlocks)))
|
||||||
|
"Div" -> mkBlock (uncurry Div) (peekPair peekAttr peekBlocks)
|
||||||
|
"Header" -> mkBlock (\(lvl, attr, lst) -> Header lvl attr lst)
|
||||||
|
(peekTriple peekIntegral peekAttr peekInlines)
|
||||||
|
"HorizontalRule" -> return HorizontalRule
|
||||||
|
"LineBlock" -> mkBlock LineBlock (peekList peekInlines)
|
||||||
|
"OrderedList" -> mkBlock (uncurry OrderedList)
|
||||||
|
(peekPair peekListAttributes (peekList peekBlocks))
|
||||||
|
"Null" -> return Null
|
||||||
|
"Para" -> mkBlock Para peekInlines
|
||||||
|
"Plain" -> mkBlock Plain peekInlines
|
||||||
|
"RawBlock" -> mkBlock (uncurry RawBlock)
|
||||||
|
(peekPair peekFormat peekText)
|
||||||
|
"Table" -> mkBlock id
|
||||||
|
(retrieving "Table" . (liftLua . absindex >=> (\idx' -> cleanup $ do
|
||||||
|
attr <- liftLua (rawgeti idx' 1) *> peekAttr top
|
||||||
|
capt <- liftLua (rawgeti idx' 2) *> peekCaption top
|
||||||
|
cs <- liftLua (rawgeti idx' 3) *> peekList peekColSpec top
|
||||||
|
thead <- liftLua (rawgeti idx' 4) *> peekTableHead top
|
||||||
|
tbods <- liftLua (rawgeti idx' 5) *> peekList peekTableBody top
|
||||||
|
tfoot <- liftLua (rawgeti idx' 6) *> peekTableFoot top
|
||||||
|
return $! Table attr capt cs thead tbods tfoot)))
|
||||||
|
Name tag -> failPeek ("Unknown block type: " <> tag)
|
||||||
|
|
||||||
instance Pushable Caption where
|
peekBlocks :: LuaError e => Peeker e [Block]
|
||||||
push = pushCaption
|
peekBlocks = peekList peekBlock
|
||||||
|
|
||||||
instance Peekable Caption where
|
peekInlines :: LuaError e => Peeker e [Inline]
|
||||||
peek = peekCaption
|
peekInlines = peekList peekInline
|
||||||
|
|
||||||
-- | Push Caption element
|
-- | Push Caption element
|
||||||
pushCaption :: Caption -> Lua ()
|
pushCaption :: LuaError e => Caption -> LuaE e ()
|
||||||
pushCaption (Caption shortCaption longCaption) = do
|
pushCaption (Caption shortCaption longCaption) = do
|
||||||
Lua.newtable
|
Lua.newtable
|
||||||
LuaUtil.addField "short" (Lua.Optional shortCaption)
|
LuaUtil.addField "short" (Lua.Optional shortCaption)
|
||||||
LuaUtil.addField "long" longCaption
|
LuaUtil.addField "long" longCaption
|
||||||
|
|
||||||
-- | Peek Caption element
|
-- | Peek Caption element
|
||||||
peekCaption :: StackIndex -> Lua Caption
|
peekCaption :: LuaError e => Peeker e Caption
|
||||||
peekCaption idx = Caption
|
peekCaption = retrieving "Caption" . \idx -> do
|
||||||
<$!> (Lua.fromOptional <$!> LuaUtil.rawField idx "short")
|
short <- optional $ peekFieldRaw peekInlines "short" idx
|
||||||
<*> LuaUtil.rawField idx "long"
|
long <- peekFieldRaw peekBlocks "long" idx
|
||||||
|
return $! Caption short long
|
||||||
|
|
||||||
instance Peekable ColWidth where
|
peekColWidth :: LuaError e => Peeker e ColWidth
|
||||||
peek idx = do
|
peekColWidth = retrieving "ColWidth" . \idx -> do
|
||||||
width <- Lua.fromOptional <$!> Lua.peek idx
|
maybe ColWidthDefault ColWidth <$!> optional (peekRealFloat idx)
|
||||||
return $! maybe ColWidthDefault ColWidth width
|
|
||||||
|
peekColSpec :: LuaError e => Peeker e ColSpec
|
||||||
|
peekColSpec = peekPair peekRead peekColWidth
|
||||||
|
|
||||||
instance Pushable ColWidth where
|
instance Pushable ColWidth where
|
||||||
push = \case
|
push = \case
|
||||||
|
@ -240,7 +273,12 @@ instance Pushable Row where
|
||||||
push (Row attr cells) = Lua.push (attr, cells)
|
push (Row attr cells) = Lua.push (attr, cells)
|
||||||
|
|
||||||
instance Peekable Row where
|
instance Peekable Row where
|
||||||
peek = fmap (uncurry Row) . Lua.peek
|
peek = forcePeek . peekRow
|
||||||
|
|
||||||
|
peekRow :: LuaError e => Peeker e Row
|
||||||
|
peekRow = ((uncurry Row) <$!>)
|
||||||
|
. retrieving "Row"
|
||||||
|
. peekPair peekAttr (peekList peekCell)
|
||||||
|
|
||||||
instance Pushable TableBody where
|
instance Pushable TableBody where
|
||||||
push (TableBody attr (RowHeadColumns rowHeadColumns) head' body) = do
|
push (TableBody attr (RowHeadColumns rowHeadColumns) head' body) = do
|
||||||
|
@ -250,32 +288,38 @@ instance Pushable TableBody where
|
||||||
LuaUtil.addField "head" head'
|
LuaUtil.addField "head" head'
|
||||||
LuaUtil.addField "body" body
|
LuaUtil.addField "body" body
|
||||||
|
|
||||||
instance Peekable TableBody where
|
peekTableBody :: LuaError e => Peeker e TableBody
|
||||||
peek idx = TableBody
|
peekTableBody = fmap (retrieving "TableBody")
|
||||||
<$!> LuaUtil.rawField idx "attr"
|
. typeChecked "table" Lua.istable
|
||||||
<*> (RowHeadColumns <$!> LuaUtil.rawField idx "row_head_columns")
|
$ \idx -> TableBody
|
||||||
<*> LuaUtil.rawField idx "head"
|
<$!> peekFieldRaw peekAttr "attr" idx
|
||||||
<*> LuaUtil.rawField idx "body"
|
<*> peekFieldRaw ((fmap RowHeadColumns) . peekIntegral) "row_head_columns" idx
|
||||||
|
<*> peekFieldRaw (peekList peekRow) "head" idx
|
||||||
|
<*> peekFieldRaw (peekList peekRow) "body" idx
|
||||||
|
|
||||||
instance Pushable TableHead where
|
instance Pushable TableHead where
|
||||||
push (TableHead attr rows) = Lua.push (attr, rows)
|
push (TableHead attr rows) = Lua.push (attr, rows)
|
||||||
|
|
||||||
instance Peekable TableHead where
|
peekTableHead :: LuaError e => Peeker e TableHead
|
||||||
peek = fmap (uncurry TableHead) . Lua.peek
|
peekTableHead = ((uncurry TableHead) <$!>)
|
||||||
|
. retrieving "TableHead"
|
||||||
|
. peekPair peekAttr (peekList peekRow)
|
||||||
|
|
||||||
instance Pushable TableFoot where
|
instance Pushable TableFoot where
|
||||||
push (TableFoot attr cells) = Lua.push (attr, cells)
|
push (TableFoot attr cells) = Lua.push (attr, cells)
|
||||||
|
|
||||||
instance Peekable TableFoot where
|
peekTableFoot :: LuaError e => Peeker e TableFoot
|
||||||
peek = fmap (uncurry TableFoot) . Lua.peek
|
peekTableFoot = ((uncurry TableFoot) <$!>)
|
||||||
|
. retrieving "TableFoot"
|
||||||
|
. peekPair peekAttr (peekList peekRow)
|
||||||
|
|
||||||
instance Pushable Cell where
|
instance Pushable Cell where
|
||||||
push = pushCell
|
push = pushCell
|
||||||
|
|
||||||
instance Peekable Cell where
|
instance Peekable Cell where
|
||||||
peek = peekCell
|
peek = forcePeek . peekCell
|
||||||
|
|
||||||
pushCell :: Cell -> Lua ()
|
pushCell :: LuaError e => Cell -> LuaE e ()
|
||||||
pushCell (Cell attr align (RowSpan rowSpan) (ColSpan colSpan) contents) = do
|
pushCell (Cell attr align (RowSpan rowSpan) (ColSpan colSpan) contents) = do
|
||||||
Lua.newtable
|
Lua.newtable
|
||||||
LuaUtil.addField "attr" attr
|
LuaUtil.addField "attr" attr
|
||||||
|
@ -284,95 +328,112 @@ pushCell (Cell attr align (RowSpan rowSpan) (ColSpan colSpan) contents) = do
|
||||||
LuaUtil.addField "col_span" colSpan
|
LuaUtil.addField "col_span" colSpan
|
||||||
LuaUtil.addField "contents" contents
|
LuaUtil.addField "contents" contents
|
||||||
|
|
||||||
peekCell :: StackIndex -> Lua Cell
|
peekCell :: LuaError e => Peeker e Cell
|
||||||
peekCell idx = Cell
|
peekCell = fmap (retrieving "Cell")
|
||||||
<$!> (fromLuaAttr <$!> LuaUtil.rawField idx "attr")
|
. typeChecked "table" Lua.istable
|
||||||
<*> LuaUtil.rawField idx "alignment"
|
$ \idx -> do
|
||||||
<*> (RowSpan <$!> LuaUtil.rawField idx "row_span")
|
attr <- peekFieldRaw peekAttr "attr" idx
|
||||||
<*> (ColSpan <$!> LuaUtil.rawField idx "col_span")
|
algn <- peekFieldRaw peekRead "alignment" idx
|
||||||
<*> LuaUtil.rawField idx "contents"
|
rs <- RowSpan <$!> peekFieldRaw peekIntegral "row_span" idx
|
||||||
|
cs <- ColSpan <$!> peekFieldRaw peekIntegral "col_span" idx
|
||||||
|
blks <- peekFieldRaw peekBlocks "contents" idx
|
||||||
|
return $! Cell attr algn rs cs blks
|
||||||
|
|
||||||
-- | Push an inline element to the top of the lua stack.
|
-- | Push an inline element to the top of the lua stack.
|
||||||
pushInline :: Inline -> Lua ()
|
pushInline :: forall e. LuaError e => Inline -> LuaE e ()
|
||||||
pushInline = \case
|
pushInline = \case
|
||||||
Cite citations lst -> pushViaConstructor "Cite" lst citations
|
Cite citations lst -> pushViaConstructor @e "Cite" lst citations
|
||||||
Code attr lst -> pushViaConstructor "Code" lst (LuaAttr attr)
|
Code attr lst -> pushViaConstr' @e "Code"
|
||||||
Emph inlns -> pushViaConstructor "Emph" inlns
|
[push lst, pushAttr attr]
|
||||||
Underline inlns -> pushViaConstructor "Underline" inlns
|
Emph inlns -> pushViaConstructor @e "Emph" inlns
|
||||||
Image attr alt (src,tit) -> pushViaConstructor "Image" alt src tit (LuaAttr attr)
|
Underline inlns -> pushViaConstructor @e "Underline" inlns
|
||||||
LineBreak -> pushViaConstructor "LineBreak"
|
Image attr alt (src,tit) -> pushViaConstr' @e "Image"
|
||||||
Link attr lst (src,tit) -> pushViaConstructor "Link" lst src tit (LuaAttr attr)
|
[push alt, push src, push tit, pushAttr attr]
|
||||||
Note blcks -> pushViaConstructor "Note" blcks
|
LineBreak -> pushViaConstructor @e "LineBreak"
|
||||||
Math mty str -> pushViaConstructor "Math" mty str
|
Link attr lst (src,tit) -> pushViaConstr' @e "Link"
|
||||||
Quoted qt inlns -> pushViaConstructor "Quoted" qt inlns
|
[push lst, push src, push tit, pushAttr attr]
|
||||||
RawInline f cs -> pushViaConstructor "RawInline" f cs
|
Note blcks -> pushViaConstructor @e "Note" blcks
|
||||||
SmallCaps inlns -> pushViaConstructor "SmallCaps" inlns
|
Math mty str -> pushViaConstructor @e "Math" mty str
|
||||||
SoftBreak -> pushViaConstructor "SoftBreak"
|
Quoted qt inlns -> pushViaConstructor @e "Quoted" qt inlns
|
||||||
Space -> pushViaConstructor "Space"
|
RawInline f cs -> pushViaConstructor @e "RawInline" f cs
|
||||||
Span attr inlns -> pushViaConstructor "Span" inlns (LuaAttr attr)
|
SmallCaps inlns -> pushViaConstructor @e "SmallCaps" inlns
|
||||||
Str str -> pushViaConstructor "Str" str
|
SoftBreak -> pushViaConstructor @e "SoftBreak"
|
||||||
Strikeout inlns -> pushViaConstructor "Strikeout" inlns
|
Space -> pushViaConstructor @e "Space"
|
||||||
Strong inlns -> pushViaConstructor "Strong" inlns
|
Span attr inlns -> pushViaConstr' @e "Span"
|
||||||
Subscript inlns -> pushViaConstructor "Subscript" inlns
|
[push inlns, pushAttr attr]
|
||||||
Superscript inlns -> pushViaConstructor "Superscript" inlns
|
Str str -> pushViaConstructor @e "Str" str
|
||||||
|
Strikeout inlns -> pushViaConstructor @e "Strikeout" inlns
|
||||||
|
Strong inlns -> pushViaConstructor @e "Strong" inlns
|
||||||
|
Subscript inlns -> pushViaConstructor @e "Subscript" inlns
|
||||||
|
Superscript inlns -> pushViaConstructor @e "Superscript" inlns
|
||||||
|
|
||||||
-- | Return the value at the given index as inline if possible.
|
-- | Return the value at the given index as inline if possible.
|
||||||
peekInline :: StackIndex -> Lua Inline
|
peekInline :: forall e. LuaError e => Peeker e Inline
|
||||||
peekInline idx = defineHowTo "get Inline value" $ do
|
peekInline = retrieving "Inline" . \idx -> do
|
||||||
tag <- LuaUtil.getTag idx
|
-- Get the contents of an AST element.
|
||||||
case tag of
|
let mkBlock :: (a -> Inline) -> Peeker e a -> Peek e Inline
|
||||||
"Cite" -> uncurry Cite <$!> elementContent
|
mkBlock f p = f <$!> peekFieldRaw p "c" idx
|
||||||
"Code" -> withAttr Code <$!> elementContent
|
LuaUtil.getTag idx >>= \case
|
||||||
"Emph" -> Emph <$!> elementContent
|
"Cite" -> mkBlock (uncurry Cite) $
|
||||||
"Underline" -> Underline <$!> elementContent
|
peekPair (peekList peekCitation) peekInlines
|
||||||
"Image" -> (\(LuaAttr !attr, !lst, !tgt) -> Image attr lst tgt)
|
"Code" -> mkBlock (uncurry Code) (peekPair peekAttr peekText)
|
||||||
<$!> elementContent
|
"Emph" -> mkBlock Emph peekInlines
|
||||||
"Link" -> (\(LuaAttr !attr, !lst, !tgt) -> Link attr lst tgt)
|
"Underline" -> mkBlock Underline peekInlines
|
||||||
<$!> elementContent
|
"Image" -> mkBlock (\(attr, lst, tgt) -> Image attr lst tgt)
|
||||||
|
$ peekTriple peekAttr peekInlines
|
||||||
|
(peekPair peekText peekText)
|
||||||
|
"Link" -> mkBlock (\(attr, lst, tgt) -> Link attr lst tgt) $
|
||||||
|
peekTriple peekAttr peekInlines (peekPair peekText peekText)
|
||||||
"LineBreak" -> return LineBreak
|
"LineBreak" -> return LineBreak
|
||||||
"Note" -> Note <$!> elementContent
|
"Note" -> mkBlock Note peekBlocks
|
||||||
"Math" -> uncurry Math <$!> elementContent
|
"Math" -> mkBlock (uncurry Math) (peekPair peekRead peekText)
|
||||||
"Quoted" -> uncurry Quoted <$!> elementContent
|
"Quoted" -> mkBlock (uncurry Quoted) (peekPair peekRead peekInlines)
|
||||||
"RawInline" -> uncurry RawInline <$!> elementContent
|
"RawInline" -> mkBlock (uncurry RawInline) (peekPair peekFormat peekText)
|
||||||
"SmallCaps" -> SmallCaps <$!> elementContent
|
"SmallCaps" -> mkBlock SmallCaps peekInlines
|
||||||
"SoftBreak" -> return SoftBreak
|
"SoftBreak" -> return SoftBreak
|
||||||
"Space" -> return Space
|
"Space" -> return Space
|
||||||
"Span" -> withAttr Span <$!> elementContent
|
"Span" -> mkBlock (uncurry Span) (peekPair peekAttr peekInlines)
|
||||||
-- strict to Lua string is copied before gc
|
"Str" -> mkBlock Str peekText
|
||||||
"Str" -> Str <$!> elementContent
|
"Strikeout" -> mkBlock Strikeout peekInlines
|
||||||
"Strikeout" -> Strikeout <$!> elementContent
|
"Strong" -> mkBlock Strong peekInlines
|
||||||
"Strong" -> Strong <$!> elementContent
|
"Subscript" -> mkBlock Subscript peekInlines
|
||||||
"Subscript" -> Subscript <$!> elementContent
|
"Superscript"-> mkBlock Superscript peekInlines
|
||||||
"Superscript"-> Superscript <$!> elementContent
|
Name tag -> Lua.failPeek ("Unknown inline type: " <> tag)
|
||||||
_ -> Lua.throwMessage ("Unknown inline type: " <> tag)
|
|
||||||
where
|
|
||||||
-- Get the contents of an AST element.
|
|
||||||
elementContent :: Peekable a => Lua a
|
|
||||||
elementContent = LuaUtil.rawField idx "c"
|
|
||||||
|
|
||||||
try :: Lua a -> Lua (Either PandocError a)
|
pushAttr :: forall e. LuaError e => Attr -> LuaE e ()
|
||||||
try = Catch.try
|
pushAttr (id', classes, kv) = pushViaConstr' @e "Attr"
|
||||||
|
[ pushText id'
|
||||||
|
, pushList pushText classes
|
||||||
|
, pushList (pushPair pushText pushText) kv
|
||||||
|
]
|
||||||
|
|
||||||
withAttr :: (Attr -> a -> b) -> (LuaAttr, a) -> b
|
peekAttr :: LuaError e => Peeker e Attr
|
||||||
withAttr f (attributes, x) = f (fromLuaAttr attributes) x
|
peekAttr = retrieving "Attr" . peekTriple
|
||||||
|
peekText
|
||||||
|
(peekList peekText)
|
||||||
|
(peekList (peekPair peekText peekText))
|
||||||
|
|
||||||
-- | Wrapper for Attr
|
pushListAttributes :: forall e. LuaError e => ListAttributes -> LuaE e ()
|
||||||
newtype LuaAttr = LuaAttr { fromLuaAttr :: Attr }
|
pushListAttributes (start, style, delimiter) =
|
||||||
|
pushViaConstr' "ListAttributes"
|
||||||
|
[ push start, push style, push delimiter ]
|
||||||
|
|
||||||
instance Pushable LuaAttr where
|
peekListAttributes :: LuaError e => Peeker e ListAttributes
|
||||||
push (LuaAttr (id', classes, kv)) =
|
peekListAttributes = retrieving "ListAttributes" . peekTriple
|
||||||
pushViaConstructor "Attr" id' classes kv
|
peekIntegral
|
||||||
|
peekRead
|
||||||
|
peekRead
|
||||||
|
|
||||||
instance Peekable LuaAttr where
|
-- These instances exist only for testing. It's a hack to avoid making
|
||||||
peek idx = defineHowTo "get Attr value" $! (LuaAttr <$!> Lua.peek idx)
|
-- the marshalling modules public.
|
||||||
|
instance Peekable Inline where
|
||||||
|
peek = forcePeek . peekInline
|
||||||
|
|
||||||
-- | Wrapper for ListAttributes
|
instance Peekable Block where
|
||||||
newtype LuaListAttributes = LuaListAttributes ListAttributes
|
peek = forcePeek . peekBlock
|
||||||
|
|
||||||
instance Pushable LuaListAttributes where
|
instance Peekable Meta where
|
||||||
push (LuaListAttributes (start, style, delimiter)) =
|
peek = forcePeek . peekMeta
|
||||||
pushViaConstructor "ListAttributes" start style delimiter
|
|
||||||
|
|
||||||
instance Peekable LuaListAttributes where
|
instance Peekable Pandoc where
|
||||||
peek = defineHowTo "get ListAttributes value" .
|
peek = forcePeek . peekPandoc
|
||||||
fmap LuaListAttributes . Lua.peek
|
|
||||||
|
|
|
@ -1,24 +0,0 @@
|
||||||
{- |
|
|
||||||
Module : Text.Pandoc.Lua.Marshaling.AnyValue
|
|
||||||
Copyright : © 2017-2021 Albert Krewinkel
|
|
||||||
License : GNU GPL, version 2 or above
|
|
||||||
|
|
||||||
Maintainer : Albert Krewinkel <tarleb+pandoc@moltkeplatz.de>
|
|
||||||
Stability : alpha
|
|
||||||
|
|
||||||
Helper type to work with raw Lua stack indices instead of unmarshaled
|
|
||||||
values.
|
|
||||||
|
|
||||||
TODO: Most of this module should be abstracted, factored out, and go
|
|
||||||
into HsLua.
|
|
||||||
-}
|
|
||||||
module Text.Pandoc.Lua.Marshaling.AnyValue (AnyValue (..)) where
|
|
||||||
|
|
||||||
import Foreign.Lua (Peekable (peek), StackIndex)
|
|
||||||
|
|
||||||
-- | Dummy type to allow values of arbitrary Lua type. This just wraps
|
|
||||||
-- stack indices, using it requires extra care.
|
|
||||||
newtype AnyValue = AnyValue StackIndex
|
|
||||||
|
|
||||||
instance Peekable AnyValue where
|
|
||||||
peek = return . AnyValue
|
|
|
@ -1,5 +1,3 @@
|
||||||
{-# OPTIONS_GHC -fno-warn-orphans #-}
|
|
||||||
{-# LANGUAGE LambdaCase #-}
|
|
||||||
{-# LANGUAGE OverloadedStrings #-}
|
{-# LANGUAGE OverloadedStrings #-}
|
||||||
{- |
|
{- |
|
||||||
Module : Text.Pandoc.Lua.Marshaling.CommonState
|
Module : Text.Pandoc.Lua.Marshaling.CommonState
|
||||||
|
@ -11,92 +9,62 @@
|
||||||
|
|
||||||
Instances to marshal (push) and unmarshal (peek) the common state.
|
Instances to marshal (push) and unmarshal (peek) the common state.
|
||||||
-}
|
-}
|
||||||
module Text.Pandoc.Lua.Marshaling.CommonState () where
|
module Text.Pandoc.Lua.Marshaling.CommonState
|
||||||
|
( typeCommonState
|
||||||
|
, peekCommonState
|
||||||
|
, pushCommonState
|
||||||
|
) where
|
||||||
|
|
||||||
import Foreign.Lua (Lua, Peekable, Pushable)
|
import HsLua.Core
|
||||||
import Foreign.Lua.Types.Peekable (reportValueOnFailure)
|
import HsLua.Marshalling
|
||||||
import Foreign.Lua.Userdata (ensureUserdataMetatable, pushAnyWithMetatable,
|
import HsLua.Packaging
|
||||||
toAnyWithName)
|
|
||||||
import Text.Pandoc.Class (CommonState (..))
|
import Text.Pandoc.Class (CommonState (..))
|
||||||
import Text.Pandoc.Logging (LogMessage, showLogMessage)
|
import Text.Pandoc.Logging (LogMessage, showLogMessage)
|
||||||
import Text.Pandoc.Lua.Marshaling.AnyValue (AnyValue (..))
|
import Text.Pandoc.Lua.Marshaling.List (pushPandocList)
|
||||||
|
|
||||||
import qualified Data.Map as Map
|
-- | Lua type used for the @CommonState@ object.
|
||||||
import qualified Data.Text as Text
|
typeCommonState :: LuaError e => DocumentedType e CommonState
|
||||||
import qualified Foreign.Lua as Lua
|
typeCommonState = deftype "pandoc CommonState" []
|
||||||
import qualified Text.Pandoc.Lua.Util as LuaUtil
|
[ readonly "input_files" "input files passed to pandoc"
|
||||||
|
(pushPandocList pushString, stInputFiles)
|
||||||
|
|
||||||
-- | Name used by Lua for the @CommonState@ type.
|
, readonly "output_file" "the file to which pandoc will write"
|
||||||
commonStateTypeName :: String
|
(maybe pushnil pushString, stOutputFile)
|
||||||
commonStateTypeName = "Pandoc CommonState"
|
|
||||||
|
|
||||||
instance Peekable CommonState where
|
, readonly "log" "list of log messages"
|
||||||
peek idx = reportValueOnFailure commonStateTypeName
|
(pushPandocList (pushUD typeLogMessage), stLog)
|
||||||
(`toAnyWithName` commonStateTypeName) idx
|
|
||||||
|
|
||||||
instance Pushable CommonState where
|
, readonly "request_headers" "headers to add for HTTP requests"
|
||||||
push st = pushAnyWithMetatable pushCommonStateMetatable st
|
(pushPandocList (pushPair pushText pushText), stRequestHeaders)
|
||||||
where
|
|
||||||
pushCommonStateMetatable = ensureUserdataMetatable commonStateTypeName $ do
|
|
||||||
LuaUtil.addFunction "__index" indexCommonState
|
|
||||||
LuaUtil.addFunction "__pairs" pairsCommonState
|
|
||||||
|
|
||||||
indexCommonState :: CommonState -> AnyValue -> Lua Lua.NumResults
|
, readonly "resource_path"
|
||||||
indexCommonState st (AnyValue idx) = Lua.ltype idx >>= \case
|
"path to search for resources like included images"
|
||||||
Lua.TypeString -> 1 <$ (Lua.peek idx >>= pushField)
|
(pushPandocList pushString, stResourcePath)
|
||||||
_ -> 1 <$ Lua.pushnil
|
|
||||||
where
|
|
||||||
pushField :: Text.Text -> Lua ()
|
|
||||||
pushField name = case lookup name commonStateFields of
|
|
||||||
Just pushValue -> pushValue st
|
|
||||||
Nothing -> Lua.pushnil
|
|
||||||
|
|
||||||
pairsCommonState :: CommonState -> Lua Lua.NumResults
|
, readonly "source_url" "absolute URL + dir of 1st source file"
|
||||||
pairsCommonState st = do
|
(maybe pushnil pushText, stSourceURL)
|
||||||
Lua.pushHaskellFunction nextFn
|
|
||||||
Lua.pushnil
|
|
||||||
Lua.pushnil
|
|
||||||
return 3
|
|
||||||
where
|
|
||||||
nextFn :: AnyValue -> AnyValue -> Lua Lua.NumResults
|
|
||||||
nextFn _ (AnyValue idx) =
|
|
||||||
Lua.ltype idx >>= \case
|
|
||||||
Lua.TypeNil -> case commonStateFields of
|
|
||||||
[] -> 2 <$ (Lua.pushnil *> Lua.pushnil)
|
|
||||||
(key, pushValue):_ -> 2 <$ (Lua.push key *> pushValue st)
|
|
||||||
Lua.TypeString -> do
|
|
||||||
key <- Lua.peek idx
|
|
||||||
case tail $ dropWhile ((/= key) . fst) commonStateFields of
|
|
||||||
[] -> 2 <$ (Lua.pushnil *> Lua.pushnil)
|
|
||||||
(nextKey, pushValue):_ -> 2 <$ (Lua.push nextKey *> pushValue st)
|
|
||||||
_ -> 2 <$ (Lua.pushnil *> Lua.pushnil)
|
|
||||||
|
|
||||||
commonStateFields :: [(Text.Text, CommonState -> Lua ())]
|
, readonly "user_data_dir" "directory to search for data files"
|
||||||
commonStateFields =
|
(maybe pushnil pushString, stUserDataDir)
|
||||||
[ ("input_files", Lua.push . stInputFiles)
|
|
||||||
, ("output_file", Lua.push . Lua.Optional . stOutputFile)
|
, readonly "trace" "controls whether tracing messages are issued"
|
||||||
, ("log", Lua.push . stLog)
|
(pushBool, stTrace)
|
||||||
, ("request_headers", Lua.push . Map.fromList . stRequestHeaders)
|
|
||||||
, ("resource_path", Lua.push . stResourcePath)
|
, readonly "verbosity" "verbosity level"
|
||||||
, ("source_url", Lua.push . Lua.Optional . stSourceURL)
|
(pushString . show, stVerbosity)
|
||||||
, ("user_data_dir", Lua.push . Lua.Optional . stUserDataDir)
|
|
||||||
, ("trace", Lua.push . stTrace)
|
|
||||||
, ("verbosity", Lua.push . show . stVerbosity)
|
|
||||||
]
|
]
|
||||||
|
|
||||||
-- | Name used by Lua for the @CommonState@ type.
|
peekCommonState :: LuaError e => Peeker e CommonState
|
||||||
logMessageTypeName :: String
|
peekCommonState = peekUD typeCommonState
|
||||||
logMessageTypeName = "Pandoc LogMessage"
|
|
||||||
|
|
||||||
instance Peekable LogMessage where
|
pushCommonState :: LuaError e => Pusher e CommonState
|
||||||
peek idx = reportValueOnFailure logMessageTypeName
|
pushCommonState = pushUD typeCommonState
|
||||||
(`toAnyWithName` logMessageTypeName) idx
|
|
||||||
|
|
||||||
instance Pushable LogMessage where
|
typeLogMessage :: LuaError e => DocumentedType e LogMessage
|
||||||
push msg = pushAnyWithMetatable pushLogMessageMetatable msg
|
typeLogMessage = deftype "pandoc LogMessage"
|
||||||
where
|
[ operation Index $ defun "__tostring"
|
||||||
pushLogMessageMetatable = ensureUserdataMetatable logMessageTypeName $
|
### liftPure showLogMessage
|
||||||
LuaUtil.addFunction "__tostring" tostringLogMessage
|
<#> udparam typeLogMessage "msg" "object"
|
||||||
|
=#> functionResult pushText "string" "stringified log message"
|
||||||
tostringLogMessage :: LogMessage -> Lua Text.Text
|
]
|
||||||
tostringLogMessage = return . showLogMessage
|
mempty -- no members
|
||||||
|
|
|
@ -12,8 +12,8 @@ Marshaling instance for doctemplates Context and its components.
|
||||||
-}
|
-}
|
||||||
module Text.Pandoc.Lua.Marshaling.Context () where
|
module Text.Pandoc.Lua.Marshaling.Context () where
|
||||||
|
|
||||||
import qualified Foreign.Lua as Lua
|
import qualified HsLua as Lua
|
||||||
import Foreign.Lua (Pushable)
|
import HsLua (Pushable)
|
||||||
import Text.DocTemplates (Context(..), Val(..), TemplateTarget)
|
import Text.DocTemplates (Context(..), Val(..), TemplateTarget)
|
||||||
import Text.DocLayout (render)
|
import Text.DocLayout (render)
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
{-# LANGUAGE DeriveDataTypeable #-}
|
{-# LANGUAGE DeriveDataTypeable #-}
|
||||||
{-# LANGUAGE FlexibleInstances #-}
|
{-# LANGUAGE FlexibleInstances #-}
|
||||||
{-# LANGUAGE MultiParamTypeClasses #-}
|
{-# LANGUAGE MultiParamTypeClasses #-}
|
||||||
|
{-# LANGUAGE OverloadedStrings #-}
|
||||||
{-# LANGUAGE UndecidableInstances #-}
|
{-# LANGUAGE UndecidableInstances #-}
|
||||||
{- |
|
{- |
|
||||||
Module : Text.Pandoc.Lua.Marshaling.List
|
Module : Text.Pandoc.Lua.Marshaling.List
|
||||||
|
@ -14,27 +15,30 @@ Marshaling/unmarshaling instances for @pandoc.List@s.
|
||||||
-}
|
-}
|
||||||
module Text.Pandoc.Lua.Marshaling.List
|
module Text.Pandoc.Lua.Marshaling.List
|
||||||
( List (..)
|
( List (..)
|
||||||
|
, peekList'
|
||||||
|
, pushPandocList
|
||||||
) where
|
) where
|
||||||
|
|
||||||
|
import Control.Monad ((<$!>))
|
||||||
import Data.Data (Data)
|
import Data.Data (Data)
|
||||||
import Foreign.Lua (Peekable, Pushable)
|
import HsLua (LuaError, Peeker, Pusher, Pushable (push), peekList, pushList)
|
||||||
import Text.Pandoc.Walk (Walkable (..))
|
import Text.Pandoc.Walk (Walkable (..))
|
||||||
import Text.Pandoc.Lua.Util (defineHowTo, pushViaConstructor)
|
import Text.Pandoc.Lua.Util (pushViaConstr')
|
||||||
|
|
||||||
import qualified Foreign.Lua as Lua
|
|
||||||
|
|
||||||
-- | List wrapper which is marshalled as @pandoc.List@.
|
-- | List wrapper which is marshalled as @pandoc.List@.
|
||||||
newtype List a = List { fromList :: [a] }
|
newtype List a = List { fromList :: [a] }
|
||||||
deriving (Data, Eq, Show)
|
deriving (Data, Eq, Show)
|
||||||
|
|
||||||
instance Pushable a => Pushable (List a) where
|
instance Pushable a => Pushable (List a) where
|
||||||
push (List xs) =
|
push (List xs) = pushPandocList push xs
|
||||||
pushViaConstructor "List" xs
|
|
||||||
|
|
||||||
instance Peekable a => Peekable (List a) where
|
-- | Pushes a list as a numerical Lua table, setting a metatable that offers a
|
||||||
peek idx = defineHowTo "get List" $ do
|
-- number of convenience functions.
|
||||||
xs <- Lua.peek idx
|
pushPandocList :: LuaError e => Pusher e a -> Pusher e [a]
|
||||||
return $ List xs
|
pushPandocList pushItem xs = pushViaConstr' "List" [pushList pushItem xs]
|
||||||
|
|
||||||
|
peekList' :: LuaError e => Peeker e a -> Peeker e (List a)
|
||||||
|
peekList' p = (List <$!>) . peekList p
|
||||||
|
|
||||||
-- List is just a wrapper, so we can reuse the walk instance for
|
-- List is just a wrapper, so we can reuse the walk instance for
|
||||||
-- unwrapped Hasekll lists.
|
-- unwrapped Hasekll lists.
|
||||||
|
|
|
@ -1,73 +0,0 @@
|
||||||
{- |
|
|
||||||
Module : Text.Pandoc.Lua.Marshaling.MediaBag
|
|
||||||
Copyright : © 2012-2021 John MacFarlane
|
|
||||||
© 2017-2021 Albert Krewinkel
|
|
||||||
License : GNU GPL, version 2 or above
|
|
||||||
Maintainer : Albert Krewinkel <tarleb+pandoc@moltkeplatz.de>
|
|
||||||
Stability : alpha
|
|
||||||
|
|
||||||
Instances to marshal (push) and unmarshal (peek) media data.
|
|
||||||
-}
|
|
||||||
module Text.Pandoc.Lua.Marshaling.MediaBag (pushIterator) where
|
|
||||||
|
|
||||||
import Foreign.Ptr (Ptr)
|
|
||||||
import Foreign.StablePtr (StablePtr, deRefStablePtr, newStablePtr)
|
|
||||||
import Foreign.Lua (Lua, NumResults, Peekable, Pushable, StackIndex)
|
|
||||||
import Foreign.Lua.Types.Peekable (reportValueOnFailure)
|
|
||||||
import Foreign.Lua.Userdata (ensureUserdataMetatable, pushAnyWithMetatable,
|
|
||||||
toAnyWithName)
|
|
||||||
import Text.Pandoc.MediaBag (MediaBag, mediaItems)
|
|
||||||
import Text.Pandoc.MIME (MimeType)
|
|
||||||
import Text.Pandoc.Lua.Marshaling.AnyValue (AnyValue (..))
|
|
||||||
|
|
||||||
import qualified Data.ByteString.Lazy as BL
|
|
||||||
import qualified Foreign.Lua as Lua
|
|
||||||
import qualified Foreign.Storable as Storable
|
|
||||||
|
|
||||||
-- | A list of 'MediaBag' items.
|
|
||||||
newtype MediaItems = MediaItems [(String, MimeType, BL.ByteString)]
|
|
||||||
|
|
||||||
instance Pushable MediaItems where
|
|
||||||
push = pushMediaItems
|
|
||||||
|
|
||||||
instance Peekable MediaItems where
|
|
||||||
peek = peekMediaItems
|
|
||||||
|
|
||||||
-- | Push an iterator triple to be used with Lua's @for@ loop construct.
|
|
||||||
-- Each iterator invocation returns a triple containing the item's
|
|
||||||
-- filename, MIME type, and content.
|
|
||||||
pushIterator :: MediaBag -> Lua NumResults
|
|
||||||
pushIterator mb = do
|
|
||||||
Lua.pushHaskellFunction nextItem
|
|
||||||
Lua.push (MediaItems $ mediaItems mb)
|
|
||||||
Lua.pushnil
|
|
||||||
return 3
|
|
||||||
|
|
||||||
-- | Lua type name for @'MediaItems'@.
|
|
||||||
mediaItemsTypeName :: String
|
|
||||||
mediaItemsTypeName = "pandoc MediaItems"
|
|
||||||
|
|
||||||
-- | Push a @MediaItems@ element to the stack.
|
|
||||||
pushMediaItems :: MediaItems -> Lua ()
|
|
||||||
pushMediaItems xs = pushAnyWithMetatable pushMT xs
|
|
||||||
where
|
|
||||||
pushMT = ensureUserdataMetatable mediaItemsTypeName (return ())
|
|
||||||
|
|
||||||
-- | Retrieve a @MediaItems@ element from the stack.
|
|
||||||
peekMediaItems :: StackIndex -> Lua MediaItems
|
|
||||||
peekMediaItems = reportValueOnFailure mediaItemsTypeName
|
|
||||||
(`toAnyWithName` mediaItemsTypeName)
|
|
||||||
|
|
||||||
-- | Retrieve a list of items from an iterator state, return the first
|
|
||||||
-- item (if present), and advance the state.
|
|
||||||
nextItem :: Ptr (StablePtr MediaItems) -> AnyValue -> Lua NumResults
|
|
||||||
nextItem ptr _ = do
|
|
||||||
(MediaItems items) <- Lua.liftIO $ deRefStablePtr =<< Storable.peek ptr
|
|
||||||
case items of
|
|
||||||
[] -> 2 <$ (Lua.pushnil *> Lua.pushnil)
|
|
||||||
(key, mt, content):xs -> do
|
|
||||||
Lua.liftIO $ Storable.poke ptr =<< newStablePtr (MediaItems xs)
|
|
||||||
Lua.push key
|
|
||||||
Lua.push mt
|
|
||||||
Lua.push content
|
|
||||||
return 3
|
|
|
@ -1,7 +1,7 @@
|
||||||
{-# OPTIONS_GHC -fno-warn-orphans #-}
|
|
||||||
{-# LANGUAGE LambdaCase #-}
|
{-# LANGUAGE LambdaCase #-}
|
||||||
{-# LANGUAGE OverloadedStrings #-}
|
{-# LANGUAGE OverloadedStrings #-}
|
||||||
{-# LANGUAGE ScopedTypeVariables #-}
|
{-# LANGUAGE ScopedTypeVariables #-}
|
||||||
|
{-# LANGUAGE TypeApplications #-}
|
||||||
{- |
|
{- |
|
||||||
Module : Text.Pandoc.Lua.Marshaling.PandocError
|
Module : Text.Pandoc.Lua.Marshaling.PandocError
|
||||||
Copyright : © 2020-2021 Albert Krewinkel
|
Copyright : © 2020-2021 Albert Krewinkel
|
||||||
|
@ -15,51 +15,37 @@ Marshaling of @'PandocError'@ values.
|
||||||
module Text.Pandoc.Lua.Marshaling.PandocError
|
module Text.Pandoc.Lua.Marshaling.PandocError
|
||||||
( peekPandocError
|
( peekPandocError
|
||||||
, pushPandocError
|
, pushPandocError
|
||||||
|
, typePandocError
|
||||||
)
|
)
|
||||||
where
|
where
|
||||||
|
|
||||||
import Foreign.Lua (Lua, Peekable, Pushable, StackIndex)
|
import HsLua.Core (LuaError)
|
||||||
|
import HsLua.Marshalling (Peeker, Pusher, pushString, liftLua)
|
||||||
|
import HsLua.Packaging
|
||||||
import Text.Pandoc.Error (PandocError (PandocLuaError))
|
import Text.Pandoc.Error (PandocError (PandocLuaError))
|
||||||
|
|
||||||
import qualified Foreign.Lua as Lua
|
import qualified HsLua as Lua
|
||||||
import qualified Foreign.Lua.Userdata as Lua
|
|
||||||
import qualified Text.Pandoc.Lua.Util as LuaUtil
|
|
||||||
import qualified Text.Pandoc.UTF8 as UTF8
|
import qualified Text.Pandoc.UTF8 as UTF8
|
||||||
|
|
||||||
-- | Userdata name used by Lua for the @PandocError@ type.
|
-- | Lua userdata type definition for PandocError.
|
||||||
pandocErrorName :: String
|
typePandocError :: LuaError e => DocumentedType e PandocError
|
||||||
pandocErrorName = "pandoc error"
|
typePandocError = deftype "PandocError"
|
||||||
|
[ operation Tostring $ defun "__tostring"
|
||||||
|
### liftPure (show @PandocError)
|
||||||
|
<#> udparam typePandocError "obj" "PandocError object"
|
||||||
|
=#> functionResult pushString "string" "string representation of error."
|
||||||
|
]
|
||||||
|
mempty -- no members
|
||||||
|
|
||||||
-- | Peek a @'PandocError'@ element to the Lua stack.
|
-- | Peek a @'PandocError'@ element to the Lua stack.
|
||||||
pushPandocError :: PandocError -> Lua ()
|
pushPandocError :: LuaError e => Pusher e PandocError
|
||||||
pushPandocError = Lua.pushAnyWithMetatable pushPandocErrorMT
|
pushPandocError = pushUD typePandocError
|
||||||
where
|
|
||||||
pushPandocErrorMT = Lua.ensureUserdataMetatable pandocErrorName $
|
|
||||||
LuaUtil.addFunction "__tostring" __tostring
|
|
||||||
|
|
||||||
-- | Retrieve a @'PandocError'@ from the Lua stack.
|
-- | Retrieve a @'PandocError'@ from the Lua stack.
|
||||||
peekPandocError :: StackIndex -> Lua PandocError
|
peekPandocError :: LuaError e => Peeker e PandocError
|
||||||
peekPandocError idx = Lua.ltype idx >>= \case
|
peekPandocError idx = Lua.retrieving "PandocError" $
|
||||||
Lua.TypeUserdata -> do
|
liftLua (Lua.ltype idx) >>= \case
|
||||||
errMb <- Lua.toAnyWithName idx pandocErrorName
|
Lua.TypeUserdata -> peekUD typePandocError idx
|
||||||
return $ case errMb of
|
|
||||||
Just err -> err
|
|
||||||
Nothing -> PandocLuaError "could not retrieve original error"
|
|
||||||
_ -> do
|
_ -> do
|
||||||
Lua.pushvalue idx
|
msg <- liftLua $ Lua.state >>= \l -> Lua.liftIO (Lua.popErrorMessage l)
|
||||||
msg <- Lua.state >>= \l -> Lua.liftIO (Lua.errorMessage l)
|
|
||||||
return $ PandocLuaError (UTF8.toText msg)
|
return $ PandocLuaError (UTF8.toText msg)
|
||||||
|
|
||||||
-- | Convert to string.
|
|
||||||
__tostring :: PandocError -> Lua String
|
|
||||||
__tostring = return . show
|
|
||||||
|
|
||||||
--
|
|
||||||
-- Instances
|
|
||||||
--
|
|
||||||
|
|
||||||
instance Pushable PandocError where
|
|
||||||
push = pushPandocError
|
|
||||||
|
|
||||||
instance Peekable PandocError where
|
|
||||||
peek = peekPandocError
|
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
{-# LANGUAGE LambdaCase #-}
|
|
||||||
{-# LANGUAGE OverloadedStrings #-}
|
{-# LANGUAGE OverloadedStrings #-}
|
||||||
{-# LANGUAGE ScopedTypeVariables #-}
|
{-# LANGUAGE ScopedTypeVariables #-}
|
||||||
{-# OPTIONS_GHC -fno-warn-orphans #-}
|
{-# OPTIONS_GHC -fno-warn-orphans #-}
|
||||||
|
@ -13,67 +12,60 @@
|
||||||
|
|
||||||
Marshaling instance for ReaderOptions and its components.
|
Marshaling instance for ReaderOptions and its components.
|
||||||
-}
|
-}
|
||||||
module Text.Pandoc.Lua.Marshaling.ReaderOptions () where
|
module Text.Pandoc.Lua.Marshaling.ReaderOptions
|
||||||
|
( peekReaderOptions
|
||||||
|
, pushReaderOptions
|
||||||
|
) where
|
||||||
|
|
||||||
import Data.Data (showConstr, toConstr)
|
import HsLua as Lua
|
||||||
import Foreign.Lua (Lua, Pushable)
|
import Text.Pandoc.Lua.Marshaling.List (pushPandocList)
|
||||||
import Text.Pandoc.Extensions (Extensions)
|
import Text.Pandoc.Options (ReaderOptions (..))
|
||||||
import Text.Pandoc.Lua.Marshaling.AnyValue (AnyValue (..))
|
|
||||||
import Text.Pandoc.Lua.Marshaling.CommonState ()
|
|
||||||
import Text.Pandoc.Options (ReaderOptions (..), TrackChanges)
|
|
||||||
|
|
||||||
import qualified Data.Set as Set
|
|
||||||
import qualified Data.Text as Text
|
|
||||||
import qualified Foreign.Lua as Lua
|
|
||||||
import qualified Text.Pandoc.Lua.Util as LuaUtil
|
|
||||||
|
|
||||||
--
|
--
|
||||||
-- Reader Options
|
-- Reader Options
|
||||||
--
|
--
|
||||||
instance Pushable Extensions where
|
|
||||||
push exts = Lua.push (show exts)
|
|
||||||
|
|
||||||
instance Pushable TrackChanges where
|
peekReaderOptions :: LuaError e => Peeker e ReaderOptions
|
||||||
push = Lua.push . showConstr . toConstr
|
peekReaderOptions = peekUD typeReaderOptions
|
||||||
|
|
||||||
instance Pushable ReaderOptions where
|
pushReaderOptions :: LuaError e => Pusher e ReaderOptions
|
||||||
push ro = do
|
pushReaderOptions = pushUD typeReaderOptions
|
||||||
let ReaderOptions
|
|
||||||
(extensions :: Extensions)
|
|
||||||
(standalone :: Bool)
|
|
||||||
(columns :: Int)
|
|
||||||
(tabStop :: Int)
|
|
||||||
(indentedCodeClasses :: [Text.Text])
|
|
||||||
(abbreviations :: Set.Set Text.Text)
|
|
||||||
(defaultImageExtension :: Text.Text)
|
|
||||||
(trackChanges :: TrackChanges)
|
|
||||||
(stripComments :: Bool)
|
|
||||||
= ro
|
|
||||||
Lua.newtable
|
|
||||||
LuaUtil.addField "extensions" extensions
|
|
||||||
LuaUtil.addField "standalone" standalone
|
|
||||||
LuaUtil.addField "columns" columns
|
|
||||||
LuaUtil.addField "tab_stop" tabStop
|
|
||||||
LuaUtil.addField "indented_code_classes" indentedCodeClasses
|
|
||||||
LuaUtil.addField "abbreviations" abbreviations
|
|
||||||
LuaUtil.addField "default_image_extension" defaultImageExtension
|
|
||||||
LuaUtil.addField "track_changes" trackChanges
|
|
||||||
LuaUtil.addField "strip_comments" stripComments
|
|
||||||
|
|
||||||
-- add metatable
|
typeReaderOptions :: LuaError e => DocumentedType e ReaderOptions
|
||||||
let indexReaderOptions :: AnyValue -> AnyValue -> Lua Lua.NumResults
|
typeReaderOptions = deftype "pandoc ReaderOptions"
|
||||||
indexReaderOptions _tbl (AnyValue key) = do
|
[ operation Tostring luaShow
|
||||||
Lua.ltype key >>= \case
|
]
|
||||||
Lua.TypeString -> Lua.peek key >>= \case
|
[ readonly "extensions" ""
|
||||||
("defaultImageExtension" :: Text.Text)
|
( pushString . show
|
||||||
-> Lua.push defaultImageExtension
|
, readerExtensions)
|
||||||
"indentedCodeClasses" -> Lua.push indentedCodeClasses
|
, readonly "standalone" ""
|
||||||
"stripComments" -> Lua.push stripComments
|
( pushBool
|
||||||
"tabStop" -> Lua.push tabStop
|
, readerStandalone)
|
||||||
"trackChanges" -> Lua.push trackChanges
|
, readonly "columns" ""
|
||||||
_ -> Lua.pushnil
|
( pushIntegral
|
||||||
_ -> Lua.pushnil
|
, readerColumns)
|
||||||
return 1
|
, readonly "tab_stop" ""
|
||||||
Lua.newtable
|
( pushIntegral
|
||||||
LuaUtil.addFunction "__index" indexReaderOptions
|
, readerTabStop)
|
||||||
Lua.setmetatable (Lua.nthFromTop 2)
|
, readonly "indented_code_classes" ""
|
||||||
|
( pushPandocList pushText
|
||||||
|
, readerIndentedCodeClasses)
|
||||||
|
, readonly "abbreviations" ""
|
||||||
|
( pushSet pushText
|
||||||
|
, readerAbbreviations)
|
||||||
|
, readonly "track_changes" ""
|
||||||
|
( pushString . show
|
||||||
|
, readerTrackChanges)
|
||||||
|
, readonly "strip_comments" ""
|
||||||
|
( pushBool
|
||||||
|
, readerStripComments)
|
||||||
|
, readonly "default_image_extension" ""
|
||||||
|
( pushText
|
||||||
|
, readerDefaultImageExtension)
|
||||||
|
]
|
||||||
|
|
||||||
|
luaShow :: LuaError e => DocumentedFunction e
|
||||||
|
luaShow = defun "__tostring"
|
||||||
|
### liftPure show
|
||||||
|
<#> udparam typeReaderOptions "state" "object to print in native format"
|
||||||
|
=#> functionResult pushString "string" "Haskell representation"
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
{-# LANGUAGE OverloadedStrings #-}
|
||||||
|
{-# LANGUAGE ScopedTypeVariables #-}
|
||||||
|
{-# LANGUAGE TypeApplications #-}
|
||||||
{- |
|
{- |
|
||||||
Module : Text.Pandoc.Lua.Marshaling.SimpleTable
|
Module : Text.Pandoc.Lua.Marshaling.SimpleTable
|
||||||
Copyright : © 2020-2021 Albert Krewinkel
|
Copyright : © 2020-2021 Albert Krewinkel
|
||||||
|
@ -16,12 +19,11 @@ module Text.Pandoc.Lua.Marshaling.SimpleTable
|
||||||
)
|
)
|
||||||
where
|
where
|
||||||
|
|
||||||
import Foreign.Lua (Lua, Peekable, Pushable, StackIndex)
|
import Control.Monad ((<$!>))
|
||||||
|
import HsLua as Lua
|
||||||
import Text.Pandoc.Definition
|
import Text.Pandoc.Definition
|
||||||
import Text.Pandoc.Lua.Util (defineHowTo, pushViaConstructor, rawField)
|
import Text.Pandoc.Lua.Util (pushViaConstructor)
|
||||||
import Text.Pandoc.Lua.Marshaling.AST ()
|
import Text.Pandoc.Lua.Marshaling.AST
|
||||||
|
|
||||||
import qualified Foreign.Lua as Lua
|
|
||||||
|
|
||||||
-- | A simple (legacy-style) table.
|
-- | A simple (legacy-style) table.
|
||||||
data SimpleTable = SimpleTable
|
data SimpleTable = SimpleTable
|
||||||
|
@ -32,16 +34,10 @@ data SimpleTable = SimpleTable
|
||||||
, simpleTableBody :: [[[Block]]]
|
, simpleTableBody :: [[[Block]]]
|
||||||
}
|
}
|
||||||
|
|
||||||
instance Pushable SimpleTable where
|
|
||||||
push = pushSimpleTable
|
|
||||||
|
|
||||||
instance Peekable SimpleTable where
|
|
||||||
peek = peekSimpleTable
|
|
||||||
|
|
||||||
-- | Push a simple table to the stack by calling the
|
-- | Push a simple table to the stack by calling the
|
||||||
-- @pandoc.SimpleTable@ constructor.
|
-- @pandoc.SimpleTable@ constructor.
|
||||||
pushSimpleTable :: SimpleTable -> Lua ()
|
pushSimpleTable :: forall e. LuaError e => SimpleTable -> LuaE e ()
|
||||||
pushSimpleTable tbl = pushViaConstructor "SimpleTable"
|
pushSimpleTable tbl = pushViaConstructor @e "SimpleTable"
|
||||||
(simpleTableCaption tbl)
|
(simpleTableCaption tbl)
|
||||||
(simpleTableAlignments tbl)
|
(simpleTableAlignments tbl)
|
||||||
(simpleTableColumnWidths tbl)
|
(simpleTableColumnWidths tbl)
|
||||||
|
@ -49,11 +45,10 @@ pushSimpleTable tbl = pushViaConstructor "SimpleTable"
|
||||||
(simpleTableBody tbl)
|
(simpleTableBody tbl)
|
||||||
|
|
||||||
-- | Retrieve a simple table from the stack.
|
-- | Retrieve a simple table from the stack.
|
||||||
peekSimpleTable :: StackIndex -> Lua SimpleTable
|
peekSimpleTable :: forall e. LuaError e => Peeker e SimpleTable
|
||||||
peekSimpleTable idx = defineHowTo "get SimpleTable" $
|
peekSimpleTable idx = retrieving "SimpleTable" $ SimpleTable
|
||||||
SimpleTable
|
<$!> peekFieldRaw peekInlines "caption" idx
|
||||||
<$> rawField idx "caption"
|
<*> peekFieldRaw (peekList peekRead) "aligns" idx
|
||||||
<*> rawField idx "aligns"
|
<*> peekFieldRaw (peekList peekRealFloat) "widths" idx
|
||||||
<*> rawField idx "widths"
|
<*> peekFieldRaw (peekList peekBlocks) "headers" idx
|
||||||
<*> rawField idx "headers"
|
<*> peekFieldRaw (peekList (peekList peekBlocks)) "rows" idx
|
||||||
<*> rawField idx "rows"
|
|
||||||
|
|
|
@ -16,133 +16,92 @@ default comparison operators (like @>@ and @<=@).
|
||||||
module Text.Pandoc.Lua.Marshaling.Version
|
module Text.Pandoc.Lua.Marshaling.Version
|
||||||
( peekVersion
|
( peekVersion
|
||||||
, pushVersion
|
, pushVersion
|
||||||
|
, peekVersionFuzzy
|
||||||
)
|
)
|
||||||
where
|
where
|
||||||
|
|
||||||
import Data.Text (Text)
|
|
||||||
import Data.Maybe (fromMaybe)
|
import Data.Maybe (fromMaybe)
|
||||||
import Data.Version (Version (..), makeVersion, parseVersion, showVersion)
|
import Data.Version (Version (..), makeVersion, parseVersion, showVersion)
|
||||||
import Foreign.Lua (Lua, Optional (..), NumResults,
|
import HsLua as Lua
|
||||||
Peekable, Pushable, StackIndex)
|
import Safe (lastMay)
|
||||||
import Foreign.Lua.Types.Peekable (reportValueOnFailure)
|
|
||||||
import Foreign.Lua.Userdata (ensureUserdataMetatable, pushAnyWithMetatable,
|
|
||||||
toAnyWithName)
|
|
||||||
import Safe (atMay, lastMay)
|
|
||||||
import Text.Pandoc.Lua.Marshaling.AnyValue (AnyValue (..))
|
|
||||||
import Text.ParserCombinators.ReadP (readP_to_S)
|
import Text.ParserCombinators.ReadP (readP_to_S)
|
||||||
|
import qualified Text.Pandoc.UTF8 as UTF8
|
||||||
|
|
||||||
import qualified Foreign.Lua as Lua
|
instance Peekable Version where
|
||||||
import qualified Text.Pandoc.Lua.Util as LuaUtil
|
peek = forcePeek . peekVersionFuzzy
|
||||||
|
|
||||||
-- | Push a @'Version'@ element to the Lua stack.
|
|
||||||
pushVersion :: Version -> Lua ()
|
|
||||||
pushVersion version = pushAnyWithMetatable pushVersionMT version
|
|
||||||
where
|
|
||||||
pushVersionMT = ensureUserdataMetatable versionTypeName $ do
|
|
||||||
LuaUtil.addFunction "__eq" __eq
|
|
||||||
LuaUtil.addFunction "__le" __le
|
|
||||||
LuaUtil.addFunction "__lt" __lt
|
|
||||||
LuaUtil.addFunction "__len" __len
|
|
||||||
LuaUtil.addFunction "__index" __index
|
|
||||||
LuaUtil.addFunction "__pairs" __pairs
|
|
||||||
LuaUtil.addFunction "__tostring" __tostring
|
|
||||||
|
|
||||||
instance Pushable Version where
|
instance Pushable Version where
|
||||||
push = pushVersion
|
push = pushVersion
|
||||||
|
|
||||||
peekVersion :: StackIndex -> Lua Version
|
-- | Push a @'Version'@ element to the Lua stack.
|
||||||
peekVersion idx = Lua.ltype idx >>= \case
|
pushVersion :: LuaError e => Pusher e Version
|
||||||
|
pushVersion = pushUD typeVersion
|
||||||
|
|
||||||
|
peekVersionFuzzy :: LuaError e => Peeker e Version
|
||||||
|
peekVersionFuzzy idx = retrieving "Version" $ liftLua (Lua.ltype idx) >>= \case
|
||||||
|
Lua.TypeUserdata -> peekVersion idx
|
||||||
Lua.TypeString -> do
|
Lua.TypeString -> do
|
||||||
versionStr <- Lua.peek idx
|
versionStr <- peekString idx
|
||||||
let parses = readP_to_S parseVersion versionStr
|
let parses = readP_to_S parseVersion versionStr
|
||||||
case lastMay parses of
|
case lastMay parses of
|
||||||
Just (v, "") -> return v
|
Just (v, "") -> return v
|
||||||
_ -> Lua.throwMessage $ "could not parse as Version: " ++ versionStr
|
_ -> Lua.failPeek $
|
||||||
|
UTF8.fromString $ "could not parse as Version: " ++ versionStr
|
||||||
|
|
||||||
Lua.TypeUserdata ->
|
|
||||||
reportValueOnFailure versionTypeName
|
|
||||||
(`toAnyWithName` versionTypeName)
|
|
||||||
idx
|
|
||||||
Lua.TypeNumber -> do
|
Lua.TypeNumber -> do
|
||||||
n <- Lua.peek idx
|
(makeVersion . (:[])) <$> peekIntegral idx
|
||||||
return (makeVersion [n])
|
|
||||||
|
|
||||||
Lua.TypeTable ->
|
Lua.TypeTable ->
|
||||||
makeVersion <$> Lua.peek idx
|
makeVersion <$> peekList peekIntegral idx
|
||||||
|
|
||||||
_ ->
|
_ ->
|
||||||
Lua.throwMessage "could not peek Version"
|
Lua.failPeek "could not peek Version"
|
||||||
|
|
||||||
instance Peekable Version where
|
peekVersion :: LuaError e => Peeker e Version
|
||||||
peek = peekVersion
|
peekVersion = peekUD typeVersion
|
||||||
|
|
||||||
-- | Name used by Lua for the @CommonState@ type.
|
typeVersion :: LuaError e => DocumentedType e Version
|
||||||
versionTypeName :: String
|
typeVersion = deftype "Version"
|
||||||
versionTypeName = "HsLua Version"
|
[ operation Eq $ defun "__eq"
|
||||||
|
### liftPure2 (==)
|
||||||
__eq :: Version -> Version -> Lua Bool
|
<#> parameter peekVersionFuzzy "Version" "v1" ""
|
||||||
__eq v1 v2 = return (v1 == v2)
|
<#> parameter peekVersionFuzzy "Version" "v2" ""
|
||||||
|
=#> functionResult pushBool "boolean" "true iff v1 == v2"
|
||||||
__le :: Version -> Version -> Lua Bool
|
, operation Lt $ defun "__lt"
|
||||||
__le v1 v2 = return (v1 <= v2)
|
### liftPure2 (<)
|
||||||
|
<#> parameter peekVersionFuzzy "Version" "v1" ""
|
||||||
__lt :: Version -> Version -> Lua Bool
|
<#> parameter peekVersionFuzzy "Version" "v2" ""
|
||||||
__lt v1 v2 = return (v1 < v2)
|
=#> functionResult pushBool "boolean" "true iff v1 < v2"
|
||||||
|
, operation Le $ defun "__le"
|
||||||
-- | Get number of version components.
|
### liftPure2 (<=)
|
||||||
__len :: Version -> Lua Int
|
<#> parameter peekVersionFuzzy "Version" "v1" ""
|
||||||
__len = return . length . versionBranch
|
<#> parameter peekVersionFuzzy "Version" "v2" ""
|
||||||
|
=#> functionResult pushBool "boolean" "true iff v1 <= v2"
|
||||||
-- | Access fields.
|
, operation Len $ defun "__len"
|
||||||
__index :: Version -> AnyValue -> Lua NumResults
|
### liftPure (length . versionBranch)
|
||||||
__index v (AnyValue k) = do
|
<#> parameter peekVersionFuzzy "Version" "v1" ""
|
||||||
ty <- Lua.ltype k
|
=#> functionResult pushIntegral "integer" "number of version components"
|
||||||
case ty of
|
, operation Tostring $ defun "__tostring"
|
||||||
Lua.TypeNumber -> do
|
### liftPure showVersion
|
||||||
n <- Lua.peek k
|
<#> parameter peekVersionFuzzy "Version" "version" ""
|
||||||
let versionPart = atMay (versionBranch v) (n - 1)
|
=#> functionResult pushString "string" "stringified version"
|
||||||
Lua.push (Lua.Optional versionPart)
|
]
|
||||||
return 1
|
[ method $ defun "must_be_at_least"
|
||||||
Lua.TypeString -> do
|
### must_be_at_least
|
||||||
(str :: Text) <- Lua.peek k
|
<#> parameter peekVersionFuzzy "Version" "self" "version to check"
|
||||||
if str == "must_be_at_least"
|
<#> parameter peekVersionFuzzy "Version" "reference" "minimum version"
|
||||||
then 1 <$ Lua.pushHaskellFunction must_be_at_least
|
<#> optionalParameter peekString "string" "msg" "alternative message"
|
||||||
else 1 <$ Lua.pushnil
|
=?> "Returns no result, and throws an error if this version is older than reference"
|
||||||
_ -> 1 <$ Lua.pushnil
|
]
|
||||||
|
|
||||||
-- | Create iterator.
|
|
||||||
__pairs :: Version -> Lua NumResults
|
|
||||||
__pairs v = do
|
|
||||||
Lua.pushHaskellFunction nextFn
|
|
||||||
Lua.pushnil
|
|
||||||
Lua.pushnil
|
|
||||||
return 3
|
|
||||||
where
|
|
||||||
nextFn :: AnyValue -> Optional Int -> Lua Lua.NumResults
|
|
||||||
nextFn _ (Optional key) =
|
|
||||||
case key of
|
|
||||||
Nothing -> case versionBranch v of
|
|
||||||
[] -> 2 <$ (Lua.pushnil *> Lua.pushnil)
|
|
||||||
n:_ -> 2 <$ (Lua.push (1 :: Int) *> Lua.push n)
|
|
||||||
Just n -> case atMay (versionBranch v) n of
|
|
||||||
Nothing -> 2 <$ (Lua.pushnil *> Lua.pushnil)
|
|
||||||
Just b -> 2 <$ (Lua.push (n + 1) *> Lua.push b)
|
|
||||||
|
|
||||||
-- | Convert to string.
|
|
||||||
__tostring :: Version -> Lua String
|
|
||||||
__tostring v = return (showVersion v)
|
|
||||||
|
|
||||||
-- | Default error message when a version is too old. This message is
|
|
||||||
-- formatted in Lua with the expected and actual versions as arguments.
|
|
||||||
versionTooOldMessage :: String
|
|
||||||
versionTooOldMessage = "expected version %s or newer, got %s"
|
|
||||||
|
|
||||||
-- | Throw an error if this version is older than the given version.
|
-- | Throw an error if this version is older than the given version.
|
||||||
-- FIXME: This function currently requires the string library to be
|
-- FIXME: This function currently requires the string library to be
|
||||||
-- loaded.
|
-- loaded.
|
||||||
must_be_at_least :: Version -> Version -> Optional String -> Lua NumResults
|
must_be_at_least :: LuaError e
|
||||||
must_be_at_least actual expected optMsg = do
|
=> Version -> Version -> Maybe String
|
||||||
let msg = fromMaybe versionTooOldMessage (fromOptional optMsg)
|
-> LuaE e NumResults
|
||||||
|
must_be_at_least actual expected mMsg = do
|
||||||
|
let msg = fromMaybe versionTooOldMessage mMsg
|
||||||
if expected <= actual
|
if expected <= actual
|
||||||
then return 0
|
then return 0
|
||||||
else do
|
else do
|
||||||
|
@ -152,3 +111,8 @@ must_be_at_least actual expected optMsg = do
|
||||||
Lua.push (showVersion actual)
|
Lua.push (showVersion actual)
|
||||||
Lua.call 3 1
|
Lua.call 3 1
|
||||||
Lua.error
|
Lua.error
|
||||||
|
|
||||||
|
-- | Default error message when a version is too old. This message is
|
||||||
|
-- formatted in Lua with the expected and actual versions as arguments.
|
||||||
|
versionTooOldMessage :: String
|
||||||
|
versionTooOldMessage = "expected version %s or newer, got %s"
|
||||||
|
|
|
@ -15,18 +15,19 @@ module Text.Pandoc.Lua.Module.MediaBag
|
||||||
|
|
||||||
import Prelude hiding (lookup)
|
import Prelude hiding (lookup)
|
||||||
import Control.Monad (zipWithM_)
|
import Control.Monad (zipWithM_)
|
||||||
import Foreign.Lua (Lua, NumResults, Optional)
|
import HsLua (LuaE, NumResults, Optional)
|
||||||
|
import HsLua.Marshalling (pushIterator)
|
||||||
import Text.Pandoc.Class.CommonState (CommonState (..))
|
import Text.Pandoc.Class.CommonState (CommonState (..))
|
||||||
import Text.Pandoc.Class.PandocMonad (fetchItem, getMediaBag, modifyCommonState,
|
import Text.Pandoc.Class.PandocMonad (fetchItem, getMediaBag, modifyCommonState,
|
||||||
setMediaBag)
|
setMediaBag)
|
||||||
|
import Text.Pandoc.Error (PandocError)
|
||||||
import Text.Pandoc.Lua.Marshaling ()
|
import Text.Pandoc.Lua.Marshaling ()
|
||||||
import Text.Pandoc.Lua.Marshaling.MediaBag (pushIterator)
|
|
||||||
import Text.Pandoc.Lua.PandocLua (PandocLua (..), liftPandocLua, addFunction)
|
import Text.Pandoc.Lua.PandocLua (PandocLua (..), liftPandocLua, addFunction)
|
||||||
import Text.Pandoc.MIME (MimeType)
|
import Text.Pandoc.MIME (MimeType)
|
||||||
|
|
||||||
import qualified Data.ByteString.Lazy as BL
|
import qualified Data.ByteString.Lazy as BL
|
||||||
import qualified Data.Text as T
|
import qualified Data.Text as T
|
||||||
import qualified Foreign.Lua as Lua
|
import qualified HsLua as Lua
|
||||||
import qualified Text.Pandoc.MediaBag as MB
|
import qualified Text.Pandoc.MediaBag as MB
|
||||||
|
|
||||||
--
|
--
|
||||||
|
@ -65,7 +66,15 @@ insert fp optionalMime contents = do
|
||||||
|
|
||||||
-- | Returns iterator values to be used with a Lua @for@ loop.
|
-- | Returns iterator values to be used with a Lua @for@ loop.
|
||||||
items :: PandocLua NumResults
|
items :: PandocLua NumResults
|
||||||
items = getMediaBag >>= liftPandocLua . pushIterator
|
items = do
|
||||||
|
mb <- getMediaBag
|
||||||
|
liftPandocLua $ do
|
||||||
|
let pushItem (fp, mimetype, contents) = do
|
||||||
|
Lua.pushString fp
|
||||||
|
Lua.pushText mimetype
|
||||||
|
Lua.pushByteString $ BL.toStrict contents
|
||||||
|
return (Lua.NumResults 3)
|
||||||
|
pushIterator pushItem (MB.mediaItems mb)
|
||||||
|
|
||||||
lookup :: FilePath
|
lookup :: FilePath
|
||||||
-> PandocLua NumResults
|
-> PandocLua NumResults
|
||||||
|
@ -86,7 +95,7 @@ list = do
|
||||||
zipWithM_ addEntry [1..] dirContents
|
zipWithM_ addEntry [1..] dirContents
|
||||||
return 1
|
return 1
|
||||||
where
|
where
|
||||||
addEntry :: Lua.Integer -> (FilePath, MimeType, Int) -> Lua ()
|
addEntry :: Lua.Integer -> (FilePath, MimeType, Int) -> LuaE PandocError ()
|
||||||
addEntry idx (fp, mimeType, contentLength) = do
|
addEntry idx (fp, mimeType, contentLength) = do
|
||||||
Lua.newtable
|
Lua.newtable
|
||||||
Lua.push ("path" :: T.Text) *> Lua.push fp *> Lua.rawset (-3)
|
Lua.push ("path" :: T.Text) *> Lua.push fp *> Lua.rawset (-3)
|
||||||
|
|
|
@ -15,29 +15,30 @@ module Text.Pandoc.Lua.Module.Pandoc
|
||||||
) where
|
) where
|
||||||
|
|
||||||
import Prelude hiding (read)
|
import Prelude hiding (read)
|
||||||
import Control.Monad (when)
|
import Control.Monad ((>=>), when)
|
||||||
import Control.Monad.Except (throwError)
|
import Control.Monad.Except (throwError)
|
||||||
import Data.Default (Default (..))
|
import Data.Default (Default (..))
|
||||||
import Data.Maybe (fromMaybe)
|
import Data.Maybe (fromMaybe)
|
||||||
import Foreign.Lua (Lua, NumResults, Optional, Peekable, Pushable)
|
import HsLua as Lua hiding (pushModule)
|
||||||
|
import HsLua.Class.Peekable (PeekError)
|
||||||
import System.Exit (ExitCode (..))
|
import System.Exit (ExitCode (..))
|
||||||
import Text.Pandoc.Class.PandocIO (runIO)
|
import Text.Pandoc.Class.PandocIO (runIO)
|
||||||
import Text.Pandoc.Definition (Block, Inline)
|
import Text.Pandoc.Definition (Block, Inline)
|
||||||
import Text.Pandoc.Lua.Filter (LuaFilter, SingletonsList (..), walkInlines,
|
import Text.Pandoc.Lua.Filter (SingletonsList (..), walkInlines,
|
||||||
walkInlineLists, walkBlocks, walkBlockLists)
|
walkInlineLists, walkBlocks, walkBlockLists)
|
||||||
import Text.Pandoc.Lua.Marshaling ()
|
import Text.Pandoc.Lua.Marshaling ()
|
||||||
|
import Text.Pandoc.Lua.Marshaling.AST
|
||||||
import Text.Pandoc.Lua.Marshaling.List (List (..))
|
import Text.Pandoc.Lua.Marshaling.List (List (..))
|
||||||
import Text.Pandoc.Lua.PandocLua (PandocLua, addFunction, liftPandocLua,
|
import Text.Pandoc.Lua.PandocLua (PandocLua, addFunction, liftPandocLua,
|
||||||
loadDefaultModule)
|
loadDefaultModule)
|
||||||
import Text.Pandoc.Walk (Walkable)
|
|
||||||
import Text.Pandoc.Options (ReaderOptions (readerExtensions))
|
import Text.Pandoc.Options (ReaderOptions (readerExtensions))
|
||||||
import Text.Pandoc.Process (pipeProcess)
|
import Text.Pandoc.Process (pipeProcess)
|
||||||
import Text.Pandoc.Readers (Reader (..), getReader)
|
import Text.Pandoc.Readers (Reader (..), getReader)
|
||||||
|
import Text.Pandoc.Walk (Walkable)
|
||||||
|
|
||||||
import qualified Data.ByteString.Lazy as BL
|
import qualified Data.ByteString.Lazy as BL
|
||||||
import qualified Data.ByteString.Lazy.Char8 as BSL
|
import qualified Data.ByteString.Lazy.Char8 as BSL
|
||||||
import qualified Data.Text as T
|
import qualified Data.Text as T
|
||||||
import qualified Foreign.Lua as Lua
|
|
||||||
import qualified Text.Pandoc.Lua.Util as LuaUtil
|
import qualified Text.Pandoc.Lua.Util as LuaUtil
|
||||||
import Text.Pandoc.Error
|
import Text.Pandoc.Error
|
||||||
|
|
||||||
|
@ -48,23 +49,25 @@ pushModule = do
|
||||||
loadDefaultModule "pandoc"
|
loadDefaultModule "pandoc"
|
||||||
addFunction "read" read
|
addFunction "read" read
|
||||||
addFunction "pipe" pipe
|
addFunction "pipe" pipe
|
||||||
addFunction "walk_block" walk_block
|
addFunction "walk_block" (walkElement peekBlock pushBlock)
|
||||||
addFunction "walk_inline" walk_inline
|
addFunction "walk_inline" (walkElement peekInline pushInline)
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
walkElement :: (Walkable (SingletonsList Inline) a,
|
walkElement :: (Walkable (SingletonsList Inline) a,
|
||||||
Walkable (SingletonsList Block) a,
|
Walkable (SingletonsList Block) a,
|
||||||
Walkable (List Inline) a,
|
Walkable (List Inline) a,
|
||||||
Walkable (List Block) a)
|
Walkable (List Block) a)
|
||||||
=> a -> LuaFilter -> PandocLua a
|
=> Peeker PandocError a -> Pusher PandocError a
|
||||||
walkElement x f = liftPandocLua $
|
-> LuaE PandocError NumResults
|
||||||
walkInlines f x >>= walkInlineLists f >>= walkBlocks f >>= walkBlockLists f
|
walkElement peek' push' = do
|
||||||
|
x <- forcePeek $ peek' (nthBottom 1)
|
||||||
walk_inline :: Inline -> LuaFilter -> PandocLua Inline
|
f <- peek (nthBottom 2)
|
||||||
walk_inline = walkElement
|
let walk' = walkInlines f
|
||||||
|
>=> walkInlineLists f
|
||||||
walk_block :: Block -> LuaFilter -> PandocLua Block
|
>=> walkBlocks f
|
||||||
walk_block = walkElement
|
>=> walkBlockLists f
|
||||||
|
walk' x >>= push'
|
||||||
|
return (NumResults 1)
|
||||||
|
|
||||||
read :: T.Text -> Optional T.Text -> PandocLua NumResults
|
read :: T.Text -> Optional T.Text -> PandocLua NumResults
|
||||||
read content formatSpecOrNil = liftPandocLua $ do
|
read content formatSpecOrNil = liftPandocLua $ do
|
||||||
|
@ -93,7 +96,9 @@ pipe command args input = liftPandocLua $ do
|
||||||
(ec, output) <- Lua.liftIO $ pipeProcess Nothing command args input
|
(ec, output) <- Lua.liftIO $ pipeProcess Nothing command args input
|
||||||
case ec of
|
case ec of
|
||||||
ExitSuccess -> 1 <$ Lua.push output
|
ExitSuccess -> 1 <$ Lua.push output
|
||||||
ExitFailure n -> Lua.raiseError (PipeError (T.pack command) n output)
|
ExitFailure n -> do
|
||||||
|
pushPipeError (PipeError (T.pack command) n output)
|
||||||
|
Lua.error
|
||||||
|
|
||||||
data PipeError = PipeError
|
data PipeError = PipeError
|
||||||
{ pipeErrorCommand :: T.Text
|
{ pipeErrorCommand :: T.Text
|
||||||
|
@ -101,15 +106,15 @@ data PipeError = PipeError
|
||||||
, pipeErrorOutput :: BL.ByteString
|
, pipeErrorOutput :: BL.ByteString
|
||||||
}
|
}
|
||||||
|
|
||||||
instance Peekable PipeError where
|
peekPipeError :: PeekError e => StackIndex -> LuaE e PipeError
|
||||||
peek idx =
|
peekPipeError idx =
|
||||||
PipeError
|
PipeError
|
||||||
<$> (Lua.getfield idx "command" *> Lua.peek (-1) <* Lua.pop 1)
|
<$> (Lua.getfield idx "command" *> Lua.peek (-1) <* Lua.pop 1)
|
||||||
<*> (Lua.getfield idx "error_code" *> Lua.peek (-1) <* Lua.pop 1)
|
<*> (Lua.getfield idx "error_code" *> Lua.peek (-1) <* Lua.pop 1)
|
||||||
<*> (Lua.getfield idx "output" *> Lua.peek (-1) <* Lua.pop 1)
|
<*> (Lua.getfield idx "output" *> Lua.peek (-1) <* Lua.pop 1)
|
||||||
|
|
||||||
instance Pushable PipeError where
|
pushPipeError :: PeekError e => Pusher e PipeError
|
||||||
push pipeErr = do
|
pushPipeError pipeErr = do
|
||||||
Lua.newtable
|
Lua.newtable
|
||||||
LuaUtil.addField "command" (pipeErrorCommand pipeErr)
|
LuaUtil.addField "command" (pipeErrorCommand pipeErr)
|
||||||
LuaUtil.addField "error_code" (pipeErrorCode pipeErr)
|
LuaUtil.addField "error_code" (pipeErrorCode pipeErr)
|
||||||
|
@ -117,13 +122,18 @@ instance Pushable PipeError where
|
||||||
pushPipeErrorMetaTable
|
pushPipeErrorMetaTable
|
||||||
Lua.setmetatable (-2)
|
Lua.setmetatable (-2)
|
||||||
where
|
where
|
||||||
pushPipeErrorMetaTable :: Lua ()
|
pushPipeErrorMetaTable :: PeekError e => LuaE e ()
|
||||||
pushPipeErrorMetaTable = do
|
pushPipeErrorMetaTable = do
|
||||||
v <- Lua.newmetatable "pandoc pipe error"
|
v <- Lua.newmetatable "pandoc pipe error"
|
||||||
when v $ LuaUtil.addFunction "__tostring" pipeErrorMessage
|
when v $ do
|
||||||
|
pushName "__tostring"
|
||||||
|
pushHaskellFunction pipeErrorMessage
|
||||||
|
rawset (nth 3)
|
||||||
|
|
||||||
pipeErrorMessage :: PipeError -> Lua BL.ByteString
|
pipeErrorMessage :: PeekError e => LuaE e NumResults
|
||||||
pipeErrorMessage (PipeError cmd errorCode output) = return $ mconcat
|
pipeErrorMessage = do
|
||||||
|
(PipeError cmd errorCode output) <- peekPipeError (nthBottom 1)
|
||||||
|
pushByteString . BSL.toStrict . BSL.concat $
|
||||||
[ BSL.pack "Error running "
|
[ BSL.pack "Error running "
|
||||||
, BSL.pack $ T.unpack cmd
|
, BSL.pack $ T.unpack cmd
|
||||||
, BSL.pack " (error code "
|
, BSL.pack " (error code "
|
||||||
|
@ -131,3 +141,4 @@ instance Pushable PipeError where
|
||||||
, BSL.pack "): "
|
, BSL.pack "): "
|
||||||
, if output == mempty then BSL.pack "<no output>" else output
|
, if output == mempty then BSL.pack "<no output>" else output
|
||||||
]
|
]
|
||||||
|
return (NumResults 1)
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
{-# LANGUAGE OverloadedStrings #-}
|
||||||
|
{-# LANGUAGE ScopedTypeVariables #-}
|
||||||
{- |
|
{- |
|
||||||
Module : Text.Pandoc.Lua.Module.System
|
Module : Text.Pandoc.Lua.Module.System
|
||||||
Copyright : © 2019-2021 Albert Krewinkel
|
Copyright : © 2019-2021 Albert Krewinkel
|
||||||
|
@ -12,22 +14,31 @@ module Text.Pandoc.Lua.Module.System
|
||||||
( pushModule
|
( pushModule
|
||||||
) where
|
) where
|
||||||
|
|
||||||
import Foreign.Lua (Lua, NumResults)
|
import HsLua hiding (pushModule)
|
||||||
import Foreign.Lua.Module.System (arch, env, getwd, os,
|
import HsLua.Module.System
|
||||||
with_env, with_tmpdir, with_wd)
|
(arch, env, getwd, os, with_env, with_tmpdir, with_wd)
|
||||||
import Text.Pandoc.Lua.Util (addFunction, addField)
|
import Text.Pandoc.Error (PandocError)
|
||||||
|
import Text.Pandoc.Lua.ErrorConversion ()
|
||||||
|
|
||||||
import qualified Foreign.Lua as Lua
|
import qualified HsLua as Lua
|
||||||
|
|
||||||
-- | Push the pandoc.system module on the Lua stack.
|
-- | Push the pandoc.system module on the Lua stack.
|
||||||
pushModule :: Lua NumResults
|
pushModule :: LuaE PandocError NumResults
|
||||||
pushModule = do
|
pushModule = do
|
||||||
Lua.newtable
|
Lua.pushModule $ Module
|
||||||
addField "arch" arch
|
{ moduleName = "system"
|
||||||
addField "os" os
|
, moduleDescription = "system functions"
|
||||||
addFunction "environment" env
|
, moduleFields =
|
||||||
addFunction "get_working_directory" getwd
|
[ arch
|
||||||
addFunction "with_environment" with_env
|
, os
|
||||||
addFunction "with_temporary_directory" with_tmpdir
|
]
|
||||||
addFunction "with_working_directory" with_wd
|
, moduleFunctions =
|
||||||
|
[ setName "environment" env
|
||||||
|
, setName "get_working_directory" getwd
|
||||||
|
, setName "with_environment" with_env
|
||||||
|
, setName "with_temporary_directory" with_tmpdir
|
||||||
|
, setName "with_working_directory" with_wd
|
||||||
|
]
|
||||||
|
, moduleOperations = []
|
||||||
|
}
|
||||||
return 1
|
return 1
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
{-# LANGUAGE OverloadedStrings #-}
|
||||||
{- |
|
{- |
|
||||||
Module : Text.Pandoc.Lua.Module.Types
|
Module : Text.Pandoc.Lua.Module.Types
|
||||||
Copyright : © 2019-2021 Albert Krewinkel
|
Copyright : © 2019-2021 Albert Krewinkel
|
||||||
|
@ -13,56 +14,41 @@ module Text.Pandoc.Lua.Module.Types
|
||||||
) where
|
) where
|
||||||
|
|
||||||
import Data.Version (Version)
|
import Data.Version (Version)
|
||||||
import Foreign.Lua (Lua, NumResults)
|
import HsLua (LuaE, NumResults, Peeker, Pusher)
|
||||||
import Text.Pandoc.Definition
|
import Text.Pandoc.Error (PandocError)
|
||||||
import Text.Pandoc.Lua.Marshaling.AST (LuaAttr, LuaListAttributes)
|
import Text.Pandoc.Lua.ErrorConversion ()
|
||||||
|
import Text.Pandoc.Lua.Marshaling.AST
|
||||||
import Text.Pandoc.Lua.Marshaling.Version ()
|
import Text.Pandoc.Lua.Marshaling.Version ()
|
||||||
import Text.Pandoc.Lua.Util (addFunction)
|
import Text.Pandoc.Lua.Util (addFunction)
|
||||||
|
|
||||||
import qualified Foreign.Lua as Lua
|
import qualified HsLua as Lua
|
||||||
|
|
||||||
-- | Push the pandoc.system module on the Lua stack.
|
-- | Push the pandoc.types module on the Lua stack.
|
||||||
pushModule :: Lua NumResults
|
pushModule :: LuaE PandocError NumResults
|
||||||
pushModule = do
|
pushModule = do
|
||||||
Lua.newtable
|
Lua.newtable
|
||||||
addFunction "Version" (return :: Version -> Lua Version)
|
addFunction "Version" (return :: Version -> LuaE PandocError Version)
|
||||||
pushCloneTable
|
pushCloneTable
|
||||||
Lua.setfield (Lua.nthFromTop 2) "clone"
|
Lua.setfield (Lua.nth 2) "clone"
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
pushCloneTable :: Lua NumResults
|
pushCloneTable :: LuaE PandocError NumResults
|
||||||
pushCloneTable = do
|
pushCloneTable = do
|
||||||
Lua.newtable
|
Lua.newtable
|
||||||
addFunction "Attr" cloneAttr
|
addFunction "Attr" $ cloneWith peekAttr pushAttr
|
||||||
addFunction "Block" cloneBlock
|
addFunction "Block" $ cloneWith peekBlock pushBlock
|
||||||
addFunction "Citation" cloneCitation
|
addFunction "Citation" $ cloneWith peekCitation Lua.push
|
||||||
addFunction "Inline" cloneInline
|
addFunction "Inline" $ cloneWith peekInline pushInline
|
||||||
addFunction "Meta" cloneMeta
|
addFunction "Meta" $ cloneWith peekMeta Lua.push
|
||||||
addFunction "MetaValue" cloneMetaValue
|
addFunction "MetaValue" $ cloneWith peekMetaValue pushMetaValue
|
||||||
addFunction "ListAttributes" cloneListAttributes
|
addFunction "ListAttributes" $ cloneWith peekListAttributes pushListAttributes
|
||||||
addFunction "Pandoc" clonePandoc
|
addFunction "Pandoc" $ cloneWith peekPandoc pushPandoc
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
cloneAttr :: LuaAttr -> Lua LuaAttr
|
cloneWith :: Peeker PandocError a
|
||||||
cloneAttr = return
|
-> Pusher PandocError a
|
||||||
|
-> LuaE PandocError NumResults
|
||||||
cloneBlock :: Block -> Lua Block
|
cloneWith peeker pusher = do
|
||||||
cloneBlock = return
|
x <- Lua.forcePeek $ peeker (Lua.nthBottom 1)
|
||||||
|
pusher x
|
||||||
cloneCitation :: Citation -> Lua Citation
|
return (Lua.NumResults 1)
|
||||||
cloneCitation = return
|
|
||||||
|
|
||||||
cloneInline :: Inline -> Lua Inline
|
|
||||||
cloneInline = return
|
|
||||||
|
|
||||||
cloneListAttributes :: LuaListAttributes -> Lua LuaListAttributes
|
|
||||||
cloneListAttributes = return
|
|
||||||
|
|
||||||
cloneMeta :: Meta -> Lua Meta
|
|
||||||
cloneMeta = return
|
|
||||||
|
|
||||||
cloneMetaValue :: MetaValue -> Lua MetaValue
|
|
||||||
cloneMetaValue = return
|
|
||||||
|
|
||||||
clonePandoc :: Pandoc -> Lua Pandoc
|
|
||||||
clonePandoc = return
|
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
{-# LANGUAGE LambdaCase #-}
|
{-# LANGUAGE LambdaCase #-}
|
||||||
|
{-# LANGUAGE OverloadedStrings #-}
|
||||||
{-# LANGUAGE ScopedTypeVariables #-}
|
{-# LANGUAGE ScopedTypeVariables #-}
|
||||||
|
{-# LANGUAGE TypeApplications #-}
|
||||||
{- |
|
{- |
|
||||||
Module : Text.Pandoc.Lua.Module.Utils
|
Module : Text.Pandoc.Lua.Module.Utils
|
||||||
Copyright : Copyright © 2017-2021 Albert Krewinkel
|
Copyright : Copyright © 2017-2021 Albert Krewinkel
|
||||||
|
@ -15,82 +17,137 @@ module Text.Pandoc.Lua.Module.Utils
|
||||||
) where
|
) where
|
||||||
|
|
||||||
import Control.Applicative ((<|>))
|
import Control.Applicative ((<|>))
|
||||||
import Control.Monad.Catch (try)
|
import Control.Monad ((<$!>))
|
||||||
import Data.Data (showConstr, toConstr)
|
import Data.Data (showConstr, toConstr)
|
||||||
import Data.Default (def)
|
import Data.Default (def)
|
||||||
import Data.Version (Version)
|
import Data.Version (Version)
|
||||||
import Foreign.Lua (Peekable, Lua, NumResults (..))
|
import HsLua as Lua hiding (pushModule)
|
||||||
|
import HsLua.Class.Peekable (PeekError)
|
||||||
import Text.Pandoc.Definition
|
import Text.Pandoc.Definition
|
||||||
import Text.Pandoc.Error (PandocError)
|
import Text.Pandoc.Error (PandocError)
|
||||||
import Text.Pandoc.Lua.Marshaling ()
|
import Text.Pandoc.Lua.Marshaling ()
|
||||||
|
import Text.Pandoc.Lua.Marshaling.AST
|
||||||
|
( peekBlock, peekInline, peekPandoc, pushBlock, pushInline, pushPandoc
|
||||||
|
, peekAttr, peekListAttributes, peekMeta, peekMetaValue)
|
||||||
|
import Text.Pandoc.Lua.Marshaling.List (pushPandocList)
|
||||||
import Text.Pandoc.Lua.Marshaling.SimpleTable
|
import Text.Pandoc.Lua.Marshaling.SimpleTable
|
||||||
( SimpleTable (..)
|
( SimpleTable (..), peekSimpleTable, pushSimpleTable )
|
||||||
, pushSimpleTable
|
import Text.Pandoc.Lua.Marshaling.Version (peekVersionFuzzy, pushVersion)
|
||||||
)
|
import Text.Pandoc.Lua.PandocLua (PandocLua (unPandocLua))
|
||||||
import Text.Pandoc.Lua.PandocLua (PandocLua, addFunction, liftPandocLua)
|
|
||||||
|
|
||||||
import qualified Data.Digest.Pure.SHA as SHA
|
import qualified Data.Digest.Pure.SHA as SHA
|
||||||
import qualified Data.ByteString.Lazy as BSL
|
import qualified Data.ByteString.Lazy as BSL
|
||||||
import qualified Data.Text as T
|
import qualified Data.Text as T
|
||||||
import qualified Foreign.Lua as Lua
|
import qualified HsLua.Packaging as Lua
|
||||||
import qualified Text.Pandoc.Builder as B
|
import qualified Text.Pandoc.Builder as B
|
||||||
import qualified Text.Pandoc.Filter.JSON as JSONFilter
|
import qualified Text.Pandoc.Filter.JSON as JSONFilter
|
||||||
import qualified Text.Pandoc.Shared as Shared
|
import qualified Text.Pandoc.Shared as Shared
|
||||||
|
import qualified Text.Pandoc.UTF8 as UTF8
|
||||||
import qualified Text.Pandoc.Writers.Shared as Shared
|
import qualified Text.Pandoc.Writers.Shared as Shared
|
||||||
|
|
||||||
-- | Push the "pandoc.utils" module to the Lua stack.
|
-- | Push the "pandoc.utils" module to the Lua stack.
|
||||||
pushModule :: PandocLua NumResults
|
pandocUtilsModule :: Module PandocError
|
||||||
pushModule = do
|
pandocUtilsModule = Module
|
||||||
liftPandocLua Lua.newtable
|
{ moduleName = "pandoc.utils"
|
||||||
addFunction "blocks_to_inlines" blocksToInlines
|
, moduleDescription = "pandoc utility functions"
|
||||||
addFunction "equals" equals
|
, moduleFields = []
|
||||||
addFunction "from_simple_table" from_simple_table
|
, moduleOperations = []
|
||||||
addFunction "make_sections" makeSections
|
, moduleFunctions =
|
||||||
addFunction "normalize_date" normalizeDate
|
[ defun "blocks_to_inlines"
|
||||||
addFunction "run_json_filter" runJSONFilter
|
### (\blks mSep -> do
|
||||||
addFunction "sha1" sha1
|
let sep = maybe Shared.defaultBlocksSeparator B.fromList mSep
|
||||||
addFunction "stringify" stringify
|
return $ B.toList (Shared.blocksToInlinesWithSep sep blks))
|
||||||
addFunction "to_roman_numeral" toRomanNumeral
|
<#> parameter (peekList peekBlock) "list of blocks"
|
||||||
addFunction "to_simple_table" to_simple_table
|
"blocks" ""
|
||||||
addFunction "Version" (return :: Version -> Lua Version)
|
<#> optionalParameter (peekList peekInline) "list of inlines"
|
||||||
return 1
|
"inline" ""
|
||||||
|
=#> functionResult (pushPandocList pushInline) "list of inlines" ""
|
||||||
|
|
||||||
-- | Squashes a list of blocks into inlines.
|
, defun "equals"
|
||||||
blocksToInlines :: [Block] -> Lua.Optional [Inline] -> PandocLua [Inline]
|
### liftPure2 (==)
|
||||||
blocksToInlines blks optSep = liftPandocLua $ do
|
<#> parameter peekAstElement "AST element" "elem1" ""
|
||||||
let sep = maybe Shared.defaultBlocksSeparator B.fromList
|
<#> parameter peekAstElement "AST element" "elem2" ""
|
||||||
$ Lua.fromOptional optSep
|
=#> functionResult pushBool "boolean" "true iff elem1 == elem2"
|
||||||
return $ B.toList (Shared.blocksToInlinesWithSep sep blks)
|
|
||||||
|
|
||||||
-- | Convert list of Pandoc blocks into sections using Divs.
|
, defun "make_sections"
|
||||||
makeSections :: Bool -> Lua.Optional Int -> [Block] -> Lua [Block]
|
### liftPure3 Shared.makeSections
|
||||||
makeSections number baselevel =
|
<#> parameter peekBool "boolean" "numbering" "add header numbers"
|
||||||
return . Shared.makeSections number (Lua.fromOptional baselevel)
|
<#> parameter (\i -> (Nothing <$ peekNil i) <|> (Just <$!> peekIntegral i))
|
||||||
|
"integer or nil" "baselevel" ""
|
||||||
|
<#> parameter (peekList peekBlock) "list of blocks"
|
||||||
|
"blocks" "document blocks to process"
|
||||||
|
=#> functionResult (pushPandocList pushBlock) "list of Blocks"
|
||||||
|
"processes blocks"
|
||||||
|
|
||||||
-- | Parse a date and convert (if possible) to "YYYY-MM-DD" format. We
|
, defun "normalize_date"
|
||||||
-- limit years to the range 1601-9999 (ISO 8601 accepts greater than
|
### liftPure Shared.normalizeDate
|
||||||
-- or equal to 1583, but MS Word only accepts dates starting 1601).
|
<#> parameter peekText "string" "date" "the date string"
|
||||||
-- Returns nil instead of a string if the conversion failed.
|
=#> functionResult (maybe pushnil pushText) "string or nil"
|
||||||
normalizeDate :: T.Text -> Lua (Lua.Optional T.Text)
|
"normalized date, or nil if normalization failed."
|
||||||
normalizeDate = return . Lua.Optional . Shared.normalizeDate
|
#? T.unwords
|
||||||
|
[ "Parse a date and convert (if possible) to \"YYYY-MM-DD\" format. We"
|
||||||
|
, "limit years to the range 1601-9999 (ISO 8601 accepts greater than"
|
||||||
|
, "or equal to 1583, but MS Word only accepts dates starting 1601)."
|
||||||
|
, "Returns nil instead of a string if the conversion failed."
|
||||||
|
]
|
||||||
|
|
||||||
-- | Run a JSON filter on the given document.
|
, defun "sha1"
|
||||||
runJSONFilter :: Pandoc
|
### liftPure (SHA.showDigest . SHA.sha1)
|
||||||
-> FilePath
|
<#> parameter (fmap BSL.fromStrict . peekByteString) "string"
|
||||||
-> Lua.Optional [String]
|
"input" ""
|
||||||
-> PandocLua Pandoc
|
=#> functionResult pushString "string" "hexadecimal hash value"
|
||||||
runJSONFilter doc filterFile optArgs = do
|
#? "Compute the hash of the given string value."
|
||||||
args <- case Lua.fromOptional optArgs of
|
|
||||||
Just x -> return x
|
, defun "Version"
|
||||||
Nothing -> liftPandocLua $ do
|
### liftPure (id @Version)
|
||||||
|
<#> parameter peekVersionFuzzy
|
||||||
|
"version string, list of integers, or integer"
|
||||||
|
"v" "version description"
|
||||||
|
=#> functionResult pushVersion "Version" "new Version object"
|
||||||
|
#? "Creates a Version object."
|
||||||
|
|
||||||
|
, defun "run_json_filter"
|
||||||
|
### (\doc filterPath margs -> do
|
||||||
|
args <- case margs of
|
||||||
|
Just xs -> return xs
|
||||||
|
Nothing -> do
|
||||||
Lua.getglobal "FORMAT"
|
Lua.getglobal "FORMAT"
|
||||||
(:[]) <$> Lua.popValue
|
(forcePeek ((:[]) <$!> peekString top) <* pop 1)
|
||||||
JSONFilter.apply def args filterFile doc
|
JSONFilter.apply def args filterPath doc
|
||||||
|
)
|
||||||
|
<#> parameter peekPandoc "Pandoc" "doc" "input document"
|
||||||
|
<#> parameter peekString "filepath" "filter_path" "path to filter"
|
||||||
|
<#> optionalParameter (peekList peekString) "list of strings"
|
||||||
|
"args" "arguments to pass to the filter"
|
||||||
|
=#> functionResult pushPandoc "Pandoc" "filtered document"
|
||||||
|
|
||||||
|
, defun "stringify"
|
||||||
|
### unPandocLua . stringify
|
||||||
|
<#> parameter peekAstElement "AST element" "elem" "some pandoc AST element"
|
||||||
|
=#> functionResult pushText "string" "stringified element"
|
||||||
|
|
||||||
|
, defun "from_simple_table"
|
||||||
|
### from_simple_table
|
||||||
|
<#> parameter peekSimpleTable "SimpleTable" "simple_tbl" ""
|
||||||
|
=?> "Simple table"
|
||||||
|
|
||||||
|
, defun "to_roman_numeral"
|
||||||
|
### liftPure Shared.toRomanNumeral
|
||||||
|
<#> parameter (peekIntegral @Int) "integer" "n" "number smaller than 4000"
|
||||||
|
=#> functionResult pushText "string" "roman numeral"
|
||||||
|
#? "Converts a number < 4000 to uppercase roman numeral."
|
||||||
|
|
||||||
|
, defun "to_simple_table"
|
||||||
|
### to_simple_table
|
||||||
|
<#> parameter peekTable "Block" "tbl" "a table"
|
||||||
|
=#> functionResult pushSimpleTable "SimpleTable" "SimpleTable object"
|
||||||
|
#? "Converts a table into an old/simple table."
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
pushModule :: LuaE PandocError NumResults
|
||||||
|
pushModule = 1 <$ Lua.pushModule pandocUtilsModule
|
||||||
|
|
||||||
-- | Calculate the hash of the given contents.
|
|
||||||
sha1 :: BSL.ByteString
|
|
||||||
-> Lua T.Text
|
|
||||||
sha1 = return . T.pack . SHA.showDigest . SHA.sha1
|
|
||||||
|
|
||||||
-- | Convert pandoc structure to a string with formatting removed.
|
-- | Convert pandoc structure to a string with formatting removed.
|
||||||
-- Footnotes are skipped (since we don't want their contents in link
|
-- Footnotes are skipped (since we don't want their contents in link
|
||||||
|
@ -111,9 +168,6 @@ stringifyMetaValue mv = case mv of
|
||||||
MetaString s -> s
|
MetaString s -> s
|
||||||
_ -> Shared.stringify mv
|
_ -> Shared.stringify mv
|
||||||
|
|
||||||
equals :: AstElement -> AstElement -> PandocLua Bool
|
|
||||||
equals e1 e2 = return (e1 == e2)
|
|
||||||
|
|
||||||
data AstElement
|
data AstElement
|
||||||
= PandocElement Pandoc
|
= PandocElement Pandoc
|
||||||
| MetaElement Meta
|
| MetaElement Meta
|
||||||
|
@ -125,22 +179,19 @@ data AstElement
|
||||||
| CitationElement Citation
|
| CitationElement Citation
|
||||||
deriving (Eq, Show)
|
deriving (Eq, Show)
|
||||||
|
|
||||||
instance Peekable AstElement where
|
peekAstElement :: PeekError e => Peeker e AstElement
|
||||||
peek idx = do
|
peekAstElement = retrieving "pandoc AST element" . choice
|
||||||
res <- try $ (PandocElement <$> Lua.peek idx)
|
[ (fmap PandocElement . peekPandoc)
|
||||||
<|> (InlineElement <$> Lua.peek idx)
|
, (fmap InlineElement . peekInline)
|
||||||
<|> (BlockElement <$> Lua.peek idx)
|
, (fmap BlockElement . peekBlock)
|
||||||
<|> (AttrElement <$> Lua.peek idx)
|
, (fmap AttrElement . peekAttr)
|
||||||
<|> (ListAttributesElement <$> Lua.peek idx)
|
, (fmap ListAttributesElement . peekListAttributes)
|
||||||
<|> (MetaElement <$> Lua.peek idx)
|
, (fmap MetaElement . peekMeta)
|
||||||
<|> (MetaValueElement <$> Lua.peek idx)
|
, (fmap MetaValueElement . peekMetaValue)
|
||||||
case res of
|
]
|
||||||
Right x -> return x
|
|
||||||
Left (_ :: PandocError) -> Lua.throwMessage
|
|
||||||
"Expected an AST element, but could not parse value as such."
|
|
||||||
|
|
||||||
-- | Converts an old/simple table into a normal table block element.
|
-- | Converts an old/simple table into a normal table block element.
|
||||||
from_simple_table :: SimpleTable -> Lua NumResults
|
from_simple_table :: SimpleTable -> LuaE PandocError NumResults
|
||||||
from_simple_table (SimpleTable capt aligns widths head' body) = do
|
from_simple_table (SimpleTable capt aligns widths head' body) = do
|
||||||
Lua.push $ Table
|
Lua.push $ Table
|
||||||
nullAttr
|
nullAttr
|
||||||
|
@ -159,17 +210,19 @@ from_simple_table (SimpleTable capt aligns widths head' body) = do
|
||||||
toColWidth w = ColWidth w
|
toColWidth w = ColWidth w
|
||||||
|
|
||||||
-- | Converts a table into an old/simple table.
|
-- | Converts a table into an old/simple table.
|
||||||
to_simple_table :: Block -> Lua NumResults
|
to_simple_table :: Block -> LuaE PandocError SimpleTable
|
||||||
to_simple_table = \case
|
to_simple_table = \case
|
||||||
Table _attr caption specs thead tbodies tfoot -> do
|
Table _attr caption specs thead tbodies tfoot -> do
|
||||||
let (capt, aligns, widths, headers, rows) =
|
let (capt, aligns, widths, headers, rows) =
|
||||||
Shared.toLegacyTable caption specs thead tbodies tfoot
|
Shared.toLegacyTable caption specs thead tbodies tfoot
|
||||||
pushSimpleTable $ SimpleTable capt aligns widths headers rows
|
return $ SimpleTable capt aligns widths headers rows
|
||||||
return (NumResults 1)
|
blk -> Lua.failLua $ mconcat
|
||||||
blk ->
|
[ "Expected Table, got ", showConstr (toConstr blk), "." ]
|
||||||
Lua.throwMessage $
|
|
||||||
"Expected Table, got " <> showConstr (toConstr blk) <> "."
|
|
||||||
|
|
||||||
-- | Convert a number < 4000 to uppercase roman numeral.
|
peekTable :: LuaError e => Peeker e Block
|
||||||
toRomanNumeral :: Lua.Integer -> PandocLua T.Text
|
peekTable idx = peekBlock idx >>= \case
|
||||||
toRomanNumeral = return . Shared.toRomanNumeral . fromIntegral
|
t@(Table {}) -> return t
|
||||||
|
b -> Lua.failPeek $ mconcat
|
||||||
|
[ "Expected Table, got "
|
||||||
|
, UTF8.fromString $ showConstr (toConstr b)
|
||||||
|
, "." ]
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
{-# LANGUAGE FlexibleContexts #-}
|
||||||
|
{-# LANGUAGE OverloadedStrings #-}
|
||||||
|
{-# LANGUAGE TypeApplications #-}
|
||||||
{- |
|
{- |
|
||||||
Module : Text.Pandoc.Lua.Packages
|
Module : Text.Pandoc.Lua.Packages
|
||||||
Copyright : Copyright © 2017-2021 Albert Krewinkel
|
Copyright : Copyright © 2017-2021 Albert Krewinkel
|
||||||
|
@ -13,12 +16,13 @@ module Text.Pandoc.Lua.Packages
|
||||||
) where
|
) where
|
||||||
|
|
||||||
import Control.Monad (forM_)
|
import Control.Monad (forM_)
|
||||||
import Foreign.Lua (NumResults)
|
import HsLua (NumResults)
|
||||||
|
import Text.Pandoc.Error (PandocError)
|
||||||
import Text.Pandoc.Lua.PandocLua (PandocLua, liftPandocLua, loadDefaultModule)
|
import Text.Pandoc.Lua.PandocLua (PandocLua, liftPandocLua, loadDefaultModule)
|
||||||
|
|
||||||
import qualified Foreign.Lua as Lua
|
import qualified HsLua as Lua
|
||||||
import qualified Foreign.Lua.Module.Path as Path
|
import qualified HsLua.Module.Path as Path
|
||||||
import qualified Foreign.Lua.Module.Text as Text
|
import qualified HsLua.Module.Text as Text
|
||||||
import qualified Text.Pandoc.Lua.Module.Pandoc as Pandoc
|
import qualified Text.Pandoc.Lua.Module.Pandoc as Pandoc
|
||||||
import qualified Text.Pandoc.Lua.Module.MediaBag as MediaBag
|
import qualified Text.Pandoc.Lua.Module.MediaBag as MediaBag
|
||||||
import qualified Text.Pandoc.Lua.Module.System as System
|
import qualified Text.Pandoc.Lua.Module.System as System
|
||||||
|
@ -30,8 +34,8 @@ installPandocPackageSearcher :: PandocLua ()
|
||||||
installPandocPackageSearcher = liftPandocLua $ do
|
installPandocPackageSearcher = liftPandocLua $ do
|
||||||
Lua.getglobal' "package.searchers"
|
Lua.getglobal' "package.searchers"
|
||||||
shiftArray
|
shiftArray
|
||||||
Lua.pushHaskellFunction pandocPackageSearcher
|
Lua.pushHaskellFunction $ Lua.toHaskellFunction pandocPackageSearcher
|
||||||
Lua.rawseti (Lua.nthFromTop 2) 1
|
Lua.rawseti (Lua.nth 2) 1
|
||||||
Lua.pop 1 -- remove 'package.searchers' from stack
|
Lua.pop 1 -- remove 'package.searchers' from stack
|
||||||
where
|
where
|
||||||
shiftArray = forM_ [4, 3, 2, 1] $ \i -> do
|
shiftArray = forM_ [4, 3, 2, 1] $ \i -> do
|
||||||
|
@ -42,14 +46,16 @@ installPandocPackageSearcher = liftPandocLua $ do
|
||||||
pandocPackageSearcher :: String -> PandocLua NumResults
|
pandocPackageSearcher :: String -> PandocLua NumResults
|
||||||
pandocPackageSearcher pkgName =
|
pandocPackageSearcher pkgName =
|
||||||
case pkgName of
|
case pkgName of
|
||||||
"pandoc" -> pushWrappedHsFun Pandoc.pushModule
|
"pandoc" -> pushWrappedHsFun $ Lua.toHaskellFunction @PandocError Pandoc.pushModule
|
||||||
"pandoc.mediabag" -> pushWrappedHsFun MediaBag.pushModule
|
"pandoc.mediabag" -> pushWrappedHsFun $ Lua.toHaskellFunction @PandocError MediaBag.pushModule
|
||||||
"pandoc.path" -> pushWrappedHsFun Path.pushModule
|
"pandoc.path" -> pushWrappedHsFun
|
||||||
"pandoc.system" -> pushWrappedHsFun System.pushModule
|
(Lua.NumResults 1 <$ Lua.pushModule @PandocError Path.documentedModule)
|
||||||
"pandoc.types" -> pushWrappedHsFun Types.pushModule
|
"pandoc.system" -> pushWrappedHsFun $ Lua.toHaskellFunction System.pushModule
|
||||||
"pandoc.utils" -> pushWrappedHsFun Utils.pushModule
|
"pandoc.types" -> pushWrappedHsFun $ Lua.toHaskellFunction @PandocError Types.pushModule
|
||||||
"text" -> pushWrappedHsFun Text.pushModule
|
"pandoc.utils" -> pushWrappedHsFun $ Lua.toHaskellFunction @PandocError Utils.pushModule
|
||||||
"pandoc.List" -> pushWrappedHsFun (loadDefaultModule pkgName)
|
"text" -> pushWrappedHsFun
|
||||||
|
(Lua.NumResults 1 <$ Lua.pushModule @PandocError Text.documentedModule)
|
||||||
|
"pandoc.List" -> pushWrappedHsFun $ Lua.toHaskellFunction @PandocError (loadDefaultModule pkgName)
|
||||||
_ -> reportPandocSearcherFailure
|
_ -> reportPandocSearcherFailure
|
||||||
where
|
where
|
||||||
pushWrappedHsFun f = liftPandocLua $ do
|
pushWrappedHsFun f = liftPandocLua $ do
|
||||||
|
|
|
@ -28,20 +28,19 @@ module Text.Pandoc.Lua.PandocLua
|
||||||
|
|
||||||
import Control.Monad.Catch (MonadCatch, MonadMask, MonadThrow)
|
import Control.Monad.Catch (MonadCatch, MonadMask, MonadThrow)
|
||||||
import Control.Monad.Except (MonadError (catchError, throwError))
|
import Control.Monad.Except (MonadError (catchError, throwError))
|
||||||
import Control.Monad.IO.Class (MonadIO (liftIO))
|
import Control.Monad.IO.Class (MonadIO)
|
||||||
import Foreign.Lua (Lua (..), NumResults, Pushable, ToHaskellFunction)
|
import HsLua as Lua
|
||||||
import Text.Pandoc.Class.PandocMonad (PandocMonad (..), readDefaultDataFile)
|
import Text.Pandoc.Class.PandocMonad (PandocMonad (..), readDefaultDataFile)
|
||||||
import Text.Pandoc.Error (PandocError (PandocLuaError))
|
import Text.Pandoc.Error (PandocError (PandocLuaError))
|
||||||
import Text.Pandoc.Lua.Global (Global (..), setGlobals)
|
import Text.Pandoc.Lua.Global (Global (..), setGlobals)
|
||||||
import Text.Pandoc.Lua.ErrorConversion (errorConversion)
|
import Text.Pandoc.Lua.Marshaling.CommonState (peekCommonState)
|
||||||
|
|
||||||
import qualified Control.Monad.Catch as Catch
|
import qualified Control.Monad.Catch as Catch
|
||||||
import qualified Data.Text as T
|
import qualified Data.Text as T
|
||||||
import qualified Foreign.Lua as Lua
|
|
||||||
import qualified Text.Pandoc.Class.IO as IO
|
import qualified Text.Pandoc.Class.IO as IO
|
||||||
|
|
||||||
-- | Type providing access to both, pandoc and Lua operations.
|
-- | Type providing access to both, pandoc and Lua operations.
|
||||||
newtype PandocLua a = PandocLua { unPandocLua :: Lua a }
|
newtype PandocLua a = PandocLua { unPandocLua :: LuaE PandocError a }
|
||||||
deriving
|
deriving
|
||||||
( Applicative
|
( Applicative
|
||||||
, Functor
|
, Functor
|
||||||
|
@ -53,7 +52,7 @@ newtype PandocLua a = PandocLua { unPandocLua :: Lua a }
|
||||||
)
|
)
|
||||||
|
|
||||||
-- | Lift a @'Lua'@ operation into the @'PandocLua'@ type.
|
-- | Lift a @'Lua'@ operation into the @'PandocLua'@ type.
|
||||||
liftPandocLua :: Lua a -> PandocLua a
|
liftPandocLua :: LuaE PandocError a -> PandocLua a
|
||||||
liftPandocLua = PandocLua
|
liftPandocLua = PandocLua
|
||||||
|
|
||||||
-- | Evaluate a @'PandocLua'@ computation, running all contained Lua
|
-- | Evaluate a @'PandocLua'@ computation, running all contained Lua
|
||||||
|
@ -62,7 +61,7 @@ runPandocLua :: (PandocMonad m, MonadIO m) => PandocLua a -> m a
|
||||||
runPandocLua pLua = do
|
runPandocLua pLua = do
|
||||||
origState <- getCommonState
|
origState <- getCommonState
|
||||||
globals <- defaultGlobals
|
globals <- defaultGlobals
|
||||||
(result, newState) <- liftIO . Lua.run' errorConversion . unPandocLua $ do
|
(result, newState) <- liftIO . Lua.run . unPandocLua $ do
|
||||||
putCommonState origState
|
putCommonState origState
|
||||||
liftPandocLua $ setGlobals globals
|
liftPandocLua $ setGlobals globals
|
||||||
r <- pLua
|
r <- pLua
|
||||||
|
@ -71,17 +70,17 @@ runPandocLua pLua = do
|
||||||
putCommonState newState
|
putCommonState newState
|
||||||
return result
|
return result
|
||||||
|
|
||||||
instance {-# OVERLAPPING #-} ToHaskellFunction (PandocLua NumResults) where
|
instance {-# OVERLAPPING #-} Exposable PandocError (PandocLua NumResults) where
|
||||||
toHsFun _narg = unPandocLua
|
partialApply _narg = unPandocLua
|
||||||
|
|
||||||
instance Pushable a => ToHaskellFunction (PandocLua a) where
|
instance Pushable a => Exposable PandocError (PandocLua a) where
|
||||||
toHsFun _narg x = 1 <$ (unPandocLua x >>= Lua.push)
|
partialApply _narg x = 1 <$ (unPandocLua x >>= Lua.push)
|
||||||
|
|
||||||
-- | Add a function to the table at the top of the stack, using the given name.
|
-- | Add a function to the table at the top of the stack, using the given name.
|
||||||
addFunction :: ToHaskellFunction a => String -> a -> PandocLua ()
|
addFunction :: Exposable PandocError a => Name -> a -> PandocLua ()
|
||||||
addFunction name fn = liftPandocLua $ do
|
addFunction name fn = liftPandocLua $ do
|
||||||
Lua.push name
|
Lua.pushName name
|
||||||
Lua.pushHaskellFunction fn
|
Lua.pushHaskellFunction $ toHaskellFunction fn
|
||||||
Lua.rawset (-3)
|
Lua.rawset (-3)
|
||||||
|
|
||||||
-- | Load a pure Lua module included with pandoc. Leaves the result on
|
-- | Load a pure Lua module included with pandoc. Leaves the result on
|
||||||
|
@ -93,8 +92,8 @@ addFunction name fn = liftPandocLua $ do
|
||||||
loadDefaultModule :: String -> PandocLua NumResults
|
loadDefaultModule :: String -> PandocLua NumResults
|
||||||
loadDefaultModule name = do
|
loadDefaultModule name = do
|
||||||
script <- readDefaultDataFile (name <> ".lua")
|
script <- readDefaultDataFile (name <> ".lua")
|
||||||
status <- liftPandocLua $ Lua.dostring script
|
result <- liftPandocLua $ Lua.dostring script
|
||||||
if status == Lua.OK
|
if result == Lua.OK
|
||||||
then return (1 :: NumResults)
|
then return (1 :: NumResults)
|
||||||
else do
|
else do
|
||||||
msg <- liftPandocLua Lua.popValue
|
msg <- liftPandocLua Lua.popValue
|
||||||
|
@ -135,7 +134,7 @@ instance PandocMonad PandocLua where
|
||||||
|
|
||||||
getCommonState = PandocLua $ do
|
getCommonState = PandocLua $ do
|
||||||
Lua.getglobal "PANDOC_STATE"
|
Lua.getglobal "PANDOC_STATE"
|
||||||
Lua.peek Lua.stackTop
|
forcePeek $ peekCommonState Lua.top
|
||||||
putCommonState = PandocLua . setGlobals . (:[]) . PANDOC_STATE
|
putCommonState = PandocLua . setGlobals . (:[]) . PANDOC_STATE
|
||||||
|
|
||||||
logOutput = IO.logOutput
|
logOutput = IO.logOutput
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
|
{-# LANGUAGE AllowAmbiguousTypes #-}
|
||||||
{-# LANGUAGE FlexibleInstances #-}
|
{-# LANGUAGE FlexibleInstances #-}
|
||||||
{-# LANGUAGE LambdaCase #-}
|
{-# LANGUAGE MultiParamTypeClasses #-}
|
||||||
{-# LANGUAGE OverloadedStrings #-}
|
{-# LANGUAGE OverloadedStrings #-}
|
||||||
|
{-# LANGUAGE ScopedTypeVariables #-}
|
||||||
|
{-# LANGUAGE TypeApplications #-}
|
||||||
{- |
|
{- |
|
||||||
Module : Text.Pandoc.Lua.Util
|
Module : Text.Pandoc.Lua.Util
|
||||||
Copyright : © 2012-2021 John MacFarlane,
|
Copyright : © 2012-2021 John MacFarlane,
|
||||||
|
@ -14,114 +17,91 @@ Lua utility functions.
|
||||||
-}
|
-}
|
||||||
module Text.Pandoc.Lua.Util
|
module Text.Pandoc.Lua.Util
|
||||||
( getTag
|
( getTag
|
||||||
, rawField
|
|
||||||
, addField
|
, addField
|
||||||
, addFunction
|
, addFunction
|
||||||
, addValue
|
|
||||||
, pushViaConstructor
|
, pushViaConstructor
|
||||||
, defineHowTo
|
|
||||||
, throwTopMessageAsError'
|
|
||||||
, callWithTraceback
|
, callWithTraceback
|
||||||
, dofileWithTraceback
|
, dofileWithTraceback
|
||||||
|
, pushViaConstr'
|
||||||
) where
|
) where
|
||||||
|
|
||||||
import Control.Monad (unless, when)
|
import Control.Monad (unless, when)
|
||||||
import Data.Text (Text)
|
import HsLua
|
||||||
import Foreign.Lua ( Lua, NumArgs, NumResults, Peekable, Pushable, StackIndex
|
import qualified HsLua as Lua
|
||||||
, Status, ToHaskellFunction )
|
|
||||||
import qualified Foreign.Lua as Lua
|
|
||||||
import qualified Text.Pandoc.UTF8 as UTF8
|
|
||||||
|
|
||||||
-- | Get value behind key from table at given index.
|
|
||||||
rawField :: Peekable a => StackIndex -> String -> Lua a
|
|
||||||
rawField idx key = do
|
|
||||||
absidx <- Lua.absindex idx
|
|
||||||
Lua.push key
|
|
||||||
Lua.rawget absidx
|
|
||||||
Lua.popValue
|
|
||||||
|
|
||||||
-- | Add a value to the table at the top of the stack at a string-index.
|
-- | Add a value to the table at the top of the stack at a string-index.
|
||||||
addField :: Pushable a => String -> a -> Lua ()
|
addField :: (LuaError e, Pushable a) => String -> a -> LuaE e ()
|
||||||
addField = addValue
|
addField key value = do
|
||||||
|
|
||||||
-- | Add a key-value pair to the table at the top of the stack.
|
|
||||||
addValue :: (Pushable a, Pushable b) => a -> b -> Lua ()
|
|
||||||
addValue key value = do
|
|
||||||
Lua.push key
|
Lua.push key
|
||||||
Lua.push value
|
Lua.push value
|
||||||
Lua.rawset (Lua.nthFromTop 3)
|
Lua.rawset (Lua.nth 3)
|
||||||
|
|
||||||
-- | Add a function to the table at the top of the stack, using the given name.
|
-- | Add a function to the table at the top of the stack, using the
|
||||||
addFunction :: ToHaskellFunction a => String -> a -> Lua ()
|
-- given name.
|
||||||
|
addFunction :: Exposable e a => String -> a -> LuaE e ()
|
||||||
addFunction name fn = do
|
addFunction name fn = do
|
||||||
Lua.push name
|
Lua.push name
|
||||||
Lua.pushHaskellFunction fn
|
Lua.pushHaskellFunction $ toHaskellFunction fn
|
||||||
Lua.rawset (-3)
|
Lua.rawset (-3)
|
||||||
|
|
||||||
-- | Helper class for pushing a single value to the stack via a lua function.
|
-- | Helper class for pushing a single value to the stack via a lua
|
||||||
-- See @pushViaCall@.
|
-- function. See @pushViaCall@.
|
||||||
class PushViaCall a where
|
class LuaError e => PushViaCall e a where
|
||||||
pushViaCall' :: String -> Lua () -> NumArgs -> a
|
pushViaCall' :: LuaError e => Name -> LuaE e () -> NumArgs -> a
|
||||||
|
|
||||||
instance PushViaCall (Lua ()) where
|
instance LuaError e => PushViaCall e (LuaE e ()) where
|
||||||
pushViaCall' fn pushArgs num = do
|
pushViaCall' fn pushArgs num = do
|
||||||
Lua.push fn
|
Lua.pushName @e fn
|
||||||
Lua.rawget Lua.registryindex
|
Lua.rawget Lua.registryindex
|
||||||
pushArgs
|
pushArgs
|
||||||
Lua.call num 1
|
Lua.call num 1
|
||||||
|
|
||||||
instance (Pushable a, PushViaCall b) => PushViaCall (a -> b) where
|
instance (LuaError e, Pushable a, PushViaCall e b) =>
|
||||||
|
PushViaCall e (a -> b) where
|
||||||
pushViaCall' fn pushArgs num x =
|
pushViaCall' fn pushArgs num x =
|
||||||
pushViaCall' fn (pushArgs *> Lua.push x) (num + 1)
|
pushViaCall' @e fn (pushArgs *> Lua.push x) (num + 1)
|
||||||
|
|
||||||
-- | Push an value to the stack via a lua function. The lua function is called
|
-- | Push an value to the stack via a lua function. The lua function is called
|
||||||
-- with all arguments that are passed to this function and is expected to return
|
-- with all arguments that are passed to this function and is expected to return
|
||||||
-- a single value.
|
-- a single value.
|
||||||
pushViaCall :: PushViaCall a => String -> a
|
pushViaCall :: forall e a. LuaError e => PushViaCall e a => Name -> a
|
||||||
pushViaCall fn = pushViaCall' fn (return ()) 0
|
pushViaCall fn = pushViaCall' @e fn (return ()) 0
|
||||||
|
|
||||||
-- | Call a pandoc element constructor within Lua, passing all given arguments.
|
-- | Call a pandoc element constructor within Lua, passing all given arguments.
|
||||||
pushViaConstructor :: PushViaCall a => String -> a
|
pushViaConstructor :: forall e a. LuaError e => PushViaCall e a => Name -> a
|
||||||
pushViaConstructor pandocFn = pushViaCall ("pandoc." ++ pandocFn)
|
pushViaConstructor pandocFn = pushViaCall @e ("pandoc." <> pandocFn)
|
||||||
|
|
||||||
-- | Get the tag of a value. This is an optimized and specialized version of
|
-- | Get the tag of a value. This is an optimized and specialized version of
|
||||||
-- @Lua.getfield idx "tag"@. It only checks for the field on the table at index
|
-- @Lua.getfield idx "tag"@. It only checks for the field on the table at index
|
||||||
-- @idx@ and on its metatable, also ignoring any @__index@ value on the
|
-- @idx@ and on its metatable, also ignoring any @__index@ value on the
|
||||||
-- metatable.
|
-- metatable.
|
||||||
getTag :: StackIndex -> Lua String
|
getTag :: LuaError e => Peeker e Name
|
||||||
getTag idx = do
|
getTag idx = do
|
||||||
-- push metatable or just the table
|
-- push metatable or just the table
|
||||||
|
liftLua $ do
|
||||||
Lua.getmetatable idx >>= \hasMT -> unless hasMT (Lua.pushvalue idx)
|
Lua.getmetatable idx >>= \hasMT -> unless hasMT (Lua.pushvalue idx)
|
||||||
Lua.push ("tag" :: Text)
|
Lua.pushName "tag"
|
||||||
Lua.rawget (Lua.nthFromTop 2)
|
Lua.rawget (Lua.nth 2)
|
||||||
Lua.tostring Lua.stackTop <* Lua.pop 2 >>= \case
|
Lua.peekName Lua.top `lastly` Lua.pop 2 -- table/metatable and `tag` field
|
||||||
Nothing -> Lua.throwMessage "untagged value"
|
|
||||||
Just x -> return (UTF8.toString x)
|
|
||||||
|
|
||||||
-- | Modify the message at the top of the stack before throwing it as an
|
pushViaConstr' :: forall e. LuaError e => Name -> [LuaE e ()] -> LuaE e ()
|
||||||
-- Exception.
|
pushViaConstr' fnname pushArgs = do
|
||||||
throwTopMessageAsError' :: (String -> String) -> Lua a
|
pushName @e ("pandoc." <> fnname)
|
||||||
throwTopMessageAsError' modifier = do
|
rawget @e registryindex
|
||||||
msg <- Lua.tostring' Lua.stackTop
|
sequence_ pushArgs
|
||||||
Lua.pop 2 -- remove error and error string pushed by tostring'
|
call @e (fromIntegral (length pushArgs)) 1
|
||||||
Lua.throwMessage (modifier (UTF8.toString msg))
|
|
||||||
|
|
||||||
-- | Mark the context of a Lua computation for better error reporting.
|
|
||||||
defineHowTo :: String -> Lua a -> Lua a
|
|
||||||
defineHowTo ctx op = Lua.errorConversion >>= \ec ->
|
|
||||||
Lua.addContextToException ec ("Could not " <> ctx <> ": ") op
|
|
||||||
|
|
||||||
-- | Like @'Lua.pcall'@, but uses a predefined error handler which adds a
|
-- | Like @'Lua.pcall'@, but uses a predefined error handler which adds a
|
||||||
-- traceback on error.
|
-- traceback on error.
|
||||||
pcallWithTraceback :: NumArgs -> NumResults -> Lua Status
|
pcallWithTraceback :: LuaError e => NumArgs -> NumResults -> LuaE e Status
|
||||||
pcallWithTraceback nargs nresults = do
|
pcallWithTraceback nargs nresults = do
|
||||||
let traceback' :: Lua NumResults
|
let traceback' :: LuaError e => LuaE e NumResults
|
||||||
traceback' = do
|
traceback' = do
|
||||||
l <- Lua.state
|
l <- Lua.state
|
||||||
msg <- Lua.tostring' (Lua.nthFromBottom 1)
|
msg <- Lua.tostring' (Lua.nthBottom 1)
|
||||||
Lua.traceback l (Just (UTF8.toString msg)) 2
|
Lua.traceback l (Just msg) 2
|
||||||
return 1
|
return 1
|
||||||
tracebackIdx <- Lua.absindex (Lua.nthFromTop (Lua.fromNumArgs nargs + 1))
|
tracebackIdx <- Lua.absindex (Lua.nth (Lua.fromNumArgs nargs + 1))
|
||||||
Lua.pushHaskellFunction traceback'
|
Lua.pushHaskellFunction traceback'
|
||||||
Lua.insert tracebackIdx
|
Lua.insert tracebackIdx
|
||||||
result <- Lua.pcall nargs nresults (Just tracebackIdx)
|
result <- Lua.pcall nargs nresults (Just tracebackIdx)
|
||||||
|
@ -129,15 +109,15 @@ pcallWithTraceback nargs nresults = do
|
||||||
return result
|
return result
|
||||||
|
|
||||||
-- | Like @'Lua.call'@, but adds a traceback to the error message (if any).
|
-- | Like @'Lua.call'@, but adds a traceback to the error message (if any).
|
||||||
callWithTraceback :: NumArgs -> NumResults -> Lua ()
|
callWithTraceback :: LuaError e => NumArgs -> NumResults -> LuaE e ()
|
||||||
callWithTraceback nargs nresults = do
|
callWithTraceback nargs nresults = do
|
||||||
result <- pcallWithTraceback nargs nresults
|
result <- pcallWithTraceback nargs nresults
|
||||||
when (result /= Lua.OK)
|
when (result /= Lua.OK)
|
||||||
Lua.throwTopMessage
|
Lua.throwErrorAsException
|
||||||
|
|
||||||
-- | Run the given string as a Lua program, while also adding a traceback to the
|
-- | Run the given string as a Lua program, while also adding a traceback to the
|
||||||
-- error message if an error occurs.
|
-- error message if an error occurs.
|
||||||
dofileWithTraceback :: FilePath -> Lua Status
|
dofileWithTraceback :: LuaError e => FilePath -> LuaE e Status
|
||||||
dofileWithTraceback fp = do
|
dofileWithTraceback fp = do
|
||||||
loadRes <- Lua.loadfile fp
|
loadRes <- Lua.loadfile fp
|
||||||
case loadRes of
|
case loadRes of
|
||||||
|
|
|
@ -1,5 +1,8 @@
|
||||||
|
{-# LANGUAGE FlexibleContexts #-}
|
||||||
{-# LANGUAGE FlexibleInstances #-}
|
{-# LANGUAGE FlexibleInstances #-}
|
||||||
{-# LANGUAGE OverloadedStrings #-}
|
{-# LANGUAGE OverloadedStrings #-}
|
||||||
|
{-# LANGUAGE ScopedTypeVariables #-}
|
||||||
|
{-# LANGUAGE TypeApplications #-}
|
||||||
{- |
|
{- |
|
||||||
Module : Text.Pandoc.Writers.Custom
|
Module : Text.Pandoc.Writers.Custom
|
||||||
Copyright : Copyright (C) 2012-2021 John MacFarlane
|
Copyright : Copyright (C) 2012-2021 John MacFarlane
|
||||||
|
@ -10,7 +13,7 @@
|
||||||
Portability : portable
|
Portability : portable
|
||||||
|
|
||||||
Conversion of 'Pandoc' documents to custom markup using
|
Conversion of 'Pandoc' documents to custom markup using
|
||||||
a lua writer.
|
a Lua writer.
|
||||||
-}
|
-}
|
||||||
module Text.Pandoc.Writers.Custom ( writeCustom ) where
|
module Text.Pandoc.Writers.Custom ( writeCustom ) where
|
||||||
import Control.Arrow ((***))
|
import Control.Arrow ((***))
|
||||||
|
@ -20,7 +23,8 @@ import Data.List (intersperse)
|
||||||
import qualified Data.Map as M
|
import qualified Data.Map as M
|
||||||
import qualified Data.Text as T
|
import qualified Data.Text as T
|
||||||
import Data.Text (Text, pack)
|
import Data.Text (Text, pack)
|
||||||
import Foreign.Lua (Lua, Pushable)
|
import HsLua as Lua hiding (Operation (Div), render)
|
||||||
|
import HsLua.Class.Peekable (PeekError)
|
||||||
import Text.DocLayout (render, literal)
|
import Text.DocLayout (render, literal)
|
||||||
import Control.Monad.IO.Class (MonadIO)
|
import Control.Monad.IO.Class (MonadIO)
|
||||||
import Text.Pandoc.Definition
|
import Text.Pandoc.Definition
|
||||||
|
@ -31,39 +35,39 @@ import Text.Pandoc.Class (PandocMonad)
|
||||||
import Text.Pandoc.Templates (renderTemplate)
|
import Text.Pandoc.Templates (renderTemplate)
|
||||||
import Text.Pandoc.Writers.Shared
|
import Text.Pandoc.Writers.Shared
|
||||||
|
|
||||||
import qualified Foreign.Lua as Lua
|
|
||||||
|
|
||||||
attrToMap :: Attr -> M.Map T.Text T.Text
|
attrToMap :: Attr -> M.Map T.Text T.Text
|
||||||
attrToMap (id',classes,keyvals) = M.fromList
|
attrToMap (id',classes,keyvals) = M.fromList
|
||||||
$ ("id", id')
|
$ ("id", id')
|
||||||
: ("class", T.unwords classes)
|
: ("class", T.unwords classes)
|
||||||
: keyvals
|
: keyvals
|
||||||
|
|
||||||
newtype Stringify a = Stringify a
|
newtype Stringify e a = Stringify a
|
||||||
|
|
||||||
instance Pushable (Stringify Format) where
|
instance Pushable (Stringify e Format) where
|
||||||
push (Stringify (Format f)) = Lua.push (T.toLower f)
|
push (Stringify (Format f)) = Lua.push (T.toLower f)
|
||||||
|
|
||||||
instance Pushable (Stringify [Inline]) where
|
instance PeekError e => Pushable (Stringify e [Inline]) where
|
||||||
push (Stringify ils) = Lua.push =<< inlineListToCustom ils
|
push (Stringify ils) = Lua.push =<<
|
||||||
|
changeErrorType ((inlineListToCustom @e) ils)
|
||||||
|
|
||||||
instance Pushable (Stringify [Block]) where
|
instance PeekError e => Pushable (Stringify e [Block]) where
|
||||||
push (Stringify blks) = Lua.push =<< blockListToCustom blks
|
push (Stringify blks) = Lua.push =<<
|
||||||
|
changeErrorType ((blockListToCustom @e) blks)
|
||||||
|
|
||||||
instance Pushable (Stringify MetaValue) where
|
instance PeekError e => Pushable (Stringify e MetaValue) where
|
||||||
push (Stringify (MetaMap m)) = Lua.push (fmap Stringify m)
|
push (Stringify (MetaMap m)) = Lua.push (fmap (Stringify @e) m)
|
||||||
push (Stringify (MetaList xs)) = Lua.push (map Stringify xs)
|
push (Stringify (MetaList xs)) = Lua.push (map (Stringify @e) xs)
|
||||||
push (Stringify (MetaBool x)) = Lua.push x
|
push (Stringify (MetaBool x)) = Lua.push x
|
||||||
push (Stringify (MetaString s)) = Lua.push s
|
push (Stringify (MetaString s)) = Lua.push s
|
||||||
push (Stringify (MetaInlines ils)) = Lua.push (Stringify ils)
|
push (Stringify (MetaInlines ils)) = Lua.push (Stringify @e ils)
|
||||||
push (Stringify (MetaBlocks bs)) = Lua.push (Stringify bs)
|
push (Stringify (MetaBlocks bs)) = Lua.push (Stringify @e bs)
|
||||||
|
|
||||||
instance Pushable (Stringify Citation) where
|
instance PeekError e => Pushable (Stringify e Citation) where
|
||||||
push (Stringify cit) = do
|
push (Stringify cit) = do
|
||||||
Lua.createtable 6 0
|
Lua.createtable 6 0
|
||||||
addField "citationId" $ citationId cit
|
addField "citationId" $ citationId cit
|
||||||
addField "citationPrefix" . Stringify $ citationPrefix cit
|
addField "citationPrefix" . Stringify @e $ citationPrefix cit
|
||||||
addField "citationSuffix" . Stringify $ citationSuffix cit
|
addField "citationSuffix" . Stringify @e $ citationSuffix cit
|
||||||
addField "citationMode" $ show (citationMode cit)
|
addField "citationMode" $ show (citationMode cit)
|
||||||
addField "citationNoteNum" $ citationNoteNum cit
|
addField "citationNoteNum" $ citationNoteNum cit
|
||||||
addField "citationHash" $ citationHash cit
|
addField "citationHash" $ citationHash cit
|
||||||
|
@ -77,7 +81,7 @@ instance (Pushable a, Pushable b) => Pushable (KeyValue a b) where
|
||||||
Lua.newtable
|
Lua.newtable
|
||||||
Lua.push k
|
Lua.push k
|
||||||
Lua.push v
|
Lua.push v
|
||||||
Lua.rawset (Lua.nthFromTop 3)
|
Lua.rawset (Lua.nth 3)
|
||||||
|
|
||||||
-- | Convert Pandoc to custom markup.
|
-- | Convert Pandoc to custom markup.
|
||||||
writeCustom :: (PandocMonad m, MonadIO m)
|
writeCustom :: (PandocMonad m, MonadIO m)
|
||||||
|
@ -92,7 +96,7 @@ writeCustom luaFile opts doc@(Pandoc meta _) = do
|
||||||
-- check for error in lua script (later we'll change the return type
|
-- check for error in lua script (later we'll change the return type
|
||||||
-- to handle this more gracefully):
|
-- to handle this more gracefully):
|
||||||
when (stat /= Lua.OK)
|
when (stat /= Lua.OK)
|
||||||
Lua.throwTopMessage
|
Lua.throwErrorAsException
|
||||||
rendered <- docToCustom opts doc
|
rendered <- docToCustom opts doc
|
||||||
context <- metaToContext opts
|
context <- metaToContext opts
|
||||||
(fmap (literal . pack) . blockListToCustom)
|
(fmap (literal . pack) . blockListToCustom)
|
||||||
|
@ -107,126 +111,132 @@ writeCustom luaFile opts doc@(Pandoc meta _) = do
|
||||||
Just tpl -> render Nothing $
|
Just tpl -> render Nothing $
|
||||||
renderTemplate tpl $ setField "body" body context
|
renderTemplate tpl $ setField "body" body context
|
||||||
|
|
||||||
docToCustom :: WriterOptions -> Pandoc -> Lua String
|
docToCustom :: forall e. PeekError e
|
||||||
|
=> WriterOptions -> Pandoc -> LuaE e String
|
||||||
docToCustom opts (Pandoc (Meta metamap) blocks) = do
|
docToCustom opts (Pandoc (Meta metamap) blocks) = do
|
||||||
body <- blockListToCustom blocks
|
body <- blockListToCustom blocks
|
||||||
Lua.callFunc "Doc" body (fmap Stringify metamap) (writerVariables opts)
|
invoke @e "Doc" body (fmap (Stringify @e) metamap) (writerVariables opts)
|
||||||
|
|
||||||
-- | Convert Pandoc block element to Custom.
|
-- | Convert Pandoc block element to Custom.
|
||||||
blockToCustom :: Block -- ^ Block element
|
blockToCustom :: forall e. PeekError e
|
||||||
-> Lua String
|
=> Block -- ^ Block element
|
||||||
|
-> LuaE e String
|
||||||
|
|
||||||
blockToCustom Null = return ""
|
blockToCustom Null = return ""
|
||||||
|
|
||||||
blockToCustom (Plain inlines) = Lua.callFunc "Plain" (Stringify inlines)
|
blockToCustom (Plain inlines) = invoke @e "Plain" (Stringify @e inlines)
|
||||||
|
|
||||||
blockToCustom (Para [Image attr txt (src,tit)]) =
|
blockToCustom (Para [Image attr txt (src,tit)]) =
|
||||||
Lua.callFunc "CaptionedImage" src tit (Stringify txt) (attrToMap attr)
|
invoke @e "CaptionedImage" src tit (Stringify @e txt) (attrToMap attr)
|
||||||
|
|
||||||
blockToCustom (Para inlines) = Lua.callFunc "Para" (Stringify inlines)
|
blockToCustom (Para inlines) = invoke @e "Para" (Stringify @e inlines)
|
||||||
|
|
||||||
blockToCustom (LineBlock linesList) =
|
blockToCustom (LineBlock linesList) =
|
||||||
Lua.callFunc "LineBlock" (map Stringify linesList)
|
invoke @e "LineBlock" (map (Stringify @e) linesList)
|
||||||
|
|
||||||
blockToCustom (RawBlock format str) =
|
blockToCustom (RawBlock format str) =
|
||||||
Lua.callFunc "RawBlock" (Stringify format) str
|
invoke @e "RawBlock" (Stringify @e format) str
|
||||||
|
|
||||||
blockToCustom HorizontalRule = Lua.callFunc "HorizontalRule"
|
blockToCustom HorizontalRule = invoke @e "HorizontalRule"
|
||||||
|
|
||||||
blockToCustom (Header level attr inlines) =
|
blockToCustom (Header level attr inlines) =
|
||||||
Lua.callFunc "Header" level (Stringify inlines) (attrToMap attr)
|
invoke @e "Header" level (Stringify @e inlines) (attrToMap attr)
|
||||||
|
|
||||||
blockToCustom (CodeBlock attr str) =
|
blockToCustom (CodeBlock attr str) =
|
||||||
Lua.callFunc "CodeBlock" str (attrToMap attr)
|
invoke @e "CodeBlock" str (attrToMap attr)
|
||||||
|
|
||||||
blockToCustom (BlockQuote blocks) =
|
blockToCustom (BlockQuote blocks) =
|
||||||
Lua.callFunc "BlockQuote" (Stringify blocks)
|
invoke @e "BlockQuote" (Stringify @e blocks)
|
||||||
|
|
||||||
blockToCustom (Table _ blkCapt specs thead tbody tfoot) =
|
blockToCustom (Table _ blkCapt specs thead tbody tfoot) =
|
||||||
let (capt, aligns, widths, headers, rows) = toLegacyTable blkCapt specs thead tbody tfoot
|
let (capt, aligns, widths, headers, rows) = toLegacyTable blkCapt specs thead tbody tfoot
|
||||||
aligns' = map show aligns
|
aligns' = map show aligns
|
||||||
capt' = Stringify capt
|
capt' = Stringify @e capt
|
||||||
headers' = map Stringify headers
|
headers' = map (Stringify @e) headers
|
||||||
rows' = map (map Stringify) rows
|
rows' = map (map (Stringify @e)) rows
|
||||||
in Lua.callFunc "Table" capt' aligns' widths headers' rows'
|
in invoke @e "Table" capt' aligns' widths headers' rows'
|
||||||
|
|
||||||
blockToCustom (BulletList items) =
|
blockToCustom (BulletList items) =
|
||||||
Lua.callFunc "BulletList" (map Stringify items)
|
invoke @e "BulletList" (map (Stringify @e) items)
|
||||||
|
|
||||||
blockToCustom (OrderedList (num,sty,delim) items) =
|
blockToCustom (OrderedList (num,sty,delim) items) =
|
||||||
Lua.callFunc "OrderedList" (map Stringify items) num (show sty) (show delim)
|
invoke @e "OrderedList" (map (Stringify @e) items) num (show sty) (show delim)
|
||||||
|
|
||||||
blockToCustom (DefinitionList items) =
|
blockToCustom (DefinitionList items) =
|
||||||
Lua.callFunc "DefinitionList"
|
invoke @e "DefinitionList"
|
||||||
(map (KeyValue . (Stringify *** map Stringify)) items)
|
(map (KeyValue . (Stringify @e *** map (Stringify @e))) items)
|
||||||
|
|
||||||
blockToCustom (Div attr items) =
|
blockToCustom (Div attr items) =
|
||||||
Lua.callFunc "Div" (Stringify items) (attrToMap attr)
|
invoke @e "Div" (Stringify @e items) (attrToMap attr)
|
||||||
|
|
||||||
-- | Convert list of Pandoc block elements to Custom.
|
-- | Convert list of Pandoc block elements to Custom.
|
||||||
blockListToCustom :: [Block] -- ^ List of block elements
|
blockListToCustom :: forall e. PeekError e
|
||||||
-> Lua String
|
=> [Block] -- ^ List of block elements
|
||||||
|
-> LuaE e String
|
||||||
blockListToCustom xs = do
|
blockListToCustom xs = do
|
||||||
blocksep <- Lua.callFunc "Blocksep"
|
blocksep <- invoke @e "Blocksep"
|
||||||
bs <- mapM blockToCustom xs
|
bs <- mapM blockToCustom xs
|
||||||
return $ mconcat $ intersperse blocksep bs
|
return $ mconcat $ intersperse blocksep bs
|
||||||
|
|
||||||
-- | Convert list of Pandoc inline elements to Custom.
|
-- | Convert list of Pandoc inline elements to Custom.
|
||||||
inlineListToCustom :: [Inline] -> Lua String
|
inlineListToCustom :: forall e. PeekError e => [Inline] -> LuaE e String
|
||||||
inlineListToCustom lst = do
|
inlineListToCustom lst = do
|
||||||
xs <- mapM inlineToCustom lst
|
xs <- mapM (inlineToCustom @e) lst
|
||||||
return $ mconcat xs
|
return $ mconcat xs
|
||||||
|
|
||||||
-- | Convert Pandoc inline element to Custom.
|
-- | Convert Pandoc inline element to Custom.
|
||||||
inlineToCustom :: Inline -> Lua String
|
inlineToCustom :: forall e. PeekError e => Inline -> LuaE e String
|
||||||
|
|
||||||
inlineToCustom (Str str) = Lua.callFunc "Str" str
|
inlineToCustom (Str str) = invoke @e "Str" str
|
||||||
|
|
||||||
inlineToCustom Space = Lua.callFunc "Space"
|
inlineToCustom Space = invoke @e "Space"
|
||||||
|
|
||||||
inlineToCustom SoftBreak = Lua.callFunc "SoftBreak"
|
inlineToCustom SoftBreak = invoke @e "SoftBreak"
|
||||||
|
|
||||||
inlineToCustom (Emph lst) = Lua.callFunc "Emph" (Stringify lst)
|
inlineToCustom (Emph lst) = invoke @e "Emph" (Stringify @e lst)
|
||||||
|
|
||||||
inlineToCustom (Underline lst) = Lua.callFunc "Underline" (Stringify lst)
|
inlineToCustom (Underline lst) = invoke @e "Underline" (Stringify @e lst)
|
||||||
|
|
||||||
inlineToCustom (Strong lst) = Lua.callFunc "Strong" (Stringify lst)
|
inlineToCustom (Strong lst) = invoke @e "Strong" (Stringify @e lst)
|
||||||
|
|
||||||
inlineToCustom (Strikeout lst) = Lua.callFunc "Strikeout" (Stringify lst)
|
inlineToCustom (Strikeout lst) = invoke @e "Strikeout" (Stringify @e lst)
|
||||||
|
|
||||||
inlineToCustom (Superscript lst) = Lua.callFunc "Superscript" (Stringify lst)
|
inlineToCustom (Superscript lst) = invoke @e "Superscript" (Stringify @e lst)
|
||||||
|
|
||||||
inlineToCustom (Subscript lst) = Lua.callFunc "Subscript" (Stringify lst)
|
inlineToCustom (Subscript lst) = invoke @e "Subscript" (Stringify @e lst)
|
||||||
|
|
||||||
inlineToCustom (SmallCaps lst) = Lua.callFunc "SmallCaps" (Stringify lst)
|
inlineToCustom (SmallCaps lst) = invoke @e "SmallCaps" (Stringify @e lst)
|
||||||
|
|
||||||
inlineToCustom (Quoted SingleQuote lst) = Lua.callFunc "SingleQuoted" (Stringify lst)
|
inlineToCustom (Quoted SingleQuote lst) =
|
||||||
|
invoke @e "SingleQuoted" (Stringify @e lst)
|
||||||
|
|
||||||
inlineToCustom (Quoted DoubleQuote lst) = Lua.callFunc "DoubleQuoted" (Stringify lst)
|
inlineToCustom (Quoted DoubleQuote lst) =
|
||||||
|
invoke @e "DoubleQuoted" (Stringify @e lst)
|
||||||
|
|
||||||
inlineToCustom (Cite cs lst) = Lua.callFunc "Cite" (Stringify lst) (map Stringify cs)
|
inlineToCustom (Cite cs lst) =
|
||||||
|
invoke @e "Cite" (Stringify @e lst) (map (Stringify @e) cs)
|
||||||
|
|
||||||
inlineToCustom (Code attr str) =
|
inlineToCustom (Code attr str) =
|
||||||
Lua.callFunc "Code" str (attrToMap attr)
|
invoke @e "Code" str (attrToMap attr)
|
||||||
|
|
||||||
inlineToCustom (Math DisplayMath str) =
|
inlineToCustom (Math DisplayMath str) =
|
||||||
Lua.callFunc "DisplayMath" str
|
invoke @e "DisplayMath" str
|
||||||
|
|
||||||
inlineToCustom (Math InlineMath str) =
|
inlineToCustom (Math InlineMath str) =
|
||||||
Lua.callFunc "InlineMath" str
|
invoke @e "InlineMath" str
|
||||||
|
|
||||||
inlineToCustom (RawInline format str) =
|
inlineToCustom (RawInline format str) =
|
||||||
Lua.callFunc "RawInline" (Stringify format) str
|
invoke @e "RawInline" (Stringify @e format) str
|
||||||
|
|
||||||
inlineToCustom LineBreak = Lua.callFunc "LineBreak"
|
inlineToCustom LineBreak = invoke @e "LineBreak"
|
||||||
|
|
||||||
inlineToCustom (Link attr txt (src,tit)) =
|
inlineToCustom (Link attr txt (src,tit)) =
|
||||||
Lua.callFunc "Link" (Stringify txt) src tit (attrToMap attr)
|
invoke @e "Link" (Stringify @e txt) src tit (attrToMap attr)
|
||||||
|
|
||||||
inlineToCustom (Image attr alt (src,tit)) =
|
inlineToCustom (Image attr alt (src,tit)) =
|
||||||
Lua.callFunc "Image" (Stringify alt) src tit (attrToMap attr)
|
invoke @e "Image" (Stringify @e alt) src tit (attrToMap attr)
|
||||||
|
|
||||||
inlineToCustom (Note contents) = Lua.callFunc "Note" (Stringify contents)
|
inlineToCustom (Note contents) = invoke @e "Note" (Stringify @e contents)
|
||||||
|
|
||||||
inlineToCustom (Span attr items) =
|
inlineToCustom (Span attr items) =
|
||||||
Lua.callFunc "Span" (Stringify items) (attrToMap attr)
|
invoke @e "Span" (Stringify @e items) (attrToMap attr)
|
||||||
|
|
15
stack.yaml
15
stack.yaml
|
@ -12,6 +12,19 @@ extra-deps:
|
||||||
- doctemplates-0.10
|
- doctemplates-0.10
|
||||||
- emojis-0.1.2
|
- emojis-0.1.2
|
||||||
- doclayout-0.3.1.1
|
- doclayout-0.3.1.1
|
||||||
|
- hslua-2.0.0
|
||||||
|
- hslua-classes-2.0.0
|
||||||
|
- hslua-core-2.0.0
|
||||||
|
- hslua-marshalling-2.0.0
|
||||||
|
- hslua-module-path-1.0.0
|
||||||
|
- hslua-module-system-1.0.0
|
||||||
|
- hslua-module-text-1.0.0
|
||||||
|
- hslua-module-version-1.0.0
|
||||||
|
- hslua-objectorientation-2.0.0
|
||||||
|
- hslua-packaging-2.0.0
|
||||||
|
- lua-2.0.0
|
||||||
|
- tasty-hslua-1.0.0
|
||||||
|
- tasty-lua-1.0.0
|
||||||
- git: https://github.com/jgm/pandoc-types.git
|
- git: https://github.com/jgm/pandoc-types.git
|
||||||
commit: 99402a46361a3e52805935b1fbe9dfe54f852d6a
|
commit: 99402a46361a3e52805935b1fbe9dfe54f852d6a
|
||||||
- git: https://github.com/jgm/texmath.git
|
- git: https://github.com/jgm/texmath.git
|
||||||
|
@ -26,5 +39,3 @@ ghc-options:
|
||||||
resolver: lts-18.10
|
resolver: lts-18.10
|
||||||
nix:
|
nix:
|
||||||
packages: [zlib]
|
packages: [zlib]
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
{-# LANGUAGE OverloadedStrings #-}
|
{-# LANGUAGE OverloadedStrings #-}
|
||||||
{-# LANGUAGE ScopedTypeVariables #-}
|
{-# LANGUAGE ScopedTypeVariables #-}
|
||||||
|
{-# LANGUAGE TypeApplications #-}
|
||||||
{- |
|
{- |
|
||||||
Module : Tests.Lua
|
Module : Tests.Lua
|
||||||
Copyright : © 2017-2021 Albert Krewinkel
|
Copyright : © 2017-2021 Albert Krewinkel
|
||||||
|
@ -14,9 +15,10 @@ Unit and integration tests for pandoc's Lua subsystem.
|
||||||
module Tests.Lua ( runLuaTest, tests ) where
|
module Tests.Lua ( runLuaTest, tests ) where
|
||||||
|
|
||||||
import Control.Monad (when)
|
import Control.Monad (when)
|
||||||
|
import HsLua as Lua hiding (Operation (Div), error)
|
||||||
import System.FilePath ((</>))
|
import System.FilePath ((</>))
|
||||||
import Test.Tasty (TestTree, localOption)
|
import Test.Tasty (TestTree, localOption)
|
||||||
import Test.Tasty.HUnit (Assertion, assertEqual, testCase)
|
import Test.Tasty.HUnit (Assertion, HasCallStack, assertEqual, testCase)
|
||||||
import Test.Tasty.QuickCheck (QuickCheckTests (..), ioProperty, testProperty)
|
import Test.Tasty.QuickCheck (QuickCheckTests (..), ioProperty, testProperty)
|
||||||
import Text.Pandoc.Arbitrary ()
|
import Text.Pandoc.Arbitrary ()
|
||||||
import Text.Pandoc.Builder (bulletList, definitionList, displayMath, divWith,
|
import Text.Pandoc.Builder (bulletList, definitionList, displayMath, divWith,
|
||||||
|
@ -25,8 +27,8 @@ import Text.Pandoc.Builder (bulletList, definitionList, displayMath, divWith,
|
||||||
singleQuoted, space, str, strong,
|
singleQuoted, space, str, strong,
|
||||||
HasMeta (setMeta))
|
HasMeta (setMeta))
|
||||||
import Text.Pandoc.Class (runIOorExplode, setUserDataDir)
|
import Text.Pandoc.Class (runIOorExplode, setUserDataDir)
|
||||||
import Text.Pandoc.Definition (Block (BlockQuote, Div, Para), Inline (Emph, Str),
|
import Text.Pandoc.Definition (Attr, Block (BlockQuote, Div, Para), Pandoc,
|
||||||
Attr, Meta, Pandoc, pandocTypesVersion)
|
Inline (Emph, Str), Meta, pandocTypesVersion)
|
||||||
import Text.Pandoc.Error (PandocError (PandocLuaError))
|
import Text.Pandoc.Error (PandocError (PandocLuaError))
|
||||||
import Text.Pandoc.Filter (Filter (LuaFilter), applyFilters)
|
import Text.Pandoc.Filter (Filter (LuaFilter), applyFilters)
|
||||||
import Text.Pandoc.Lua (runLua)
|
import Text.Pandoc.Lua (runLua)
|
||||||
|
@ -34,23 +36,22 @@ import Text.Pandoc.Options (def)
|
||||||
import Text.Pandoc.Shared (pandocVersion)
|
import Text.Pandoc.Shared (pandocVersion)
|
||||||
|
|
||||||
import qualified Control.Monad.Catch as Catch
|
import qualified Control.Monad.Catch as Catch
|
||||||
import qualified Foreign.Lua as Lua
|
|
||||||
import qualified Data.Text as T
|
import qualified Data.Text as T
|
||||||
import qualified Data.Text.Encoding as TE
|
import qualified Data.Text.Encoding as TE
|
||||||
|
|
||||||
tests :: [TestTree]
|
tests :: [TestTree]
|
||||||
tests = map (localOption (QuickCheckTests 20))
|
tests = map (localOption (QuickCheckTests 20))
|
||||||
[ testProperty "inline elements can be round-tripped through the lua stack" $
|
[ testProperty "inline elements can be round-tripped through the lua stack" $
|
||||||
\x -> ioProperty (roundtripEqual (x::Inline))
|
ioProperty . roundtripEqual @Inline
|
||||||
|
|
||||||
, testProperty "block elements can be round-tripped through the lua stack" $
|
, testProperty "block elements can be round-tripped through the lua stack" $
|
||||||
\x -> ioProperty (roundtripEqual (x::Block))
|
ioProperty . roundtripEqual @Block
|
||||||
|
|
||||||
, testProperty "meta blocks can be round-tripped through the lua stack" $
|
, testProperty "meta blocks can be round-tripped through the lua stack" $
|
||||||
\x -> ioProperty (roundtripEqual (x::Meta))
|
ioProperty . roundtripEqual @Meta
|
||||||
|
|
||||||
, testProperty "documents can be round-tripped through the lua stack" $
|
, testProperty "documents can be round-tripped through the lua stack" $
|
||||||
\x -> ioProperty (roundtripEqual (x::Pandoc))
|
ioProperty . roundtripEqual @Pandoc
|
||||||
|
|
||||||
, testCase "macro expansion via filter" $
|
, testCase "macro expansion via filter" $
|
||||||
assertFilterConversion "a '{{helloworld}}' string is expanded"
|
assertFilterConversion "a '{{helloworld}}' string is expanded"
|
||||||
|
@ -163,12 +164,12 @@ tests = map (localOption (QuickCheckTests 20))
|
||||||
Lua.getglobal "PANDOC_VERSION"
|
Lua.getglobal "PANDOC_VERSION"
|
||||||
Lua.liftIO .
|
Lua.liftIO .
|
||||||
assertEqual "pandoc version is wrong" (TE.encodeUtf8 pandocVersion)
|
assertEqual "pandoc version is wrong" (TE.encodeUtf8 pandocVersion)
|
||||||
=<< Lua.tostring' Lua.stackTop
|
=<< Lua.tostring' Lua.top
|
||||||
|
|
||||||
, testCase "Pandoc types version is set" . runLuaTest $ do
|
, testCase "Pandoc types version is set" . runLuaTest $ do
|
||||||
Lua.getglobal "PANDOC_API_VERSION"
|
Lua.getglobal "PANDOC_API_VERSION"
|
||||||
Lua.liftIO . assertEqual "pandoc-types version is wrong" pandocTypesVersion
|
Lua.liftIO . assertEqual "pandoc-types version is wrong" pandocTypesVersion
|
||||||
=<< Lua.peek Lua.stackTop
|
=<< Lua.peek Lua.top
|
||||||
|
|
||||||
, testCase "require file" $
|
, testCase "require file" $
|
||||||
assertFilterConversion "requiring file failed"
|
assertFilterConversion "requiring file failed"
|
||||||
|
@ -177,38 +178,47 @@ tests = map (localOption (QuickCheckTests 20))
|
||||||
(doc $ para (str . T.pack $ "lua" </> "require-file.lua"))
|
(doc $ para (str . T.pack $ "lua" </> "require-file.lua"))
|
||||||
|
|
||||||
, testCase "Allow singleton inline in constructors" . runLuaTest $ do
|
, testCase "Allow singleton inline in constructors" . runLuaTest $ do
|
||||||
Lua.liftIO . assertEqual "Not the expected Emph" (Emph [Str "test"])
|
Lua.liftIO . assertEqual "Not the expected Emph"
|
||||||
=<< Lua.callFunc "pandoc.Emph" (Str "test")
|
(Emph [Str "test"]) =<< do
|
||||||
Lua.liftIO . assertEqual "Unexpected element" (Para [Str "test"])
|
Lua.OK <- Lua.dostring "return pandoc.Emph"
|
||||||
=<< Lua.callFunc "pandoc.Para" ("test" :: String)
|
Lua.push @Inline (Str "test")
|
||||||
|
Lua.call 1 1
|
||||||
|
Lua.peek @Inline top
|
||||||
|
Lua.liftIO . assertEqual "Unexpected element"
|
||||||
|
(Para [Str "test"]) =<< do
|
||||||
|
Lua.getglobal' "pandoc.Para"
|
||||||
|
Lua.pushString "test"
|
||||||
|
Lua.call 1 1
|
||||||
|
Lua.peek @Block top
|
||||||
Lua.liftIO . assertEqual "Unexptected element"
|
Lua.liftIO . assertEqual "Unexptected element"
|
||||||
(BlockQuote [Para [Str "foo"]]) =<< (
|
(BlockQuote [Para [Str "foo"]]) =<< (
|
||||||
do
|
do
|
||||||
Lua.getglobal' "pandoc.BlockQuote"
|
Lua.getglobal' "pandoc.BlockQuote"
|
||||||
Lua.push (Para [Str "foo"])
|
Lua.push (Para [Str "foo"])
|
||||||
_ <- Lua.call 1 1
|
_ <- Lua.call 1 1
|
||||||
Lua.peek Lua.stackTop
|
Lua.peek @Block Lua.top
|
||||||
)
|
)
|
||||||
|
|
||||||
, testCase "Elements with Attr have `attr` accessor" . runLuaTest $ do
|
, testCase "Elements with Attr have `attr` accessor" . runLuaTest $ do
|
||||||
Lua.push (Div ("hi", ["moin"], [])
|
Lua.push (Div ("hi", ["moin"], [])
|
||||||
[Para [Str "ignored"]])
|
[Para [Str "ignored"]])
|
||||||
Lua.getfield Lua.stackTop "attr"
|
Lua.getfield Lua.top "attr"
|
||||||
Lua.liftIO . assertEqual "no accessor" (("hi", ["moin"], []) :: Attr)
|
Lua.liftIO . assertEqual "no accessor" (("hi", ["moin"], []) :: Attr)
|
||||||
=<< Lua.peek Lua.stackTop
|
=<< Lua.peek Lua.top
|
||||||
|
|
||||||
, testCase "module `pandoc.system` is present" . runLuaTest $ do
|
, testCase "module `pandoc.system` is present" . runLuaTest $ do
|
||||||
Lua.getglobal' "pandoc.system"
|
Lua.getglobal' "pandoc.system"
|
||||||
ty <- Lua.ltype Lua.stackTop
|
ty <- Lua.ltype Lua.top
|
||||||
Lua.liftIO $ assertEqual "module should be a table" Lua.TypeTable ty
|
Lua.liftIO $ assertEqual "module should be a table" Lua.TypeTable ty
|
||||||
|
|
||||||
, testCase "informative error messages" . runLuaTest $ do
|
, testCase "informative error messages" . runLuaTest $ do
|
||||||
Lua.pushboolean True
|
Lua.pushboolean True
|
||||||
eitherPandoc <- Catch.try (Lua.peek Lua.stackTop :: Lua.Lua Pandoc)
|
-- Lua.newtable
|
||||||
|
eitherPandoc <- Catch.try (peek @Pandoc Lua.top)
|
||||||
case eitherPandoc of
|
case eitherPandoc of
|
||||||
Left (PandocLuaError msg) -> do
|
Left (PandocLuaError msg) -> do
|
||||||
let expectedMsg = "Could not get Pandoc value: "
|
let expectedMsg = "table expected, got boolean\n"
|
||||||
<> "table expected, got boolean"
|
<> "\twhile retrieving Pandoc value"
|
||||||
Lua.liftIO $ assertEqual "unexpected error message" expectedMsg msg
|
Lua.liftIO $ assertEqual "unexpected error message" expectedMsg msg
|
||||||
Left e -> error ("Expected a Lua error, but got " <> show e)
|
Left e -> error ("Expected a Lua error, but got " <> show e)
|
||||||
Right _ -> error "Getting a Pandoc element from a bool should fail."
|
Right _ -> error "Getting a Pandoc element from a bool should fail."
|
||||||
|
@ -221,19 +231,20 @@ assertFilterConversion msg filterPath docIn expectedDoc = do
|
||||||
applyFilters def [LuaFilter ("lua" </> filterPath)] ["HTML"] docIn
|
applyFilters def [LuaFilter ("lua" </> filterPath)] ["HTML"] docIn
|
||||||
assertEqual msg expectedDoc actualDoc
|
assertEqual msg expectedDoc actualDoc
|
||||||
|
|
||||||
roundtripEqual :: (Eq a, Lua.Peekable a, Lua.Pushable a) => a -> IO Bool
|
roundtripEqual :: forall a. (Eq a, Lua.Pushable a, Lua.Peekable a)
|
||||||
|
=> a -> IO Bool
|
||||||
roundtripEqual x = (x ==) <$> roundtripped
|
roundtripEqual x = (x ==) <$> roundtripped
|
||||||
where
|
where
|
||||||
roundtripped :: Lua.Peekable a => IO a
|
roundtripped :: IO a
|
||||||
roundtripped = runLuaTest $ do
|
roundtripped = runLuaTest $ do
|
||||||
oldSize <- Lua.gettop
|
oldSize <- Lua.gettop
|
||||||
Lua.push x
|
Lua.push x
|
||||||
size <- Lua.gettop
|
size <- Lua.gettop
|
||||||
when (size - oldSize /= 1) $
|
when (size - oldSize /= 1) $
|
||||||
error ("not exactly one additional element on the stack: " ++ show size)
|
error ("not exactly one additional element on the stack: " ++ show size)
|
||||||
Lua.peek (-1)
|
Lua.peek Lua.top
|
||||||
|
|
||||||
runLuaTest :: Lua.Lua a -> IO a
|
runLuaTest :: HasCallStack => Lua.LuaE PandocError a -> IO a
|
||||||
runLuaTest op = runIOorExplode $ do
|
runLuaTest op = runIOorExplode $ do
|
||||||
setUserDataDir (Just "../data")
|
setUserDataDir (Just "../data")
|
||||||
res <- runLua op
|
res <- runLua op
|
||||||
|
|
|
@ -55,31 +55,6 @@ return {
|
||||||
end),
|
end),
|
||||||
},
|
},
|
||||||
|
|
||||||
group 'list-like behavior' {
|
|
||||||
test('can access version component numbers', function ()
|
|
||||||
local version = Version '2.7.3'
|
|
||||||
assert.is_nil(version[0])
|
|
||||||
assert.are_equal(version[1], 2)
|
|
||||||
assert.are_equal(version[2], 7)
|
|
||||||
assert.are_equal(version[3], 3)
|
|
||||||
end),
|
|
||||||
test('can be iterated over', function ()
|
|
||||||
local version_list = {2, 7, 3}
|
|
||||||
local final_index = 0
|
|
||||||
for i, v in pairs(Version(version_list)) do
|
|
||||||
assert.are_equal(v, version_list[i])
|
|
||||||
final_index = i
|
|
||||||
end
|
|
||||||
assert.are_equal(final_index, 3)
|
|
||||||
end),
|
|
||||||
test('length is the number of components', function ()
|
|
||||||
assert.are_equal(#(Version '0'), 1)
|
|
||||||
assert.are_equal(#(Version '1.6'), 2)
|
|
||||||
assert.are_equal(#(Version '8.7.5'), 3)
|
|
||||||
assert.are_equal(#(Version '2.9.1.5'), 4)
|
|
||||||
end)
|
|
||||||
},
|
|
||||||
|
|
||||||
group 'conversion to string' {
|
group 'conversion to string' {
|
||||||
test('converting from and to string is a noop', function ()
|
test('converting from and to string is a noop', function ()
|
||||||
local version_string = '1.19.4'
|
local version_string = '1.19.4'
|
||||||
|
|
Loading…
Reference in a new issue