@@ -47,6 +47,7 @@ readLaTeX :: ParserState -- ^ Parser state, including options for parser
4747readLaTeX = readWith parseLaTeX
4848
4949-- characters with special meaning
50+specialChars :: [Char]
5051specialChars = "\\`$%^&_~#{}\n \t|<>'\"-"
5152
5253--
@@ -54,21 +55,26 @@ specialChars = "\\`$%^&_~#{}\n \t|<>'\"-"
5455--
5556
5657-- | Returns text between brackets and its matching pair.
58+bracketedText :: Char -> Char -> GenParser Char st [Char]
5759bracketedText openB closeB = do
5860 result <- charsInBalanced' openB closeB
5961 return $ [openB] ++ result ++ [closeB]
6062
6163-- | Returns an option or argument of a LaTeX command.
64+optOrArg :: GenParser Char st [Char]
6265optOrArg = bracketedText '{' '}' <|> bracketedText '[' ']'
6366
6467-- | True if the string begins with '{'.
65−isArg ('{':rest) = True
66−isArg other = False
68+isArg :: [Char] -> Bool
69+isArg ('{':_) = True
70+isArg _ = False
6771
6872-- | Returns list of options and arguments of a LaTeX command.
73+commandArgs :: GenParser Char st [[Char]]
6974commandArgs = many optOrArg
7075
7176-- | Parses LaTeX command, returns (name, star, list of options or arguments).
77+command :: GenParser Char st ([Char], [Char], [[Char]])
7278command = do
7379 char '\\'
7480 name <- many1 letter
@@ -76,12 +82,14 @@ command = do
7682 args <- commandArgs
7783 return (name, star, args)
7884
85+begin :: [Char] -> GenParser Char st [Char]
7986begin name = try $ do
8087 string $ "\\begin{" ++ name ++ "}"
8188 optional commandArgs
8289 spaces
8390 return name
8491
92+end :: [Char] -> GenParser Char st [Char]
8593end name = try $ do
8694 string $ "\\end{" ++ name ++ "}"
8795 spaces
@@ -106,12 +114,14 @@ anyEnvironment = try $ do
106114--
107115
108116-- | Process LaTeX preamble, extracting metadata.
117+processLaTeXPreamble :: GenParser Char ParserState ()
109118processLaTeXPreamble = try $ manyTill
110119 (choice [bibliographic, comment, unknownCommand, nullBlock])
111120 (try (string "\\begin{document}")) >>
112121 spaces
113122
114123-- | Parse LaTeX and return 'Pandoc'.
124+parseLaTeX :: GenParser Char ParserState Pandoc
115125parseLaTeX = do
116126 optional processLaTeXPreamble -- preamble might not be present (fragment)
117127 spaces
@@ -158,14 +168,15 @@ header = try $ do
158168 string "section"
159169 optional (char '*')
160170 char '{'
161− title <- manyTill inline (char '}')
171+ title' <- manyTill inline (char '}')
162172 spaces
163− return $ Header (length subs + 1) (normalizeSpaces title)
173+ return $ Header (length subs + 1) (normalizeSpaces title')
164174
165175--
166176-- hrule block
167177--
168178
179+hrule :: GenParser Char st Block
169180hrule = oneOfStrings [ "\\begin{center}\\rule{3in}{0.4pt}\\end{center}\n\n",
170181 "\\newpage" ] >> spaces >> return HorizontalRule
171182
@@ -173,8 +184,10 @@ hrule = oneOfStrings [ "\\begin{center}\\rule{3in}{0.4pt}\\end{center}\n\n",
173184-- code blocks
174185--
175186
187+codeBlock :: GenParser Char st Block
176188codeBlock = codeBlock1 <|> codeBlock2
177189
190+codeBlock1 :: GenParser Char st Block
178191codeBlock1 = try $ do
179192 string "\\begin{verbatim}" -- don't use begin function because it
180193 -- gobbles whitespace
@@ -184,6 +197,7 @@ codeBlock1 = try $ do
184197 spaces
185198 return $ CodeBlock ("",[],[]) (stripTrailingNewlines contents)
186199
200+codeBlock2 :: GenParser Char st Block
187201codeBlock2 = try $ do
188202 string "\\begin{Verbatim}" -- used by fancyvrb package
189203 optional blanklines
@@ -202,15 +216,19 @@ blockQuote = (environment "quote" <|> environment "quotation") >>~ spaces >>=
202216-- math block
203217--
204218
219+mathBlock :: GenParser Char st Block
205220mathBlock = mathBlockWith (begin "equation") (end "equation") <|>
206221 mathBlockWith (begin "displaymath") (end "displaymath") <|>
207222 mathBlockWith (try $ string "\\[") (try $ string "\\]") <?>
208223 "math block"
209224
210−mathBlockWith start end = try $ do
225+mathBlockWith :: GenParser Char st t
226+ -> GenParser Char st end
227+ -> GenParser Char st Block
228+mathBlockWith start end' = try $ do
211229 start
212230 spaces
213− result <- manyTill anyChar end
231+ result <- manyTill anyChar end'
214232 spaces
215233 return $ BlockQuote [Para [Math result]]
216234
@@ -225,9 +243,9 @@ listItem = try $ do
225243 spaces
226244 state <- getState
227245 let oldParserContext = stateParserContext state
228− updateState (\state -> state {stateParserContext = ListItemState})
246+ updateState (\s -> s {stateParserContext = ListItemState})
229247 blocks <- many block
230− updateState (\state -> state {stateParserContext = oldParserContext})
248+ updateState (\s -> s {stateParserContext = oldParserContext})
231249 opt <- case args of
232250 ([x]) | "[" `isPrefixOf` x && "]" `isSuffixOf` x ->
233251 parseFromString (many inline) $ tail $ init x
@@ -288,6 +306,7 @@ para = many1 inline >>~ spaces >>= return . Para . normalizeSpaces
288306
289307bibliographic = choice [ maketitle, title, authors, date ]
290308
309+maketitle :: GenParser Char st Block
291310maketitle = try (string "\\maketitle") >> spaces >> return Null
292311
293312title = try $ do
@@ -297,15 +316,17 @@ title = try $ do
297316 updateState (\state -> state { stateTitle = tit })
298317 return Null
299318
319+authors :: GenParser Char ParserState Block
300320authors = try $ do
301321 string "\\author{"
302− authors <- manyTill anyChar (char '}')
322+ authors' <- manyTill anyChar (char '}')
303323 spaces
304− let authors' = map removeLeadingTrailingSpace $ lines $
305− substitute "\\\\" "\n" authors
306− updateState (\state -> state { stateAuthors = authors' })
324+ let authors'' = map removeLeadingTrailingSpace $ lines $
325+ substitute "\\\\" "\n" authors'
326+ updateState (\s -> s { stateAuthors = authors'' })
307327 return Null
308328
329+date :: GenParser Char ParserState Block
309330date = try $ do
310331 string "\\date{"
311332 date' <- manyTill anyChar (char '}')
@@ -319,6 +340,7 @@ date = try $ do
319340--
320341
321342-- this forces items to be parsed in different blocks
343+itemBlock :: GenParser Char ParserState Block
322344itemBlock = try $ do
323345 ("item", _, args) <- command
324346 state <- getState
@@ -332,6 +354,7 @@ itemBlock = try $ do
332354-- raw LaTeX
333355--
334356
357+specialEnvironment :: GenParser Char st Block
335358specialEnvironment = do -- these are always parsed as raw
336359 lookAhead (choice (map (\name -> begin name) ["tabular", "figure",
337360 "tabbing", "eqnarry", "picture", "table", "verse", "theorem"]))
@@ -350,8 +373,8 @@ rawLaTeXEnvironment = try $ do
350373 let argStr = concat args
351374 contents <- manyTill (choice [ (many1 (noneOf "\\")),
352375 (do
353− (Para [TeX str]) <- rawLaTeXEnvironment
354− return str),
376+ (Para [TeX s]) <- rawLaTeXEnvironment
377+ return s),
355378 string "\\" ])
356379 (end name')
357380 spaces
@@ -365,6 +388,7 @@ unknownEnvironment = try $ do
365388 else anyEnvironment -- otherwise just the contents
366389 return result
367390
391+unknownCommand :: GenParser Char ParserState Block
368392unknownCommand = try $ do
369393 notFollowedBy' $ choice $ map end ["itemize", "enumerate", "description",
370394 "document"]
@@ -380,12 +404,14 @@ unknownCommand = try $ do
380404 else return $ Plain [Str (joinWithSep " " args)]
381405
382406-- latex comment
407+comment :: GenParser Char st Block
383408comment = try $ char '%' >> manyTill anyChar newline >> spaces >> return Null
384409
385410--
386411-- inline
387412--
388413
414+inline :: GenParser Char ParserState Inline
389415inline = choice [ str
390416 , endline
391417 , whitespace
@@ -524,30 +550,39 @@ code2 = try $ do
524550 result <- manyTill (noneOf "\\\n~$%^&{}") (char '}')
525551 return $ Code result
526552
553+emph :: GenParser Char ParserState Inline
527554emph = try $ oneOfStrings [ "\\emph{", "\\textit{" ] >>
528555 manyTill inline (char '}') >>= return . Emph
529556
557+strikeout :: GenParser Char ParserState Inline
530558strikeout = try $ string "\\sout{" >> manyTill inline (char '}') >>=
531559 return . Strikeout
532560
561+superscript :: GenParser Char ParserState Inline
533562superscript = try $ string "\\textsuperscript{" >>
534563 manyTill inline (char '}') >>= return . Superscript
535564
536565-- note: \textsubscript isn't a standard latex command, but we use
537566-- a defined version in pandoc.
567+subscript :: GenParser Char ParserState Inline
538568subscript = try $ string "\\textsubscript{" >> manyTill inline (char '}') >>=
539569 return . Subscript
540570
571+apostrophe :: GenParser Char ParserState Inline
541572apostrophe = char '\'' >> return Apostrophe
542573
574+quoted :: GenParser Char ParserState Inline
543575quoted = doubleQuoted <|> singleQuoted
544576
577+singleQuoted :: GenParser Char ParserState Inline
545578singleQuoted = enclosed singleQuoteStart singleQuoteEnd inline >>=
546579 return . Quoted SingleQuote . normalizeSpaces
547580
581+doubleQuoted :: GenParser Char ParserState Inline
548582doubleQuoted = enclosed doubleQuoteStart doubleQuoteEnd inline >>=
549583 return . Quoted DoubleQuote . normalizeSpaces
550584
585+singleQuoteStart :: GenParser Char st Char
551586singleQuoteStart = char '`'
552587
553588singleQuoteEnd = try $ char '\'' >> notFollowedBy alphaNum
@@ -572,6 +607,7 @@ lab = try $ do
572607
573608ref = try (string "\\ref{") >> manyTill anyChar (char '}') >>= return . Str
574609
610+strong :: GenParser Char ParserState Inline
575611strong = try (string "\\textbf{") >> manyTill inline (char '}') >>=
576612 return . Strong
577613