git.iko.soy/typeable/pandoc
on master help sign in
Rewrote LaTeX reader with proper tokenization.

This rewrite is primarily motivated by the need to get macros working properly. A side benefit is that the reader is significantly faster (27s -> 19s in one benchmark, and there is a lot of room for further optimization). We now tokenize the input text, then parse the token stream. Macros modify the token stream, so they should now be effective in any context, including math. Thus, we no longer need the clunky macro processing capacities of texmath. A custom state LaTeXState is used instead of ParserState. This, plus the tokenization, will require some rewriting of the exported functions rawLaTeXInline, inlineCommand, rawLaTeXBlock. * Added Text.Pandoc.Readers.LaTeX.Types (new exported module). Exports Macro, Tok, TokType, Line, Column. [API change] * Text.Pandoc.Parsing: adjusted type of `insertIncludedFile` so it can be used with token parser. * Removed old texmath macro stuff from Parsing. Use Macro from Text.Pandoc.Readers.LaTeX.Types instead. * Removed texmath macro material from Markdown reader. * Changed types for Text.Pandoc.Readers.LaTeX's rawLaTeXInline and rawLaTeXBlock. (Both now return a String, and they are polymorphic in state.) * Added orgMacros field to OrgState. [API change] * Removed readerApplyMacros from ReaderOptions. Now we just check the `latex_macros` reader extension. * Allow `\newcommand\foo{blah}` without braces. Fixes #1390. Fixes #2118. Fixes #3236. Fixes #3779. Fixes #934. Fixes #982.

