From f2f6851713674545e2f303b95589cbaff8e6a6b9 Mon Sep 17 00:00:00 2001 From: Albert Krewinkel Date: Mon, 20 Mar 2017 15:17:03 +0100 Subject: Lua filters (#3514) * Add `--lua-filter` option. This works like `--filter` but takes pathnames of special lua filters and uses the lua interpreter baked into pandoc, so that no external interpreter is needed. Note that lua filters are all applied after regular filters, regardless of their position on the command line. * Add Text.Pandoc.Lua, exporting `runLuaFilter`. Add `pandoc.lua` to data files. * Add private module Text.Pandoc.Lua.PandocModule to supply the default lua module. * Add Tests.Lua to tests. * Add data/pandoc.lua, the lua module pandoc imports when processing its lua filters. * Document in MANUAL.txt. --- src/Text/Pandoc/Lua.hs | 226 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 226 insertions(+) create mode 100644 src/Text/Pandoc/Lua.hs (limited to 'src/Text/Pandoc/Lua.hs') diff --git a/src/Text/Pandoc/Lua.hs b/src/Text/Pandoc/Lua.hs new file mode 100644 index 000000000..6fa6b2020 --- /dev/null +++ b/src/Text/Pandoc/Lua.hs @@ -0,0 +1,226 @@ +{- +Copyright © 2017 Albert Krewinkel + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +-} +{-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE LambdaCase #-} +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE ScopedTypeVariables #-} +{-# OPTIONS_GHC -fno-warn-orphans #-} +{- | + Module : Text.Pandoc.Lua + Copyright : Copyright © 2017 Albert Krewinkel + License : GNU GPL, version 2 or above + + Maintainer : Albert Krewinkel + Stability : alpha + +Pandoc lua utils. +-} +module Text.Pandoc.Lua ( runLuaFilter ) where + +import Control.Monad ( (>=>), when ) +import Control.Monad.Trans ( MonadIO(..) ) +import Data.Aeson ( FromJSON(..), ToJSON(..), Result(..), Value, fromJSON ) +import Data.HashMap.Lazy ( HashMap ) +import Data.Text ( Text, pack, unpack ) +import Data.Text.Encoding ( decodeUtf8 ) +import Scripting.Lua ( LuaState, StackValue(..) ) +import Scripting.Lua.Aeson () +import Text.Pandoc.Definition ( Block(..), Inline(..), Pandoc(..) ) +import Text.Pandoc.Lua.PandocModule +import Text.Pandoc.Walk + +import qualified Data.HashMap.Lazy as HashMap +import qualified Scripting.Lua as Lua +import qualified Scripting.Lua as LuaAeson + +runLuaFilter :: (MonadIO m) + => FilePath -> [String] -> Pandoc -> m Pandoc +runLuaFilter filterPath args pd = liftIO $ do + lua <- LuaAeson.newstate + Lua.openlibs lua + Lua.newtable lua + Lua.setglobal lua "PANDOC_FILTER_FUNCTIONS" -- hack, store functions here + pushPandocModule lua + Lua.setglobal lua "pandoc" + status <- Lua.loadfile lua filterPath + if (status /= 0) + then do + luaErrMsg <- unpack . decodeUtf8 <$> Lua.tostring lua 1 + error luaErrMsg + else do + Lua.call lua 0 1 + Just luaFilters <- Lua.peek lua (-1) + Lua.push lua (map pack args) + Lua.setglobal lua "PandocParameters" + doc <- runAll luaFilters >=> luaFilter lua "filter_doc" $ pd + Lua.close lua + return doc + +runAll :: [LuaFilter] -> Pandoc -> IO Pandoc +runAll [] = return +runAll (x:xs) = walkMWithLuaFilter x >=> runAll xs + +luaFilter :: Lua.LuaState -> String -> Pandoc -> IO Pandoc +luaFilter lua luaFn x = do + fnExists <- isLuaFunction lua luaFn + if fnExists + then walkM (Lua.callfunc lua luaFn :: Pandoc -> IO Pandoc) x + else return x + +walkMWithLuaFilter :: LuaFilter -> Pandoc -> IO Pandoc +walkMWithLuaFilter (LuaFilter lua inlineFnMap blockFnMap docFnMap) = + walkM (execInlineLuaFilter lua inlineFnMap) >=> + walkM (execBlockLuaFilter lua blockFnMap) >=> + walkM (execDocLuaFilter lua docFnMap) + +type InlineFunctionMap = HashMap Text (LuaFilterFunction Inline) +type BlockFunctionMap = HashMap Text (LuaFilterFunction Block) +type DocFunctionMap = HashMap Text (LuaFilterFunction Pandoc) +data LuaFilter = + LuaFilter LuaState InlineFunctionMap BlockFunctionMap DocFunctionMap + +newtype LuaFilterFunction a = LuaFilterFunction { functionIndex :: Int } + +execDocLuaFilter :: LuaState + -> HashMap Text (LuaFilterFunction Pandoc) + -> Pandoc -> IO Pandoc +execDocLuaFilter lua fnMap x = do + let docFnName = "Doc" + case HashMap.lookup docFnName fnMap of + Nothing -> return x + Just fn -> runLuaFilterFunction lua fn x + +execBlockLuaFilter :: LuaState + -> HashMap Text (LuaFilterFunction Block) + -> Block -> IO Block +execBlockLuaFilter lua fnMap x = do + let filterOrId constr = case HashMap.lookup constr fnMap of + Nothing -> return x + Just fn -> runLuaFilterFunction lua fn x + case x of + Plain _ -> filterOrId "Plain" + Para _ -> filterOrId "Para" + LineBlock _ -> filterOrId "LineBlock" + CodeBlock _ _ -> filterOrId "CodeBlock" + RawBlock _ _ -> filterOrId "RawBlock" + BlockQuote _ -> filterOrId "BlockQuote" + OrderedList _ _ -> filterOrId "OrderedList" + BulletList _ -> filterOrId "BulletList" + DefinitionList _ -> filterOrId "DefinitionList" + Header _ _ _ -> filterOrId "Header" + HorizontalRule -> filterOrId "HorizontalRule" + Table _ _ _ _ _ -> filterOrId "Table" + Div _ _ -> filterOrId "Div" + Null -> filterOrId "Null" + +execInlineLuaFilter :: LuaState + -> HashMap Text (LuaFilterFunction Inline) + -> Inline -> IO Inline +execInlineLuaFilter lua fnMap x = do + let filterOrId constr = case HashMap.lookup constr fnMap of + Nothing -> return x + Just fn -> runLuaFilterFunction lua fn x + case x of + Cite _ _ -> filterOrId "Cite" + Code _ _ -> filterOrId "Code" + Emph _ -> filterOrId "Emph" + Image _ _ _ -> filterOrId "Image" + LineBreak -> filterOrId "LineBreak" + Link _ _ _ -> filterOrId "Link" + Math _ _ -> filterOrId "Math" + Note _ -> filterOrId "Note" + Quoted _ _ -> filterOrId "Quoted" + RawInline _ _ -> filterOrId "RawInline" + SmallCaps _ -> filterOrId "SmallCaps" + SoftBreak -> filterOrId "SoftBreak" + Space -> filterOrId "Space" + Span _ _ -> filterOrId "Span" + Str _ -> filterOrId "Str" + Strikeout _ -> filterOrId "Strikeout" + Strong _ -> filterOrId "Strong" + Subscript _ -> filterOrId "Subscript" + Superscript _ -> filterOrId "Superscript" + +instance StackValue LuaFilter where + valuetype _ = Lua.TTABLE + push = undefined + peek lua i = do + -- TODO: find a more efficient way of doing this in a typesafe manner. + inlineFnMap <- Lua.peek lua i + blockFnMap <- Lua.peek lua i + docFnMap <- Lua.peek lua i + return $ LuaFilter lua <$> inlineFnMap <*> blockFnMap <*> docFnMap + +runLuaFilterFunction :: (StackValue a) + => LuaState -> LuaFilterFunction a -> a -> IO a +runLuaFilterFunction lua lf inline = do + pushFilterFunction lua lf + Lua.push lua inline + Lua.call lua 1 1 + Just res <- Lua.peek lua (-1) + Lua.pop lua 1 + return res + +-- FIXME: use registry +pushFilterFunction :: Lua.LuaState -> LuaFilterFunction a -> IO () +pushFilterFunction lua lf = do + Lua.getglobal lua "PANDOC_FILTER_FUNCTIONS" + Lua.rawgeti lua (-1) (functionIndex lf) + Lua.remove lua (-2) -- remove global from stack + +instance StackValue (LuaFilterFunction a) where + valuetype _ = Lua.TFUNCTION + push lua v = pushFilterFunction lua v + peek lua i = do + isFn <- Lua.isfunction lua i + when (not isFn) (error $ "Not a function at index " ++ (show i)) + Lua.pushvalue lua i + Lua.getglobal lua "PANDOC_FILTER_FUNCTIONS" + len <- Lua.objlen lua (-1) + Lua.insert lua (-2) + Lua.rawseti lua (-2) (len + 1) + Lua.pop lua 1 + return . Just $ LuaFilterFunction (len + 1) + + +isLuaFunction :: Lua.LuaState -> String -> IO Bool +isLuaFunction lua fnName = do + Lua.getglobal lua fnName + res <- Lua.isfunction lua (-1) + Lua.pop lua (-1) + return res + +maybeFromJson :: (FromJSON a) => Maybe Value -> Maybe a +maybeFromJson mv = fromJSON <$> mv >>= \case + Success x -> Just x + _ -> Nothing + +instance StackValue Pandoc where + push lua = Lua.push lua . toJSON + peek lua i = maybeFromJson <$> peek lua i + valuetype _ = Lua.TTABLE + +instance StackValue Block where + push lua = Lua.push lua . toJSON + peek lua i = maybeFromJson <$> peek lua i + valuetype _ = Lua.TTABLE + +instance StackValue Inline where + push lua = Lua.push lua . toJSON + peek lua i = maybeFromJson <$> peek lua i + valuetype _ = Lua.TTABLE -- cgit v1.2.3 From e7eb21ecca46daaf240e33584c55b9d5101eebc7 Mon Sep 17 00:00:00 2001 From: Albert Krewinkel Date: Sun, 2 Apr 2017 17:21:22 +0200 Subject: Lua module: add readers submodule Plain text readers are exposed to lua scripts via the `pandoc.reader` submodule, which is further subdivided by format. Converting e.g. a markdown string into a pandoc document is possible from within lua: doc = pandoc.reader.markdown.read_doc("Hello, World!") A `read_block` convenience function is provided for all formats, although it will still parse the whole string but return only the first block as the result. Custom reader options are not supported yet, default options are used for all parsing operations. --- src/Text/Pandoc/Lua.hs | 31 +++---------------------------- 1 file changed, 3 insertions(+), 28 deletions(-) (limited to 'src/Text/Pandoc/Lua.hs') diff --git a/src/Text/Pandoc/Lua.hs b/src/Text/Pandoc/Lua.hs index 6fa6b2020..d754b43b8 100644 --- a/src/Text/Pandoc/Lua.hs +++ b/src/Text/Pandoc/Lua.hs @@ -15,11 +15,7 @@ You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -} -{-# LANGUAGE FlexibleContexts #-} -{-# LANGUAGE LambdaCase #-} {-# LANGUAGE OverloadedStrings #-} -{-# LANGUAGE ScopedTypeVariables #-} -{-# OPTIONS_GHC -fno-warn-orphans #-} {- | Module : Text.Pandoc.Lua Copyright : Copyright © 2017 Albert Krewinkel @@ -34,24 +30,23 @@ module Text.Pandoc.Lua ( runLuaFilter ) where import Control.Monad ( (>=>), when ) import Control.Monad.Trans ( MonadIO(..) ) -import Data.Aeson ( FromJSON(..), ToJSON(..), Result(..), Value, fromJSON ) import Data.HashMap.Lazy ( HashMap ) import Data.Text ( Text, pack, unpack ) import Data.Text.Encoding ( decodeUtf8 ) import Scripting.Lua ( LuaState, StackValue(..) ) -import Scripting.Lua.Aeson () +import Scripting.Lua.Aeson ( newstate ) import Text.Pandoc.Definition ( Block(..), Inline(..), Pandoc(..) ) import Text.Pandoc.Lua.PandocModule +import Text.Pandoc.Lua.StackInstances () import Text.Pandoc.Walk import qualified Data.HashMap.Lazy as HashMap import qualified Scripting.Lua as Lua -import qualified Scripting.Lua as LuaAeson runLuaFilter :: (MonadIO m) => FilePath -> [String] -> Pandoc -> m Pandoc runLuaFilter filterPath args pd = liftIO $ do - lua <- LuaAeson.newstate + lua <- newstate Lua.openlibs lua Lua.newtable lua Lua.setglobal lua "PANDOC_FILTER_FUNCTIONS" -- hack, store functions here @@ -204,23 +199,3 @@ isLuaFunction lua fnName = do res <- Lua.isfunction lua (-1) Lua.pop lua (-1) return res - -maybeFromJson :: (FromJSON a) => Maybe Value -> Maybe a -maybeFromJson mv = fromJSON <$> mv >>= \case - Success x -> Just x - _ -> Nothing - -instance StackValue Pandoc where - push lua = Lua.push lua . toJSON - peek lua i = maybeFromJson <$> peek lua i - valuetype _ = Lua.TTABLE - -instance StackValue Block where - push lua = Lua.push lua . toJSON - peek lua i = maybeFromJson <$> peek lua i - valuetype _ = Lua.TTABLE - -instance StackValue Inline where - push lua = Lua.push lua . toJSON - peek lua i = maybeFromJson <$> peek lua i - valuetype _ = Lua.TTABLE -- cgit v1.2.3 From fca93efb624af48a212a4597a116bfcde8b7192f Mon Sep 17 00:00:00 2001 From: Albert Krewinkel Date: Tue, 4 Apr 2017 21:51:51 +0200 Subject: Use lua registry instead of named globals This is slightly cleaner while keeping performance approximately the same. --- src/Text/Pandoc/Lua.hs | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) (limited to 'src/Text/Pandoc/Lua.hs') diff --git a/src/Text/Pandoc/Lua.hs b/src/Text/Pandoc/Lua.hs index d754b43b8..a68810bd7 100644 --- a/src/Text/Pandoc/Lua.hs +++ b/src/Text/Pandoc/Lua.hs @@ -48,8 +48,11 @@ runLuaFilter :: (MonadIO m) runLuaFilter filterPath args pd = liftIO $ do lua <- newstate Lua.openlibs lua + -- create table in registry to store filter functions + Lua.push lua ("PANDOC_FILTER_FUNCTIONS"::String) Lua.newtable lua - Lua.setglobal lua "PANDOC_FILTER_FUNCTIONS" -- hack, store functions here + Lua.rawset lua Lua.registryindex + -- store module in global "pandoc" pushPandocModule lua Lua.setglobal lua "pandoc" status <- Lua.loadfile lua filterPath @@ -171,12 +174,14 @@ runLuaFilterFunction lua lf inline = do Lua.pop lua 1 return res --- FIXME: use registry +-- | Push the filter function to the top of the stack. pushFilterFunction :: Lua.LuaState -> LuaFilterFunction a -> IO () pushFilterFunction lua lf = do - Lua.getglobal lua "PANDOC_FILTER_FUNCTIONS" + -- The function is stored in a lua registry table, retrieve it from there. + push lua ("PANDOC_FILTER_FUNCTIONS"::String) + Lua.rawget lua Lua.registryindex Lua.rawgeti lua (-1) (functionIndex lf) - Lua.remove lua (-2) -- remove global from stack + Lua.remove lua (-2) -- remove registry table from stack instance StackValue (LuaFilterFunction a) where valuetype _ = Lua.TFUNCTION @@ -185,7 +190,8 @@ instance StackValue (LuaFilterFunction a) where isFn <- Lua.isfunction lua i when (not isFn) (error $ "Not a function at index " ++ (show i)) Lua.pushvalue lua i - Lua.getglobal lua "PANDOC_FILTER_FUNCTIONS" + push lua ("PANDOC_FILTER_FUNCTIONS"::String) + Lua.rawget lua Lua.registryindex len <- Lua.objlen lua (-1) Lua.insert lua (-2) Lua.rawseti lua (-2) (len + 1) -- cgit v1.2.3 From 7e3705c1c4a7b63ce6818c1e3cb3496ff618ac0f Mon Sep 17 00:00:00 2001 From: Albert Krewinkel Date: Tue, 11 Apr 2017 23:31:55 +0200 Subject: Lua filter: use custom StackValue Inline instance Inline elements are no longer pushed and pulled via aeson's Value. --- src/Text/Pandoc/Lua.hs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'src/Text/Pandoc/Lua.hs') diff --git a/src/Text/Pandoc/Lua.hs b/src/Text/Pandoc/Lua.hs index a68810bd7..d7c54b6af 100644 --- a/src/Text/Pandoc/Lua.hs +++ b/src/Text/Pandoc/Lua.hs @@ -170,9 +170,11 @@ runLuaFilterFunction lua lf inline = do pushFilterFunction lua lf Lua.push lua inline Lua.call lua 1 1 - Just res <- Lua.peek lua (-1) - Lua.pop lua 1 - return res + mbres <- Lua.peek lua (-1) + case mbres of + Nothing -> error $ "Error while trying to get a filter's return " + ++ "value from lua stack." + Just res -> res <$ Lua.pop lua 1 -- | Push the filter function to the top of the stack. pushFilterFunction :: Lua.LuaState -> LuaFilterFunction a -> IO () -- cgit v1.2.3 From 2761a38e530735499f7ac0075b768feb101190a5 Mon Sep 17 00:00:00 2001 From: Albert Krewinkel Date: Wed, 12 Apr 2017 20:48:44 +0200 Subject: Lua filter: use destructured functions for inline filters Instead of taking the whole inline element, forcing users to destructure it themselves, the components of the elements are passed to the filtering functions. --- src/Text/Pandoc/Lua.hs | 91 ++++++++++++++++++++++++++++++++------------------ 1 file changed, 58 insertions(+), 33 deletions(-) (limited to 'src/Text/Pandoc/Lua.hs') diff --git a/src/Text/Pandoc/Lua.hs b/src/Text/Pandoc/Lua.hs index d7c54b6af..ccd820682 100644 --- a/src/Text/Pandoc/Lua.hs +++ b/src/Text/Pandoc/Lua.hs @@ -15,6 +15,9 @@ You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -} +{-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE FlexibleInstances #-} +{-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE OverloadedStrings #-} {- | Module : Text.Pandoc.Lua @@ -130,29 +133,35 @@ execInlineLuaFilter :: LuaState -> HashMap Text (LuaFilterFunction Inline) -> Inline -> IO Inline execInlineLuaFilter lua fnMap x = do - let filterOrId constr = case HashMap.lookup constr fnMap of - Nothing -> return x - Just fn -> runLuaFilterFunction lua fn x + let runFn :: PushViaFilterFunction Inline a => LuaFilterFunction Inline -> a + runFn fn = runLuaFilterFunction lua fn + let tryFilter :: Text -> (LuaFilterFunction Inline -> IO Inline) -> IO Inline + tryFilter fnName callFilterFn = + case HashMap.lookup fnName fnMap of + Nothing -> return x + Just fn -> callFilterFn fn case x of - Cite _ _ -> filterOrId "Cite" - Code _ _ -> filterOrId "Code" - Emph _ -> filterOrId "Emph" - Image _ _ _ -> filterOrId "Image" - LineBreak -> filterOrId "LineBreak" - Link _ _ _ -> filterOrId "Link" - Math _ _ -> filterOrId "Math" - Note _ -> filterOrId "Note" - Quoted _ _ -> filterOrId "Quoted" - RawInline _ _ -> filterOrId "RawInline" - SmallCaps _ -> filterOrId "SmallCaps" - SoftBreak -> filterOrId "SoftBreak" - Space -> filterOrId "Space" - Span _ _ -> filterOrId "Span" - Str _ -> filterOrId "Str" - Strikeout _ -> filterOrId "Strikeout" - Strong _ -> filterOrId "Strong" - Subscript _ -> filterOrId "Subscript" - Superscript _ -> filterOrId "Superscript" + LineBreak -> tryFilter "LineBreak" runFn + SoftBreak -> tryFilter "SoftBreak" runFn + Space -> tryFilter "Space" runFn + Cite cs lst -> tryFilter "Cite" $ \fn -> runFn fn lst cs + Code attr str -> tryFilter "Code" $ \fn -> runFn fn str attr + Emph lst -> tryFilter "Emph" $ \fn -> runFn fn lst + Math mt lst -> tryFilter "Math" $ \fn -> runFn fn lst mt + Note blks -> tryFilter "Note" $ \fn -> runFn fn blks + Quoted qt lst -> tryFilter "Quoted" $ \fn -> runFn fn qt lst + RawInline f str -> tryFilter "RawInline" $ \fn -> runFn fn f str + SmallCaps lst -> tryFilter "SmallCaps" $ \fn -> runFn fn lst + Span attr lst -> tryFilter "Span" $ \fn -> runFn fn lst attr + Str str -> tryFilter "Str" $ \fn -> runFn fn str + Strikeout lst -> tryFilter "Strikeout" $ \fn -> runFn fn lst + Strong lst -> tryFilter "Strong" $ \fn -> runFn fn lst + Subscript lst -> tryFilter "Subscript" $ \fn -> runFn fn lst + Superscript lst -> tryFilter "Superscript" $ \fn -> runFn fn lst + Link attr txt (src, tit) -> tryFilter "Link" $ + \fn -> runFn fn txt src tit attr + Image attr alt (src, tit) -> tryFilter "Image" $ + \fn -> runFn fn alt src tit attr instance StackValue LuaFilter where valuetype _ = Lua.TTABLE @@ -164,17 +173,33 @@ instance StackValue LuaFilter where docFnMap <- Lua.peek lua i return $ LuaFilter lua <$> inlineFnMap <*> blockFnMap <*> docFnMap -runLuaFilterFunction :: (StackValue a) - => LuaState -> LuaFilterFunction a -> a -> IO a -runLuaFilterFunction lua lf inline = do - pushFilterFunction lua lf - Lua.push lua inline - Lua.call lua 1 1 - mbres <- Lua.peek lua (-1) - case mbres of - Nothing -> error $ "Error while trying to get a filter's return " - ++ "value from lua stack." - Just res -> res <$ Lua.pop lua 1 +-- | Helper class for pushing a single value to the stack via a lua function. +-- See @pushViaCall@. +class PushViaFilterFunction a b where + pushViaFilterFunction' :: LuaState -> LuaFilterFunction a -> IO () -> Int -> b + +instance (StackValue a) => PushViaFilterFunction a (IO a) where + pushViaFilterFunction' lua lf pushArgs num = do + pushFilterFunction lua lf + pushArgs + Lua.call lua num 1 + mbres <- Lua.peek lua (-1) + case mbres of + Nothing -> error $ "Error while trying to get a filter's return " + ++ "value from lua stack." + Just res -> res <$ Lua.pop lua 1 + +instance (PushViaFilterFunction a c, StackValue b) => + PushViaFilterFunction a (b -> c) where + pushViaFilterFunction' lua lf pushArgs num x = + pushViaFilterFunction' lua lf (pushArgs *> push lua x) (num + 1) + +-- | Push an value to the stack via a lua filter function. The function is +-- called with all arguments that are passed to this function and is expected to +-- return a single value. +runLuaFilterFunction :: (StackValue a, PushViaFilterFunction a b) + => LuaState -> LuaFilterFunction a -> b +runLuaFilterFunction lua lf = pushViaFilterFunction' lua lf (return ()) 0 -- | Push the filter function to the top of the stack. pushFilterFunction :: Lua.LuaState -> LuaFilterFunction a -> IO () -- cgit v1.2.3 From 425df8fff435c105590986e1b85efbcca8986931 Mon Sep 17 00:00:00 2001 From: Albert Krewinkel Date: Thu, 13 Apr 2017 22:57:50 +0200 Subject: Use lua constructors to push meta values --- src/Text/Pandoc/Lua.hs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/Text/Pandoc/Lua.hs') diff --git a/src/Text/Pandoc/Lua.hs b/src/Text/Pandoc/Lua.hs index ccd820682..95bc1ef35 100644 --- a/src/Text/Pandoc/Lua.hs +++ b/src/Text/Pandoc/Lua.hs @@ -29,7 +29,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Pandoc lua utils. -} -module Text.Pandoc.Lua ( runLuaFilter ) where +module Text.Pandoc.Lua ( runLuaFilter, pushPandocModule ) where import Control.Monad ( (>=>), when ) import Control.Monad.Trans ( MonadIO(..) ) @@ -39,7 +39,7 @@ import Data.Text.Encoding ( decodeUtf8 ) import Scripting.Lua ( LuaState, StackValue(..) ) import Scripting.Lua.Aeson ( newstate ) import Text.Pandoc.Definition ( Block(..), Inline(..), Pandoc(..) ) -import Text.Pandoc.Lua.PandocModule +import Text.Pandoc.Lua.PandocModule ( pushPandocModule ) import Text.Pandoc.Lua.StackInstances () import Text.Pandoc.Walk -- cgit v1.2.3 From 0516b5127c3674786f92c61f4131428ed3b8bd4b Mon Sep 17 00:00:00 2001 From: Albert Krewinkel Date: Fri, 14 Apr 2017 10:43:44 +0200 Subject: Drop dependency on hslua-aeson Pushing values to the lua stack via custom functions is faster and more flexible. --- src/Text/Pandoc/Lua.hs | 33 +++++++++++++++------------------ 1 file changed, 15 insertions(+), 18 deletions(-) (limited to 'src/Text/Pandoc/Lua.hs') diff --git a/src/Text/Pandoc/Lua.hs b/src/Text/Pandoc/Lua.hs index 95bc1ef35..bca6a2589 100644 --- a/src/Text/Pandoc/Lua.hs +++ b/src/Text/Pandoc/Lua.hs @@ -33,23 +33,20 @@ module Text.Pandoc.Lua ( runLuaFilter, pushPandocModule ) where import Control.Monad ( (>=>), when ) import Control.Monad.Trans ( MonadIO(..) ) -import Data.HashMap.Lazy ( HashMap ) -import Data.Text ( Text, pack, unpack ) -import Data.Text.Encoding ( decodeUtf8 ) +import Data.Map ( Map ) import Scripting.Lua ( LuaState, StackValue(..) ) -import Scripting.Lua.Aeson ( newstate ) import Text.Pandoc.Definition ( Block(..), Inline(..), Pandoc(..) ) import Text.Pandoc.Lua.PandocModule ( pushPandocModule ) import Text.Pandoc.Lua.StackInstances () import Text.Pandoc.Walk -import qualified Data.HashMap.Lazy as HashMap +import qualified Data.Map as Map import qualified Scripting.Lua as Lua runLuaFilter :: (MonadIO m) => FilePath -> [String] -> Pandoc -> m Pandoc runLuaFilter filterPath args pd = liftIO $ do - lua <- newstate + lua <- Lua.newstate Lua.openlibs lua -- create table in registry to store filter functions Lua.push lua ("PANDOC_FILTER_FUNCTIONS"::String) @@ -61,12 +58,12 @@ runLuaFilter filterPath args pd = liftIO $ do status <- Lua.loadfile lua filterPath if (status /= 0) then do - luaErrMsg <- unpack . decodeUtf8 <$> Lua.tostring lua 1 + Just luaErrMsg <- Lua.peek lua 1 error luaErrMsg else do Lua.call lua 0 1 Just luaFilters <- Lua.peek lua (-1) - Lua.push lua (map pack args) + Lua.push lua args Lua.setglobal lua "PandocParameters" doc <- runAll luaFilters >=> luaFilter lua "filter_doc" $ pd Lua.close lua @@ -89,28 +86,28 @@ walkMWithLuaFilter (LuaFilter lua inlineFnMap blockFnMap docFnMap) = walkM (execBlockLuaFilter lua blockFnMap) >=> walkM (execDocLuaFilter lua docFnMap) -type InlineFunctionMap = HashMap Text (LuaFilterFunction Inline) -type BlockFunctionMap = HashMap Text (LuaFilterFunction Block) -type DocFunctionMap = HashMap Text (LuaFilterFunction Pandoc) +type InlineFunctionMap = Map String (LuaFilterFunction Inline) +type BlockFunctionMap = Map String (LuaFilterFunction Block) +type DocFunctionMap = Map String (LuaFilterFunction Pandoc) data LuaFilter = LuaFilter LuaState InlineFunctionMap BlockFunctionMap DocFunctionMap newtype LuaFilterFunction a = LuaFilterFunction { functionIndex :: Int } execDocLuaFilter :: LuaState - -> HashMap Text (LuaFilterFunction Pandoc) + -> Map String (LuaFilterFunction Pandoc) -> Pandoc -> IO Pandoc execDocLuaFilter lua fnMap x = do let docFnName = "Doc" - case HashMap.lookup docFnName fnMap of + case Map.lookup docFnName fnMap of Nothing -> return x Just fn -> runLuaFilterFunction lua fn x execBlockLuaFilter :: LuaState - -> HashMap Text (LuaFilterFunction Block) + -> Map String (LuaFilterFunction Block) -> Block -> IO Block execBlockLuaFilter lua fnMap x = do - let filterOrId constr = case HashMap.lookup constr fnMap of + let filterOrId constr = case Map.lookup constr fnMap of Nothing -> return x Just fn -> runLuaFilterFunction lua fn x case x of @@ -130,14 +127,14 @@ execBlockLuaFilter lua fnMap x = do Null -> filterOrId "Null" execInlineLuaFilter :: LuaState - -> HashMap Text (LuaFilterFunction Inline) + -> Map String (LuaFilterFunction Inline) -> Inline -> IO Inline execInlineLuaFilter lua fnMap x = do let runFn :: PushViaFilterFunction Inline a => LuaFilterFunction Inline -> a runFn fn = runLuaFilterFunction lua fn - let tryFilter :: Text -> (LuaFilterFunction Inline -> IO Inline) -> IO Inline + let tryFilter :: String -> (LuaFilterFunction Inline -> IO Inline) -> IO Inline tryFilter fnName callFilterFn = - case HashMap.lookup fnName fnMap of + case Map.lookup fnName fnMap of Nothing -> return x Just fn -> callFilterFn fn case x of -- cgit v1.2.3 From 07f41a5515c0d753c8b3fa074132ba219db8360c Mon Sep 17 00:00:00 2001 From: Albert Krewinkel Date: Fri, 14 Apr 2017 11:21:58 +0200 Subject: Lua filter: use destructured functions for block filters Filtering functions take element components as arguments instead of the whole block elements. This resembles the way elements are handled in custom writers. --- src/Text/Pandoc/Lua.hs | 40 +++++++++++++++++++++++----------------- 1 file changed, 23 insertions(+), 17 deletions(-) (limited to 'src/Text/Pandoc/Lua.hs') diff --git a/src/Text/Pandoc/Lua.hs b/src/Text/Pandoc/Lua.hs index bca6a2589..9903d4df6 100644 --- a/src/Text/Pandoc/Lua.hs +++ b/src/Text/Pandoc/Lua.hs @@ -107,24 +107,30 @@ execBlockLuaFilter :: LuaState -> Map String (LuaFilterFunction Block) -> Block -> IO Block execBlockLuaFilter lua fnMap x = do - let filterOrId constr = case Map.lookup constr fnMap of - Nothing -> return x - Just fn -> runLuaFilterFunction lua fn x + let runFn :: PushViaFilterFunction Block a => LuaFilterFunction Block -> a + runFn fn = runLuaFilterFunction lua fn + let tryFilter :: String -> (LuaFilterFunction Block -> IO Block) -> IO Block + tryFilter fnName callFilterFn = + case Map.lookup fnName fnMap of + Nothing -> return x + Just fn -> callFilterFn fn case x of - Plain _ -> filterOrId "Plain" - Para _ -> filterOrId "Para" - LineBlock _ -> filterOrId "LineBlock" - CodeBlock _ _ -> filterOrId "CodeBlock" - RawBlock _ _ -> filterOrId "RawBlock" - BlockQuote _ -> filterOrId "BlockQuote" - OrderedList _ _ -> filterOrId "OrderedList" - BulletList _ -> filterOrId "BulletList" - DefinitionList _ -> filterOrId "DefinitionList" - Header _ _ _ -> filterOrId "Header" - HorizontalRule -> filterOrId "HorizontalRule" - Table _ _ _ _ _ -> filterOrId "Table" - Div _ _ -> filterOrId "Div" - Null -> filterOrId "Null" + HorizontalRule -> tryFilter "HorizontalRule" runFn + Null -> tryFilter "Null" runFn + BlockQuote blcks -> tryFilter "BlockQuote" $ \fn -> runFn fn blcks + BulletList items -> tryFilter "BulletList" $ \fn -> runFn fn items + CodeBlock attr code -> tryFilter "CodeBlock" $ \fn -> runFn fn attr code + DefinitionList lst -> tryFilter "DefinitionList" $ \fn -> runFn fn lst + Div attr content -> tryFilter "Div" $ \fn -> runFn fn content attr + Header lvl attr inlns -> tryFilter "Header" $ \fn -> runFn fn lvl inlns attr + LineBlock inlns -> tryFilter "LineBlock" $ \fn -> runFn fn inlns + Para inlns -> tryFilter "Para" $ \fn -> runFn fn inlns + Plain inlns -> tryFilter "Plain" $ \fn -> runFn fn inlns + RawBlock format str -> tryFilter "RawBlock" $ \fn -> runFn fn format str + OrderedList (num,sty,delim) items -> + tryFilter "OrderedList" $ \fn -> runFn fn items (num,sty,delim) + Table capt aligns widths headers rows -> + tryFilter "Table" $ \fn -> runFn fn capt aligns widths headers rows execInlineLuaFilter :: LuaState -> Map String (LuaFilterFunction Inline) -- cgit v1.2.3 From feb1c1a9301667cc3b6c36c5fda65c7014cfcdcf Mon Sep 17 00:00:00 2001 From: Albert Krewinkel Date: Fri, 14 Apr 2017 18:26:42 +0200 Subject: Extract lua helper functions into Lua.Util module --- src/Text/Pandoc/Lua.hs | 20 ++------------------ 1 file changed, 2 insertions(+), 18 deletions(-) (limited to 'src/Text/Pandoc/Lua.hs') diff --git a/src/Text/Pandoc/Lua.hs b/src/Text/Pandoc/Lua.hs index 9903d4df6..d8b9f62f0 100644 --- a/src/Text/Pandoc/Lua.hs +++ b/src/Text/Pandoc/Lua.hs @@ -18,7 +18,6 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE MultiParamTypeClasses #-} -{-# LANGUAGE OverloadedStrings #-} {- | Module : Text.Pandoc.Lua Copyright : Copyright © 2017 Albert Krewinkel @@ -49,7 +48,7 @@ runLuaFilter filterPath args pd = liftIO $ do lua <- Lua.newstate Lua.openlibs lua -- create table in registry to store filter functions - Lua.push lua ("PANDOC_FILTER_FUNCTIONS"::String) + Lua.push lua "PANDOC_FILTER_FUNCTIONS" Lua.newtable lua Lua.rawset lua Lua.registryindex -- store module in global "pandoc" @@ -65,7 +64,7 @@ runLuaFilter filterPath args pd = liftIO $ do Just luaFilters <- Lua.peek lua (-1) Lua.push lua args Lua.setglobal lua "PandocParameters" - doc <- runAll luaFilters >=> luaFilter lua "filter_doc" $ pd + doc <- runAll luaFilters pd Lua.close lua return doc @@ -73,13 +72,6 @@ runAll :: [LuaFilter] -> Pandoc -> IO Pandoc runAll [] = return runAll (x:xs) = walkMWithLuaFilter x >=> runAll xs -luaFilter :: Lua.LuaState -> String -> Pandoc -> IO Pandoc -luaFilter lua luaFn x = do - fnExists <- isLuaFunction lua luaFn - if fnExists - then walkM (Lua.callfunc lua luaFn :: Pandoc -> IO Pandoc) x - else return x - walkMWithLuaFilter :: LuaFilter -> Pandoc -> IO Pandoc walkMWithLuaFilter (LuaFilter lua inlineFnMap blockFnMap docFnMap) = walkM (execInlineLuaFilter lua inlineFnMap) >=> @@ -227,11 +219,3 @@ instance StackValue (LuaFilterFunction a) where Lua.rawseti lua (-2) (len + 1) Lua.pop lua 1 return . Just $ LuaFilterFunction (len + 1) - - -isLuaFunction :: Lua.LuaState -> String -> IO Bool -isLuaFunction lua fnName = do - Lua.getglobal lua fnName - res <- Lua.isfunction lua (-1) - Lua.pop lua (-1) - return res -- cgit v1.2.3 From 3aeed816e163b1ad3925caff0496fa05a63d1369 Mon Sep 17 00:00:00 2001 From: Albert Krewinkel Date: Fri, 14 Apr 2017 23:24:52 +0200 Subject: Lua filter: allow shorthand functions for math and quoted Allow to use functions named `SingleQuoted`, `DoubleQuoted`, `DisplayMath`, and `InlineMath` in filters. --- src/Text/Pandoc/Lua.hs | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) (limited to 'src/Text/Pandoc/Lua.hs') diff --git a/src/Text/Pandoc/Lua.hs b/src/Text/Pandoc/Lua.hs index d8b9f62f0..a89da52bc 100644 --- a/src/Text/Pandoc/Lua.hs +++ b/src/Text/Pandoc/Lua.hs @@ -34,7 +34,7 @@ import Control.Monad ( (>=>), when ) import Control.Monad.Trans ( MonadIO(..) ) import Data.Map ( Map ) import Scripting.Lua ( LuaState, StackValue(..) ) -import Text.Pandoc.Definition ( Block(..), Inline(..), Pandoc(..) ) +import Text.Pandoc.Definition import Text.Pandoc.Lua.PandocModule ( pushPandocModule ) import Text.Pandoc.Lua.StackInstances () import Text.Pandoc.Walk @@ -135,6 +135,12 @@ execInlineLuaFilter lua fnMap x = do case Map.lookup fnName fnMap of Nothing -> return x Just fn -> callFilterFn fn + let tryFilterAlternatives :: [(String, LuaFilterFunction Inline -> IO Inline)] -> IO Inline + tryFilterAlternatives [] = return x + tryFilterAlternatives ((fnName, callFilterFn) : alternatives) = + case Map.lookup fnName fnMap of + Nothing -> tryFilterAlternatives alternatives + Just fn -> callFilterFn fn case x of LineBreak -> tryFilter "LineBreak" runFn SoftBreak -> tryFilter "SoftBreak" runFn @@ -142,9 +148,7 @@ execInlineLuaFilter lua fnMap x = do Cite cs lst -> tryFilter "Cite" $ \fn -> runFn fn lst cs Code attr str -> tryFilter "Code" $ \fn -> runFn fn str attr Emph lst -> tryFilter "Emph" $ \fn -> runFn fn lst - Math mt lst -> tryFilter "Math" $ \fn -> runFn fn lst mt Note blks -> tryFilter "Note" $ \fn -> runFn fn blks - Quoted qt lst -> tryFilter "Quoted" $ \fn -> runFn fn qt lst RawInline f str -> tryFilter "RawInline" $ \fn -> runFn fn f str SmallCaps lst -> tryFilter "SmallCaps" $ \fn -> runFn fn lst Span attr lst -> tryFilter "Span" $ \fn -> runFn fn lst attr @@ -153,6 +157,22 @@ execInlineLuaFilter lua fnMap x = do Strong lst -> tryFilter "Strong" $ \fn -> runFn fn lst Subscript lst -> tryFilter "Subscript" $ \fn -> runFn fn lst Superscript lst -> tryFilter "Superscript" $ \fn -> runFn fn lst + Math DisplayMath lst -> tryFilterAlternatives + [ ("DisplayMath", \fn -> runFn fn lst) + , ("Math", \fn -> runFn fn DisplayMath lst) + ] + Math InlineMath lst -> tryFilterAlternatives + [ ("InlineMath", \fn -> runFn fn lst) + , ("Math", \fn -> runFn fn InlineMath lst) + ] + Quoted SingleQuote lst -> tryFilterAlternatives + [ ("SingleQuoted", \fn -> runFn fn lst) + , ("Quoted", \fn -> runFn fn SingleQuote lst) + ] + Quoted DoubleQuote lst -> tryFilterAlternatives + [ ("DoubleQuoted", \fn -> runFn fn lst) + , ("Quoted", \fn -> runFn fn DoubleQuote lst) + ] Link attr txt (src, tit) -> tryFilter "Link" $ \fn -> runFn fn txt src tit attr Image attr alt (src, tit) -> tryFilter "Image" $ -- cgit v1.2.3 From e6a536befcfd433aba66a3085e62792383867695 Mon Sep 17 00:00:00 2001 From: Albert Krewinkel Date: Sat, 15 Apr 2017 21:40:48 +0200 Subject: Lua filter: revert to non-destructuring filters We want to provide an interface familiar to users of other filtering libraries. --- src/Text/Pandoc/Lua.hs | 112 ++++++++++++++++++++----------------------------- 1 file changed, 46 insertions(+), 66 deletions(-) (limited to 'src/Text/Pandoc/Lua.hs') diff --git a/src/Text/Pandoc/Lua.hs b/src/Text/Pandoc/Lua.hs index a89da52bc..f4cd4e0d6 100644 --- a/src/Text/Pandoc/Lua.hs +++ b/src/Text/Pandoc/Lua.hs @@ -99,84 +99,64 @@ execBlockLuaFilter :: LuaState -> Map String (LuaFilterFunction Block) -> Block -> IO Block execBlockLuaFilter lua fnMap x = do - let runFn :: PushViaFilterFunction Block a => LuaFilterFunction Block -> a - runFn fn = runLuaFilterFunction lua fn - let tryFilter :: String -> (LuaFilterFunction Block -> IO Block) -> IO Block - tryFilter fnName callFilterFn = - case Map.lookup fnName fnMap of + let tryFilter :: String -> IO Block + tryFilter filterFnName = + case Map.lookup filterFnName fnMap of Nothing -> return x - Just fn -> callFilterFn fn + Just fn -> runLuaFilterFunction lua fn x case x of - HorizontalRule -> tryFilter "HorizontalRule" runFn - Null -> tryFilter "Null" runFn - BlockQuote blcks -> tryFilter "BlockQuote" $ \fn -> runFn fn blcks - BulletList items -> tryFilter "BulletList" $ \fn -> runFn fn items - CodeBlock attr code -> tryFilter "CodeBlock" $ \fn -> runFn fn attr code - DefinitionList lst -> tryFilter "DefinitionList" $ \fn -> runFn fn lst - Div attr content -> tryFilter "Div" $ \fn -> runFn fn content attr - Header lvl attr inlns -> tryFilter "Header" $ \fn -> runFn fn lvl inlns attr - LineBlock inlns -> tryFilter "LineBlock" $ \fn -> runFn fn inlns - Para inlns -> tryFilter "Para" $ \fn -> runFn fn inlns - Plain inlns -> tryFilter "Plain" $ \fn -> runFn fn inlns - RawBlock format str -> tryFilter "RawBlock" $ \fn -> runFn fn format str - OrderedList (num,sty,delim) items -> - tryFilter "OrderedList" $ \fn -> runFn fn items (num,sty,delim) - Table capt aligns widths headers rows -> - tryFilter "Table" $ \fn -> runFn fn capt aligns widths headers rows + BlockQuote _ -> tryFilter "BlockQuote" + BulletList _ -> tryFilter "BulletList" + CodeBlock _ _ -> tryFilter "CodeBlock" + DefinitionList _ -> tryFilter "DefinitionList" + Div _ _ -> tryFilter "Div" + Header _ _ _ -> tryFilter "Header" + HorizontalRule -> tryFilter "HorizontalRule" + LineBlock _ -> tryFilter "LineBlock" + Null -> tryFilter "Null" + Para _ -> tryFilter "Para" + Plain _ -> tryFilter "Plain" + RawBlock _ _ -> tryFilter "RawBlock" + OrderedList _ _ -> tryFilter "OrderedList" + Table _ _ _ _ _ -> tryFilter "Table" execInlineLuaFilter :: LuaState -> Map String (LuaFilterFunction Inline) -> Inline -> IO Inline execInlineLuaFilter lua fnMap x = do - let runFn :: PushViaFilterFunction Inline a => LuaFilterFunction Inline -> a - runFn fn = runLuaFilterFunction lua fn - let tryFilter :: String -> (LuaFilterFunction Inline -> IO Inline) -> IO Inline - tryFilter fnName callFilterFn = - case Map.lookup fnName fnMap of + let tryFilter :: String -> IO Inline + tryFilter filterFnName = + case Map.lookup filterFnName fnMap of Nothing -> return x - Just fn -> callFilterFn fn - let tryFilterAlternatives :: [(String, LuaFilterFunction Inline -> IO Inline)] -> IO Inline + Just fn -> runLuaFilterFunction lua fn x + let tryFilterAlternatives :: [String] -> IO Inline tryFilterAlternatives [] = return x - tryFilterAlternatives ((fnName, callFilterFn) : alternatives) = + tryFilterAlternatives (fnName : alternatives) = case Map.lookup fnName fnMap of Nothing -> tryFilterAlternatives alternatives - Just fn -> callFilterFn fn + Just fn -> runLuaFilterFunction lua fn x case x of - LineBreak -> tryFilter "LineBreak" runFn - SoftBreak -> tryFilter "SoftBreak" runFn - Space -> tryFilter "Space" runFn - Cite cs lst -> tryFilter "Cite" $ \fn -> runFn fn lst cs - Code attr str -> tryFilter "Code" $ \fn -> runFn fn str attr - Emph lst -> tryFilter "Emph" $ \fn -> runFn fn lst - Note blks -> tryFilter "Note" $ \fn -> runFn fn blks - RawInline f str -> tryFilter "RawInline" $ \fn -> runFn fn f str - SmallCaps lst -> tryFilter "SmallCaps" $ \fn -> runFn fn lst - Span attr lst -> tryFilter "Span" $ \fn -> runFn fn lst attr - Str str -> tryFilter "Str" $ \fn -> runFn fn str - Strikeout lst -> tryFilter "Strikeout" $ \fn -> runFn fn lst - Strong lst -> tryFilter "Strong" $ \fn -> runFn fn lst - Subscript lst -> tryFilter "Subscript" $ \fn -> runFn fn lst - Superscript lst -> tryFilter "Superscript" $ \fn -> runFn fn lst - Math DisplayMath lst -> tryFilterAlternatives - [ ("DisplayMath", \fn -> runFn fn lst) - , ("Math", \fn -> runFn fn DisplayMath lst) - ] - Math InlineMath lst -> tryFilterAlternatives - [ ("InlineMath", \fn -> runFn fn lst) - , ("Math", \fn -> runFn fn InlineMath lst) - ] - Quoted SingleQuote lst -> tryFilterAlternatives - [ ("SingleQuoted", \fn -> runFn fn lst) - , ("Quoted", \fn -> runFn fn SingleQuote lst) - ] - Quoted DoubleQuote lst -> tryFilterAlternatives - [ ("DoubleQuoted", \fn -> runFn fn lst) - , ("Quoted", \fn -> runFn fn DoubleQuote lst) - ] - Link attr txt (src, tit) -> tryFilter "Link" $ - \fn -> runFn fn txt src tit attr - Image attr alt (src, tit) -> tryFilter "Image" $ - \fn -> runFn fn alt src tit attr + Cite _ _ -> tryFilter "Cite" + Code _ _ -> tryFilter "Code" + Emph _ -> tryFilter "Emph" + Image _ _ _ -> tryFilter "Image" + LineBreak -> tryFilter "LineBreak" + Link _ _ _ -> tryFilter "Link" + Math DisplayMath _ -> tryFilterAlternatives ["DisplayMath", "Math"] + Math InlineMath _ -> tryFilterAlternatives ["InlineMath", "Math"] + Note _ -> tryFilter "Note" + Quoted DoubleQuote _ -> tryFilterAlternatives ["DoubleQuoted", "Quoted"] + Quoted SingleQuote _ -> tryFilterAlternatives ["SingleQuoted", "Quoted"] + RawInline _ _ -> tryFilter "RawInline" + SmallCaps _ -> tryFilter "SmallCaps" + SoftBreak -> tryFilter "SoftBreak" + Space -> tryFilter "Space" + Span _ _ -> tryFilter "Span" + Str _ -> tryFilter "Str" + Strikeout _ -> tryFilter "Strikeout" + Strong _ -> tryFilter "Strong" + Subscript _ -> tryFilter "Subscript" + Superscript _ -> tryFilter "Superscript" instance StackValue LuaFilter where valuetype _ = Lua.TTABLE -- cgit v1.2.3 From 57a0759def058df0322152823bca003664b961c5 Mon Sep 17 00:00:00 2001 From: Albert Krewinkel Date: Tue, 18 Apr 2017 19:05:52 +0200 Subject: Lua: drop useless filter function type parameter The return-type parameter for lua filter functions is removed. It only complicated the code without introducing any additional type safety. --- src/Text/Pandoc/Lua.hs | 48 ++++++++++++++++++++---------------------------- 1 file changed, 20 insertions(+), 28 deletions(-) (limited to 'src/Text/Pandoc/Lua.hs') diff --git a/src/Text/Pandoc/Lua.hs b/src/Text/Pandoc/Lua.hs index f4cd4e0d6..0d1c6cf45 100644 --- a/src/Text/Pandoc/Lua.hs +++ b/src/Text/Pandoc/Lua.hs @@ -73,21 +73,18 @@ runAll [] = return runAll (x:xs) = walkMWithLuaFilter x >=> runAll xs walkMWithLuaFilter :: LuaFilter -> Pandoc -> IO Pandoc -walkMWithLuaFilter (LuaFilter lua inlineFnMap blockFnMap docFnMap) = - walkM (execInlineLuaFilter lua inlineFnMap) >=> - walkM (execBlockLuaFilter lua blockFnMap) >=> - walkM (execDocLuaFilter lua docFnMap) +walkMWithLuaFilter (LuaFilter lua fnMap) = + walkM (execInlineLuaFilter lua fnMap) >=> + walkM (execBlockLuaFilter lua fnMap) >=> + walkM (execDocLuaFilter lua fnMap) -type InlineFunctionMap = Map String (LuaFilterFunction Inline) -type BlockFunctionMap = Map String (LuaFilterFunction Block) -type DocFunctionMap = Map String (LuaFilterFunction Pandoc) -data LuaFilter = - LuaFilter LuaState InlineFunctionMap BlockFunctionMap DocFunctionMap +type FunctionMap = Map String LuaFilterFunction +data LuaFilter = LuaFilter LuaState FunctionMap -newtype LuaFilterFunction a = LuaFilterFunction { functionIndex :: Int } +newtype LuaFilterFunction = LuaFilterFunction { functionIndex :: Int } execDocLuaFilter :: LuaState - -> Map String (LuaFilterFunction Pandoc) + -> FunctionMap -> Pandoc -> IO Pandoc execDocLuaFilter lua fnMap x = do let docFnName = "Doc" @@ -96,7 +93,7 @@ execDocLuaFilter lua fnMap x = do Just fn -> runLuaFilterFunction lua fn x execBlockLuaFilter :: LuaState - -> Map String (LuaFilterFunction Block) + -> FunctionMap -> Block -> IO Block execBlockLuaFilter lua fnMap x = do let tryFilter :: String -> IO Block @@ -121,7 +118,7 @@ execBlockLuaFilter lua fnMap x = do Table _ _ _ _ _ -> tryFilter "Table" execInlineLuaFilter :: LuaState - -> Map String (LuaFilterFunction Inline) + -> FunctionMap -> Inline -> IO Inline execInlineLuaFilter lua fnMap x = do let tryFilter :: String -> IO Inline @@ -161,19 +158,14 @@ execInlineLuaFilter lua fnMap x = do instance StackValue LuaFilter where valuetype _ = Lua.TTABLE push = undefined - peek lua i = do - -- TODO: find a more efficient way of doing this in a typesafe manner. - inlineFnMap <- Lua.peek lua i - blockFnMap <- Lua.peek lua i - docFnMap <- Lua.peek lua i - return $ LuaFilter lua <$> inlineFnMap <*> blockFnMap <*> docFnMap + peek lua idx = fmap (LuaFilter lua) <$> Lua.peek lua idx -- | Helper class for pushing a single value to the stack via a lua function. -- See @pushViaCall@. -class PushViaFilterFunction a b where - pushViaFilterFunction' :: LuaState -> LuaFilterFunction a -> IO () -> Int -> b +class PushViaFilterFunction a where + pushViaFilterFunction' :: LuaState -> LuaFilterFunction -> IO () -> Int -> a -instance (StackValue a) => PushViaFilterFunction a (IO a) where +instance StackValue a => PushViaFilterFunction (IO a) where pushViaFilterFunction' lua lf pushArgs num = do pushFilterFunction lua lf pushArgs @@ -184,20 +176,20 @@ instance (StackValue a) => PushViaFilterFunction a (IO a) where ++ "value from lua stack." Just res -> res <$ Lua.pop lua 1 -instance (PushViaFilterFunction a c, StackValue b) => - PushViaFilterFunction a (b -> c) where +instance (StackValue a, PushViaFilterFunction b) => + PushViaFilterFunction (a -> b) where pushViaFilterFunction' lua lf pushArgs num x = pushViaFilterFunction' lua lf (pushArgs *> push lua x) (num + 1) -- | Push an value to the stack via a lua filter function. The function is -- called with all arguments that are passed to this function and is expected to -- return a single value. -runLuaFilterFunction :: (StackValue a, PushViaFilterFunction a b) - => LuaState -> LuaFilterFunction a -> b +runLuaFilterFunction :: PushViaFilterFunction a + => LuaState -> LuaFilterFunction -> a runLuaFilterFunction lua lf = pushViaFilterFunction' lua lf (return ()) 0 -- | Push the filter function to the top of the stack. -pushFilterFunction :: Lua.LuaState -> LuaFilterFunction a -> IO () +pushFilterFunction :: Lua.LuaState -> LuaFilterFunction -> IO () pushFilterFunction lua lf = do -- The function is stored in a lua registry table, retrieve it from there. push lua ("PANDOC_FILTER_FUNCTIONS"::String) @@ -205,7 +197,7 @@ pushFilterFunction lua lf = do Lua.rawgeti lua (-1) (functionIndex lf) Lua.remove lua (-2) -- remove registry table from stack -instance StackValue (LuaFilterFunction a) where +instance StackValue LuaFilterFunction where valuetype _ = Lua.TFUNCTION push lua v = pushFilterFunction lua v peek lua i = do -- cgit v1.2.3 From 9cd20c9b8b1fa3bd4581399327d61551558cf899 Mon Sep 17 00:00:00 2001 From: Albert Krewinkel Date: Sun, 16 Apr 2017 21:06:50 +0200 Subject: Lua filter: allow filtering of meta data only --- src/Text/Pandoc/Lua.hs | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) (limited to 'src/Text/Pandoc/Lua.hs') diff --git a/src/Text/Pandoc/Lua.hs b/src/Text/Pandoc/Lua.hs index 0d1c6cf45..ffc57c9c2 100644 --- a/src/Text/Pandoc/Lua.hs +++ b/src/Text/Pandoc/Lua.hs @@ -75,7 +75,8 @@ runAll (x:xs) = walkMWithLuaFilter x >=> runAll xs walkMWithLuaFilter :: LuaFilter -> Pandoc -> IO Pandoc walkMWithLuaFilter (LuaFilter lua fnMap) = walkM (execInlineLuaFilter lua fnMap) >=> - walkM (execBlockLuaFilter lua fnMap) >=> + walkM (execBlockLuaFilter lua fnMap) >=> + walkM (execMetaLuaFilter lua fnMap) >=> walkM (execDocLuaFilter lua fnMap) type FunctionMap = Map String LuaFilterFunction @@ -92,6 +93,17 @@ execDocLuaFilter lua fnMap x = do Nothing -> return x Just fn -> runLuaFilterFunction lua fn x +execMetaLuaFilter :: LuaState + -> FunctionMap + -> Pandoc -> IO Pandoc +execMetaLuaFilter lua fnMap pd@(Pandoc meta blks) = do + let metaFnName = "Meta" + case Map.lookup metaFnName fnMap of + Nothing -> return pd + Just fn -> do + meta' <- runLuaFilterFunction lua fn meta + return $ Pandoc meta' blks + execBlockLuaFilter :: LuaState -> FunctionMap -> Block -> IO Block -- cgit v1.2.3 From ae21a8bb2a9d892491424f257ed0146c1b2affa2 Mon Sep 17 00:00:00 2001 From: Albert Krewinkel Date: Sun, 30 Apr 2017 16:14:33 +0200 Subject: Lua filter: fall-back to global filters when none is returned The implicitly defined global filter (i.e. all element filtering functions defined in the global lua environment) is used if no filter is returned from a lua script. This allows to just write top-level functions in order to define a lua filter. E.g function Emph(elem) return pandoc.Strong(elem.content) end --- src/Text/Pandoc/Lua.hs | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) (limited to 'src/Text/Pandoc/Lua.hs') diff --git a/src/Text/Pandoc/Lua.hs b/src/Text/Pandoc/Lua.hs index ffc57c9c2..f4a22b92a 100644 --- a/src/Text/Pandoc/Lua.hs +++ b/src/Text/Pandoc/Lua.hs @@ -54,13 +54,17 @@ runLuaFilter filterPath args pd = liftIO $ do -- store module in global "pandoc" pushPandocModule lua Lua.setglobal lua "pandoc" + top <- Lua.gettop lua status <- Lua.loadfile lua filterPath if (status /= 0) then do Just luaErrMsg <- Lua.peek lua 1 error luaErrMsg else do - Lua.call lua 0 1 + Lua.call lua 0 Lua.multret + newtop <- Lua.gettop lua + -- Use the implicitly defined global filter if nothing was returned + when (newtop - top < 1) $ pushGlobalFilter lua Just luaFilters <- Lua.peek lua (-1) Lua.push lua args Lua.setglobal lua "PandocParameters" @@ -68,6 +72,13 @@ runLuaFilter filterPath args pd = liftIO $ do Lua.close lua return doc +pushGlobalFilter :: LuaState -> IO () +pushGlobalFilter lua = + Lua.newtable lua + *> Lua.getglobal2 lua "pandoc.global_filter" + *> Lua.call lua 0 1 + *> Lua.rawseti lua (-2) 1 + runAll :: [LuaFilter] -> Pandoc -> IO Pandoc runAll [] = return runAll (x:xs) = walkMWithLuaFilter x >=> runAll xs -- cgit v1.2.3 From 55d679e382954dd458acd6233609851748522d99 Mon Sep 17 00:00:00 2001 From: Albert Krewinkel Date: Sat, 3 Jun 2017 12:28:52 +0200 Subject: Improve code style in lua and org modules --- src/Text/Pandoc/Lua.hs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) (limited to 'src/Text/Pandoc/Lua.hs') diff --git a/src/Text/Pandoc/Lua.hs b/src/Text/Pandoc/Lua.hs index f4a22b92a..f74c0e425 100644 --- a/src/Text/Pandoc/Lua.hs +++ b/src/Text/Pandoc/Lua.hs @@ -15,8 +15,8 @@ You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -} -{-# LANGUAGE FlexibleContexts #-} -{-# LANGUAGE FlexibleInstances #-} +{-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE MultiParamTypeClasses #-} {- | Module : Text.Pandoc.Lua @@ -30,12 +30,12 @@ Pandoc lua utils. -} module Text.Pandoc.Lua ( runLuaFilter, pushPandocModule ) where -import Control.Monad ( (>=>), when ) -import Control.Monad.Trans ( MonadIO(..) ) -import Data.Map ( Map ) -import Scripting.Lua ( LuaState, StackValue(..) ) +import Control.Monad (unless, when, (>=>)) +import Control.Monad.Trans (MonadIO (..)) +import Data.Map (Map) +import Scripting.Lua (LuaState, StackValue (..)) import Text.Pandoc.Definition -import Text.Pandoc.Lua.PandocModule ( pushPandocModule ) +import Text.Pandoc.Lua.PandocModule (pushPandocModule) import Text.Pandoc.Lua.StackInstances () import Text.Pandoc.Walk @@ -80,7 +80,7 @@ pushGlobalFilter lua = *> Lua.rawseti lua (-2) 1 runAll :: [LuaFilter] -> Pandoc -> IO Pandoc -runAll [] = return +runAll [] = return runAll (x:xs) = walkMWithLuaFilter x >=> runAll xs walkMWithLuaFilter :: LuaFilter -> Pandoc -> IO Pandoc @@ -225,7 +225,7 @@ instance StackValue LuaFilterFunction where push lua v = pushFilterFunction lua v peek lua i = do isFn <- Lua.isfunction lua i - when (not isFn) (error $ "Not a function at index " ++ (show i)) + unless isFn (error $ "Not a function at index " ++ (show i)) Lua.pushvalue lua i push lua ("PANDOC_FILTER_FUNCTIONS"::String) Lua.rawget lua Lua.registryindex -- cgit v1.2.3 From b78afbd9803c75fcf2db32b4ce4ded1b8fa0224a Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Tue, 20 Jun 2017 16:09:33 +0200 Subject: Text.Pandoc.Lua: throw LuaException instead of using 'error'. Text.Pandoc.App: trap LuaException and issue a PandocFilterError. --- src/Text/Pandoc/Lua.hs | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) (limited to 'src/Text/Pandoc/Lua.hs') diff --git a/src/Text/Pandoc/Lua.hs b/src/Text/Pandoc/Lua.hs index f74c0e425..e9184c7ce 100644 --- a/src/Text/Pandoc/Lua.hs +++ b/src/Text/Pandoc/Lua.hs @@ -28,11 +28,15 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Pandoc lua utils. -} -module Text.Pandoc.Lua ( runLuaFilter, pushPandocModule ) where +module Text.Pandoc.Lua ( LuaException(..), + runLuaFilter, + pushPandocModule ) where +import Control.Exception import Control.Monad (unless, when, (>=>)) import Control.Monad.Trans (MonadIO (..)) import Data.Map (Map) +import Data.Typeable (Typeable) import Scripting.Lua (LuaState, StackValue (..)) import Text.Pandoc.Definition import Text.Pandoc.Lua.PandocModule (pushPandocModule) @@ -42,6 +46,11 @@ import Text.Pandoc.Walk import qualified Data.Map as Map import qualified Scripting.Lua as Lua +data LuaException = LuaException String + deriving (Show, Typeable) + +instance Exception LuaException + runLuaFilter :: (MonadIO m) => FilePath -> [String] -> Pandoc -> m Pandoc runLuaFilter filterPath args pd = liftIO $ do @@ -59,7 +68,7 @@ runLuaFilter filterPath args pd = liftIO $ do if (status /= 0) then do Just luaErrMsg <- Lua.peek lua 1 - error luaErrMsg + throwIO (LuaException luaErrMsg) else do Lua.call lua 0 Lua.multret newtop <- Lua.gettop lua @@ -195,8 +204,9 @@ instance StackValue a => PushViaFilterFunction (IO a) where Lua.call lua num 1 mbres <- Lua.peek lua (-1) case mbres of - Nothing -> error $ "Error while trying to get a filter's return " - ++ "value from lua stack." + Nothing -> throwIO $ LuaException + ("Error while trying to get a filter's return " + ++ "value from lua stack.") Just res -> res <$ Lua.pop lua 1 instance (StackValue a, PushViaFilterFunction b) => @@ -225,7 +235,8 @@ instance StackValue LuaFilterFunction where push lua v = pushFilterFunction lua v peek lua i = do isFn <- Lua.isfunction lua i - unless isFn (error $ "Not a function at index " ++ (show i)) + unless isFn (throwIO $ LuaException $ + "Not a function at index " ++ (show i)) Lua.pushvalue lua i push lua ("PANDOC_FILTER_FUNCTIONS"::String) Lua.rawget lua Lua.registryindex -- cgit v1.2.3 From bd5a7e525800b41752e422dc9fb6e47ed8bf4479 Mon Sep 17 00:00:00 2001 From: Albert Krewinkel Date: Tue, 20 Jun 2017 19:20:50 +0200 Subject: Lua: apply hslint suggestions --- src/Text/Pandoc/Lua.hs | 65 +++++++++++++++++++++++++------------------------- 1 file changed, 32 insertions(+), 33 deletions(-) (limited to 'src/Text/Pandoc/Lua.hs') diff --git a/src/Text/Pandoc/Lua.hs b/src/Text/Pandoc/Lua.hs index e9184c7ce..7cdcfd3d3 100644 --- a/src/Text/Pandoc/Lua.hs +++ b/src/Text/Pandoc/Lua.hs @@ -46,7 +46,7 @@ import Text.Pandoc.Walk import qualified Data.Map as Map import qualified Scripting.Lua as Lua -data LuaException = LuaException String +newtype LuaException = LuaException String deriving (Show, Typeable) instance Exception LuaException @@ -65,7 +65,7 @@ runLuaFilter filterPath args pd = liftIO $ do Lua.setglobal lua "pandoc" top <- Lua.gettop lua status <- Lua.loadfile lua filterPath - if (status /= 0) + if status /= 0 then do Just luaErrMsg <- Lua.peek lua 1 throwIO (LuaException luaErrMsg) @@ -89,8 +89,7 @@ pushGlobalFilter lua = *> Lua.rawseti lua (-2) 1 runAll :: [LuaFilter] -> Pandoc -> IO Pandoc -runAll [] = return -runAll (x:xs) = walkMWithLuaFilter x >=> runAll xs +runAll = foldr ((>=>) . walkMWithLuaFilter) return walkMWithLuaFilter :: LuaFilter -> Pandoc -> IO Pandoc walkMWithLuaFilter (LuaFilter lua fnMap) = @@ -134,20 +133,20 @@ execBlockLuaFilter lua fnMap x = do Nothing -> return x Just fn -> runLuaFilterFunction lua fn x case x of - BlockQuote _ -> tryFilter "BlockQuote" - BulletList _ -> tryFilter "BulletList" - CodeBlock _ _ -> tryFilter "CodeBlock" - DefinitionList _ -> tryFilter "DefinitionList" - Div _ _ -> tryFilter "Div" - Header _ _ _ -> tryFilter "Header" + BlockQuote{} -> tryFilter "BlockQuote" + BulletList{} -> tryFilter "BulletList" + CodeBlock{} -> tryFilter "CodeBlock" + DefinitionList{} -> tryFilter "DefinitionList" + Div{} -> tryFilter "Div" + Header{} -> tryFilter "Header" HorizontalRule -> tryFilter "HorizontalRule" - LineBlock _ -> tryFilter "LineBlock" + LineBlock{} -> tryFilter "LineBlock" Null -> tryFilter "Null" - Para _ -> tryFilter "Para" - Plain _ -> tryFilter "Plain" - RawBlock _ _ -> tryFilter "RawBlock" - OrderedList _ _ -> tryFilter "OrderedList" - Table _ _ _ _ _ -> tryFilter "Table" + Para{} -> tryFilter "Para" + Plain{} -> tryFilter "Plain" + RawBlock{} -> tryFilter "RawBlock" + OrderedList{} -> tryFilter "OrderedList" + Table{} -> tryFilter "Table" execInlineLuaFilter :: LuaState -> FunctionMap @@ -165,27 +164,27 @@ execInlineLuaFilter lua fnMap x = do Nothing -> tryFilterAlternatives alternatives Just fn -> runLuaFilterFunction lua fn x case x of - Cite _ _ -> tryFilter "Cite" - Code _ _ -> tryFilter "Code" - Emph _ -> tryFilter "Emph" - Image _ _ _ -> tryFilter "Image" + Cite{} -> tryFilter "Cite" + Code{} -> tryFilter "Code" + Emph{} -> tryFilter "Emph" + Image{} -> tryFilter "Image" LineBreak -> tryFilter "LineBreak" - Link _ _ _ -> tryFilter "Link" + Link{} -> tryFilter "Link" Math DisplayMath _ -> tryFilterAlternatives ["DisplayMath", "Math"] Math InlineMath _ -> tryFilterAlternatives ["InlineMath", "Math"] - Note _ -> tryFilter "Note" + Note{} -> tryFilter "Note" Quoted DoubleQuote _ -> tryFilterAlternatives ["DoubleQuoted", "Quoted"] Quoted SingleQuote _ -> tryFilterAlternatives ["SingleQuoted", "Quoted"] - RawInline _ _ -> tryFilter "RawInline" - SmallCaps _ -> tryFilter "SmallCaps" + RawInline{} -> tryFilter "RawInline" + SmallCaps{} -> tryFilter "SmallCaps" SoftBreak -> tryFilter "SoftBreak" Space -> tryFilter "Space" - Span _ _ -> tryFilter "Span" - Str _ -> tryFilter "Str" - Strikeout _ -> tryFilter "Strikeout" - Strong _ -> tryFilter "Strong" - Subscript _ -> tryFilter "Subscript" - Superscript _ -> tryFilter "Superscript" + Span{} -> tryFilter "Span" + Str{} -> tryFilter "Str" + Strikeout{} -> tryFilter "Strikeout" + Strong{} -> tryFilter "Strong" + Subscript{} -> tryFilter "Subscript" + Superscript{} -> tryFilter "Superscript" instance StackValue LuaFilter where valuetype _ = Lua.TTABLE @@ -232,11 +231,11 @@ pushFilterFunction lua lf = do instance StackValue LuaFilterFunction where valuetype _ = Lua.TFUNCTION - push lua v = pushFilterFunction lua v + push = pushFilterFunction peek lua i = do isFn <- Lua.isfunction lua i - unless isFn (throwIO $ LuaException $ - "Not a function at index " ++ (show i)) + unless isFn . + throwIO . LuaException $ "Not a function at index " ++ show i Lua.pushvalue lua i push lua ("PANDOC_FILTER_FUNCTIONS"::String) Lua.rawget lua Lua.registryindex -- cgit v1.2.3 From f4c12606e170ffaf558d07c21514ef5dd44d1b40 Mon Sep 17 00:00:00 2001 From: Albert Krewinkel Date: Tue, 20 Jun 2017 20:51:10 +0200 Subject: Lua: use registry to store function references Using the registry directly instead of a custom table is cleaner and more efficient. The performance improvement is especially noticable when filtering on frequent elements like Str. --- src/Text/Pandoc/Lua.hs | 48 +++++++++++++++++++----------------------------- 1 file changed, 19 insertions(+), 29 deletions(-) (limited to 'src/Text/Pandoc/Lua.hs') diff --git a/src/Text/Pandoc/Lua.hs b/src/Text/Pandoc/Lua.hs index 7cdcfd3d3..f965bd95d 100644 --- a/src/Text/Pandoc/Lua.hs +++ b/src/Text/Pandoc/Lua.hs @@ -56,10 +56,6 @@ runLuaFilter :: (MonadIO m) runLuaFilter filterPath args pd = liftIO $ do lua <- Lua.newstate Lua.openlibs lua - -- create table in registry to store filter functions - Lua.push lua "PANDOC_FILTER_FUNCTIONS" - Lua.newtable lua - Lua.rawset lua Lua.registryindex -- store module in global "pandoc" pushPandocModule lua Lua.setglobal lua "pandoc" @@ -110,7 +106,7 @@ execDocLuaFilter lua fnMap x = do let docFnName = "Doc" case Map.lookup docFnName fnMap of Nothing -> return x - Just fn -> runLuaFilterFunction lua fn x + Just fn -> runFilterFunction lua fn x execMetaLuaFilter :: LuaState -> FunctionMap @@ -120,7 +116,7 @@ execMetaLuaFilter lua fnMap pd@(Pandoc meta blks) = do case Map.lookup metaFnName fnMap of Nothing -> return pd Just fn -> do - meta' <- runLuaFilterFunction lua fn meta + meta' <- runFilterFunction lua fn meta return $ Pandoc meta' blks execBlockLuaFilter :: LuaState @@ -131,7 +127,7 @@ execBlockLuaFilter lua fnMap x = do tryFilter filterFnName = case Map.lookup filterFnName fnMap of Nothing -> return x - Just fn -> runLuaFilterFunction lua fn x + Just fn -> runFilterFunction lua fn x case x of BlockQuote{} -> tryFilter "BlockQuote" BulletList{} -> tryFilter "BulletList" @@ -156,13 +152,13 @@ execInlineLuaFilter lua fnMap x = do tryFilter filterFnName = case Map.lookup filterFnName fnMap of Nothing -> return x - Just fn -> runLuaFilterFunction lua fn x + Just fn -> runFilterFunction lua fn x let tryFilterAlternatives :: [String] -> IO Inline tryFilterAlternatives [] = return x tryFilterAlternatives (fnName : alternatives) = case Map.lookup fnName fnMap of Nothing -> tryFilterAlternatives alternatives - Just fn -> runLuaFilterFunction lua fn x + Just fn -> runFilterFunction lua fn x case x of Cite{} -> tryFilter "Cite" Code{} -> tryFilter "Code" @@ -213,34 +209,28 @@ instance (StackValue a, PushViaFilterFunction b) => pushViaFilterFunction' lua lf pushArgs num x = pushViaFilterFunction' lua lf (pushArgs *> push lua x) (num + 1) --- | Push an value to the stack via a lua filter function. The function is +-- | Push a value to the stack via a lua filter function. The filter function is -- called with all arguments that are passed to this function and is expected to -- return a single value. -runLuaFilterFunction :: PushViaFilterFunction a +runFilterFunction :: PushViaFilterFunction a => LuaState -> LuaFilterFunction -> a -runLuaFilterFunction lua lf = pushViaFilterFunction' lua lf (return ()) 0 +runFilterFunction lua lf = pushViaFilterFunction' lua lf (return ()) 0 -- | Push the filter function to the top of the stack. pushFilterFunction :: Lua.LuaState -> LuaFilterFunction -> IO () -pushFilterFunction lua lf = do +pushFilterFunction lua lf = -- The function is stored in a lua registry table, retrieve it from there. - push lua ("PANDOC_FILTER_FUNCTIONS"::String) - Lua.rawget lua Lua.registryindex - Lua.rawgeti lua (-1) (functionIndex lf) - Lua.remove lua (-2) -- remove registry table from stack + Lua.rawgeti lua Lua.registryindex (functionIndex lf) + +registerFilterFunction :: LuaState -> Int -> IO LuaFilterFunction +registerFilterFunction lua idx = do + isFn <- Lua.isfunction lua idx + unless isFn . throwIO . LuaException $ "Not a function at index " ++ show idx + Lua.pushvalue lua idx + refIdx <- Lua.ref lua Lua.registryindex + return $ LuaFilterFunction refIdx instance StackValue LuaFilterFunction where valuetype _ = Lua.TFUNCTION push = pushFilterFunction - peek lua i = do - isFn <- Lua.isfunction lua i - unless isFn . - throwIO . LuaException $ "Not a function at index " ++ show i - Lua.pushvalue lua i - push lua ("PANDOC_FILTER_FUNCTIONS"::String) - Lua.rawget lua Lua.registryindex - len <- Lua.objlen lua (-1) - Lua.insert lua (-2) - Lua.rawseti lua (-2) (len + 1) - Lua.pop lua 1 - return . Just $ LuaFilterFunction (len + 1) + peek = fmap (fmap Just) . registerFilterFunction -- cgit v1.2.3 From 5ec84bfeb42e73acb4e309ccde34905b3254fb5c Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Tue, 20 Jun 2017 21:11:01 +0200 Subject: Text.Pandoc.Lua - added DeriveDataTypeable for ghc 7.8. --- src/Text/Pandoc/Lua.hs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'src/Text/Pandoc/Lua.hs') diff --git a/src/Text/Pandoc/Lua.hs b/src/Text/Pandoc/Lua.hs index f965bd95d..90f72d685 100644 --- a/src/Text/Pandoc/Lua.hs +++ b/src/Text/Pandoc/Lua.hs @@ -1,3 +1,7 @@ +{-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE FlexibleInstances #-} +{-# LANGUAGE MultiParamTypeClasses #-} +{-# LANGUAGE DeriveDataTypeable #-} {- Copyright © 2017 Albert Krewinkel @@ -15,9 +19,6 @@ You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -} -{-# LANGUAGE FlexibleContexts #-} -{-# LANGUAGE FlexibleInstances #-} -{-# LANGUAGE MultiParamTypeClasses #-} {- | Module : Text.Pandoc.Lua Copyright : Copyright © 2017 Albert Krewinkel -- cgit v1.2.3 From beb78a552cb3480d55b8eca8c0c77bccd5804506 Mon Sep 17 00:00:00 2001 From: Albert Krewinkel Date: Tue, 27 Jun 2017 17:11:42 +0200 Subject: Text.Pandoc.Lua: simplify filter function runner The code still allowed to pass an arbitrary number of arguments to the filter function, as element properties were passed as function arguments at some point. Now we only pass the element as the single arg, so the code to handle multiple arguments is no longer necessary. --- src/Text/Pandoc/Lua.hs | 36 +++++++++++------------------------- 1 file changed, 11 insertions(+), 25 deletions(-) (limited to 'src/Text/Pandoc/Lua.hs') diff --git a/src/Text/Pandoc/Lua.hs b/src/Text/Pandoc/Lua.hs index 90f72d685..858212df1 100644 --- a/src/Text/Pandoc/Lua.hs +++ b/src/Text/Pandoc/Lua.hs @@ -188,34 +188,20 @@ instance StackValue LuaFilter where push = undefined peek lua idx = fmap (LuaFilter lua) <$> Lua.peek lua idx --- | Helper class for pushing a single value to the stack via a lua function. --- See @pushViaCall@. -class PushViaFilterFunction a where - pushViaFilterFunction' :: LuaState -> LuaFilterFunction -> IO () -> Int -> a - -instance StackValue a => PushViaFilterFunction (IO a) where - pushViaFilterFunction' lua lf pushArgs num = do - pushFilterFunction lua lf - pushArgs - Lua.call lua num 1 - mbres <- Lua.peek lua (-1) - case mbres of - Nothing -> throwIO $ LuaException - ("Error while trying to get a filter's return " - ++ "value from lua stack.") - Just res -> res <$ Lua.pop lua 1 - -instance (StackValue a, PushViaFilterFunction b) => - PushViaFilterFunction (a -> b) where - pushViaFilterFunction' lua lf pushArgs num x = - pushViaFilterFunction' lua lf (pushArgs *> push lua x) (num + 1) - -- | Push a value to the stack via a lua filter function. The filter function is -- called with all arguments that are passed to this function and is expected to -- return a single value. -runFilterFunction :: PushViaFilterFunction a - => LuaState -> LuaFilterFunction -> a -runFilterFunction lua lf = pushViaFilterFunction' lua lf (return ()) 0 +runFilterFunction :: StackValue a => LuaState -> LuaFilterFunction -> a -> IO a +runFilterFunction lua lf x = do + pushFilterFunction lua lf + Lua.push lua x + Lua.call lua 1 1 + mbres <- Lua.peek lua (-1) + case mbres of + Nothing -> throwIO $ LuaException + ("Error while trying to get a filter's return " + ++ "value from lua stack.") + Just res -> res <$ Lua.pop lua 1 -- | Push the filter function to the top of the stack. pushFilterFunction :: Lua.LuaState -> LuaFilterFunction -> IO () -- cgit v1.2.3 From 4282abbd0781cf5e6731a9b43dc8cfeb1dca58fa Mon Sep 17 00:00:00 2001 From: Albert Krewinkel Date: Tue, 27 Jun 2017 17:11:42 +0200 Subject: Text.Pandoc.Lua: keep element unchanged if filter returns nil This was suggested by jgm and is consistent with the behavior of other filtering libraries. --- src/Text/Pandoc/Lua.hs | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) (limited to 'src/Text/Pandoc/Lua.hs') diff --git a/src/Text/Pandoc/Lua.hs b/src/Text/Pandoc/Lua.hs index 858212df1..3770880f3 100644 --- a/src/Text/Pandoc/Lua.hs +++ b/src/Text/Pandoc/Lua.hs @@ -189,19 +189,24 @@ instance StackValue LuaFilter where peek lua idx = fmap (LuaFilter lua) <$> Lua.peek lua idx -- | Push a value to the stack via a lua filter function. The filter function is --- called with all arguments that are passed to this function and is expected to --- return a single value. +-- called with given element as argument and is expected to return an element. +-- Alternatively, the function can return nothing or nil, in which case the +-- element is left unchanged. runFilterFunction :: StackValue a => LuaState -> LuaFilterFunction -> a -> IO a runFilterFunction lua lf x = do pushFilterFunction lua lf Lua.push lua x Lua.call lua 1 1 - mbres <- Lua.peek lua (-1) - case mbres of - Nothing -> throwIO $ LuaException - ("Error while trying to get a filter's return " - ++ "value from lua stack.") - Just res -> res <$ Lua.pop lua 1 + resType <- Lua.ltype lua (-1) + case resType of + Lua.TNIL -> Lua.pop lua 1 *> return x + _ -> do + mbres <- Lua.peek lua (-1) + case mbres of + Nothing -> throwIO $ LuaException + ("Error while trying to get a filter's return " + ++ "value from lua stack.") + Just res -> res <$ Lua.pop lua 1 -- | Push the filter function to the top of the stack. pushFilterFunction :: Lua.LuaState -> LuaFilterFunction -> IO () -- cgit v1.2.3 From f5f84859230568ddafb2e7e23b5d9b3e98fdbba5 Mon Sep 17 00:00:00 2001 From: Albert Krewinkel Date: Tue, 27 Jun 2017 17:55:47 +0200 Subject: Text.Pandoc.Lua: catch lua errors in filter functions Replace lua errors with `LuaException`s. --- src/Text/Pandoc/Lua.hs | 31 ++++++++++++++++++++----------- 1 file changed, 20 insertions(+), 11 deletions(-) (limited to 'src/Text/Pandoc/Lua.hs') diff --git a/src/Text/Pandoc/Lua.hs b/src/Text/Pandoc/Lua.hs index 3770880f3..2ee8d0847 100644 --- a/src/Text/Pandoc/Lua.hs +++ b/src/Text/Pandoc/Lua.hs @@ -196,17 +196,26 @@ runFilterFunction :: StackValue a => LuaState -> LuaFilterFunction -> a -> IO a runFilterFunction lua lf x = do pushFilterFunction lua lf Lua.push lua x - Lua.call lua 1 1 - resType <- Lua.ltype lua (-1) - case resType of - Lua.TNIL -> Lua.pop lua 1 *> return x - _ -> do - mbres <- Lua.peek lua (-1) - case mbres of - Nothing -> throwIO $ LuaException - ("Error while trying to get a filter's return " - ++ "value from lua stack.") - Just res -> res <$ Lua.pop lua 1 + z <- Lua.pcall lua 1 1 0 + if (z /= 0) + then do + msg <- Lua.peek lua (-1) + let prefix = "Error while running filter function: " + throwIO . LuaException $ + case msg of + Nothing -> prefix ++ "could not read error message" + Just msg' -> prefix ++ msg' + else do + resType <- Lua.ltype lua (-1) + case resType of + Lua.TNIL -> Lua.pop lua 1 *> return x + _ -> do + mbres <- Lua.peek lua (-1) + case mbres of + Nothing -> throwIO $ LuaException + ("Error while trying to get a filter's return " + ++ "value from lua stack.") + Just res -> res <$ Lua.pop lua 1 -- | Push the filter function to the top of the stack. pushFilterFunction :: Lua.LuaState -> LuaFilterFunction -> IO () -- cgit v1.2.3 From 6ad74815f66cb36ec4039c597b38473db853eb6c Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Thu, 29 Jun 2017 14:00:26 +0200 Subject: Text.Pandoc.Lua: use generics to reduce boilerplate. I tested this with the str.lua filter on MANUAL.txt, and I could see no significant performance degradation. Doing things this way will ease maintenance, as we won't have to manually modify this module when types change. @tarleb, do we really need special cases for things like DoubleQuoted and InlineMath? --- src/Text/Pandoc/Lua.hs | 35 +++-------------------------------- 1 file changed, 3 insertions(+), 32 deletions(-) (limited to 'src/Text/Pandoc/Lua.hs') diff --git a/src/Text/Pandoc/Lua.hs b/src/Text/Pandoc/Lua.hs index 2ee8d0847..85a080277 100644 --- a/src/Text/Pandoc/Lua.hs +++ b/src/Text/Pandoc/Lua.hs @@ -36,6 +36,7 @@ module Text.Pandoc.Lua ( LuaException(..), import Control.Exception import Control.Monad (unless, when, (>=>)) import Control.Monad.Trans (MonadIO (..)) +import Data.Data (toConstr) import Data.Map (Map) import Data.Typeable (Typeable) import Scripting.Lua (LuaState, StackValue (..)) @@ -129,21 +130,7 @@ execBlockLuaFilter lua fnMap x = do case Map.lookup filterFnName fnMap of Nothing -> return x Just fn -> runFilterFunction lua fn x - case x of - BlockQuote{} -> tryFilter "BlockQuote" - BulletList{} -> tryFilter "BulletList" - CodeBlock{} -> tryFilter "CodeBlock" - DefinitionList{} -> tryFilter "DefinitionList" - Div{} -> tryFilter "Div" - Header{} -> tryFilter "Header" - HorizontalRule -> tryFilter "HorizontalRule" - LineBlock{} -> tryFilter "LineBlock" - Null -> tryFilter "Null" - Para{} -> tryFilter "Para" - Plain{} -> tryFilter "Plain" - RawBlock{} -> tryFilter "RawBlock" - OrderedList{} -> tryFilter "OrderedList" - Table{} -> tryFilter "Table" + tryFilter (show (toConstr x)) execInlineLuaFilter :: LuaState -> FunctionMap @@ -161,27 +148,11 @@ execInlineLuaFilter lua fnMap x = do Nothing -> tryFilterAlternatives alternatives Just fn -> runFilterFunction lua fn x case x of - Cite{} -> tryFilter "Cite" - Code{} -> tryFilter "Code" - Emph{} -> tryFilter "Emph" - Image{} -> tryFilter "Image" - LineBreak -> tryFilter "LineBreak" - Link{} -> tryFilter "Link" Math DisplayMath _ -> tryFilterAlternatives ["DisplayMath", "Math"] Math InlineMath _ -> tryFilterAlternatives ["InlineMath", "Math"] - Note{} -> tryFilter "Note" Quoted DoubleQuote _ -> tryFilterAlternatives ["DoubleQuoted", "Quoted"] Quoted SingleQuote _ -> tryFilterAlternatives ["SingleQuoted", "Quoted"] - RawInline{} -> tryFilter "RawInline" - SmallCaps{} -> tryFilter "SmallCaps" - SoftBreak -> tryFilter "SoftBreak" - Space -> tryFilter "Space" - Span{} -> tryFilter "Span" - Str{} -> tryFilter "Str" - Strikeout{} -> tryFilter "Strikeout" - Strong{} -> tryFilter "Strong" - Subscript{} -> tryFilter "Subscript" - Superscript{} -> tryFilter "Superscript" + _ -> tryFilter (show (toConstr x)) instance StackValue LuaFilter where valuetype _ = Lua.TTABLE -- cgit v1.2.3 From 5c80aca0e20492eaa31b9280fb5524d76f5e8098 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Thu, 29 Jun 2017 14:31:02 +0200 Subject: Text.Pandoc.Lua: refactored to remove duplicated code. --- src/Text/Pandoc/Lua.hs | 59 +++++++++++++++++++++----------------------------- 1 file changed, 25 insertions(+), 34 deletions(-) (limited to 'src/Text/Pandoc/Lua.hs') diff --git a/src/Text/Pandoc/Lua.hs b/src/Text/Pandoc/Lua.hs index 85a080277..3bb11b705 100644 --- a/src/Text/Pandoc/Lua.hs +++ b/src/Text/Pandoc/Lua.hs @@ -104,55 +104,46 @@ newtype LuaFilterFunction = LuaFilterFunction { functionIndex :: Int } execDocLuaFilter :: LuaState -> FunctionMap -> Pandoc -> IO Pandoc -execDocLuaFilter lua fnMap x = do - let docFnName = "Doc" - case Map.lookup docFnName fnMap of - Nothing -> return x - Just fn -> runFilterFunction lua fn x +execDocLuaFilter lua fnMap = tryFilter lua fnMap "Doc" execMetaLuaFilter :: LuaState -> FunctionMap -> Pandoc -> IO Pandoc -execMetaLuaFilter lua fnMap pd@(Pandoc meta blks) = do - let metaFnName = "Meta" - case Map.lookup metaFnName fnMap of - Nothing -> return pd - Just fn -> do - meta' <- runFilterFunction lua fn meta - return $ Pandoc meta' blks +execMetaLuaFilter lua fnMap (Pandoc meta blks) = do + meta' <- tryFilter lua fnMap "Meta" meta + return $ Pandoc meta' blks execBlockLuaFilter :: LuaState -> FunctionMap -> Block -> IO Block execBlockLuaFilter lua fnMap x = do - let tryFilter :: String -> IO Block - tryFilter filterFnName = - case Map.lookup filterFnName fnMap of - Nothing -> return x - Just fn -> runFilterFunction lua fn x - tryFilter (show (toConstr x)) + tryFilter lua fnMap (show (toConstr x)) x + +tryFilter :: StackValue a => LuaState -> FunctionMap -> String -> a -> IO a +tryFilter lua fnMap filterFnName x = + case Map.lookup filterFnName fnMap of + Nothing -> return x + Just fn -> runFilterFunction lua fn x + +tryFilterAlternatives :: StackValue a + => LuaState -> FunctionMap -> [String] -> a -> IO a +tryFilterAlternatives _ _ [] x = return x +tryFilterAlternatives lua fnMap (fnName : alternatives) x = + case Map.lookup fnName fnMap of + Nothing -> tryFilterAlternatives lua fnMap alternatives x + Just fn -> runFilterFunction lua fn x execInlineLuaFilter :: LuaState -> FunctionMap -> Inline -> IO Inline execInlineLuaFilter lua fnMap x = do - let tryFilter :: String -> IO Inline - tryFilter filterFnName = - case Map.lookup filterFnName fnMap of - Nothing -> return x - Just fn -> runFilterFunction lua fn x - let tryFilterAlternatives :: [String] -> IO Inline - tryFilterAlternatives [] = return x - tryFilterAlternatives (fnName : alternatives) = - case Map.lookup fnName fnMap of - Nothing -> tryFilterAlternatives alternatives - Just fn -> runFilterFunction lua fn x + let tryAlt = tryFilterAlternatives lua fnMap case x of - Math DisplayMath _ -> tryFilterAlternatives ["DisplayMath", "Math"] - Math InlineMath _ -> tryFilterAlternatives ["InlineMath", "Math"] - Quoted DoubleQuote _ -> tryFilterAlternatives ["DoubleQuoted", "Quoted"] - Quoted SingleQuote _ -> tryFilterAlternatives ["SingleQuoted", "Quoted"] - _ -> tryFilter (show (toConstr x)) + Math DisplayMath _ -> tryAlt ["DisplayMath", "Math"] x + Math InlineMath _ -> tryAlt ["InlineMath", "Math"] x + Quoted DoubleQuote _ -> tryAlt ["DoubleQuoted", "Quoted"] x + Quoted SingleQuote _ -> tryAlt ["SingleQuoted", "Quoted"] x + _ -> tryFilter lua fnMap (show (toConstr x)) x instance StackValue LuaFilter where valuetype _ = Lua.TTABLE -- cgit v1.2.3 From 780a65f8a87b40d1a9ee269cd7a51699c42d497e Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Thu, 29 Jun 2017 15:47:27 +0200 Subject: Lua filters: Remove special treatment of Quoted, Math. No more SingleQuoted, DoubleQuoted, InlineMath, DisplayMath. This makes everything uniform and predictable, though it does open up a difference btw lua filters and custom writers. --- src/Text/Pandoc/Lua.hs | 32 ++++++++------------------------ 1 file changed, 8 insertions(+), 24 deletions(-) (limited to 'src/Text/Pandoc/Lua.hs') diff --git a/src/Text/Pandoc/Lua.hs b/src/Text/Pandoc/Lua.hs index 3bb11b705..fd7bba0ac 100644 --- a/src/Text/Pandoc/Lua.hs +++ b/src/Text/Pandoc/Lua.hs @@ -101,6 +101,12 @@ data LuaFilter = LuaFilter LuaState FunctionMap newtype LuaFilterFunction = LuaFilterFunction { functionIndex :: Int } +tryFilter :: StackValue a => LuaState -> FunctionMap -> String -> a -> IO a +tryFilter lua fnMap filterFnName x = + case Map.lookup filterFnName fnMap of + Nothing -> return x + Just fn -> runFilterFunction lua fn x + execDocLuaFilter :: LuaState -> FunctionMap -> Pandoc -> IO Pandoc @@ -116,34 +122,12 @@ execMetaLuaFilter lua fnMap (Pandoc meta blks) = do execBlockLuaFilter :: LuaState -> FunctionMap -> Block -> IO Block -execBlockLuaFilter lua fnMap x = do - tryFilter lua fnMap (show (toConstr x)) x - -tryFilter :: StackValue a => LuaState -> FunctionMap -> String -> a -> IO a -tryFilter lua fnMap filterFnName x = - case Map.lookup filterFnName fnMap of - Nothing -> return x - Just fn -> runFilterFunction lua fn x - -tryFilterAlternatives :: StackValue a - => LuaState -> FunctionMap -> [String] -> a -> IO a -tryFilterAlternatives _ _ [] x = return x -tryFilterAlternatives lua fnMap (fnName : alternatives) x = - case Map.lookup fnName fnMap of - Nothing -> tryFilterAlternatives lua fnMap alternatives x - Just fn -> runFilterFunction lua fn x +execBlockLuaFilter lua fnMap x = tryFilter lua fnMap (show (toConstr x)) x execInlineLuaFilter :: LuaState -> FunctionMap -> Inline -> IO Inline -execInlineLuaFilter lua fnMap x = do - let tryAlt = tryFilterAlternatives lua fnMap - case x of - Math DisplayMath _ -> tryAlt ["DisplayMath", "Math"] x - Math InlineMath _ -> tryAlt ["InlineMath", "Math"] x - Quoted DoubleQuote _ -> tryAlt ["DoubleQuoted", "Quoted"] x - Quoted SingleQuote _ -> tryAlt ["SingleQuoted", "Quoted"] x - _ -> tryFilter lua fnMap (show (toConstr x)) x +execInlineLuaFilter lua fnMap x = tryFilter lua fnMap (show (toConstr x)) x instance StackValue LuaFilter where valuetype _ = Lua.TTABLE -- cgit v1.2.3 From cb25326fa313690c3c67caa2a8b44642409fd24c Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Thu, 29 Jun 2017 17:07:30 +0200 Subject: Text.Pandoc.Lua: more code simplification. Also, now we check before running walkM that the function table actually does contain something relevant. E.g. if your filter just defines Str, there's no need to run walkM for blocks, meta, or the whole document. This should help performance a bit (and it does, in my tests). --- src/Text/Pandoc/Lua.hs | 56 +++++++++++++++++++++++--------------------------- 1 file changed, 26 insertions(+), 30 deletions(-) (limited to 'src/Text/Pandoc/Lua.hs') diff --git a/src/Text/Pandoc/Lua.hs b/src/Text/Pandoc/Lua.hs index fd7bba0ac..87fb8fd6b 100644 --- a/src/Text/Pandoc/Lua.hs +++ b/src/Text/Pandoc/Lua.hs @@ -2,6 +2,7 @@ {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE DeriveDataTypeable #-} +{-# LANGUAGE ScopedTypeVariables #-} {- Copyright © 2017 Albert Krewinkel @@ -34,10 +35,11 @@ module Text.Pandoc.Lua ( LuaException(..), pushPandocModule ) where import Control.Exception -import Control.Monad (unless, when, (>=>)) +import Control.Monad (unless, when, (>=>), mplus) import Control.Monad.Trans (MonadIO (..)) -import Data.Data (toConstr) +import Data.Data (toConstr, showConstr, dataTypeOf, dataTypeConstrs, Data) import Data.Map (Map) +import Data.Maybe (isJust) import Data.Typeable (Typeable) import Scripting.Lua (LuaState, StackValue (..)) import Text.Pandoc.Definition @@ -91,44 +93,38 @@ runAll = foldr ((>=>) . walkMWithLuaFilter) return walkMWithLuaFilter :: LuaFilter -> Pandoc -> IO Pandoc walkMWithLuaFilter (LuaFilter lua fnMap) = - walkM (execInlineLuaFilter lua fnMap) >=> - walkM (execBlockLuaFilter lua fnMap) >=> - walkM (execMetaLuaFilter lua fnMap) >=> - walkM (execDocLuaFilter lua fnMap) + (if hasOneOf (constructorsFor (dataTypeOf (Str []))) + then walkM (tryFilter lua fnMap :: Inline -> IO Inline) + else return) + >=> + (if hasOneOf (constructorsFor (dataTypeOf (Para []))) + then walkM (tryFilter lua fnMap :: Block -> IO Block) + else return) + >=> + (case Map.lookup "Meta" fnMap of + Just fn -> walkM (\(Pandoc meta blocks) -> do + meta' <- runFilterFunction lua fn meta + return $ Pandoc meta' blocks) + Nothing -> return) + >=> + (case Map.lookup "Pandoc" fnMap `mplus` Map.lookup "Doc" fnMap of + Just fn -> (runFilterFunction lua fn) :: Pandoc -> IO Pandoc + Nothing -> return) + where hasOneOf = any (\k -> isJust (Map.lookup k fnMap)) + constructorsFor x = map show (dataTypeConstrs x) type FunctionMap = Map String LuaFilterFunction data LuaFilter = LuaFilter LuaState FunctionMap newtype LuaFilterFunction = LuaFilterFunction { functionIndex :: Int } -tryFilter :: StackValue a => LuaState -> FunctionMap -> String -> a -> IO a -tryFilter lua fnMap filterFnName x = +tryFilter :: (Data a, StackValue a) => LuaState -> FunctionMap -> a -> IO a +tryFilter lua fnMap x = + let filterFnName = showConstr (toConstr x) in case Map.lookup filterFnName fnMap of Nothing -> return x Just fn -> runFilterFunction lua fn x -execDocLuaFilter :: LuaState - -> FunctionMap - -> Pandoc -> IO Pandoc -execDocLuaFilter lua fnMap = tryFilter lua fnMap "Doc" - -execMetaLuaFilter :: LuaState - -> FunctionMap - -> Pandoc -> IO Pandoc -execMetaLuaFilter lua fnMap (Pandoc meta blks) = do - meta' <- tryFilter lua fnMap "Meta" meta - return $ Pandoc meta' blks - -execBlockLuaFilter :: LuaState - -> FunctionMap - -> Block -> IO Block -execBlockLuaFilter lua fnMap x = tryFilter lua fnMap (show (toConstr x)) x - -execInlineLuaFilter :: LuaState - -> FunctionMap - -> Inline -> IO Inline -execInlineLuaFilter lua fnMap x = tryFilter lua fnMap (show (toConstr x)) x - instance StackValue LuaFilter where valuetype _ = Lua.TTABLE push = undefined -- cgit v1.2.3 From 5e00cf8086e0960e81c31f7cd981ace646623f09 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Thu, 29 Jun 2017 17:13:19 +0200 Subject: Added parameter for user data directory to runLuaFilter. in Text.Pandoc.Lua. Also to pushPandocModule. This change allows users to override pandoc.lua with a file in their local data directory, adding custom functions, etc. @tarleb, if you think this is a bad idea, you can revert this. But in general our data files are all overridable. --- src/Text/Pandoc/Lua.hs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src/Text/Pandoc/Lua.hs') diff --git a/src/Text/Pandoc/Lua.hs b/src/Text/Pandoc/Lua.hs index 87fb8fd6b..22b68d5e0 100644 --- a/src/Text/Pandoc/Lua.hs +++ b/src/Text/Pandoc/Lua.hs @@ -56,12 +56,12 @@ newtype LuaException = LuaException String instance Exception LuaException runLuaFilter :: (MonadIO m) - => FilePath -> [String] -> Pandoc -> m Pandoc -runLuaFilter filterPath args pd = liftIO $ do + => Maybe FilePath -> FilePath -> [String] -> Pandoc -> m Pandoc +runLuaFilter datadir filterPath args pd = liftIO $ do lua <- Lua.newstate Lua.openlibs lua -- store module in global "pandoc" - pushPandocModule lua + pushPandocModule datadir lua Lua.setglobal lua "pandoc" top <- Lua.gettop lua status <- Lua.loadfile lua filterPath -- cgit v1.2.3 From 2dc3dbd68b557cbd8974b9daf84df3d26ab5f843 Mon Sep 17 00:00:00 2001 From: Albert Krewinkel Date: Sun, 13 Aug 2017 12:37:10 +0200 Subject: Use hslua >= 0.7, update Lua code --- src/Text/Pandoc/Lua.hs | 181 ++++++++++++++++++++++++------------------------- 1 file changed, 90 insertions(+), 91 deletions(-) (limited to 'src/Text/Pandoc/Lua.hs') diff --git a/src/Text/Pandoc/Lua.hs b/src/Text/Pandoc/Lua.hs index 22b68d5e0..c5770a18b 100644 --- a/src/Text/Pandoc/Lua.hs +++ b/src/Text/Pandoc/Lua.hs @@ -41,14 +41,16 @@ import Data.Data (toConstr, showConstr, dataTypeOf, dataTypeConstrs, Data) import Data.Map (Map) import Data.Maybe (isJust) import Data.Typeable (Typeable) -import Scripting.Lua (LuaState, StackValue (..)) +import Foreign.Lua (Lua, FromLuaStack (..), ToLuaStack (..), runLua, + peekEither, getglobal', throwLuaError) +import Foreign.Lua.Types.Lua (runLuaWith, liftLua1) +import Foreign.Lua.Api import Text.Pandoc.Definition import Text.Pandoc.Lua.PandocModule (pushPandocModule) import Text.Pandoc.Lua.StackInstances () import Text.Pandoc.Walk import qualified Data.Map as Map -import qualified Scripting.Lua as Lua newtype LuaException = LuaException String deriving (Show, Typeable) @@ -57,123 +59,120 @@ instance Exception LuaException runLuaFilter :: (MonadIO m) => Maybe FilePath -> FilePath -> [String] -> Pandoc -> m Pandoc -runLuaFilter datadir filterPath args pd = liftIO $ do - lua <- Lua.newstate - Lua.openlibs lua +runLuaFilter datadir filterPath args pd = liftIO . runLua $ do + openlibs -- store module in global "pandoc" - pushPandocModule datadir lua - Lua.setglobal lua "pandoc" - top <- Lua.gettop lua - status <- Lua.loadfile lua filterPath - if status /= 0 + pushPandocModule datadir + setglobal "pandoc" + top <- gettop + stat<- dofile filterPath + if stat /= OK then do - Just luaErrMsg <- Lua.peek lua 1 - throwIO (LuaException luaErrMsg) + luaErrMsg <- peek (-1) <* pop 1 + throwLuaError luaErrMsg else do - Lua.call lua 0 Lua.multret - newtop <- Lua.gettop lua + newtop <- gettop -- Use the implicitly defined global filter if nothing was returned - when (newtop - top < 1) $ pushGlobalFilter lua - Just luaFilters <- Lua.peek lua (-1) - Lua.push lua args - Lua.setglobal lua "PandocParameters" - doc <- runAll luaFilters pd - Lua.close lua - return doc - -pushGlobalFilter :: LuaState -> IO () -pushGlobalFilter lua = - Lua.newtable lua - *> Lua.getglobal2 lua "pandoc.global_filter" - *> Lua.call lua 0 1 - *> Lua.rawseti lua (-2) 1 - -runAll :: [LuaFilter] -> Pandoc -> IO Pandoc + when (newtop - top < 1) $ pushGlobalFilter + luaFilters <- peek (-1) + push args + setglobal "PandocParameters" + runAll luaFilters pd + +pushGlobalFilter :: Lua () +pushGlobalFilter = do + newtable + getglobal' "pandoc.global_filter" + call 0 1 + rawseti (-2) 1 + +runAll :: [LuaFilter] -> Pandoc -> Lua Pandoc runAll = foldr ((>=>) . walkMWithLuaFilter) return -walkMWithLuaFilter :: LuaFilter -> Pandoc -> IO Pandoc -walkMWithLuaFilter (LuaFilter lua fnMap) = - (if hasOneOf (constructorsFor (dataTypeOf (Str []))) - then walkM (tryFilter lua fnMap :: Inline -> IO Inline) - else return) - >=> - (if hasOneOf (constructorsFor (dataTypeOf (Para []))) - then walkM (tryFilter lua fnMap :: Block -> IO Block) - else return) - >=> - (case Map.lookup "Meta" fnMap of - Just fn -> walkM (\(Pandoc meta blocks) -> do - meta' <- runFilterFunction lua fn meta - return $ Pandoc meta' blocks) - Nothing -> return) - >=> - (case Map.lookup "Pandoc" fnMap `mplus` Map.lookup "Doc" fnMap of - Just fn -> (runFilterFunction lua fn) :: Pandoc -> IO Pandoc - Nothing -> return) - where hasOneOf = any (\k -> isJust (Map.lookup k fnMap)) - constructorsFor x = map show (dataTypeConstrs x) +walkMWithLuaFilter :: LuaFilter -> Pandoc -> Lua Pandoc +walkMWithLuaFilter (LuaFilter fnMap) = liftLua1 walkLua + where + walkLua :: LuaState -> Pandoc -> IO Pandoc + walkLua l = + (if hasOneOf (constructorsFor (dataTypeOf (Str []))) + then walkM (runLuaWith l . (tryFilter fnMap :: Inline -> Lua Inline)) + else return) + >=> + (if hasOneOf (constructorsFor (dataTypeOf (Para []))) + then walkM ((runLuaWith l . (tryFilter fnMap :: Block -> Lua Block))) + else return) + >=> + (case Map.lookup "Meta" fnMap of + Just fn -> walkM ((\(Pandoc meta blocks) -> runLuaWith l $ do + meta' <- runFilterFunction fn meta + return $ Pandoc meta' blocks)) + Nothing -> return) + >=> + (case Map.lookup "Pandoc" fnMap `mplus` Map.lookup "Doc" fnMap of + Just fn -> runLuaWith l . (runFilterFunction fn :: Pandoc -> Lua Pandoc) + Nothing -> return) + hasOneOf = any (\k -> isJust (Map.lookup k fnMap)) + constructorsFor x = map show (dataTypeConstrs x) type FunctionMap = Map String LuaFilterFunction -data LuaFilter = LuaFilter LuaState FunctionMap +data LuaFilter = LuaFilter FunctionMap newtype LuaFilterFunction = LuaFilterFunction { functionIndex :: Int } -tryFilter :: (Data a, StackValue a) => LuaState -> FunctionMap -> a -> IO a -tryFilter lua fnMap x = +-- | Try running a filter for the given element +tryFilter :: (Data a, FromLuaStack a, ToLuaStack a) => FunctionMap -> a -> Lua a +tryFilter fnMap x = let filterFnName = showConstr (toConstr x) in case Map.lookup filterFnName fnMap of Nothing -> return x - Just fn -> runFilterFunction lua fn x + Just fn -> runFilterFunction fn x -instance StackValue LuaFilter where - valuetype _ = Lua.TTABLE - push = undefined - peek lua idx = fmap (LuaFilter lua) <$> Lua.peek lua idx +instance FromLuaStack LuaFilter where + peek idx = LuaFilter <$> peek idx -- | Push a value to the stack via a lua filter function. The filter function is -- called with given element as argument and is expected to return an element. -- Alternatively, the function can return nothing or nil, in which case the -- element is left unchanged. -runFilterFunction :: StackValue a => LuaState -> LuaFilterFunction -> a -> IO a -runFilterFunction lua lf x = do - pushFilterFunction lua lf - Lua.push lua x - z <- Lua.pcall lua 1 1 0 - if (z /= 0) +runFilterFunction :: (FromLuaStack a, ToLuaStack a) + => LuaFilterFunction -> a -> Lua a +runFilterFunction lf x = do + pushFilterFunction lf + push x + z <- pcall 1 1 Nothing + if z /= OK then do - msg <- Lua.peek lua (-1) + msg <- peek (-1) let prefix = "Error while running filter function: " - throwIO . LuaException $ - case msg of - Nothing -> prefix ++ "could not read error message" - Just msg' -> prefix ++ msg' + throwLuaError $ prefix ++ msg else do - resType <- Lua.ltype lua (-1) + resType <- ltype (-1) case resType of - Lua.TNIL -> Lua.pop lua 1 *> return x - _ -> do - mbres <- Lua.peek lua (-1) + TypeNil -> pop 1 *> return x + _ -> do + mbres <- peekEither (-1) case mbres of - Nothing -> throwIO $ LuaException - ("Error while trying to get a filter's return " - ++ "value from lua stack.") - Just res -> res <$ Lua.pop lua 1 + Left err -> throwLuaError + ("Error while trying to get a filter's return " + ++ "value from lua stack.\n" ++ err) + Right res -> res <$ pop 1 -- | Push the filter function to the top of the stack. -pushFilterFunction :: Lua.LuaState -> LuaFilterFunction -> IO () -pushFilterFunction lua lf = +pushFilterFunction :: LuaFilterFunction -> Lua () +pushFilterFunction lf = -- The function is stored in a lua registry table, retrieve it from there. - Lua.rawgeti lua Lua.registryindex (functionIndex lf) - -registerFilterFunction :: LuaState -> Int -> IO LuaFilterFunction -registerFilterFunction lua idx = do - isFn <- Lua.isfunction lua idx - unless isFn . throwIO . LuaException $ "Not a function at index " ++ show idx - Lua.pushvalue lua idx - refIdx <- Lua.ref lua Lua.registryindex + rawgeti registryindex (functionIndex lf) + +registerFilterFunction :: StackIndex -> Lua LuaFilterFunction +registerFilterFunction idx = do + isFn <- isfunction idx + unless isFn . throwLuaError $ "Not a function at index " ++ show idx + pushvalue idx + refIdx <- ref registryindex return $ LuaFilterFunction refIdx -instance StackValue LuaFilterFunction where - valuetype _ = Lua.TFUNCTION +instance ToLuaStack LuaFilterFunction where push = pushFilterFunction - peek = fmap (fmap Just) . registerFilterFunction + +instance FromLuaStack LuaFilterFunction where + peek = registerFilterFunction -- cgit v1.2.3 From 6e6cee454eab678b8ad3b15edcee6e07945157ba Mon Sep 17 00:00:00 2001 From: Albert Krewinkel Date: Sun, 13 Aug 2017 14:55:33 +0200 Subject: Text.Pandoc.Lua: cleanup element walking code WalkM is general enough to work in any monad, not just IO. Also get rid of the LuaException type, sufficient to use the one defined in hslua. --- src/Text/Pandoc/Lua.hs | 48 ++++++++++++++++++++---------------------------- 1 file changed, 20 insertions(+), 28 deletions(-) (limited to 'src/Text/Pandoc/Lua.hs') diff --git a/src/Text/Pandoc/Lua.hs b/src/Text/Pandoc/Lua.hs index c5770a18b..264364006 100644 --- a/src/Text/Pandoc/Lua.hs +++ b/src/Text/Pandoc/Lua.hs @@ -30,33 +30,25 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Pandoc lua utils. -} -module Text.Pandoc.Lua ( LuaException(..), - runLuaFilter, - pushPandocModule ) where +module Text.Pandoc.Lua (LuaException (..), pushPandocModule, runLuaFilter) where -import Control.Exception import Control.Monad (unless, when, (>=>), mplus) import Control.Monad.Trans (MonadIO (..)) import Data.Data (toConstr, showConstr, dataTypeOf, dataTypeConstrs, Data) import Data.Map (Map) import Data.Maybe (isJust) -import Data.Typeable (Typeable) -import Foreign.Lua (Lua, FromLuaStack (..), ToLuaStack (..), runLua, - peekEither, getglobal', throwLuaError) -import Foreign.Lua.Types.Lua (runLuaWith, liftLua1) -import Foreign.Lua.Api +import Foreign.Lua (Lua, FromLuaStack (peek), LuaException (..), StackIndex, + Status(OK), ToLuaStack (push), call, isnil, dofile, + getglobal', gettop, isfunction, newtable, openlibs, pcall, + peekEither, pop, pushvalue, rawgeti, rawseti, ref, + registryindex, runLua, setglobal, throwLuaError) import Text.Pandoc.Definition import Text.Pandoc.Lua.PandocModule (pushPandocModule) import Text.Pandoc.Lua.StackInstances () -import Text.Pandoc.Walk +import Text.Pandoc.Walk (Walkable (walkM)) import qualified Data.Map as Map -newtype LuaException = LuaException String - deriving (Show, Typeable) - -instance Exception LuaException - runLuaFilter :: (MonadIO m) => Maybe FilePath -> FilePath -> [String] -> Pandoc -> m Pandoc runLuaFilter datadir filterPath args pd = liftIO . runLua $ do @@ -90,26 +82,26 @@ runAll :: [LuaFilter] -> Pandoc -> Lua Pandoc runAll = foldr ((>=>) . walkMWithLuaFilter) return walkMWithLuaFilter :: LuaFilter -> Pandoc -> Lua Pandoc -walkMWithLuaFilter (LuaFilter fnMap) = liftLua1 walkLua +walkMWithLuaFilter (LuaFilter fnMap) = walkLua where - walkLua :: LuaState -> Pandoc -> IO Pandoc - walkLua l = + walkLua :: Pandoc -> Lua Pandoc + walkLua = (if hasOneOf (constructorsFor (dataTypeOf (Str []))) - then walkM (runLuaWith l . (tryFilter fnMap :: Inline -> Lua Inline)) + then walkM (tryFilter fnMap :: Inline -> Lua Inline) else return) >=> (if hasOneOf (constructorsFor (dataTypeOf (Para []))) - then walkM ((runLuaWith l . (tryFilter fnMap :: Block -> Lua Block))) + then walkM (tryFilter fnMap :: Block -> Lua Block) else return) >=> (case Map.lookup "Meta" fnMap of - Just fn -> walkM ((\(Pandoc meta blocks) -> runLuaWith l $ do - meta' <- runFilterFunction fn meta - return $ Pandoc meta' blocks)) + Just fn -> walkM (\(Pandoc meta blocks) -> do + meta' <- runFilterFunction fn meta + return $ Pandoc meta' blocks) Nothing -> return) >=> (case Map.lookup "Pandoc" fnMap `mplus` Map.lookup "Doc" fnMap of - Just fn -> runLuaWith l . (runFilterFunction fn :: Pandoc -> Lua Pandoc) + Just fn -> runFilterFunction fn :: Pandoc -> Lua Pandoc Nothing -> return) hasOneOf = any (\k -> isJust (Map.lookup k fnMap)) constructorsFor x = map show (dataTypeConstrs x) @@ -146,10 +138,10 @@ runFilterFunction lf x = do let prefix = "Error while running filter function: " throwLuaError $ prefix ++ msg else do - resType <- ltype (-1) - case resType of - TypeNil -> pop 1 *> return x - _ -> do + noExplicitFilter <- isnil (-1) + if noExplicitFilter + then pop 1 *> return x + else do mbres <- peekEither (-1) case mbres of Left err -> throwLuaError -- cgit v1.2.3 From 3d87e2080a27618e70edd1ff2d4160ff959732a6 Mon Sep 17 00:00:00 2001 From: Albert Krewinkel Date: Sun, 13 Aug 2017 17:48:43 +0200 Subject: Delete Text.Pandoc.Lua.SharedInstances Stack instances for common data types are now provides by hslua. The instance for Either was useful only for a very specific case; the function that was using the `ToLuaStack Either` instance was rewritten to work without it. Closes: #3805 --- src/Text/Pandoc/Lua.hs | 1 - 1 file changed, 1 deletion(-) (limited to 'src/Text/Pandoc/Lua.hs') diff --git a/src/Text/Pandoc/Lua.hs b/src/Text/Pandoc/Lua.hs index 264364006..6190a5fcf 100644 --- a/src/Text/Pandoc/Lua.hs +++ b/src/Text/Pandoc/Lua.hs @@ -44,7 +44,6 @@ import Foreign.Lua (Lua, FromLuaStack (peek), LuaException (..), StackIndex, registryindex, runLua, setglobal, throwLuaError) import Text.Pandoc.Definition import Text.Pandoc.Lua.PandocModule (pushPandocModule) -import Text.Pandoc.Lua.StackInstances () import Text.Pandoc.Walk (Walkable (walkM)) import qualified Data.Map as Map -- cgit v1.2.3 From 56fb854ad85dafff2016892bd6d2c5d24423bff0 Mon Sep 17 00:00:00 2001 From: Albert Krewinkel Date: Tue, 22 Aug 2017 22:02:30 +0200 Subject: Text.Pandoc.Lua: respect metatable when getting filters MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This change makes it possible to define a catch-all function using lua's metatable lookup functionality. function catch_all(el) … end return { setmetatable({}, {__index = function(_) return catch_all end}) } A further effect of this change is that the map with filter functions now only contains functions corresponding to AST element constructors. --- src/Text/Pandoc/Lua.hs | 128 +++++++++++++++++++++++++++++-------------------- 1 file changed, 76 insertions(+), 52 deletions(-) (limited to 'src/Text/Pandoc/Lua.hs') diff --git a/src/Text/Pandoc/Lua.hs b/src/Text/Pandoc/Lua.hs index 6190a5fcf..db028d325 100644 --- a/src/Text/Pandoc/Lua.hs +++ b/src/Text/Pandoc/Lua.hs @@ -32,50 +32,50 @@ Pandoc lua utils. -} module Text.Pandoc.Lua (LuaException (..), pushPandocModule, runLuaFilter) where -import Control.Monad (unless, when, (>=>), mplus) +import Control.Monad (mplus, unless, when, (>=>)) import Control.Monad.Trans (MonadIO (..)) -import Data.Data (toConstr, showConstr, dataTypeOf, dataTypeConstrs, Data) +import Data.Data (DataType, Data, toConstr, showConstr, dataTypeOf, + dataTypeConstrs) +import Data.Foldable (foldrM) import Data.Map (Map) import Data.Maybe (isJust) import Foreign.Lua (Lua, FromLuaStack (peek), LuaException (..), StackIndex, - Status(OK), ToLuaStack (push), call, isnil, dofile, - getglobal', gettop, isfunction, newtable, openlibs, pcall, - peekEither, pop, pushvalue, rawgeti, rawseti, ref, - registryindex, runLua, setglobal, throwLuaError) + Status(OK), ToLuaStack (push)) import Text.Pandoc.Definition import Text.Pandoc.Lua.PandocModule (pushPandocModule) import Text.Pandoc.Walk (Walkable (walkM)) import qualified Data.Map as Map +import qualified Foreign.Lua as Lua runLuaFilter :: (MonadIO m) => Maybe FilePath -> FilePath -> [String] -> Pandoc -> m Pandoc -runLuaFilter datadir filterPath args pd = liftIO . runLua $ do - openlibs +runLuaFilter datadir filterPath args pd = liftIO . Lua.runLua $ do + Lua.openlibs -- store module in global "pandoc" pushPandocModule datadir - setglobal "pandoc" - top <- gettop - stat<- dofile filterPath + Lua.setglobal "pandoc" + top <- Lua.gettop + stat<- Lua.dofile filterPath if stat /= OK then do - luaErrMsg <- peek (-1) <* pop 1 - throwLuaError luaErrMsg + luaErrMsg <- peek (-1) <* Lua.pop 1 + Lua.throwLuaError luaErrMsg else do - newtop <- gettop + newtop <- Lua.gettop -- Use the implicitly defined global filter if nothing was returned when (newtop - top < 1) $ pushGlobalFilter luaFilters <- peek (-1) push args - setglobal "PandocParameters" + Lua.setglobal "PandocParameters" runAll luaFilters pd pushGlobalFilter :: Lua () pushGlobalFilter = do - newtable - getglobal' "pandoc.global_filter" - call 0 1 - rawseti (-2) 1 + Lua.newtable + Lua.getglobal' "pandoc.global_filter" + Lua.call 0 1 + Lua.rawseti (-2) 1 runAll :: [LuaFilter] -> Pandoc -> Lua Pandoc runAll = foldr ((>=>) . walkMWithLuaFilter) return @@ -85,29 +85,42 @@ walkMWithLuaFilter (LuaFilter fnMap) = walkLua where walkLua :: Pandoc -> Lua Pandoc walkLua = - (if hasOneOf (constructorsFor (dataTypeOf (Str []))) - then walkM (tryFilter fnMap :: Inline -> Lua Inline) - else return) - >=> - (if hasOneOf (constructorsFor (dataTypeOf (Para []))) - then walkM (tryFilter fnMap :: Block -> Lua Block) - else return) - >=> - (case Map.lookup "Meta" fnMap of - Just fn -> walkM (\(Pandoc meta blocks) -> do - meta' <- runFilterFunction fn meta - return $ Pandoc meta' blocks) - Nothing -> return) - >=> - (case Map.lookup "Pandoc" fnMap `mplus` Map.lookup "Doc" fnMap of - Just fn -> runFilterFunction fn :: Pandoc -> Lua Pandoc - Nothing -> return) + (if hasOneOf inlineFilterNames + then walkM (tryFilter fnMap :: Inline -> Lua Inline) + else return) + >=> + (if hasOneOf blockFilterNames + then walkM (tryFilter fnMap :: Block -> Lua Block) + else return) + >=> + (case Map.lookup "Meta" fnMap of + Just fn -> walkM (\(Pandoc meta blocks) -> do + meta' <- runFilterFunction fn meta + return $ Pandoc meta' blocks) + Nothing -> return) + >=> + (case foldl mplus Nothing (map (`Map.lookup` fnMap) pandocFilterNames) of + Just fn -> runFilterFunction fn :: Pandoc -> Lua Pandoc + Nothing -> return) hasOneOf = any (\k -> isJust (Map.lookup k fnMap)) - constructorsFor x = map show (dataTypeConstrs x) -type FunctionMap = Map String LuaFilterFunction -data LuaFilter = LuaFilter FunctionMap +constructorsFor :: DataType -> [String] +constructorsFor x = map show (dataTypeConstrs x) + +inlineFilterNames :: [String] +inlineFilterNames = constructorsFor (dataTypeOf (Str [])) + +blockFilterNames :: [String] +blockFilterNames = constructorsFor (dataTypeOf (Para [])) +metaFilterName :: String +metaFilterName = "Meta" + +pandocFilterNames :: [String] +pandocFilterNames = ["Pandoc", "Doc"] + +type FunctionMap = Map String LuaFilterFunction +newtype LuaFilter = LuaFilter FunctionMap newtype LuaFilterFunction = LuaFilterFunction { functionIndex :: Int } -- | Try running a filter for the given element @@ -119,7 +132,18 @@ tryFilter fnMap x = Just fn -> runFilterFunction fn x instance FromLuaStack LuaFilter where - peek idx = LuaFilter <$> peek idx + peek idx = + let constrs = metaFilterName : pandocFilterNames + ++ blockFilterNames + ++ inlineFilterNames + fn c acc = do + Lua.getfield idx c + filterFn <- Lua.tryLua (peek (-1)) + Lua.pop 1 + return $ case filterFn of + Left _ -> acc + Right f -> (c, f) : acc + in LuaFilter . Map.fromList <$> foldrM fn [] constrs -- | Push a value to the stack via a lua filter function. The filter function is -- called with given element as argument and is expected to return an element. @@ -130,36 +154,36 @@ runFilterFunction :: (FromLuaStack a, ToLuaStack a) runFilterFunction lf x = do pushFilterFunction lf push x - z <- pcall 1 1 Nothing + z <- Lua.pcall 1 1 Nothing if z /= OK then do msg <- peek (-1) let prefix = "Error while running filter function: " - throwLuaError $ prefix ++ msg + Lua.throwLuaError $ prefix ++ msg else do - noExplicitFilter <- isnil (-1) + noExplicitFilter <- Lua.isnil (-1) if noExplicitFilter - then pop 1 *> return x + then Lua.pop 1 *> return x else do - mbres <- peekEither (-1) + mbres <- Lua.peekEither (-1) case mbres of - Left err -> throwLuaError + Left err -> Lua.throwLuaError ("Error while trying to get a filter's return " ++ "value from lua stack.\n" ++ err) - Right res -> res <$ pop 1 + Right res -> res <$ Lua.pop 1 -- | Push the filter function to the top of the stack. pushFilterFunction :: LuaFilterFunction -> Lua () pushFilterFunction lf = -- The function is stored in a lua registry table, retrieve it from there. - rawgeti registryindex (functionIndex lf) + Lua.rawgeti Lua.registryindex (functionIndex lf) registerFilterFunction :: StackIndex -> Lua LuaFilterFunction registerFilterFunction idx = do - isFn <- isfunction idx - unless isFn . throwLuaError $ "Not a function at index " ++ show idx - pushvalue idx - refIdx <- ref registryindex + isFn <- Lua.isfunction idx + unless isFn . Lua.throwLuaError $ "Not a function at index " ++ show idx + Lua.pushvalue idx + refIdx <- Lua.ref Lua.registryindex return $ LuaFilterFunction refIdx instance ToLuaStack LuaFilterFunction where -- cgit v1.2.3 From 41baaff32737e57dd9ec0a1153416ca24a12dca1 Mon Sep 17 00:00:00 2001 From: Albert Krewinkel Date: Tue, 22 Aug 2017 23:12:39 +0200 Subject: Text.Pandoc.Lua: support Inline and Block catch-alls Try function `Inline`/`Block` if no other filter function of the respective type matches an element. Closes: #3859 --- src/Text/Pandoc/Lua.hs | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) (limited to 'src/Text/Pandoc/Lua.hs') diff --git a/src/Text/Pandoc/Lua.hs b/src/Text/Pandoc/Lua.hs index db028d325..6c6676e4f 100644 --- a/src/Text/Pandoc/Lua.hs +++ b/src/Text/Pandoc/Lua.hs @@ -35,7 +35,7 @@ module Text.Pandoc.Lua (LuaException (..), pushPandocModule, runLuaFilter) where import Control.Monad (mplus, unless, when, (>=>)) import Control.Monad.Trans (MonadIO (..)) import Data.Data (DataType, Data, toConstr, showConstr, dataTypeOf, - dataTypeConstrs) + dataTypeConstrs, dataTypeName) import Data.Foldable (foldrM) import Data.Map (Map) import Data.Maybe (isJust) @@ -108,10 +108,10 @@ constructorsFor :: DataType -> [String] constructorsFor x = map show (dataTypeConstrs x) inlineFilterNames :: [String] -inlineFilterNames = constructorsFor (dataTypeOf (Str [])) +inlineFilterNames = "Inline" : constructorsFor (dataTypeOf (Str [])) blockFilterNames :: [String] -blockFilterNames = constructorsFor (dataTypeOf (Para [])) +blockFilterNames = "Block" : constructorsFor (dataTypeOf (Para [])) metaFilterName :: String metaFilterName = "Meta" @@ -126,10 +126,12 @@ newtype LuaFilterFunction = LuaFilterFunction { functionIndex :: Int } -- | Try running a filter for the given element tryFilter :: (Data a, FromLuaStack a, ToLuaStack a) => FunctionMap -> a -> Lua a tryFilter fnMap x = - let filterFnName = showConstr (toConstr x) in - case Map.lookup filterFnName fnMap of - Nothing -> return x + let filterFnName = showConstr (toConstr x) + catchAllName = dataTypeName (dataTypeOf x) + in + case Map.lookup filterFnName fnMap `mplus` Map.lookup catchAllName fnMap of Just fn -> runFilterFunction fn x + Nothing -> return x instance FromLuaStack LuaFilter where peek idx = -- cgit v1.2.3 From f8dce4a9e3a51f77597da20892fad2ca79879005 Mon Sep 17 00:00:00 2001 From: Albert Krewinkel Date: Wed, 23 Aug 2017 09:43:49 +0200 Subject: Text.Pandoc.Lua: fix fallback functions with GHC 7.8 --- src/Text/Pandoc/Lua.hs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/Text/Pandoc/Lua.hs') diff --git a/src/Text/Pandoc/Lua.hs b/src/Text/Pandoc/Lua.hs index 6c6676e4f..d6e5def4a 100644 --- a/src/Text/Pandoc/Lua.hs +++ b/src/Text/Pandoc/Lua.hs @@ -35,7 +35,7 @@ module Text.Pandoc.Lua (LuaException (..), pushPandocModule, runLuaFilter) where import Control.Monad (mplus, unless, when, (>=>)) import Control.Monad.Trans (MonadIO (..)) import Data.Data (DataType, Data, toConstr, showConstr, dataTypeOf, - dataTypeConstrs, dataTypeName) + dataTypeConstrs, dataTypeName, tyconUQname) import Data.Foldable (foldrM) import Data.Map (Map) import Data.Maybe (isJust) @@ -127,7 +127,7 @@ newtype LuaFilterFunction = LuaFilterFunction { functionIndex :: Int } tryFilter :: (Data a, FromLuaStack a, ToLuaStack a) => FunctionMap -> a -> Lua a tryFilter fnMap x = let filterFnName = showConstr (toConstr x) - catchAllName = dataTypeName (dataTypeOf x) + catchAllName = tyconUQname $ dataTypeName (dataTypeOf x) in case Map.lookup filterFnName fnMap `mplus` Map.lookup catchAllName fnMap of Just fn -> runFilterFunction fn x -- cgit v1.2.3 From 71f69cd0868f0eecf43ddb606be3074f83a8295c Mon Sep 17 00:00:00 2001 From: Albert Krewinkel Date: Tue, 12 Sep 2017 01:20:49 +0200 Subject: Allow lua filters to return lists of elements Closes: #3918 --- src/Text/Pandoc/Lua.hs | 121 ++++++++++++++++++++++++++++++------------------- 1 file changed, 75 insertions(+), 46 deletions(-) (limited to 'src/Text/Pandoc/Lua.hs') diff --git a/src/Text/Pandoc/Lua.hs b/src/Text/Pandoc/Lua.hs index d6e5def4a..477076191 100644 --- a/src/Text/Pandoc/Lua.hs +++ b/src/Text/Pandoc/Lua.hs @@ -1,8 +1,9 @@ +{-# LANGUAGE CPP #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE MultiParamTypeClasses #-} -{-# LANGUAGE DeriveDataTypeable #-} {-# LANGUAGE ScopedTypeVariables #-} +{-# OPTIONS_GHC -fno-warn-orphans #-} {- Copyright © 2017 Albert Krewinkel @@ -33,6 +34,7 @@ Pandoc lua utils. module Text.Pandoc.Lua (LuaException (..), pushPandocModule, runLuaFilter) where import Control.Monad (mplus, unless, when, (>=>)) +import Control.Monad.Identity (Identity) import Control.Monad.Trans (MonadIO (..)) import Data.Data (DataType, Data, toConstr, showConstr, dataTypeOf, dataTypeConstrs, dataTypeName, tyconUQname) @@ -40,10 +42,10 @@ import Data.Foldable (foldrM) import Data.Map (Map) import Data.Maybe (isJust) import Foreign.Lua (Lua, FromLuaStack (peek), LuaException (..), StackIndex, - Status(OK), ToLuaStack (push)) + Status (OK), ToLuaStack (push)) import Text.Pandoc.Definition import Text.Pandoc.Lua.PandocModule (pushPandocModule) -import Text.Pandoc.Walk (Walkable (walkM)) +import Text.Pandoc.Walk (walkM) import qualified Data.Map as Map import qualified Foreign.Lua as Lua @@ -56,7 +58,7 @@ runLuaFilter datadir filterPath args pd = liftIO . Lua.runLua $ do pushPandocModule datadir Lua.setglobal "pandoc" top <- Lua.gettop - stat<- Lua.dofile filterPath + stat <- Lua.dofile filterPath if stat /= OK then do luaErrMsg <- peek (-1) <* Lua.pop 1 @@ -64,7 +66,7 @@ runLuaFilter datadir filterPath args pd = liftIO . Lua.runLua $ do else do newtop <- Lua.gettop -- Use the implicitly defined global filter if nothing was returned - when (newtop - top < 1) $ pushGlobalFilter + when (newtop - top < 1) pushGlobalFilter luaFilters <- peek (-1) push args Lua.setglobal "PandocParameters" @@ -81,27 +83,36 @@ runAll :: [LuaFilter] -> Pandoc -> Lua Pandoc runAll = foldr ((>=>) . walkMWithLuaFilter) return walkMWithLuaFilter :: LuaFilter -> Pandoc -> Lua Pandoc -walkMWithLuaFilter (LuaFilter fnMap) = walkLua - where - walkLua :: Pandoc -> Lua Pandoc - walkLua = - (if hasOneOf inlineFilterNames - then walkM (tryFilter fnMap :: Inline -> Lua Inline) - else return) - >=> - (if hasOneOf blockFilterNames - then walkM (tryFilter fnMap :: Block -> Lua Block) - else return) - >=> - (case Map.lookup "Meta" fnMap of - Just fn -> walkM (\(Pandoc meta blocks) -> do - meta' <- runFilterFunction fn meta - return $ Pandoc meta' blocks) - Nothing -> return) - >=> - (case foldl mplus Nothing (map (`Map.lookup` fnMap) pandocFilterNames) of - Just fn -> runFilterFunction fn :: Pandoc -> Lua Pandoc - Nothing -> return) +walkMWithLuaFilter (LuaFilter fnMap) = + walkInlines >=> walkBlocks >=> walkMeta >=> walkPandoc + where + walkInlines :: Pandoc -> Lua Pandoc + walkInlines = + if hasOneOf inlineFilterNames + then walkM (mconcatMapM (tryFilter fnMap :: Inline -> Lua [Inline])) + else return + + walkBlocks :: Pandoc -> Lua Pandoc + walkBlocks = + if hasOneOf blockFilterNames + then walkM (mconcatMapM (tryFilter fnMap :: Block -> Lua [Block])) + else return + + walkMeta :: Pandoc -> Lua Pandoc + walkMeta = + case Map.lookup "Meta" fnMap of + Just fn -> walkM (\(Pandoc meta blocks) -> do + meta' <- runFilterFunction fn meta *> singleElement meta + return $ Pandoc meta' blocks) + Nothing -> return + + walkPandoc :: Pandoc -> Lua Pandoc + walkPandoc = + case foldl mplus Nothing (map (`Map.lookup` fnMap) pandocFilterNames) of + Just fn -> \x -> runFilterFunction fn x *> singleElement x + Nothing -> return + + mconcatMapM f = fmap mconcat . mapM f hasOneOf = any (\k -> isJust (Map.lookup k fnMap)) constructorsFor :: DataType -> [String] @@ -124,14 +135,15 @@ newtype LuaFilter = LuaFilter FunctionMap newtype LuaFilterFunction = LuaFilterFunction { functionIndex :: Int } -- | Try running a filter for the given element -tryFilter :: (Data a, FromLuaStack a, ToLuaStack a) => FunctionMap -> a -> Lua a +tryFilter :: (Data a, FromLuaStack a, ToLuaStack a) + => FunctionMap -> a -> Lua [a] tryFilter fnMap x = let filterFnName = showConstr (toConstr x) catchAllName = tyconUQname $ dataTypeName (dataTypeOf x) in case Map.lookup filterFnName fnMap `mplus` Map.lookup catchAllName fnMap of - Just fn -> runFilterFunction fn x - Nothing -> return x + Just fn -> runFilterFunction fn x *> elementOrList x + Nothing -> return [x] instance FromLuaStack LuaFilter where peek idx = @@ -151,28 +163,42 @@ instance FromLuaStack LuaFilter where -- called with given element as argument and is expected to return an element. -- Alternatively, the function can return nothing or nil, in which case the -- element is left unchanged. -runFilterFunction :: (FromLuaStack a, ToLuaStack a) - => LuaFilterFunction -> a -> Lua a +runFilterFunction :: ToLuaStack a => LuaFilterFunction -> a -> Lua () runFilterFunction lf x = do pushFilterFunction lf push x z <- Lua.pcall 1 1 Nothing - if z /= OK - then do - msg <- peek (-1) - let prefix = "Error while running filter function: " - Lua.throwLuaError $ prefix ++ msg + when (z /= OK) $ do + msg <- Lua.peek (-1) <* Lua.pop 1 + let prefix = "Error while running filter function: " + Lua.throwLuaError $ prefix ++ msg + +elementOrList :: FromLuaStack a => a -> Lua [a] +elementOrList x = do + let topOfStack = Lua.StackIndex (-1) + elementUnchanged <- Lua.isnil topOfStack + if elementUnchanged + then [x] <$ Lua.pop 1 + else do + mbres <- Lua.peekEither topOfStack + case mbres of + Right res -> [res] <$ Lua.pop 1 + Left _ -> Lua.toList topOfStack <* Lua.pop 1 + +singleElement :: FromLuaStack a => a -> Lua a +singleElement x = do + elementUnchanged <- Lua.isnil (-1) + if elementUnchanged + then x <$ Lua.pop 1 else do - noExplicitFilter <- Lua.isnil (-1) - if noExplicitFilter - then Lua.pop 1 *> return x - else do - mbres <- Lua.peekEither (-1) - case mbres of - Left err -> Lua.throwLuaError - ("Error while trying to get a filter's return " - ++ "value from lua stack.\n" ++ err) - Right res -> res <$ Lua.pop 1 + mbres <- Lua.peekEither (-1) + case mbres of + Right res -> res <$ Lua.pop 1 + Left err -> do + Lua.pop 1 + Lua.throwLuaError $ + "Error while trying to get a filter's return " ++ + "value from lua stack.\n" ++ err -- | Push the filter function to the top of the stack. pushFilterFunction :: LuaFilterFunction -> Lua () @@ -188,6 +214,9 @@ registerFilterFunction idx = do refIdx <- Lua.ref Lua.registryindex return $ LuaFilterFunction refIdx +instance (FromLuaStack a) => FromLuaStack (Identity a) where + peek = fmap return . peek + instance ToLuaStack LuaFilterFunction where push = pushFilterFunction -- cgit v1.2.3 From 3fe4aad5a16545a92088510a00d2109a04fd25b8 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Tue, 26 Sep 2017 10:05:56 -0700 Subject: Lua: set "arg" instead of "PandocParameters". This is standard for lua scripts, and I see no reason to depart from the standard here. Also, "arg" is now pushed onto the stack before the script is loaded. Previously it was not, and thus "PandocParameters" was not available at the top level. --- src/Text/Pandoc/Lua.hs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/Text/Pandoc/Lua.hs') diff --git a/src/Text/Pandoc/Lua.hs b/src/Text/Pandoc/Lua.hs index 477076191..2860b84df 100644 --- a/src/Text/Pandoc/Lua.hs +++ b/src/Text/Pandoc/Lua.hs @@ -57,6 +57,8 @@ runLuaFilter datadir filterPath args pd = liftIO . Lua.runLua $ do -- store module in global "pandoc" pushPandocModule datadir Lua.setglobal "pandoc" + push args + Lua.setglobal "arg" top <- Lua.gettop stat <- Lua.dofile filterPath if stat /= OK @@ -68,8 +70,6 @@ runLuaFilter datadir filterPath args pd = liftIO . Lua.runLua $ do -- Use the implicitly defined global filter if nothing was returned when (newtop - top < 1) pushGlobalFilter luaFilters <- peek (-1) - push args - Lua.setglobal "PandocParameters" runAll luaFilters pd pushGlobalFilter :: Lua () -- cgit v1.2.3 From 9a47c7863b7c9d23928e51fd23b8ebc7ac684d16 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Tue, 26 Sep 2017 20:20:09 -0700 Subject: Lua filters: set global FORMAT instead of args. This changes the type of runLuaFilter. --- src/Text/Pandoc/Lua.hs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'src/Text/Pandoc/Lua.hs') diff --git a/src/Text/Pandoc/Lua.hs b/src/Text/Pandoc/Lua.hs index 2860b84df..ab3b5f4ca 100644 --- a/src/Text/Pandoc/Lua.hs +++ b/src/Text/Pandoc/Lua.hs @@ -51,14 +51,14 @@ import qualified Data.Map as Map import qualified Foreign.Lua as Lua runLuaFilter :: (MonadIO m) - => Maybe FilePath -> FilePath -> [String] -> Pandoc -> m Pandoc -runLuaFilter datadir filterPath args pd = liftIO . Lua.runLua $ do + => Maybe FilePath -> FilePath -> String -> Pandoc -> m Pandoc +runLuaFilter datadir filterPath format pd = liftIO . Lua.runLua $ do Lua.openlibs -- store module in global "pandoc" pushPandocModule datadir Lua.setglobal "pandoc" - push args - Lua.setglobal "arg" + push format + Lua.setglobal "FORMAT" top <- Lua.gettop stat <- Lua.dofile filterPath if stat /= OK -- cgit v1.2.3 From 2f47e04206a3869eadc5c93076e0b50d4362f9df Mon Sep 17 00:00:00 2001 From: Albert Krewinkel Date: Fri, 29 Sep 2017 00:11:52 +0200 Subject: Text.Pandoc.Lua: add mediabag submodule --- src/Text/Pandoc/Lua.hs | 36 ++++++++++++++++++++++++++++++------ 1 file changed, 30 insertions(+), 6 deletions(-) (limited to 'src/Text/Pandoc/Lua.hs') diff --git a/src/Text/Pandoc/Lua.hs b/src/Text/Pandoc/Lua.hs index ab3b5f4ca..f7e74d0a8 100644 --- a/src/Text/Pandoc/Lua.hs +++ b/src/Text/Pandoc/Lua.hs @@ -39,26 +39,40 @@ import Control.Monad.Trans (MonadIO (..)) import Data.Data (DataType, Data, toConstr, showConstr, dataTypeOf, dataTypeConstrs, dataTypeName, tyconUQname) import Data.Foldable (foldrM) +import Data.IORef (IORef, newIORef, readIORef) import Data.Map (Map) import Data.Maybe (isJust) import Foreign.Lua (Lua, FromLuaStack (peek), LuaException (..), StackIndex, Status (OK), ToLuaStack (push)) +import Text.Pandoc.Class (PandocIO, getMediaBag, setMediaBag) +import Text.Pandoc.MediaBag (MediaBag) import Text.Pandoc.Definition -import Text.Pandoc.Lua.PandocModule (pushPandocModule) +import Text.Pandoc.Lua.PandocModule (pushPandocModule, pushMediaBagModule) import Text.Pandoc.Walk (walkM) import qualified Data.Map as Map import qualified Foreign.Lua as Lua -runLuaFilter :: (MonadIO m) - => Maybe FilePath -> FilePath -> String -> Pandoc -> m Pandoc -runLuaFilter datadir filterPath format pd = liftIO . Lua.runLua $ do +runLuaFilter :: Maybe FilePath -> FilePath -> String + -> Pandoc -> PandocIO (Either LuaException Pandoc) +runLuaFilter datadir filterPath format pd = do + mediaBag <- getMediaBag + mediaBagRef <- liftIO (newIORef mediaBag) + res <- liftIO . Lua.runLuaEither $ + runLuaFilter' datadir filterPath format mediaBagRef pd + newMediaBag <- liftIO (readIORef mediaBagRef) + setMediaBag newMediaBag + return res + +runLuaFilter' :: Maybe FilePath -> FilePath -> String -> IORef MediaBag + -> Pandoc -> Lua Pandoc +runLuaFilter' datadir filterPath format mbRef pd = do Lua.openlibs -- store module in global "pandoc" pushPandocModule datadir Lua.setglobal "pandoc" - push format - Lua.setglobal "FORMAT" + addMediaBagModule + registerFormat top <- Lua.gettop stat <- Lua.dofile filterPath if stat /= OK @@ -71,6 +85,16 @@ runLuaFilter datadir filterPath format pd = liftIO . Lua.runLua $ do when (newtop - top < 1) pushGlobalFilter luaFilters <- peek (-1) runAll luaFilters pd + where + addMediaBagModule = do + Lua.getglobal "pandoc" + push "mediabag" + pushMediaBagModule mbRef + Lua.rawset (-3) + registerFormat = do + push format + Lua.setglobal "FORMAT" + pushGlobalFilter :: Lua () pushGlobalFilter = do -- cgit v1.2.3 From 896c288625a8c48e290fe86e90b65109bd4fce9f Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Sat, 30 Sep 2017 17:19:39 -0500 Subject: Lua filters: make sure whole CommonState is passed through... to insertResource (`fetch`). --- src/Text/Pandoc/Lua.hs | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) (limited to 'src/Text/Pandoc/Lua.hs') diff --git a/src/Text/Pandoc/Lua.hs b/src/Text/Pandoc/Lua.hs index f7e74d0a8..2e4204898 100644 --- a/src/Text/Pandoc/Lua.hs +++ b/src/Text/Pandoc/Lua.hs @@ -44,7 +44,8 @@ import Data.Map (Map) import Data.Maybe (isJust) import Foreign.Lua (Lua, FromLuaStack (peek), LuaException (..), StackIndex, Status (OK), ToLuaStack (push)) -import Text.Pandoc.Class (PandocIO, getMediaBag, setMediaBag) +import Text.Pandoc.Class (PandocIO, getMediaBag, setMediaBag, + getCommonState, CommonState) import Text.Pandoc.MediaBag (MediaBag) import Text.Pandoc.Definition import Text.Pandoc.Lua.PandocModule (pushPandocModule, pushMediaBagModule) @@ -56,17 +57,19 @@ import qualified Foreign.Lua as Lua runLuaFilter :: Maybe FilePath -> FilePath -> String -> Pandoc -> PandocIO (Either LuaException Pandoc) runLuaFilter datadir filterPath format pd = do + commonState <- getCommonState mediaBag <- getMediaBag mediaBagRef <- liftIO (newIORef mediaBag) res <- liftIO . Lua.runLuaEither $ - runLuaFilter' datadir filterPath format mediaBagRef pd + runLuaFilter' commonState datadir filterPath format mediaBagRef pd newMediaBag <- liftIO (readIORef mediaBagRef) setMediaBag newMediaBag return res -runLuaFilter' :: Maybe FilePath -> FilePath -> String -> IORef MediaBag +runLuaFilter' :: CommonState + -> Maybe FilePath -> FilePath -> String -> IORef MediaBag -> Pandoc -> Lua Pandoc -runLuaFilter' datadir filterPath format mbRef pd = do +runLuaFilter' commonState datadir filterPath format mbRef pd = do Lua.openlibs -- store module in global "pandoc" pushPandocModule datadir @@ -89,7 +92,7 @@ runLuaFilter' datadir filterPath format mbRef pd = do addMediaBagModule = do Lua.getglobal "pandoc" push "mediabag" - pushMediaBagModule mbRef + pushMediaBagModule commonState mbRef Lua.rawset (-3) registerFormat = do push format -- cgit v1.2.3 From 12f8efe0128ade1bd6497a59508f6bd836eb3788 Mon Sep 17 00:00:00 2001 From: Albert Krewinkel Date: Thu, 5 Oct 2017 11:41:59 +0200 Subject: pandoc.lua: throw better error when pipe command fails A table containing the error code, command, and command output is thrown instead of just a string error message. --- src/Text/Pandoc/Lua.hs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'src/Text/Pandoc/Lua.hs') diff --git a/src/Text/Pandoc/Lua.hs b/src/Text/Pandoc/Lua.hs index 2e4204898..583d43a2e 100644 --- a/src/Text/Pandoc/Lua.hs +++ b/src/Text/Pandoc/Lua.hs @@ -196,9 +196,8 @@ runFilterFunction lf x = do push x z <- Lua.pcall 1 1 Nothing when (z /= OK) $ do - msg <- Lua.peek (-1) <* Lua.pop 1 - let prefix = "Error while running filter function: " - Lua.throwLuaError $ prefix ++ msg + let addPrefix = ("Error while running filter function: " ++) + Lua.throwTopMessageAsError' addPrefix elementOrList :: FromLuaStack a => a -> Lua [a] elementOrList x = do -- cgit v1.2.3 From ff16db1aa306113132cc6cfaa70791a0db75e0a5 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Fri, 27 Oct 2017 20:28:29 -0700 Subject: Automatic reformating by stylish-haskell. --- src/Text/Pandoc/Lua.hs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) (limited to 'src/Text/Pandoc/Lua.hs') diff --git a/src/Text/Pandoc/Lua.hs b/src/Text/Pandoc/Lua.hs index 583d43a2e..091deab8c 100644 --- a/src/Text/Pandoc/Lua.hs +++ b/src/Text/Pandoc/Lua.hs @@ -36,19 +36,19 @@ module Text.Pandoc.Lua (LuaException (..), pushPandocModule, runLuaFilter) where import Control.Monad (mplus, unless, when, (>=>)) import Control.Monad.Identity (Identity) import Control.Monad.Trans (MonadIO (..)) -import Data.Data (DataType, Data, toConstr, showConstr, dataTypeOf, - dataTypeConstrs, dataTypeName, tyconUQname) +import Data.Data (Data, DataType, dataTypeConstrs, dataTypeName, dataTypeOf, + showConstr, toConstr, tyconUQname) import Data.Foldable (foldrM) import Data.IORef (IORef, newIORef, readIORef) import Data.Map (Map) import Data.Maybe (isJust) -import Foreign.Lua (Lua, FromLuaStack (peek), LuaException (..), StackIndex, +import Foreign.Lua (FromLuaStack (peek), Lua, LuaException (..), StackIndex, Status (OK), ToLuaStack (push)) -import Text.Pandoc.Class (PandocIO, getMediaBag, setMediaBag, - getCommonState, CommonState) -import Text.Pandoc.MediaBag (MediaBag) +import Text.Pandoc.Class (CommonState, PandocIO, getCommonState, getMediaBag, + setMediaBag) import Text.Pandoc.Definition -import Text.Pandoc.Lua.PandocModule (pushPandocModule, pushMediaBagModule) +import Text.Pandoc.Lua.PandocModule (pushMediaBagModule, pushPandocModule) +import Text.Pandoc.MediaBag (MediaBag) import Text.Pandoc.Walk (walkM) import qualified Data.Map as Map @@ -182,7 +182,7 @@ instance FromLuaStack LuaFilter where filterFn <- Lua.tryLua (peek (-1)) Lua.pop 1 return $ case filterFn of - Left _ -> acc + Left _ -> acc Right f -> (c, f) : acc in LuaFilter . Map.fromList <$> foldrM fn [] constrs @@ -209,7 +209,7 @@ elementOrList x = do mbres <- Lua.peekEither topOfStack case mbres of Right res -> [res] <$ Lua.pop 1 - Left _ -> Lua.toList topOfStack <* Lua.pop 1 + Left _ -> Lua.toList topOfStack <* Lua.pop 1 singleElement :: FromLuaStack a => a -> Lua a singleElement x = do -- cgit v1.2.3 From 6174b5bea5e8c4c35c191bd62f1f42e4d7fce69e Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Sat, 11 Nov 2017 11:01:38 -0500 Subject: Add lua filter functions to walk inline and block elements. Refactored some code from Text.Pandoc.Lua.PandocModule into new internal module Text.Pandoc.Lua.Filter. Add `walk_inline` and `walk_block` in pandoc lua module. --- src/Text/Pandoc/Lua.hs | 150 +------------------------------------------------ 1 file changed, 3 insertions(+), 147 deletions(-) (limited to 'src/Text/Pandoc/Lua.hs') diff --git a/src/Text/Pandoc/Lua.hs b/src/Text/Pandoc/Lua.hs index 091deab8c..355a5baf1 100644 --- a/src/Text/Pandoc/Lua.hs +++ b/src/Text/Pandoc/Lua.hs @@ -33,25 +33,18 @@ Pandoc lua utils. -} module Text.Pandoc.Lua (LuaException (..), pushPandocModule, runLuaFilter) where -import Control.Monad (mplus, unless, when, (>=>)) +import Control.Monad (when, (>=>)) import Control.Monad.Identity (Identity) import Control.Monad.Trans (MonadIO (..)) -import Data.Data (Data, DataType, dataTypeConstrs, dataTypeName, dataTypeOf, - showConstr, toConstr, tyconUQname) -import Data.Foldable (foldrM) import Data.IORef (IORef, newIORef, readIORef) -import Data.Map (Map) -import Data.Maybe (isJust) -import Foreign.Lua (FromLuaStack (peek), Lua, LuaException (..), StackIndex, +import Foreign.Lua (FromLuaStack (peek), Lua, LuaException (..), Status (OK), ToLuaStack (push)) import Text.Pandoc.Class (CommonState, PandocIO, getCommonState, getMediaBag, setMediaBag) import Text.Pandoc.Definition import Text.Pandoc.Lua.PandocModule (pushMediaBagModule, pushPandocModule) +import Text.Pandoc.Lua.Filter (LuaFilter, walkMWithLuaFilter) import Text.Pandoc.MediaBag (MediaBag) -import Text.Pandoc.Walk (walkM) - -import qualified Data.Map as Map import qualified Foreign.Lua as Lua runLuaFilter :: Maybe FilePath -> FilePath -> String @@ -109,142 +102,5 @@ pushGlobalFilter = do runAll :: [LuaFilter] -> Pandoc -> Lua Pandoc runAll = foldr ((>=>) . walkMWithLuaFilter) return -walkMWithLuaFilter :: LuaFilter -> Pandoc -> Lua Pandoc -walkMWithLuaFilter (LuaFilter fnMap) = - walkInlines >=> walkBlocks >=> walkMeta >=> walkPandoc - where - walkInlines :: Pandoc -> Lua Pandoc - walkInlines = - if hasOneOf inlineFilterNames - then walkM (mconcatMapM (tryFilter fnMap :: Inline -> Lua [Inline])) - else return - - walkBlocks :: Pandoc -> Lua Pandoc - walkBlocks = - if hasOneOf blockFilterNames - then walkM (mconcatMapM (tryFilter fnMap :: Block -> Lua [Block])) - else return - - walkMeta :: Pandoc -> Lua Pandoc - walkMeta = - case Map.lookup "Meta" fnMap of - Just fn -> walkM (\(Pandoc meta blocks) -> do - meta' <- runFilterFunction fn meta *> singleElement meta - return $ Pandoc meta' blocks) - Nothing -> return - - walkPandoc :: Pandoc -> Lua Pandoc - walkPandoc = - case foldl mplus Nothing (map (`Map.lookup` fnMap) pandocFilterNames) of - Just fn -> \x -> runFilterFunction fn x *> singleElement x - Nothing -> return - - mconcatMapM f = fmap mconcat . mapM f - hasOneOf = any (\k -> isJust (Map.lookup k fnMap)) - -constructorsFor :: DataType -> [String] -constructorsFor x = map show (dataTypeConstrs x) - -inlineFilterNames :: [String] -inlineFilterNames = "Inline" : constructorsFor (dataTypeOf (Str [])) - -blockFilterNames :: [String] -blockFilterNames = "Block" : constructorsFor (dataTypeOf (Para [])) - -metaFilterName :: String -metaFilterName = "Meta" - -pandocFilterNames :: [String] -pandocFilterNames = ["Pandoc", "Doc"] - -type FunctionMap = Map String LuaFilterFunction -newtype LuaFilter = LuaFilter FunctionMap -newtype LuaFilterFunction = LuaFilterFunction { functionIndex :: Int } - --- | Try running a filter for the given element -tryFilter :: (Data a, FromLuaStack a, ToLuaStack a) - => FunctionMap -> a -> Lua [a] -tryFilter fnMap x = - let filterFnName = showConstr (toConstr x) - catchAllName = tyconUQname $ dataTypeName (dataTypeOf x) - in - case Map.lookup filterFnName fnMap `mplus` Map.lookup catchAllName fnMap of - Just fn -> runFilterFunction fn x *> elementOrList x - Nothing -> return [x] - -instance FromLuaStack LuaFilter where - peek idx = - let constrs = metaFilterName : pandocFilterNames - ++ blockFilterNames - ++ inlineFilterNames - fn c acc = do - Lua.getfield idx c - filterFn <- Lua.tryLua (peek (-1)) - Lua.pop 1 - return $ case filterFn of - Left _ -> acc - Right f -> (c, f) : acc - in LuaFilter . Map.fromList <$> foldrM fn [] constrs - --- | Push a value to the stack via a lua filter function. The filter function is --- called with given element as argument and is expected to return an element. --- Alternatively, the function can return nothing or nil, in which case the --- element is left unchanged. -runFilterFunction :: ToLuaStack a => LuaFilterFunction -> a -> Lua () -runFilterFunction lf x = do - pushFilterFunction lf - push x - z <- Lua.pcall 1 1 Nothing - when (z /= OK) $ do - let addPrefix = ("Error while running filter function: " ++) - Lua.throwTopMessageAsError' addPrefix - -elementOrList :: FromLuaStack a => a -> Lua [a] -elementOrList x = do - let topOfStack = Lua.StackIndex (-1) - elementUnchanged <- Lua.isnil topOfStack - if elementUnchanged - then [x] <$ Lua.pop 1 - else do - mbres <- Lua.peekEither topOfStack - case mbres of - Right res -> [res] <$ Lua.pop 1 - Left _ -> Lua.toList topOfStack <* Lua.pop 1 - -singleElement :: FromLuaStack a => a -> Lua a -singleElement x = do - elementUnchanged <- Lua.isnil (-1) - if elementUnchanged - then x <$ Lua.pop 1 - else do - mbres <- Lua.peekEither (-1) - case mbres of - Right res -> res <$ Lua.pop 1 - Left err -> do - Lua.pop 1 - Lua.throwLuaError $ - "Error while trying to get a filter's return " ++ - "value from lua stack.\n" ++ err - --- | Push the filter function to the top of the stack. -pushFilterFunction :: LuaFilterFunction -> Lua () -pushFilterFunction lf = - -- The function is stored in a lua registry table, retrieve it from there. - Lua.rawgeti Lua.registryindex (functionIndex lf) - -registerFilterFunction :: StackIndex -> Lua LuaFilterFunction -registerFilterFunction idx = do - isFn <- Lua.isfunction idx - unless isFn . Lua.throwLuaError $ "Not a function at index " ++ show idx - Lua.pushvalue idx - refIdx <- Lua.ref Lua.registryindex - return $ LuaFilterFunction refIdx - instance (FromLuaStack a) => FromLuaStack (Identity a) where peek = fmap return . peek - -instance ToLuaStack LuaFilterFunction where - push = pushFilterFunction - -instance FromLuaStack LuaFilterFunction where - peek = registerFilterFunction -- cgit v1.2.3 From 53aafd66434d97f5e0e9209650581177e2c79a91 Mon Sep 17 00:00:00 2001 From: Albert Krewinkel Date: Sat, 18 Nov 2017 22:24:06 +0100 Subject: Lua filters: preload text module (#4077) The `text` module is preloaded in lua. The module contains some UTF-8 aware string functions, implemented in Haskell. The module is loaded on request only, e.g.: text = require 'text' function Str (s) s.text = text.upper(s.text) return s end --- src/Text/Pandoc/Lua.hs | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src/Text/Pandoc/Lua.hs') diff --git a/src/Text/Pandoc/Lua.hs b/src/Text/Pandoc/Lua.hs index 355a5baf1..148e7a23d 100644 --- a/src/Text/Pandoc/Lua.hs +++ b/src/Text/Pandoc/Lua.hs @@ -46,6 +46,7 @@ import Text.Pandoc.Lua.PandocModule (pushMediaBagModule, pushPandocModule) import Text.Pandoc.Lua.Filter (LuaFilter, walkMWithLuaFilter) import Text.Pandoc.MediaBag (MediaBag) import qualified Foreign.Lua as Lua +import qualified Foreign.Lua.Module.Text as Lua runLuaFilter :: Maybe FilePath -> FilePath -> String -> Pandoc -> PandocIO (Either LuaException Pandoc) @@ -64,6 +65,7 @@ runLuaFilter' :: CommonState -> Pandoc -> Lua Pandoc runLuaFilter' commonState datadir filterPath format mbRef pd = do Lua.openlibs + Lua.preloadTextModule "text" -- store module in global "pandoc" pushPandocModule datadir Lua.setglobal "pandoc" -- cgit v1.2.3 From d5b1c7b767a24bda592ea35902b8e1dc971d6d80 Mon Sep 17 00:00:00 2001 From: Albert Krewinkel Date: Sat, 2 Dec 2017 23:07:29 +0100 Subject: Lua filters: refactor lua module handling The integration with Lua's package/module system is improved: A pandoc-specific package searcher is prepended to the searchers in `package.searchers`. The modules `pandoc` and `pandoc.mediabag` can now be loaded via `require`. --- src/Text/Pandoc/Lua.hs | 65 ++++++++++++++++++++++++++++++++------------------ 1 file changed, 42 insertions(+), 23 deletions(-) (limited to 'src/Text/Pandoc/Lua.hs') diff --git a/src/Text/Pandoc/Lua.hs b/src/Text/Pandoc/Lua.hs index 148e7a23d..1ca67dced 100644 --- a/src/Text/Pandoc/Lua.hs +++ b/src/Text/Pandoc/Lua.hs @@ -31,45 +31,46 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Pandoc lua utils. -} -module Text.Pandoc.Lua (LuaException (..), pushPandocModule, runLuaFilter) where +module Text.Pandoc.Lua + ( LuaException (..) + , LuaPackageParams (..) + , pushPandocModule + , runLuaFilter + , initLuaState + , luaPackageParams + ) where import Control.Monad (when, (>=>)) import Control.Monad.Identity (Identity) import Control.Monad.Trans (MonadIO (..)) -import Data.IORef (IORef, newIORef, readIORef) +import Data.IORef (newIORef, readIORef) import Foreign.Lua (FromLuaStack (peek), Lua, LuaException (..), Status (OK), ToLuaStack (push)) -import Text.Pandoc.Class (CommonState, PandocIO, getCommonState, getMediaBag, - setMediaBag) +import Text.Pandoc.Class (PandocIO, getCommonState, getMediaBag, setMediaBag) import Text.Pandoc.Definition +import Text.Pandoc.Lua.Packages (LuaPackageParams (..), + installPandocPackageSearcher) import Text.Pandoc.Lua.PandocModule (pushMediaBagModule, pushPandocModule) import Text.Pandoc.Lua.Filter (LuaFilter, walkMWithLuaFilter) -import Text.Pandoc.MediaBag (MediaBag) import qualified Foreign.Lua as Lua import qualified Foreign.Lua.Module.Text as Lua runLuaFilter :: Maybe FilePath -> FilePath -> String -> Pandoc -> PandocIO (Either LuaException Pandoc) runLuaFilter datadir filterPath format pd = do - commonState <- getCommonState - mediaBag <- getMediaBag - mediaBagRef <- liftIO (newIORef mediaBag) + luaPkgParams <- luaPackageParams datadir res <- liftIO . Lua.runLuaEither $ - runLuaFilter' commonState datadir filterPath format mediaBagRef pd - newMediaBag <- liftIO (readIORef mediaBagRef) + runLuaFilter' luaPkgParams filterPath format pd + newMediaBag <- liftIO (readIORef (luaPkgMediaBag luaPkgParams)) setMediaBag newMediaBag return res -runLuaFilter' :: CommonState - -> Maybe FilePath -> FilePath -> String -> IORef MediaBag +runLuaFilter' :: LuaPackageParams + -> FilePath -> String -> Pandoc -> Lua Pandoc -runLuaFilter' commonState datadir filterPath format mbRef pd = do - Lua.openlibs - Lua.preloadTextModule "text" +runLuaFilter' luaPkgOpts filterPath format pd = do + initLuaState luaPkgOpts -- store module in global "pandoc" - pushPandocModule datadir - Lua.setglobal "pandoc" - addMediaBagModule registerFormat top <- Lua.gettop stat <- Lua.dofile filterPath @@ -84,15 +85,33 @@ runLuaFilter' commonState datadir filterPath format mbRef pd = do luaFilters <- peek (-1) runAll luaFilters pd where - addMediaBagModule = do - Lua.getglobal "pandoc" - push "mediabag" - pushMediaBagModule commonState mbRef - Lua.rawset (-3) registerFormat = do push format Lua.setglobal "FORMAT" +luaPackageParams :: Maybe FilePath -> PandocIO LuaPackageParams +luaPackageParams datadir = do + commonState <- getCommonState + mbRef <- liftIO . newIORef =<< getMediaBag + return LuaPackageParams + { luaPkgCommonState = commonState + , luaPkgDataDir = datadir + , luaPkgMediaBag = mbRef + } + +-- Initialize the lua state with all required values +initLuaState :: LuaPackageParams -> Lua () +initLuaState luaPkgParams@(LuaPackageParams commonState datadir mbRef) = do + Lua.openlibs + Lua.preloadTextModule "text" + installPandocPackageSearcher luaPkgParams + pushPandocModule datadir + -- add MediaBag module + push "mediabag" + pushMediaBagModule commonState mbRef + Lua.rawset (-3) + Lua.setglobal "pandoc" + return () pushGlobalFilter :: Lua () pushGlobalFilter = do -- cgit v1.2.3 From 4066a385ace1cee53336bf4c10734239044a92ae Mon Sep 17 00:00:00 2001 From: Albert Krewinkel Date: Wed, 6 Dec 2017 20:45:38 +0100 Subject: Lua filters: use script to initialize the interpreter The file `init.lua` is used to initialize the Lua interpreter which is used in Lua filters. This gives users the option to require libraries which they want to use in all of their filters, and to extend default modules. --- src/Text/Pandoc/Lua.hs | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) (limited to 'src/Text/Pandoc/Lua.hs') diff --git a/src/Text/Pandoc/Lua.hs b/src/Text/Pandoc/Lua.hs index 1ca67dced..7132ad718 100644 --- a/src/Text/Pandoc/Lua.hs +++ b/src/Text/Pandoc/Lua.hs @@ -48,10 +48,11 @@ import Foreign.Lua (FromLuaStack (peek), Lua, LuaException (..), Status (OK), ToLuaStack (push)) import Text.Pandoc.Class (PandocIO, getCommonState, getMediaBag, setMediaBag) import Text.Pandoc.Definition +import Text.Pandoc.Lua.Filter (LuaFilter, walkMWithLuaFilter) import Text.Pandoc.Lua.Packages (LuaPackageParams (..), installPandocPackageSearcher) -import Text.Pandoc.Lua.PandocModule (pushMediaBagModule, pushPandocModule) -import Text.Pandoc.Lua.Filter (LuaFilter, walkMWithLuaFilter) +import Text.Pandoc.Lua.PandocModule (pushPandocModule) -- TODO: remove +import Text.Pandoc.Lua.Util (loadScriptFromDataDir) import qualified Foreign.Lua as Lua import qualified Foreign.Lua.Module.Text as Lua @@ -101,17 +102,11 @@ luaPackageParams datadir = do -- Initialize the lua state with all required values initLuaState :: LuaPackageParams -> Lua () -initLuaState luaPkgParams@(LuaPackageParams commonState datadir mbRef) = do +initLuaState luaPkgParams = do Lua.openlibs Lua.preloadTextModule "text" installPandocPackageSearcher luaPkgParams - pushPandocModule datadir - -- add MediaBag module - push "mediabag" - pushMediaBagModule commonState mbRef - Lua.rawset (-3) - Lua.setglobal "pandoc" - return () + loadScriptFromDataDir (luaPkgDataDir luaPkgParams) "init.lua" pushGlobalFilter :: Lua () pushGlobalFilter = do -- cgit v1.2.3 From f9d0e1c89cf8deca97a005d8cd6d2d601e422e24 Mon Sep 17 00:00:00 2001 From: Albert Krewinkel Date: Tue, 12 Dec 2017 08:58:47 +0100 Subject: Lua filters: drop unused code, language extensions --- src/Text/Pandoc/Lua.hs | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) (limited to 'src/Text/Pandoc/Lua.hs') diff --git a/src/Text/Pandoc/Lua.hs b/src/Text/Pandoc/Lua.hs index 7132ad718..696f4de44 100644 --- a/src/Text/Pandoc/Lua.hs +++ b/src/Text/Pandoc/Lua.hs @@ -1,9 +1,3 @@ -{-# LANGUAGE CPP #-} -{-# LANGUAGE FlexibleContexts #-} -{-# LANGUAGE FlexibleInstances #-} -{-# LANGUAGE MultiParamTypeClasses #-} -{-# LANGUAGE ScopedTypeVariables #-} -{-# OPTIONS_GHC -fno-warn-orphans #-} {- Copyright © 2017 Albert Krewinkel @@ -29,7 +23,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Maintainer : Albert Krewinkel Stability : alpha -Pandoc lua utils. +Running pandoc Lua filters. -} module Text.Pandoc.Lua ( LuaException (..) @@ -41,7 +35,6 @@ module Text.Pandoc.Lua ) where import Control.Monad (when, (>=>)) -import Control.Monad.Identity (Identity) import Control.Monad.Trans (MonadIO (..)) import Data.IORef (newIORef, readIORef) import Foreign.Lua (FromLuaStack (peek), Lua, LuaException (..), @@ -117,6 +110,3 @@ pushGlobalFilter = do runAll :: [LuaFilter] -> Pandoc -> Lua Pandoc runAll = foldr ((>=>) . walkMWithLuaFilter) return - -instance (FromLuaStack a) => FromLuaStack (Identity a) where - peek = fmap return . peek -- cgit v1.2.3 From 4c64af4407776e6ceb2fcc8a803b83568b4c1964 Mon Sep 17 00:00:00 2001 From: Albert Krewinkel Date: Wed, 13 Dec 2017 21:15:41 +0100 Subject: Custom writer: use init file to setup Lua interpreter The same init file (`data/init`) that is used to setup the Lua interpreter for Lua filters is also used to setup the interpreter of custom writers.lua. --- src/Text/Pandoc/Lua.hs | 55 ++++++++++++-------------------------------------- 1 file changed, 13 insertions(+), 42 deletions(-) (limited to 'src/Text/Pandoc/Lua.hs') diff --git a/src/Text/Pandoc/Lua.hs b/src/Text/Pandoc/Lua.hs index 696f4de44..a56e89511 100644 --- a/src/Text/Pandoc/Lua.hs +++ b/src/Text/Pandoc/Lua.hs @@ -27,43 +27,32 @@ Running pandoc Lua filters. -} module Text.Pandoc.Lua ( LuaException (..) - , LuaPackageParams (..) - , pushPandocModule , runLuaFilter - , initLuaState - , luaPackageParams + , runPandocLua + , pushPandocModule ) where import Control.Monad (when, (>=>)) -import Control.Monad.Trans (MonadIO (..)) -import Data.IORef (newIORef, readIORef) import Foreign.Lua (FromLuaStack (peek), Lua, LuaException (..), Status (OK), ToLuaStack (push)) -import Text.Pandoc.Class (PandocIO, getCommonState, getMediaBag, setMediaBag) -import Text.Pandoc.Definition +import Text.Pandoc.Class (PandocIO) +import Text.Pandoc.Definition (Pandoc) import Text.Pandoc.Lua.Filter (LuaFilter, walkMWithLuaFilter) -import Text.Pandoc.Lua.Packages (LuaPackageParams (..), - installPandocPackageSearcher) +import Text.Pandoc.Lua.Init (runPandocLua) import Text.Pandoc.Lua.PandocModule (pushPandocModule) -- TODO: remove -import Text.Pandoc.Lua.Util (loadScriptFromDataDir) import qualified Foreign.Lua as Lua -import qualified Foreign.Lua.Module.Text as Lua -runLuaFilter :: Maybe FilePath -> FilePath -> String +-- | Run the Lua filter in @filterPath@ for a transformation to target +-- format @format@. Pandoc uses Lua init files to setup the Lua +-- interpreter. +runLuaFilter :: FilePath -> String -> Pandoc -> PandocIO (Either LuaException Pandoc) -runLuaFilter datadir filterPath format pd = do - luaPkgParams <- luaPackageParams datadir - res <- liftIO . Lua.runLuaEither $ - runLuaFilter' luaPkgParams filterPath format pd - newMediaBag <- liftIO (readIORef (luaPkgMediaBag luaPkgParams)) - setMediaBag newMediaBag - return res +runLuaFilter filterPath format doc = + runPandocLua (runLuaFilter' filterPath format doc) -runLuaFilter' :: LuaPackageParams - -> FilePath -> String +runLuaFilter' :: FilePath -> String -> Pandoc -> Lua Pandoc -runLuaFilter' luaPkgOpts filterPath format pd = do - initLuaState luaPkgOpts +runLuaFilter' filterPath format pd = do -- store module in global "pandoc" registerFormat top <- Lua.gettop @@ -83,24 +72,6 @@ runLuaFilter' luaPkgOpts filterPath format pd = do push format Lua.setglobal "FORMAT" -luaPackageParams :: Maybe FilePath -> PandocIO LuaPackageParams -luaPackageParams datadir = do - commonState <- getCommonState - mbRef <- liftIO . newIORef =<< getMediaBag - return LuaPackageParams - { luaPkgCommonState = commonState - , luaPkgDataDir = datadir - , luaPkgMediaBag = mbRef - } - --- Initialize the lua state with all required values -initLuaState :: LuaPackageParams -> Lua () -initLuaState luaPkgParams = do - Lua.openlibs - Lua.preloadTextModule "text" - installPandocPackageSearcher luaPkgParams - loadScriptFromDataDir (luaPkgDataDir luaPkgParams) "init.lua" - pushGlobalFilter :: Lua () pushGlobalFilter = do Lua.newtable -- cgit v1.2.3 From ab3c5065847f60f0c3f3b097331a28d27716dc8d Mon Sep 17 00:00:00 2001 From: Albert Krewinkel Date: Thu, 21 Dec 2017 21:37:40 +0100 Subject: Lua modules: move to dedicated submodule The Haskell module defining the Lua `pandoc` module is moved to Text.Pandoc.Lua.Module.Pandoc. Change: minor --- src/Text/Pandoc/Lua.hs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'src/Text/Pandoc/Lua.hs') diff --git a/src/Text/Pandoc/Lua.hs b/src/Text/Pandoc/Lua.hs index a56e89511..ee259e3fd 100644 --- a/src/Text/Pandoc/Lua.hs +++ b/src/Text/Pandoc/Lua.hs @@ -39,7 +39,7 @@ import Text.Pandoc.Class (PandocIO) import Text.Pandoc.Definition (Pandoc) import Text.Pandoc.Lua.Filter (LuaFilter, walkMWithLuaFilter) import Text.Pandoc.Lua.Init (runPandocLua) -import Text.Pandoc.Lua.PandocModule (pushPandocModule) -- TODO: remove +import Text.Pandoc.Lua.Module.Pandoc (pushModule) -- TODO: remove import qualified Foreign.Lua as Lua -- | Run the Lua filter in @filterPath@ for a transformation to target @@ -81,3 +81,7 @@ pushGlobalFilter = do runAll :: [LuaFilter] -> Pandoc -> Lua Pandoc runAll = foldr ((>=>) . walkMWithLuaFilter) return + +-- | DEPRECATED: Push the pandoc module to the Lua Stack. +pushPandocModule :: Maybe FilePath -> Lua Lua.NumResults +pushPandocModule = pushModule -- cgit v1.2.3 From 9be2c7624cb0cf3ef63516e5df959672958058bc Mon Sep 17 00:00:00 2001 From: Albert Krewinkel Date: Fri, 29 Dec 2017 09:40:22 +0100 Subject: data/pandoc.lua: drop function pandoc.global_filter The function `global_filter` was used internally to get the implicitly defined global filter. It was of little value to end-users, but caused unnecessary code duplication in pandoc. The function has hence been dropped. Internally, the global filter is now received by interpreting the global table as lua filter. This is a Lua API change. --- src/Text/Pandoc/Lua.hs | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) (limited to 'src/Text/Pandoc/Lua.hs') diff --git a/src/Text/Pandoc/Lua.hs b/src/Text/Pandoc/Lua.hs index ee259e3fd..02e1b0424 100644 --- a/src/Text/Pandoc/Lua.hs +++ b/src/Text/Pandoc/Lua.hs @@ -32,7 +32,7 @@ module Text.Pandoc.Lua , pushPandocModule ) where -import Control.Monad (when, (>=>)) +import Control.Monad ((>=>)) import Foreign.Lua (FromLuaStack (peek), Lua, LuaException (..), Status (OK), ToLuaStack (push)) import Text.Pandoc.Class (PandocIO) @@ -40,6 +40,7 @@ import Text.Pandoc.Definition (Pandoc) import Text.Pandoc.Lua.Filter (LuaFilter, walkMWithLuaFilter) import Text.Pandoc.Lua.Init (runPandocLua) import Text.Pandoc.Lua.Module.Pandoc (pushModule) -- TODO: remove +import Text.Pandoc.Lua.Util (popValue) import qualified Foreign.Lua as Lua -- | Run the Lua filter in @filterPath@ for a transformation to target @@ -63,22 +64,17 @@ runLuaFilter' filterPath format pd = do Lua.throwLuaError luaErrMsg else do newtop <- Lua.gettop - -- Use the implicitly defined global filter if nothing was returned - when (newtop - top < 1) pushGlobalFilter - luaFilters <- peek (-1) + -- Use the returned filters, or the implicitly defined global filter if + -- nothing was returned. + luaFilters <- if (newtop - top >= 1) + then peek (-1) + else Lua.getglobal "_G" *> fmap (:[]) popValue runAll luaFilters pd where registerFormat = do push format Lua.setglobal "FORMAT" -pushGlobalFilter :: Lua () -pushGlobalFilter = do - Lua.newtable - Lua.getglobal' "pandoc.global_filter" - Lua.call 0 1 - Lua.rawseti (-2) 1 - runAll :: [LuaFilter] -> Pandoc -> Lua Pandoc runAll = foldr ((>=>) . walkMWithLuaFilter) return -- cgit v1.2.3 From f42839ee2c14cf707c1059c0b3f5e4b31c642efb Mon Sep 17 00:00:00 2001 From: Albert Krewinkel Date: Fri, 29 Dec 2017 10:06:38 +0100 Subject: Lua filters: stop exporting pushPandocModule The function `pushPandocModule` was exported by Text.Pandoc.Lua to enable simpler testing. The introduction of `runPandocLua` renders direct use of this function obsolete. (API change) --- src/Text/Pandoc/Lua.hs | 6 ------ 1 file changed, 6 deletions(-) (limited to 'src/Text/Pandoc/Lua.hs') diff --git a/src/Text/Pandoc/Lua.hs b/src/Text/Pandoc/Lua.hs index 02e1b0424..d02963418 100644 --- a/src/Text/Pandoc/Lua.hs +++ b/src/Text/Pandoc/Lua.hs @@ -29,7 +29,6 @@ module Text.Pandoc.Lua ( LuaException (..) , runLuaFilter , runPandocLua - , pushPandocModule ) where import Control.Monad ((>=>)) @@ -39,7 +38,6 @@ import Text.Pandoc.Class (PandocIO) import Text.Pandoc.Definition (Pandoc) import Text.Pandoc.Lua.Filter (LuaFilter, walkMWithLuaFilter) import Text.Pandoc.Lua.Init (runPandocLua) -import Text.Pandoc.Lua.Module.Pandoc (pushModule) -- TODO: remove import Text.Pandoc.Lua.Util (popValue) import qualified Foreign.Lua as Lua @@ -77,7 +75,3 @@ runLuaFilter' filterPath format pd = do runAll :: [LuaFilter] -> Pandoc -> Lua Pandoc runAll = foldr ((>=>) . walkMWithLuaFilter) return - --- | DEPRECATED: Push the pandoc module to the Lua Stack. -pushPandocModule :: Maybe FilePath -> Lua Lua.NumResults -pushPandocModule = pushModule -- cgit v1.2.3 From 0d935bd081bb4013168dc114461ab7c47fec2f44 Mon Sep 17 00:00:00 2001 From: Albert Krewinkel Date: Fri, 5 Jan 2018 20:19:47 +0100 Subject: Update copyright notices to include 2018 --- src/Text/Pandoc/Lua.hs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/Text/Pandoc/Lua.hs') diff --git a/src/Text/Pandoc/Lua.hs b/src/Text/Pandoc/Lua.hs index d02963418..48518aa54 100644 --- a/src/Text/Pandoc/Lua.hs +++ b/src/Text/Pandoc/Lua.hs @@ -1,5 +1,5 @@ {- -Copyright © 2017 Albert Krewinkel +Copyright © 2017–2018 Albert Krewinkel This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -17,7 +17,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -} {- | Module : Text.Pandoc.Lua - Copyright : Copyright © 2017 Albert Krewinkel + Copyright : Copyright © 2017–2018 Albert Krewinkel License : GNU GPL, version 2 or above Maintainer : Albert Krewinkel -- cgit v1.2.3 From 5b852f8d2ad6e2d9713e894bbb80489c1d383847 Mon Sep 17 00:00:00 2001 From: Albert Krewinkel Date: Fri, 12 Jan 2018 08:56:33 +0100 Subject: Lua filters: make PANDOC_READER_OPTIONS available The options which were used to read the document are made available to Lua filters via the `PANDOC_READER_OPTIONS` global. --- src/Text/Pandoc/Lua.hs | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) (limited to 'src/Text/Pandoc/Lua.hs') diff --git a/src/Text/Pandoc/Lua.hs b/src/Text/Pandoc/Lua.hs index 48518aa54..edf803b45 100644 --- a/src/Text/Pandoc/Lua.hs +++ b/src/Text/Pandoc/Lua.hs @@ -39,21 +39,22 @@ import Text.Pandoc.Definition (Pandoc) import Text.Pandoc.Lua.Filter (LuaFilter, walkMWithLuaFilter) import Text.Pandoc.Lua.Init (runPandocLua) import Text.Pandoc.Lua.Util (popValue) +import Text.Pandoc.Options (ReaderOptions) import qualified Foreign.Lua as Lua -- | Run the Lua filter in @filterPath@ for a transformation to target -- format @format@. Pandoc uses Lua init files to setup the Lua -- interpreter. -runLuaFilter :: FilePath -> String +runLuaFilter :: ReaderOptions -> FilePath -> String -> Pandoc -> PandocIO (Either LuaException Pandoc) -runLuaFilter filterPath format doc = - runPandocLua (runLuaFilter' filterPath format doc) +runLuaFilter ropts filterPath format doc = + runPandocLua (runLuaFilter' ropts filterPath format doc) -runLuaFilter' :: FilePath -> String +runLuaFilter' :: ReaderOptions -> FilePath -> String -> Pandoc -> Lua Pandoc -runLuaFilter' filterPath format pd = do - -- store module in global "pandoc" +runLuaFilter' ropts filterPath format pd = do registerFormat + registerReaderOptions top <- Lua.gettop stat <- Lua.dofile filterPath if stat /= OK @@ -73,5 +74,9 @@ runLuaFilter' filterPath format pd = do push format Lua.setglobal "FORMAT" + registerReaderOptions = do + push ropts + Lua.setglobal "PANDOC_READER_OPTIONS" + runAll :: [LuaFilter] -> Pandoc -> Lua Pandoc runAll = foldr ((>=>) . walkMWithLuaFilter) return -- cgit v1.2.3 From b8ffd834cff717fe424f22e506351f2ecec4655a Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Fri, 19 Jan 2018 21:25:24 -0800 Subject: hlint code improvements. --- src/Text/Pandoc/Lua.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/Text/Pandoc/Lua.hs') diff --git a/src/Text/Pandoc/Lua.hs b/src/Text/Pandoc/Lua.hs index edf803b45..790be47d5 100644 --- a/src/Text/Pandoc/Lua.hs +++ b/src/Text/Pandoc/Lua.hs @@ -65,7 +65,7 @@ runLuaFilter' ropts filterPath format pd = do newtop <- Lua.gettop -- Use the returned filters, or the implicitly defined global filter if -- nothing was returned. - luaFilters <- if (newtop - top >= 1) + luaFilters <- if newtop - top >= 1 then peek (-1) else Lua.getglobal "_G" *> fmap (:[]) popValue runAll luaFilters pd -- cgit v1.2.3 From b5bd8a9461dc317ff61abec68feba4a86d39e9f2 Mon Sep 17 00:00:00 2001 From: Albert Krewinkel Date: Sat, 24 Feb 2018 21:59:50 +0100 Subject: Lua: register script name in global variable The name of the Lua script which is executed is made available in the global Lua variable `PANDOC_SCRIPT_FILE`, both for Lua filters and custom writers. Closes: #4393 --- src/Text/Pandoc/Lua.hs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'src/Text/Pandoc/Lua.hs') diff --git a/src/Text/Pandoc/Lua.hs b/src/Text/Pandoc/Lua.hs index 790be47d5..79955509d 100644 --- a/src/Text/Pandoc/Lua.hs +++ b/src/Text/Pandoc/Lua.hs @@ -37,7 +37,7 @@ import Foreign.Lua (FromLuaStack (peek), Lua, LuaException (..), import Text.Pandoc.Class (PandocIO) import Text.Pandoc.Definition (Pandoc) import Text.Pandoc.Lua.Filter (LuaFilter, walkMWithLuaFilter) -import Text.Pandoc.Lua.Init (runPandocLua) +import Text.Pandoc.Lua.Init (runPandocLua, registerScriptPath) import Text.Pandoc.Lua.Util (popValue) import Text.Pandoc.Options (ReaderOptions) import qualified Foreign.Lua as Lua @@ -55,11 +55,12 @@ runLuaFilter' :: ReaderOptions -> FilePath -> String runLuaFilter' ropts filterPath format pd = do registerFormat registerReaderOptions + registerScriptPath filterPath top <- Lua.gettop stat <- Lua.dofile filterPath if stat /= OK then do - luaErrMsg <- peek (-1) <* Lua.pop 1 + luaErrMsg <- popValue Lua.throwLuaError luaErrMsg else do newtop <- Lua.gettop -- cgit v1.2.3