git.iko.soy/typeable/pandoc
on master help sign in
Moved parsing functions from Text.Pandoc.Shared to new module.
998fd09John MacFarlane
pandoc.cabal+1
@@ -166,6 +166,7 @@ Library
166 Text.Pandoc.Definition,
166 Text.Pandoc.Definition,
167 Text.Pandoc.CharacterReferences,
167 Text.Pandoc.CharacterReferences,
168 Text.Pandoc.Shared,
168 Text.Pandoc.Shared,
169+ Text.Pandoc.Parsing,
169 Text.Pandoc.Highlighting,
170 Text.Pandoc.Highlighting,
170 Text.Pandoc.Readers.HTML,
171 Text.Pandoc.Readers.HTML,
171 Text.Pandoc.Readers.LaTeX,
172 Text.Pandoc.Readers.LaTeX,
src/Text/Pandoc.hs+1
@@ -119,6 +119,7 @@ import Text.Pandoc.Writers.Man
119import Text.Pandoc.Writers.RTF
119import Text.Pandoc.Writers.RTF
120import Text.Pandoc.Writers.MediaWiki
120import Text.Pandoc.Writers.MediaWiki
121import Text.Pandoc.Templates
121import Text.Pandoc.Templates
122+import Text.Pandoc.Parsing
122import Text.Pandoc.Shared
123import Text.Pandoc.Shared
123import Data.Version (showVersion)
124import Data.Version (showVersion)
124import Paths_pandoc (version)
125import Paths_pandoc (version)
src/Text/Pandoc/Parsing.hsnew file+537
@@ -0,0 +1,537 @@
1+{-
2+Copyright (C) 2006-2010 John MacFarlane <jgm@berkeley.edu>
3+
4+This program is free software; you can redistribute it and/or modify
5+it under the terms of the GNU General Public License as published by
6+the Free Software Foundation; either version 2 of the License, or
7+(at your option) any later version.
8+
9+This program is distributed in the hope that it will be useful,
10+but WITHOUT ANY WARRANTY; without even the implied warranty of
11+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+GNU General Public License for more details.
13+
14+You should have received a copy of the GNU General Public License
15+along with this program; if not, write to the Free Software
16+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17+-}
18+
19+{- |
20+ Module : Text.Pandoc.Parsing
21+ Copyright : Copyright (C) 2006-2010 John MacFarlane
22+ License : GNU GPL, version 2 or above
23+
24+ Maintainer : John MacFarlane <jgm@berkeley.edu>
25+ Stability : alpha
26+ Portability : portable
27+
28+A utility library with parsers used in pandoc readers.
29+-}
30+module Text.Pandoc.Parsing ( (>>~),
31+ anyLine,
32+ many1Till,
33+ notFollowedBy',
34+ oneOfStrings,
35+ spaceChar,
36+ skipSpaces,
37+ blankline,
38+ blanklines,
39+ enclosed,
40+ stringAnyCase,
41+ parseFromString,
42+ lineClump,
43+ charsInBalanced,
44+ charsInBalanced',
45+ romanNumeral,
46+ emailAddress,
47+ uri,
48+ withHorizDisplacement,
49+ nullBlock,
50+ failIfStrict,
51+ failUnlessLHS,
52+ escaped,
53+ anyOrderedListMarker,
54+ orderedListMarker,
55+ charRef,
56+ readWith,
57+ testStringWith,
58+ ParserState (..),
59+ defaultParserState,
60+ HeaderType (..),
61+ ParserContext (..),
62+ QuoteContext (..),
63+ NoteTable,
64+ KeyTable,
65+ Key (..),
66+ lookupKeySrc,
67+ refsMatch )
68+where
69+
70+import Text.Pandoc.Definition
71+import qualified Text.Pandoc.UTF8 as UTF8 (putStrLn)
72+import Text.ParserCombinators.Parsec
73+import Text.Pandoc.CharacterReferences ( characterReference )
74+import Data.Char ( toLower, toUpper, ord, isAscii )
75+import Data.List ( intercalate )
76+import Network.URI ( parseURI, URI (..), isAllowedInURI )
77+import Control.Monad (join)
78+import Text.Pandoc.Shared (escapeURI)
79+import qualified Data.Map as M
80+
81+-- | Like >>, but returns the operation on the left.
82+-- (Suggested by Tillmann Rendel on Haskell-cafe list.)
83+(>>~) :: (Monad m) => m a -> m b -> m a
84+a >>~ b = a >>= \x -> b >> return x
85+
86+-- | Parse any line of text
87+anyLine :: GenParser Char st [Char]
88+anyLine = manyTill anyChar newline
89+
90+-- | Like @manyTill@, but reads at least one item.
91+many1Till :: GenParser tok st a
92+ -> GenParser tok st end
93+ -> GenParser tok st [a]
94+many1Till p end = do
95+ first <- p
96+ rest <- manyTill p end
97+ return (first:rest)
98+
99+-- | A more general form of @notFollowedBy@. This one allows any
100+-- type of parser to be specified, and succeeds only if that parser fails.
101+-- It does not consume any input.
102+notFollowedBy' :: Show b => GenParser a st b -> GenParser a st ()
103+notFollowedBy' p = try $ join $ do a <- try p
104+ return (unexpected (show a))
105+ <|>
106+ return (return ())
107+-- (This version due to Andrew Pimlott on the Haskell mailing list.)
108+
109+-- | Parses one of a list of strings (tried in order).
110+oneOfStrings :: [String] -> GenParser Char st String
111+oneOfStrings listOfStrings = choice $ map (try . string) listOfStrings
112+
113+-- | Parses a space or tab.
114+spaceChar :: CharParser st Char
115+spaceChar = char ' ' <|> char '\t'
116+
117+-- | Skips zero or more spaces or tabs.
118+skipSpaces :: GenParser Char st ()
119+skipSpaces = skipMany spaceChar
120+
121+-- | Skips zero or more spaces or tabs, then reads a newline.
122+blankline :: GenParser Char st Char
123+blankline = try $ skipSpaces >> newline
124+
125+-- | Parses one or more blank lines and returns a string of newlines.
126+blanklines :: GenParser Char st [Char]
127+blanklines = many1 blankline
128+
129+-- | Parses material enclosed between start and end parsers.
130+enclosed :: GenParser Char st t -- ^ start parser
131+ -> GenParser Char st end -- ^ end parser
132+ -> GenParser Char st a -- ^ content parser (to be used repeatedly)
133+ -> GenParser Char st [a]
134+enclosed start end parser = try $
135+ start >> notFollowedBy space >> many1Till parser end
136+
137+-- | Parse string, case insensitive.
138+stringAnyCase :: [Char] -> CharParser st String
139+stringAnyCase [] = string ""
140+stringAnyCase (x:xs) = do
141+ firstChar <- char (toUpper x) <|> char (toLower x)
142+ rest <- stringAnyCase xs
143+ return (firstChar:rest)
144+
145+-- | Parse contents of 'str' using 'parser' and return result.
146+parseFromString :: GenParser tok st a -> [tok] -> GenParser tok st a
147+parseFromString parser str = do
148+ oldPos <- getPosition
149+ oldInput <- getInput
150+ setInput str
151+ result <- parser
152+ setInput oldInput
153+ setPosition oldPos
154+ return result
155+
156+-- | Parse raw line block up to and including blank lines.
157+lineClump :: GenParser Char st String
158+lineClump = blanklines
159+ <|> (many1 (notFollowedBy blankline >> anyLine) >>= return . unlines)
160+
161+-- | Parse a string of characters between an open character
162+-- and a close character, including text between balanced
163+-- pairs of open and close, which must be different. For example,
164+-- @charsInBalanced '(' ')'@ will parse "(hello (there))"
165+-- and return "hello (there)". Stop if a blank line is
166+-- encountered.
167+charsInBalanced :: Char -> Char -> GenParser Char st String
168+charsInBalanced open close = try $ do
169+ char open
170+ raw <- many $ (many1 (noneOf [open, close, '\n']))
171+ <|> (do res <- charsInBalanced open close
172+ return $ [open] ++ res ++ [close])
173+ <|> try (string "\n" >>~ notFollowedBy' blanklines)
174+ char close
175+ return $ concat raw
176+
177+-- | Like @charsInBalanced@, but allow blank lines in the content.
178+charsInBalanced' :: Char -> Char -> GenParser Char st String
179+charsInBalanced' open close = try $ do
180+ char open
181+ raw <- many $ (many1 (noneOf [open, close]))
182+ <|> (do res <- charsInBalanced' open close
183+ return $ [open] ++ res ++ [close])
184+ char close
185+ return $ concat raw
186+
187+-- Auxiliary functions for romanNumeral:
188+
189+lowercaseRomanDigits :: [Char]
190+lowercaseRomanDigits = ['i','v','x','l','c','d','m']
191+
192+uppercaseRomanDigits :: [Char]
193+uppercaseRomanDigits = map toUpper lowercaseRomanDigits
194+
195+-- | Parses a roman numeral (uppercase or lowercase), returns number.
196+romanNumeral :: Bool -- ^ Uppercase if true
197+ -> GenParser Char st Int
198+romanNumeral upperCase = do
199+ let romanDigits = if upperCase
200+ then uppercaseRomanDigits
201+ else lowercaseRomanDigits
202+ lookAhead $ oneOf romanDigits
203+ let [one, five, ten, fifty, hundred, fivehundred, thousand] =
204+ map char romanDigits
205+ thousands <- many thousand >>= (return . (1000 *) . length)
206+ ninehundreds <- option 0 $ try $ hundred >> thousand >> return 900
207+ fivehundreds <- many fivehundred >>= (return . (500 *) . length)
208+ fourhundreds <- option 0 $ try $ hundred >> fivehundred >> return 400
209+ hundreds <- many hundred >>= (return . (100 *) . length)
210+ nineties <- option 0 $ try $ ten >> hundred >> return 90
211+ fifties <- many fifty >>= (return . (50 *) . length)
212+ forties <- option 0 $ try $ ten >> fifty >> return 40
213+ tens <- many ten >>= (return . (10 *) . length)
214+ nines <- option 0 $ try $ one >> ten >> return 9
215+ fives <- many five >>= (return . (5 *) . length)
216+ fours <- option 0 $ try $ one >> five >> return 4
217+ ones <- many one >>= (return . length)
218+ let total = thousands + ninehundreds + fivehundreds + fourhundreds +
219+ hundreds + nineties + fifties + forties + tens + nines +
220+ fives + fours + ones
221+ if total == 0
222+ then fail "not a roman numeral"
223+ else return total
224+
225+-- Parsers for email addresses and URIs
226+
227+emailChar :: GenParser Char st Char
228+emailChar = alphaNum <|> oneOf "-+_."
229+
230+domainChar :: GenParser Char st Char
231+domainChar = alphaNum <|> char '-'
232+
233+domain :: GenParser Char st [Char]
234+domain = do
235+ first <- many1 domainChar
236+ dom <- many1 $ try (char '.' >> many1 domainChar )
237+ return $ intercalate "." (first:dom)
238+
239+-- | Parses an email address; returns original and corresponding
240+-- escaped mailto: URI.
241+emailAddress :: GenParser Char st (String, String)
242+emailAddress = try $ do
243+ firstLetter <- alphaNum
244+ restAddr <- many emailChar
245+ let addr = firstLetter:restAddr
246+ char '@'
247+ dom <- domain
248+ let full = addr ++ '@':dom
249+ return (full, escapeURI $ "mailto:" ++ full)
250+
251+-- | Parses a URI. Returns pair of original and URI-escaped version.
252+uri :: GenParser Char st (String, String)
253+uri = try $ do
254+ let protocols = [ "http:", "https:", "ftp:", "file:", "mailto:",
255+ "news:", "telnet:" ]
256+ lookAhead $ oneOfStrings protocols
257+ -- scan non-ascii characters and ascii characters allowed in a URI
258+ str <- many1 $ satisfy (\c -> not (isAscii c) || isAllowedInURI c)
259+ -- now see if they amount to an absolute URI
260+ case parseURI (escapeURI str) of
261+ Just uri' -> if uriScheme uri' `elem` protocols
262+ then return (str, show uri')
263+ else fail "not a URI"
264+ Nothing -> fail "not a URI"
265+
266+-- | Applies a parser, returns tuple of its results and its horizontal
267+-- displacement (the difference between the source column at the end
268+-- and the source column at the beginning). Vertical displacement
269+-- (source row) is ignored.
270+withHorizDisplacement :: GenParser Char st a -- ^ Parser to apply
271+ -> GenParser Char st (a, Int) -- ^ (result, displacement)
272+withHorizDisplacement parser = do
273+ pos1 <- getPosition
274+ result <- parser
275+ pos2 <- getPosition
276+ return (result, sourceColumn pos2 - sourceColumn pos1)
277+
278+-- | Parses a character and returns 'Null' (so that the parser can move on
279+-- if it gets stuck).
280+nullBlock :: GenParser Char st Block
281+nullBlock = anyChar >> return Null
282+
283+-- | Fail if reader is in strict markdown syntax mode.
284+failIfStrict :: GenParser Char ParserState ()
285+failIfStrict = do
286+ state <- getState
287+ if stateStrict state then fail "strict mode" else return ()
288+
289+-- | Fail unless we're in literate haskell mode.
290+failUnlessLHS :: GenParser tok ParserState ()
291+failUnlessLHS = do
292+ state <- getState
293+ if stateLiterateHaskell state then return () else fail "Literate haskell feature"
294+
295+-- | Parses backslash, then applies character parser.
296+escaped :: GenParser Char st Char -- ^ Parser for character to escape
297+ -> GenParser Char st Inline
298+escaped parser = try $ do
299+ char '\\'
300+ result <- parser
301+ return (Str [result])
302+
303+-- | Parses an uppercase roman numeral and returns (UpperRoman, number).
304+upperRoman :: GenParser Char st (ListNumberStyle, Int)
305+upperRoman = do
306+ num <- romanNumeral True
307+ return (UpperRoman, num)
308+
309+-- | Parses a lowercase roman numeral and returns (LowerRoman, number).
310+lowerRoman :: GenParser Char st (ListNumberStyle, Int)
311+lowerRoman = do
312+ num <- romanNumeral False
313+ return (LowerRoman, num)
314+
315+-- | Parses a decimal numeral and returns (Decimal, number).
316+decimal :: GenParser Char st (ListNumberStyle, Int)
317+decimal = do
318+ num <- many1 digit
319+ return (Decimal, read num)
320+
321+-- | Parses a '#' returns (DefaultStyle, 1).
322+defaultNum :: GenParser Char st (ListNumberStyle, Int)
323+defaultNum = do
324+ char '#'
325+ return (DefaultStyle, 1)
326+
327+-- | Parses a lowercase letter and returns (LowerAlpha, number).
328+lowerAlpha :: GenParser Char st (ListNumberStyle, Int)
329+lowerAlpha = do
330+ ch <- oneOf ['a'..'z']
331+ return (LowerAlpha, ord ch - ord 'a' + 1)
332+
333+-- | Parses an uppercase letter and returns (UpperAlpha, number).
334+upperAlpha :: GenParser Char st (ListNumberStyle, Int)
335+upperAlpha = do
336+ ch <- oneOf ['A'..'Z']
337+ return (UpperAlpha, ord ch - ord 'A' + 1)
338+
339+-- | Parses a roman numeral i or I
340+romanOne :: GenParser Char st (ListNumberStyle, Int)
341+romanOne = (char 'i' >> return (LowerRoman, 1)) <|>
342+ (char 'I' >> return (UpperRoman, 1))
343+
344+-- | Parses an ordered list marker and returns list attributes.
345+anyOrderedListMarker :: GenParser Char st ListAttributes
346+anyOrderedListMarker = choice $
347+ [delimParser numParser | delimParser <- [inPeriod, inOneParen, inTwoParens],
348+ numParser <- [decimal, defaultNum, romanOne,
349+ lowerAlpha, lowerRoman, upperAlpha, upperRoman]]
350+
351+-- | Parses a list number (num) followed by a period, returns list attributes.
352+inPeriod :: GenParser Char st (ListNumberStyle, Int)
353+ -> GenParser Char st ListAttributes
354+inPeriod num = try $ do
355+ (style, start) <- num
356+ char '.'
357+ let delim = if style == DefaultStyle
358+ then DefaultDelim
359+ else Period
360+ return (start, style, delim)
361+
362+-- | Parses a list number (num) followed by a paren, returns list attributes.
363+inOneParen :: GenParser Char st (ListNumberStyle, Int)
364+ -> GenParser Char st ListAttributes
365+inOneParen num = try $ do
366+ (style, start) <- num
367+ char ')'
368+ return (start, style, OneParen)
369+
370+-- | Parses a list number (num) enclosed in parens, returns list attributes.
371+inTwoParens :: GenParser Char st (ListNumberStyle, Int)
372+ -> GenParser Char st ListAttributes
373+inTwoParens num = try $ do
374+ char '('
375+ (style, start) <- num
376+ char ')'
377+ return (start, style, TwoParens)
378+
379+-- | Parses an ordered list marker with a given style and delimiter,
380+-- returns number.
381+orderedListMarker :: ListNumberStyle
382+ -> ListNumberDelim
383+ -> GenParser Char st Int
384+orderedListMarker style delim = do
385+ let num = defaultNum <|> -- # can continue any kind of list
386+ case style of
387+ DefaultStyle -> decimal
388+ Decimal -> decimal
389+ UpperRoman -> upperRoman
390+ LowerRoman -> lowerRoman
391+ UpperAlpha -> upperAlpha
392+ LowerAlpha -> lowerAlpha
393+ let context = case delim of
394+ DefaultDelim -> inPeriod
395+ Period -> inPeriod
396+ OneParen -> inOneParen
397+ TwoParens -> inTwoParens
398+ (start, _, _) <- context num
399+ return start
400+
401+-- | Parses a character reference and returns a Str element.
402+charRef :: GenParser Char st Inline
403+charRef = do
404+ c <- characterReference
405+ return $ Str [c]
406+
407+-- | Parse a string with a given parser and state.
408+readWith :: GenParser Char ParserState a -- ^ parser
409+ -> ParserState -- ^ initial state
410+ -> String -- ^ input string
411+ -> a
412+readWith parser state input =
413+ case runParser parser state "source" input of
414+ Left err -> error $ "\nError:\n" ++ show err
415+ Right result -> result
416+
417+-- | Parse a string with @parser@ (for testing).
418+testStringWith :: (Show a) => GenParser Char ParserState a
419+ -> String
420+ -> IO ()
421+testStringWith parser str = UTF8.putStrLn $ show $
422+ readWith parser defaultParserState str
423+
424+-- | Parsing options.
425+data ParserState = ParserState
426+ { stateParseRaw :: Bool, -- ^ Parse raw HTML and LaTeX?
427+ stateParserContext :: ParserContext, -- ^ Inside list?
428+ stateQuoteContext :: QuoteContext, -- ^ Inside quoted environment?
429+ stateSanitizeHTML :: Bool, -- ^ Sanitize HTML?
430+ stateKeys :: KeyTable, -- ^ List of reference keys
431+#ifdef _CITEPROC
432+ stateCitations :: [String], -- ^ List of available citations
433+#endif
434+ stateNotes :: NoteTable, -- ^ List of notes
435+ stateTabStop :: Int, -- ^ Tab stop
436+ stateStandalone :: Bool, -- ^ Parse bibliographic info?
437+ stateTitle :: [Inline], -- ^ Title of document
438+ stateAuthors :: [[Inline]], -- ^ Authors of document
439+ stateDate :: [Inline], -- ^ Date of document
440+ stateStrict :: Bool, -- ^ Use strict markdown syntax?
441+ stateSmart :: Bool, -- ^ Use smart typography?
442+ stateLiterateHaskell :: Bool, -- ^ Treat input as literate haskell
443+ stateColumns :: Int, -- ^ Number of columns in terminal
444+ stateHeaderTable :: [HeaderType], -- ^ Ordered list of header types used
445+ stateIndentedCodeClasses :: [String] -- ^ Classes to use for indented code blocks
446+ }
447+ deriving Show
448+
449+defaultParserState :: ParserState
450+defaultParserState =
451+ ParserState { stateParseRaw = False,
452+ stateParserContext = NullState,
453+ stateQuoteContext = NoQuote,
454+ stateSanitizeHTML = False,
455+ stateKeys = M.empty,
456+#ifdef _CITEPROC
457+ stateCitations = [],
458+#endif
459+ stateNotes = [],
460+ stateTabStop = 4,
461+ stateStandalone = False,
462+ stateTitle = [],
463+ stateAuthors = [],
464+ stateDate = [],
465+ stateStrict = False,
466+ stateSmart = False,
467+ stateLiterateHaskell = False,
468+ stateColumns = 80,
469+ stateHeaderTable = [],
470+ stateIndentedCodeClasses = [] }
471+
472+data HeaderType
473+ = SingleHeader Char -- ^ Single line of characters underneath
474+ | DoubleHeader Char -- ^ Lines of characters above and below
475+ deriving (Eq, Show)
476+
477+data ParserContext
478+ = ListItemState -- ^ Used when running parser on list item contents
479+ | NullState -- ^ Default state
480+ deriving (Eq, Show)
481+
482+data QuoteContext
483+ = InSingleQuote -- ^ Used when parsing inside single quotes
484+ | InDoubleQuote -- ^ Used when parsing inside double quotes
485+ | NoQuote -- ^ Used when not parsing inside quotes
486+ deriving (Eq, Show)
487+
488+type NoteTable = [(String, String)]
489+
490+newtype Key = Key [Inline] deriving (Show, Read)
491+
492+instance Eq Key where
493+ Key a == Key b = refsMatch a b
494+
495+instance Ord Key where
496+ compare (Key a) (Key b) = if a == b then EQ else compare a b
497+
498+type KeyTable = M.Map Key Target
499+
500+-- | Look up key in key table and return target object.
501+lookupKeySrc :: KeyTable -- ^ Key table
502+ -> Key -- ^ Key
503+ -> Maybe Target
504+lookupKeySrc table key = case M.lookup key table of
505+ Nothing -> Nothing
506+ Just src -> Just src
507+
508+-- | Returns @True@ if keys match (case insensitive).
509+refsMatch :: [Inline] -> [Inline] -> Bool
510+refsMatch ((Str x):restx) ((Str y):resty) =
511+ ((map toLower x) == (map toLower y)) && refsMatch restx resty
512+refsMatch ((Emph x):restx) ((Emph y):resty) =
513+ refsMatch x y && refsMatch restx resty
514+refsMatch ((Strong x):restx) ((Strong y):resty) =
515+ refsMatch x y && refsMatch restx resty
516+refsMatch ((Strikeout x):restx) ((Strikeout y):resty) =
517+ refsMatch x y && refsMatch restx resty
518+refsMatch ((Superscript x):restx) ((Superscript y):resty) =
519+ refsMatch x y && refsMatch restx resty
520+refsMatch ((Subscript x):restx) ((Subscript y):resty) =
521+ refsMatch x y && refsMatch restx resty
522+refsMatch ((SmallCaps x):restx) ((SmallCaps y):resty) =
523+ refsMatch x y && refsMatch restx resty
524+refsMatch ((Quoted t x):restx) ((Quoted u y):resty) =
525+ t == u && refsMatch x y && refsMatch restx resty
526+refsMatch ((Code x):restx) ((Code y):resty) =
527+ ((map toLower x) == (map toLower y)) && refsMatch restx resty
528+refsMatch ((Math t x):restx) ((Math u y):resty) =
529+ ((map toLower x) == (map toLower y)) && t == u && refsMatch restx resty
530+refsMatch ((TeX x):restx) ((TeX y):resty) =
531+ ((map toLower x) == (map toLower y)) && refsMatch restx resty
532+refsMatch ((HtmlInline x):restx) ((HtmlInline y):resty) =
533+ ((map toLower x) == (map toLower y)) && refsMatch restx resty
534+refsMatch (x:restx) (y:resty) = (x == y) && refsMatch restx resty
535+refsMatch [] x = null x
536+refsMatch x [] = null x
537+
src/Text/Pandoc/Readers/HTML.hs+2−1
@@ -44,7 +44,8 @@ module Text.Pandoc.Readers.HTML (
44
44
45import Text.ParserCombinators.Parsec
45import Text.ParserCombinators.Parsec
46import Text.Pandoc.Definition
46import Text.Pandoc.Definition
47−import Text.Pandoc.Shared
47+import Text.Pandoc.Shared
48+import Text.Pandoc.Parsing
48import Text.Pandoc.CharacterReferences ( decodeCharacterReferences )
49import Text.Pandoc.CharacterReferences ( decodeCharacterReferences )
49import Data.Maybe ( fromMaybe )
50import Data.Maybe ( fromMaybe )
50import Data.List ( isPrefixOf, isSuffixOf, intercalate )
51import Data.List ( isPrefixOf, isSuffixOf, intercalate )
src/Text/Pandoc/Readers/LaTeX.hs+2−1
@@ -35,7 +35,8 @@ module Text.Pandoc.Readers.LaTeX (
35
35
36import Text.ParserCombinators.Parsec
36import Text.ParserCombinators.Parsec
37import Text.Pandoc.Definition
37import Text.Pandoc.Definition
38−import Text.Pandoc.Shared
38+import Text.Pandoc.Shared
39+import Text.Pandoc.Parsing
39import Data.Maybe ( fromMaybe )
40import Data.Maybe ( fromMaybe )
40import Data.Char ( chr )
41import Data.Char ( chr )
41import Data.List ( isPrefixOf, isSuffixOf )
42import Data.List ( isPrefixOf, isSuffixOf )
src/Text/Pandoc/Readers/Markdown.hs+2−1
@@ -37,7 +37,8 @@ import Data.Ord ( comparing )
37import Data.Char ( isAlphaNum )
37import Data.Char ( isAlphaNum )
38import Data.Maybe
38import Data.Maybe
39import Text.Pandoc.Definition
39import Text.Pandoc.Definition
40−import Text.Pandoc.Shared
40+import Text.Pandoc.Shared
41+import Text.Pandoc.Parsing
41import Text.Pandoc.Readers.LaTeX ( rawLaTeXInline, rawLaTeXEnvironment' )
42import Text.Pandoc.Readers.LaTeX ( rawLaTeXInline, rawLaTeXEnvironment' )
42import Text.Pandoc.Readers.HTML ( rawHtmlBlock, anyHtmlBlockTag,
43import Text.Pandoc.Readers.HTML ( rawHtmlBlock, anyHtmlBlockTag,
43 anyHtmlInlineTag, anyHtmlTag,
44 anyHtmlInlineTag, anyHtmlTag,
src/Text/Pandoc/Readers/RST.hs+2−1
@@ -31,7 +31,8 @@ module Text.Pandoc.Readers.RST (
31 readRST
31 readRST
32 ) where
32 ) where
33import Text.Pandoc.Definition
33import Text.Pandoc.Definition
34−import Text.Pandoc.Shared
34+import Text.Pandoc.Shared
35+import Text.Pandoc.Parsing
35import Text.ParserCombinators.Parsec
36import Text.ParserCombinators.Parsec
36import Control.Monad ( when, unless, liftM )
37import Control.Monad ( when, unless, liftM )
37import Data.List ( findIndex, intercalate, transpose, sort )
38import Data.List ( findIndex, intercalate, transpose, sort )
src/Text/Pandoc/Shared.hs+3−507
@@ -52,45 +52,6 @@ module Text.Pandoc.Shared (
52 BlockWrapper (..),
52 BlockWrapper (..),
53 wrappedBlocksToDoc,
53 wrappedBlocksToDoc,
54 tabFilter,
54 tabFilter,
55− -- * Parsing
56− (>>~),
57− anyLine,
58− many1Till,
59− notFollowedBy',
60− oneOfStrings,
61− spaceChar,
62− skipSpaces,
63− blankline,
64− blanklines,
65− enclosed,
66− stringAnyCase,
67− parseFromString,
68− lineClump,
69− charsInBalanced,
70− charsInBalanced',
71− romanNumeral,
72− emailAddress,
73− uri,
74− withHorizDisplacement,
75− nullBlock,
76− failIfStrict,
77− failUnlessLHS,
78− escaped,
79− anyOrderedListMarker,
80− orderedListMarker,
81− charRef,
82− readWith,
83− testStringWith,
84− ParserState (..),
85− defaultParserState,
86− HeaderType (..),
87− ParserContext (..),
88− QuoteContext (..),
89− NoteTable,
90− KeyTable,
91− Key (..),
92− lookupKeySrc,
93− refsMatch,
94 -- * Prettyprinting
55 -- * Prettyprinting
95 hang',
56 hang',
96 prettyPandoc,
57 prettyPandoc,
@@ -113,22 +74,18 @@ module Text.Pandoc.Shared (
113 ) where
74 ) where
114
75
115import Text.Pandoc.Definition
76import Text.Pandoc.Definition
116−import qualified Text.Pandoc.UTF8 as UTF8 (readFile, putStrLn)
77+import qualified Text.Pandoc.UTF8 as UTF8 (readFile)
117−import Text.ParserCombinators.Parsec
118import Text.PrettyPrint.HughesPJ ( Doc, fsep, ($$), (<>), empty, isEmpty, text, nest )
78import Text.PrettyPrint.HughesPJ ( Doc, fsep, ($$), (<>), empty, isEmpty, text, nest )
119import qualified Text.PrettyPrint.HughesPJ as PP
79import qualified Text.PrettyPrint.HughesPJ as PP
120−import Text.Pandoc.CharacterReferences ( characterReference )
121−import Data.Char ( toLower, toUpper, ord, isLower, isUpper, isAlpha, isAscii,
80+import Data.Char ( toLower, isLower, isUpper, isAlpha, isAscii,
122 isLetter, isDigit )
81 isLetter, isDigit )
123import Data.List ( find, isPrefixOf, intercalate )
82import Data.List ( find, isPrefixOf, intercalate )
124−import Network.URI ( parseURI, URI (..), isAllowedInURI, escapeURIString, unEscapeString )
83+import Network.URI ( isAllowedInURI, escapeURIString, unEscapeString )
125import Codec.Binary.UTF8.String ( encodeString, decodeString )
84import Codec.Binary.UTF8.String ( encodeString, decodeString )
126import System.Directory
85import System.Directory
127import System.FilePath ( (</>) )
86import System.FilePath ( (</>) )
128import Data.Generics (Typeable, Data)
87import Data.Generics (Typeable, Data)
129import qualified Control.Monad.State as S
88import qualified Control.Monad.State as S
130−import Control.Monad (join)
131−import qualified Data.Map as M
132import Paths_pandoc (getDataFileName)
89import Paths_pandoc (getDataFileName)
133
90
134--
91--
@@ -328,467 +285,6 @@ tabFilter tabStop =
328 x : go (spsToNextStop - 1) xs
285 x : go (spsToNextStop - 1) xs
329 in go tabStop
286 in go tabStop
330
287
331−--
332−-- Parsing
333−--
334−
335−-- | Like >>, but returns the operation on the left.
336−-- (Suggested by Tillmann Rendel on Haskell-cafe list.)
337−(>>~) :: (Monad m) => m a -> m b -> m a
338−a >>~ b = a >>= \x -> b >> return x
339−
340−-- | Parse any line of text
341−anyLine :: GenParser Char st [Char]
342−anyLine = manyTill anyChar newline
343−
344−-- | Like @manyTill@, but reads at least one item.
345−many1Till :: GenParser tok st a
346− -> GenParser tok st end
347− -> GenParser tok st [a]
348−many1Till p end = do
349− first <- p
350− rest <- manyTill p end
351− return (first:rest)
352−
353−-- | A more general form of @notFollowedBy@. This one allows any
354−-- type of parser to be specified, and succeeds only if that parser fails.
355−-- It does not consume any input.
356−notFollowedBy' :: Show b => GenParser a st b -> GenParser a st ()
357−notFollowedBy' p = try $ join $ do a <- try p
358− return (unexpected (show a))
359− <|>
360− return (return ())
361−-- (This version due to Andrew Pimlott on the Haskell mailing list.)
362−
363−-- | Parses one of a list of strings (tried in order).
364−oneOfStrings :: [String] -> GenParser Char st String
365−oneOfStrings listOfStrings = choice $ map (try . string) listOfStrings
366−
367−-- | Parses a space or tab.
368−spaceChar :: CharParser st Char
369−spaceChar = char ' ' <|> char '\t'
370−
371−-- | Skips zero or more spaces or tabs.
372−skipSpaces :: GenParser Char st ()
373−skipSpaces = skipMany spaceChar
374−
375−-- | Skips zero or more spaces or tabs, then reads a newline.
376−blankline :: GenParser Char st Char
377−blankline = try $ skipSpaces >> newline
378−
379−-- | Parses one or more blank lines and returns a string of newlines.
380−blanklines :: GenParser Char st [Char]
381−blanklines = many1 blankline
382−
383−-- | Parses material enclosed between start and end parsers.
384−enclosed :: GenParser Char st t -- ^ start parser
385− -> GenParser Char st end -- ^ end parser
386− -> GenParser Char st a -- ^ content parser (to be used repeatedly)
387− -> GenParser Char st [a]
388−enclosed start end parser = try $
389− start >> notFollowedBy space >> many1Till parser end
390−
391−-- | Parse string, case insensitive.
392−stringAnyCase :: [Char] -> CharParser st String
393−stringAnyCase [] = string ""
394−stringAnyCase (x:xs) = do
395− firstChar <- char (toUpper x) <|> char (toLower x)
396− rest <- stringAnyCase xs
397− return (firstChar:rest)
398−
399−-- | Parse contents of 'str' using 'parser' and return result.
400−parseFromString :: GenParser tok st a -> [tok] -> GenParser tok st a
401−parseFromString parser str = do
402− oldPos <- getPosition
403− oldInput <- getInput
404− setInput str
405− result <- parser
406− setInput oldInput
407− setPosition oldPos
408− return result
409−
410−-- | Parse raw line block up to and including blank lines.
411−lineClump :: GenParser Char st String
412−lineClump = blanklines
413− <|> (many1 (notFollowedBy blankline >> anyLine) >>= return . unlines)
414−
415−-- | Parse a string of characters between an open character
416−-- and a close character, including text between balanced
417−-- pairs of open and close, which must be different. For example,
418−-- @charsInBalanced '(' ')'@ will parse "(hello (there))"
419−-- and return "hello (there)". Stop if a blank line is
420−-- encountered.
421−charsInBalanced :: Char -> Char -> GenParser Char st String
422−charsInBalanced open close = try $ do
423− char open
424− raw <- many $ (many1 (noneOf [open, close, '\n']))
425− <|> (do res <- charsInBalanced open close
426− return $ [open] ++ res ++ [close])
427− <|> try (string "\n" >>~ notFollowedBy' blanklines)
428− char close
429− return $ concat raw
430−
431−-- | Like @charsInBalanced@, but allow blank lines in the content.
432−charsInBalanced' :: Char -> Char -> GenParser Char st String
433−charsInBalanced' open close = try $ do
434− char open
435− raw <- many $ (many1 (noneOf [open, close]))
436− <|> (do res <- charsInBalanced' open close
437− return $ [open] ++ res ++ [close])
438− char close
439− return $ concat raw
440−
441−-- Auxiliary functions for romanNumeral:
442−
443−lowercaseRomanDigits :: [Char]
444−lowercaseRomanDigits = ['i','v','x','l','c','d','m']
445−
446−uppercaseRomanDigits :: [Char]
447−uppercaseRomanDigits = map toUpper lowercaseRomanDigits
448−
449−-- | Parses a roman numeral (uppercase or lowercase), returns number.
450−romanNumeral :: Bool -- ^ Uppercase if true
451− -> GenParser Char st Int
452−romanNumeral upperCase = do
453− let romanDigits = if upperCase
454− then uppercaseRomanDigits
455− else lowercaseRomanDigits
456− lookAhead $ oneOf romanDigits
457− let [one, five, ten, fifty, hundred, fivehundred, thousand] =
458− map char romanDigits
459− thousands <- many thousand >>= (return . (1000 *) . length)
460− ninehundreds <- option 0 $ try $ hundred >> thousand >> return 900
461− fivehundreds <- many fivehundred >>= (return . (500 *) . length)
462− fourhundreds <- option 0 $ try $ hundred >> fivehundred >> return 400
463− hundreds <- many hundred >>= (return . (100 *) . length)
464− nineties <- option 0 $ try $ ten >> hundred >> return 90
465− fifties <- many fifty >>= (return . (50 *) . length)
466− forties <- option 0 $ try $ ten >> fifty >> return 40
467− tens <- many ten >>= (return . (10 *) . length)
468− nines <- option 0 $ try $ one >> ten >> return 9
469− fives <- many five >>= (return . (5 *) . length)
470− fours <- option 0 $ try $ one >> five >> return 4
471− ones <- many one >>= (return . length)
472− let total = thousands + ninehundreds + fivehundreds + fourhundreds +
473− hundreds + nineties + fifties + forties + tens + nines +
474− fives + fours + ones
475− if total == 0
476− then fail "not a roman numeral"
477− else return total
478−
479−-- Parsers for email addresses and URIs
480−
481−emailChar :: GenParser Char st Char
482−emailChar = alphaNum <|> oneOf "-+_."
483−
484−domainChar :: GenParser Char st Char
485−domainChar = alphaNum <|> char '-'
486−
487−domain :: GenParser Char st [Char]
488−domain = do
489− first <- many1 domainChar
490− dom <- many1 $ try (char '.' >> many1 domainChar )
491− return $ intercalate "." (first:dom)
492−
493−-- | Parses an email address; returns original and corresponding
494−-- escaped mailto: URI.
495−emailAddress :: GenParser Char st (String, String)
496−emailAddress = try $ do
497− firstLetter <- alphaNum
498− restAddr <- many emailChar
499− let addr = firstLetter:restAddr
500− char '@'
501− dom <- domain
502− let full = addr ++ '@':dom
503− return (full, escapeURI $ "mailto:" ++ full)
504−
505−-- | Parses a URI. Returns pair of original and URI-escaped version.
506−uri :: GenParser Char st (String, String)
507−uri = try $ do
508− let protocols = [ "http:", "https:", "ftp:", "file:", "mailto:",
509− "news:", "telnet:" ]
510− lookAhead $ oneOfStrings protocols
511− -- scan non-ascii characters and ascii characters allowed in a URI
512− str <- many1 $ satisfy (\c -> not (isAscii c) || isAllowedInURI c)
513− -- now see if they amount to an absolute URI
514− case parseURI (escapeURI str) of
515− Just uri' -> if uriScheme uri' `elem` protocols
516− then return (str, show uri')
517− else fail "not a URI"
518− Nothing -> fail "not a URI"
519−
520−-- | Applies a parser, returns tuple of its results and its horizontal
521−-- displacement (the difference between the source column at the end
522−-- and the source column at the beginning). Vertical displacement
523−-- (source row) is ignored.
524−withHorizDisplacement :: GenParser Char st a -- ^ Parser to apply
525− -> GenParser Char st (a, Int) -- ^ (result, displacement)
526−withHorizDisplacement parser = do
527− pos1 <- getPosition
528− result <- parser
529− pos2 <- getPosition
530− return (result, sourceColumn pos2 - sourceColumn pos1)
531−
532−-- | Parses a character and returns 'Null' (so that the parser can move on
533−-- if it gets stuck).
534−nullBlock :: GenParser Char st Block
535−nullBlock = anyChar >> return Null
536−
537−-- | Fail if reader is in strict markdown syntax mode.
538−failIfStrict :: GenParser Char ParserState ()
539−failIfStrict = do
540− state <- getState
541− if stateStrict state then fail "strict mode" else return ()
542−
543−-- | Fail unless we're in literate haskell mode.
544−failUnlessLHS :: GenParser tok ParserState ()
545−failUnlessLHS = do
546− state <- getState
547− if stateLiterateHaskell state then return () else fail "Literate haskell feature"
548−
549−-- | Parses backslash, then applies character parser.
550−escaped :: GenParser Char st Char -- ^ Parser for character to escape
551− -> GenParser Char st Inline
552−escaped parser = try $ do
553− char '\\'
554− result <- parser
555− return (Str [result])
556−
557−-- | Parses an uppercase roman numeral and returns (UpperRoman, number).
558−upperRoman :: GenParser Char st (ListNumberStyle, Int)
559−upperRoman = do
560− num <- romanNumeral True
561− return (UpperRoman, num)
562−
563−-- | Parses a lowercase roman numeral and returns (LowerRoman, number).
564−lowerRoman :: GenParser Char st (ListNumberStyle, Int)
565−lowerRoman = do
566− num <- romanNumeral False
567− return (LowerRoman, num)
568−
569−-- | Parses a decimal numeral and returns (Decimal, number).
570−decimal :: GenParser Char st (ListNumberStyle, Int)
571−decimal = do
572− num <- many1 digit
573− return (Decimal, read num)
574−
575−-- | Parses a '#' returns (DefaultStyle, 1).
576−defaultNum :: GenParser Char st (ListNumberStyle, Int)
577−defaultNum = do
578− char '#'
579− return (DefaultStyle, 1)
580−
581−-- | Parses a lowercase letter and returns (LowerAlpha, number).
582−lowerAlpha :: GenParser Char st (ListNumberStyle, Int)
583−lowerAlpha = do
584− ch <- oneOf ['a'..'z']
585− return (LowerAlpha, ord ch - ord 'a' + 1)
586−
587−-- | Parses an uppercase letter and returns (UpperAlpha, number).
588−upperAlpha :: GenParser Char st (ListNumberStyle, Int)
589−upperAlpha = do
590− ch <- oneOf ['A'..'Z']
591− return (UpperAlpha, ord ch - ord 'A' + 1)
592−
593−-- | Parses a roman numeral i or I
594−romanOne :: GenParser Char st (ListNumberStyle, Int)
595−romanOne = (char 'i' >> return (LowerRoman, 1)) <|>
596− (char 'I' >> return (UpperRoman, 1))
597−
598−-- | Parses an ordered list marker and returns list attributes.
599−anyOrderedListMarker :: GenParser Char st ListAttributes
600−anyOrderedListMarker = choice $
601− [delimParser numParser | delimParser <- [inPeriod, inOneParen, inTwoParens],
602− numParser <- [decimal, defaultNum, romanOne,
603− lowerAlpha, lowerRoman, upperAlpha, upperRoman]]
604−
605−-- | Parses a list number (num) followed by a period, returns list attributes.
606−inPeriod :: GenParser Char st (ListNumberStyle, Int)
607− -> GenParser Char st ListAttributes
608−inPeriod num = try $ do
609− (style, start) <- num
610− char '.'
611− let delim = if style == DefaultStyle
612− then DefaultDelim
613− else Period
614− return (start, style, delim)
615−
616−-- | Parses a list number (num) followed by a paren, returns list attributes.
617−inOneParen :: GenParser Char st (ListNumberStyle, Int)
618− -> GenParser Char st ListAttributes
619−inOneParen num = try $ do
620− (style, start) <- num
621− char ')'
622− return (start, style, OneParen)
623−
624−-- | Parses a list number (num) enclosed in parens, returns list attributes.
625−inTwoParens :: GenParser Char st (ListNumberStyle, Int)
626− -> GenParser Char st ListAttributes
627−inTwoParens num = try $ do
628− char '('
629− (style, start) <- num
630− char ')'
631− return (start, style, TwoParens)
632−
633−-- | Parses an ordered list marker with a given style and delimiter,
634−-- returns number.
635−orderedListMarker :: ListNumberStyle
636− -> ListNumberDelim
637− -> GenParser Char st Int
638−orderedListMarker style delim = do
639− let num = defaultNum <|> -- # can continue any kind of list
640− case style of
641− DefaultStyle -> decimal
642− Decimal -> decimal
643− UpperRoman -> upperRoman
644− LowerRoman -> lowerRoman
645− UpperAlpha -> upperAlpha
646− LowerAlpha -> lowerAlpha
647− let context = case delim of
648− DefaultDelim -> inPeriod
649− Period -> inPeriod
650− OneParen -> inOneParen
651− TwoParens -> inTwoParens
652− (start, _, _) <- context num
653− return start
654−
655−-- | Parses a character reference and returns a Str element.
656−charRef :: GenParser Char st Inline
657−charRef = do
658− c <- characterReference
659− return $ Str [c]
660−
661−-- | Parse a string with a given parser and state.
662−readWith :: GenParser Char ParserState a -- ^ parser
663− -> ParserState -- ^ initial state
664− -> String -- ^ input string
665− -> a
666−readWith parser state input =
667− case runParser parser state "source" input of
668− Left err -> error $ "\nError:\n" ++ show err
669− Right result -> result
670−
671−-- | Parse a string with @parser@ (for testing).
672−testStringWith :: (Show a) => GenParser Char ParserState a
673− -> String
674− -> IO ()
675−testStringWith parser str = UTF8.putStrLn $ show $
676− readWith parser defaultParserState str
677−
678−-- | Parsing options.
679−data ParserState = ParserState
680− { stateParseRaw :: Bool, -- ^ Parse raw HTML and LaTeX?
681− stateParserContext :: ParserContext, -- ^ Inside list?
682− stateQuoteContext :: QuoteContext, -- ^ Inside quoted environment?
683− stateSanitizeHTML :: Bool, -- ^ Sanitize HTML?
684− stateKeys :: KeyTable, -- ^ List of reference keys
685−#ifdef _CITEPROC
686− stateCitations :: [String], -- ^ List of available citations
687−#endif
688− stateNotes :: NoteTable, -- ^ List of notes
689− stateTabStop :: Int, -- ^ Tab stop
690− stateStandalone :: Bool, -- ^ Parse bibliographic info?
691− stateTitle :: [Inline], -- ^ Title of document
692− stateAuthors :: [[Inline]], -- ^ Authors of document
693− stateDate :: [Inline], -- ^ Date of document
694− stateStrict :: Bool, -- ^ Use strict markdown syntax?
695− stateSmart :: Bool, -- ^ Use smart typography?
696− stateLiterateHaskell :: Bool, -- ^ Treat input as literate haskell
697− stateColumns :: Int, -- ^ Number of columns in terminal
698− stateHeaderTable :: [HeaderType], -- ^ Ordered list of header types used
699− stateIndentedCodeClasses :: [String] -- ^ Classes to use for indented code blocks
700− }
701− deriving Show
702−
703−defaultParserState :: ParserState
704−defaultParserState =
705− ParserState { stateParseRaw = False,
706− stateParserContext = NullState,
707− stateQuoteContext = NoQuote,
708− stateSanitizeHTML = False,
709− stateKeys = M.empty,
710−#ifdef _CITEPROC
711− stateCitations = [],
712−#endif
713− stateNotes = [],
714− stateTabStop = 4,
715− stateStandalone = False,
716− stateTitle = [],
717− stateAuthors = [],
718− stateDate = [],
719− stateStrict = False,
720− stateSmart = False,
721− stateLiterateHaskell = False,
722− stateColumns = 80,
723− stateHeaderTable = [],
724− stateIndentedCodeClasses = [] }
725−
726−data HeaderType
727− = SingleHeader Char -- ^ Single line of characters underneath
728− | DoubleHeader Char -- ^ Lines of characters above and below
729− deriving (Eq, Show)
730−
731−data ParserContext
732− = ListItemState -- ^ Used when running parser on list item contents
733− | NullState -- ^ Default state
734− deriving (Eq, Show)
735−
736−data QuoteContext
737− = InSingleQuote -- ^ Used when parsing inside single quotes
738− | InDoubleQuote -- ^ Used when parsing inside double quotes
739− | NoQuote -- ^ Used when not parsing inside quotes
740− deriving (Eq, Show)
741−
742−type NoteTable = [(String, String)]
743−
744−newtype Key = Key [Inline] deriving (Show, Read)
745−
746−instance Eq Key where
747− Key a == Key b = refsMatch a b
748−
749−instance Ord Key where
750− compare (Key a) (Key b) = if a == b then EQ else compare a b
751−
752−type KeyTable = M.Map Key Target
753−
754−-- | Look up key in key table and return target object.
755−lookupKeySrc :: KeyTable -- ^ Key table
756− -> Key -- ^ Key
757− -> Maybe Target
758−lookupKeySrc table key = case M.lookup key table of
759− Nothing -> Nothing
760− Just src -> Just src
761−
762−-- | Returns @True@ if keys match (case insensitive).
763−refsMatch :: [Inline] -> [Inline] -> Bool
764−refsMatch ((Str x):restx) ((Str y):resty) =
765− ((map toLower x) == (map toLower y)) && refsMatch restx resty
766−refsMatch ((Emph x):restx) ((Emph y):resty) =
767− refsMatch x y && refsMatch restx resty
768−refsMatch ((Strong x):restx) ((Strong y):resty) =
769− refsMatch x y && refsMatch restx resty
770−refsMatch ((Strikeout x):restx) ((Strikeout y):resty) =
771− refsMatch x y && refsMatch restx resty
772−refsMatch ((Superscript x):restx) ((Superscript y):resty) =
773− refsMatch x y && refsMatch restx resty
774−refsMatch ((Subscript x):restx) ((Subscript y):resty) =
775− refsMatch x y && refsMatch restx resty
776−refsMatch ((SmallCaps x):restx) ((SmallCaps y):resty) =
777− refsMatch x y && refsMatch restx resty
778−refsMatch ((Quoted t x):restx) ((Quoted u y):resty) =
779− t == u && refsMatch x y && refsMatch restx resty
780−refsMatch ((Code x):restx) ((Code y):resty) =
781− ((map toLower x) == (map toLower y)) && refsMatch restx resty
782−refsMatch ((Math t x):restx) ((Math u y):resty) =
783− ((map toLower x) == (map toLower y)) && t == u && refsMatch restx resty
784−refsMatch ((TeX x):restx) ((TeX y):resty) =
785− ((map toLower x) == (map toLower y)) && refsMatch restx resty
786−refsMatch ((HtmlInline x):restx) ((HtmlInline y):resty) =
787− ((map toLower x) == (map toLower y)) && refsMatch restx resty
788−refsMatch (x:restx) (y:resty) = (x == y) && refsMatch restx resty
789−refsMatch [] x = null x
790−refsMatch x [] = null x
791−
792--
288--
793-- Prettyprinting
289-- Prettyprinting
794--
290--
src/Text/Pandoc/Writers/Markdown.hs+2−1
@@ -32,7 +32,8 @@ Markdown: <http://daringfireball.net/projects/markdown/>
32module Text.Pandoc.Writers.Markdown (writeMarkdown, writePlain) where
32module Text.Pandoc.Writers.Markdown (writeMarkdown, writePlain) where
33import Text.Pandoc.Definition
33import Text.Pandoc.Definition
34import Text.Pandoc.Templates (renderTemplate)
34import Text.Pandoc.Templates (renderTemplate)
35−import Text.Pandoc.Shared
35+import Text.Pandoc.Shared
36+import Text.Pandoc.Parsing
36import Text.Pandoc.Blocks
37import Text.Pandoc.Blocks
37import Text.ParserCombinators.Parsec ( parse, GenParser )
38import Text.ParserCombinators.Parsec ( parse, GenParser )
38import Data.List ( group, isPrefixOf, find, intersperse, transpose )
39import Data.List ( group, isPrefixOf, find, intersperse, transpose )