commit0feb7504b1c68cef76b30ea9987e2eae3101714c authorJohn MacFarlane <jgm@berkeley.edu> dateSat 1 Jul 2017, 19:31 (9 years ago) parent1dd769e
27 files changed+1876−1177
pandoc.cabal+1 src/Text/Pandoc/App.hs−1 src/Text/Pandoc/Error.hs+3 src/Text/Pandoc/Extensions.hs+1 src/Text/Pandoc/Options.hs−2 src/Text/Pandoc/Parsing.hs+22−45 src/Text/Pandoc/Readers/LaTeX.hs+1671−1106 src/Text/Pandoc/Readers/LaTeX/Types.hs+48 src/Text/Pandoc/Readers/Markdown.hs+10−9 src/Text/Pandoc/Readers/Muse.hs+1−1 src/Text/Pandoc/Readers/Org/Inlines.hs+3−2 src/Text/Pandoc/Readers/Org/ParserState.hs+10 src/Text/Pandoc/Readers/TWiki.hs+1−1 src/Text/Pandoc/Readers/Textile.hs+1−1 src/Text/Pandoc/Readers/Txt2Tags.hs+1−1 test/Tests/Readers/LaTeX.hs+2−1 test/command/1390.md+20 test/command/2118.md+11 test/command/3113.md+1−1 test/command/3236.md+9 test/command/3558.md+6−2 test/command/3779.md+28 test/command/934.md+12 test/command/982.md+11 test/latex-reader.latex−1 test/latex-reader.native+1−1 test/markdown-reader-more.native+2−2
pandoc.cabal+1
@@ -366,6 +366,7 @@ Library
366366 Text.Pandoc.Readers,
367367 Text.Pandoc.Readers.HTML,
368368 Text.Pandoc.Readers.LaTeX,
369+ Text.Pandoc.Readers.LaTeX.Types,
369370 Text.Pandoc.Readers.Markdown,
370371 Text.Pandoc.Readers.CommonMark,
371372 Text.Pandoc.Readers.MediaWiki,
src/Text/Pandoc/App.hs−1
@@ -308,7 +308,6 @@ convertWithOpts opts = do
308308 , readerColumns = optColumns opts
309309 , readerTabStop = optTabStop opts
310310 , readerIndentedCodeClasses = optIndentedCodeClasses opts
311− , readerApplyMacros = not laTeXOutput
312311 , readerDefaultImageExtension =
313312 optDefaultImageExtension opts
314313 , readerTrackChanges = optTrackChanges opts
src/Text/Pandoc/Error.hs+3
@@ -64,6 +64,7 @@ data PandocError = PandocIOError String IOError
6464 | PandocTemplateError String
6565 | PandocAppError String
6666 | PandocEpubSubdirectoryError String
67+ | PandocMacroLoop String
6768 deriving (Show, Typeable, Generic)
6869
6970instance Exception PandocError
@@ -107,6 +108,8 @@ handleError (Left e) =
107108 PandocAppError s -> err 1 s
108109 PandocEpubSubdirectoryError s -> err 31 $
109110 "EPUB subdirectory name '" ++ s ++ "' contains illegal characters"
111+ PandocMacroLoop s -> err 91 $
112+ "Loop encountered in expanding macro " ++ s
110113
111114err :: Int -> String -> IO a
112115err exitCode msg = do
src/Text/Pandoc/Extensions.hs+1
@@ -318,6 +318,7 @@ getDefaultExtensions "epub2" = getDefaultExtensions "epub"
318318getDefaultExtensions "epub3" = getDefaultExtensions "epub"
319319getDefaultExtensions "latex" = extensionsFromList
320320 [Ext_smart,
321+ Ext_latex_macros,
321322 Ext_auto_identifiers]
322323getDefaultExtensions "context" = extensionsFromList
323324 [Ext_smart,
src/Text/Pandoc/Options.hs−2
@@ -61,7 +61,6 @@ data ReaderOptions = ReaderOptions{
6161 , readerStandalone :: Bool -- ^ Standalone document with header
6262 , readerColumns :: Int -- ^ Number of columns in terminal
6363 , readerTabStop :: Int -- ^ Tab stop
64− , readerApplyMacros :: Bool -- ^ Apply macros to TeX math
6564 , readerIndentedCodeClasses :: [String] -- ^ Default classes for
6665 -- indented code blocks
6766 , readerAbbreviations :: Set.Set String -- ^ Strings to treat as abbreviations
@@ -75,7 +74,6 @@ instance Default ReaderOptions
7574 , readerStandalone = False
7675 , readerColumns = 80
7776 , readerTabStop = 4
78− , readerApplyMacros = True
7977 , readerIndentedCodeClasses = []
8078 , readerAbbreviations = defaultAbbrevs
8179 , readerDefaultImageExtension = ""
src/Text/Pandoc/Parsing.hs+22−45
@@ -109,8 +109,6 @@ module Text.Pandoc.Parsing ( anyLine,
109109 dash,
110110 nested,
111111 citeKey,
112− macro,
113− applyMacros',
114112 Parser,
115113 ParserT,
116114 F,
@@ -130,6 +128,7 @@ module Text.Pandoc.Parsing ( anyLine,
130128 runParser,
131129 runParserT,
132130 parse,
131+ tokenPrim,
133132 anyToken,
134133 getInput,
135134 setInput,
@@ -178,13 +177,16 @@ module Text.Pandoc.Parsing ( anyLine,
178177 sourceLine,
179178 setSourceColumn,
180179 setSourceLine,
181− newPos
180+ newPos,
181+ Line,
182+ Column
182183 )
183184where
184185
186+import Data.Text (Text)
185187import Text.Pandoc.Definition
186188import Text.Pandoc.Options
187−import Text.Pandoc.Builder (Blocks, Inlines, rawBlock, HasMeta(..), trimInlines)
189+import Text.Pandoc.Builder (Blocks, Inlines, HasMeta(..), trimInlines)
188190import qualified Text.Pandoc.Builder as B
189191import Text.Pandoc.XML (fromEntities)
190192import qualified Text.Pandoc.UTF8 as UTF8 (putStrLn)
@@ -195,7 +197,7 @@ import Data.Char ( toLower, toUpper, ord, chr, isAscii, isAlphaNum,
195197import Data.List ( intercalate, transpose, isSuffixOf )
196198import Text.Pandoc.Shared
197199import qualified Data.Map as M
198−import Text.TeXMath.Readers.TeX.Macros (applyMacros, Macro, pMacroDefinition)
200+import Text.Pandoc.Readers.LaTeX.Types (Macro)
199201import Text.HTML.TagSoup.Entity ( lookupEntity )
200202import Text.Pandoc.Asciify (toAsciiChar)
201203import Data.Monoid ((<>))
@@ -994,7 +996,7 @@ data ParserState = ParserState
994996 stateIdentifiers :: Set.Set String, -- ^ Header identifiers used
995997 stateNextExample :: Int, -- ^ Number of next example
996998 stateExamples :: M.Map String Int, -- ^ Map from example labels to numbers
997− stateMacros :: [Macro], -- ^ List of macros defined so far
999+ stateMacros :: M.Map Text Macro, -- ^ Table of macros defined so far
9981000 stateRstDefaultRole :: String, -- ^ Current rST default interpreted text role
9991001 stateRstCustomRoles :: M.Map String (String, Maybe String, Attr), -- ^ Current rST custom text roles
10001002 -- Triple represents: 1) Base role, 2) Optional format (only for :raw:
@@ -1057,8 +1059,8 @@ instance HasIdentifierList ParserState where
10571059 updateIdentifierList f st = st{ stateIdentifiers = f $ stateIdentifiers st }
10581060
10591061class HasMacros st where
1060− extractMacros :: st -> [Macro]
1061− updateMacros :: ([Macro] -> [Macro]) -> st -> st
1062+ extractMacros :: st -> M.Map Text Macro
1063+ updateMacros :: (M.Map Text Macro -> M.Map Text Macro) -> st -> st
10621064
10631065instance HasMacros ParserState where
10641066 extractMacros = stateMacros
@@ -1112,7 +1114,7 @@ defaultParserState =
11121114 stateIdentifiers = Set.empty,
11131115 stateNextExample = 1,
11141116 stateExamples = M.empty,
1115− stateMacros = [],
1117+ stateMacros = M.empty,
11161118 stateRstDefaultRole = "title-reference",
11171119 stateRstCustomRoles = M.empty,
11181120 stateCaption = Nothing,
@@ -1341,33 +1343,6 @@ token :: (Stream s m t)
13411343 -> ParsecT s st m a
13421344token pp pos match = tokenPrim pp (\_ t _ -> pos t) match
13431345
1344−--
1345−-- Macros
1346−--
1347−
1348−-- | Parse a \newcommand or \newenviroment macro definition.
1349−macro :: (Stream [Char] m Char, HasMacros st, HasReaderOptions st)
1350− => ParserT [Char] st m Blocks
1351−macro = do
1352− apply <- getOption readerApplyMacros
1353− (m, def') <- withRaw pMacroDefinition
1354− if apply
1355− then do
1356− updateState $ \st -> updateMacros (m:) st
1357− return mempty
1358− else return $ rawBlock "latex" def'
1359−
1360−-- | Apply current macros to string.
1361−applyMacros' :: (HasReaderOptions st, HasMacros st, Stream [Char] m Char)
1362− => String
1363− -> ParserT [Char] st m String
1364−applyMacros' target = do
1365− apply <- getOption readerApplyMacros
1366− if apply
1367− then do macros <- extractMacros <$> getState
1368− return $ applyMacros macros target
1369− else return target
1370−
13711346infixr 5 <+?>
13721347(<+?>) :: (Monoid a) => ParserT s st m a -> ParserT s st m a -> ParserT s st m a
13731348a <+?> b = a >>= flip fmap (try b <|> return mempty) . (<>)
@@ -1385,10 +1360,11 @@ extractIdClass (ident, cls, kvs) = (ident', cls', kvs')
13851360
13861361insertIncludedFile' :: (PandocMonad m, HasIncludeFiles st,
13871362 Functor mf, Applicative mf, Monad mf)
1388− => ParserT String st m (mf Blocks)
1363+ => ParserT [a] st m (mf Blocks)
1364+ -> (String -> [a])
13891365 -> [FilePath] -> FilePath
1390− -> ParserT String st m (mf Blocks)
1391−insertIncludedFile' blocks dirs f = do
1366+ -> ParserT [a] st m (mf Blocks)
1367+insertIncludedFile' blocks totoks dirs f = do
13921368 oldPos <- getPosition
13931369 oldInput <- getInput
13941370 containers <- getIncludeFiles <$> getState
@@ -1402,7 +1378,7 @@ insertIncludedFile' blocks dirs f = do
14021378 report $ CouldNotLoadIncludeFile f oldPos
14031379 return ""
14041380 setPosition $ newPos f 1 1
1405− setInput contents
1381+ setInput $ totoks contents
14061382 bs <- blocks
14071383 setInput oldInput
14081384 setPosition oldPos
@@ -1412,11 +1388,12 @@ insertIncludedFile' blocks dirs f = do
14121388-- | Parse content of include file as blocks. Circular includes result in an
14131389-- @PandocParseError@.
14141390insertIncludedFile :: (PandocMonad m, HasIncludeFiles st)
1415− => ParserT String st m Blocks
1391+ => ParserT [a] st m Blocks
1392+ -> (String -> [a])
14161393 -> [FilePath] -> FilePath
1417− -> ParserT String st m Blocks
1418−insertIncludedFile blocks dirs f =
1419− runIdentity <$> insertIncludedFile' (Identity <$> blocks) dirs f
1394+ -> ParserT [a] st m Blocks
1395+insertIncludedFile blocks totoks dirs f =
1396+ runIdentity <$> insertIncludedFile' (Identity <$> blocks) totoks dirs f
14201397
14211398-- | Parse content of include file as future blocks. Circular includes result in
14221399-- an @PandocParseError@.
@@ -1424,4 +1401,4 @@ insertIncludedFileF :: (PandocMonad m, HasIncludeFiles st)
14241401 => ParserT String st m (Future st Blocks)
14251402 -> [FilePath] -> FilePath
14261403 -> ParserT String st m (Future st Blocks)
1427−insertIncludedFileF = insertIncludedFile'
1404+insertIncludedFileF p = insertIncludedFile' p id
src/Text/Pandoc/Readers/LaTeX.hs+1671−1106
@@ -1,4 +1,7 @@
11{-# LANGUAGE OverloadedStrings #-}
2+{-# LANGUAGE PatternGuards #-}
3+{-# LANGUAGE FlexibleInstances #-}
4+{-# LANGUAGE MultiParamTypeClasses #-}
25{-# LANGUAGE ScopedTypeVariables #-}
36{-
47Copyright (C) 2006-2017 John MacFarlane <jgm@berkeley.edu>
@@ -28,20 +31,26 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
2831 Portability : portable
2932
3033Conversion of LaTeX to 'Pandoc' document.
34+
3135-}
3236module Text.Pandoc.Readers.LaTeX ( readLaTeX,
37+ applyMacros,
3338 rawLaTeXInline,
3439 rawLaTeXBlock,
35− inlineCommand,
40+ macro,
41+ inlineCommand
3642 ) where
3743
3844import Control.Applicative (many, optional, (<|>))
3945import Control.Monad
4046import Control.Monad.Except (throwError)
41−import Data.Char (chr, isAlphaNum, isLetter, ord)
42−import Data.Text (Text, unpack)
47+import Data.Char (chr, isAlphaNum, isLetter, ord, isDigit)
48+import Data.Default
49+import Data.Text (Text)
50+import qualified Data.Text as T
4351import Data.List (intercalate, isPrefixOf)
4452import qualified Data.Map as M
53+import qualified Data.Set as Set
4554import Data.Maybe (fromMaybe, maybeToList)
4655import Safe (minimumDef)
4756import System.FilePath (addExtension, replaceExtension, takeExtension)
@@ -52,10 +61,19 @@ import Text.Pandoc.Highlighting (fromListingsLanguage, languagesByExtension)
5261import Text.Pandoc.ImageSize (numUnit, showFl)
5362import Text.Pandoc.Logging
5463import Text.Pandoc.Options
55−import Text.Pandoc.Parsing hiding (many, mathDisplay, mathInline, optional,
56− space, (<|>))
64+import Text.Pandoc.Parsing hiding (many, optional, withRaw,
65+ mathInline, mathDisplay,
66+ space, (<|>), spaces, blankline)
5767import Text.Pandoc.Shared
68+import Text.Pandoc.Readers.LaTeX.Types (Macro(..), Tok(..),
69+ TokType(..), Line, Column)
5870import Text.Pandoc.Walk
71+import Text.Pandoc.Error (PandocError(PandocParsecError, PandocMacroLoop))
72+
73+-- for debugging:
74+-- import Text.Pandoc.Extensions (getDefaultExtensions)
75+-- import Text.Pandoc.Class (runIOorExplode, PandocIO)
76+-- import Debug.Trace (traceShowId)
5977
6078-- | Parse LaTeX from string and return 'Pandoc' document.
6179readLaTeX :: PandocMonad m
@@ -63,18 +81,18 @@ readLaTeX :: PandocMonad m
6381 -> Text -- ^ String to parse (assumes @'\n'@ line endings)
6482 -> m Pandoc
6583readLaTeX opts ltx = do
66− parsed <- readWithM parseLaTeX def{ stateOptions = opts }
67− (unpack (crFilter ltx))
84+ parsed <- runParserT parseLaTeX def{ sOptions = opts } "source"
85+ (tokenize (crFilter ltx))
6886 case parsed of
6987 Right result -> return result
70− Left e -> throwError e
88+ Left e -> throwError $ PandocParsecError (T.unpack ltx) e
7189
7290parseLaTeX :: PandocMonad m => LP m Pandoc
7391parseLaTeX = do
7492 bs <- blocks
7593 eof
7694 st <- getState
77− let meta = stateMeta st
95+ let meta = sMeta st
7896 let doc' = doc bs
7997 let headerLevel (Header n _ _) = [n]
8098 headerLevel _ = []
@@ -88,177 +106,476 @@ parseLaTeX = do
88106 else id) doc'
89107 return $ Pandoc meta bs'
90108
91−type LP m = ParserT String ParserState m
92−
93−anyControlSeq :: PandocMonad m => LP m String
94−anyControlSeq = do
95− char '\\'
96− next <- option '\n' anyChar
97− case next of
98− '\n' -> return ""
99− c | isLetter c -> (c:) <$> (many letter <* optional sp)
100− | otherwise -> return [c]
101−
102−controlSeq :: PandocMonad m => String -> LP m String
103−controlSeq name = try $ do
104− char '\\'
105− case name of
106− "" -> mzero
107− [c] | not (isLetter c) -> string [c]
108− cs -> string cs <* notFollowedBy letter <* optional sp
109− return name
110−
111−dimenarg :: PandocMonad m => LP m String
112−dimenarg = try $ do
113− ch <- option "" $ string "="
114− num <- many1 digit
115− dim <- oneOfStrings ["pt","pc","in","bp","cm","mm","dd","cc","sp"]
116− return $ ch ++ num ++ dim
109+-- testParser :: LP PandocIO a -> Text -> IO a
110+-- testParser p t = do
111+-- res <- runIOorExplode (runParserT p defaultLaTeXState{
112+-- sOptions = def{ readerExtensions =
113+-- enableExtension Ext_raw_tex $
114+-- getDefaultExtensions "latex" }} "source" (tokenize t))
115+-- case res of
116+-- Left e -> error (show e)
117+-- Right r -> return r
118+
119+data LaTeXState = LaTeXState{ sOptions :: ReaderOptions
120+ , sMeta :: Meta
121+ , sQuoteContext :: QuoteContext
122+ , sMacros :: M.Map Text Macro
123+ , sContainers :: [String]
124+ , sHeaders :: M.Map Inlines String
125+ , sLogMessages :: [LogMessage]
126+ , sIdentifiers :: Set.Set String
127+ , sVerbatimMode :: Bool
128+ , sCaption :: Maybe Inlines
129+ , sInListItem :: Bool
130+ , sInTableCell :: Bool
131+ }
132+ deriving Show
133+
134+defaultLaTeXState :: LaTeXState
135+defaultLaTeXState = LaTeXState{ sOptions = def
136+ , sMeta = nullMeta
137+ , sQuoteContext = NoQuote
138+ , sMacros = M.empty
139+ , sContainers = []
140+ , sHeaders = M.empty
141+ , sLogMessages = []
142+ , sIdentifiers = Set.empty
143+ , sVerbatimMode = False
144+ , sCaption = Nothing
145+ , sInListItem = False
146+ , sInTableCell = False
147+ }
148+
149+instance PandocMonad m => HasQuoteContext LaTeXState m where
150+ getQuoteContext = sQuoteContext <$> getState
151+ withQuoteContext context parser = do
152+ oldState <- getState
153+ let oldQuoteContext = sQuoteContext oldState
154+ setState oldState { sQuoteContext = context }
155+ result <- parser
156+ newState <- getState
157+ setState newState { sQuoteContext = oldQuoteContext }
158+ return result
159+
160+instance HasLogMessages LaTeXState where
161+ addLogMessage msg st = st{ sLogMessages = msg : sLogMessages st }
162+ getLogMessages st = reverse $ sLogMessages st
163+
164+instance HasIdentifierList LaTeXState where
165+ extractIdentifierList = sIdentifiers
166+ updateIdentifierList f st = st{ sIdentifiers = f $ sIdentifiers st }
167+
168+instance HasIncludeFiles LaTeXState where
169+ getIncludeFiles = sContainers
170+ addIncludeFile f s = s{ sContainers = f : sContainers s }
171+ dropLatestIncludeFile s = s { sContainers = drop 1 $ sContainers s }
172+
173+instance HasHeaderMap LaTeXState where
174+ extractHeaderMap = sHeaders
175+ updateHeaderMap f st = st{ sHeaders = f $ sHeaders st }
176+
177+instance HasMacros LaTeXState where
178+ extractMacros st = sMacros st
179+ updateMacros f st = st{ sMacros = f (sMacros st) }
180+
181+instance HasReaderOptions LaTeXState where
182+ extractReaderOptions = sOptions
183+
184+instance HasMeta LaTeXState where
185+ setMeta field val st =
186+ st{ sMeta = setMeta field val $ sMeta st }
187+ deleteMeta field st =
188+ st{ sMeta = deleteMeta field $ sMeta st }
189+
190+instance Default LaTeXState where
191+ def = defaultLaTeXState
192+
193+type LP m = ParserT [Tok] LaTeXState m
194+
195+withVerbatimMode :: PandocMonad m => LP m a -> LP m a
196+withVerbatimMode parser = do
197+ updateState $ \st -> st{ sVerbatimMode = True }
198+ result <- parser
199+ updateState $ \st -> st{ sVerbatimMode = False }
200+ return result
201+
202+rawLaTeXBlock :: (PandocMonad m, HasMacros s, HasReaderOptions s)
203+ => ParserT String s m String
204+rawLaTeXBlock = do
205+ lookAhead (try (char '\\' >> letter))
206+ inp <- getInput
207+ let toks = tokenize $ T.pack inp
208+ let rawblock = do
209+ (_, raw) <- try $
210+ withRaw (environment <|> macroDef <|> blockCommand)
211+ return raw
212+ pstate <- getState
213+ let lstate = def{ sOptions = extractReaderOptions pstate }
214+ res <- runParserT rawblock lstate "source" toks
215+ case res of
216+ Left _ -> mzero
217+ Right raw -> count (T.length (untokenize raw)) anyChar
218+
219+macro :: (PandocMonad m, HasMacros s, HasReaderOptions s)
220+ => ParserT String s m Blocks
221+macro = do
222+ guardEnabled Ext_latex_macros
223+ lookAhead (char '\\' *> oneOfStrings ["new", "renew", "provide"] *>
224+ oneOfStrings ["command", "environment"])
225+ inp <- getInput
226+ let toks = tokenize $ T.pack inp
227+ let rawblock = do
228+ (_, raw) <- withRaw $ try macroDef
229+ st <- getState
230+ return (raw, st)
231+ pstate <- getState
232+ let lstate = def{ sOptions = extractReaderOptions pstate
233+ , sMacros = extractMacros pstate }
234+ res <- runParserT rawblock lstate "source" toks
235+ case res of
236+ Left _ -> mzero
237+ Right (raw, st) -> do
238+ updateState (updateMacros (const $ sMacros st))
239+ mempty <$ count (T.length (untokenize raw)) anyChar
240+
241+applyMacros :: (PandocMonad m, HasMacros s, HasReaderOptions s)
242+ => String -> ParserT String s m String
243+applyMacros s = do
244+ (guardEnabled Ext_latex_macros >>
245+ do let retokenize = doMacros 0 *> (toksToString <$> getInput)
246+ pstate <- getState
247+ let lstate = def{ sOptions = extractReaderOptions pstate
248+ , sMacros = extractMacros pstate }
249+ res <- runParserT retokenize lstate "math" (tokenize (T.pack s))
250+ case res of
251+ Left e -> fail (show e)
252+ Right s' -> return s') <|> return s
253+
254+rawLaTeXInline :: (PandocMonad m, HasMacros s, HasReaderOptions s)
255+ => ParserT String s m String
256+rawLaTeXInline = do
257+ lookAhead (try (char '\\' >> letter) <|> char '$')
258+ inp <- getInput
259+ let toks = tokenize $ T.pack inp
260+ let rawinline = do
261+ (_, raw) <- try $ withRaw (inlineEnvironment <|> inlineCommand')
262+ st <- getState
263+ return (raw, st)
264+ pstate <- getState
265+ let lstate = def{ sOptions = extractReaderOptions pstate
266+ , sMacros = extractMacros pstate }
267+ res <- runParserT rawinline lstate "source" toks
268+ case res of
269+ Left _ -> mzero
270+ Right (raw, s) -> do
271+ updateState $ updateMacros (const $ sMacros s)
272+ count (T.length (untokenize raw)) anyChar
273+
274+inlineCommand :: PandocMonad m => ParserT String ParserState m Inlines
275+inlineCommand = do
276+ lookAhead (try (char '\\' >> letter) <|> char '$')
277+ inp <- getInput
278+ let toks = tokenize $ T.pack inp
279+ let rawinline = do
280+ (il, raw) <- try $ withRaw (inlineEnvironment <|> inlineCommand')
281+ st <- getState
282+ return (il, raw, st)
283+ pstate <- getState
284+ let lstate = def{ sOptions = extractReaderOptions pstate
285+ , sMacros = extractMacros pstate }
286+ res <- runParserT rawinline lstate "source" toks
287+ case res of
288+ Left _ -> mzero
289+ Right (il, raw, s) -> do
290+ updateState $ updateMacros (const $ sMacros s)
291+ count (T.length (untokenize raw)) anyChar
292+ return il
293+
294+tokenize :: Text -> [Tok]
295+tokenize = totoks (1, 1)
296+
297+totoks :: (Line, Column) -> Text -> [Tok]
298+totoks (lin,col) t =
299+ case T.uncons t of
300+ Nothing -> []
301+ Just (c, rest)
302+ | c == '\n' ->
303+ Tok (lin, col) Newline "\n"
304+ : totoks (lin + 1,1) rest
305+ | isSpaceOrTab c ->
306+ let (sps, rest') = T.span isSpaceOrTab t
307+ in Tok (lin, col) Spaces sps
308+ : totoks (lin, col + T.length sps) rest'
309+ | isAlphaNum c ->
310+ let (ws, rest') = T.span isAlphaNum t
311+ in Tok (lin, col) Word ws
312+ : totoks (lin, col + T.length ws) rest'
313+ | c == '%' ->
314+ let (cs, rest') = T.break (== '\n') rest
315+ in Tok (lin, col) Comment ("%" <> cs)
316+ : totoks (lin, col + 1 + T.length cs) rest'
317+ | c == '\\' ->
318+ case T.uncons rest of
319+ Nothing -> [Tok (lin, col) Symbol (T.singleton c)]
320+ Just (d, rest')
321+ | isLetter d ->
322+ let (ws, rest'') = T.span isLetter rest
323+ (ss, rest''') = T.span isSpaceOrTab rest''
324+ in Tok (lin, col) (CtrlSeq ws) ("\\" <> ws <> ss)
325+ : totoks (lin,
326+ col + 1 + T.length ws + T.length ss) rest'''
327+ | d == '\t' || d == '\n' ->
328+ Tok (lin, col) Symbol ("\\")
329+ : totoks (lin, col + 1) rest
330+ | otherwise ->
331+ Tok (lin, col) (CtrlSeq (T.singleton d)) (T.pack [c,d])
332+ : totoks (lin, col + 2) rest'
333+ | c == '#' ->
334+ let (t1, t2) = T.span (\d -> d >= '0' && d <= '9') rest
335+ in case safeRead (T.unpack t1) of
336+ Just i ->
337+ Tok (lin, col) (Arg i) ("#" <> t1)
338+ : totoks (lin, col + 1 + T.length t1) t2
339+ Nothing ->
340+ Tok (lin, col) Symbol ("#")
341+ : totoks (lin, col + 1) t2
342+ | c == '^' ->
343+ case T.uncons rest of
344+ Just ('^', rest') ->
345+ case T.uncons rest' of
346+ Just (d, rest'')
347+ | isLowerHex d ->
348+ case T.uncons rest'' of
349+ Just (e, rest''') | isLowerHex e ->
350+ Tok (lin, col) Esc2 (T.pack ['^','^',d,e])
351+ : totoks (lin, col + 4) rest'''
352+ _ ->
353+ Tok (lin, col) Esc1 (T.pack ['^','^',d])
354+ : totoks (lin, col + 3) rest''
355+ | d < '\128' ->
356+ Tok (lin, col) Esc1 (T.pack ['^','^',d])
357+ : totoks (lin, col + 3) rest''
358+ _ -> [Tok (lin, col) Symbol ("^"),
359+ Tok (lin, col + 1) Symbol ("^")]
360+ _ -> Tok (lin, col) Symbol ("^")
361+ : totoks (lin, col + 1) rest
362+ | otherwise ->
363+ Tok (lin, col) Symbol (T.singleton c) : totoks (lin, col + 1) rest
364+
365+ where isSpaceOrTab ' ' = True
366+ isSpaceOrTab '\t' = True
367+ isSpaceOrTab _ = False
368+
369+isLowerHex :: Char -> Bool
370+isLowerHex x = x >= '0' && x <= '9' || x >= 'a' && x <= 'f'
371+
372+untokenize :: [Tok] -> Text
373+untokenize = mconcat . map untoken
374+
375+untoken :: Tok -> Text
376+untoken (Tok _ _ t) = t
377+
378+satisfyTok :: PandocMonad m => (Tok -> Bool) -> LP m Tok
379+satisfyTok f =
380+ try $ do
381+ res <- tokenPrim (T.unpack . untoken) updatePos matcher
382+ doMacros 0 -- apply macros on remaining input stream
383+ return res
384+ where matcher t | f t = Just t
385+ | otherwise = Nothing
386+ updatePos :: SourcePos -> Tok -> [Tok] -> SourcePos
387+ updatePos spos _ (Tok (lin,col) _ _ : _) =
388+ setSourceColumn (setSourceLine spos lin) col
389+ updatePos spos _ [] = spos
390+
391+doMacros :: PandocMonad m => Int -> LP m ()
392+doMacros n = do
393+ verbatimMode <- sVerbatimMode <$> getState
394+ when (not verbatimMode) $ do
395+ inp <- getInput
396+ case inp of
397+ Tok spos (CtrlSeq "begin") _ : Tok _ Symbol "{" :
398+ Tok _ Word name : Tok _ Symbol "}" : ts
399+ -> handleMacros spos name ts
400+ Tok spos (CtrlSeq "end") _ : Tok _ Symbol "{" :
401+ Tok _ Word name : Tok _ Symbol "}" : ts
402+ -> handleMacros spos ("end" <> name) ts
403+ Tok spos (CtrlSeq name) _ : ts
404+ -> handleMacros spos name ts
405+ _ -> return ()
406+ where handleMacros spos name ts = do
407+ macros <- sMacros <$> getState
408+ case M.lookup name macros of
409+ Nothing -> return ()
410+ Just (Macro numargs optarg newtoks) -> do
411+ setInput ts
412+ let getarg = spaces >> braced
413+ args <- case optarg of
414+ Nothing -> count numargs getarg
415+ Just o ->
416+ (:) <$> option o bracketedToks
417+ <*> count (numargs - 1) getarg
418+ let addTok (Tok _ (Arg i) _) acc | i > 0
419+ , i <= numargs =
420+ map (setpos spos) (args !! (i - 1)) ++ acc
421+ addTok t acc = setpos spos t : acc
422+ ts' <- getInput
423+ setInput $ foldr addTok ts' newtoks
424+ if n > 20 -- detect macro expansion loops
425+ then throwError $ PandocMacroLoop (T.unpack name)
426+ else doMacros (n + 1)
427+
428+setpos :: (Line, Column) -> Tok -> Tok
429+setpos spos (Tok _ tt txt) = Tok spos tt txt
430+
431+anyControlSeq :: PandocMonad m => LP m Tok
432+anyControlSeq = satisfyTok isCtrlSeq
433+ where isCtrlSeq (Tok _ (CtrlSeq _) _) = True
434+ isCtrlSeq _ = False
435+
436+anySymbol :: PandocMonad m => LP m Tok
437+anySymbol = satisfyTok isSym
438+ where isSym (Tok _ Symbol _) = True
439+ isSym _ = False
440+
441+spaces :: PandocMonad m => LP m ()
442+spaces = skipMany (satisfyTok (tokTypeIn [Comment, Spaces, Newline]))
443+
444+spaces1 :: PandocMonad m => LP m ()
445+spaces1 = skipMany1 (satisfyTok (tokTypeIn [Comment, Spaces, Newline]))
446+
447+tokTypeIn :: [TokType] -> Tok -> Bool
448+tokTypeIn toktypes (Tok _ tt _) = tt `elem` toktypes
449+
450+controlSeq :: PandocMonad m => Text -> LP m Tok
451+controlSeq name = satisfyTok isNamed
452+ where isNamed (Tok _ (CtrlSeq n) _) = n == name
453+ isNamed _ = False
454+
455+symbol :: PandocMonad m => Char -> LP m Tok
456+symbol c = satisfyTok isc
457+ where isc (Tok _ Symbol d) = case T.uncons d of
458+ Just (c',_) -> c == c'
459+ _ -> False
460+ isc _ = False
461+
462+symbolIn :: PandocMonad m => [Char] -> LP m Tok
463+symbolIn cs = satisfyTok isInCs
464+ where isInCs (Tok _ Symbol d) = case T.uncons d of
465+ Just (c,_) -> c `elem` cs
466+ _ -> False
467+ isInCs _ = False
117468
118469sp :: PandocMonad m => LP m ()
119470sp = whitespace <|> endline
120471
121472whitespace :: PandocMonad m => LP m ()
122−whitespace = skipMany1 $ satisfy (\c -> c == ' ' || c == '\t')
473+whitespace = () <$ satisfyTok isSpaceTok
474+ where isSpaceTok (Tok _ Spaces _) = True
475+ isSpaceTok _ = False
123476
124−endline :: PandocMonad m => LP m ()
125−endline = try (newline >> lookAhead anyChar >> notFollowedBy blankline)
126−
127−isLowerHex :: Char -> Bool
128−isLowerHex x = x >= '0' && x <= '9' || x >= 'a' && x <= 'f'
477+newlineTok :: PandocMonad m => LP m ()
478+newlineTok = () <$ satisfyTok isNewlineTok
129479
130−tildeEscape :: PandocMonad m => LP m Char
131−tildeEscape = try $ do
132− string "^^"
133− c <- satisfy (\x -> x >= '\0' && x <= '\128')
134− d <- if isLowerHex c
135− then option "" $ count 1 (satisfy isLowerHex)
136− else return ""
137− if null d
138− then case ord c of
139− x | x >= 64 && x <= 127 -> return $ chr (x - 64)
140− | otherwise -> return $ chr (x + 64)
141− else return $ chr $ read ('0':'x':c:d)
480+isNewlineTok :: Tok -> Bool
481+isNewlineTok (Tok _ Newline _) = True
482+isNewlineTok _ = False
142483
143484comment :: PandocMonad m => LP m ()
144−comment = do
145− char '%'
146− skipMany (satisfy (/='\n'))
147− optional newline
148− return ()
485+comment = () <$ satisfyTok isCommentTok
486+ where isCommentTok (Tok _ Comment _) = True
487+ isCommentTok _ = False
488+
489+anyTok :: PandocMonad m => LP m Tok
490+anyTok = satisfyTok (const True)
149491
150−bgroup :: PandocMonad m => LP m ()
492+endline :: PandocMonad m => LP m ()
493+endline = try $ do
494+ newlineTok
495+ lookAhead anyTok
496+ notFollowedBy blankline
497+
498+blankline :: PandocMonad m => LP m ()
499+blankline = try $ skipMany whitespace *> newlineTok
500+
501+primEscape :: PandocMonad m => LP m Char
502+primEscape = do
503+ Tok _ toktype t <- satisfyTok (tokTypeIn [Esc1, Esc2])
504+ case toktype of
505+ Esc1 -> case T.uncons (T.drop 2 t) of
506+ Just (c, _)
507+ | c >= '\64' && c <= '\127' -> return (chr (ord c - 64))
508+ | otherwise -> return (chr (ord c + 64))
509+ Nothing -> fail "Empty content of Esc1"
510+ Esc2 -> case safeRead ('0':'x':T.unpack (T.drop 2 t)) of
511+ Just x -> return (chr x)
512+ Nothing -> fail $ "Could not read: " ++ T.unpack t
513+ _ -> fail "Expected an Esc1 or Esc2 token" -- should not happen
514+
515+bgroup :: PandocMonad m => LP m Tok
151516bgroup = try $ do
152− skipMany (spaceChar <|> try (newline <* notFollowedBy blankline))
153− () <$ char '{'
154− <|> () <$ controlSeq "bgroup"
155− <|> () <$ controlSeq "begingroup"
517+ skipMany sp
518+ symbol '{' <|> controlSeq "bgroup" <|> controlSeq "begingroup"
156519
157−egroup :: PandocMonad m => LP m ()
158−egroup = () <$ char '}'
159− <|> () <$ controlSeq "egroup"
160− <|> () <$ controlSeq "endgroup"
520+egroup :: PandocMonad m => LP m Tok
521+egroup = (symbol '}' <|> controlSeq "egroup" <|> controlSeq "endgroup")
161522
162−grouped :: PandocMonad m => Monoid a => LP m a -> LP m a
523+grouped :: (PandocMonad m, Monoid a) => LP m a -> LP m a
163524grouped parser = try $ do
164525 bgroup
165526 -- first we check for an inner 'grouped', because
166527 -- {{a,b}} should be parsed the same as {a,b}
167− try (grouped parser <* egroup)
168− <|> (mconcat <$> manyTill parser egroup)
169−
170−braced :: PandocMonad m => LP m String
171−braced = grouped chunk
172− where chunk =
173− many1 (satisfy (\c -> c /= '\\' && c /= '}' && c /= '{'))
174− <|> try (string "\\}")
175− <|> try (string "\\{")
176− <|> try (string "\\\\")
177− <|> ((\x -> "{" ++ x ++ "}") <$> braced)
178− <|> count 1 anyChar
528+ try (grouped parser <* egroup) <|> (mconcat <$> manyTill parser egroup)
529+
530+braced :: PandocMonad m => LP m [Tok]
531+braced = bgroup *> braced' 1
532+ where braced' (n :: Int) =
533+ handleEgroup n <|> handleBgroup n <|> handleOther n
534+ handleEgroup n = do
535+ t <- egroup
536+ if n == 1
537+ then return []
538+ else (t:) <$> braced' (n - 1)
539+ handleBgroup n = do
540+ t <- bgroup
541+ (t:) <$> braced' (n + 1)
542+ handleOther n = do
543+ t <- anyTok
544+ (t:) <$> braced' n
179545
180546bracketed :: PandocMonad m => Monoid a => LP m a -> LP m a
181−bracketed parser = try $ char '[' *> (mconcat <$> manyTill parser (char ']'))
182−
183−mathDisplay :: PandocMonad m => LP m String -> LP m Inlines
184−mathDisplay p = displayMath <$> (try p >>= applyMacros' . trim)
185−
186−mathInline :: PandocMonad m => LP m String -> LP m Inlines
187−mathInline p = math <$> (try p >>= applyMacros')
188−
189−mathChars :: PandocMonad m => LP m String
190−mathChars =
191− concat <$> many (escapedChar
192− <|> (snd <$> withRaw braced)
193− <|> many1 (satisfy isOrdChar))
194− where escapedChar = try $ do char '\\'
195− c <- anyChar
196− return ['\\',c]
197− isOrdChar '$' = False
198− isOrdChar '{' = False
199− isOrdChar '}' = False
200− isOrdChar '\\' = False
201− isOrdChar _ = True
202−
203−quoted' :: PandocMonad m => (Inlines -> Inlines) -> LP m String -> LP m () -> LP m Inlines
204−quoted' f starter ender = do
205− startchs <- starter
206− smart <- extensionEnabled Ext_smart <$> getOption readerExtensions
207− if smart
208− then do
209− ils <- many (notFollowedBy ender >> inline)
210− (ender >> return (f (mconcat ils))) <|>
211− (<> mconcat ils) <$>
212− lit (case startchs of
213− "``" -> "“"
214− "`" -> "‘"
215− _ -> startchs)
216− else lit startchs
547+bracketed parser = try $ do
548+ symbol '['
549+ mconcat <$> manyTill parser (symbol ']')
217550
218−doubleQuote :: PandocMonad m => LP m Inlines
219−doubleQuote = do
220− quoted' doubleQuoted (try $ string "``") (void $ try $ string "''")
221− <|> quoted' doubleQuoted (string "“") (void $ char '”')
222− -- the following is used by babel for localized quotes:
223− <|> quoted' doubleQuoted (try $ string "\"`") (void $ try $ string "\"'")
224− <|> quoted' doubleQuoted (string "\"") (void $ char '"')
551+dimenarg :: PandocMonad m => LP m Text
552+dimenarg = try $ do
553+ ch <- option False $ True <$ symbol '='
554+ Tok _ _ s <- satisfyTok isWordTok
555+ guard $ (T.take 2 (T.reverse s)) `elem`
556+ ["pt","pc","in","bp","cm","mm","dd","cc","sp"]
557+ let num = T.take (T.length s - 2) s
558+ guard $ T.length num > 0
559+ guard $ T.all isDigit num
560+ return $ T.pack ['=' | ch] <> s
225561
226−singleQuote :: PandocMonad m => LP m Inlines
227−singleQuote = do
228− smart <- extensionEnabled Ext_smart <$> getOption readerExtensions
229− if smart
230− then quoted' singleQuoted (string "`") (try $ char '\'' >> notFollowedBy letter)
231− <|> quoted' singleQuoted (string "‘") (try $ char '’' >> notFollowedBy letter)
232− else str <$> many1 (oneOf "`\'‘’")
562+-- inline elements:
233563
234−inline :: PandocMonad m => LP m Inlines
235−inline = (mempty <$ comment)
236− <|> (space <$ whitespace)
237− <|> (softbreak <$ endline)
238− <|> inlineText
239− <|> inlineCommand
240− <|> inlineEnvironment
241− <|> inlineGroup
242− <|> (char '-' *> option (str "-")
243− (char '-' *> option (str "–") (str "—" <$ char '-')))
244− <|> doubleQuote
245− <|> singleQuote
246− <|> (str "”" <$ try (string "''"))
247− <|> (str "”" <$ char '”')
248− <|> (str "’" <$ char '\'')
249− <|> (str "’" <$ char '’')
250− <|> (str "\160" <$ char '~')
251− <|> mathDisplay (string "$$" *> mathChars <* string "$$")
252− <|> mathInline (char '$' *> mathChars <* char '$')
253− <|> (guardEnabled Ext_literate_haskell *> char '|' *> doLHSverb)
254− <|> (str . (:[]) <$> tildeEscape)
255− <|> (do res <- oneOf "#&~^'`\"[]"
256− pos <- getPosition
257− report $ ParsingUnescaped [res] pos
258− return $ str [res])
564+word :: PandocMonad m => LP m Inlines
565+word = (str . T.unpack . untoken) <$> satisfyTok isWordTok
259566
260−inlines :: PandocMonad m => LP m Inlines
261−inlines = mconcat <$> many (notFollowedBy (char '}') *> inline)
567+regularSymbol :: PandocMonad m => LP m Inlines
568+regularSymbol = (str . T.unpack . untoken) <$> satisfyTok isRegularSymbol
569+ where isRegularSymbol (Tok _ Symbol t) = not $ T.any isSpecial t
570+ isRegularSymbol _ = False
571+ isSpecial c = c `Set.member` specialChars
572+
573+specialChars :: Set.Set Char
574+specialChars = Set.fromList "#$%&~_^\\{}"
575+
576+isWordTok :: Tok -> Bool
577+isWordTok (Tok _ Word _) = True
578+isWordTok _ = False
262579
263580inlineGroup :: PandocMonad m => LP m Inlines
264581inlineGroup = do
@@ -269,233 +586,564 @@ inlineGroup = do
269586 -- we need the span so we can detitlecase bibtex entries;
270587 -- we need to know when something is {C}apitalized
271588
272−block :: PandocMonad m => LP m Blocks
273−block = (mempty <$ comment)
274− <|> (mempty <$ ((spaceChar <|> newline) *> spaces))
275− <|> environment
276− <|> include
277− <|> macro
278− <|> blockCommand
279− <|> paragraph
280− <|> grouped block
281−
282−blocks :: PandocMonad m => LP m Blocks
283−blocks = mconcat <$> many block
589+doLHSverb :: PandocMonad m => LP m Inlines
590+doLHSverb =
591+ (codeWith ("",["haskell"],[]) . T.unpack . untokenize)
592+ <$> manyTill (satisfyTok (not . isNewlineTok)) (symbol '|')
284593
285−getRawCommand :: PandocMonad m => String -> LP m String
286−getRawCommand name' = do
287− rawargs <- withRaw (many (try (optional sp *> opt)) *>
288− option "" (try (optional sp *> dimenarg)) *>
289− many braced)
290− return $ '\\' : name' ++ snd rawargs
594+mkImage :: PandocMonad m => [(String, String)] -> String -> LP m Inlines
595+mkImage options src = do
596+ let replaceTextwidth (k,v) =
597+ case numUnit v of
598+ Just (num, "\\textwidth") -> (k, showFl (num * 100) ++ "%")
599+ _ -> (k, v)
600+ let kvs = map replaceTextwidth
601+ $ filter (\(k,_) -> k `elem` ["width", "height"]) options
602+ let attr = ("",[], kvs)
603+ let alt = str "image"
604+ case takeExtension src of
605+ "" -> do
606+ defaultExt <- getOption readerDefaultImageExtension
607+ return $ imageWith attr (addExtension src defaultExt) "" alt
608+ _ -> return $ imageWith attr src "" alt
291609
292−lookupListDefault :: (Ord k) => v -> [k] -> M.Map k v -> v
293−lookupListDefault d = (fromMaybe d .) . lookupList
294− where
295− lookupList l m = msum $ map (`M.lookup` m) l
610+-- converts e.g. \SI{1}[\$]{} to "$ 1" or \SI{1}{\euro} to "1 €"
611+dosiunitx :: PandocMonad m => LP m Inlines
612+dosiunitx = do
613+ skipopts
614+ value <- tok
615+ valueprefix <- option "" $ bracketed tok
616+ unit <- tok
617+ let emptyOr160 "" = ""
618+ emptyOr160 _ = "\160"
619+ return . mconcat $ [valueprefix,
620+ emptyOr160 valueprefix,
621+ value,
622+ emptyOr160 unit,
623+ unit]
296624
297−blockCommand :: PandocMonad m => LP m Blocks
298−blockCommand = try $ do
299− name <- anyControlSeq
300− guard $ name /= "begin" && name /= "end"
301− star <- option "" (string "*" <* optional sp)
302− let name' = name ++ star
303− let raw = do
304− rawcommand <- getRawCommand name'
305− transformed <- applyMacros' rawcommand
306− guard $ transformed /= rawcommand
307− notFollowedBy $ parseFromString' inlines transformed
308− parseFromString' blocks transformed
309− lookupListDefault raw [name',name] blockCommands
625+lit :: String -> LP m Inlines
626+lit = pure . str
310627
311−inBrackets :: Inlines -> Inlines
312−inBrackets x = str "[" <> x <> str "]"
628+removeDoubleQuotes :: Text -> Text
629+removeDoubleQuotes t =
630+ maybe t id $ T.stripPrefix "\"" t >>= T.stripSuffix "\""
313631
314−-- eat an optional argument and one or more arguments in braces
315−ignoreInlines :: PandocMonad m => String -> (String, LP m Inlines)
316−ignoreInlines name = (name, p)
317− where
318− p = do oa <- optargs
319− let rawCommand = '\\':name ++ oa
320− let doraw = guardRaw >> return (rawInline "latex" rawCommand)
321− doraw <|> ignore rawCommand
632+doubleQuote :: PandocMonad m => LP m Inlines
633+doubleQuote = do
634+ quoted' doubleQuoted (try $ count 2 $ symbol '`')
635+ (void $ try $ count 2 $ symbol '\'')
636+ <|> quoted' doubleQuoted ((:[]) <$> symbol '“') (void $ symbol '”')
637+ -- the following is used by babel for localized quotes:
638+ <|> quoted' doubleQuoted (try $ sequence [symbol '"', symbol '`'])
639+ (void $ try $ sequence [symbol '"', symbol '\''])
640+ <|> quoted' doubleQuoted ((:[]) <$> symbol '"')
641+ (void $ symbol '"')
322642
323−guardRaw :: PandocMonad m => LP m ()
324−guardRaw = getOption readerExtensions >>= guard . extensionEnabled Ext_raw_tex
643+singleQuote :: PandocMonad m => LP m Inlines
644+singleQuote = do
645+ quoted' singleQuoted ((:[]) <$> symbol '`')
646+ (try $ symbol '\'' >>
647+ notFollowedBy (satisfyTok startsWithLetter))
648+ <|> quoted' singleQuoted ((:[]) <$> symbol '‘')
649+ (try $ symbol '’' >>
650+ notFollowedBy (satisfyTok startsWithLetter))
651+ where startsWithLetter (Tok _ Word t) =
652+ case T.uncons t of
653+ Just (c, _) | isLetter c -> True
654+ _ -> False
655+ startsWithLetter _ = False
656+
657+quoted' :: PandocMonad m
658+ => (Inlines -> Inlines)
659+ -> LP m [Tok]
660+ -> LP m ()
661+ -> LP m Inlines
662+quoted' f starter ender = do
663+ startchs <- (T.unpack . untokenize) <$> starter
664+ smart <- extensionEnabled Ext_smart <$> getOption readerExtensions
665+ if smart
666+ then do
667+ ils <- many (notFollowedBy ender >> inline)
668+ (ender >> return (f (mconcat ils))) <|>
669+ (<> mconcat ils) <$>
670+ lit (case startchs of
671+ "``" -> "“"
672+ "`" -> "‘"
673+ cs -> cs)
674+ else lit startchs
325675
326−optargs :: PandocMonad m => LP m String
327−optargs = snd <$> withRaw (skipopts *> skipMany (try $ optional sp *> braced))
676+enquote :: PandocMonad m => LP m Inlines
677+enquote = do
678+ skipopts
679+ quoteContext <- sQuoteContext <$> getState
680+ if quoteContext == InDoubleQuote
681+ then singleQuoted <$> withQuoteContext InSingleQuote tok
682+ else doubleQuoted <$> withQuoteContext InDoubleQuote tok
328683
329−ignore :: (Monoid a, PandocMonad m) => String -> ParserT s u m a
330−ignore raw = do
331− pos <- getPosition
332− report $ SkippedContent raw pos
333− return mempty
684+doverb :: PandocMonad m => LP m Inlines
685+doverb = do
686+ Tok _ Symbol t <- anySymbol
687+ marker <- case T.uncons t of
688+ Just (c, ts) | T.null ts -> return c
689+ _ -> mzero
690+ withVerbatimMode $
691+ (code . T.unpack . untokenize) <$>
692+ manyTill (verbTok marker) (symbol marker)
693+
694+verbTok :: PandocMonad m => Char -> LP m Tok
695+verbTok stopchar = do
696+ t@(Tok (lin, col) toktype txt) <- satisfyTok (not . isNewlineTok)
697+ case T.findIndex (== stopchar) txt of
698+ Nothing -> return t
699+ Just i -> do
700+ let (t1, t2) = T.splitAt i txt
701+ inp <- getInput
702+ setInput $ Tok (lin, col + i) Symbol (T.singleton stopchar)
703+ : (totoks (lin, col + i + 1) (T.drop 1 t2)) ++ inp
704+ return $ Tok (lin, col) toktype t1
334705
335−ignoreBlocks :: PandocMonad m => String -> (String, LP m Blocks)
336−ignoreBlocks name = (name, p)
337− where
338− p = do oa <- optargs
339− let rawCommand = '\\':name ++ oa
340− let doraw = guardRaw >> return (rawBlock "latex" rawCommand)
341− doraw <|> ignore rawCommand
706+dolstinline :: PandocMonad m => LP m Inlines
707+dolstinline = do
708+ options <- option [] keyvals
709+ let classes = maybeToList $ lookup "language" options >>= fromListingsLanguage
710+ Tok _ Symbol t <- anySymbol
711+ marker <- case T.uncons t of
712+ Just (c, ts) | T.null ts -> return c
713+ _ -> mzero
714+ let stopchar = if marker == '{' then '}' else marker
715+ withVerbatimMode $
716+ (codeWith ("",classes,[]) . T.unpack . untokenize) <$>
717+ manyTill (verbTok stopchar) (symbol stopchar)
342718
343−blockCommands :: PandocMonad m => M.Map String (LP m Blocks)
344−blockCommands = M.fromList $
345− [ ("par", mempty <$ skipopts)
346− , ("parbox", braced >> grouped blocks)
347− , ("title", mempty <$ (skipopts *>
348− (grouped inline >>= addMeta "title")
349− <|> (grouped block >>= addMeta "title")))
350− , ("subtitle", mempty <$ (skipopts *> tok >>= addMeta "subtitle"))
351− , ("author", mempty <$ (skipopts *> authors))
352− -- -- in letter class, temp. store address & sig as title, author
353− , ("address", mempty <$ (skipopts *> tok >>= addMeta "address"))
354− , ("signature", mempty <$ (skipopts *> authors))
355− , ("date", mempty <$ (skipopts *> tok >>= addMeta "date"))
356− -- Koma-script metadata commands
357− , ("dedication", mempty <$ (skipopts *> tok >>= addMeta "dedication"))
358− -- sectioning
359− , ("part", section nullAttr (-1))
360− , ("part*", section nullAttr (-1))
361− , ("chapter", section nullAttr 0)
362− , ("chapter*", section ("",["unnumbered"],[]) 0)
363− , ("section", section nullAttr 1)
364− , ("section*", section ("",["unnumbered"],[]) 1)
365− , ("subsection", section nullAttr 2)
366− , ("subsection*", section ("",["unnumbered"],[]) 2)
367− , ("subsubsection", section nullAttr 3)
368− , ("subsubsection*", section ("",["unnumbered"],[]) 3)
369− , ("paragraph", section nullAttr 4)
370− , ("paragraph*", section ("",["unnumbered"],[]) 4)
371− , ("subparagraph", section nullAttr 5)
372− , ("subparagraph*", section ("",["unnumbered"],[]) 5)
373− -- beamer slides
374− , ("frametitle", section nullAttr 3)
375− , ("framesubtitle", section nullAttr 4)
376− -- letters
377− , ("opening", (para . trimInlines) <$> (skipopts *> tok))
378− , ("closing", skipopts *> closing)
379− --
380− , ("hrule", pure horizontalRule)
381− , ("strut", pure mempty)
382− , ("rule", skipopts *> tok *> tok *> pure horizontalRule)
383− , ("item", skipopts *> looseItem)
384− , ("documentclass", skipopts *> braced *> preamble)
385− , ("centerline", (para . trimInlines) <$> (skipopts *> tok))
386− , ("caption", skipopts *> setCaption)
387− , ("bibliography", mempty <$ (skipopts *> braced >>=
388− addMeta "bibliography" . splitBibs))
389− , ("addbibresource", mempty <$ (skipopts *> braced >>=
390− addMeta "bibliography" . splitBibs))
391− -- includes
392− , ("lstinputlisting", inputListing)
393− , ("graphicspath", graphicsPath)
394− -- hyperlink
395− , ("hypertarget", braced >> grouped block)
396− ] ++ map ignoreBlocks
397− -- these commands will be ignored unless --parse-raw is specified,
398− -- in which case they will appear as raw latex blocks
399− [ "newcommand", "renewcommand", "newenvironment", "renewenvironment"
400− -- newcommand, etc. should be parsed by macro, but we need this
401− -- here so these aren't parsed as inline commands to ignore
402− , "special", "pdfannot", "pdfstringdef"
403− , "bibliographystyle"
404− , "maketitle", "makeindex", "makeglossary"
405− , "addcontentsline", "addtocontents", "addtocounter"
406− -- \ignore{} is used conventionally in literate haskell for definitions
407− -- that are to be processed by the compiler but not printed.
408− , "ignore"
409− , "hyperdef"
410− , "markboth", "markright", "markleft"
411− , "hspace", "vspace"
412− , "newpage"
413− , "clearpage"
414− , "pagebreak"
415− ]
719+keyval :: PandocMonad m => LP m (String, String)
720+keyval = try $ do
721+ Tok _ Word key <- satisfyTok isWordTok
722+ let isSpecSym (Tok _ Symbol t) = t `elem` [".",":","-","|","\\"]
723+ isSpecSym _ = False
724+ val <- option [] $ do
725+ symbol '='
726+ braced <|> (many1 (satisfyTok isWordTok <|> satisfyTok isSpecSym
727+ <|> anyControlSeq))
728+ optional sp
729+ optional (symbol ',')
730+ optional sp
731+ return (T.unpack key, T.unpack . untokenize $ val)
416732
417−graphicsPath :: PandocMonad m => LP m Blocks
418−graphicsPath = do
419− ps <- bgroup *> (manyTill braced egroup)
420− getResourcePath >>= setResourcePath . (++ ps)
421− return mempty
733+keyvals :: PandocMonad m => LP m [(String, String)]
734+keyvals = try $ symbol '[' >> manyTill keyval (symbol ']')
422735
423−addMeta :: PandocMonad m => ToMetaValue a => String -> a -> LP m ()
424−addMeta field val = updateState $ \st ->
425− st{ stateMeta = addMetaField field val $ stateMeta st }
736+accent :: (Char -> String) -> Inlines -> LP m Inlines
737+accent f ils =
738+ case toList ils of
739+ (Str (x:xs) : ys) -> return $ fromList (Str (f x ++ xs) : ys)
740+ [] -> mzero
741+ _ -> return ils
426742
427−splitBibs :: String -> [Inlines]
428−splitBibs = map (str . flip replaceExtension "bib" . trim) . splitBy (==',')
743+grave :: Char -> String
744+grave 'A' = "À"
745+grave 'E' = "È"
746+grave 'I' = "Ì"
747+grave 'O' = "Ò"
748+grave 'U' = "Ù"
749+grave 'a' = "à"
750+grave 'e' = "è"
751+grave 'i' = "ì"
752+grave 'o' = "ò"
753+grave 'u' = "ù"
754+grave c = [c]
429755
430−setCaption :: PandocMonad m => LP m Blocks
431−setCaption = do
432− ils <- tok
433− mblabel <- option Nothing $
434− try $ spaces' >> controlSeq "label" >> (Just <$> tok)
435− let ils' = case mblabel of
436− Just lab -> ils <> spanWith
437− ("",[],[("data-label", stringify lab)]) mempty
438− Nothing -> ils
439− updateState $ \st -> st{ stateCaption = Just ils' }
440− return mempty
756+acute :: Char -> String
757+acute 'A' = "Á"
758+acute 'E' = "É"
759+acute 'I' = "Í"
760+acute 'O' = "Ó"
761+acute 'U' = "Ú"
762+acute 'Y' = "Ý"
763+acute 'a' = "á"
764+acute 'e' = "é"
765+acute 'i' = "í"
766+acute 'o' = "ó"
767+acute 'u' = "ú"
768+acute 'y' = "ý"
769+acute 'C' = "Ć"
770+acute 'c' = "ć"
771+acute 'L' = "Ĺ"
772+acute 'l' = "ĺ"
773+acute 'N' = "Ń"
774+acute 'n' = "ń"
775+acute 'R' = "Ŕ"
776+acute 'r' = "ŕ"
777+acute 'S' = "Ś"
778+acute 's' = "ś"
779+acute 'Z' = "Ź"
780+acute 'z' = "ź"
781+acute c = [c]
441782
442−resetCaption :: PandocMonad m => LP m ()
443−resetCaption = updateState $ \st -> st{ stateCaption = Nothing }
783+circ :: Char -> String
784+circ 'A' = "Â"
785+circ 'E' = "Ê"
786+circ 'I' = "Î"
787+circ 'O' = "Ô"
788+circ 'U' = "Û"
789+circ 'a' = "â"
790+circ 'e' = "ê"
791+circ 'i' = "î"
792+circ 'o' = "ô"
793+circ 'u' = "û"
794+circ 'C' = "Ĉ"
795+circ 'c' = "ĉ"
796+circ 'G' = "Ĝ"
797+circ 'g' = "ĝ"
798+circ 'H' = "Ĥ"
799+circ 'h' = "ĥ"
800+circ 'J' = "Ĵ"
801+circ 'j' = "ĵ"
802+circ 'S' = "Ŝ"
803+circ 's' = "ŝ"
804+circ 'W' = "Ŵ"
805+circ 'w' = "ŵ"
806+circ 'Y' = "Ŷ"
807+circ 'y' = "ŷ"
808+circ c = [c]
444809
445−authors :: PandocMonad m => LP m ()
446−authors = try $ do
447− bgroup
448− let oneAuthor = mconcat <$>
449− many1 (notFollowedBy' (controlSeq "and") >>
450− (inline <|> mempty <$ blockCommand))
451− -- skip e.g. \vspace{10pt}
452− auths <- sepBy oneAuthor (controlSeq "and")
453− egroup
454− addMeta "author" (map trimInlines auths)
810+tilde :: Char -> String
811+tilde 'A' = "Ã"
812+tilde 'a' = "ã"
813+tilde 'O' = "Õ"
814+tilde 'o' = "õ"
815+tilde 'I' = "Ĩ"
816+tilde 'i' = "ĩ"
817+tilde 'U' = "Ũ"
818+tilde 'u' = "ũ"
819+tilde 'N' = "Ñ"
820+tilde 'n' = "ñ"
821+tilde c = [c]
455822
456−section :: PandocMonad m => Attr -> Int -> LP m Blocks
457−section (ident, classes, kvs) lvl = do
458− skipopts
459− contents <- grouped inline
460− lab <- option ident $ try (spaces' >> controlSeq "label" >> spaces' >> braced)
461− attr' <- registerHeader (lab, classes, kvs) contents
462− return $ headerWith attr' lvl contents
823+umlaut :: Char -> String
824+umlaut 'A' = "Ä"
825+umlaut 'E' = "Ë"
826+umlaut 'I' = "Ï"
827+umlaut 'O' = "Ö"
828+umlaut 'U' = "Ü"
829+umlaut 'a' = "ä"
830+umlaut 'e' = "ë"
831+umlaut 'i' = "ï"
832+umlaut 'o' = "ö"
833+umlaut 'u' = "ü"
834+umlaut c = [c]
463835
464−inlineCommand :: PandocMonad m => LP m Inlines
465−inlineCommand = try $ do
466− (name, raw') <- withRaw anyControlSeq
467− guard $ name /= "begin" && name /= "end"
468− star <- option "" (string "*")
469− let name' = name ++ star
836+hungarumlaut :: Char -> String
837+hungarumlaut 'A' = "A̋"
838+hungarumlaut 'E' = "E̋"
839+hungarumlaut 'I' = "I̋"
840+hungarumlaut 'O' = "Ő"
841+hungarumlaut 'U' = "Ű"
842+hungarumlaut 'Y' = "ӳ"
843+hungarumlaut 'a' = "a̋"
844+hungarumlaut 'e' = "e̋"
845+hungarumlaut 'i' = "i̋"
846+hungarumlaut 'o' = "ő"
847+hungarumlaut 'u' = "ű"
848+hungarumlaut 'y' = "ӳ"
849+hungarumlaut c = [c]
850+
851+dot :: Char -> String
852+dot 'C' = "Ċ"
853+dot 'c' = "ċ"
854+dot 'E' = "Ė"
855+dot 'e' = "ė"
856+dot 'G' = "Ġ"
857+dot 'g' = "ġ"
858+dot 'I' = "İ"
859+dot 'Z' = "Ż"
860+dot 'z' = "ż"
861+dot c = [c]
862+
863+macron :: Char -> String
864+macron 'A' = "Ā"
865+macron 'E' = "Ē"
866+macron 'I' = "Ī"
867+macron 'O' = "Ō"
868+macron 'U' = "Ū"
869+macron 'a' = "ā"
870+macron 'e' = "ē"
871+macron 'i' = "ī"
872+macron 'o' = "ō"
873+macron 'u' = "ū"
874+macron c = [c]
875+
876+cedilla :: Char -> String
877+cedilla 'c' = "ç"
878+cedilla 'C' = "Ç"
879+cedilla 's' = "ş"
880+cedilla 'S' = "Ş"
881+cedilla 't' = "ţ"
882+cedilla 'T' = "Ţ"
883+cedilla 'e' = "ȩ"
884+cedilla 'E' = "Ȩ"
885+cedilla 'h' = "ḩ"
886+cedilla 'H' = "Ḩ"
887+cedilla 'o' = "o̧"
888+cedilla 'O' = "O̧"
889+cedilla c = [c]
890+
891+hacek :: Char -> String
892+hacek 'A' = "Ǎ"
893+hacek 'a' = "ǎ"
894+hacek 'C' = "Č"
895+hacek 'c' = "č"
896+hacek 'D' = "Ď"
897+hacek 'd' = "ď"
898+hacek 'E' = "Ě"
899+hacek 'e' = "ě"
900+hacek 'G' = "Ǧ"
901+hacek 'g' = "ǧ"
902+hacek 'H' = "Ȟ"
903+hacek 'h' = "ȟ"
904+hacek 'I' = "Ǐ"
905+hacek 'i' = "ǐ"
906+hacek 'j' = "ǰ"
907+hacek 'K' = "Ǩ"
908+hacek 'k' = "ǩ"
909+hacek 'L' = "Ľ"
910+hacek 'l' = "ľ"
911+hacek 'N' = "Ň"
912+hacek 'n' = "ň"
913+hacek 'O' = "Ǒ"
914+hacek 'o' = "ǒ"
915+hacek 'R' = "Ř"
916+hacek 'r' = "ř"
917+hacek 'S' = "Š"
918+hacek 's' = "š"
919+hacek 'T' = "Ť"
920+hacek 't' = "ť"
921+hacek 'U' = "Ǔ"
922+hacek 'u' = "ǔ"
923+hacek 'Z' = "Ž"
924+hacek 'z' = "ž"
925+hacek c = [c]
926+
927+breve :: Char -> String
928+breve 'A' = "Ă"
929+breve 'a' = "ă"
930+breve 'E' = "Ĕ"
931+breve 'e' = "ĕ"
932+breve 'G' = "Ğ"
933+breve 'g' = "ğ"
934+breve 'I' = "Ĭ"
935+breve 'i' = "ĭ"
936+breve 'O' = "Ŏ"
937+breve 'o' = "ŏ"
938+breve 'U' = "Ŭ"
939+breve 'u' = "ŭ"
940+breve c = [c]
941+
942+toksToString :: [Tok] -> String
943+toksToString = T.unpack . untokenize
944+
945+mathDisplay :: String -> Inlines
946+mathDisplay = displayMath . trim
947+
948+mathInline :: String -> Inlines
949+mathInline = math . trim
950+
951+dollarsMath :: PandocMonad m => LP m Inlines
952+dollarsMath = do
953+ symbol '$'
954+ display <- option False (True <$ symbol '$')
955+ contents <- trim . toksToString <$>
956+ many (notFollowedBy (symbol '$') >> anyTok)
957+ if display
958+ then do
959+ mathDisplay contents <$ try (symbol '$' >> symbol '$')
960+ <|> (guard (null contents) >> return (mathInline ""))
961+ else mathInline contents <$ (symbol '$')
962+
963+-- citations
964+
965+addPrefix :: [Inline] -> [Citation] -> [Citation]
966+addPrefix p (k:ks) = k {citationPrefix = p ++ citationPrefix k} : ks
967+addPrefix _ _ = []
968+
969+addSuffix :: [Inline] -> [Citation] -> [Citation]
970+addSuffix s ks@(_:_) =
971+ let k = last ks
972+ in init ks ++ [k {citationSuffix = citationSuffix k ++ s}]
973+addSuffix _ _ = []
974+
975+simpleCiteArgs :: PandocMonad m => LP m [Citation]
976+simpleCiteArgs = try $ do
977+ first <- optionMaybe $ toList <$> opt
978+ second <- optionMaybe $ toList <$> opt
979+ keys <- try $ bgroup *> (manyTill citationLabel egroup)
980+ let (pre, suf) = case (first , second ) of
981+ (Just s , Nothing) -> (mempty, s )
982+ (Just s , Just t ) -> (s , t )
983+ _ -> (mempty, mempty)
984+ conv k = Citation { citationId = k
985+ , citationPrefix = []
986+ , citationSuffix = []
987+ , citationMode = NormalCitation
988+ , citationHash = 0
989+ , citationNoteNum = 0
990+ }
991+ return $ addPrefix pre $ addSuffix suf $ map conv keys
992+
993+citationLabel :: PandocMonad m => LP m String
994+citationLabel = do
995+ optional sp
996+ toksToString <$>
997+ (many1 (satisfyTok isWordTok <|> symbolIn bibtexKeyChar)
998+ <* optional sp
999+ <* optional (symbol ',')
1000+ <* optional sp)
1001+ where bibtexKeyChar = ".:;?!`'()/*@_+=-[]" :: [Char]
1002+
1003+cites :: PandocMonad m => CitationMode -> Bool -> LP m [Citation]
1004+cites mode multi = try $ do
1005+ cits <- if multi
1006+ then many1 simpleCiteArgs
1007+ else count 1 simpleCiteArgs
1008+ let cs = concat cits
1009+ return $ case mode of
1010+ AuthorInText -> case cs of
1011+ (c:rest) -> c {citationMode = mode} : rest
1012+ [] -> []
1013+ _ -> map (\a -> a {citationMode = mode}) cs
1014+
1015+citation :: PandocMonad m => String -> CitationMode -> Bool -> LP m Inlines
1016+citation name mode multi = do
1017+ (c,raw) <- withRaw $ cites mode multi
1018+ return $ cite c (rawInline "latex" $ "\\" ++ name ++ (toksToString raw))
1019+
1020+handleCitationPart :: Inlines -> [Citation]
1021+handleCitationPart ils =
1022+ let isCite Cite{} = True
1023+ isCite _ = False
1024+ (pref, rest) = break isCite (toList ils)
1025+ in case rest of
1026+ (Cite cs _:suff) -> addPrefix pref $ addSuffix suff cs
1027+ _ -> []
1028+
1029+complexNatbibCitation :: PandocMonad m => CitationMode -> LP m Inlines
1030+complexNatbibCitation mode = try $ do
1031+ (cs, raw) <-
1032+ withRaw $ concat <$> do
1033+ bgroup
1034+ items <- mconcat <$>
1035+ many1 (notFollowedBy (symbol ';') >> inline)
1036+ `sepBy1` (symbol ';')
1037+ egroup
1038+ return $ map handleCitationPart items
1039+ case cs of
1040+ [] -> mzero
1041+ (c:cits) -> return $ cite (c{ citationMode = mode }:cits)
1042+ (rawInline "latex" $ "\\citetext" ++ toksToString raw)
1043+
1044+inNote :: Inlines -> Inlines
1045+inNote ils =
1046+ note $ para $ ils <> str "."
1047+
1048+inlineCommand' :: PandocMonad m => LP m Inlines
1049+inlineCommand' = try $ do
1050+ Tok _ (CtrlSeq name) cmd <- anyControlSeq
1051+ guard $ name /= "begin" && name /= "end"
1052+ (star, rawstar) <- withRaw $ option "" ("*" <$ symbol '*' <* optional sp)
1053+ let name' = name <> star
1054+ let names = ordNub [name', name] -- check non-starred as fallback
4701055 let raw = do
471− guard $ not (isBlockCommand name)
472− rawargs <- withRaw
473− (skipangles *> skipopts *> option "" dimenarg *> many braced)
474− let rawcommand = raw' ++ star ++ snd rawargs
475− transformed <- applyMacros' rawcommand
476− exts <- getOption readerExtensions
477− if transformed /= rawcommand
478− then parseFromString' inlines transformed
479− else if extensionEnabled Ext_raw_tex exts
480− then return $ rawInline "latex" rawcommand
481− else ignore rawcommand
482− (lookupListDefault raw [name',name] inlineCommands <*
483− optional (try (string "{}")))
484−
485−rawInlineOr :: PandocMonad m => String -> LP m Inlines -> LP m Inlines
486−rawInlineOr name' fallback = do
487− parseRaw <- extensionEnabled Ext_raw_tex <$> getOption readerExtensions
488− if parseRaw
489− then rawInline "latex" <$> getRawCommand name'
490− else fallback
1056+ guard $ isInlineCommand name || not (isBlockCommand name)
1057+ (_, rawargs) <- withRaw
1058+ (skipangles *> skipopts *> option "" dimenarg *> many braced)
1059+ let rawcommand = T.unpack $ cmd <> untokenize (rawstar ++ rawargs)
1060+ (guardEnabled Ext_raw_tex >> return (rawInline "latex" rawcommand))
1061+ <|> ignore rawcommand
1062+ lookupListDefault raw names inlineCommands
1063+
1064+tok :: PandocMonad m => LP m Inlines
1065+tok = grouped inline <|> inlineCommand' <|> singleChar
1066+ where singleChar = try $ do
1067+ Tok (lin,col) toktype t <- satisfyTok (tokTypeIn [Word, Symbol])
1068+ guard $ not $ toktype == Symbol &&
1069+ T.any (`Set.member` specialChars) t
1070+ if T.length t > 1
1071+ then do
1072+ let (t1, t2) = (T.take 1 t, T.drop 1 t)
1073+ inp <- getInput
1074+ setInput $ (Tok (lin, col + 1) toktype t2) : inp
1075+ return $ str (T.unpack t1)
1076+ else return $ str (T.unpack t)
1077+
1078+opt :: PandocMonad m => LP m Inlines
1079+opt = bracketed inline
1080+
1081+rawopt :: PandocMonad m => LP m Text
1082+rawopt = do
1083+ symbol '['
1084+ inner <- untokenize <$> manyTill anyTok (symbol ']')
1085+ optional sp
1086+ return $ "[" <> inner <> "]"
1087+
1088+skipopts :: PandocMonad m => LP m ()
1089+skipopts = skipMany rawopt
1090+
1091+-- opts in angle brackets are used in beamer
1092+rawangle :: PandocMonad m => LP m ()
1093+rawangle = try $ do
1094+ symbol '<'
1095+ () <$ manyTill anyTok (symbol '>')
1096+
1097+skipangles :: PandocMonad m => LP m ()
1098+skipangles = skipMany rawangle
1099+
1100+ignore :: (Monoid a, PandocMonad m) => String -> ParserT s u m a
1101+ignore raw = do
1102+ pos <- getPosition
1103+ report $ SkippedContent raw pos
1104+ return mempty
1105+
1106+withRaw :: PandocMonad m => LP m a -> LP m (a, [Tok])
1107+withRaw parser = do
1108+ inp <- getInput
1109+ result <- parser
1110+ nxt <- option (Tok (0,0) Word "") (lookAhead anyTok)
1111+ let raw = takeWhile (/= nxt) inp
1112+ return (result, raw)
1113+
1114+inBrackets :: Inlines -> Inlines
1115+inBrackets x = str "[" <> x <> str "]"
1116+
1117+unescapeURL :: String -> String
1118+unescapeURL ('\\':x:xs) | isEscapable x = x:unescapeURL xs
1119+ where isEscapable c = c `elem` ("#$%&~_^\\{}" :: String)
1120+unescapeURL (x:xs) = x:unescapeURL xs
1121+unescapeURL [] = ""
1122+
1123+mathEnvWith :: PandocMonad m
1124+ => (Inlines -> a) -> Maybe Text -> Text -> LP m a
1125+mathEnvWith f innerEnv name = f . mathDisplay . inner <$> mathEnv name
1126+ where inner x = case innerEnv of
1127+ Nothing -> x
1128+ Just y -> "\\begin{" ++ T.unpack y ++ "}\n" ++ x ++
1129+ "\\end{" ++ T.unpack y ++ "}"
4911130
492−isBlockCommand :: String -> Bool
493−isBlockCommand s = s `M.member` (blockCommands :: M.Map String (LP PandocPure Blocks))
1131+mathEnv :: PandocMonad m => Text -> LP m String
1132+mathEnv name = do
1133+ skipopts
1134+ optional blankline
1135+ res <- manyTill anyTok (end_ name)
1136+ return $ stripTrailingNewlines $ T.unpack $ untokenize res
4941137
1138+inlineEnvironment :: PandocMonad m => LP m Inlines
1139+inlineEnvironment = try $ do
1140+ controlSeq "begin"
1141+ name <- untokenize <$> braced
1142+ M.findWithDefault mzero name inlineEnvironments
4951143
496−inlineEnvironments :: PandocMonad m => M.Map String (LP m Inlines)
497−inlineEnvironments = M.fromList
498− [ ("displaymath", mathEnvWith id Nothing "displaymath")
1144+inlineEnvironments :: PandocMonad m => M.Map Text (LP m Inlines)
1145+inlineEnvironments = M.fromList [
1146+ ("displaymath", mathEnvWith id Nothing "displaymath")
4991147 , ("math", math <$> mathEnv "math")
5001148 , ("equation", mathEnvWith id Nothing "equation")
5011149 , ("equation*", mathEnvWith id Nothing "equation*")
@@ -511,7 +1159,7 @@ inlineEnvironments = M.fromList
5111159 , ("alignat*", mathEnvWith id (Just "aligned") "alignat*")
5121160 ]
5131161
514−inlineCommands :: PandocMonad m => M.Map String (LP m Inlines)
1162+inlineCommands :: PandocMonad m => M.Map Text (LP m Inlines)
5151163inlineCommands = M.fromList $
5161164 [ ("emph", extractSpaces emph <$> tok)
5171165 , ("textit", extractSpaces emph <$> tok)
@@ -540,9 +1188,9 @@ inlineCommands = M.fromList $
5401188 , ("textgreek", tok)
5411189 , ("sep", lit ",")
5421190 , ("cref", rawInlineOr "cref" (inBrackets <$> tok)) -- from cleveref.sty
543− , ("(", mathInline $ manyTill anyChar (try $ string "\\)"))
544− , ("[", mathDisplay $ manyTill anyChar (try $ string "\\]"))
545− , ("ensuremath", mathInline braced)
1191+ , ("(", mathInline . toksToString <$> manyTill anyTok (controlSeq ")"))
1192+ , ("[", mathDisplay . toksToString <$> manyTill anyTok (controlSeq "]"))
1193+ , ("ensuremath", mathInline . toksToString <$> braced)
5461194 , ("texorpdfstring", (\_ x -> x) <$> tok <*> tok)
5471195 , ("P", lit "¶")
5481196 , ("S", lit "§")
@@ -592,7 +1240,10 @@ inlineCommands = M.fromList $
5921240 , ("v", option (str "v") $ try $ tok >>= accent hacek)
5931241 , ("u", option (str "u") $ try $ tok >>= accent breve)
5941242 , ("i", lit "i")
595− , ("\\", linebreak <$ (optional (bracketed inline) *> spaces'))
1243+ , ("\\", linebreak <$ (do inTableCell <- sInTableCell <$> getState
1244+ guard $ not inTableCell
1245+ optional (bracketed inline)
1246+ spaces))
5961247 , (",", lit "\8198")
5971248 , ("@", pure mempty)
5981249 , (" ", lit "\160")
@@ -607,13 +1258,14 @@ inlineCommands = M.fromList $
6071258 , ("verb", doverb)
6081259 , ("lstinline", dolstinline)
6091260 , ("Verb", doverb)
610− , ("url", (unescapeURL <$> braced) >>= \url ->
611− pure (link url "" (str url)))
612− , ("href", (unescapeURL <$> braced <* optional sp) >>= \url ->
613− tok >>= \lab ->
614− pure (link url "" lab))
1261+ , ("url", ((unescapeURL . T.unpack . untokenize) <$> braced) >>= \url ->
1262+ pure (link url "" (str url)))
1263+ , ("href", (unescapeURL . toksToString <$>
1264+ braced <* optional sp) >>= \url ->
1265+ tok >>= \lab -> pure (link url "" lab))
6151266 , ("includegraphics", do options <- option [] keyvals
616− src <- unescapeURL . removeDoubleQuotes <$> braced
1267+ src <- unescapeURL . T.unpack .
1268+ removeDoubleQuotes . untokenize <$> braced
6171269 mkImage options src)
6181270 , ("enquote", enquote)
6191271 , ("cite", citation "cite" NormalCitation False)
@@ -686,362 +1338,456 @@ inlineCommands = M.fromList $
6861338 -- fontawesome
6871339 , ("faCheck", lit "\10003")
6881340 , ("faClose", lit "\10007")
689− ] ++ map ignoreInlines
690− -- these commands will be ignored unless --parse-raw is specified,
691− -- in which case they will appear as raw latex blocks:
1341+ ]
1342+
1343+ttfamily :: PandocMonad m => LP m Inlines
1344+ttfamily = (code . stringify . toList) <$> tok
1345+
1346+rawInlineOr :: PandocMonad m => Text -> LP m Inlines -> LP m Inlines
1347+rawInlineOr name' fallback = do
1348+ parseRaw <- extensionEnabled Ext_raw_tex <$> getOption readerExtensions
1349+ if parseRaw
1350+ then rawInline "latex" <$> getRawCommand name'
1351+ else fallback
1352+
1353+getRawCommand :: PandocMonad m => Text -> LP m String
1354+getRawCommand txt = do
1355+ (_, rawargs) <- withRaw
1356+ (many (try (optional sp *> opt)) *>
1357+ option "" (try (optional sp *> dimenarg)) *>
1358+ many braced)
1359+ return $ T.unpack (txt <> untokenize rawargs)
1360+
1361+isBlockCommand :: Text -> Bool
1362+isBlockCommand s =
1363+ s `M.member` (blockCommands :: M.Map Text (LP PandocPure Blocks))
1364+ || s `Set.member` treatAsBlock
1365+
1366+treatAsBlock :: Set.Set Text
1367+treatAsBlock = Set.fromList
1368+ [ "newcommand", "renewcommand"
1369+ , "newenvironment", "renewenvironment"
1370+ , "providecommand", "provideenvironment"
1371+ -- newcommand, etc. should be parsed by macroDef, but we need this
1372+ -- here so these aren't parsed as inline commands to ignore
1373+ , "special", "pdfannot", "pdfstringdef"
1374+ , "bibliographystyle"
1375+ , "maketitle", "makeindex", "makeglossary"
1376+ , "addcontentsline", "addtocontents", "addtocounter"
1377+ -- \ignore{} is used conventionally in literate haskell for definitions
1378+ -- that are to be processed by the compiler but not printed.
1379+ , "ignore"
1380+ , "hyperdef"
1381+ , "markboth", "markright", "markleft"
1382+ , "hspace", "vspace"
1383+ , "newpage"
1384+ , "clearpage"
1385+ , "pagebreak"
1386+ ]
1387+
1388+isInlineCommand :: Text -> Bool
1389+isInlineCommand s =
1390+ s `M.member` (inlineCommands :: M.Map Text (LP PandocPure Inlines))
1391+ || s `Set.member` treatAsInline
1392+
1393+treatAsInline :: Set.Set Text
1394+treatAsInline = Set.fromList
6921395 [ "index"
6931396 , "hspace"
6941397 , "vspace"
1398+ , "noindent"
6951399 , "newpage"
6961400 , "clearpage"
6971401 , "pagebreak"
6981402 ]
6991403
700−ttfamily :: PandocMonad m => LP m Inlines
701−ttfamily = (code . stringify . toList) <$> tok
1404+lookupListDefault :: (Show k, Ord k) => v -> [k] -> M.Map k v -> v
1405+lookupListDefault d = (fromMaybe d .) . lookupList
1406+ where lookupList l m = msum $ map (`M.lookup` m) l
7021407
703−mkImage :: PandocMonad m => [(String, String)] -> String -> LP m Inlines
704−mkImage options src = do
705− let replaceTextwidth (k,v) = case numUnit v of
706− Just (num, "\\textwidth") -> (k, showFl (num * 100) ++ "%")
707− _ -> (k, v)
708− let kvs = map replaceTextwidth $ filter (\(k,_) -> k `elem` ["width", "height"]) options
709− let attr = ("",[], kvs)
710− let alt = str "image"
711− case takeExtension src of
712− "" -> do
713− defaultExt <- getOption readerDefaultImageExtension
714− return $ imageWith attr (addExtension src defaultExt) "" alt
715− _ -> return $ imageWith attr src "" alt
1408+inline :: PandocMonad m => LP m Inlines
1409+inline = (mempty <$ comment)
1410+ <|> (space <$ whitespace)
1411+ <|> (softbreak <$ endline)
1412+ <|> word
1413+ <|> inlineCommand'
1414+ <|> inlineEnvironment
1415+ <|> inlineGroup
1416+ <|> (symbol '-' *>
1417+ option (str "-") (symbol '-' *>
1418+ option (str "–") (str "—" <$ symbol '-')))
1419+ <|> doubleQuote
1420+ <|> singleQuote
1421+ <|> (str "”" <$ try (symbol '\'' >> symbol '\''))
1422+ <|> (str "”" <$ symbol '”')
1423+ <|> (str "’" <$ symbol '\'')
1424+ <|> (str "’" <$ symbol '’')
1425+ <|> (str "\160" <$ symbol '~')
1426+ <|> dollarsMath
1427+ <|> (guardEnabled Ext_literate_haskell *> symbol '|' *> doLHSverb)
1428+ <|> (str . (:[]) <$> primEscape)
1429+ <|> regularSymbol
1430+ <|> (do res <- symbolIn "#^'`\"[]"
1431+ pos <- getPosition
1432+ let s = T.unpack (untoken res)
1433+ report $ ParsingUnescaped s pos
1434+ return $ str s)
7161435
717−inNote :: Inlines -> Inlines
718−inNote ils =
719− note $ para $ ils <> str "."
1436+inlines :: PandocMonad m => LP m Inlines
1437+inlines = mconcat <$> many inline
7201438
721−unescapeURL :: String -> String
722−unescapeURL ('\\':x:xs) | isEscapable x = x:unescapeURL xs
723− where isEscapable c = c `elem` ("#$%&~_^\\{}" :: String)
724−unescapeURL (x:xs) = x:unescapeURL xs
725−unescapeURL [] = ""
726−
727−enquote :: PandocMonad m => LP m Inlines
728−enquote = do
729− skipopts
730− context <- stateQuoteContext <$> getState
731− if context == InDoubleQuote
732− then singleQuoted <$> withQuoteContext InSingleQuote tok
733− else doubleQuoted <$> withQuoteContext InDoubleQuote tok
734−
735−doverb :: PandocMonad m => LP m Inlines
736−doverb = do
737− marker <- anyChar
738− code <$> manyTill (satisfy (/='\n')) (char marker)
739−
740−dolstinline :: PandocMonad m => LP m Inlines
741−dolstinline = do
742− options <- option [] keyvals
743− let classes = maybeToList $ lookup "language" options >>= fromListingsLanguage
744− marker <- char '{' <|> anyChar
745− codeWith ("",classes,[]) <$> manyTill (satisfy (/='\n')) (char '}' <|> char marker)
746−
747−doLHSverb :: PandocMonad m => LP m Inlines
748−doLHSverb = codeWith ("",["haskell"],[]) <$> manyTill (satisfy (/='\n')) (char '|')
749−
750−-- converts e.g. \SI{1}[\$]{} to "$ 1" or \SI{1}{\euro} to "1 €"
751−dosiunitx :: PandocMonad m => LP m Inlines
752−dosiunitx = do
753− skipopts
754− value <- tok
755− valueprefix <- option "" $ char '[' >> (mconcat <$> manyTill tok (char ']'))
756− unit <- tok
757− let emptyOr160 "" = ""
758− emptyOr160 _ = "\160"
759− return . mconcat $ [valueprefix,
760− emptyOr160 valueprefix,
761− value,
762− emptyOr160 unit,
763− unit]
764−
765−lit :: String -> LP m Inlines
766−lit = pure . str
767−
768−accent :: (Char -> String) -> Inlines -> LP m Inlines
769−accent f ils =
770− case toList ils of
771− (Str (x:xs) : ys) -> return $ fromList (Str (f x ++ xs) : ys)
772− [] -> mzero
773− _ -> return ils
774−
775−grave :: Char -> String
776−grave 'A' = "À"
777−grave 'E' = "È"
778−grave 'I' = "Ì"
779−grave 'O' = "Ò"
780−grave 'U' = "Ù"
781−grave 'a' = "à"
782−grave 'e' = "è"
783−grave 'i' = "ì"
784−grave 'o' = "ò"
785−grave 'u' = "ù"
786−grave c = [c]
787−
788−acute :: Char -> String
789−acute 'A' = "Á"
790−acute 'E' = "É"
791−acute 'I' = "Í"
792−acute 'O' = "Ó"
793−acute 'U' = "Ú"
794−acute 'Y' = "Ý"
795−acute 'a' = "á"
796−acute 'e' = "é"
797−acute 'i' = "í"
798−acute 'o' = "ó"
799−acute 'u' = "ú"
800−acute 'y' = "ý"
801−acute 'C' = "Ć"
802−acute 'c' = "ć"
803−acute 'L' = "Ĺ"
804−acute 'l' = "ĺ"
805−acute 'N' = "Ń"
806−acute 'n' = "ń"
807−acute 'R' = "Ŕ"
808−acute 'r' = "ŕ"
809−acute 'S' = "Ś"
810−acute 's' = "ś"
811−acute 'Z' = "Ź"
812−acute 'z' = "ź"
813−acute c = [c]
814−
815−circ :: Char -> String
816−circ 'A' = "Â"
817−circ 'E' = "Ê"
818−circ 'I' = "Î"
819−circ 'O' = "Ô"
820−circ 'U' = "Û"
821−circ 'a' = "â"
822−circ 'e' = "ê"
823−circ 'i' = "î"
824−circ 'o' = "ô"
825−circ 'u' = "û"
826−circ 'C' = "Ĉ"
827−circ 'c' = "ĉ"
828−circ 'G' = "Ĝ"
829−circ 'g' = "ĝ"
830−circ 'H' = "Ĥ"
831−circ 'h' = "ĥ"
832−circ 'J' = "Ĵ"
833−circ 'j' = "ĵ"
834−circ 'S' = "Ŝ"
835−circ 's' = "ŝ"
836−circ 'W' = "Ŵ"
837−circ 'w' = "ŵ"
838−circ 'Y' = "Ŷ"
839−circ 'y' = "ŷ"
840−circ c = [c]
1439+-- block elements:
8411440
842−tilde :: Char -> String
843−tilde 'A' = "Ã"
844−tilde 'a' = "ã"
845−tilde 'O' = "Õ"
846−tilde 'o' = "õ"
847−tilde 'I' = "Ĩ"
848−tilde 'i' = "ĩ"
849−tilde 'U' = "Ũ"
850−tilde 'u' = "ũ"
851−tilde 'N' = "Ñ"
852−tilde 'n' = "ñ"
853−tilde c = [c]
1441+begin_ :: PandocMonad m => Text -> LP m ()
1442+begin_ t = (try $ do
1443+ controlSeq "begin"
1444+ spaces
1445+ symbol '{'
1446+ spaces
1447+ Tok _ Word txt <- satisfyTok isWordTok
1448+ spaces
1449+ symbol '}'
1450+ guard (t == txt)) <?> ("\\begin{" ++ T.unpack t ++ "}")
8541451
855−umlaut :: Char -> String
856−umlaut 'A' = "Ä"
857−umlaut 'E' = "Ë"
858−umlaut 'I' = "Ï"
859−umlaut 'O' = "Ö"
860−umlaut 'U' = "Ü"
861−umlaut 'a' = "ä"
862−umlaut 'e' = "ë"
863−umlaut 'i' = "ï"
864−umlaut 'o' = "ö"
865−umlaut 'u' = "ü"
866−umlaut c = [c]
1452+end_ :: PandocMonad m => Text -> LP m ()
1453+end_ t = (try $ do
1454+ controlSeq "end"
1455+ spaces
1456+ symbol '{'
1457+ spaces
1458+ Tok _ Word txt <- satisfyTok isWordTok
1459+ spaces
1460+ symbol '}'
1461+ guard $ t == txt) <?> ("\\end{" ++ T.unpack t ++ "}")
8671462
868−hungarumlaut :: Char -> String
869−hungarumlaut 'A' = "A̋"
870−hungarumlaut 'E' = "E̋"
871−hungarumlaut 'I' = "I̋"
872−hungarumlaut 'O' = "Ő"
873−hungarumlaut 'U' = "Ű"
874−hungarumlaut 'Y' = "ӳ"
875−hungarumlaut 'a' = "a̋"
876−hungarumlaut 'e' = "e̋"
877−hungarumlaut 'i' = "i̋"
878−hungarumlaut 'o' = "ő"
879−hungarumlaut 'u' = "ű"
880−hungarumlaut 'y' = "ӳ"
881−hungarumlaut c = [c]
1463+preamble :: PandocMonad m => LP m Blocks
1464+preamble = mempty <$ many preambleBlock
1465+ where preambleBlock = spaces1
1466+ <|> void include
1467+ <|> void macroDef
1468+ <|> void blockCommand
1469+ <|> void braced
1470+ <|> (notFollowedBy (begin_ "document") >> void anyTok)
8821471
883−dot :: Char -> String
884−dot 'C' = "Ċ"
885−dot 'c' = "ċ"
886−dot 'E' = "Ė"
887−dot 'e' = "ė"
888−dot 'G' = "Ġ"
889−dot 'g' = "ġ"
890−dot 'I' = "İ"
891−dot 'Z' = "Ż"
892−dot 'z' = "ż"
893−dot c = [c]
1472+paragraph :: PandocMonad m => LP m Blocks
1473+paragraph = do
1474+ x <- trimInlines . mconcat <$> many1 inline
1475+ if x == mempty
1476+ then return mempty
1477+ else return $ para x
8941478
895−macron :: Char -> String
896−macron 'A' = "Ā"
897−macron 'E' = "Ē"
898−macron 'I' = "Ī"
899−macron 'O' = "Ō"
900−macron 'U' = "Ū"
901−macron 'a' = "ā"
902−macron 'e' = "ē"
903−macron 'i' = "ī"
904−macron 'o' = "ō"
905−macron 'u' = "ū"
906−macron c = [c]
1479+include :: PandocMonad m => LP m Blocks
1480+include = do
1481+ (Tok _ (CtrlSeq name) _) <-
1482+ controlSeq "include" <|> controlSeq "input" <|>
1483+ controlSeq "subfile" <|> controlSeq "usepackage"
1484+ skipMany $ bracketed inline -- skip options
1485+ fs <- (map trim . splitBy (==',') . T.unpack . untokenize) <$> braced
1486+ let fs' = if name == "usepackage"
1487+ then map (maybeAddExtension ".sty") fs
1488+ else map (maybeAddExtension ".tex") fs
1489+ dirs <- (splitBy (==':') . fromMaybe ".") <$> lookupEnv "TEXINPUTS"
1490+ mconcat <$> mapM (insertIncludedFile blocks (tokenize . T.pack) dirs) fs'
9071491
908−cedilla :: Char -> String
909−cedilla 'c' = "ç"
910−cedilla 'C' = "Ç"
911−cedilla 's' = "ş"
912−cedilla 'S' = "Ş"
913−cedilla 't' = "ţ"
914−cedilla 'T' = "Ţ"
915−cedilla 'e' = "ȩ"
916−cedilla 'E' = "Ȩ"
917−cedilla 'h' = "ḩ"
918−cedilla 'H' = "Ḩ"
919−cedilla 'o' = "o̧"
920−cedilla 'O' = "O̧"
921−cedilla c = [c]
1492+maybeAddExtension :: String -> FilePath -> FilePath
1493+maybeAddExtension ext fp =
1494+ if null (takeExtension fp)
1495+ then addExtension fp ext
1496+ else fp
9221497
923−hacek :: Char -> String
924−hacek 'A' = "Ǎ"
925−hacek 'a' = "ǎ"
926−hacek 'C' = "Č"
927−hacek 'c' = "č"
928−hacek 'D' = "Ď"
929−hacek 'd' = "ď"
930−hacek 'E' = "Ě"
931−hacek 'e' = "ě"
932−hacek 'G' = "Ǧ"
933−hacek 'g' = "ǧ"
934−hacek 'H' = "Ȟ"
935−hacek 'h' = "ȟ"
936−hacek 'I' = "Ǐ"
937−hacek 'i' = "ǐ"
938−hacek 'j' = "ǰ"
939−hacek 'K' = "Ǩ"
940−hacek 'k' = "ǩ"
941−hacek 'L' = "Ľ"
942−hacek 'l' = "ľ"
943−hacek 'N' = "Ň"
944−hacek 'n' = "ň"
945−hacek 'O' = "Ǒ"
946−hacek 'o' = "ǒ"
947−hacek 'R' = "Ř"
948−hacek 'r' = "ř"
949−hacek 'S' = "Š"
950−hacek 's' = "š"
951−hacek 'T' = "Ť"
952−hacek 't' = "ť"
953−hacek 'U' = "Ǔ"
954−hacek 'u' = "ǔ"
955−hacek 'Z' = "Ž"
956−hacek 'z' = "ž"
957−hacek c = [c]
1498+addMeta :: PandocMonad m => ToMetaValue a => String -> a -> LP m ()
1499+addMeta field val = updateState $ \st ->
1500+ st{ sMeta = addMetaField field val $ sMeta st }
9581501
959−breve :: Char -> String
960−breve 'A' = "Ă"
961−breve 'a' = "ă"
962−breve 'E' = "Ĕ"
963−breve 'e' = "ĕ"
964−breve 'G' = "Ğ"
965−breve 'g' = "ğ"
966−breve 'I' = "Ĭ"
967−breve 'i' = "ĭ"
968−breve 'O' = "Ŏ"
969−breve 'o' = "ŏ"
970−breve 'U' = "Ŭ"
971−breve 'u' = "ŭ"
972−breve c = [c]
1502+authors :: PandocMonad m => LP m ()
1503+authors = try $ do
1504+ bgroup
1505+ let oneAuthor = mconcat <$>
1506+ many1 (notFollowedBy' (controlSeq "and") >>
1507+ (inline <|> mempty <$ blockCommand))
1508+ -- skip e.g. \vspace{10pt}
1509+ auths <- sepBy oneAuthor (controlSeq "and")
1510+ egroup
1511+ addMeta "author" (map trimInlines auths)
9731512
974−tok :: PandocMonad m => LP m Inlines
975−tok = try $ grouped inline <|> inlineCommand <|> str <$> count 1 inlineChar
1513+macroDef :: PandocMonad m => LP m Blocks
1514+macroDef = do
1515+ guardEnabled Ext_latex_macros
1516+ mempty <$ ((commandDef <|> environmentDef) <* doMacros 0)
1517+ where commandDef = do
1518+ (name, macro') <- newcommand
1519+ updateState $ \s -> s{ sMacros = M.insert name macro' (sMacros s) }
1520+ environmentDef = do
1521+ (name, macro1, macro2) <- newenvironment
1522+ updateState $ \s -> s{ sMacros =
1523+ M.insert name macro1 (sMacros s) }
1524+ updateState $ \s -> s{ sMacros =
1525+ M.insert ("end" <> name) macro2 (sMacros s) }
1526+ -- @\newenvironment{envname}[n-args][default]{begin}{end}@
1527+ -- is equivalent to
1528+ -- @\newcommand{\envname}[n-args][default]{begin}@
1529+ -- @\newcommand{\endenvname}@
1530+
1531+newcommand :: PandocMonad m => LP m (Text, Macro)
1532+newcommand = do
1533+ pos <- getPosition
1534+ Tok _ (CtrlSeq mtype) _ <- controlSeq "newcommand" <|>
1535+ controlSeq "renewcommand" <|>
1536+ controlSeq "providecommand"
1537+ optional $ symbol '*'
1538+ Tok _ (CtrlSeq name) txt <- withVerbatimMode $ anyControlSeq <|>
1539+ (symbol '{' *> spaces *> anyControlSeq <* spaces <* symbol '}')
1540+ spaces
1541+ numargs <- option 0 $ try bracketedNum
1542+ spaces
1543+ optarg <- option Nothing $ Just <$> try bracketedToks
1544+ spaces
1545+ contents <- braced
1546+ when (mtype == "newcommand") $ do
1547+ macros <- sMacros <$> getState
1548+ case M.lookup name macros of
1549+ Just _ -> report $ MacroAlreadyDefined (T.unpack txt) pos
1550+ Nothing -> return ()
1551+ return (name, Macro numargs optarg contents)
1552+
1553+newenvironment :: PandocMonad m => LP m (Text, Macro, Macro)
1554+newenvironment = do
1555+ pos <- getPosition
1556+ Tok _ (CtrlSeq mtype) _ <- controlSeq "newenvironment" <|>
1557+ controlSeq "renewenvironment" <|>
1558+ controlSeq "provideenvironment"
1559+ optional $ symbol '*'
1560+ symbol '{'
1561+ spaces
1562+ Tok _ Word name <- satisfyTok isWordTok
1563+ spaces
1564+ symbol '}'
1565+ spaces
1566+ numargs <- option 0 $ try bracketedNum
1567+ spaces
1568+ optarg <- option Nothing $ Just <$> try bracketedToks
1569+ spaces
1570+ startcontents <- braced
1571+ spaces
1572+ endcontents <- braced
1573+ when (mtype == "newenvironment") $ do
1574+ macros <- sMacros <$> getState
1575+ case M.lookup name macros of
1576+ Just _ -> report $ MacroAlreadyDefined (T.unpack name) pos
1577+ Nothing -> return ()
1578+ return (name, Macro numargs optarg startcontents,
1579+ Macro 0 Nothing endcontents)
1580+
1581+bracketedToks :: PandocMonad m => LP m [Tok]
1582+bracketedToks = do
1583+ symbol '['
1584+ manyTill anyTok (symbol ']')
1585+
1586+bracketedNum :: PandocMonad m => LP m Int
1587+bracketedNum = do
1588+ ds <- untokenize <$> bracketedToks
1589+ case safeRead (T.unpack ds) of
1590+ Just i -> return i
1591+ _ -> return 0
9761592
977−opt :: PandocMonad m => LP m Inlines
978−opt = bracketed inline
1593+setCaption :: PandocMonad m => LP m Blocks
1594+setCaption = do
1595+ ils <- tok
1596+ mblabel <- option Nothing $
1597+ try $ spaces >> controlSeq "label" >> (Just <$> tok)
1598+ let ils' = case mblabel of
1599+ Just lab -> ils <> spanWith
1600+ ("",[],[("data-label", stringify lab)]) mempty
1601+ Nothing -> ils
1602+ updateState $ \st -> st{ sCaption = Just ils' }
1603+ return mempty
9791604
980−rawopt :: PandocMonad m => LP m String
981−rawopt = do
982− contents <- bracketed (many1 (noneOf "[]") <|> try (string "\\]") <|>
983− try (string "\\[") <|> rawopt)
984− optional sp
985− return $ "[" ++ contents ++ "]"
1605+looseItem :: PandocMonad m => LP m Blocks
1606+looseItem = do
1607+ inListItem <- sInListItem <$> getState
1608+ guard $ not inListItem
1609+ skipopts
1610+ return mempty
9861611
987−skipopts :: PandocMonad m => LP m ()
988−skipopts = skipMany rawopt
1612+resetCaption :: PandocMonad m => LP m ()
1613+resetCaption = updateState $ \st -> st{ sCaption = Nothing }
9891614
990−-- opts in angle brackets are used in beamer
991−rawangle :: PandocMonad m => LP m ()
992−rawangle = try $ do
993− char '<'
994− skipMany (noneOf ">")
995− char '>'
996− return ()
1615+section :: PandocMonad m => Attr -> Int -> LP m Blocks
1616+section (ident, classes, kvs) lvl = do
1617+ skipopts
1618+ contents <- grouped inline
1619+ lab <- option ident $
1620+ try (spaces >> controlSeq "label"
1621+ >> spaces >> toksToString <$> braced)
1622+ attr' <- registerHeader (lab, classes, kvs) contents
1623+ return $ headerWith attr' lvl contents
9971624
998−skipangles :: PandocMonad m => LP m ()
999−skipangles = skipMany rawangle
1625+blockCommand :: PandocMonad m => LP m Blocks
1626+blockCommand = try $ do
1627+ Tok _ (CtrlSeq name) txt <- anyControlSeq
1628+ guard $ name /= "begin" && name /= "end"
1629+ star <- option "" ("*" <$ symbol '*' <* optional sp)
1630+ let name' = name <> star
1631+ let names = ordNub [name', name]
1632+ let raw = do
1633+ guard $ isBlockCommand name || not (isInlineCommand name)
1634+ rawBlock "latex" <$> getRawCommand txt
1635+ lookupListDefault raw names blockCommands
10001636
1001−inlineText :: PandocMonad m => LP m Inlines
1002−inlineText = str <$> many1 inlineChar
1637+closing :: PandocMonad m => LP m Blocks
1638+closing = do
1639+ contents <- tok
1640+ st <- getState
1641+ let extractInlines (MetaBlocks [Plain ys]) = ys
1642+ extractInlines (MetaBlocks [Para ys ]) = ys
1643+ extractInlines _ = []
1644+ let sigs = case lookupMeta "author" (sMeta st) of
1645+ Just (MetaList xs) ->
1646+ para $ trimInlines $ fromList $
1647+ intercalate [LineBreak] $ map extractInlines xs
1648+ _ -> mempty
1649+ return $ para (trimInlines contents) <> sigs
10031650
1004−inlineChar :: PandocMonad m => LP m Char
1005−inlineChar = noneOf "\\$%&~#{}^'`\"‘’“”-[] \t\n"
1651+blockCommands :: PandocMonad m => M.Map Text (LP m Blocks)
1652+blockCommands = M.fromList $
1653+ [ ("par", mempty <$ skipopts)
1654+ , ("parbox", braced >> grouped blocks)
1655+ , ("title", mempty <$ (skipopts *>
1656+ (grouped inline >>= addMeta "title")
1657+ <|> (grouped block >>= addMeta "title")))
1658+ , ("subtitle", mempty <$ (skipopts *> tok >>= addMeta "subtitle"))
1659+ , ("author", mempty <$ (skipopts *> authors))
1660+ -- -- in letter class, temp. store address & sig as title, author
1661+ , ("address", mempty <$ (skipopts *> tok >>= addMeta "address"))
1662+ , ("signature", mempty <$ (skipopts *> authors))
1663+ , ("date", mempty <$ (skipopts *> tok >>= addMeta "date"))
1664+ -- Koma-script metadata commands
1665+ , ("dedication", mempty <$ (skipopts *> tok >>= addMeta "dedication"))
1666+ -- sectioning
1667+ , ("part", section nullAttr (-1))
1668+ , ("part*", section nullAttr (-1))
1669+ , ("chapter", section nullAttr 0)
1670+ , ("chapter*", section ("",["unnumbered"],[]) 0)
1671+ , ("section", section nullAttr 1)
1672+ , ("section*", section ("",["unnumbered"],[]) 1)
1673+ , ("subsection", section nullAttr 2)
1674+ , ("subsection*", section ("",["unnumbered"],[]) 2)
1675+ , ("subsubsection", section nullAttr 3)
1676+ , ("subsubsection*", section ("",["unnumbered"],[]) 3)
1677+ , ("paragraph", section nullAttr 4)
1678+ , ("paragraph*", section ("",["unnumbered"],[]) 4)
1679+ , ("subparagraph", section nullAttr 5)
1680+ , ("subparagraph*", section ("",["unnumbered"],[]) 5)
1681+ -- beamer slides
1682+ , ("frametitle", section nullAttr 3)
1683+ , ("framesubtitle", section nullAttr 4)
1684+ -- letters
1685+ , ("opening", (para . trimInlines) <$> (skipopts *> tok))
1686+ , ("closing", skipopts *> closing)
1687+ --
1688+ , ("hrule", pure horizontalRule)
1689+ , ("strut", pure mempty)
1690+ , ("rule", skipopts *> tok *> tok *> pure horizontalRule)
1691+ , ("item", looseItem)
1692+ , ("documentclass", skipopts *> braced *> preamble)
1693+ , ("centerline", (para . trimInlines) <$> (skipopts *> tok))
1694+ , ("caption", skipopts *> setCaption)
1695+ , ("bibliography", mempty <$ (skipopts *> braced >>=
1696+ addMeta "bibliography" . splitBibs . toksToString))
1697+ , ("addbibresource", mempty <$ (skipopts *> braced >>=
1698+ addMeta "bibliography" . splitBibs . toksToString))
1699+ -- includes
1700+ , ("lstinputlisting", inputListing)
1701+ , ("graphicspath", graphicsPath)
1702+ -- hyperlink
1703+ , ("hypertarget", try $ braced >> grouped block)
1704+ ]
1705+
1706+
1707+environments :: PandocMonad m => M.Map Text (LP m Blocks)
1708+environments = M.fromList
1709+ [ ("document", env "document" blocks)
1710+ , ("abstract", mempty <$ (env "abstract" blocks >>= addMeta "abstract"))
1711+ , ("letter", env "letter" letterContents)
1712+ , ("minipage", env "minipage" $
1713+ skipopts *> spaces *> optional braced *> spaces *> blocks)
1714+ , ("figure", env "figure" $ skipopts *> figure)
1715+ , ("subfigure", env "subfigure" $ skipopts *> tok *> figure)
1716+ , ("center", env "center" blocks)
1717+ , ("longtable", env "longtable" $
1718+ resetCaption *> simpTable "longtable" False >>= addTableCaption)
1719+ , ("table", env "table" $
1720+ resetCaption *> skipopts *> blocks >>= addTableCaption)
1721+ , ("tabular*", env "tabular" $ simpTable "tabular*" True)
1722+ , ("tabularx", env "tabularx" $ simpTable "tabularx" True)
1723+ , ("tabular", env "tabular" $ simpTable "tabular" False)
1724+ , ("quote", blockQuote <$> env "quote" blocks)
1725+ , ("quotation", blockQuote <$> env "quotation" blocks)
1726+ , ("verse", blockQuote <$> env "verse" blocks)
1727+ , ("itemize", bulletList <$> listenv "itemize" (many item))
1728+ , ("description", definitionList <$> listenv "description" (many descItem))
1729+ , ("enumerate", orderedList')
1730+ , ("alltt", alltt <$> env "alltt" blocks)
1731+ , ("code", guardEnabled Ext_literate_haskell *>
1732+ (codeBlockWith ("",["sourceCode","literate","haskell"],[]) <$>
1733+ verbEnv "code"))
1734+ , ("comment", mempty <$ verbEnv "comment")
1735+ , ("verbatim", codeBlock <$> verbEnv "verbatim")
1736+ , ("Verbatim", fancyverbEnv "Verbatim")
1737+ , ("BVerbatim", fancyverbEnv "BVerbatim")
1738+ , ("lstlisting", do attr <- parseListingsOptions <$> option [] keyvals
1739+ codeBlockWith attr <$> verbEnv "lstlisting")
1740+ , ("minted", minted)
1741+ , ("obeylines", obeylines)
1742+ , ("displaymath", mathEnvWith para Nothing "displaymath")
1743+ , ("equation", mathEnvWith para Nothing "equation")
1744+ , ("equation*", mathEnvWith para Nothing "equation*")
1745+ , ("gather", mathEnvWith para (Just "gathered") "gather")
1746+ , ("gather*", mathEnvWith para (Just "gathered") "gather*")
1747+ , ("multline", mathEnvWith para (Just "gathered") "multline")
1748+ , ("multline*", mathEnvWith para (Just "gathered") "multline*")
1749+ , ("eqnarray", mathEnvWith para (Just "aligned") "eqnarray")
1750+ , ("eqnarray*", mathEnvWith para (Just "aligned") "eqnarray*")
1751+ , ("align", mathEnvWith para (Just "aligned") "align")
1752+ , ("align*", mathEnvWith para (Just "aligned") "align*")
1753+ , ("alignat", mathEnvWith para (Just "aligned") "alignat")
1754+ , ("alignat*", mathEnvWith para (Just "aligned") "alignat*")
1755+ , ("tikzpicture", rawVerbEnv "tikzpicture")
1756+ ]
10061757
10071758environment :: PandocMonad m => LP m Blocks
10081759environment = do
10091760 controlSeq "begin"
1010− name <- braced
1761+ name <- untokenize <$> braced
10111762 M.findWithDefault mzero name environments
10121763 <|> rawEnv name
10131764
1014−inlineEnvironment :: PandocMonad m => LP m Inlines
1015−inlineEnvironment = try $ do
1016− controlSeq "begin"
1017− name <- braced
1018− M.findWithDefault mzero name inlineEnvironments
1765+env :: PandocMonad m => Text -> LP m a -> LP m a
1766+env name p = p <* end_ name
10191767
1020−rawEnv :: PandocMonad m => String -> LP m Blocks
1768+rawEnv :: PandocMonad m => Text -> LP m Blocks
10211769rawEnv name = do
10221770 exts <- getOption readerExtensions
10231771 let parseRaw = extensionEnabled Ext_raw_tex exts
10241772 rawOptions <- mconcat <$> many rawopt
1025− let beginCommand = "\\begin{" ++ name ++ "}" ++ rawOptions
1773+ let beginCommand = "\\begin{" <> name <> "}" <> rawOptions
10261774 pos1 <- getPosition
10271775 (bs, raw) <- withRaw $ env name blocks
1028− raw' <- applyMacros' $ beginCommand ++ raw
1029− if raw' /= beginCommand ++ raw
1030− then parseFromString' blocks raw'
1031− else if parseRaw
1032− then return $ rawBlock "latex" $ beginCommand ++ raw'
1033− else do
1034− unless parseRaw $ do
1035− report $ SkippedContent beginCommand pos1
1036− pos2 <- getPosition
1037− report $ SkippedContent ("\\end{" ++ name ++ "}") pos2
1038− return bs
1039−
1040−rawVerbEnv :: PandocMonad m => String -> LP m Blocks
1776+ if parseRaw
1777+ then return $ rawBlock "latex"
1778+ $ T.unpack $ beginCommand <> untokenize raw
1779+ else do
1780+ unless parseRaw $ do
1781+ report $ SkippedContent (T.unpack beginCommand) pos1
1782+ pos2 <- getPosition
1783+ report $ SkippedContent ("\\end{" ++ T.unpack name ++ "}") pos2
1784+ return bs
1785+
1786+rawVerbEnv :: PandocMonad m => Text -> LP m Blocks
10411787rawVerbEnv name = do
10421788 pos <- getPosition
10431789 (_, raw) <- withRaw $ verbEnv name
1044− let raw' = "\\begin{tikzpicture}" ++ raw
1790+ let raw' = "\\begin{tikzpicture}" ++ toksToString raw
10451791 exts <- getOption readerExtensions
10461792 let parseRaw = extensionEnabled Ext_raw_tex exts
10471793 if parseRaw
@@ -1050,36 +1796,106 @@ rawVerbEnv name = do
10501796 report $ SkippedContent raw' pos
10511797 return mempty
10521798
1053−----
1799+verbEnv :: PandocMonad m => Text -> LP m String
1800+verbEnv name = withVerbatimMode $ do
1801+ skipopts
1802+ optional blankline
1803+ res <- manyTill anyTok (end_ name)
1804+ return $ stripTrailingNewlines $ toksToString res
1805+
1806+fancyverbEnv :: PandocMonad m => Text -> LP m Blocks
1807+fancyverbEnv name = do
1808+ options <- option [] keyvals
1809+ let kvs = [ (if k == "firstnumber"
1810+ then "startFrom"
1811+ else k, v) | (k,v) <- options ]
1812+ let classes = [ "numberLines" |
1813+ lookup "numbers" options == Just "left" ]
1814+ let attr = ("",classes,kvs)
1815+ codeBlockWith attr <$> verbEnv name
1816+
1817+obeylines :: PandocMonad m => LP m Blocks
1818+obeylines = do
1819+ para . fromList . removeLeadingTrailingBreaks .
1820+ walk softBreakToHard . toList <$> env "obeylines" inlines
1821+ where softBreakToHard SoftBreak = LineBreak
1822+ softBreakToHard x = x
1823+ removeLeadingTrailingBreaks = reverse . dropWhile isLineBreak .
1824+ reverse . dropWhile isLineBreak
1825+ isLineBreak LineBreak = True
1826+ isLineBreak _ = False
1827+
1828+minted :: PandocMonad m => LP m Blocks
1829+minted = do
1830+ options <- option [] keyvals
1831+ lang <- toksToString <$> braced
1832+ let kvs = [ (if k == "firstnumber"
1833+ then "startFrom"
1834+ else k, v) | (k,v) <- options ]
1835+ let classes = [ lang | not (null lang) ] ++
1836+ [ "numberLines" |
1837+ lookup "linenos" options == Just "true" ]
1838+ let attr = ("",classes,kvs)
1839+ codeBlockWith attr <$> verbEnv "minted"
1840+
1841+letterContents :: PandocMonad m => LP m Blocks
1842+letterContents = do
1843+ bs <- blocks
1844+ st <- getState
1845+ -- add signature (author) and address (title)
1846+ let addr = case lookupMeta "address" (sMeta st) of
1847+ Just (MetaBlocks [Plain xs]) ->
1848+ para $ trimInlines $ fromList xs
1849+ _ -> mempty
1850+ return $ addr <> bs -- sig added by \closing
1851+
1852+figure :: PandocMonad m => LP m Blocks
1853+figure = try $ do
1854+ resetCaption
1855+ blocks >>= addImageCaption
1856+
1857+addImageCaption :: PandocMonad m => Blocks -> LP m Blocks
1858+addImageCaption = walkM go
1859+ where go (Image attr alt (src,tit))
1860+ | not ("fig:" `isPrefixOf` tit) = do
1861+ mbcapt <- sCaption <$> getState
1862+ return $ case mbcapt of
1863+ Just ils -> Image attr (toList ils) (src, "fig:" ++ tit)
1864+ Nothing -> Image attr alt (src,tit)
1865+ go x = return x
10541866
1055−maybeAddExtension :: String -> FilePath -> FilePath
1056−maybeAddExtension ext fp =
1057− if null (takeExtension fp)
1058− then addExtension fp ext
1059− else fp
1867+graphicsPath :: PandocMonad m => LP m Blocks
1868+graphicsPath = do
1869+ ps <- map toksToString <$> (bgroup *> manyTill braced egroup)
1870+ getResourcePath >>= setResourcePath . (++ ps)
1871+ return mempty
10601872
1061−include :: PandocMonad m => LP m Blocks
1062−include = do
1063− fs' <- try $ do
1064− char '\\'
1065− name <- try (string "include")
1066− <|> try (string "input")
1067− <|> try (string "subfile")
1068− <|> string "usepackage"
1069− -- skip options
1070− skipMany $ try $ char '[' *> manyTill anyChar (char ']')
1071− fs <- (map trim . splitBy (==',')) <$> braced
1072− return $ if name == "usepackage"
1073− then map (maybeAddExtension ".sty") fs
1074− else map (maybeAddExtension ".tex") fs
1075− dirs <- (splitBy (==':') . fromMaybe ".") <$> lookupEnv "TEXINPUTS"
1076− mconcat <$> mapM (insertIncludedFile blocks dirs) fs'
1873+splitBibs :: String -> [Inlines]
1874+splitBibs = map (str . flip replaceExtension "bib" . trim) . splitBy (==',')
1875+
1876+alltt :: Blocks -> Blocks
1877+alltt = walk strToCode
1878+ where strToCode (Str s) = Code nullAttr s
1879+ strToCode Space = RawInline (Format "latex") "\\ "
1880+ strToCode SoftBreak = LineBreak
1881+ strToCode x = x
1882+
1883+parseListingsOptions :: [(String, String)] -> Attr
1884+parseListingsOptions options =
1885+ let kvs = [ (if k == "firstnumber"
1886+ then "startFrom"
1887+ else k, v) | (k,v) <- options ]
1888+ classes = [ "numberLines" |
1889+ lookup "numbers" options == Just "left" ]
1890+ ++ maybeToList (lookup "language" options
1891+ >>= fromListingsLanguage)
1892+ in (fromMaybe "" (lookup "label" options), classes, kvs)
10771893
10781894inputListing :: PandocMonad m => LP m Blocks
10791895inputListing = do
10801896 pos <- getPosition
10811897 options <- option [] keyvals
1082− f <- filter (/='"') <$> braced
1898+ f <- filter (/='"') . toksToString <$> braced
10831899 dirs <- (splitBy (==':') . fromMaybe ".") <$> lookupEnv "TEXINPUTS"
10841900 mbCode <- readFileFromDirs dirs f
10851901 codeLines <- case mbCode of
@@ -1098,169 +1914,10 @@ inputListing = do
10981914 drop (firstline - 1) codeLines
10991915 return $ codeBlockWith (ident,ordNub (classes ++ language),kvs) codeContents
11001916
1101−parseListingsOptions :: [(String, String)] -> Attr
1102−parseListingsOptions options =
1103− let kvs = [ (if k == "firstnumber"
1104− then "startFrom"
1105− else k, v) | (k,v) <- options ]
1106− classes = [ "numberLines" |
1107− lookup "numbers" options == Just "left" ]
1108− ++ maybeToList (lookup "language" options
1109− >>= fromListingsLanguage)
1110− in (fromMaybe "" (lookup "label" options), classes, kvs)
1111−
1112−----
1113−
1114−keyval :: PandocMonad m => LP m (String, String)
1115−keyval = try $ do
1116− key <- many1 alphaNum
1117− val <- option "" $ char '=' >> braced <|> (many1 (alphaNum <|> oneOf ".:-|\\"))
1118− skipMany spaceChar
1119− optional (char ',')
1120− skipMany spaceChar
1121− return (key, val)
1122−
1123−
1124−keyvals :: PandocMonad m => LP m [(String, String)]
1125−keyvals = try $ char '[' *> manyTill keyval (char ']')
1126−
1127−alltt :: PandocMonad m => String -> LP m Blocks
1128−alltt t = walk strToCode <$> parseFromString' blocks
1129− (substitute " " "\\ " $ substitute "%" "\\%" $
1130− intercalate "\\\\\n" $ lines t)
1131− where strToCode (Str s) = Code nullAttr s
1132− strToCode x = x
1133−
1134−rawLaTeXBlock :: PandocMonad m => LP m String
1135−rawLaTeXBlock = snd <$> try (withRaw (environment <|> blockCommand))
1136−
1137−rawLaTeXInline :: PandocMonad m => LP m Inline
1138−rawLaTeXInline = do
1139− raw <- (snd <$> withRaw inlineCommand)
1140− <|> (snd <$> withRaw inlineEnvironment)
1141− <|> (snd <$> withRaw blockCommand)
1142− RawInline "latex" <$> applyMacros' raw
1143−
1144−addImageCaption :: PandocMonad m => Blocks -> LP m Blocks
1145−addImageCaption = walkM go
1146− where go (Image attr alt (src,tit))
1147− | not ("fig:" `isPrefixOf` tit) = do
1148− mbcapt <- stateCaption <$> getState
1149− return $ case mbcapt of
1150− Just ils -> Image attr (toList ils) (src, "fig:" ++ tit)
1151− Nothing -> Image attr alt (src,tit)
1152− go x = return x
1153−
1154−addTableCaption :: PandocMonad m => Blocks -> LP m Blocks
1155−addTableCaption = walkM go
1156− where go (Table c als ws hs rs) = do
1157− mbcapt <- stateCaption <$> getState
1158− return $ case mbcapt of
1159− Just ils -> Table (toList ils) als ws hs rs
1160− Nothing -> Table c als ws hs rs
1161− go x = return x
1162−
1163−environments :: PandocMonad m => M.Map String (LP m Blocks)
1164−environments = M.fromList
1165− [ ("document", env "document" blocks <* skipMany anyChar)
1166− , ("abstract", mempty <$ (env "abstract" blocks >>= addMeta "abstract"))
1167− , ("letter", env "letter" letterContents)
1168− , ("minipage", env "minipage" $
1169− skipopts *> spaces' *> optional braced *> spaces' *> blocks)
1170− , ("figure", env "figure" $ skipopts *> figure)
1171− , ("subfigure", env "subfigure" $ skipopts *> tok *> figure)
1172− , ("center", env "center" blocks)
1173− , ("longtable", env "longtable" $
1174− resetCaption *> simpTable "longtable" False >>= addTableCaption)
1175− , ("table", env "table" $
1176− resetCaption *> skipopts *> blocks >>= addTableCaption)
1177− , ("tabular*", env "tabular" $ simpTable "tabular*" True)
1178− , ("tabularx", env "tabularx" $ simpTable "tabularx" True)
1179− , ("tabular", env "tabular" $ simpTable "tabular" False)
1180− , ("quote", blockQuote <$> env "quote" blocks)
1181− , ("quotation", blockQuote <$> env "quotation" blocks)
1182− , ("verse", blockQuote <$> env "verse" blocks)
1183− , ("itemize", bulletList <$> listenv "itemize" (many item))
1184− , ("description", definitionList <$> listenv "description" (many descItem))
1185− , ("enumerate", orderedList')
1186− , ("alltt", alltt =<< verbEnv "alltt")
1187− , ("code", guardEnabled Ext_literate_haskell *>
1188− (codeBlockWith ("",["sourceCode","literate","haskell"],[]) <$>
1189− verbEnv "code"))
1190− , ("comment", mempty <$ verbEnv "comment")
1191− , ("verbatim", codeBlock <$> verbEnv "verbatim")
1192− , ("Verbatim", fancyverbEnv "Verbatim")
1193− , ("BVerbatim", fancyverbEnv "BVerbatim")
1194− , ("lstlisting", do attr <- parseListingsOptions <$> option [] keyvals
1195− codeBlockWith attr <$> verbEnv "lstlisting")
1196− , ("minted", do options <- option [] keyvals
1197− lang <- grouped (many1 $ satisfy (/='}'))
1198− let kvs = [ (if k == "firstnumber"
1199− then "startFrom"
1200− else k, v) | (k,v) <- options ]
1201− let classes = [ lang | not (null lang) ] ++
1202− [ "numberLines" |
1203− lookup "linenos" options == Just "true" ]
1204− let attr = ("",classes,kvs)
1205− codeBlockWith attr <$> verbEnv "minted")
1206− , ("obeylines", parseFromString
1207− (para . trimInlines . mconcat <$> many inline) =<<
1208− intercalate "\\\\\n" . lines <$> verbEnv "obeylines")
1209− , ("displaymath", mathEnvWith para Nothing "displaymath")
1210− , ("equation", mathEnvWith para Nothing "equation")
1211− , ("equation*", mathEnvWith para Nothing "equation*")
1212− , ("gather", mathEnvWith para (Just "gathered") "gather")
1213− , ("gather*", mathEnvWith para (Just "gathered") "gather*")
1214− , ("multline", mathEnvWith para (Just "gathered") "multline")
1215− , ("multline*", mathEnvWith para (Just "gathered") "multline*")
1216− , ("eqnarray", mathEnvWith para (Just "aligned") "eqnarray")
1217− , ("eqnarray*", mathEnvWith para (Just "aligned") "eqnarray*")
1218− , ("align", mathEnvWith para (Just "aligned") "align")
1219− , ("align*", mathEnvWith para (Just "aligned") "align*")
1220− , ("alignat", mathEnvWith para (Just "aligned") "alignat")
1221− , ("alignat*", mathEnvWith para (Just "aligned") "alignat*")
1222− , ("tikzpicture", rawVerbEnv "tikzpicture")
1223− ]
1224−
1225−figure :: PandocMonad m => LP m Blocks
1226−figure = try $ do
1227− resetCaption
1228− blocks >>= addImageCaption
1229−
1230−letterContents :: PandocMonad m => LP m Blocks
1231−letterContents = do
1232− bs <- blocks
1233− st <- getState
1234− -- add signature (author) and address (title)
1235− let addr = case lookupMeta "address" (stateMeta st) of
1236− Just (MetaBlocks [Plain xs]) ->
1237− para $ trimInlines $ fromList xs
1238− _ -> mempty
1239− return $ addr <> bs -- sig added by \closing
1240−
1241−closing :: PandocMonad m => LP m Blocks
1242−closing = do
1243− contents <- tok
1244− st <- getState
1245− let extractInlines (MetaBlocks [Plain ys]) = ys
1246− extractInlines (MetaBlocks [Para ys ]) = ys
1247− extractInlines _ = []
1248− let sigs = case lookupMeta "author" (stateMeta st) of
1249− Just (MetaList xs) ->
1250− para $ trimInlines $ fromList $
1251− intercalate [LineBreak] $ map extractInlines xs
1252− _ -> mempty
1253− return $ para (trimInlines contents) <> sigs
1917+-- lists
12541918
12551919item :: PandocMonad m => LP m Blocks
1256−item = blocks *> controlSeq "item" *> skipopts *> blocks
1257−
1258−looseItem :: PandocMonad m => LP m Blocks
1259−looseItem = do
1260− ctx <- stateParserContext `fmap` getState
1261− if ctx == ListItemState
1262− then mzero
1263− else return mempty
1920+item = void blocks *> controlSeq "item" *> skipopts *> blocks
12641921
12651922descItem :: PandocMonad m => LP m (Inlines, [Blocks])
12661923descItem = do
@@ -1271,302 +1928,210 @@ descItem = do
12711928 bs <- blocks
12721929 return (ils, [bs])
12731930
1274−env :: PandocMonad m => String -> LP m a -> LP m a
1275−env name p = p <*
1276− (try (controlSeq "end" *> braced >>= guard . (== name))
1277− <?> ("\\end{" ++ name ++ "}"))
1278−
1279−listenv :: PandocMonad m => String -> LP m a -> LP m a
1931+listenv :: PandocMonad m => Text -> LP m a -> LP m a
12801932listenv name p = try $ do
1281− oldCtx <- stateParserContext `fmap` getState
1282− updateState $ \st -> st{ stateParserContext = ListItemState }
1933+ oldInListItem <- sInListItem `fmap` getState
1934+ updateState $ \st -> st{ sInListItem = True }
12831935 res <- env name p
1284− updateState $ \st -> st{ stateParserContext = oldCtx }
1936+ updateState $ \st -> st{ sInListItem = oldInListItem }
12851937 return res
12861938
1287−mathEnvWith :: PandocMonad m
1288− => (Inlines -> a) -> Maybe String -> String -> LP m a
1289−mathEnvWith f innerEnv name = f <$> mathDisplay (inner <$> mathEnv name)
1290− where inner x = case innerEnv of
1291− Nothing -> x
1292− Just y -> "\\begin{" ++ y ++ "}\n" ++ x ++
1293− "\\end{" ++ y ++ "}"
1294−
1295−mathEnv :: PandocMonad m => String -> LP m String
1296−mathEnv name = do
1297− skipopts
1298− optional blankline
1299− let endEnv = try $ controlSeq "end" *> braced >>= guard . (== name)
1300− charMuncher = skipMany comment *>
1301− (many1 (noneOf "\\%") <|> try (string "\\%")
1302− <|> try (string "\\\\") <|> count 1 anyChar)
1303− res <- concat <$> manyTill charMuncher endEnv
1304− return $ stripTrailingNewlines res
1305−
1306−verbEnv :: PandocMonad m => String -> LP m String
1307−verbEnv name = do
1308− skipopts
1309− optional blankline
1310− let endEnv = try $ controlSeq "end" *> braced >>= guard . (== name)
1311− charMuncher = anyChar
1312− res <- manyTill charMuncher endEnv
1313− return $ stripTrailingNewlines res
1314−
1315−fancyverbEnv :: PandocMonad m => String -> LP m Blocks
1316−fancyverbEnv name = do
1317− options <- option [] keyvals
1318− let kvs = [ (if k == "firstnumber"
1319− then "startFrom"
1320− else k, v) | (k,v) <- options ]
1321− let classes = [ "numberLines" |
1322− lookup "numbers" options == Just "left" ]
1323− let attr = ("",classes,kvs)
1324− codeBlockWith attr <$> verbEnv name
1325−
13261939orderedList' :: PandocMonad m => LP m Blocks
13271940orderedList' = try $ do
1328− optional sp
1329− (_, style, delim) <- option (1, DefaultStyle, DefaultDelim) $
1330− try $ char '[' *> anyOrderedListMarker <* char ']'
13311941 spaces
1332− optional $ try $ controlSeq "setlength" *> grouped (controlSeq "itemindent") *> braced
1942+ let markerSpec = do
1943+ symbol '['
1944+ ts <- toksToString <$> manyTill anyTok (symbol ']')
1945+ case runParser anyOrderedListMarker def "option" ts of
1946+ Right r -> return r
1947+ Left _ -> do
1948+ pos <- getPosition
1949+ report $ SkippedContent ("[" ++ ts ++ "]") pos
1950+ return (1, DefaultStyle, DefaultDelim)
1951+ (_, style, delim) <- option (1, DefaultStyle, DefaultDelim) markerSpec
13331952 spaces
1334− start <- option 1 $ try $ do controlSeq "setcounter"
1335− grouped (string "enum" *> many1 (oneOf "iv"))
1953+ optional $ try $ controlSeq "setlength"
1954+ *> grouped (count 1 $ controlSeq "itemindent")
1955+ *> braced
1956+ spaces
1957+ start <- option 1 $ try $ do pos <- getPosition
1958+ controlSeq "setcounter"
1959+ ctr <- toksToString <$> braced
1960+ guard $ "enum" `isPrefixOf` ctr
1961+ guard $ all (`elem` ['i','v']) (drop 4 ctr)
13361962 optional sp
1337− num <- grouped (many1 digit)
1338− spaces
1339− return (read num + 1 :: Int)
1963+ num <- toksToString <$> braced
1964+ case safeRead num of
1965+ Just i -> return (i + 1 :: Int)
1966+ Nothing -> do
1967+ report $ SkippedContent
1968+ ("\\setcounter{" ++ ctr ++
1969+ "}{" ++ num ++ "}") pos
1970+ return 1
13401971 bs <- listenv "enumerate" (many item)
13411972 return $ orderedListWith (start, style, delim) bs
13421973
1343−paragraph :: PandocMonad m => LP m Blocks
1344−paragraph = do
1345− x <- trimInlines . mconcat <$> many1 inline
1346− if x == mempty
1347− then return mempty
1348− else return $ para x
1349−
1350−preamble :: PandocMonad m => LP m Blocks
1351−preamble = mempty <$> manyTill preambleBlock beginDoc
1352− where beginDoc = lookAhead $ try $ controlSeq "begin" *> string "{document}"
1353− preambleBlock = void comment
1354− <|> void sp
1355− <|> void blanklines
1356− <|> void include
1357− <|> void macro
1358− <|> void blockCommand
1359− <|> void anyControlSeq
1360− <|> void braced
1361− <|> void anyChar
1362−
1363−-------
1364−
1365−-- citations
1366−
1367−addPrefix :: [Inline] -> [Citation] -> [Citation]
1368−addPrefix p (k:ks) = k {citationPrefix = p ++ citationPrefix k} : ks
1369−addPrefix _ _ = []
1370−
1371−addSuffix :: [Inline] -> [Citation] -> [Citation]
1372−addSuffix s ks@(_:_) =
1373− let k = last ks
1374− in init ks ++ [k {citationSuffix = citationSuffix k ++ s}]
1375−addSuffix _ _ = []
1376−
1377−simpleCiteArgs :: PandocMonad m => LP m [Citation]
1378−simpleCiteArgs = try $ do
1379− first <- optionMaybe $ toList <$> opt
1380− second <- optionMaybe $ toList <$> opt
1381− keys <- try $ bgroup *> (manyTill citationLabel egroup)
1382− let (pre, suf) = case (first , second ) of
1383− (Just s , Nothing) -> (mempty, s )
1384− (Just s , Just t ) -> (s , t )
1385− _ -> (mempty, mempty)
1386− conv k = Citation { citationId = k
1387− , citationPrefix = []
1388− , citationSuffix = []
1389− , citationMode = NormalCitation
1390− , citationHash = 0
1391− , citationNoteNum = 0
1392− }
1393− return $ addPrefix pre $ addSuffix suf $ map conv keys
1394−
1395−citationLabel :: PandocMonad m => LP m String
1396−citationLabel = optional sp *>
1397− (many1 (satisfy isBibtexKeyChar)
1398− <* optional sp
1399− <* optional (char ',')
1400− <* optional sp)
1401− where isBibtexKeyChar c = isAlphaNum c || c `elem` (".:;?!`'()/*@_+=-[]" :: String)
1974+-- tables
14021975
1403−cites :: PandocMonad m => CitationMode -> Bool -> LP m [Citation]
1404−cites mode multi = try $ do
1405− cits <- if multi
1406− then many1 simpleCiteArgs
1407− else count 1 simpleCiteArgs
1408− let cs = concat cits
1409− return $ case mode of
1410− AuthorInText -> case cs of
1411− (c:rest) -> c {citationMode = mode} : rest
1412− [] -> []
1413− _ -> map (\a -> a {citationMode = mode}) cs
1976+hline :: PandocMonad m => LP m ()
1977+hline = try $ do
1978+ spaces
1979+ controlSeq "hline" <|>
1980+ -- booktabs rules:
1981+ controlSeq "toprule" <|>
1982+ controlSeq "bottomrule" <|>
1983+ controlSeq "midrule" <|>
1984+ controlSeq "endhead" <|>
1985+ controlSeq "endfirsthead"
1986+ spaces
1987+ optional $ bracketed inline
1988+ return ()
14141989
1415−citation :: PandocMonad m => String -> CitationMode -> Bool -> LP m Inlines
1416−citation name mode multi = do
1417− (c,raw) <- withRaw $ cites mode multi
1418− return $ cite c (rawInline "latex" $ "\\" ++ name ++ raw)
1990+lbreak :: PandocMonad m => LP m Tok
1991+lbreak = (controlSeq "\\" <|> controlSeq "tabularnewline") <* spaces
14191992
1420−complexNatbibCitation :: PandocMonad m => CitationMode -> LP m Inlines
1421−complexNatbibCitation mode = try $ do
1422− let ils = (toList . trimInlines . mconcat) <$>
1423− many (notFollowedBy (oneOf "\\};") >> inline)
1424− let parseOne = try $ do
1425− skipSpaces
1426− pref <- ils
1427− cit' <- inline -- expect a citation
1428− let citlist = toList cit'
1429− cits' <- case citlist of
1430− [Cite cs _] -> return cs
1431− _ -> mzero
1432− suff <- ils
1433− skipSpaces
1434− optional $ char ';'
1435− return $ addPrefix pref $ addSuffix suff cits'
1436− (c:cits, raw) <- withRaw $ grouped parseOne
1437− return $ cite (c{ citationMode = mode }:cits)
1438− (rawInline "latex" $ "\\citetext" ++ raw)
1993+amp :: PandocMonad m => LP m Tok
1994+amp = symbol '&'
14391995
1440−-- tables
1996+-- Split a Word into individual Symbols (for parseAligns)
1997+splitWordTok :: PandocMonad m => LP m ()
1998+splitWordTok = do
1999+ inp <- getInput
2000+ case inp of
2001+ (Tok spos Word t : rest) -> do
2002+ setInput $ map (Tok spos Symbol . T.singleton) (T.unpack t) ++ rest
2003+ _ -> return ()
14412004
1442−parseAligns :: PandocMonad m => LP m [(Alignment, Double, (String, String))]
2005+parseAligns :: PandocMonad m => LP m [(Alignment, Double, ([Tok], [Tok]))]
14432006parseAligns = try $ do
1444− bgroup
1445− let maybeBar = skipMany $ sp <|> () <$ char '|' <|> () <$ (char '@' >> braced)
1446− maybeBar
1447− let cAlign = AlignCenter <$ char 'c'
1448− let lAlign = AlignLeft <$ char 'l'
1449− let rAlign = AlignRight <$ char 'r'
1450− let parAlign = AlignLeft <$ char 'p'
1451− -- algins from tabularx
1452− let xAlign = AlignLeft <$ char 'X'
1453− let mAlign = AlignLeft <$ char 'm'
1454− let bAlign = AlignLeft <$ char 'b'
1455− let alignChar = cAlign <|> lAlign <|> rAlign <|> parAlign
1456− <|> xAlign <|> mAlign <|> bAlign
1457− let alignPrefix = char '>' >> braced
1458− let alignSuffix = char '<' >> braced
2007+ let maybeBar = skipMany $
2008+ sp <|> () <$ symbol '|' <|> () <$ (symbol '@' >> braced)
2009+ let cAlign = AlignCenter <$ symbol 'c'
2010+ let lAlign = AlignLeft <$ symbol 'l'
2011+ let rAlign = AlignRight <$ symbol 'r'
2012+ let parAlign = AlignLeft <$ symbol 'p'
2013+ -- aligns from tabularx
2014+ let xAlign = AlignLeft <$ symbol 'X'
2015+ let mAlign = AlignLeft <$ symbol 'm'
2016+ let bAlign = AlignLeft <$ symbol 'b'
2017+ let alignChar = splitWordTok *> ( cAlign <|> lAlign <|> rAlign <|> parAlign
2018+ <|> xAlign <|> mAlign <|> bAlign )
2019+ let alignPrefix = symbol '>' >> braced
2020+ let alignSuffix = symbol '<' >> braced
14592021 let colWidth = try $ do
1460− char '{'
1461− ds <- many1 (oneOf "0123456789.")
2022+ symbol '{'
2023+ ds <- trim . toksToString <$> manyTill anyTok (controlSeq "linewidth")
14622024 spaces
1463− string "\\linewidth"
1464− char '}'
2025+ symbol '}'
14652026 case safeRead ds of
14662027 Just w -> return w
14672028 Nothing -> return 0.0
1468− let alignSpec = do
2029+ let alignSpec = try $ do
14692030 spaces
1470− pref <- option "" alignPrefix
2031+ pref <- option [] alignPrefix
14712032 spaces
14722033 al <- alignChar
1473− width <- colWidth <|> option 0.0 (do s <- braced
2034+ width <- colWidth <|> option 0.0 (do s <- toksToString <$> braced
14742035 pos <- getPosition
14752036 report $ SkippedContent s pos
14762037 return 0.0)
14772038 spaces
1478− suff <- option "" alignSuffix
2039+ suff <- option [] alignSuffix
14792040 return (al, width, (pref, suff))
1480− aligns' <- sepEndBy alignSpec maybeBar
2041+ bgroup
2042+ spaces
2043+ maybeBar
2044+ aligns' <- many (alignSpec <* maybeBar)
14812045 spaces
14822046 egroup
14832047 spaces
1484− return $ aligns'
1485−
1486−hline :: PandocMonad m => LP m ()
1487−hline = try $ do
1488− spaces'
1489− controlSeq "hline" <|>
1490− -- booktabs rules:
1491− controlSeq "toprule" <|>
1492− controlSeq "bottomrule" <|>
1493− controlSeq "midrule" <|>
1494− controlSeq "endhead" <|>
1495− controlSeq "endfirsthead"
1496− spaces'
1497− optional $ bracketed (many1 (satisfy (/=']')))
1498− return ()
1499−
1500−lbreak :: PandocMonad m => LP m ()
1501−lbreak = () <$ try (spaces' *>
1502− (controlSeq "\\" <|> controlSeq "tabularnewline") <*
1503− spaces')
1504−
1505−amp :: PandocMonad m => LP m ()
1506−amp = () <$ try (spaces' *> char '&' <* spaces')
2048+ return aligns'
15072049
15082050parseTableRow :: PandocMonad m
1509− => String -- ^ table environment name
1510− -> [(String, String)] -- ^ pref/suffixes
2051+ => Text -- ^ table environment name
2052+ -> [([Tok], [Tok])] -- ^ pref/suffixes
15112053 -> LP m [Blocks]
1512−parseTableRow envname prefsufs = try $ do
2054+parseTableRow envname prefsufs = do
2055+ notFollowedBy (spaces *> end_ envname)
15132056 let cols = length prefsufs
1514− let tableCellRaw = concat <$> many
1515− (do notFollowedBy amp
1516− notFollowedBy lbreak
1517− notFollowedBy $ () <$ try (string ("\\end{" ++ envname ++ "}"))
1518− many1 (noneOf "&%\n\r\\")
1519− <|> try (string "\\&")
1520− <|> count 1 anyChar)
1521− let plainify bs = case toList bs of
1522− [Para ils] -> plain (fromList ils)
1523− _ -> bs
1524− rawcells <- sepBy1 tableCellRaw amp
1525− guard $ length rawcells == cols
1526− let rawcells' = zipWith (\c (p, s) -> p ++ trim c ++ s) rawcells prefsufs
1527− let tableCell = plainify <$> blocks
1528− cells' <- mapM (parseFromString' tableCell) rawcells'
1529− let numcells = length cells'
2057+ -- add prefixes and suffixes in token stream:
2058+ let celltoks (pref, suff) = do
2059+ prefpos <- getPosition
2060+ contents <- many (notFollowedBy
2061+ (() <$ amp <|> () <$ lbreak <|> end_ envname)
2062+ >> anyTok)
2063+ suffpos <- getPosition
2064+ option [] (count 1 amp)
2065+ return $ map (setpos (sourceLine prefpos, sourceColumn prefpos)) pref
2066+ ++ contents ++
2067+ map (setpos (sourceLine suffpos, sourceColumn suffpos)) suff
2068+ rawcells <- sequence (map celltoks prefsufs)
2069+ oldInput <- getInput
2070+ cells <- sequence $ map (\ts -> setInput ts >> parseTableCell) rawcells
2071+ setInput oldInput
2072+ spaces
2073+ let numcells = length cells
15302074 guard $ numcells <= cols && numcells >= 1
1531− guard $ cells' /= [mempty]
2075+ guard $ cells /= [mempty]
15322076 -- note: a & b in a three-column table leaves an empty 3rd cell:
1533− let cells'' = cells' ++ replicate (cols - numcells) mempty
1534− spaces'
1535− return cells''
2077+ return $ cells ++ replicate (cols - numcells) mempty
15362078
1537−spaces' :: PandocMonad m => LP m ()
1538−spaces' = spaces *> skipMany (comment *> spaces)
2079+parseTableCell :: PandocMonad m => LP m Blocks
2080+parseTableCell = do
2081+ let plainify bs = case toList bs of
2082+ [Para ils] -> plain (fromList ils)
2083+ _ -> bs
2084+ updateState $ \st -> st{ sInTableCell = True }
2085+ cells <- plainify <$> blocks
2086+ updateState $ \st -> st{ sInTableCell = False }
2087+ return cells
15392088
1540−simpTable :: PandocMonad m => String -> Bool -> LP m Blocks
2089+simpTable :: PandocMonad m => Text -> Bool -> LP m Blocks
15412090simpTable envname hasWidthParameter = try $ do
1542− when hasWidthParameter $ () <$ (spaces' >> tok)
2091+ when hasWidthParameter $ () <$ (spaces >> tok)
15432092 skipopts
15442093 colspecs <- parseAligns
15452094 let (aligns, widths, prefsufs) = unzip3 colspecs
15462095 let cols = length colspecs
15472096 optional $ controlSeq "caption" *> skipopts *> setCaption
15482097 optional lbreak
1549− spaces'
2098+ spaces
15502099 skipMany hline
1551− spaces'
2100+ spaces
15522101 header' <- option [] $ try (parseTableRow envname prefsufs <*
15532102 lbreak <* many1 hline)
1554− spaces'
2103+ spaces
15552104 rows <- sepEndBy (parseTableRow envname prefsufs)
15562105 (lbreak <* optional (skipMany hline))
1557− spaces'
2106+ spaces
15582107 optional $ controlSeq "caption" *> skipopts *> setCaption
15592108 optional lbreak
1560− spaces'
2109+ spaces
15612110 let header'' = if null header'
15622111 then replicate cols mempty
15632112 else header'
15642113 lookAhead $ controlSeq "end" -- make sure we're at end
15652114 return $ table mempty (zip aligns widths) header'' rows
15662115
1567−removeDoubleQuotes :: String -> String
1568−removeDoubleQuotes ('"':xs) =
1569− case reverse xs of
1570− '"':ys -> reverse ys
1571− _ -> '"':xs
1572−removeDoubleQuotes xs = xs
2116+addTableCaption :: PandocMonad m => Blocks -> LP m Blocks
2117+addTableCaption = walkM go
2118+ where go (Table c als ws hs rs) = do
2119+ mbcapt <- sCaption <$> getState
2120+ return $ case mbcapt of
2121+ Just ils -> Table (toList ils) als ws hs rs
2122+ Nothing -> Table c als ws hs rs
2123+ go x = return x
2124+
2125+
2126+block :: PandocMonad m => LP m Blocks
2127+block = (mempty <$ spaces1)
2128+ <|> environment
2129+ <|> include
2130+ <|> macroDef
2131+ <|> paragraph
2132+ <|> blockCommand
2133+ <|> grouped block
2134+
2135+blocks :: PandocMonad m => LP m Blocks
2136+blocks = mconcat <$> many block
2137+
src/Text/Pandoc/Readers/LaTeX/Types.hsnew file+48
@@ -0,0 +1,48 @@
1+{-
2+Copyright (C) 2017 John MacFarlane <jgm@berkeley.edu>
3+
4+This program is free software; you can redistribute it and/or modify
5+it under the terms of the GNU General Public License as published by
6+the Free Software Foundation; either version 2 of the License, or
7+(at your option) any later version.
8+
9+This program is distributed in the hope that it will be useful,
10+but WITHOUT ANY WARRANTY; without even the implied warranty of
11+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+GNU General Public License for more details.
13+
14+You should have received a copy of the GNU General Public License
15+along with this program; if not, write to the Free Software
16+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17+-}
18+
19+{- |
20+ Module : Text.Pandoc.Readers.LaTeX.Types
21+ Copyright : Copyright (C) 2017 John MacFarlane
22+ License : GNU GPL, version 2 or above
23+
24+ Maintainer : John MacFarlane <jgm@berkeley.edu>
25+ Stability : alpha
26+ Portability : portable
27+
28+Types for LaTeX tokens and macros.
29+-}
30+module Text.Pandoc.Readers.LaTeX.Types ( Tok(..)
31+ , TokType(..)
32+ , Macro(..)
33+ , Line
34+ , Column )
35+where
36+import Data.Text (Text)
37+import Text.Parsec.Pos (Line, Column)
38+
39+data TokType = CtrlSeq Text | Spaces | Newline | Symbol | Word | Comment |
40+ Esc1 | Esc2 | Arg Int
41+ deriving (Eq, Ord, Show)
42+
43+data Tok = Tok (Line, Column) TokType Text
44+ deriving (Eq, Ord, Show)
45+
46+data Macro = Macro Int (Maybe [Tok]) [Tok]
47+ deriving Show
48+
src/Text/Pandoc/Readers/Markdown.hs+10−9
@@ -61,7 +61,8 @@ import Text.Pandoc.Options
6161import Text.Pandoc.Parsing hiding (tableWith)
6262import Text.Pandoc.Readers.HTML (htmlInBalanced, htmlTag, isBlockTag,
6363 isCommentTag, isInlineTag, isTextTag)
64−import Text.Pandoc.Readers.LaTeX (rawLaTeXBlock, rawLaTeXInline)
64+import Text.Pandoc.Readers.LaTeX (rawLaTeXBlock, rawLaTeXInline, applyMacros,
65+ macro)
6566import Text.Pandoc.Shared
6667import qualified Text.Pandoc.UTF8 as UTF8
6768import Text.Pandoc.XML (fromEntities)
@@ -1105,10 +1106,11 @@ latexMacro = try $ do
11051106rawTeXBlock :: PandocMonad m => MarkdownParser m (F Blocks)
11061107rawTeXBlock = do
11071108 guardEnabled Ext_raw_tex
1108− result <- (B.rawBlock "latex" . concat <$>
1109− rawLaTeXBlock `sepEndBy1` blankline)
1110− <|> (B.rawBlock "context" . concat <$>
1109+ result <- (B.rawBlock "context" . concat <$>
11111110 rawConTeXtEnvironment `sepEndBy1` blankline)
1111+ <|> (B.rawBlock "latex" . concat <$>
1112+ rawLaTeXBlock `sepEndBy1` blankline)
1113+
11121114 spaces
11131115 return $ return result
11141116
@@ -1553,8 +1555,8 @@ code = try $ do
15531555 Right attr -> B.codeWith attr result
15541556
15551557math :: PandocMonad m => MarkdownParser m (F Inlines)
1556−math = (return . B.displayMath <$> (mathDisplay >>= applyMacros'))
1557− <|> (return . B.math <$> (mathInline >>= applyMacros')) <+?>
1558+math = (return . B.displayMath <$> (mathDisplay >>= applyMacros))
1559+ <|> (return . B.math <$> (mathInline >>= applyMacros)) <+?>
15581560 (guardEnabled Ext_smart *> (return <$> apostrophe)
15591561 <* notFollowedBy (space <|> satisfy isPunctuation))
15601562
@@ -1878,9 +1880,8 @@ rawLaTeXInline' = try $ do
18781880 guardEnabled Ext_raw_tex
18791881 lookAhead (char '\\')
18801882 notFollowedBy' rawConTeXtEnvironment
1881− RawInline _ s <- rawLaTeXInline
1882− return $ return $ B.rawInline "tex" s
1883− -- "tex" because it might be context or latex
1883+ s <- rawLaTeXInline
1884+ return $ return $ B.rawInline "tex" s -- "tex" because it might be context
18841885
18851886rawConTeXtEnvironment :: PandocMonad m => ParserT [Char] st m String
18861887rawConTeXtEnvironment = try $ do
src/Text/Pandoc/Readers/Muse.hs+1−1
@@ -58,7 +58,7 @@ import Text.Pandoc.Definition
5858import Text.Pandoc.Logging
5959import Text.Pandoc.Options
6060import Text.Pandoc.Shared (crFilter)
61−import Text.Pandoc.Parsing hiding (macro, nested)
61+import Text.Pandoc.Parsing hiding (nested)
6262import Text.Pandoc.Readers.HTML (htmlTag)
6363import Text.Pandoc.XML (fromEntities)
6464import System.FilePath (takeExtension)
src/Text/Pandoc/Readers/Org/Inlines.hs+3−2
@@ -826,9 +826,10 @@ maybeRight = either (const Nothing) Just
826826inlineLaTeXCommand :: PandocMonad m => OrgParser m String
827827inlineLaTeXCommand = try $ do
828828 rest <- getInput
829− parsed <- (lift . lift) $ runParserT rawLaTeXInline def "source" rest
829+ st <- getState
830+ parsed <- (lift . lift) $ runParserT rawLaTeXInline st "source" rest
830831 case parsed of
831− Right (RawInline _ cs) -> do
832+ Right cs -> do
832833 -- drop any trailing whitespace, those are not be part of the command as
833834 -- far as org mode is concerned.
834835 let cmdNoSpc = dropWhileEnd isSpace cs
src/Text/Pandoc/Readers/Org/ParserState.hs+10
@@ -33,6 +33,7 @@ module Text.Pandoc.Readers.Org.ParserState
3333 , OrgNoteRecord
3434 , HasReaderOptions (..)
3535 , HasQuoteContext (..)
36+ , HasMacros (..)
3637 , TodoMarker (..)
3738 , TodoSequence
3839 , TodoState (..)
@@ -57,14 +58,17 @@ import Control.Monad.Reader (ReaderT, asks, local)
5758import Data.Default (Default (..))
5859import qualified Data.Map as M
5960import qualified Data.Set as Set
61+import Data.Text (Text)
6062
6163import Text.Pandoc.Builder (Blocks, Inlines)
6264import Text.Pandoc.Definition (Meta (..), nullMeta)
6365import Text.Pandoc.Logging
6466import Text.Pandoc.Options (ReaderOptions (..))
67+import Text.Pandoc.Readers.LaTeX.Types (Macro)
6568import Text.Pandoc.Parsing (Future, HasHeaderMap (..), HasIdentifierList (..),
6669 HasIncludeFiles (..), HasLastStrPosition (..),
6770 HasLogMessages (..), HasQuoteContext (..),
71+ HasMacros (..),
6872 HasReaderOptions (..), ParserContext (..),
6973 QuoteContext (..), SourcePos, askF, asksF, returnF,
7074 runF, trimInlinesF)
@@ -118,6 +122,7 @@ data OrgParserState = OrgParserState
118122 , orgStateParserContext :: ParserContext
119123 , orgStateTodoSequences :: [TodoSequence]
120124 , orgLogMessages :: [LogMessage]
125+ , orgMacros :: M.Map Text Macro
121126 }
122127
123128data OrgParserLocal = OrgParserLocal { orgLocalQuoteContext :: QuoteContext }
@@ -148,6 +153,10 @@ instance HasLogMessages OrgParserState where
148153 addLogMessage msg st = st{ orgLogMessages = msg : orgLogMessages st }
149154 getLogMessages st = reverse $ orgLogMessages st
150155
156+instance HasMacros OrgParserState where
157+ extractMacros st = orgMacros st
158+ updateMacros f st = st{ orgMacros = f (orgMacros st) }
159+
151160instance HasIncludeFiles OrgParserState where
152161 getIncludeFiles = orgStateIncludeFiles
153162 addIncludeFile f st = st { orgStateIncludeFiles = f : orgStateIncludeFiles st }
@@ -178,6 +187,7 @@ defaultOrgParserState = OrgParserState
178187 , orgStateParserContext = NullState
179188 , orgStateTodoSequences = []
180189 , orgLogMessages = []
190+ , orgMacros = M.empty
181191 }
182192
183193optionsToParserState :: ReaderOptions -> OrgParserState
src/Text/Pandoc/Readers/TWiki.hs+1−1
@@ -45,7 +45,7 @@ import qualified Text.Pandoc.Builder as B
4545import Text.Pandoc.Class (PandocMonad(..))
4646import Text.Pandoc.Definition
4747import Text.Pandoc.Options
48−import Text.Pandoc.Parsing hiding (enclosed, macro, nested)
48+import Text.Pandoc.Parsing hiding (enclosed, nested)
4949import Text.Pandoc.Readers.HTML (htmlTag, isCommentTag)
5050import Text.Pandoc.XML (fromEntities)
5151import Text.Pandoc.Shared (crFilter)
src/Text/Pandoc/Readers/Textile.hs+1−1
@@ -573,7 +573,7 @@ rawHtmlInline = B.rawInline "html" . snd <$> htmlTag isInlineTag
573573rawLaTeXInline' :: PandocMonad m => ParserT [Char] ParserState m Inlines
574574rawLaTeXInline' = try $ do
575575 guardEnabled Ext_raw_tex
576− B.singleton <$> rawLaTeXInline
576+ B.rawInline "latex" <$> rawLaTeXInline
577577
578578-- | Textile standard link syntax is "label":target. But we
579579-- can also have ["label":target].
src/Text/Pandoc/Readers/Txt2Tags.hs+1−1
@@ -40,7 +40,7 @@ import Text.Pandoc.Builder (Blocks, Inlines, trimInlines)
4040import qualified Text.Pandoc.Builder as B
4141import Text.Pandoc.Definition
4242import Text.Pandoc.Options
43−import Text.Pandoc.Parsing hiding (macro, space, spaces, uri)
43+import Text.Pandoc.Parsing hiding (space, spaces, uri)
4444import Text.Pandoc.Shared (compactify, compactifyDL, escapeURI, crFilter)
4545import Control.Monad (guard, void, when)
4646import Control.Monad.Reader (Reader, asks, runReader)
test/Tests/Readers/LaTeX.hs+2−1
@@ -58,7 +58,8 @@ tests = [ testGroup "basic"
5858 , "blank lines + space + comments" =:
5959 "% my comment\n\n \n % another\n\nhi" =?> para "hi"
6060 , "comment in paragraph" =:
61− "hi % this is a comment\nthere\n" =?> para "hi there"
61+ "hi % this is a comment\nthere\n" =?>
62+ para ("hi" <> softbreak <> "there")
6263 ]
6364
6465 , testGroup "code blocks"
test/command/1390.mdnew file+20
@@ -0,0 +1,20 @@
1+```
2+% pandoc -f latex -t native
3+\newcommand\foo{+}
4+Testing: $\mu\foo\eta$.
5+^D
6+[Para [Str "Testing:",Space,Math InlineMath "\\mu+\\eta",Str "."]]
7+```
8+
9+<!-- It would be nice to handle this case, but I don't
10+know how:
11+
12+```
13+% pandoc -f latex -t native
14+\newcommand{\vecx}{a + b}
15+$\hat\vecx$
16+^D
17+[Para [Math InlineMath "\\hat{a+b}"]]
18+```
19+-->
20+
test/command/2118.mdnew file+11
@@ -0,0 +1,11 @@
1+```
2+% pandoc -f latex -t native
3+\newcommand{\inclgraph}{\includegraphics[width=0.8\textwidth]}
4+\begin{figure}[ht]
5+ \inclgraph{setminus.png}
6+ \caption{Set subtraction}
7+ \label{fig:setminus}
8+\end{figure}
9+^D
10+[Para [Image ("",[],[("width","80%")]) [Str "Set",Space,Str "subtraction",Span ("",[],[("data-label","fig:setminus")]) []] ("setminus.png","fig:")]]
11+```
test/command/3113.md+1−1
@@ -8,6 +8,6 @@ C&=&D,\\
88E&=&F
99\end{eqnarray}
1010^D
11−[Para [Math DisplayMath "\\begin{aligned}\nA&=&B,\\\\\nC&=&D,\\\\\nE&=&F\\end{aligned}"]]
11+[Para [Math DisplayMath "\\begin{aligned}\nA&=&B,\\\\\nC&=&D,\\\\\n%\\end{eqnarray}\n%\\begin{eqnarray}\nE&=&F\\end{aligned}"]]
1212```
1313
test/command/3236.mdnew file+9
@@ -0,0 +1,9 @@
1+```
2+pandoc -f latex -t native
3+\newcommand{\mycolor}{red}
4+
5+\includegraphics[width=17cm]{\mycolor /header}
6+Magnificent \mycolor{} header.
7+^D
8+[Para [Image ("",[],[("width","17cm")]) [Str "image"] ("red/header",""),SoftBreak,Str "Magnificent",Space,Str "red",Space,Str "header."]]
9+```
test/command/3558.md+6−2
@@ -1,8 +1,12 @@
11```
22% pandoc -t native
3−\startmulti
3+\multi
4+
45hello
6+
57\endmulti
68^D
7−[Para [RawInline (Format "tex") "\\startmulti\n",Str "hello",SoftBreak,RawInline (Format "tex") "\\endmulti"]]
9+[RawBlock (Format "latex") "\\multi"
10+,Para [Str "hello"]
11+,RawBlock (Format "latex") "\\endmulti"]
812```
test/command/3779.mdnew file+28
@@ -0,0 +1,28 @@
1+```
2+% pandoc -f latex -t native
3+\newcommand{\fakeitemize}[1]{
4+ \begin{itemize}
5+ #1
6+ \end{itemize}
7+}
8+\newcommand{\testcmd}[1]{
9+ #1
10+}
11+\fakeitemize{
12+ \item Pandoc is 100\% awesome.
13+}
14+
15+\begin{itemize}
16+ \item Pandoc is 200\% awesome.
17+\end{itemize}
18+
19+\testcmd{
20+ Pandoc is 300\% awesome.
21+}
22+^D
23+[BulletList
24+ [[Para [Str "Pandoc",Space,Str "is",Space,Str "100%",Space,Str "awesome."]]]
25+,BulletList
26+ [[Para [Str "Pandoc",Space,Str "is",Space,Str "200%",Space,Str "awesome."]]]
27+,Para [Str "Pandoc",Space,Str "is",Space,Str "300%",Space,Str "awesome."]]
28+```
test/command/934.mdnew file+12
@@ -0,0 +1,12 @@
1+```
2+% pandoc -f latex -t native
3+\newcommand{\ddb}[2]{
4+ \textit{``#1''}
5+
6+ \textbf{#2}
7+}
8+\ddb{This should be italic and in quotes}{And this is the attribution}
9+^D
10+[Para [Emph [Quoted DoubleQuote [Str "This",Space,Str "should",Space,Str "be",Space,Str "italic",Space,Str "and",Space,Str "in",Space,Str "quotes"]]]
11+,Para [Strong [Str "And",Space,Str "this",Space,Str "is",Space,Str "the",Space,Str "attribution"]]]
12+```
test/command/982.mdnew file+11
@@ -0,0 +1,11 @@
1+```
2+% pandoc -f latex -t native
3+\newcommand{\BEQ}{\begin{equation}}
4+\newcommand{\EEQ}{\end{equation}}
5+
6+\BEQ
7+y=x^2
8+\EEQ
9+^D
10+[Para [Math DisplayMath "y=x^2"]]
11+```
test/latex-reader.latex−1
@@ -4,7 +4,6 @@
44\setlength{\parindent}{0pt}
55\setlength{\parskip}{6pt plus 2pt minus 1pt}
66
7−\newcommand{\textsubscript}[1]{\ensuremath{_{\scriptsize\textrm{#1}}}}
87\usepackage[breaklinks=true,unicode=true]{hyperref}
98\usepackage[normalem]{ulem}
109% avoid problems with \sout in headers with hyperref:
test/latex-reader.native+1−1
@@ -261,7 +261,7 @@ Pandoc (Meta {unMeta = fromList [("author",MetaList [MetaInlines [Str "John",Spa
261261,Header 1 ("latex",[],[]) [Str "LaTeX"]
262262,BulletList
263263 [[Para [Cite [Citation {citationId = "smith.1899", citationPrefix = [], citationSuffix = [Str "22-23"], citationMode = NormalCitation, citationNoteNum = 0, citationHash = 0}] [RawInline (Format "latex") "\\cite[22-23]{smith.1899}"]]]
264− ,[Para [RawInline (Format "latex") "\\doublespacing\n"]]
264+ ,[Para [RawInline (Format "latex") "\\doublespacing"]]
265265 ,[Para [Math InlineMath "2+2=4"]]
266266 ,[Para [Math InlineMath "x \\in y"]]
267267 ,[Para [Math InlineMath "\\alpha \\wedge \\omega"]]
test/markdown-reader-more.native+2−2
@@ -3,7 +3,7 @@ Pandoc (Meta {unMeta = fromList [("author",MetaList [MetaInlines [Str "Author",S
33,Header 2 ("blank-line-before-url-in-link-reference",[],[]) [Str "Blank",Space,Str "line",Space,Str "before",Space,Str "URL",Space,Str "in",Space,Str "link",Space,Str "reference"]
44,Para [Link ("",[],[]) [Str "foo"] ("/url",""),Space,Str "and",Space,Link ("",[],[]) [Str "bar"] ("/url","title")]
55,Header 2 ("raw-context-environments",[],[]) [Str "Raw",Space,Str "ConTeXt",Space,Str "environments"]
6−,Plain [RawInline (Format "tex") "\\placeformula "]
6+,RawBlock (Format "latex") "\\placeformula "
77,RawBlock (Format "context") "\\startformula\n L_{1} = L_{2}\n \\stopformula"
88,RawBlock (Format "context") "\\start[a2]\n\\start[a2]\n\\stop[a2]\n\\stop[a2]"
99,Header 2 ("raw-latex-environments",[],[]) [Str "Raw",Space,Str "LaTeX",Space,Str "environments"]
@@ -56,7 +56,7 @@ Pandoc (Meta {unMeta = fromList [("author",MetaList [MetaInlines [Str "Author",S
5656,OrderedList (3,Example,TwoParens)
5757 [[Plain [Str "Third",Space,Str "example."]]]
5858,Header 2 ("macros",[],[]) [Str "Macros"]
59−,Para [Math InlineMath "{\\langle x,y \\rangle}"]
59+,Para [Math InlineMath "\\langle x,y \\rangle"]
6060,Header 2 ("case-insensitive-references",[],[]) [Str "Case-insensitive",Space,Str "references"]
6161,Para [Link ("",[],[]) [Str "Fum"] ("/fum","")]
6262,Para [Link ("",[],[]) [Str "FUM"] ("/fum","")]