git.iko.soy/typeable/pandoc
on master help sign in
Code cleanup in LaTeX reader.
3e2afa7fiddlosopher
Text/Pandoc/Readers/LaTeX.hs+50−14
@@ -47,6 +47,7 @@ readLaTeX :: ParserState -- ^ Parser state, including options for parser
47readLaTeX = readWith parseLaTeX
47readLaTeX = readWith parseLaTeX
48
48
49-- characters with special meaning
49-- characters with special meaning
50+specialChars :: [Char]
50specialChars = "\\`$%^&_~#{}\n \t|<>'\"-"
51specialChars = "\\`$%^&_~#{}\n \t|<>'\"-"
51
52
52--
53--
@@ -54,21 +55,26 @@ specialChars = "\\`$%^&_~#{}\n \t|<>'\"-"
54--
55--
55
56
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]
60
62
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 '[' ']'
63
66
64-- | True if the string begins with '{'.
67-- | True if the string begins with '{'.
65−isArg ('{':rest) = True
68+isArg :: [Char] -> Bool
69+isArg ('{':_) = True
66−isArg other = False
70+isArg _ = False
67
71
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
70
75
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
73 char '\\'
79 char '\\'
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)
78
84
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
82 spaces
89 spaces
83 return name
90 return name
84
91
92+end :: [Char] -> GenParser Char st [Char]
85end name = try $ do
93end name = try $ do
86 string $ "\\end{" ++ name ++ "}"
94 string $ "\\end{" ++ name ++ "}"
87 spaces
95 spaces
@@ -106,12 +114,14 @@ anyEnvironment = try $ do
106--
114--
107
115
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}")) >>
112 spaces
121 spaces
113
122
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)
117 spaces
127 spaces
@@ -158,14 +168,15 @@ header = try $ do
158 string "section"
168 string "section"
159 optional (char '*')
169 optional (char '*')
160 char '{'
170 char '{'
161− title <- manyTill inline (char '}')
171+ title' <- manyTill inline (char '}')
162 spaces
172 spaces
163− return $ Header (length subs + 1) (normalizeSpaces title)
173+ return $ Header (length subs + 1) (normalizeSpaces title')
164
174
165--
175--
166-- hrule block
176-- hrule block
167--
177--
168
178
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
171
182
@@ -173,8 +184,10 @@ hrule = oneOfStrings [ "\\begin{center}\\rule{3in}{0.4pt}\\end{center}\n\n",
173-- code blocks
184-- code blocks
174--
185--
175
186
187+codeBlock :: GenParser Char st Block
176codeBlock = codeBlock1 <|> codeBlock2
188codeBlock = codeBlock1 <|> codeBlock2
177
189
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
184 spaces
197 spaces
185 return $ CodeBlock ("",[],[]) (stripTrailingNewlines contents)
198 return $ CodeBlock ("",[],[]) (stripTrailingNewlines contents)
186
199
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
203--
217--
204
218
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"
209
224
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
211 start
229 start
212 spaces
230 spaces
213− result <- manyTill anyChar end
231+ result <- manyTill anyChar end'
214 spaces
232 spaces
215 return $ BlockQuote [Para [Math result]]
233 return $ BlockQuote [Para [Math result]]
216
234
@@ -225,9 +243,9 @@ listItem = try $ do
225 spaces
243 spaces
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
288
306
289bibliographic = choice [ maketitle, title, authors, date ]
307bibliographic = choice [ maketitle, title, authors, date ]
290
308
309+maketitle :: GenParser Char st Block
291maketitle = try (string "\\maketitle") >> spaces >> return Null
310maketitle = try (string "\\maketitle") >> spaces >> return Null
292
311
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
299
318
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 '}')
303 spaces
323 spaces
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
308
328
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
319--
340--
320
341
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
333--
355--
334
356
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 "\\")),
352 (do
375 (do
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')
357 spaces
380 spaces
@@ -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
367
390
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)]
381
405
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
384
409
385--
410--
386-- inline
411-- inline
387--
412--
388
413
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
526
552
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
529
556
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
532
560
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
535
564
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
540
570
571+apostrophe :: GenParser Char ParserState Inline
541apostrophe = char '\'' >> return Apostrophe
572apostrophe = char '\'' >> return Apostrophe
542
573
574+quoted :: GenParser Char ParserState Inline
543quoted = doubleQuoted <|> singleQuoted
575quoted = doubleQuoted <|> singleQuoted
544
576
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
547
580
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
550
584
585+singleQuoteStart :: GenParser Char st Char
551singleQuoteStart = char '`'
586singleQuoteStart = char '`'
552
587
553singleQuoteEnd = try $ char '\'' >> notFollowedBy alphaNum
588singleQuoteEnd = try $ char '\'' >> notFollowedBy alphaNum
@@ -572,6 +607,7 @@ lab = try $ do
572
607
573ref = try (string "\\ref{") >> manyTill anyChar (char '}') >>= return . Str
608ref = try (string "\\ref{") >> manyTill anyChar (char '}') >>= return . Str
574
609
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
577
613