git.iko.soy/typeable/pandoc
on master help sign in
Complete rewrite of LaTeX reader.

* The new reader is more robust, accurate, and extensible. It is still quite incomplete, but it should be easier now to add features. * Text.Pandoc.Parsing: Added withRaw combinator. * Markdown reader: do escapedChar before raw latex inline. Otherwise we capture commands like \{. * Fixed latex citation tests for new citeproc. * Handle \include{} commands in latex. This is done in pandoc.hs, not the (pure) latex reader. But the reader exports the needed function, handleIncludes. * Moved err and warn from pandoc.hs to Shared. * Fixed tests - raw tex should sometimes have trailing space. * Updated lhs-test for highlighting-kate changes.

commit75485c2f112cdc2e1f95f871d01cc356510166ae authorJohn MacFarlane <fiddlosopher@gmail.com> dateSun 29 Jan 2012, 23:54 (15 years ago) parent23ca68a
10 files changed+835−1046
src/Tests/Readers/LaTeX.hs+4−4 src/Text/Pandoc/Parsing.hs+20−4 src/Text/Pandoc/Readers/LaTeX.hs+728−969 src/Text/Pandoc/Readers/Markdown.hs+9−12 src/Text/Pandoc/Shared.hs+23−1 src/pandoc.hs+11−16 tests/latex-reader.native+37−37 tests/lhs-test.html+1−1 tests/lhs-test.html+lhs+1−1 tests/markdown-reader-more.native+1−1
src/Tests/Readers/LaTeX.hs+4−4
@@ -87,7 +87,7 @@ natbibCitations = testGroup "natbib"
8787 , "group" =: "\\citetext{\\citealp[see][p.~34--35]{item1}; \\citealp[also][chap. 3]{item3}}"
8888 =?> para (cite [baseCitation{ citationMode = NormalCitation
8989 , citationPrefix = [Str "see"]
90− , citationSuffix = [Str "p.\160\&34",Str "-",Str "35"] }
90+ , citationSuffix = [Str "p.\160\&34\8211\&35"] }
9191 ,baseCitation{ citationMode = NormalCitation
9292 , citationId = "item3"
9393 , citationPrefix = [Str "also"]
@@ -95,7 +95,7 @@ natbibCitations = testGroup "natbib"
9595 ] mempty)
9696 , "suffix and locator" =: "\\citep[pp.~33, 35--37, and nowhere else]{item1}"
9797 =?> para (cite [baseCitation{ citationMode = NormalCitation
98− , citationSuffix = [Str "pp.\160\&33,",Space,Str "35",Str "-",Str "37,",Space,Str "and",Space,Str "nowhere",Space, Str "else"] }] mempty)
98+ , citationSuffix = [Str "pp.\160\&33,",Space,Str "35\8211\&37,",Space,Str "and",Space,Str "nowhere",Space, Str "else"] }] mempty)
9999 , "suffix only" =: "\\citep[and nowhere else]{item1}"
100100 =?> para (cite [baseCitation{ citationMode = NormalCitation
101101 , citationSuffix = toList $ text "and nowhere else" }] mempty)
@@ -134,7 +134,7 @@ biblatexCitations = testGroup "biblatex"
134134 , "group" =: "\\autocites[see][p.~34--35]{item1}[also][chap. 3]{item3}"
135135 =?> para (cite [baseCitation{ citationMode = NormalCitation
136136 , citationPrefix = [Str "see"]
137− , citationSuffix = [Str "p.\160\&34",Str "-",Str "35"] }
137+ , citationSuffix = [Str "p.\160\&34\8211\&35"] }
138138 ,baseCitation{ citationMode = NormalCitation
139139 , citationId = "item3"
140140 , citationPrefix = [Str "also"]
@@ -142,7 +142,7 @@ biblatexCitations = testGroup "biblatex"
142142 ] mempty)
143143 , "suffix and locator" =: "\\autocite[pp.~33, 35--37, and nowhere else]{item1}"
144144 =?> para (cite [baseCitation{ citationMode = NormalCitation
145− , citationSuffix = [Str "pp.\160\&33,",Space,Str "35",Str "-",Str "37,",Space,Str "and",Space,Str "nowhere",Space, Str "else"] }] mempty)
145+ , citationSuffix = [Str "pp.\160\&33,",Space,Str "35\8211\&37,",Space,Str "and",Space,Str "nowhere",Space, Str "else"] }] mempty)
146146 , "suffix only" =: "\\autocite[and nowhere else]{item1}"
147147 =?> para (cite [baseCitation{ citationMode = NormalCitation
148148 , citationSuffix = toList $ text "and nowhere else" }] mempty)
src/Text/Pandoc/Parsing.hs+20−4
@@ -46,6 +46,7 @@ module Text.Pandoc.Parsing ( (>>~),
4646 emailAddress,
4747 uri,
4848 withHorizDisplacement,
49+ withRaw,
4950 nullBlock,
5051 failIfStrict,
5152 failUnlessLHS,
@@ -299,6 +300,23 @@ withHorizDisplacement parser = do
299300 pos2 <- getPosition
300301 return (result, sourceColumn pos2 - sourceColumn pos1)
301302
303+-- | Applies a parser and returns the raw string that was parsed,
304+-- along with the value produced by the parser.
305+withRaw :: GenParser Char st a -> GenParser Char st (a, [Char])
306+withRaw parser = do
307+ pos1 <- getPosition
308+ inp <- getInput
309+ result <- parser
310+ pos2 <- getPosition
311+ let (l1,c1) = (sourceLine pos1, sourceColumn pos1)
312+ let (l2,c2) = (sourceLine pos2, sourceColumn pos2)
313+ let inplines = take ((l2 - l1) + 1) $ lines inp
314+ let raw = case inplines of
315+ [] -> error "raw: inplines is null" -- shouldn't happen
316+ [l] -> take (c2 - c1) l
317+ ls -> unlines (init ls) ++ take (c2 - 1) (last ls)
318+ return (result, raw)
319+
302320-- | Parses a character and returns 'Null' (so that the parser can move on
303321-- if it gets stuck).
304322nullBlock :: GenParser Char st Block
@@ -312,9 +330,7 @@ failIfStrict = do
312330
313331-- | Fail unless we're in literate haskell mode.
314332failUnlessLHS :: GenParser tok ParserState ()
315−failUnlessLHS = do
316− state <- getState
317− if stateLiterateHaskell state then return () else fail "Literate haskell feature"
333+failUnlessLHS = getState >>= guard . stateLiterateHaskell
318334
319335-- | Parses backslash, then applies character parser.
320336escaped :: GenParser Char st Char -- ^ Parser for character to escape
@@ -588,7 +604,7 @@ readWith :: GenParser t ParserState a -- ^ parser
588604 -> a
589605readWith parser state input =
590606 case runParser parser state "source" input of
591− Left err -> error $ "\nError:\n" ++ show err
607+ Left err' -> error $ "\nError:\n" ++ show err'
592608 Right result -> result
593609
594610-- | Parse a string with @parser@ (for testing).
src/Text/Pandoc/Readers/LaTeX.hs+728−969
@@ -1,5 +1,5 @@
11{-
2−Copyright (C) 2006-2010 John MacFarlane <jgm@berkeley.edu>
2+Copyright (C) 2006-2012 John MacFarlane <jgm@berkeley.edu>
33
44This program is free software; you can redistribute it and/or modify
55it under the terms of the GNU General Public License as published by
@@ -18,8 +18,8 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
1818
1919{- |
2020 Module : Text.Pandoc.Readers.LaTeX
21− Copyright : Copyright (C) 2006-2010 John MacFarlane
22− License : GNU GPL, version 2 or above
21+ Copyright : Copyright (C) 2006-2012 John MacFarlane
22+ License : GNU GPL, version 2 or above
2323
2424 Maintainer : John MacFarlane <jgm@berkeley.edu>
2525 Stability : alpha
@@ -27,20 +27,24 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
2727
2828Conversion of LaTeX to 'Pandoc' document.
2929-}
30−module Text.Pandoc.Readers.LaTeX (
31− readLaTeX,
32− rawLaTeXInline,
33− rawLaTeXEnvironment'
30+module Text.Pandoc.Readers.LaTeX ( readLaTeX,
31+ rawLaTeXInline,
32+ rawLaTeXBlock,
33+ handleIncludes
3434 ) where
3535
36−import Text.ParserCombinators.Parsec
36+import Text.ParserCombinators.Parsec hiding ((<|>), space, many, optional)
3737import Text.Pandoc.Definition
3838import Text.Pandoc.Shared
3939import Text.Pandoc.Parsing
40−import Data.Maybe ( fromMaybe )
41−import Data.Char ( chr, toUpper )
42−import Data.List ( intercalate, isPrefixOf, isSuffixOf )
40+import Data.Char ( chr, ord )
4341import Control.Monad
42+import Text.Pandoc.Builder
43+import Data.Char (isLetter)
44+import Control.Applicative
45+import Data.Monoid
46+import System.FilePath (replaceExtension)
47+import qualified Data.Map as M
4448
4549-- | Parse LaTeX from string and return 'Pandoc' document.
4650readLaTeX :: ParserState -- ^ Parser state, including options for parser
@@ -48,1002 +52,757 @@ readLaTeX :: ParserState -- ^ Parser state, including options for parser
4852 -> Pandoc
4953readLaTeX = readWith parseLaTeX
5054
51−-- characters with special meaning
52−specialChars :: [Char]
53−specialChars = "\\`$%^&_~#{}[]\n \t|<>'\"-"
54−
55−--
56−-- utility functions
57−--
58−
59−-- | Returns text between brackets and its matching pair.
60−bracketedText :: Char -> Char -> GenParser Char st [Char]
61−bracketedText openB closeB = do
62− result <- charsInBalanced openB closeB anyChar
63− return $ [openB] ++ result ++ [closeB]
64−
65−-- | Returns an option or argument of a LaTeX command.
66−optOrArg :: GenParser Char st [Char]
67−optOrArg = try $ spaces >> (bracketedText '{' '}' <|> bracketedText '[' ']')
68−
69−-- | True if the string begins with '{'.
70−isArg :: [Char] -> Bool
71−isArg ('{':_) = True
72−isArg _ = False
55+parseLaTeX :: LP Pandoc
56+parseLaTeX = do
57+ bs <- blocks
58+ eof
59+ st <- getState
60+ let title' = stateTitle st
61+ let authors' = stateAuthors st
62+ let date' = stateDate st
63+ return $ Pandoc (Meta title' authors' date') $ toList bs
7364
74−-- | Returns list of options and arguments of a LaTeX command.
75−commandArgs :: GenParser Char st [[Char]]
76−commandArgs = many optOrArg
65+type LP = GenParser Char ParserState
7766
78−-- | Parses LaTeX command, returns (name, star, list of options or arguments).
79−command :: GenParser Char st ([Char], [Char], [[Char]])
80−command = do
67+anyControlSeq :: LP String
68+anyControlSeq = do
8169 char '\\'
82− name <- many1 letter
83− star <- option "" (string "*") -- some commands have starred versions
84− args <- commandArgs
85− return (name, star, args)
86−
87−begin :: [Char] -> GenParser Char st [Char]
88−begin name = try $ do
89− string "\\begin"
90− spaces
91− char '{'
92− string name
93− char '}'
94− optional commandArgs
95− spaces
70+ next <- option '\n' anyChar
71+ name <- case next of
72+ '\n' -> return ""
73+ c | isLetter c -> (c:) <$> (many letter <* optional sp)
74+ | otherwise -> return [c]
9675 return name
9776
98−end :: [Char] -> GenParser Char st [Char]
99−end name = try $ do
100− string "\\end"
101− spaces
102− char '{'
103− string name
104− char '}'
77+controlSeq :: String -> LP String
78+controlSeq name = try $ do
79+ char '\\'
80+ case name of
81+ "" -> mzero
82+ [c] | not (isLetter c) -> string [c]
83+ cs -> string cs <* optional sp
10584 return name
10685
107−-- | Returns a list of block elements containing the contents of an
108−-- environment.
109−environment :: [Char] -> GenParser Char ParserState [Block]
110−environment name = try $ begin name >> spaces >> manyTill block (end name) >>~ spaces
111−
112−anyEnvironment :: GenParser Char ParserState Block
113−anyEnvironment = try $ do
114− string "\\begin"
115− spaces
86+sp :: LP ()
87+sp = skipMany1 $ satisfy (\c -> c == ' ' || c == '\t')
88+ <|> (try $ newline >>~ lookAhead anyChar >>~ notFollowedBy blankline)
89+
90+isLowerHex :: Char -> Bool
91+isLowerHex x = x >= '0' && x <= '9' || x >= 'a' && x <= 'f'
92+
93+tildeEscape :: LP Char
94+tildeEscape = try $ do
95+ string "^^"
96+ c <- satisfy (\x -> x >= '\0' && x <= '\128')
97+ d <- if isLowerHex c
98+ then option "" $ count 1 (satisfy isLowerHex)
99+ else return ""
100+ if null d
101+ then case ord c of
102+ x | x >= 64 && x <= 127 -> return $ chr (x - 64)
103+ | otherwise -> return $ chr (x + 64)
104+ else return $ chr $ read ('0':'x':c:d)
105+
106+comment :: LP ()
107+comment = do
108+ char '%'
109+ skipMany (satisfy (/='\n'))
110+ newline
111+ return ()
112+
113+grouped :: Monoid a => LP a -> LP a
114+grouped parser = try $ char '{' *> (mconcat <$> manyTill parser (char '}'))
115+
116+braced :: LP String
117+braced = char '{' *> (concat <$> manyTill
118+ ( many1 (satisfy (\c -> c /= '\\' && c /= '}' && c /= '{'))
119+ <|> try (string "\\}")
120+ <|> try (string "\\{")
121+ <|> ((\x -> "{" ++ x ++ "}") <$> braced)
122+ <|> count 1 anyChar
123+ ) (char '}'))
124+
125+bracketed :: Monoid a => LP a -> LP a
126+bracketed parser = try $ char '[' *> (mconcat <$> manyTill parser (char ']'))
127+
128+trim :: String -> String
129+trim = removeLeadingTrailingSpace
130+
131+mathDisplay :: LP String -> LP Inlines
132+mathDisplay p = displayMath <$> (try p >>= applyMacros' . trim)
133+
134+mathInline :: LP String -> LP Inlines
135+mathInline p = math <$> (try p >>= applyMacros')
136+
137+double_quote :: LP Inlines
138+double_quote = (doubleQuoted . mconcat) <$>
139+ (try $ string "``" *> manyTill inline (try $ string "''"))
140+
141+single_quote :: LP Inlines
142+single_quote = (singleQuoted . mconcat) <$>
143+ (try $ char '`' *> manyTill inline (try $ char '\'' >> notFollowedBy letter))
144+
145+inline :: LP Inlines
146+inline = (mempty <$ comment)
147+ <|> (space <$ sp)
148+ <|> inlineText
149+ <|> inlineCommand
150+ <|> grouped inline
151+ <|> (char '-' *> option (str "-")
152+ ((char '-') *> option (str "–") (str "—" <$ char '-')))
153+ <|> double_quote
154+ <|> single_quote
155+ <|> (str "’" <$ char '\'')
156+ <|> (str "\160" <$ char '~')
157+ <|> (mathDisplay $ string "$$" *> manyTill anyChar (try $ string "$$"))
158+ <|> (mathInline $ char '$' *> manyTill anyChar (char '$'))
159+ <|> (superscript <$> (char '^' *> tok))
160+ <|> (subscript <$> (char '_' *> tok))
161+ <|> (failUnlessLHS *> char '|' *> doLHSverb)
162+ <|> (str <$> count 1 tildeEscape)
163+ <|> (str <$> string "]")
164+ <|> (str <$> count 1 (satisfy (\c -> c /= '\\' && c /='\n' && c /='}' && c /='{'))) -- eat random leftover characters
165+
166+inlines :: LP Inlines
167+inlines = mconcat <$> many (notFollowedBy (char '}') *> inline)
168+
169+block :: LP Blocks
170+block = (mempty <$ comment)
171+ <|> (mempty <$ ((spaceChar <|> blankline) *> spaces))
172+ <|> environment
173+ <|> mempty <$ macro -- TODO improve macros, make them work everywhere
174+ <|> blockCommand
175+ <|> grouped block
176+ <|> paragraph
177+
178+
179+blocks :: LP Blocks
180+blocks = mconcat <$> many block
181+
182+blockCommand :: LP Blocks
183+blockCommand = try $ do
184+ name <- anyControlSeq
185+ star <- option "" (string "*" <* optional sp)
186+ let name' = name ++ star
187+ case M.lookup name' blockCommands of
188+ Just p -> p
189+ Nothing -> case M.lookup name blockCommands of
190+ Just p -> p
191+ Nothing -> mzero
192+
193+inBrackets :: Inlines -> Inlines
194+inBrackets x = (str "[") <> x <> (str "]")
195+
196+blockCommands :: M.Map String (LP Blocks)
197+blockCommands = M.fromList
198+ [ ("par", pure mempty)
199+ , ("title", mempty <$ (tok >>= addTitle))
200+ , ("subtitle", mempty <$ (tok >>= addSubtitle))
201+ , ("author", mempty <$ authors)
202+ , ("date", mempty <$ (tok >>= addDate))
203+ , ("maketitle", pure mempty)
204+ -- \ignore{} is used conventionally in literate haskell for definitions
205+ -- that are to be processed by the compiler but not printed.
206+ , ("ignore", mempty <$ tok)
207+ , ("hyperdef", mempty <$ (tok *> tok))
208+ , ("chapter", updateState (\s -> s{ stateHasChapters = True }) *> section 0)
209+ , ("section", section 1)
210+ , ("subsection", section 2)
211+ , ("subsubsection", section 3)
212+ , ("paragraph", section 4)
213+ , ("subparagraph", section 5)
214+ , ("opening", (para . trimInlines) <$> tok)
215+ , ("closing", (para . trimInlines) <$> tok)
216+ , ("rule", optional opt *> tok *> tok *> pure horizontalRule)
217+ , ("begin", mzero) -- these are here so they won't be interpreted as inline
218+ , ("end", mzero)
219+ , ("item", loose_item)
220+ , ("documentclass", optional opt *> braced *> preamble)
221+ -- should be parsed by macro, but we need this
222+ -- here so these aren't parsed as inline
223+ , ("newcommand", mempty <$ (tok *> optional opt *> tok))
224+ , ("renewcommand", mempty <$ (tok *> optional opt *> tok))
225+ , ("newenvironment", mempty <$ (tok *> tok *> tok))
226+ , ("renewenvironment", mempty <$ (tok *> tok *> tok))
227+ , ("special", pure mempty)
228+ , ("pdfannot", pure mempty)
229+ , ("pdfstringdef", pure mempty)
230+ , ("index", pure mempty)
231+ , ("bibliography", pure mempty)
232+ ]
233+
234+addTitle :: Inlines -> LP ()
235+addTitle tit = updateState (\s -> s{ stateTitle = toList tit })
236+
237+addSubtitle :: Inlines -> LP ()
238+addSubtitle tit = updateState (\s -> s{ stateTitle = stateTitle s ++
239+ toList (str ":" <> linebreak <> tit) })
240+
241+authors :: LP ()
242+authors = try $ do
116243 char '{'
117− name <- many letter
118− star <- option "" (string "*") -- some environments have starred variants
119− char '}'
120− optional commandArgs
121− spaces
122− contents <- manyTill block (end (name ++ star))
123− spaces
124− return $ BlockQuote contents
125−
126−--
127−-- parsing documents
128−--
244+ let oneAuthor = mconcat <$> many1 (notFollowedBy' (controlSeq "and") >> inline)
245+ auths <- sepBy oneAuthor (controlSeq "and")
246+ updateState (\s -> s { stateAuthors = map (normalizeSpaces . toList) auths })
247+
248+addDate :: Inlines -> LP ()
249+addDate dat = updateState (\s -> s{ stateDate = toList dat })
250+
251+section :: Int -> LP Blocks
252+section lvl = do
253+ hasChapters <- stateHasChapters `fmap` getState
254+ let lvl' = if hasChapters then lvl + 1 else lvl
255+ optional sp
256+ optional opt
257+ contents <- grouped inline
258+ return $ header lvl' contents
259+
260+inlineCommand :: LP Inlines
261+inlineCommand = try $ do
262+ name <- anyControlSeq
263+ guard $ not $ isBlockCommand name
264+ parseRaw <- stateParseRaw `fmap` getState
265+ star <- option "" (string "*")
266+ let name' = name ++ star
267+ case M.lookup name' inlineCommands of
268+ Just p -> p
269+ Nothing -> case M.lookup name inlineCommands of
270+ Just p -> p
271+ Nothing
272+ | parseRaw ->
273+ (rawInline "latex" . (('\\':name') ++)) <$>
274+ (withRaw (optional opt *> many braced)
275+ >>= applyMacros' . snd)
276+ | otherwise -> return mempty
277+
278+isBlockCommand :: String -> Bool
279+isBlockCommand s = maybe False (const True) $ M.lookup s blockCommands
280+
281+inlineCommands :: M.Map String (LP Inlines)
282+inlineCommands = M.fromList
283+ [ ("emph", emph <$> tok)
284+ , ("textit", emph <$> tok)
285+ , ("textsc", smallcaps <$> tok)
286+ , ("sout", strikeout <$> tok)
287+ , ("textsuperscript", superscript <$> tok)
288+ , ("textsubscript", subscript <$> tok)
289+ , ("textbackslash", lit "\\")
290+ , ("backslash", lit "\\")
291+ , ("textbf", strong <$> tok)
292+ , ("ldots", lit "…")
293+ , ("dots", lit "…")
294+ , ("mdots", lit "…")
295+ , ("sim", lit "~")
296+ , ("label", inBrackets <$> tok)
297+ , ("ref", inBrackets <$> tok)
298+ , ("(", mathInline $ manyTill anyChar (try $ string "\\)"))
299+ , ("[", mathDisplay $ manyTill anyChar (try $ string "\\]"))
300+ , ("ensuremath", mathInline $ braced)
301+ , ("$", lit "$")
302+ , ("%", lit "%")
303+ , ("&", lit "&")
304+ , ("#", lit "#")
305+ , ("_", lit "_")
306+ , ("{", lit "{")
307+ , ("}", lit "}")
308+ -- old TeX commands
309+ , ("em", emph <$> inlines)
310+ , ("it", emph <$> inlines)
311+ , ("sl", emph <$> inlines)
312+ , ("bf", strong <$> inlines)
313+ , ("rm", inlines)
314+ , ("itshape", emph <$> inlines)
315+ , ("slshape", emph <$> inlines)
316+ , ("scshape", smallcaps <$> inlines)
317+ , ("bfseries", strong <$> inlines)
318+ , ("/", pure mempty) -- italic correction
319+ , ("cc", lit "ç")
320+ , ("cC", lit "Ç")
321+ , ("aa", lit "å")
322+ , ("AA", lit "Å")
323+ , ("ss", lit "ß")
324+ , ("o", lit "ø")
325+ , ("O", lit "Ø")
326+ , ("L", lit "Ł")
327+ , ("l", lit "ł")
328+ , ("ae", lit "æ")
329+ , ("AE", lit "Æ")
330+ , ("pounds", lit "£")
331+ , ("euro", lit "€")
332+ , ("copyright", lit "©")
333+ , ("sect", lit "§")
334+ , ("`", option (str "`") $ try $ tok >>= accent grave)
335+ , ("'", option (str "'") $ try $ tok >>= accent acute)
336+ , ("^", option (str "^") $ try $ tok >>= accent hat)
337+ , ("~", option (str "~") $ try $ tok >>= accent circ)
338+ , ("\"", option (str "\"") $ try $ tok >>= accent umlaut)
339+ , ("i", lit "i")
340+ , ("\\", linebreak <$ optional (bracketed inline *> optional sp))
341+ , (",", pure mempty)
342+ , ("@", pure mempty)
343+ , (" ", lit "\160")
344+ , ("bar", lit "|")
345+ , ("textless", lit "<")
346+ , ("textgreater", lit ">")
347+ , ("thanks", (note . mconcat) <$> (char '{' *> manyTill block (char '}')))
348+ , ("footnote", (note . mconcat) <$> (char '{' *> manyTill block (char '}')))
349+ , ("verb", doverb)
350+ , ("lstinline", doverb)
351+ , ("texttt", (code . stringify . toList) <$> tok)
352+ , ("url", (unescapeURL <$> braced) >>= \url ->
353+ pure (link url "" (codeWith ("",["url"],[]) url)))
354+ , ("href", (unescapeURL <$> braced <* optional sp) >>= \url ->
355+ tok >>= \lab ->
356+ pure (link url "" lab))
357+ , ("includegraphics", optional opt *> (unescapeURL <$> braced) >>=
358+ (\src -> pure (image src "" (str "image"))))
359+ , ("cite", citation NormalCitation False)
360+ , ("citep", citation NormalCitation False)
361+ , ("citep*", citation NormalCitation False)
362+ , ("citeal", citation NormalCitation False)
363+ , ("citealp", citation NormalCitation False)
364+ , ("citealp*", citation NormalCitation False)
365+ , ("autocite", citation NormalCitation False)
366+ , ("footcite", citation NormalCitation False)
367+ , ("parencite", citation NormalCitation False)
368+ , ("supercite", citation NormalCitation False)
369+ , ("footcitetext", citation NormalCitation False)
370+ , ("citeyearpar", citation SuppressAuthor False)
371+ , ("citeyear", citation SuppressAuthor False)
372+ , ("autocite*", citation SuppressAuthor False)
373+ , ("cite*", citation SuppressAuthor False)
374+ , ("parencite*", citation SuppressAuthor False)
375+ , ("textcite", citation AuthorInText False)
376+ , ("citet", citation AuthorInText False)
377+ , ("citet*", citation AuthorInText False)
378+ , ("citealt", citation AuthorInText False)
379+ , ("citealt*", citation AuthorInText False)
380+ , ("textcites", citation AuthorInText True)
381+ , ("cites", citation NormalCitation True)
382+ , ("autocites", citation NormalCitation True)
383+ , ("footcites", citation NormalCitation True)
384+ , ("parencites", citation NormalCitation True)
385+ , ("supercites", citation NormalCitation True)
386+ , ("footcitetexts", citation NormalCitation True)
387+ , ("Autocite", citation NormalCitation False)
388+ , ("Footcite", citation NormalCitation False)
389+ , ("Parencite", citation NormalCitation False)
390+ , ("Supercite", citation NormalCitation False)
391+ , ("Footcitetext", citation NormalCitation False)
392+ , ("Citeyearpar", citation SuppressAuthor False)
393+ , ("Citeyear", citation SuppressAuthor False)
394+ , ("Autocite*", citation SuppressAuthor False)
395+ , ("Cite*", citation SuppressAuthor False)
396+ , ("Parencite*", citation SuppressAuthor False)
397+ , ("Textcite", citation AuthorInText False)
398+ , ("Textcites", citation AuthorInText True)
399+ , ("Cites", citation NormalCitation True)
400+ , ("Autocites", citation NormalCitation True)
401+ , ("Footcites", citation NormalCitation True)
402+ , ("Parencites", citation NormalCitation True)
403+ , ("Supercites", citation NormalCitation True)
404+ , ("Footcitetexts", citation NormalCitation True)
405+ , ("citetext", complexNatbibCitation NormalCitation)
406+ , ("citeauthor", (try (tok *> optional sp *> controlSeq "citetext") *>
407+ complexNatbibCitation AuthorInText)
408+ <|> citation AuthorInText False)
409+ ]
410+
411+unescapeURL :: String -> String
412+unescapeURL ('\\':x:xs) | isEscapable x = x:unescapeURL xs
413+ where isEscapable '%' = True
414+ isEscapable '#' = True
415+ isEscapable _ = False
416+unescapeURL (x:xs) = x:unescapeURL xs
417+unescapeURL [] = ""
418+
419+doverb :: LP Inlines
420+doverb = do
421+ marker <- anyChar
422+ code <$> manyTill (satisfy (/='\n')) (char marker)
423+
424+doLHSverb :: LP Inlines
425+doLHSverb = codeWith ("",["haskell"],[]) <$> manyTill (satisfy (/='\n')) (char '|')
426+
427+lit :: String -> LP Inlines
428+lit = pure . str
429+
430+accent :: (Char -> Char) -> Inlines -> LP Inlines
431+accent f ils =
432+ case toList ils of
433+ (Str (x:xs) : ys) -> return $ fromList $ (Str (f x : xs) : ys)
434+ [] -> mzero
435+ _ -> return ils
436+
437+grave :: Char -> Char
438+grave 'A' = 'À'
439+grave 'E' = 'È'
440+grave 'I' = 'Ì'
441+grave 'O' = 'Ò'
442+grave 'U' = 'Ù'
443+grave 'a' = 'à'
444+grave 'e' = 'è'
445+grave 'i' = 'ì'
446+grave 'o' = 'ò'
447+grave 'u' = 'ù'
448+grave c = c
449+
450+acute :: Char -> Char
451+acute 'A' = 'Á'
452+acute 'E' = 'É'
453+acute 'I' = 'Í'
454+acute 'O' = 'Ó'
455+acute 'U' = 'Ú'
456+acute 'a' = 'á'
457+acute 'e' = 'é'
458+acute 'i' = 'í'
459+acute 'o' = 'ó'
460+acute 'u' = 'ú'
461+acute c = c
462+
463+hat :: Char -> Char
464+hat 'A' = 'Â'
465+hat 'E' = 'Ê'
466+hat 'I' = 'Î'
467+hat 'O' = 'Ô'
468+hat 'U' = 'Û'
469+hat 'a' = 'ã'
470+hat 'e' = 'ê'
471+hat 'i' = 'î'
472+hat 'o' = 'ô'
473+hat 'u' = 'û'
474+hat c = c
475+
476+circ :: Char -> Char
477+circ 'A' = 'Ã'
478+circ 'O' = 'Õ'
479+circ 'o' = 'õ'
480+circ 'N' = 'Ñ'
481+circ 'n' = 'ñ'
482+circ c = c
483+
484+umlaut :: Char -> Char
485+umlaut 'A' = 'Ä'
486+umlaut 'E' = 'Ë'
487+umlaut 'I' = 'Ï'
488+umlaut 'O' = 'Ö'
489+umlaut 'U' = 'Ü'
490+umlaut 'a' = 'ä'
491+umlaut 'e' = 'ë'
492+umlaut 'i' = 'ï'
493+umlaut 'o' = 'ö'
494+umlaut 'u' = 'ü'
495+umlaut c = c
496+
497+tok :: LP Inlines
498+tok = try $ grouped inline <|> inlineCommand <|> str <$> (count 1 $ inlineChar)
499+
500+opt :: LP Inlines
501+opt = bracketed inline <* optional sp
502+
503+inlineText :: LP Inlines
504+inlineText = str <$> many1 inlineChar
505+
506+inlineChar :: LP Char
507+inlineChar = satisfy $ \c ->
508+ not (c == '\\' || c == '$' || c == '%' || c == '^' || c == '_' ||
509+ c == '&' || c == '~' || c == '#' || c == '{' || c == '}' ||
510+ c == '^' || c == '\'' || c == '`' || c == '-' || c == ']' ||
511+ c == ' ' || c == '\t' || c == '\n' )
512+
513+environment :: LP Blocks
514+environment = do
515+ controlSeq "begin"
516+ name <- braced
517+ parseRaw <- stateParseRaw `fmap` getState
518+ let addBegin x = "\\begin{" ++ name ++ "}" ++ x
519+ case M.lookup name environments of
520+ Just p -> p
521+ Nothing -> if parseRaw
522+ then (rawBlock "latex" . addBegin) <$>
523+ (withRaw (env name blocks) >>= applyMacros' . snd)
524+ else env name blocks
525+
526+-- | Replace "include" commands with file contents.
527+handleIncludes :: String -> IO String
528+handleIncludes [] = return []
529+handleIncludes ('\\':xs) =
530+ case runParser include defaultParserState "input" ('\\':xs) of
531+ Right (f, rest) -> do ys <- catch (readFile (replaceExtension f ".tex"))
532+ (\e -> warn
533+ ("could not open included file `" ++
534+ f ++ "': " ++ show e) >> return "")
535+ (ys ++) `fmap` handleIncludes rest
536+ _ -> case runParser verbatimEnv defaultParserState "input" ('\\':xs) of
537+ Right (r, rest) -> (r ++) `fmap` handleIncludes rest
538+ _ -> ('\\':) `fmap` handleIncludes xs
539+handleIncludes (x:xs) = (x:) `fmap` handleIncludes xs
540+
541+include :: LP (FilePath, String)
542+include = do
543+ controlSeq "include"
544+ f <- braced
545+ rest <- getInput
546+ return (f, rest)
547+
548+verbatimEnv :: LP (String, String)
549+verbatimEnv = do
550+ (_,r) <- withRaw $ do
551+ controlSeq "begin"
552+ name <- braced
553+ guard $ name == "verbatim" || name == "Verbatim" ||
554+ name == "lstlisting"
555+ verbEnv name
556+ rest <- getInput
557+ return (r,rest)
129558
130−-- | Process LaTeX preamble, extracting metadata.
131−processLaTeXPreamble :: GenParser Char ParserState ()
132−processLaTeXPreamble = do
133− try $ string "\\documentclass"
134− skipMany $ bibliographic <|> macro <|> commentBlock <|> skipChar
559+-- | Parse any LaTeX environment and return a string containing
560+-- the whole literal environment as raw TeX.
561+rawLaTeXBlock :: GenParser Char ParserState String
562+rawLaTeXBlock =
563+ (rawLaTeXEnvironment <|> (snd <$> withRaw blockCommand)) >>= applyMacros'
135564
136−-- | Parse LaTeX and return 'Pandoc'.
137−parseLaTeX :: GenParser Char ParserState Pandoc
138−parseLaTeX = do
139− spaces
140− skipMany $ comment >> spaces
141− blocks <- try (processLaTeXPreamble >> environment "document")
142− <|> (many block >>~ (spaces >> eof))
143− state <- getState
144− let blocks' = filter (/= Null) blocks
145− let title' = stateTitle state
146− let authors' = stateAuthors state
147− let date' = stateDate state
148− return $ Pandoc (Meta title' authors' date') blocks'
149−
150−--
151−-- parsing blocks
152−--
153−
154−parseBlocks :: GenParser Char ParserState [Block]
155−parseBlocks = spaces >> many block
156−
157−block :: GenParser Char ParserState Block
158−block = choice [ hrule
159− , codeBlock
160− , header
161− , list
162− , blockQuote
163− , simpleTable
164− , commentBlock
165− , macro
166− , bibliographic
167− , para
168− , itemBlock
169− , unknownEnvironment
170− , ignore
171− , unknownCommand
172− ] <?> "block"
173−
174−--
175−-- header blocks
176−--
177−
178−header :: GenParser Char ParserState Block
179−header = section <|> chapter
180−
181−chapter :: GenParser Char ParserState Block
182−chapter = try $ do
183− string "\\chapter"
184− result <- headerWithLevel 1
185− updateState $ \s -> s{ stateHasChapters = True }
186− return result
187−
188−section :: GenParser Char ParserState Block
189−section = try $ do
190− char '\\'
191− subs <- many (try (string "sub"))
192− base <- try (string "section" >> return 1) <|> (string "paragraph" >> return 4)
193− st <- getState
194− let lev = if stateHasChapters st
195− then length subs + base + 1
196− else length subs + base
197− headerWithLevel lev
565+rawLaTeXEnvironment :: GenParser Char ParserState String
566+rawLaTeXEnvironment = try $ do
567+ controlSeq "begin"
568+ name <- braced
569+ let addBegin x = "\\begin{" ++ name ++ "}" ++ x
570+ addBegin <$> (withRaw (env name blocks) >>= applyMacros' . snd)
198571
199−headerWithLevel :: Int -> GenParser Char ParserState Block
200−headerWithLevel lev = try $ do
201− spaces
202− optional (char '*')
203− spaces
204− optional $ bracketedText '[' ']' -- alt title
572+rawLaTeXInline :: GenParser Char ParserState Inline
573+rawLaTeXInline = do
574+ (res, raw) <- withRaw inlineCommand
575+ if res == mempty
576+ then return (Str "")
577+ else RawInline "latex" <$> (applyMacros' raw)
578+
579+environments :: M.Map String (LP Blocks)
580+environments = M.fromList
581+ [ ("document", env "document" blocks)
582+ , ("letter", env "letter" blocks)
583+ , ("center", env "center" blocks)
584+ , ("tabular", env "tabular" simpTable)
585+ , ("quote", blockQuote <$> env "quote" blocks)
586+ , ("quotation", blockQuote <$> env "quotation" blocks)
587+ , ("itemize", bulletList <$> listenv "itemize" (many item))
588+ , ("description", definitionList <$> listenv "description" (many descItem))
589+ , ("enumerate", ordered_list)
590+ , ("code", failUnlessLHS *>
591+ (codeBlockWith ("",["sourceCode","literate","haskell"],[]) <$> verbEnv "code"))
592+ , ("verbatim", codeBlock <$> (verbEnv "verbatim"))
593+ , ("Verbatim", codeBlock <$> (verbEnv "Verbatim"))
594+ , ("lstlisting", codeBlock <$> (verbEnv "listlisting"))
595+ , ("displaymath", mathEnv Nothing "displaymath")
596+ , ("equation", mathEnv Nothing "equation")
597+ , ("equation*", mathEnv Nothing "equation*")
598+ , ("gather", mathEnv (Just "gathered") "gather")
599+ , ("gather*", mathEnv (Just "gathered") "gather*")
600+ , ("multiline", mathEnv (Just "gathered") "multiline")
601+ , ("multiline*", mathEnv (Just "gathered") "multiline*")
602+ , ("eqnarray", mathEnv (Just "aligned*") "eqnarray")
603+ , ("eqnarray*", mathEnv (Just "aligned*") "eqnarray*")
604+ , ("align", mathEnv (Just "aligned*") "align")
605+ , ("align*", mathEnv (Just "aligned*") "align*")
606+ , ("alignat", mathEnv (Just "aligned*") "alignat")
607+ , ("alignat*", mathEnv (Just "aligned*") "alignat*")
608+ ]
609+
610+item :: LP Blocks
611+item = blocks *> controlSeq "item" *> optional opt *> blocks
612+
613+loose_item :: LP Blocks
614+loose_item = do
615+ ctx <- stateParserContext `fmap` getState
616+ if ctx == ListItemState
617+ then mzero
618+ else return mempty
619+
620+descItem :: LP (Inlines, [Blocks])
621+descItem = do
622+ blocks -- skip blocks before item
623+ controlSeq "item"
624+ optional sp
625+ ils <- opt
626+ bs <- blocks
627+ return (ils, [bs])
628+
629+env :: String -> LP a -> LP a
630+env name p = p <* (controlSeq "end" *> braced >>= guard . (== name))
631+
632+listenv :: String -> LP a -> LP a
633+listenv name p = try $ do
634+ oldCtx <- stateParserContext `fmap` getState
635+ updateState $ \st -> st{ stateParserContext = ListItemState }
636+ res <- env name p
637+ updateState $ \st -> st{ stateParserContext = oldCtx }
638+ return res
639+
640+mathEnv :: Maybe String -> String -> LP Blocks
641+mathEnv innerEnv name = para <$> mathDisplay (inner <$> verbEnv name)
642+ where inner x = case innerEnv of
643+ Nothing -> x
644+ Just y -> "\\begin{" ++ y ++ "}\n" ++ x ++
645+ "\\end{" ++ y ++ "}"
646+
647+verbEnv :: String -> LP String
648+verbEnv name = do
649+ optional opt
650+ optional blankline
651+ let endEnv = try $ controlSeq "end" *> braced >>= guard . (== name)
652+ res <- manyTill anyChar endEnv
653+ return $ stripTrailingNewlines res
654+
655+ordered_list :: LP Blocks
656+ordered_list = do
657+ optional sp
658+ (_, style, delim) <- option (1, DefaultStyle, DefaultDelim) $
659+ try $ char '[' *> anyOrderedListMarker <* char ']'
205660 spaces
206− char '{'
207− title' <- manyTill inline (char '}')
661+ optional $ try $ controlSeq "setlength" *> grouped (controlSeq "itemindent") *> braced
208662 spaces
209− return $ Header lev (normalizeSpaces title')
663+ start <- option 1 $ try $ do controlSeq "setcounter"
664+ grouped (string "enum" *> many1 (oneOf "iv"))
665+ optional sp
666+ num <- grouped (many1 digit)
667+ spaces
668+ return $ (read num + 1 :: Int)
669+ bs <- listenv "enumerate" (many item)
670+ return $ orderedListWith (start, style, delim) bs
671+
672+paragraph :: LP Blocks
673+paragraph = do
674+ x <- mconcat <$> many1 inline
675+ if x == mempty
676+ then return mempty
677+ else return $ para $ trimInlines x
678+
679+preamble :: LP Blocks
680+preamble = mempty <$> manyTill preambleBlock beginDoc
681+ where beginDoc = lookAhead $ controlSeq "begin" *> string "{document}"
682+ preambleBlock = (mempty <$ comment)
683+ <|> (mempty <$ sp)
684+ <|> (mempty <$ blanklines)
685+ <|> (mempty <$ macro)
686+ <|> blockCommand
687+ <|> (mempty <$ anyControlSeq)
688+ <|> (mempty <$ braced)
689+ <|> (mempty <$ anyChar)
690+
691+-------
692+
693+-- citations
694+
695+addPrefix :: Inlines -> [Citation] -> [Citation]
696+addPrefix p (k:ks) = k {citationPrefix = toList p ++ citationPrefix k} : ks
697+addPrefix _ _ = []
210698
211−--
212−-- hrule block
213−--
699+addSuffix :: Inlines -> [Citation] -> [Citation]
700+addSuffix s ks@(_:_) =
701+ let k = last ks
702+ in init ks ++ [k {citationSuffix = citationSuffix k ++ toList s}]
703+addSuffix _ _ = []
704+
705+simpleCiteArgs :: LP [Citation]
706+simpleCiteArgs = try $ do
707+ first <- optionMaybe opt
708+ second <- optionMaybe opt
709+ char '{'
710+ keys <- manyTill citationLabel (char '}')
711+ let (pre, suf) = case (first , second ) of
712+ (Just s , Nothing) -> (mempty, s )
713+ (Just s , Just t ) -> (s , t )
714+ _ -> (mempty, mempty)
715+ conv k = Citation { citationId = k
716+ , citationPrefix = []
717+ , citationSuffix = []
718+ , citationMode = NormalCitation
719+ , citationHash = 0
720+ , citationNoteNum = 0
721+ }
722+ return $ addPrefix pre $ addSuffix suf $ map conv keys
214723
215−hrule :: GenParser Char st Block
216−hrule = oneOfStrings [ "\\begin{center}\\rule{3in}{0.4pt}\\end{center}\n\n",
217− "\\newpage" ] >> spaces >> return HorizontalRule
724+citationLabel :: LP String
725+citationLabel = trim <$>
726+ (many1 (satisfy $ \c -> c /=',' && c /='}') <* optional (char ',') <* optional sp)
218727
219−-- tables
728+cites :: CitationMode -> Bool -> LP [Citation]
729+cites mode multi = try $ do
730+ cits <- if multi
731+ then many1 simpleCiteArgs
732+ else count 1 simpleCiteArgs
733+ let (c:cs) = concat cits
734+ return $ case mode of
735+ AuthorInText -> c {citationMode = mode} : cs
736+ _ -> map (\a -> a {citationMode = mode}) (c:cs)
220737
221−simpleTable :: GenParser Char ParserState Block
222−simpleTable = try $ do
223− string "\\begin"
224− spaces
225− string "{tabular}"
226− spaces
227− aligns <- parseAligns
228− let cols = length aligns
229− optional hline
230− header' <- option [] $ parseTableHeader cols
231− rows <- many (parseTableRow cols >>~ optional hline)
232− spaces
233− end "tabular"
234− spaces
235− let header'' = if null header'
236− then replicate cols []
237− else header'
238− return $ Table [] aligns (replicate cols 0) header'' rows
738+citation :: CitationMode -> Bool -> LP Inlines
739+citation mode multi = (flip cite mempty) <$> cites mode multi
740+
741+complexNatbibCitation :: CitationMode -> LP Inlines
742+complexNatbibCitation mode = try $ do
743+ let ils = (trimInlines . mconcat) <$>
744+ many (notFollowedBy (oneOf "\\};") >> inline)
745+ let parseOne = try $ do
746+ skipSpaces
747+ pref <- ils
748+ cit' <- inline -- expect a citation
749+ let citlist = toList cit'
750+ cits' <- case citlist of
751+ [Cite cs _] -> return cs
752+ _ -> mzero
753+ suff <- ils
754+ skipSpaces
755+ optional $ char ';'
756+ return $ addPrefix pref $ addSuffix suff $ cits'
757+ (c:cits) <- grouped parseOne
758+ return $ cite (c{ citationMode = mode }:cits) mempty
239759
240−hline :: GenParser Char st ()
241−hline = try $ spaces >> string "\\hline" >> return ()
760+-- tables
242761
243−parseAligns :: GenParser Char ParserState [Alignment]
762+parseAligns :: LP [Alignment]
244763parseAligns = try $ do
245764 char '{'
246765 optional $ char '|'
247766 let cAlign = char 'c' >> return AlignCenter
248767 let lAlign = char 'l' >> return AlignLeft
249768 let rAlign = char 'r' >> return AlignRight
250− let alignChar = cAlign <|> lAlign <|> rAlign
769+ let alignChar = optional sp *> (cAlign <|> lAlign <|> rAlign)
251770 aligns' <- sepEndBy alignChar (optional $ char '|')
771+ spaces
252772 char '}'
253773 spaces
254774 return aligns'
255775
256−parseTableHeader :: Int -- ^ number of columns
257− -> GenParser Char ParserState [TableCell]
258−parseTableHeader cols = try $ do
259− cells' <- parseTableRow cols
260− hline
261− return cells'
776+hline :: LP ()
777+hline = () <$ (try $ spaces >> controlSeq "hline")
262778
263779parseTableRow :: Int -- ^ number of columns
264− -> GenParser Char ParserState [TableCell]
780+ -> LP [Blocks]
265781parseTableRow cols = try $ do
266− let tableCellInline = notFollowedBy (char '&' <|>
267− (try $ char '\\' >> char '\\')) >> inline
268− cells' <- sepBy (spaces >> liftM ((:[]) . Plain . normalizeSpaces)
269− (many tableCellInline)) (char '&')
782+ let amp = try $ spaces *> string "&"
783+ let tableCellInline = notFollowedBy (amp <|> controlSeq "\\") >> inline
784+ cells' <- sepBy (spaces *> ((plain . trimInlines . mconcat) <$>
785+ many tableCellInline)) amp
270786 guard $ length cells' == cols
271787 spaces
272− (try $ string "\\\\" >> spaces) <|>
273− (lookAhead (end "tabular") >> return ())
788+ try $ controlSeq "\\" <|> lookAhead (try $ controlSeq "end" >> string "{tabular}")
274789 return cells'
275790
276−--
277−-- code blocks
278−--
279−
280−codeBlock :: GenParser Char ParserState Block
281−codeBlock = codeBlockWith "verbatim" <|> codeBlockWith "Verbatim" <|> codeBlockWith "lstlisting" <|> lhsCodeBlock
282−-- Note: Verbatim is from fancyvrb.
283−
284−codeBlockWith :: String -> GenParser Char st Block
285−codeBlockWith env = try $ do
286− string "\\begin"
287− spaces -- don't use begin function because it
288− string $ "{" ++ env ++ "}" -- gobbles whitespace; we want to gobble
289− optional blanklines -- blank lines, but not leading space
290− contents <- manyTill anyChar (try (string $ "\\end{" ++ env ++ "}"))
291− spaces
292− let classes = if env == "code" then ["haskell"] else []
293− return $ CodeBlock ("",classes,[]) (stripTrailingNewlines contents)
294−
295−lhsCodeBlock :: GenParser Char ParserState Block
296−lhsCodeBlock = do
297− failUnlessLHS
298− (CodeBlock (_,_,_) cont) <- codeBlockWith "code"
299− return $ CodeBlock ("", ["sourceCode","literate","haskell"], []) cont
300−
301−--
302−-- block quotes
303−--
304−
305−blockQuote :: GenParser Char ParserState Block
306−blockQuote = (environment "quote" <|> environment "quotation") >>~ spaces >>=
307− return . BlockQuote
308−
309−--
310−-- list blocks
311−--
312−
313−list :: GenParser Char ParserState Block
314−list = bulletList <|> orderedList <|> definitionList <?> "list"
315−
316−listItem :: GenParser Char ParserState ([Inline], [Block])
317−listItem = try $ do
318− ("item", _, args) <- command
319− spaces
320− state <- getState
321− let oldParserContext = stateParserContext state
322− updateState (\s -> s {stateParserContext = ListItemState})
323− blocks <- many block
324− updateState (\s -> s {stateParserContext = oldParserContext})
325− opt <- case args of
326− ([x]) | "[" `isPrefixOf` x && "]" `isSuffixOf` x ->
327− parseFromString (many inline) $ tail $ init x
328− _ -> return []
329− return (opt, blocks)
330−
331−orderedList :: GenParser Char ParserState Block
332−orderedList = try $ do
333− string "\\begin"
334− spaces
335− string "{enumerate}"
336− spaces
337− (_, style, delim) <- option (1, DefaultStyle, DefaultDelim) $
338− try $ do failIfStrict
339− char '['
340− res <- anyOrderedListMarker
341− char ']'
342− return res
343− spaces
344− option "" $ try $ do string "\\setlength{\\itemindent}"
345− char '{'
346− manyTill anyChar (char '}')
347− spaces
348− start <- option 1 $ try $ do failIfStrict
349− string "\\setcounter{enum"
350− many1 (oneOf "iv")
351− string "}{"
352− num <- many1 digit
353− char '}'
354− spaces
355− return $ (read num) + 1
356− items <- many listItem
357− end "enumerate"
358− spaces
359− return $ OrderedList (start, style, delim) $ map snd items
360−
361−bulletList :: GenParser Char ParserState Block
362−bulletList = try $ do
363− begin "itemize"
364− items <- many listItem
365− end "itemize"
366− spaces
367− return (BulletList $ map snd items)
368−
369−definitionList :: GenParser Char ParserState Block
370−definitionList = try $ do
371− begin "description"
372− items <- many listItem
373− end "description"
374− spaces
375− return $ DefinitionList $ map (\(t,d) -> (t,[d])) items
376−
377−--
378−-- paragraph block
379−--
380−
381−para :: GenParser Char ParserState Block
382−para = do
383− res <- many1 inline
384− spaces
385− return $ if null (filter (`notElem` [Str "", Space]) res)
386− then Null
387− else Para $ normalizeSpaces res
388−
389−--
390−-- title authors date
391−--
392−
393−bibliographic :: GenParser Char ParserState Block
394−bibliographic = choice [ maketitle, title, subtitle, authors, date ]
395−
396−maketitle :: GenParser Char st Block
397−maketitle = try (string "\\maketitle") >> spaces >> return Null
398−
399−title :: GenParser Char ParserState Block
400−title = try $ do
401− string "\\title{"
402− tit <- manyTill inline (char '}')
403− spaces
404− updateState (\state -> state { stateTitle = tit })
405− return Null
406−
407−subtitle :: GenParser Char ParserState Block
408−subtitle = try $ do
409− string "\\subtitle{"
410− tit <- manyTill inline (char '}')
411− spaces
412− updateState (\state -> state { stateTitle = stateTitle state ++
413− Str ":" : LineBreak : tit })
414− return Null
415−
416−authors :: GenParser Char ParserState Block
417−authors = try $ do
418− string "\\author{"
419− let andsep = try $ string "\\and" >> notFollowedBy letter >>
420− spaces >> return '&'
421− raw <- sepBy (many $ notFollowedBy (char '}' <|> andsep) >> inline) andsep
422− let authors' = map normalizeSpaces raw
423− char '}'
424− spaces
425− updateState (\s -> s { stateAuthors = authors' })
426− return Null
427−
428−date :: GenParser Char ParserState Block
429−date = try $ do
430− string "\\date{"
431− date' <- manyTill inline (char '}')
432− spaces
433− updateState (\state -> state { stateDate = normalizeSpaces date' })
434− return Null
435−
436−--
437−-- item block
438−-- for use in unknown environments that aren't being parsed as raw latex
439−--
440−
441−-- this forces items to be parsed in different blocks
442−itemBlock :: GenParser Char ParserState Block
443−itemBlock = try $ do
444− ("item", _, args) <- command
445− state <- getState
446− if stateParserContext state == ListItemState
447− then fail "item should be handled by list block"
448− else if null args
449− then return Null
450− else return $ Plain [Str (stripFirstAndLast (head args))]
451−
452−--
453−-- raw LaTeX
454−--
455−
456−-- | Parse any LaTeX environment and return a Para block containing
457−-- the whole literal environment as raw TeX.
458−rawLaTeXEnvironment :: GenParser Char st Block
459−rawLaTeXEnvironment = do
460− contents <- rawLaTeXEnvironment'
461− spaces
462− return $ RawBlock "latex" contents
463−
464−-- | Parse any LaTeX environment and return a string containing
465−-- the whole literal environment as raw TeX.
466−rawLaTeXEnvironment' :: GenParser Char st String
467−rawLaTeXEnvironment' = try $ do
468− string "\\begin"
469− spaces
470− char '{'
471− name <- many1 letter
472− star <- option "" (string "*") -- for starred variants
473− let name' = name ++ star
474− char '}'
475− args <- option [] commandArgs
476− let argStr = concat args
477− contents <- manyTill (choice [ (many1 (noneOf "\\")),
478− rawLaTeXEnvironment',
479− string "\\" ])
480− (end name')
481− return $ "\\begin{" ++ name' ++ "}" ++ argStr ++
482− concat contents ++ "\\end{" ++ name' ++ "}"
483−
484−unknownEnvironment :: GenParser Char ParserState Block
485−unknownEnvironment = try $ do
486− state <- getState
487− result <- if stateParseRaw state -- check whether we should include raw TeX
488− then rawLaTeXEnvironment -- if so, get whole raw environment
489− else anyEnvironment -- otherwise just the contents
490− return result
491−
492−-- \ignore{} is used conventionally in literate haskell for definitions
493−-- that are to be processed by the compiler but not printed.
494−ignore :: GenParser Char ParserState Block
495−ignore = try $ do
496− ("ignore", _, _) <- command
497− spaces
498− return Null
499−
500−demacro :: (String, String, [String]) -> GenParser Char ParserState Inline
501−demacro (n,st,args) = try $ do
502− let raw = "\\" ++ n ++ st ++ concat args
503− s' <- applyMacros' raw
504− if raw == s'
505− then return $ RawInline "latex" raw
506− else do
507− inp <- getInput
508− setInput $ s' ++ inp
509− return $ Str ""
510−
511−unknownCommand :: GenParser Char ParserState Block
512−unknownCommand = try $ do
513− spaces
514− notFollowedBy' $ oneOfStrings ["\\begin","\\end","\\item"] >>
515− notFollowedBy letter
516− state <- getState
517− when (stateParserContext state == ListItemState) $
518− notFollowedBy' (string "\\item")
519− if stateParseRaw state
520− then command >>= demacro >>= return . Plain . (:[])
521− else do
522− (name, _, args) <- command
523− spaces
524− unless (name `elem` commandsToIgnore) $ do
525− -- put arguments back in input to be parsed
526− inp <- getInput
527− setInput $ intercalate " " args ++ inp
528− return Null
529−
530−commandsToIgnore :: [String]
531−commandsToIgnore = ["special","pdfannot","pdfstringdef", "index","bibliography"]
532−
533−skipChar :: GenParser Char ParserState Block
534−skipChar = do
535− satisfy (/='\\') <|>
536− (notFollowedBy' (try $
537− string "\\begin" >> spaces >> string "{document}") >>
538− anyChar)
539− spaces
540− return Null
541−
542−commentBlock :: GenParser Char st Block
543−commentBlock = many1 (comment >> spaces) >> return Null
544−
545−--
546−-- inline
547−--
548−
549−inline :: GenParser Char ParserState Inline
550−inline = choice [ str
551− , endline
552− , whitespace
553− , quoted
554− , apostrophe
555− , strong
556− , math
557− , ellipses
558− , emDash
559− , enDash
560− , hyphen
561− , emph
562− , strikeout
563− , superscript
564− , subscript
565− , code
566− , url
567− , link
568− , image
569− , footnote
570− , linebreak
571− , accentedChar
572− , nonbreakingSpace
573− , cite
574− , specialChar
575− , ensureMath
576− , rawLaTeXInline'
577− , escapedChar
578− , emptyGroup
579− , unescapedChar
580− , comment
581− ] <?> "inline"
582−
583−
584−-- latex comment
585−comment :: GenParser Char st Inline
586−comment = try $ char '%' >> manyTill anyChar newline >> spaces >> return (Str "")
587−
588−accentedChar :: GenParser Char st Inline
589−accentedChar = normalAccentedChar <|> specialAccentedChar
590−
591−normalAccentedChar :: GenParser Char st Inline
592−normalAccentedChar = try $ do
593− char '\\'
594− accent <- oneOf "'`^\"~"
595− character <- (try $ char '{' >> letter >>~ char '}') <|> letter
596− let table = fromMaybe [] $ lookup character accentTable
597− let result = case lookup accent table of
598− Just num -> chr num
599− Nothing -> '?'
600− return $ Str [result]
601−
602−-- an association list of letters and association list of accents
603−-- and decimal character numbers.
604−accentTable :: [(Char, [(Char, Int)])]
605−accentTable =
606− [ ('A', [('`', 192), ('\'', 193), ('^', 194), ('~', 195), ('"', 196)]),
607− ('E', [('`', 200), ('\'', 201), ('^', 202), ('"', 203)]),
608− ('I', [('`', 204), ('\'', 205), ('^', 206), ('"', 207)]),
609− ('N', [('~', 209)]),
610− ('O', [('`', 210), ('\'', 211), ('^', 212), ('~', 213), ('"', 214)]),
611− ('U', [('`', 217), ('\'', 218), ('^', 219), ('"', 220)]),
612− ('a', [('`', 224), ('\'', 225), ('^', 227), ('"', 228)]),
613− ('e', [('`', 232), ('\'', 233), ('^', 234), ('"', 235)]),
614− ('i', [('`', 236), ('\'', 237), ('^', 238), ('"', 239)]),
615− ('n', [('~', 241)]),
616− ('o', [('`', 242), ('\'', 243), ('^', 244), ('~', 245), ('"', 246)]),
617− ('u', [('`', 249), ('\'', 250), ('^', 251), ('"', 252)]) ]
618−
619−specialAccentedChar :: GenParser Char st Inline
620−specialAccentedChar = choice [ ccedil, aring, iuml, szlig, aelig, lslash,
621− oslash, pound, euro, copyright, sect ]
622−
623−ccedil :: GenParser Char st Inline
624−ccedil = try $ do
625− char '\\'
626− letter' <- oneOfStrings ["cc", "cC"]
627− notFollowedBy letter
628− let num = if letter' == "cc" then 231 else 199
629− return $ Str [chr num]
630−
631−aring :: GenParser Char st Inline
632−aring = try $ do
633− char '\\'
634− letter' <- oneOfStrings ["aa", "AA"]
635− notFollowedBy letter
636− let num = if letter' == "aa" then 229 else 197
637− return $ Str [chr num]
638−
639−iuml :: GenParser Char st Inline
640−iuml = try (string "\\\"") >> oneOfStrings ["\\i", "{\\i}"] >>
641− return (Str [chr 239])
642−
643−szlig :: GenParser Char st Inline
644−szlig = try (string "\\ss") >> notFollowedBy letter >> return (Str [chr 223])
645−
646−oslash :: GenParser Char st Inline
647−oslash = try $ do
648− char '\\'
649− letter' <- choice [char 'o', char 'O']
650− notFollowedBy letter
651− let num = if letter' == 'o' then 248 else 216
652− return $ Str [chr num]
653−
654−lslash :: GenParser Char st Inline
655−lslash = try $ do
656− cmd <- oneOfStrings ["{\\L}","{\\l}"]
657− <|> (oneOfStrings ["\\L ","\\l "] >>~ notFollowedBy letter)
658− return $ if 'l' `elem` cmd
659− then Str "\x142"
660− else Str "\x141"
661−
662−aelig :: GenParser Char st Inline
663−aelig = try $ do
664− char '\\'
665− letter' <- oneOfStrings ["ae", "AE"]
666− notFollowedBy letter
667− let num = if letter' == "ae" then 230 else 198
668− return $ Str [chr num]
669−
670−pound :: GenParser Char st Inline
671−pound = try (string "\\pounds" >> notFollowedBy letter) >> return (Str [chr 163])
672−
673−euro :: GenParser Char st Inline
674−euro = try (string "\\euro" >> notFollowedBy letter) >> return (Str [chr 8364])
675−
676−copyright :: GenParser Char st Inline
677−copyright = try (string "\\copyright" >> notFollowedBy letter) >> return (Str [chr 169])
678−
679−sect :: GenParser Char st Inline
680−sect = try (string "\\S" >> notFollowedBy letter) >> return (Str [chr 167])
681−
682−escapedChar :: GenParser Char st Inline
683−escapedChar = do
684− result <- escaped (oneOf specialChars)
685− return $ if result == '\n' then Str " " else Str [result]
791+parseTableHeader :: Int -- ^ number of columns
792+ -> LP [Blocks]
793+parseTableHeader cols = try $ parseTableRow cols <* hline
686794
687−emptyGroup :: GenParser Char st Inline
688−emptyGroup = try $ do
689− char '{'
795+simpTable :: LP Blocks
796+simpTable = try $ do
690797 spaces
691− char '}'
692− return $ Str ""
693−
694−-- nonescaped special characters
695−unescapedChar :: GenParser Char st Inline
696−unescapedChar = oneOf "`$^&_#{}[]|<>" >>= return . (\c -> Str [c])
697−
698−specialChar :: GenParser Char st Inline
699−specialChar = choice [ spacer, interwordSpace, sentenceEnd,
700− backslash, tilde, caret,
701− bar, lt, gt, doubleQuote ]
702−
703−spacer :: GenParser Char st Inline
704−spacer = try (string "\\,") >> return (Str "")
705−
706−sentenceEnd :: GenParser Char st Inline
707−sentenceEnd = try (string "\\@") >> return (Str "")
708−
709−interwordSpace :: GenParser Char st Inline
710−interwordSpace = try (string "\\ ") >> return (Str "\160")
711−
712−backslash :: GenParser Char st Inline
713−backslash = try (string "\\textbackslash") >> optional (try $ string "{}") >> return (Str "\\")
714−
715−tilde :: GenParser Char st Inline
716−tilde = try (string "\\ensuremath{\\sim}") >> return (Str "~")
717−
718−caret :: GenParser Char st Inline
719−caret = try (string "\\^{}") >> return (Str "^")
720−
721−bar :: GenParser Char st Inline
722−bar = try (string "\\textbar") >> optional (try $ string "{}") >> return (Str "\\")
723−
724−lt :: GenParser Char st Inline
725−lt = try (string "\\textless") >> optional (try $ string "{}") >> return (Str "<")
726−
727−gt :: GenParser Char st Inline
728−gt = try (string "\\textgreater") >> optional (try $ string "{}") >> return (Str ">")
729−
730−doubleQuote :: GenParser Char st Inline
731−doubleQuote = char '"' >> return (Str "\"")
732−
733−code :: GenParser Char ParserState Inline
734−code = code1 <|> code2 <|> code3 <|> lhsInlineCode
735−
736−code1 :: GenParser Char st Inline
737−code1 = try $ do
738− string "\\verb"
739− marker <- anyChar
740− result <- manyTill anyChar (char marker)
741− return $ Code nullAttr $ removeLeadingTrailingSpace result
742−
743−code2 :: GenParser Char st Inline
744−code2 = try $ do
745− string "\\texttt{"
746− result <- manyTill (noneOf "\\\n~$%^&{}") (char '}')
747− return $ Code nullAttr result
748−
749−code3 :: GenParser Char st Inline
750−code3 = try $ do
751− string "\\lstinline"
752− marker <- anyChar
753− result <- manyTill anyChar (char marker)
754− return $ Code nullAttr $ removeLeadingTrailingSpace result
755−
756−lhsInlineCode :: GenParser Char ParserState Inline
757−lhsInlineCode = try $ do
758− failUnlessLHS
759− char '|'
760− result <- manyTill (noneOf "|\n") (char '|')
761− return $ Code ("",["haskell"],[]) result
762−
763−emph :: GenParser Char ParserState Inline
764−emph = try $ oneOfStrings [ "\\emph{", "\\textit{" ] >>
765− manyTill inline (char '}') >>= return . Emph
766−
767−strikeout :: GenParser Char ParserState Inline
768−strikeout = try $ string "\\sout{" >> manyTill inline (char '}') >>=
769− return . Strikeout
770−
771−superscript :: GenParser Char ParserState Inline
772−superscript = try $ string "\\textsuperscript{" >>
773− manyTill inline (char '}') >>= return . Superscript
774−
775−-- note: \textsubscript isn't a standard latex command, but we use
776−-- a defined version in pandoc.
777−subscript :: GenParser Char ParserState Inline
778−subscript = try $ string "\\textsubscript{" >> manyTill inline (char '}') >>=
779− return . Subscript
780−
781−apostrophe :: GenParser Char ParserState Inline
782−apostrophe = char '\'' >> return (Str "\x2019")
783−
784−quoted :: GenParser Char ParserState Inline
785−quoted = doubleQuoted <|> singleQuoted
786−
787−singleQuoted :: GenParser Char ParserState Inline
788−singleQuoted = enclosed singleQuoteStart singleQuoteEnd inline >>=
789− return . Quoted SingleQuote . normalizeSpaces
790−
791−doubleQuoted :: GenParser Char ParserState Inline
792−doubleQuoted = enclosed doubleQuoteStart doubleQuoteEnd inline >>=
793− return . Quoted DoubleQuote . normalizeSpaces
794−
795−singleQuoteStart :: GenParser Char st Char
796−singleQuoteStart = char '`'
797−
798−singleQuoteEnd :: GenParser Char st ()
799−singleQuoteEnd = try $ char '\'' >> notFollowedBy alphaNum
800−
801−doubleQuoteStart :: CharParser st String
802−doubleQuoteStart = string "``"
803−
804−doubleQuoteEnd :: CharParser st String
805−doubleQuoteEnd = try $ string "''"
806−
807−ellipses :: GenParser Char st Inline
808−ellipses = try $ do
809− char '\\'
810− optional $ char 'l'
811− string "dots"
812− optional $ try $ string "{}"
813− return (Str "…")
814−
815−enDash :: GenParser Char st Inline
816−enDash = try (string "--") >> return (Str "-")
817−
818−emDash :: GenParser Char st Inline
819−emDash = try (string "---") >> return (Str "—")
820−
821−hyphen :: GenParser Char st Inline
822−hyphen = char '-' >> return (Str "-")
823−
824−strong :: GenParser Char ParserState Inline
825−strong = try (string "\\textbf{") >> manyTill inline (char '}') >>=
826− return . Strong
827−
828−whitespace :: GenParser Char st Inline
829−whitespace = many1 (oneOf " \t") >> return Space
830−
831−nonbreakingSpace :: GenParser Char st Inline
832−nonbreakingSpace = char '~' >> return (Str "\160")
833−
834−-- hard line break
835−linebreak :: GenParser Char st Inline
836−linebreak = try $ do
837− string "\\\\"
838− optional $ bracketedText '[' ']' -- e.g. \\[10pt]
798+ aligns <- parseAligns
799+ let cols = length aligns
800+ optional hline
801+ header' <- option [] $ parseTableHeader cols
802+ rows <- many (parseTableRow cols <* optional hline)
839803 spaces
840− return LineBreak
841−
842−str :: GenParser Char st Inline
843−str = many1 (noneOf specialChars) >>= return . Str
844−
845−-- endline internal to paragraph
846−endline :: GenParser Char st Inline
847−endline = try $ newline >> notFollowedBy blankline >> return Space
848−
849−-- math
850−math :: GenParser Char ParserState Inline
851−math = (math3 >>= applyMacros' >>= return . Math DisplayMath)
852− <|> (math1 >>= applyMacros' >>= return . Math InlineMath)
853− <|> (math2 >>= applyMacros' >>= return . Math InlineMath)
854− <|> (math4 >>= applyMacros' >>= return . Math DisplayMath)
855− <|> (math5 >>= applyMacros' >>= return . Math DisplayMath)
856− <|> (math6 >>= applyMacros' >>= return . Math DisplayMath)
857− <?> "math"
858−
859−math1 :: GenParser Char st String
860−math1 = try $ char '$' >> manyTill anyChar (char '$')
861−
862−math2 :: GenParser Char st String
863−math2 = try $ string "\\(" >> manyTill anyChar (try $ string "\\)")
864−
865−math3 :: GenParser Char st String
866−math3 = try $ char '$' >> math1 >>~ char '$'
867−
868−math4 :: GenParser Char st String
869−math4 = try $ do
870− name <- begin "displaymath" <|> begin "equation" <|> begin "equation*" <|>
871− begin "gather" <|> begin "gather*" <|> begin "gathered" <|>
872− begin "multline" <|> begin "multline*"
873− manyTill anyChar (end name)
874−
875−math5 :: GenParser Char st String
876−math5 = try $ (string "\\[") >> spaces >> manyTill anyChar (try $ string "\\]")
877−
878−math6 :: GenParser Char st String
879−math6 = try $ do
880− name <- begin "eqnarray" <|> begin "eqnarray*" <|> begin "align" <|>
881− begin "align*" <|> begin "alignat" <|> begin "alignat*" <|>
882− begin "split" <|> begin "aligned" <|> begin "alignedat"
883− res <- manyTill anyChar (end name)
884− return $ filter (/= '&') res -- remove alignment codes
885−
886−ensureMath :: GenParser Char st Inline
887−ensureMath = try $ do
888− (n, _, args) <- command
889− guard $ n == "ensuremath" && not (null args)
890− return $ Math InlineMath $ tail $ init $ head args
891−
892−--
893−-- links and images
894−--
895−
896−url :: GenParser Char ParserState Inline
897−url = try $ do
898− string "\\url"
899− url' <- charsInBalanced '{' '}' anyChar
900− return $ Link [Code ("",["url"],[]) url'] (escapeURI url', "")
901−
902−link :: GenParser Char ParserState Inline
903−link = try $ do
904− string "\\href{"
905− url' <- manyTill anyChar (char '}')
906− char '{'
907− label' <- manyTill inline (char '}')
908− return $ Link (normalizeSpaces label') (escapeURI url', "")
909−
910−image :: GenParser Char ParserState Inline
911−image = try $ do
912− ("includegraphics", _, args) <- command
913− let args' = filter isArg args -- filter out options
914− let (src,tit) = case args' of
915− [] -> ("", "")
916− (x:_) -> (stripFirstAndLast x, "")
917− return $ Image [Str "image"] (escapeURI src, tit)
918−
919−footnote :: GenParser Char ParserState Inline
920−footnote = try $ do
921− (name, _, (contents:[])) <- command
922− if ((name == "footnote") || (name == "thanks"))
923− then string ""
924− else fail "not a footnote or thanks command"
925− let contents' = stripFirstAndLast contents
926− -- parse the extracted block, which may contain various block elements:
927− rest <- getInput
928− setInput $ contents'
929− blocks <- parseBlocks
930− setInput rest
931− return $ Note blocks
932−
933−-- | citations
934−cite :: GenParser Char ParserState Inline
935−cite = simpleCite <|> complexNatbibCites
936−
937−simpleCiteArgs :: GenParser Char ParserState [Citation]
938−simpleCiteArgs = try $ do
939− first <- optionMaybe $ (char '[') >> manyTill inline (char ']')
940− second <- optionMaybe $ (char '[') >> manyTill inline (char ']')
941− char '{'
942− keys <- many1Till citationLabel (char '}')
943− let (pre, suf) = case (first , second ) of
944− (Just s , Nothing) -> ([], s )
945− (Just s , Just t ) -> (s , t )
946− _ -> ([], [])
947− conv k = Citation { citationId = k
948− , citationPrefix = []
949− , citationSuffix = []
950− , citationMode = NormalCitation
951− , citationHash = 0
952− , citationNoteNum = 0
953− }
954− return $ addPrefix pre $ addSuffix suf $ map conv keys
955−
956−
957−simpleCite :: GenParser Char ParserState Inline
958−simpleCite = try $ do
959− char '\\'
960− let biblatex = [a ++ "cite" | a <- ["auto", "foot", "paren", "super", ""]]
961− ++ ["footcitetext"]
962− normal = ["cite" ++ a ++ b | a <- ["al", ""], b <- ["p", "p*", ""]]
963− ++ biblatex
964− supress = ["citeyearpar", "citeyear", "autocite*", "cite*", "parencite*"]
965− intext = ["textcite"] ++ ["cite" ++ a ++ b | a <- ["al", ""], b <- ["t", "t*"]]
966− mintext = ["textcites"]
967− mnormal = map (++ "s") biblatex
968− cmdend = notFollowedBy (letter <|> char '*')
969− capit [] = []
970− capit (x:xs) = toUpper x : xs
971− addUpper xs = xs ++ map capit xs
972− toparser l t = try $ oneOfStrings (addUpper l) >> cmdend >> return t
973− (mode, multi) <- toparser normal (NormalCitation, False)
974− <|> toparser supress (SuppressAuthor, False)
975− <|> toparser intext (AuthorInText , False)
976− <|> toparser mnormal (NormalCitation, True )
977− <|> toparser mintext (AuthorInText , True )
978− cits <- if multi then
979− many1 simpleCiteArgs
980− else
981− simpleCiteArgs >>= \c -> return [c]
982− let (c:cs) = concat cits
983− cits' = case mode of
984− AuthorInText -> c {citationMode = mode} : cs
985− _ -> map (\a -> a {citationMode = mode}) (c:cs)
986− return $ Cite cits' []
987−
988−complexNatbibCites :: GenParser Char ParserState Inline
989−complexNatbibCites = complexNatbibTextual <|> complexNatbibParenthetical
990−
991−complexNatbibTextual :: GenParser Char ParserState Inline
992−complexNatbibTextual = try $ do
993− string "\\citeauthor{"
994− manyTill (noneOf "}") (char '}')
995− skipSpaces
996− Cite (c:cs) _ <- complexNatbibParenthetical
997− return $ Cite (c {citationMode = AuthorInText} : cs) []
998−
999−
1000−complexNatbibParenthetical :: GenParser Char ParserState Inline
1001−complexNatbibParenthetical = try $ do
1002− string "\\citetext{"
1003− cits <- many1Till parseOne (char '}')
1004− return $ Cite (concat cits) []
1005− where
1006− parseOne = do
1007− skipSpaces
1008− pref <- many (notFollowedBy (oneOf "\\}") >> inline)
1009− (Cite cites _) <- simpleCite
1010− suff <- many (notFollowedBy (oneOf "\\};") >> inline)
1011− skipSpaces
1012− optional $ char ';'
1013− return $ addPrefix pref $ addSuffix suff $ cites
1014−
1015−addPrefix :: [Inline] -> [Citation] -> [Citation]
1016−addPrefix p (k:ks) = k {citationPrefix = p ++ citationPrefix k} : ks
1017−addPrefix _ _ = []
1018−
1019−addSuffix :: [Inline] -> [Citation] -> [Citation]
1020−addSuffix s ks@(_:_) = let k = last ks
1021− in init ks ++ [k {citationSuffix = citationSuffix k ++ s}]
1022−addSuffix _ _ = []
1023−
1024−citationLabel :: GenParser Char ParserState String
1025−citationLabel = do
1026− res <- many1 $ noneOf ",}"
1027− optional $ char ','
1028− return $ removeLeadingTrailingSpace res
1029−
1030−-- | Parse any LaTeX inline command and return it in a raw TeX inline element.
1031−rawLaTeXInline' :: GenParser Char ParserState Inline
1032−rawLaTeXInline' = do
1033− notFollowedBy' $ oneOfStrings ["\\begin", "\\end", "\\item", "\\ignore",
1034− "\\section"]
1035− rawLaTeXInline
804+ let header'' = if null header'
805+ then replicate cols mempty
806+ else header'
807+ return $ table mempty (zip aligns (repeat 0)) header'' rows
1036808
1037−-- | Parse any LaTeX command and return it in a raw TeX inline element.
1038−rawLaTeXInline :: GenParser Char ParserState Inline
1039−rawLaTeXInline = try $ do
1040− state <- getState
1041− if stateParseRaw state
1042− then command >>= demacro
1043− else do
1044− (name,st,args) <- command
1045− x <- demacro (name,st,args)
1046− unless (x == Str "" || name `elem` commandsToIgnore) $ do
1047− inp <- getInput
1048− setInput $ intercalate " " args ++ inp
1049− return $ Str ""
src/Text/Pandoc/Readers/Markdown.hs+9−12
@@ -39,7 +39,7 @@ import Text.Pandoc.Definition
3939import Text.Pandoc.Generic
4040import Text.Pandoc.Shared
4141import Text.Pandoc.Parsing
42−import Text.Pandoc.Readers.LaTeX ( rawLaTeXInline, rawLaTeXEnvironment' )
42+import Text.Pandoc.Readers.LaTeX ( rawLaTeXInline, rawLaTeXBlock )
4343import Text.Pandoc.Readers.HTML ( htmlTag, htmlInBalanced, isInlineTag, isBlockTag,
4444 isTextTag, isCommentTag )
4545import Text.Pandoc.CharacterReferences ( decodeCharacterReferences )
@@ -729,8 +729,8 @@ rawVerbatimBlock = try $ do
729729rawTeXBlock :: GenParser Char ParserState Block
730730rawTeXBlock = do
731731 failIfStrict
732− result <- liftM (RawBlock "latex") rawLaTeXEnvironment'
733− <|> liftM (RawBlock "context") rawConTeXtEnvironment'
732+ result <- liftM (RawBlock "latex") rawLaTeXBlock
733+ <|> liftM (RawBlock "context") rawConTeXtEnvironment
734734 spaces
735735 return result
736736
@@ -933,8 +933,8 @@ inlineParsers = [ whitespace
933933 , inlineNote -- after superscript because of ^[link](/foo)^
934934 , autoLink
935935 , rawHtmlInline
936− , rawLaTeXInline'
937936 , escapedChar
937+ , rawLaTeXInline'
938938 , exampleRef
939939 , smartPunctuation inline
940940 , charRef
@@ -977,8 +977,7 @@ symbol :: GenParser Char ParserState Inline
977977symbol = do
978978 result <- noneOf "<\\\n\t "
979979 <|> try (do lookAhead $ char '\\'
980− notFollowedBy' $ rawLaTeXEnvironment'
981− <|> rawConTeXtEnvironment'
980+ notFollowedBy' rawTeXBlock
982981 char '\\')
983982 return $ Str [result]
984983
@@ -1246,18 +1245,16 @@ inlineNote = try $ do
12461245rawLaTeXInline' :: GenParser Char ParserState Inline
12471246rawLaTeXInline' = try $ do
12481247 failIfStrict
1249− lookAhead $ char '\\'
1250− notFollowedBy' $ rawLaTeXEnvironment'
1251− <|> rawConTeXtEnvironment'
1248+ lookAhead $ char '\\' >> notFollowedBy' (string "start") -- context env
12521249 RawInline _ s <- rawLaTeXInline
12531250 return $ RawInline "tex" s -- "tex" because it might be context or latex
12541251
1255−rawConTeXtEnvironment' :: GenParser Char st String
1256−rawConTeXtEnvironment' = try $ do
1252+rawConTeXtEnvironment :: GenParser Char st String
1253+rawConTeXtEnvironment = try $ do
12571254 string "\\start"
12581255 completion <- inBrackets (letter <|> digit <|> spaceChar)
12591256 <|> (many1 letter)
1260− contents <- manyTill (rawConTeXtEnvironment' <|> (count 1 anyChar))
1257+ contents <- manyTill (rawConTeXtEnvironment <|> (count 1 anyChar))
12611258 (try $ string "\\stop" >> string completion)
12621259 return $ "\\start" ++ completion ++ concat contents ++ "\\stop" ++ completion
12631260
src/Text/Pandoc/Shared.hs+23−1
@@ -70,11 +70,16 @@ module Text.Pandoc.Shared (
7070 inDirectory,
7171 findDataFile,
7272 readDataFile,
73+ -- * Error handling
74+ err,
75+ warn,
7376 ) where
7477
7578import Text.Pandoc.Definition
7679import Text.Pandoc.Generic
77−import qualified Text.Pandoc.UTF8 as UTF8 (readFile)
80+import qualified Text.Pandoc.UTF8 as UTF8
81+import System.Environment (getProgName)
82+import System.Exit (exitWith, ExitCode(..))
7883import Data.Char ( toLower, isLower, isUpper, isAlpha,
7984 isLetter, isDigit, isSpace )
8085import Data.List ( find, isPrefixOf, intercalate )
@@ -89,6 +94,7 @@ import Text.Pandoc.Highlighting (Style, pygments)
8994import Text.Pandoc.Pretty (charWidth)
9095import System.Locale (defaultTimeLocale)
9196import Data.Time
97+import System.IO (stderr)
9298
9399--
94100-- List processing
@@ -581,3 +587,19 @@ findDataFile (Just u) f = do
581587-- Cabal data directory.
582588readDataFile :: Maybe FilePath -> FilePath -> IO String
583589readDataFile userDir fname = findDataFile userDir fname >>= UTF8.readFile
590+
591+--
592+-- Error reporting
593+--
594+
595+err :: Int -> String -> IO a
596+err exitCode msg = do
597+ name <- getProgName
598+ UTF8.hPutStrLn stderr $ name ++ ": " ++ msg
599+ exitWith $ ExitFailure exitCode
600+ return undefined
601+
602+warn :: String -> IO ()
603+warn msg = do
604+ name <- getProgName
605+ UTF8.hPutStrLn stderr $ name ++ ": " ++ msg
src/pandoc.hs+11−16
@@ -31,8 +31,9 @@ writers.
3131module Main where
3232import Text.Pandoc
3333import Text.Pandoc.PDF (tex2pdf)
34+import Text.Pandoc.Readers.LaTeX (handleIncludes)
3435import Text.Pandoc.Shared ( tabFilter, ObfuscationMethod (..), readDataFile,
35− headerShift, findDataFile, normalize )
36+ headerShift, findDataFile, normalize, err, warn )
3637import Text.Pandoc.SelfContained ( makeSelfContained )
3738import Text.Pandoc.Highlighting ( languages, Style, tango, pygments,
3839 espresso, kate, haddock, monochrome )
@@ -43,7 +44,7 @@ import System.Console.GetOpt
4344import Data.Char ( toLower )
4445import Data.List ( intercalate, isSuffixOf, isPrefixOf )
4546import System.Directory ( getAppUserDataDirectory, doesFileExist, findExecutable )
46−import System.IO ( stdout, stderr )
47+import System.IO ( stdout )
4748import System.IO.Error ( isDoesNotExistError )
4849import Control.Exception.Extensible ( throwIO )
4950import qualified Text.Pandoc.UTF8 as UTF8
@@ -738,19 +739,6 @@ defaultWriterName x =
738739 ['.',y] | y `elem` ['1'..'9'] -> "man"
739740 _ -> "html"
740741
741−
742−err :: Int -> String -> IO a
743−err exitCode msg = do
744− name <- getProgName
745− UTF8.hPutStrLn stderr $ name ++ ": " ++ msg
746− exitWith $ ExitFailure exitCode
747− return undefined
748−
749−warn :: String -> IO ()
750−warn msg = do
751− name <- getProgName
752− UTF8.hPutStrLn stderr $ name ++ ": " ++ msg
753−
754742main :: IO ()
755743main = do
756744
@@ -996,7 +984,14 @@ main = do
996984
997985 let convertTabs = tabFilter (if preserveTabs then 0 else tabStop)
998986
999− doc <- fmap (reader startParserState . convertTabs . intercalate "\n") (readSources sources)
987+ let handleIncludes' = if readerName' == "latex" || readerName' == "beamer" ||
988+ readerName' == "latex+lhs" ||
989+ readerName' == "context"
990+ then handleIncludes
991+ else return
992+
993+ doc <- (reader startParserState) `fmap` (readSources sources >>=
994+ handleIncludes' . convertTabs . intercalate "\n")
1000995
1001996 let doc0 = foldr ($) doc transforms
1002997
tests/latex-reader.native+37−37
@@ -1,5 +1,5 @@
11Pandoc (Meta {docTitle = [Str "Pandoc",Space,Str "Test",Space,Str "Suite"], docAuthors = [[Str "John",Space,Str "MacFarlane"],[Str "Anonymous"]], docDate = [Str "July",Space,Str "17,",Space,Str "2006"]})
2−[Para [Str "This",Space,Str "is",Space,Str "a",Space,Str "set",Space,Str "of",Space,Str "tests",Space,Str "for",Space,Str "pandoc.",Space,Str "Most",Space,Str "of",Space,Str "them",Space,Str "are",Space,Str "adapted",Space,Str "from",Space,Str "John",Space,Str "Gruber",Str "\8217",Str "s",Space,Str "markdown",Space,Str "test",Space,Str "suite."]
2+[Para [Str "This",Space,Str "is",Space,Str "a",Space,Str "set",Space,Str "of",Space,Str "tests",Space,Str "for",Space,Str "pandoc.",Space,Str "Most",Space,Str "of",Space,Str "them",Space,Str "are",Space,Str "adapted",Space,Str "from",Space,Str "John",Space,Str "Gruber\8217s",Space,Str "markdown",Space,Str "test",Space,Str "suite."]
33,HorizontalRule
44,Header 1 [Str "Headers"]
55,Header 2 [Str "Level",Space,Str "2",Space,Str "with",Space,Str "an",Space,Link [Str "embedded",Space,Str "link"] ("/url","")]
@@ -14,13 +14,13 @@ Pandoc (Meta {docTitle = [Str "Pandoc",Space,Str "Test",Space,Str "Suite"], docA
1414,Para [Str "with",Space,Str "no",Space,Str "blank",Space,Str "line"]
1515,HorizontalRule
1616,Header 1 [Str "Paragraphs"]
17−,Para [Str "Here",Str "\8217",Str "s",Space,Str "a",Space,Str "regular",Space,Str "paragraph."]
18−,Para [Str "In",Space,Str "Markdown",Space,Str "1.0.0",Space,Str "and",Space,Str "earlier.",Space,Str "Version",Space,Str "8.",Space,Str "This",Space,Str "line",Space,Str "turns",Space,Str "into",Space,Str "a",Space,Str "list",Space,Str "item.",Space,Str "Because",Space,Str "a",Space,Str "hard",Str "-",Str "wrapped",Space,Str "line",Space,Str "in",Space,Str "the",Space,Str "middle",Space,Str "of",Space,Str "a",Space,Str "paragraph",Space,Str "looked",Space,Str "like",Space,Str "a",Space,Str "list",Space,Str "item."]
19−,Para [Str "Here",Str "\8217",Str "s",Space,Str "one",Space,Str "with",Space,Str "a",Space,Str "bullet.",Space,Str "*",Space,Str "criminey."]
17+,Para [Str "Here\8217s",Space,Str "a",Space,Str "regular",Space,Str "paragraph."]
18+,Para [Str "In",Space,Str "Markdown",Space,Str "1.0.0",Space,Str "and",Space,Str "earlier.",Space,Str "Version",Space,Str "8.",Space,Str "This",Space,Str "line",Space,Str "turns",Space,Str "into",Space,Str "a",Space,Str "list",Space,Str "item.",Space,Str "Because",Space,Str "a",Space,Str "hard-wrapped",Space,Str "line",Space,Str "in",Space,Str "the",Space,Str "middle",Space,Str "of",Space,Str "a",Space,Str "paragraph",Space,Str "looked",Space,Str "like",Space,Str "a",Space,Str "list",Space,Str "item."]
19+,Para [Str "Here\8217s",Space,Str "one",Space,Str "with",Space,Str "a",Space,Str "bullet.",Space,Str "*",Space,Str "criminey."]
2020,Para [Str "There",Space,Str "should",Space,Str "be",Space,Str "a",Space,Str "hard",Space,Str "line",Space,Str "break",LineBreak,Str "here."]
2121,HorizontalRule
2222,Header 1 [Str "Block",Space,Str "Quotes"]
23−,Para [Str "E",Str "-",Str "mail",Space,Str "style:"]
23+,Para [Str "E-mail",Space,Str "style:"]
2424,BlockQuote
2525 [Para [Str "This",Space,Str "is",Space,Str "a",Space,Str "block",Space,Str "quote.",Space,Str "It",Space,Str "is",Space,Str "pretty",Space,Str "short."]]
2626,BlockQuote
@@ -36,7 +36,7 @@ Pandoc (Meta {docTitle = [Str "Pandoc",Space,Str "Test",Space,Str "Suite"], docA
3636 ,BlockQuote
3737 [Para [Str "nested"]]]
3838,Para [Str "This",Space,Str "should",Space,Str "not",Space,Str "be",Space,Str "a",Space,Str "block",Space,Str "quote:",Space,Str "2",Space,Str ">",Space,Str "1."]
39−,Para [Str "Box",Str "-",Str "style:"]
39+,Para [Str "Box-style:"]
4040,BlockQuote
4141 [Para [Str "Example:"]
4242 ,CodeBlock ("",[],[]) "sub status {\n print \"working\";\n}"]
@@ -44,11 +44,11 @@ Pandoc (Meta {docTitle = [Str "Pandoc",Space,Str "Test",Space,Str "Suite"], docA
4444 [OrderedList (1,Decimal,Period)
4545 [[Para [Str "do",Space,Str "laundry"]]
4646 ,[Para [Str "take",Space,Str "out",Space,Str "the",Space,Str "trash"]]]]
47−,Para [Str "Here",Str "\8217",Str "s",Space,Str "a",Space,Str "nested",Space,Str "one:"]
47+,Para [Str "Here\8217s",Space,Str "a",Space,Str "nested",Space,Str "one:"]
4848,BlockQuote
4949 [Para [Str "Joe",Space,Str "said:"]
5050 ,BlockQuote
51− [Para [Str "Don",Str "\8217",Str "t",Space,Str "quote",Space,Str "me."]]]
51+ [Para [Str "Don\8217t",Space,Str "quote",Space,Str "me."]]]
5252,Para [Str "And",Space,Str "a",Space,Str "following",Space,Str "paragraph."]
5353,HorizontalRule
5454,Header 1 [Str "Code",Space,Str "Blocks"]
@@ -113,7 +113,7 @@ Pandoc (Meta {docTitle = [Str "Pandoc",Space,Str "Test",Space,Str "Suite"], docA
113113,Para [Str "Multiple",Space,Str "paragraphs:"]
114114,OrderedList (1,Decimal,Period)
115115 [[Para [Str "Item",Space,Str "1,",Space,Str "graf",Space,Str "one."]
116− ,Para [Str "Item",Space,Str "1.",Space,Str "graf",Space,Str "two.",Space,Str "The",Space,Str "quick",Space,Str "brown",Space,Str "fox",Space,Str "jumped",Space,Str "over",Space,Str "the",Space,Str "lazy",Space,Str "dog",Str "\8217",Str "s",Space,Str "back."]]
116+ ,Para [Str "Item",Space,Str "1.",Space,Str "graf",Space,Str "two.",Space,Str "The",Space,Str "quick",Space,Str "brown",Space,Str "fox",Space,Str "jumped",Space,Str "over",Space,Str "the",Space,Str "lazy",Space,Str "dog\8217s",Space,Str "back."]]
117117 ,[Para [Str "Item",Space,Str "2."]]
118118 ,[Para [Str "Item",Space,Str "3."]]]
119119,Header 2 [Str "Nested"]
@@ -123,7 +123,7 @@ Pandoc (Meta {docTitle = [Str "Pandoc",Space,Str "Test",Space,Str "Suite"], docA
123123 [[Para [Str "Tab"]
124124 ,BulletList
125125 [[Para [Str "Tab"]]]]]]]
126−,Para [Str "Here",Str "\8217",Str "s",Space,Str "another:"]
126+,Para [Str "Here\8217s",Space,Str "another:"]
127127,OrderedList (1,Decimal,Period)
128128 [[Para [Str "First"]]
129129 ,[Para [Str "Second:"]
@@ -217,7 +217,7 @@ Pandoc (Meta {docTitle = [Str "Pandoc",Space,Str "Test",Space,Str "Suite"], docA
217217,Para [Str "Simple",Space,Str "block",Space,Str "on",Space,Str "one",Space,Str "line:"]
218218,Para [Str "foo",Space,Str "And",Space,Str "nested",Space,Str "without",Space,Str "indentation:"]
219219,Para [Str "foo",Space,Str "bar",Space,Str "Interpreted",Space,Str "markdown",Space,Str "in",Space,Str "a",Space,Str "table:"]
220−,Para [Str "This",Space,Str "is",Space,Emph [Str "emphasized"],Space,Str "And",Space,Str "this",Space,Str "is",Space,Strong [Str "strong"],Space,Str "Here",Str "\8217",Str "s",Space,Str "a",Space,Str "simple",Space,Str "block:"]
220+,Para [Str "This",Space,Str "is",Space,Emph [Str "emphasized"],Space,Str "And",Space,Str "this",Space,Str "is",Space,Strong [Str "strong"],Space,Str "Here\8217s",Space,Str "a",Space,Str "simple",Space,Str "block:"]
221221,Para [Str "foo",Space,Str "This",Space,Str "should",Space,Str "be",Space,Str "a",Space,Str "code",Space,Str "block,",Space,Str "though:"]
222222,CodeBlock ("",[],[]) "<div>\n foo\n</div>"
223223,Para [Str "As",Space,Str "should",Space,Str "this:"]
@@ -230,7 +230,7 @@ Pandoc (Meta {docTitle = [Str "Pandoc",Space,Str "Test",Space,Str "Suite"], docA
230230,Para [Str "Just",Space,Str "plain",Space,Str "comment,",Space,Str "with",Space,Str "trailing",Space,Str "spaces",Space,Str "on",Space,Str "the",Space,Str "line:"]
231231,Para [Str "Code:"]
232232,CodeBlock ("",[],[]) "<hr />"
233−,Para [Str "Hr",Str "\8217",Str "s:"]
233+,Para [Str "Hr\8217s:"]
234234,HorizontalRule
235235,Header 1 [Str "Inline",Space,Str "Markup"]
236236,Para [Str "This",Space,Str "is",Space,Emph [Str "emphasized"],Str ",",Space,Str "and",Space,Str "so",Space,Emph [Str "is",Space,Str "this"],Str "."]
@@ -244,35 +244,35 @@ Pandoc (Meta {docTitle = [Str "Pandoc",Space,Str "Test",Space,Str "Suite"], docA
244244,Para [Strikeout [Str "This",Space,Str "is",Space,Emph [Str "strikeout"],Str "."]]
245245,Para [Str "Superscripts:",Space,Str "a",Superscript [Str "bc"],Str "d",Space,Str "a",Superscript [Emph [Str "hello"]],Space,Str "a",Superscript [Str "hello",Space,Str "there"],Str "."]
246246,Para [Str "Subscripts:",Space,Str "H",Subscript [Str "2"],Str "O,",Space,Str "H",Subscript [Str "23"],Str "O,",Space,Str "H",Subscript [Str "many",Space,Str "of",Space,Str "them"],Str "O."]
247−,Para [Str "These",Space,Str "should",Space,Str "not",Space,Str "be",Space,Str "superscripts",Space,Str "or",Space,Str "subscripts,",Space,Str "because",Space,Str "of",Space,Str "the",Space,Str "unescaped",Space,Str "spaces:",Space,Str "a",Str "^",Str "b",Space,Str "c",Str "^",Str "d,",Space,Str "a",Str "~",Str "b",Space,Str "c",Str "~",Str "d."]
247+,Para [Str "These",Space,Str "should",Space,Str "not",Space,Str "be",Space,Str "superscripts",Space,Str "or",Space,Str "subscripts,",Space,Str "because",Space,Str "of",Space,Str "the",Space,Str "unescaped",Space,Str "spaces:",Space,Str "a^b",Space,Str "c^d,",Space,Str "a",Math InlineMath "\\sim",Str "b",Space,Str "c",Math InlineMath "\\sim",Str "d."]
248248,HorizontalRule
249249,Header 1 [Str "Smart",Space,Str "quotes,",Space,Str "ellipses,",Space,Str "dashes"]
250250,Para [Quoted DoubleQuote [Str "Hello,"],Space,Str "said",Space,Str "the",Space,Str "spider.",Space,Quoted DoubleQuote [Quoted SingleQuote [Str "Shelob"],Space,Str "is",Space,Str "my",Space,Str "name."]]
251251,Para [Quoted SingleQuote [Str "A"],Str ",",Space,Quoted SingleQuote [Str "B"],Str ",",Space,Str "and",Space,Quoted SingleQuote [Str "C"],Space,Str "are",Space,Str "letters."]
252252,Para [Quoted SingleQuote [Str "Oak,"],Space,Quoted SingleQuote [Str "elm,"],Space,Str "and",Space,Quoted SingleQuote [Str "beech"],Space,Str "are",Space,Str "names",Space,Str "of",Space,Str "trees.",Space,Str "So",Space,Str "is",Space,Quoted SingleQuote [Str "pine."]]
253−,Para [Quoted SingleQuote [Str "He",Space,Str "said,",Space,Quoted DoubleQuote [Str "I",Space,Str "want",Space,Str "to",Space,Str "go."]],Space,Str "Were",Space,Str "you",Space,Str "alive",Space,Str "in",Space,Str "the",Space,Str "70",Str "\8217",Str "s?"]
253+,Para [Quoted SingleQuote [Str "He",Space,Str "said,",Space,Quoted DoubleQuote [Str "I",Space,Str "want",Space,Str "to",Space,Str "go."]],Space,Str "Were",Space,Str "you",Space,Str "alive",Space,Str "in",Space,Str "the",Space,Str "70\8217s?"]
254254,Para [Str "Here",Space,Str "is",Space,Str "some",Space,Str "quoted",Space,Quoted SingleQuote [Code ("",[],[]) "code"],Space,Str "and",Space,Str "a",Space,Quoted DoubleQuote [Link [Str "quoted",Space,Str "link"] ("http://example.com/?foo=1&bar=2","")],Str "."]
255−,Para [Str "Some",Space,Str "dashes:",Space,Str "one",Str "\8212",Str "two",Str "\8212",Str "three",Str "\8212",Str "four",Str "\8212",Str "five."]
256−,Para [Str "Dashes",Space,Str "between",Space,Str "numbers:",Space,Str "5",Str "-",Str "7,",Space,Str "255",Str "-",Str "66,",Space,Str "1987",Str "-",Str "1999."]
257−,Para [Str "Ellipses",Str "\8230",Str "and",Str "\8230",Str "and",Str "\8230",Str "."]
255+,Para [Str "Some",Space,Str "dashes:",Space,Str "one\8212two\8212three\8212four\8212five."]
256+,Para [Str "Dashes",Space,Str "between",Space,Str "numbers:",Space,Str "5\8211\&7,",Space,Str "255\8211\&66,",Space,Str "1987\8211\&1999."]
257+,Para [Str "Ellipses\8230and\8230and\8230."]
258258,HorizontalRule
259259,Header 1 [Str "LaTeX"]
260260,BulletList
261− [[Para [Cite [Citation {citationId = "smith.1899", citationPrefix = [], citationSuffix = [Str "22",Str "-",Str "23"], citationMode = NormalCitation, citationNoteNum = 0, citationHash = 0}] []]]
261+ [[Para [Cite [Citation {citationId = "smith.1899", citationPrefix = [], citationSuffix = [Str "22-23"], citationMode = NormalCitation, citationNoteNum = 0, citationHash = 0}] []]]
262262 ,[Para [RawInline "latex" "\\doublespacing"]]
263263 ,[Para [Math InlineMath "2+2=4"]]
264264 ,[Para [Math InlineMath "x \\in y"]]
265265 ,[Para [Math InlineMath "\\alpha \\wedge \\omega"]]
266266 ,[Para [Math InlineMath "223"]]
267− ,[Para [Math InlineMath "p",Str "-",Str "Tree"]]
267+ ,[Para [Math InlineMath "p",Str "-Tree"]]
268268 ,[Para [Math InlineMath "\\frac{d}{dx}f(x)=\\lim_{h\\to 0}\\frac{f(x+h)-f(x)}{h}"]]
269− ,[Para [Str "Here",Str "\8217",Str "s",Space,Str "one",Space,Str "that",Space,Str "has",Space,Str "a",Space,Str "line",Space,Str "break",Space,Str "in",Space,Str "it:",Space,Math InlineMath "\\alpha + \\omega \\times x^2",Str "."]]]
270−,Para [Str "These",Space,Str "shouldn",Str "\8217",Str "t",Space,Str "be",Space,Str "math:"]
269+ ,[Para [Str "Here\8217s",Space,Str "one",Space,Str "that",Space,Str "has",Space,Str "a",Space,Str "line",Space,Str "break",Space,Str "in",Space,Str "it:",Space,Math InlineMath "\\alpha + \\omega \\times x^2",Str "."]]]
270+,Para [Str "These",Space,Str "shouldn\8217t",Space,Str "be",Space,Str "math:"]
271271,BulletList
272272 [[Para [Str "To",Space,Str "get",Space,Str "the",Space,Str "famous",Space,Str "equation,",Space,Str "write",Space,Code ("",[],[]) "$e = mc^2$",Str "."]]
273− ,[Para [Str "$",Str "22,000",Space,Str "is",Space,Str "a",Space,Emph [Str "lot"],Space,Str "of",Space,Str "money.",Space,Str "So",Space,Str "is",Space,Str "$",Str "34,000.",Space,Str "(It",Space,Str "worked",Space,Str "if",Space,Quoted DoubleQuote [Str "lot"],Space,Str "is",Space,Str "emphasized.)"]]
274− ,[Para [Str "Escaped",Space,Code ("",[],[]) "$",Str ":",Space,Str "$",Str "73",Space,Emph [Str "this",Space,Str "should",Space,Str "be",Space,Str "emphasized"],Space,Str "23",Str "$",Str "."]]]
275−,Para [Str "Here",Str "\8217",Str "s",Space,Str "a",Space,Str "LaTeX",Space,Str "table:"]
273+ ,[Para [Str "$22,000",Space,Str "is",Space,Str "a",Space,Emph [Str "lot"],Space,Str "of",Space,Str "money.",Space,Str "So",Space,Str "is",Space,Str "$34,000.",Space,Str "(It",Space,Str "worked",Space,Str "if",Space,Quoted DoubleQuote [Str "lot"],Space,Str "is",Space,Str "emphasized.)"]]
274+ ,[Para [Str "Escaped",Space,Code ("",[],[]) "$",Str ":",Space,Str "$73",Space,Emph [Str "this",Space,Str "should",Space,Str "be",Space,Str "emphasized"],Space,Str "23$."]]]
275+,Para [Str "Here\8217s",Space,Str "a",Space,Str "LaTeX",Space,Str "table:"]
276276,Table [] [AlignLeft,AlignLeft] [0.0,0.0]
277277 [[Plain [Str "Animal"]]
278278 ,[Plain [Str "Number"]]]
@@ -289,8 +289,8 @@ Pandoc (Meta {docTitle = [Str "Pandoc",Space,Str "Test",Space,Str "Suite"], docA
289289 ,[Para [Str "section:",Space,Str "\167"]]
290290 ,[Para [Str "set",Space,Str "membership:",Space,Str "\8712"]]
291291 ,[Para [Str "copyright:",Space,Str "\169"]]]
292−,Para [Str "AT",Str "&",Str "T",Space,Str "has",Space,Str "an",Space,Str "ampersand",Space,Str "in",Space,Str "their",Space,Str "name."]
293−,Para [Str "AT",Str "&",Str "T",Space,Str "is",Space,Str "another",Space,Str "way",Space,Str "to",Space,Str "write",Space,Str "it."]
292+,Para [Str "AT&T",Space,Str "has",Space,Str "an",Space,Str "ampersand",Space,Str "in",Space,Str "their",Space,Str "name."]
293+,Para [Str "AT&T",Space,Str "is",Space,Str "another",Space,Str "way",Space,Str "to",Space,Str "write",Space,Str "it."]
294294,Para [Str "This",Space,Str "&",Space,Str "that."]
295295,Para [Str "4",Space,Str "<",Space,Str "5."]
296296,Para [Str "6",Space,Str ">",Space,Str "5."]
@@ -304,7 +304,7 @@ Pandoc (Meta {docTitle = [Str "Pandoc",Space,Str "Test",Space,Str "Suite"], docA
304304,Para [Str "Right",Space,Str "bracket:",Space,Str "]"]
305305,Para [Str "Left",Space,Str "paren:",Space,Str "("]
306306,Para [Str "Right",Space,Str "paren:",Space,Str ")"]
307−,Para [Str "Greater",Str "-",Str "than:",Space,Str ">"]
307+,Para [Str "Greater-than:",Space,Str ">"]
308308,Para [Str "Hash:",Space,Str "#"]
309309,Para [Str "Period:",Space,Str "."]
310310,Para [Str "Bang:",Space,Str "!"]
@@ -319,37 +319,37 @@ Pandoc (Meta {docTitle = [Str "Pandoc",Space,Str "Test",Space,Str "Suite"], docA
319319,Para [Link [Str "URL",Space,Str "and",Space,Str "title"] ("/url/",""),Str "."]
320320,Para [Link [Str "URL",Space,Str "and",Space,Str "title"] ("/url/","")]
321321,Para [Link [Str "URL",Space,Str "and",Space,Str "title"] ("/url/","")]
322−,Para [Link [Str "with",Str "_",Str "underscore"] ("/url/with_underscore","")]
322+,Para [Link [Str "with_underscore"] ("/url/with_underscore","")]
323323,Para [Link [Str "Email",Space,Str "link"] ("mailto:nobody@nowhere.net","")]
324324,Para [Link [Str "Empty"] ("",""),Str "."]
325325,Header 2 [Str "Reference"]
326326,Para [Str "Foo",Space,Link [Str "bar"] ("/url/",""),Str "."]
327327,Para [Str "Foo",Space,Link [Str "bar"] ("/url/",""),Str "."]
328328,Para [Str "Foo",Space,Link [Str "bar"] ("/url/",""),Str "."]
329−,Para [Str "With",Space,Link [Str "embedded",Space,Str "[",Str "brackets",Str "]"] ("/url/",""),Str "."]
329+,Para [Str "With",Space,Link [Str "embedded",Space,Str "[brackets]"] ("/url/",""),Str "."]
330330,Para [Link [Str "b"] ("/url/",""),Space,Str "by",Space,Str "itself",Space,Str "should",Space,Str "be",Space,Str "a",Space,Str "link."]
331331,Para [Str "Indented",Space,Link [Str "once"] ("/url",""),Str "."]
332332,Para [Str "Indented",Space,Link [Str "twice"] ("/url",""),Str "."]
333333,Para [Str "Indented",Space,Link [Str "thrice"] ("/url",""),Str "."]
334−,Para [Str "This",Space,Str "should",Space,Str "[",Str "not",Str "]",Str "[",Str "]",Space,Str "be",Space,Str "a",Space,Str "link."]
334+,Para [Str "This",Space,Str "should",Space,Str "[not][]",Space,Str "be",Space,Str "a",Space,Str "link."]
335335,CodeBlock ("",[],[]) "[not]: /url"
336336,Para [Str "Foo",Space,Link [Str "bar"] ("/url/",""),Str "."]
337337,Para [Str "Foo",Space,Link [Str "biz"] ("/url/",""),Str "."]
338338,Header 2 [Str "With",Space,Str "ampersands"]
339−,Para [Str "Here",Str "\8217",Str "s",Space,Str "a",Space,Link [Str "link",Space,Str "with",Space,Str "an",Space,Str "ampersand",Space,Str "in",Space,Str "the",Space,Str "URL"] ("http://example.com/?foo=1&bar=2",""),Str "."]
340−,Para [Str "Here",Str "\8217",Str "s",Space,Str "a",Space,Str "link",Space,Str "with",Space,Str "an",Space,Str "amersand",Space,Str "in",Space,Str "the",Space,Str "link",Space,Str "text:",Space,Link [Str "AT",Str "&",Str "T"] ("http://att.com/",""),Str "."]
341−,Para [Str "Here",Str "\8217",Str "s",Space,Str "an",Space,Link [Str "inline",Space,Str "link"] ("/script?foo=1&bar=2",""),Str "."]
342−,Para [Str "Here",Str "\8217",Str "s",Space,Str "an",Space,Link [Str "inline",Space,Str "link",Space,Str "in",Space,Str "pointy",Space,Str "braces"] ("/script?foo=1&bar=2",""),Str "."]
339+,Para [Str "Here\8217s",Space,Str "a",Space,Link [Str "link",Space,Str "with",Space,Str "an",Space,Str "ampersand",Space,Str "in",Space,Str "the",Space,Str "URL"] ("http://example.com/?foo=1&bar=2",""),Str "."]
340+,Para [Str "Here\8217s",Space,Str "a",Space,Str "link",Space,Str "with",Space,Str "an",Space,Str "amersand",Space,Str "in",Space,Str "the",Space,Str "link",Space,Str "text:",Space,Link [Str "AT&T"] ("http://att.com/",""),Str "."]
341+,Para [Str "Here\8217s",Space,Str "an",Space,Link [Str "inline",Space,Str "link"] ("/script?foo=1&bar=2",""),Str "."]
342+,Para [Str "Here\8217s",Space,Str "an",Space,Link [Str "inline",Space,Str "link",Space,Str "in",Space,Str "pointy",Space,Str "braces"] ("/script?foo=1&bar=2",""),Str "."]
343343,Header 2 [Str "Autolinks"]
344344,Para [Str "With",Space,Str "an",Space,Str "ampersand:",Space,Link [Code ("",["url"],[]) "http://example.com/?foo=1&bar=2"] ("http://example.com/?foo=1&bar=2","")]
345345,BulletList
346346 [[Para [Str "In",Space,Str "a",Space,Str "list?"]]
347347 ,[Para [Link [Code ("",["url"],[]) "http://example.com/"] ("http://example.com/","")]]
348348 ,[Para [Str "It",Space,Str "should."]]]
349−,Para [Str "An",Space,Str "e",Str "-",Str "mail",Space,Str "address:",Space,Link [Code ("",[],[]) "nobody@nowhere.net"] ("mailto:nobody@nowhere.net","")]
349+,Para [Str "An",Space,Str "e-mail",Space,Str "address:",Space,Link [Code ("",[],[]) "nobody@nowhere.net"] ("mailto:nobody@nowhere.net","")]
350350,BlockQuote
351351 [Para [Str "Blockquoted:",Space,Link [Code ("",["url"],[]) "http://example.com/"] ("http://example.com/","")]]
352−,Para [Str "Auto",Str "-",Str "links",Space,Str "should",Space,Str "not",Space,Str "occur",Space,Str "here:",Space,Code ("",[],[]) "<http://example.com/>"]
352+,Para [Str "Auto-links",Space,Str "should",Space,Str "not",Space,Str "occur",Space,Str "here:",Space,Code ("",[],[]) "<http://example.com/>"]
353353,CodeBlock ("",[],[]) "or here: <http://example.com/>"
354354,HorizontalRule
355355,Header 1 [Str "Images"]
@@ -358,7 +358,7 @@ Pandoc (Meta {docTitle = [Str "Pandoc",Space,Str "Test",Space,Str "Suite"], docA
358358,Para [Str "Here",Space,Str "is",Space,Str "a",Space,Str "movie",Space,Image [Str "image"] ("movie.jpg",""),Space,Str "icon."]
359359,HorizontalRule
360360,Header 1 [Str "Footnotes"]
361−,Para [Str "Here",Space,Str "is",Space,Str "a",Space,Str "footnote",Space,Str "reference,",Note [Para [Str "Here",Space,Str "is",Space,Str "the",Space,Str "footnote.",Space,Str "It",Space,Str "can",Space,Str "go",Space,Str "anywhere",Space,Str "after",Space,Str "the",Space,Str "footnote",Space,Str "reference.",Space,Str "It",Space,Str "need",Space,Str "not",Space,Str "be",Space,Str "placed",Space,Str "at",Space,Str "the",Space,Str "end",Space,Str "of",Space,Str "the",Space,Str "document."]],Space,Str "and",Space,Str "another.",Note [Para [Str "Here",Str "\8217",Str "s",Space,Str "the",Space,Str "long",Space,Str "note.",Space,Str "This",Space,Str "one",Space,Str "contains",Space,Str "multiple",Space,Str "blocks."],Para [Str "Subsequent",Space,Str "blocks",Space,Str "are",Space,Str "indented",Space,Str "to",Space,Str "show",Space,Str "that",Space,Str "they",Space,Str "belong",Space,Str "to",Space,Str "the",Space,Str "footnote",Space,Str "(as",Space,Str "with",Space,Str "list",Space,Str "items)."],CodeBlock ("",[],[]) " { <code> }",Para [Str "If",Space,Str "you",Space,Str "want,",Space,Str "you",Space,Str "can",Space,Str "indent",Space,Str "every",Space,Str "line,",Space,Str "but",Space,Str "you",Space,Str "can",Space,Str "also",Space,Str "be",Space,Str "lazy",Space,Str "and",Space,Str "just",Space,Str "indent",Space,Str "the",Space,Str "first",Space,Str "line",Space,Str "of",Space,Str "each",Space,Str "block."]],Space,Str "This",Space,Str "should",Space,Emph [Str "not"],Space,Str "be",Space,Str "a",Space,Str "footnote",Space,Str "reference,",Space,Str "because",Space,Str "it",Space,Str "contains",Space,Str "a",Space,Str "space.",Str "[",Str "^",Str "my",Space,Str "note",Str "]",Space,Str "Here",Space,Str "is",Space,Str "an",Space,Str "inline",Space,Str "note.",Note [Para [Str "This",Space,Str "is",Space,Emph [Str "easier"],Space,Str "to",Space,Str "type.",Space,Str "Inline",Space,Str "notes",Space,Str "may",Space,Str "contain",Space,Link [Str "links"] ("http://google.com",""),Space,Str "and",Space,Code ("",[],[]) "]",Space,Str "verbatim",Space,Str "characters,",Space,Str "as",Space,Str "well",Space,Str "as",Space,Str "[",Str "bracketed",Space,Str "text",Str "]",Str "."]]]
361+,Para [Str "Here",Space,Str "is",Space,Str "a",Space,Str "footnote",Space,Str "reference,",Note [Para [Str "Here",Space,Str "is",Space,Str "the",Space,Str "footnote.",Space,Str "It",Space,Str "can",Space,Str "go",Space,Str "anywhere",Space,Str "after",Space,Str "the",Space,Str "footnote",Space,Str "reference.",Space,Str "It",Space,Str "need",Space,Str "not",Space,Str "be",Space,Str "placed",Space,Str "at",Space,Str "the",Space,Str "end",Space,Str "of",Space,Str "the",Space,Str "document."]],Space,Str "and",Space,Str "another.",Note [Para [Str "Here\8217s",Space,Str "the",Space,Str "long",Space,Str "note.",Space,Str "This",Space,Str "one",Space,Str "contains",Space,Str "multiple",Space,Str "blocks."],Para [Str "Subsequent",Space,Str "blocks",Space,Str "are",Space,Str "indented",Space,Str "to",Space,Str "show",Space,Str "that",Space,Str "they",Space,Str "belong",Space,Str "to",Space,Str "the",Space,Str "footnote",Space,Str "(as",Space,Str "with",Space,Str "list",Space,Str "items)."],CodeBlock ("",[],[]) " { <code> }",Para [Str "If",Space,Str "you",Space,Str "want,",Space,Str "you",Space,Str "can",Space,Str "indent",Space,Str "every",Space,Str "line,",Space,Str "but",Space,Str "you",Space,Str "can",Space,Str "also",Space,Str "be",Space,Str "lazy",Space,Str "and",Space,Str "just",Space,Str "indent",Space,Str "the",Space,Str "first",Space,Str "line",Space,Str "of",Space,Str "each",Space,Str "block."]],Space,Str "This",Space,Str "should",Space,Emph [Str "not"],Space,Str "be",Space,Str "a",Space,Str "footnote",Space,Str "reference,",Space,Str "because",Space,Str "it",Space,Str "contains",Space,Str "a",Space,Str "space.[^my",Space,Str "note]",Space,Str "Here",Space,Str "is",Space,Str "an",Space,Str "inline",Space,Str "note.",Note [Para [Str "This",Space,Str "is",Space,Emph [Str "easier"],Space,Str "to",Space,Str "type.",Space,Str "Inline",Space,Str "notes",Space,Str "may",Space,Str "contain",Space,Link [Str "links"] ("http://google.com",""),Space,Str "and",Space,Code ("",[],[]) "]",Space,Str "verbatim",Space,Str "characters,",Space,Str "as",Space,Str "well",Space,Str "as",Space,Str "[bracketed",Space,Str "text]."]]]
362362,BlockQuote
363363 [Para [Str "Notes",Space,Str "can",Space,Str "go",Space,Str "in",Space,Str "quotes.",Note [Para [Str "In",Space,Str "quote."]]]]
364364,OrderedList (1,Decimal,Period)
tests/lhs-test.html+1−1
@@ -8,7 +8,7 @@
88 <style type="text/css">
99table.sourceCode, tr.sourceCode, td.lineNumbers, td.sourceCode {
1010 margin: 0; padding: 0; vertical-align: baseline; border: none; }
11−table.sourceCode { }
11+table.sourceCode { width: 100%; }
1212td.lineNumbers { text-align: right; padding-right: 4px; padding-left: 4px; color: #aaaaaa; border-right: 1px solid #aaaaaa; }
1313td.sourceCode { padding-left: 5px; }
1414code > span.kw { color: #007020; font-weight: bold; }
tests/lhs-test.html+lhs+1−1
@@ -8,7 +8,7 @@
88 <style type="text/css">
99table.sourceCode, tr.sourceCode, td.lineNumbers, td.sourceCode {
1010 margin: 0; padding: 0; vertical-align: baseline; border: none; }
11−table.sourceCode { }
11+table.sourceCode { width: 100%; }
1212td.lineNumbers { text-align: right; padding-right: 4px; padding-left: 4px; color: #aaaaaa; border-right: 1px solid #aaaaaa; }
1313td.sourceCode { padding-left: 5px; }
1414code > span.kw { color: #007020; font-weight: bold; }
tests/markdown-reader-more.native+1−1
@@ -2,7 +2,7 @@
22,Header 2 [Str "Blank",Space,Str "line",Space,Str "before",Space,Str "URL",Space,Str "in",Space,Str "link",Space,Str "reference"]
33,Para [Link [Str "foo"] ("/url",""),Space,Str "and",Space,Link [Str "bar"] ("/url","title")]
44,Header 2 [Str "Raw",Space,Str "ConTeXt",Space,Str "environments"]
5−,Plain [RawInline "tex" "\\placeformula"]
5+,Plain [RawInline "tex" "\\placeformula "]
66,RawBlock "context" "\\startformula\n L_{1} = L_{2}\n \\stopformula"
77,RawBlock "context" "\\start[a2]\n\\start[a2]\n\\stop[a2]\n\\stop[a2]"
88,Header 2 [Str "URLs",Space,Str "with",Space,Str "spaces"]