aboutsummaryrefslogtreecommitdiff
path: root/src/Text/Pandoc/Readers/LaTeX/Macro.hs
diff options
context:
space:
mode:
Diffstat (limited to 'src/Text/Pandoc/Readers/LaTeX/Macro.hs')
-rw-r--r--src/Text/Pandoc/Readers/LaTeX/Macro.hs117
1 files changed, 82 insertions, 35 deletions
diff --git a/src/Text/Pandoc/Readers/LaTeX/Macro.hs b/src/Text/Pandoc/Readers/LaTeX/Macro.hs
index 5495a8e74..d40277eb5 100644
--- a/src/Text/Pandoc/Readers/LaTeX/Macro.hs
+++ b/src/Text/Pandoc/Readers/LaTeX/Macro.hs
@@ -15,6 +15,8 @@ import Control.Applicative ((<|>), optional)
import qualified Data.Map as M
import Data.Text (Text)
import qualified Data.Text as T
+import qualified Data.List.NonEmpty as NonEmpty
+import Data.List.NonEmpty (NonEmpty(..))
macroDef :: (PandocMonad m, Monoid a) => (Text -> a) -> LP m a
macroDef constructor = do
@@ -23,51 +25,91 @@ macroDef constructor = do
guardDisabled Ext_latex_macros)
<|> return mempty
where commandDef = do
- nameMacroPairs <- newcommand <|> letmacro <|> defmacro <|> newif
+ nameMacroPairs <- newcommand <|>
+ checkGlobal (letmacro <|> edefmacro <|> defmacro <|> newif)
guardDisabled Ext_latex_macros <|>
- mapM_ (\(name, macro') ->
- updateState (\s -> s{ sMacros = M.insert name macro'
- (sMacros s) })) nameMacroPairs
+ mapM_ insertMacro nameMacroPairs
environmentDef = do
mbenv <- newenvironment
case mbenv of
Nothing -> return ()
Just (name, macro1, macro2) ->
guardDisabled Ext_latex_macros <|>
- do updateState $ \s -> s{ sMacros =
- M.insert name macro1 (sMacros s) }
- updateState $ \s -> s{ sMacros =
- M.insert ("end" <> name) macro2 (sMacros s) }
+ do insertMacro (name, macro1)
+ insertMacro ("end" <> name, macro2)
-- @\newenvironment{envname}[n-args][default]{begin}{end}@
-- is equivalent to
-- @\newcommand{\envname}[n-args][default]{begin}@
-- @\newcommand{\endenvname}@
+insertMacro :: PandocMonad m => (Text, Macro) -> LP m ()
+insertMacro (name, macro'@(Macro GlobalScope _ _ _ _)) =
+ updateState $ \s ->
+ s{ sMacros = NonEmpty.map (M.insert name macro') (sMacros s) }
+insertMacro (name, macro'@(Macro GroupScope _ _ _ _)) =
+ updateState $ \s ->
+ s{ sMacros = M.insert name macro' (NonEmpty.head (sMacros s)) :|
+ NonEmpty.tail (sMacros s) }
+
+lookupMacro :: PandocMonad m => Text -> LP m Macro
+lookupMacro name = do
+ macros :| _ <- sMacros <$> getState
+ case M.lookup name macros of
+ Just m -> return m
+ Nothing -> fail "Macro not found"
+
letmacro :: PandocMonad m => LP m [(Text, Macro)]
letmacro = do
controlSeq "let"
- (name, contents) <- withVerbatimMode $ do
+ withVerbatimMode $ do
Tok _ (CtrlSeq name) _ <- anyControlSeq
optional $ symbol '='
spaces
-- we first parse in verbatim mode, and then expand macros,
-- because we don't want \let\foo\bar to turn into
-- \let\foo hello if we have previously \def\bar{hello}
+ target <- anyControlSeq <|> singleChar
+ case target of
+ (Tok _ (CtrlSeq name') _) ->
+ (do m <- lookupMacro name'
+ pure [(name, m)])
+ <|> pure [(name,
+ Macro GroupScope ExpandWhenDefined [] Nothing [target])]
+ _ -> pure [(name, Macro GroupScope ExpandWhenDefined [] Nothing [target])]
+
+checkGlobal :: PandocMonad m => LP m [(Text, Macro)] -> LP m [(Text, Macro)]
+checkGlobal p =
+ (controlSeq "global" *>
+ (map (\(n, Macro _ expand arg optarg contents) ->
+ (n, Macro GlobalScope expand arg optarg contents)) <$> p))
+ <|> p
+
+edefmacro :: PandocMonad m => LP m [(Text, Macro)]
+edefmacro = do
+ scope <- (GroupScope <$ controlSeq "edef")
+ <|> (GlobalScope <$ controlSeq "xdef")
+ (name, contents) <- withVerbatimMode $ do
+ Tok _ (CtrlSeq name) _ <- anyControlSeq
+ -- we first parse in verbatim mode, and then expand macros,
+ -- because we don't want \let\foo\bar to turn into
+ -- \let\foo hello if we have previously \def\bar{hello}
contents <- bracedOrToken
return (name, contents)
- contents' <- doMacros' 0 contents
- return [(name, Macro ExpandWhenDefined [] Nothing contents')]
+ -- expand macros
+ contents' <- parseFromToks (many anyTok) contents
+ return [(name, Macro scope ExpandWhenDefined [] Nothing contents')]
defmacro :: PandocMonad m => LP m [(Text, Macro)]
defmacro = do
-- we use withVerbatimMode, because macros are to be expanded
-- at point of use, not point of definition
- controlSeq "def"
+ scope <- (GroupScope <$ controlSeq "def")
+ <|> (GlobalScope <$ controlSeq "gdef")
withVerbatimMode $ do
Tok _ (CtrlSeq name) _ <- anyControlSeq
argspecs <- many (argspecArg <|> argspecPattern)
contents <- bracedOrToken
- return [(name, Macro ExpandWhenUsed argspecs Nothing contents)]
+ return [(name, Macro scope ExpandWhenUsed argspecs Nothing contents)]
-- \newif\iffoo' defines:
-- \iffoo to be \iffalse
@@ -82,16 +124,16 @@ newif = do
-- \def\footrue{\def\iffoo\iftrue}
-- \def\foofalse{\def\iffoo\iffalse}
let base = T.drop 2 name
- return [ (name, Macro ExpandWhenUsed [] Nothing
+ return [ (name, Macro GroupScope ExpandWhenUsed [] Nothing
[Tok pos (CtrlSeq "iffalse") "\\iffalse"])
, (base <> "true",
- Macro ExpandWhenUsed [] Nothing
+ Macro GroupScope ExpandWhenUsed [] Nothing
[ Tok pos (CtrlSeq "def") "\\def"
, Tok pos (CtrlSeq name) ("\\" <> name)
, Tok pos (CtrlSeq "iftrue") "\\iftrue"
])
, (base <> "false",
- Macro ExpandWhenUsed [] Nothing
+ Macro GroupScope ExpandWhenUsed [] Nothing
[ Tok pos (CtrlSeq "def") "\\def"
, Tok pos (CtrlSeq name) ("\\" <> name)
, Tok pos (CtrlSeq "iffalse") "\\iffalse"
@@ -138,14 +180,13 @@ newcommand = do
: (contents' ++
[ Tok pos Symbol "}", Tok pos Symbol "}" ])
_ -> contents'
- macros <- sMacros <$> getState
- case M.lookup name macros of
- Just macro
- | mtype == "newcommand" -> do
- report $ MacroAlreadyDefined txt pos
- return [(name, macro)]
- | mtype == "providecommand" -> return [(name, macro)]
- _ -> return [(name, Macro ExpandWhenUsed argspecs optarg contents)]
+ let macro = Macro GroupScope ExpandWhenUsed argspecs optarg contents
+ (do lookupMacro name
+ case mtype of
+ "providecommand" -> return []
+ "renewcommand" -> return [(name, macro)]
+ _ -> [] <$ report (MacroAlreadyDefined txt pos))
+ <|> pure [(name, macro)]
newenvironment :: PandocMonad m => LP m (Maybe (Text, Macro, Macro))
newenvironment = do
@@ -164,17 +205,23 @@ newenvironment = do
let argspecs = map (\i -> ArgNum i) [1..numargs]
startcontents <- spaces >> bracedOrToken
endcontents <- spaces >> bracedOrToken
- macros <- sMacros <$> getState
- case M.lookup name macros of
- Just _
- | mtype == "newenvironment" -> do
- report $ MacroAlreadyDefined name pos
- return Nothing
- | mtype == "provideenvironment" ->
- return Nothing
- _ -> return $ Just (name,
- Macro ExpandWhenUsed argspecs optarg startcontents,
- Macro ExpandWhenUsed [] Nothing endcontents)
+ -- we need the environment to be in a group so macros defined
+ -- inside behave correctly:
+ let bg = Tok pos (CtrlSeq "bgroup") "\\bgroup "
+ let eg = Tok pos (CtrlSeq "egroup") "\\egroup "
+ let result = (name,
+ Macro GroupScope ExpandWhenUsed argspecs optarg
+ (bg:startcontents),
+ Macro GroupScope ExpandWhenUsed [] Nothing
+ (endcontents ++ [eg]))
+ (do lookupMacro name
+ case mtype of
+ "provideenvironment" -> return Nothing
+ "renewenvironment" -> return (Just result)
+ _ -> do
+ report $ MacroAlreadyDefined name pos
+ return Nothing)
+ <|> return (Just result)
bracketedNum :: PandocMonad m => LP m Int
bracketedNum = do