From cb25326fa313690c3c67caa2a8b44642409fd24c Mon Sep 17 00:00:00 2001
From: John MacFarlane <jgm@berkeley.edu>
Date: Thu, 29 Jun 2017 17:07:30 +0200
Subject: [PATCH] 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(-)

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 <tarleb+pandoc@moltkeplatz.de>
 
@@ -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