@@ -47,6 +47,7 @@ readLaTeX :: ParserState -- ^ Parser state, including options for parser
47readLaTeX = readWith parseLaTeX
47readLaTeX = readWith parseLaTeX
49-- characters with special meaning
49-- characters with special meaning
50+specialChars :: [Char]
50specialChars = "\\`$%^&_~#{}\n \t|<>'\"-"
51specialChars = "\\`$%^&_~#{}\n \t|<>'\"-"
@@ -54,21 +55,26 @@ specialChars = "\\`$%^&_~#{}\n \t|<>'\"-"
56-- | Returns text between brackets and its matching pair.
57-- | Returns text between brackets and its matching pair.
58+bracketedText :: Char -> Char -> GenParser Char st [Char]
57bracketedText openB closeB = do
59bracketedText openB closeB = do
58 result <- charsInBalanced' openB closeB
60 result <- charsInBalanced' openB closeB
59 return $ [openB] ++ result ++ [closeB]
61 return $ [openB] ++ result ++ [closeB]
61-- | Returns an option or argument of a LaTeX command.
63-- | Returns an option or argument of a LaTeX command.
64+optOrArg :: GenParser Char st [Char]
62optOrArg = bracketedText '{' '}' <|> bracketedText '[' ']'
65optOrArg = bracketedText '{' '}' <|> bracketedText '[' ']'
64-- | True if the string begins with '{'.
67-- | True if the string begins with '{'.
65−isArg ('{':rest) = True
68+isArg :: [Char] -> Bool
66−isArg other = False
70+isArg _ = False
68-- | Returns list of options and arguments of a LaTeX command.
72-- | Returns list of options and arguments of a LaTeX command.
73+commandArgs :: GenParser Char st [[Char]]
69commandArgs = many optOrArg
74commandArgs = many optOrArg
71-- | Parses LaTeX command, returns (name, star, list of options or arguments).
76-- | Parses LaTeX command, returns (name, star, list of options or arguments).
77+command :: GenParser Char st ([Char], [Char], [[Char]])
72command = do
78command = do
74 name <- many1 letter
80 name <- many1 letter
@@ -76,12 +82,14 @@ command = do
76 args <- commandArgs
82 args <- commandArgs
77 return (name, star, args)
83 return (name, star, args)
85+begin :: [Char] -> GenParser Char st [Char]
79begin name = try $ do
86begin name = try $ do
80 string $ "\\begin{" ++ name ++ "}"
87 string $ "\\begin{" ++ name ++ "}"
81 optional commandArgs
88 optional commandArgs
83 return name
90 return name
92+end :: [Char] -> GenParser Char st [Char]
85end name = try $ do
93end name = try $ do
86 string $ "\\end{" ++ name ++ "}"
94 string $ "\\end{" ++ name ++ "}"
@@ -106,12 +114,14 @@ anyEnvironment = try $ do
108-- | Process LaTeX preamble, extracting metadata.
116-- | Process LaTeX preamble, extracting metadata.
117+processLaTeXPreamble :: GenParser Char ParserState ()
109processLaTeXPreamble = try $ manyTill
118processLaTeXPreamble = try $ manyTill
110 (choice [bibliographic, comment, unknownCommand, nullBlock])
119 (choice [bibliographic, comment, unknownCommand, nullBlock])
111 (try (string "\\begin{document}")) >>
120 (try (string "\\begin{document}")) >>
114-- | Parse LaTeX and return 'Pandoc'.
123-- | Parse LaTeX and return 'Pandoc'.
124+parseLaTeX :: GenParser Char ParserState Pandoc
115parseLaTeX = do
125parseLaTeX = do
116 optional processLaTeXPreamble -- preamble might not be present (fragment)
126 optional processLaTeXPreamble -- preamble might not be present (fragment)
@@ -158,14 +168,15 @@ header = try $ do
158 string "section"
168 string "section"
159 optional (char '*')
169 optional (char '*')
161− title <- manyTill inline (char '}')
171+ title' <- manyTill inline (char '}')
163− return $ Header (length subs + 1) (normalizeSpaces title)
173+ return $ Header (length subs + 1) (normalizeSpaces title')
166-- hrule block
176-- hrule block
179+hrule :: GenParser Char st Block
169hrule = oneOfStrings [ "\\begin{center}\\rule{3in}{0.4pt}\\end{center}\n\n",
180hrule = oneOfStrings [ "\\begin{center}\\rule{3in}{0.4pt}\\end{center}\n\n",
170 "\\newpage" ] >> spaces >> return HorizontalRule
181 "\\newpage" ] >> spaces >> return HorizontalRule
@@ -173,8 +184,10 @@ hrule = oneOfStrings [ "\\begin{center}\\rule{3in}{0.4pt}\\end{center}\n\n",
173-- code blocks
184-- code blocks
187+codeBlock :: GenParser Char st Block
176codeBlock = codeBlock1 <|> codeBlock2
188codeBlock = codeBlock1 <|> codeBlock2
190+codeBlock1 :: GenParser Char st Block
178codeBlock1 = try $ do
191codeBlock1 = try $ do
179 string "\\begin{verbatim}" -- don't use begin function because it
192 string "\\begin{verbatim}" -- don't use begin function because it
180 -- gobbles whitespace
193 -- gobbles whitespace
@@ -184,6 +197,7 @@ codeBlock1 = try $ do
185 return $ CodeBlock ("",[],[]) (stripTrailingNewlines contents)
198 return $ CodeBlock ("",[],[]) (stripTrailingNewlines contents)
200+codeBlock2 :: GenParser Char st Block
187codeBlock2 = try $ do
201codeBlock2 = try $ do
188 string "\\begin{Verbatim}" -- used by fancyvrb package
202 string "\\begin{Verbatim}" -- used by fancyvrb package
189 optional blanklines
203 optional blanklines
@@ -202,15 +216,19 @@ blockQuote = (environment "quote" <|> environment "quotation") >>~ spaces >>=
202-- math block
216-- math block
219+mathBlock :: GenParser Char st Block
205mathBlock = mathBlockWith (begin "equation") (end "equation") <|>
220mathBlock = mathBlockWith (begin "equation") (end "equation") <|>
206 mathBlockWith (begin "displaymath") (end "displaymath") <|>
221 mathBlockWith (begin "displaymath") (end "displaymath") <|>
207 mathBlockWith (try $ string "\\[") (try $ string "\\]") <?>
222 mathBlockWith (try $ string "\\[") (try $ string "\\]") <?>
208 "math block"
223 "math block"
225+mathBlockWith :: GenParser Char st t
226+ -> GenParser Char st end
227+ -> GenParser Char st Block
210−mathBlockWith start end = try $ do
228+mathBlockWith start end' = try $ do
213− result <- manyTill anyChar end
231+ result <- manyTill anyChar end'
215 return $ BlockQuote [Para [Math result]]
233 return $ BlockQuote [Para [Math result]]
@@ -225,9 +243,9 @@ listItem = try $ do
226 state <- getState
244 state <- getState
227 let oldParserContext = stateParserContext state
245 let oldParserContext = stateParserContext state
228− updateState (\state -> state {stateParserContext = ListItemState})
246+ updateState (\s -> s {stateParserContext = ListItemState})
229 blocks <- many block
247 blocks <- many block
230− updateState (\state -> state {stateParserContext = oldParserContext})
248+ updateState (\s -> s {stateParserContext = oldParserContext})
231 opt <- case args of
249 opt <- case args of
232 ([x]) | "[" `isPrefixOf` x && "]" `isSuffixOf` x ->
250 ([x]) | "[" `isPrefixOf` x && "]" `isSuffixOf` x ->
233 parseFromString (many inline) $ tail $ init x
251 parseFromString (many inline) $ tail $ init x
@@ -288,6 +306,7 @@ para = many1 inline >>~ spaces >>= return . Para . normalizeSpaces
289bibliographic = choice [ maketitle, title, authors, date ]
307bibliographic = choice [ maketitle, title, authors, date ]
309+maketitle :: GenParser Char st Block
291maketitle = try (string "\\maketitle") >> spaces >> return Null
310maketitle = try (string "\\maketitle") >> spaces >> return Null
293title = try $ do
312title = try $ do
@@ -297,15 +316,17 @@ title = try $ do
297 updateState (\state -> state { stateTitle = tit })
316 updateState (\state -> state { stateTitle = tit })
298 return Null
317 return Null
319+authors :: GenParser Char ParserState Block
300authors = try $ do
320authors = try $ do
301 string "\\author{"
321 string "\\author{"
302− authors <- manyTill anyChar (char '}')
322+ authors' <- manyTill anyChar (char '}')
304− let authors' = map removeLeadingTrailingSpace $ lines $
324+ let authors'' = map removeLeadingTrailingSpace $ lines $
305− substitute "\\\\" "\n" authors
325+ substitute "\\\\" "\n" authors'
306− updateState (\state -> state { stateAuthors = authors' })
326+ updateState (\s -> s { stateAuthors = authors'' })
307 return Null
327 return Null
329+date :: GenParser Char ParserState Block
309date = try $ do
330date = try $ do
310 string "\\date{"
331 string "\\date{"
311 date' <- manyTill anyChar (char '}')
332 date' <- manyTill anyChar (char '}')
@@ -319,6 +340,7 @@ date = try $ do
321-- this forces items to be parsed in different blocks
342-- this forces items to be parsed in different blocks
343+itemBlock :: GenParser Char ParserState Block
322itemBlock = try $ do
344itemBlock = try $ do
323 ("item", _, args) <- command
345 ("item", _, args) <- command
324 state <- getState
346 state <- getState
@@ -332,6 +354,7 @@ itemBlock = try $ do
332-- raw LaTeX
354-- raw LaTeX
357+specialEnvironment :: GenParser Char st Block
335specialEnvironment = do -- these are always parsed as raw
358specialEnvironment = do -- these are always parsed as raw
336 lookAhead (choice (map (\name -> begin name) ["tabular", "figure",
359 lookAhead (choice (map (\name -> begin name) ["tabular", "figure",
337 "tabbing", "eqnarry", "picture", "table", "verse", "theorem"]))
360 "tabbing", "eqnarry", "picture", "table", "verse", "theorem"]))
@@ -350,8 +373,8 @@ rawLaTeXEnvironment = try $ do
350 let argStr = concat args
373 let argStr = concat args
351 contents <- manyTill (choice [ (many1 (noneOf "\\")),
374 contents <- manyTill (choice [ (many1 (noneOf "\\")),
353− (Para [TeX str]) <- rawLaTeXEnvironment
376+ (Para [TeX s]) <- rawLaTeXEnvironment
354− return str),
377+ return s),
355 string "\\" ])
378 string "\\" ])
356 (end name')
379 (end name')
@@ -365,6 +388,7 @@ unknownEnvironment = try $ do
365 else anyEnvironment -- otherwise just the contents
388 else anyEnvironment -- otherwise just the contents
366 return result
389 return result
391+unknownCommand :: GenParser Char ParserState Block
368unknownCommand = try $ do
392unknownCommand = try $ do
369 notFollowedBy' $ choice $ map end ["itemize", "enumerate", "description",
393 notFollowedBy' $ choice $ map end ["itemize", "enumerate", "description",
370 "document"]
394 "document"]
@@ -380,12 +404,14 @@ unknownCommand = try $ do
380 else return $ Plain [Str (joinWithSep " " args)]
404 else return $ Plain [Str (joinWithSep " " args)]
382-- latex comment
406-- latex comment
407+comment :: GenParser Char st Block
383comment = try $ char '%' >> manyTill anyChar newline >> spaces >> return Null
408comment = try $ char '%' >> manyTill anyChar newline >> spaces >> return Null
414+inline :: GenParser Char ParserState Inline
389inline = choice [ str
415inline = choice [ str
390 , endline
416 , endline
391 , whitespace
417 , whitespace
@@ -524,30 +550,39 @@ code2 = try $ do
524 result <- manyTill (noneOf "\\\n~$%^&{}") (char '}')
550 result <- manyTill (noneOf "\\\n~$%^&{}") (char '}')
525 return $ Code result
551 return $ Code result
553+emph :: GenParser Char ParserState Inline
527emph = try $ oneOfStrings [ "\\emph{", "\\textit{" ] >>
554emph = try $ oneOfStrings [ "\\emph{", "\\textit{" ] >>
528 manyTill inline (char '}') >>= return . Emph
555 manyTill inline (char '}') >>= return . Emph
557+strikeout :: GenParser Char ParserState Inline
530strikeout = try $ string "\\sout{" >> manyTill inline (char '}') >>=
558strikeout = try $ string "\\sout{" >> manyTill inline (char '}') >>=
531 return . Strikeout
559 return . Strikeout
561+superscript :: GenParser Char ParserState Inline
533superscript = try $ string "\\textsuperscript{" >>
562superscript = try $ string "\\textsuperscript{" >>
534 manyTill inline (char '}') >>= return . Superscript
563 manyTill inline (char '}') >>= return . Superscript
536-- note: \textsubscript isn't a standard latex command, but we use
565-- note: \textsubscript isn't a standard latex command, but we use
537-- a defined version in pandoc.
566-- a defined version in pandoc.
567+subscript :: GenParser Char ParserState Inline
538subscript = try $ string "\\textsubscript{" >> manyTill inline (char '}') >>=
568subscript = try $ string "\\textsubscript{" >> manyTill inline (char '}') >>=
539 return . Subscript
569 return . Subscript
571+apostrophe :: GenParser Char ParserState Inline
541apostrophe = char '\'' >> return Apostrophe
572apostrophe = char '\'' >> return Apostrophe
574+quoted :: GenParser Char ParserState Inline
543quoted = doubleQuoted <|> singleQuoted
575quoted = doubleQuoted <|> singleQuoted
577+singleQuoted :: GenParser Char ParserState Inline
545singleQuoted = enclosed singleQuoteStart singleQuoteEnd inline >>=
578singleQuoted = enclosed singleQuoteStart singleQuoteEnd inline >>=
546 return . Quoted SingleQuote . normalizeSpaces
579 return . Quoted SingleQuote . normalizeSpaces
581+doubleQuoted :: GenParser Char ParserState Inline
548doubleQuoted = enclosed doubleQuoteStart doubleQuoteEnd inline >>=
582doubleQuoted = enclosed doubleQuoteStart doubleQuoteEnd inline >>=
549 return . Quoted DoubleQuote . normalizeSpaces
583 return . Quoted DoubleQuote . normalizeSpaces
585+singleQuoteStart :: GenParser Char st Char
551singleQuoteStart = char '`'
586singleQuoteStart = char '`'
553singleQuoteEnd = try $ char '\'' >> notFollowedBy alphaNum
588singleQuoteEnd = try $ char '\'' >> notFollowedBy alphaNum
@@ -572,6 +607,7 @@ lab = try $ do
573ref = try (string "\\ref{") >> manyTill anyChar (char '}') >>= return . Str
608ref = try (string "\\ref{") >> manyTill anyChar (char '}') >>= return . Str
610+strong :: GenParser Char ParserState Inline
575strong = try (string "\\textbf{") >> manyTill inline (char '}') >>=
611strong = try (string "\\textbf{") >> manyTill inline (char '}') >>=
576 return . Strong
612 return . Strong