git.iko.soy/typeable/pandoc
on master help sign in
LaTeX reader: improve `\def` and implement `\newif`.

- Improve parsing of `\def` macros. We previously set "verbatim mode" even for parsing the initial `\def`; this caused problems for things like ``` \def\foo{\def\bar{BAR}} \foo \bar ``` - Implement `\newif`. - Add tests.

commit81eadfd99ad3e905b806cc6c80ab0fea0185286f authorJohn MacFarlane <jgm@berkeley.edu> dateWed 26 May 2021, 22:50 (5 years ago) parente0a1f7d
3 files changed+118−15
src/Text/Pandoc/Readers/LaTeX/Macro.hs+45−14 src/Text/Pandoc/Readers/LaTeX/Parsing.hs+18−1 test/command/newif.md+55
src/Text/Pandoc/Readers/LaTeX/Macro.hs+45−14
@@ -14,6 +14,7 @@ import Text.Pandoc.Parsing hiding (blankline, mathDisplay, mathInline,
1414import Control.Applicative ((<|>), optional)
1515import qualified Data.Map as M
1616import Data.Text (Text)
17+import qualified Data.Text as T
1718
1819macroDef :: (PandocMonad m, Monoid a) => (Text -> a) -> LP m a
1920macroDef constructor = do
@@ -22,9 +23,11 @@ macroDef constructor = do
2223 guardDisabled Ext_latex_macros)
2324 <|> return mempty
2425 where commandDef = do
25− (name, macro') <- newcommand <|> letmacro <|> defmacro
26+ nameMacroPairs <- newcommand <|> letmacro <|> defmacro <|> newif
2627 guardDisabled Ext_latex_macros <|>
27− updateState (\s -> s{ sMacros = M.insert name macro' (sMacros s) })
28+ mapM_ (\(name, macro') ->
29+ updateState (\s -> s{ sMacros = M.insert name macro'
30+ (sMacros s) })) nameMacroPairs
2831 environmentDef = do
2932 mbenv <- newenvironment
3033 case mbenv of
@@ -40,7 +43,7 @@ macroDef constructor = do
4043 -- @\newcommand{\envname}[n-args][default]{begin}@
4144 -- @\newcommand{\endenvname}@
4245
43−letmacro :: PandocMonad m => LP m (Text, Macro)
46+letmacro :: PandocMonad m => LP m [(Text, Macro)]
4447letmacro = do
4548 controlSeq "let"
4649 (name, contents) <- withVerbatimMode $ do
@@ -53,18 +56,47 @@ letmacro = do
5356 contents <- bracedOrToken
5457 return (name, contents)
5558 contents' <- doMacros' 0 contents
56− return (name, Macro ExpandWhenDefined [] Nothing contents')
59+ return [(name, Macro ExpandWhenDefined [] Nothing contents')]
5760
58−defmacro :: PandocMonad m => LP m (Text, Macro)
59−defmacro = try $
61+defmacro :: PandocMonad m => LP m [(Text, Macro)]
62+defmacro = do
6063 -- we use withVerbatimMode, because macros are to be expanded
6164 -- at point of use, not point of definition
65+ controlSeq "def"
6266 withVerbatimMode $ do
63− controlSeq "def"
6467 Tok _ (CtrlSeq name) _ <- anyControlSeq
6568 argspecs <- many (argspecArg <|> argspecPattern)
6669 contents <- bracedOrToken
67− return (name, Macro ExpandWhenUsed argspecs Nothing contents)
70+ return [(name, Macro ExpandWhenUsed argspecs Nothing contents)]
71+
72+-- \newif\iffoo' defines:
73+-- \iffoo to be \iffalse
74+-- \footrue to be a command that defines \iffoo to be \iftrue
75+-- \foofalse to be a command that defines \iffoo to be \iffalse
76+newif :: PandocMonad m => LP m [(Text, Macro)]
77+newif = do
78+ controlSeq "newif"
79+ withVerbatimMode $ do
80+ Tok pos (CtrlSeq name) _ <- anyControlSeq
81+ -- \def\iffoo\iffalse
82+ -- \def\footrue{\def\iffoo\iftrue}
83+ -- \def\foofalse{\def\iffoo\iffalse}
84+ let base = T.drop 2 name
85+ return [ (name, Macro ExpandWhenUsed [] Nothing
86+ [Tok pos (CtrlSeq "iffalse") "\\iffalse"])
87+ , (base <> "true",
88+ Macro ExpandWhenUsed [] Nothing
89+ [ Tok pos (CtrlSeq "def") "\\def"
90+ , Tok pos (CtrlSeq name) ("\\" <> name)
91+ , Tok pos (CtrlSeq "iftrue") "\\iftrue"
92+ ])
93+ , (base <> "false",
94+ Macro ExpandWhenUsed [] Nothing
95+ [ Tok pos (CtrlSeq "def") "\\def"
96+ , Tok pos (CtrlSeq name) ("\\" <> name)
97+ , Tok pos (CtrlSeq "iffalse") "\\iffalse"
98+ ])
99+ ]
68100
69101argspecArg :: PandocMonad m => LP m ArgSpec
70102argspecArg = do
@@ -77,10 +109,9 @@ argspecPattern =
77109 (toktype' == Symbol || toktype' == Word) &&
78110 (txt /= "{" && txt /= "\\" && txt /= "}")))
79111
80−newcommand :: PandocMonad m => LP m (Text, Macro)
112+newcommand :: PandocMonad m => LP m [(Text, Macro)]
81113newcommand = do
82− pos <- getPosition
83− Tok _ (CtrlSeq mtype) _ <- controlSeq "newcommand" <|>
114+ Tok pos (CtrlSeq mtype) _ <- controlSeq "newcommand" <|>
84115 controlSeq "renewcommand" <|>
85116 controlSeq "providecommand" <|>
86117 controlSeq "DeclareMathOperator" <|>
@@ -112,9 +143,9 @@ newcommand = do
112143 Just macro
113144 | mtype == "newcommand" -> do
114145 report $ MacroAlreadyDefined txt pos
115− return (name, macro)
116− | mtype == "providecommand" -> return (name, macro)
117− _ -> return (name, Macro ExpandWhenUsed argspecs optarg contents)
146+ return [(name, macro)]
147+ | mtype == "providecommand" -> return [(name, macro)]
148+ _ -> return [(name, Macro ExpandWhenUsed argspecs optarg contents)]
118149
119150newenvironment :: PandocMonad m => LP m (Maybe (Text, Macro, Macro))
120151newenvironment = do
src/Text/Pandoc/Readers/LaTeX/Parsing.hs+18−1
@@ -113,7 +113,6 @@ import Text.Pandoc.Readers.LaTeX.Types (ExpansionPoint (..), Macro (..),
113113 ArgSpec (..), Tok (..), TokType (..))
114114import Text.Pandoc.Shared
115115import Text.Parsec.Pos
116−-- import Debug.Trace
117116
118117newtype DottedNum = DottedNum [Int]
119118 deriving (Show, Eq)
@@ -563,8 +562,26 @@ trySpecialMacro "xspace" ts = do
563562 Tok pos Word t : _
564563 | startsWithAlphaNum t -> return $ Tok pos Spaces " " : ts'
565564 _ -> return ts'
565+trySpecialMacro "iftrue" ts = handleIf True ts
566+trySpecialMacro "iffalse" ts = handleIf False ts
566567trySpecialMacro _ _ = mzero
567568
569+handleIf :: PandocMonad m => Bool -> [Tok] -> LP m [Tok]
570+handleIf b ts = do
571+ res' <- lift $ runParserT (ifParser b) defaultLaTeXState "tokens" ts
572+ case res' of
573+ Left _ -> Prelude.fail "Could not parse conditional"
574+ Right ts' -> return ts'
575+
576+ifParser :: PandocMonad m => Bool -> LP m [Tok]
577+ifParser b = do
578+ ifToks <- many (notFollowedBy (controlSeq "else" <|> controlSeq "fi")
579+ *> anyTok)
580+ elseToks <- (controlSeq "else" >> manyTill anyTok (controlSeq "fi"))
581+ <|> ([] <$ controlSeq "fi")
582+ rest <- getInput
583+ return $ (if b then ifToks else elseToks) ++ rest
584+
568585startsWithAlphaNum :: Text -> Bool
569586startsWithAlphaNum t =
570587 case T.uncons t of
test/command/newif.mdnew file+55
@@ -0,0 +1,55 @@
1+```
2+% pandoc -f latex -t plain
3+\iftrue
4+should print
5+\iftrue
6+should print
7+\else
8+should not print
9+\fi
10+\else
11+should not print
12+\fi
13+
14+\iffalse
15+should not print
16+\else
17+\iftrue
18+should print
19+\else
20+should not print
21+\fi
22+\fi
23+
24+\newif\ifepub
25+
26+\ifepub
27+should not print
28+\fi
29+
30+\epubtrue
31+
32+\ifepub
33+should print
34+\else
35+should not print
36+\fi
37+
38+\epubfalse
39+
40+\ifepub
41+should not print
42+\else
43+should print
44+\fi
45+^D
46+should print
47+
48+should print
49+
50+should print
51+
52+should print
53+
54+should print
55+```