@@ -36,8 +36,11 @@ import Text.ParserCombinators.Pandoc
3636import Text.Pandoc.Definition
3737import Text.Pandoc.Readers.LaTeX ( rawLaTeXInline, rawLaTeXEnvironment )
3838import Text.Pandoc.Shared
39−import Text.Pandoc.Readers.HTML ( rawHtmlInline, rawHtmlBlock,
40− anyHtmlBlockTag, anyHtmlInlineTag )
39+import Text.Pandoc.Readers.HTML ( rawHtmlBlock,
40+ anyHtmlBlockTag, anyHtmlInlineTag,
41+ anyHtmlTag, anyHtmlEndTag,
42+ htmlEndTag, extractTagType,
43+ htmlBlockElement )
4144import Text.Pandoc.HtmlEntities ( decodeEntities )
4245import Text.Regex ( matchRegex, mkRegex )
4346import Text.ParserCombinators.Parsec
@@ -107,6 +110,16 @@ skipNonindentSpaces = do
107110 let tabStop = stateTabStop state
108111 choice (map (\n -> (try (count n (char ' ')))) (reverse [0..(tabStop - 1)]))
109112
113+-- | Fail if reader is in strict markdown syntax mode.
114+failIfStrict = do
115+ state <- getState
116+ if stateStrict state then fail "Strict markdown mode" else return ()
117+
118+-- | Fail unless we're at beginning of a line.
119+failUnlessBeginningOfLine = do
120+ pos <- getPosition
121+ if sourceColumn pos == 1 then return () else fail "not beginning of line"
122+
110123--
111124-- document structure
112125--
@@ -132,6 +145,7 @@ dateLine = try (do
132145 return (removeTrailingSpace date))
133146
134147titleBlock = try (do
148+ failIfStrict
135149 title <- option [] titleLine
136150 author <- option [] authorsLine
137151 date <- option "" dateLine
@@ -147,7 +161,14 @@ parseMarkdown = do
147161 updateState (\state -> state { stateParseRaw = True })
148162 -- need to parse raw HTML, since markdown allows it
149163 (title, author, date) <- option ([],[],"") titleBlock
150− blocks <- parseBlocks
164+ oldState <- getState
165+ oldInput <- getInput
166+ parseBlocks -- go through once just to get list of reference keys
167+ newState <- getState
168+ let keysUsed = stateKeysUsed newState
169+ setInput oldInput
170+ setState (oldState { stateKeysUsed = keysUsed })
171+ blocks <- parseBlocks -- go through again, for real
151172 let blocks' = filter (/= Null) blocks
152173 state <- getState
153174 let keys = reverse $ stateKeyBlocks state
@@ -165,7 +186,7 @@ parseBlocks = do
165186 return result
166187
167188block = choice [ codeBlock, note, referenceKey, header, hrule, list,
168− blockQuote, rawHtmlBlocks, rawLaTeXEnvironment, para,
189+ blockQuote, htmlBlock, rawLaTeXEnvironment', para,
169190 plain, blankBlock, nullBlock ] <?> "block"
170191
171192--
@@ -190,8 +211,7 @@ setextHeader = choice $
190211 map (\x -> setextH x) (enumFromTo 1 (length setextHChars))
191212
192213setextH n = try (do
193− txt <- many1 (do {notFollowedBy newline; inline})
194− endline
214+ txt <- many1Till inline newline
195215 many1 (char (setextHChars !! (n-1)))
196216 skipSpaces
197217 newline
@@ -256,6 +276,7 @@ rawLines = do
256276 return (concat lines)
257277
258278note = try (do
279+ failIfStrict
259280 ref <- noteMarker
260281 char ':'
261282 skipSpaces
@@ -280,6 +301,7 @@ note = try (do
280301--
281302
282303emacsBoxQuote = try (do
304+ failIfStrict
283305 string ",----"
284306 manyTill anyChar newline
285307 raw <- manyTill (try (do
@@ -336,8 +358,9 @@ bulletListStart = try (do
336358orderedListStart = try (do
337359 option ' ' newline -- if preceded by a Plain block in a list context
338360 skipNonindentSpaces
339− many1 digit <|> count 1 letter
340− oneOf orderedListDelimiters
361+ many1 digit <|> (do{failIfStrict; count 1 letter})
362+ delim <- oneOf orderedListDelimiters
363+ if delim /= '.' then failIfStrict else return ()
341364 oneOf spaceChars
342365 skipSpaces)
343366
@@ -410,10 +433,12 @@ bulletList = try (do
410433para = try (do
411434 result <- many1 inline
412435 newline
413− choice [ (do
414− followedBy' (oneOfStrings [">", ",----"])
415− return "" ),
416− blanklines ]
436+ st <- getState
437+ if stateStrict st
438+ then choice [followedBy' blockQuote, followedBy' header,
439+ (do{blanklines; return ()})]
440+ else choice [followedBy' emacsBoxQuote,
441+ (do{blanklines; return ()})]
417442 let result' = normalizeSpaces result
418443 return (Para result'))
419444
@@ -426,6 +451,36 @@ plain = do
426451-- raw html
427452--
428453
454+htmlElement = choice [strictHtmlBlock,
455+ htmlBlockElement] <?> "html element"
456+
457+htmlBlock = do
458+ st <- getState
459+ if stateStrict st
460+ then do
461+ failUnlessBeginningOfLine
462+ first <- htmlElement
463+ finalSpace <- many (oneOf spaceChars)
464+ finalNewlines <- many newline
465+ return (RawHtml (first ++ finalSpace ++ finalNewlines))
466+ else rawHtmlBlocks
467+
468+-- True if tag is self-closing
469+selfClosing tag = case (matchRegex (mkRegex "\\/[[:space:]]*>$") tag) of
470+ Just _ -> True
471+ Nothing -> False
472+
473+strictHtmlBlock = try (do
474+ tag <- anyHtmlBlockTag
475+ let tag' = extractTagType tag
476+ if selfClosing tag || tag' == "hr"
477+ then return tag
478+ else do
479+ contents <- many (do{notFollowedBy' (htmlEndTag tag');
480+ htmlElement <|> (count 1 anyChar)})
481+ end <- htmlEndTag tag'
482+ return $ tag ++ (concat contents) ++ end)
483+
429484rawHtmlBlocks = try (do
430485 htmlBlocks <- many1 rawHtmlBlock
431486 let combined = concatMap (\(RawHtml str) -> str) htmlBlocks
@@ -448,7 +503,18 @@ referenceKey = try (do
448503 option ' ' (char autoLinkEnd)
449504 tit <- option "" title
450505 blanklines
451− return (Key label (Src (removeTrailingSpace src) tit)))
506+ state <- getState
507+ let keysUsed = stateKeysUsed state
508+ updateState (\st -> st { stateKeysUsed = (label:keysUsed) })
509+ return $ Key label (Src (removeTrailingSpace src) tit))
510+
511+--
512+-- LaTeX
513+--
514+
515+rawLaTeXEnvironment' = do
516+ failIfStrict
517+ rawLaTeXEnvironment
452518
453519--
454520-- inline
@@ -457,10 +523,10 @@ referenceKey = try (do
457523text = choice [ math, strong, emph, code2, code1, str, linebreak, tabchar,
458524 whitespace, endline ] <?> "text"
459525
460−inline = choice [ rawLaTeXInline, escapedChar, special, hyphens, text,
526+inline = choice [ rawLaTeXInline', escapedChar, special, hyphens, text,
461527 ltSign, symbol ] <?> "inline"
462528
463−special = choice [ noteRef, inlineNote, link, referenceLink, rawHtmlInline,
529+special = choice [ noteRef, inlineNote, link, referenceLink, rawHtmlInline',
464530 autoLink, image ] <?> "link, inline html, note, or image"
465531
466532escapedChar = escaped anyChar
@@ -507,6 +573,7 @@ mathWord = many1 (choice [ (noneOf (" \t\n\\" ++ [mathEnd])),
507573 return c))])
508574
509575math = try (do
576+ failIfStrict
510577 char mathStart
511578 notFollowedBy space
512579 words <- sepBy1 mathWord (many1 space)
@@ -549,18 +616,17 @@ str = do
549616-- an endline character that can be treated as a space, not a structural break
550617endline = try (do
551618 newline
552− -- next line would allow block quotes without preceding blank line
553− -- Markdown.pl does allow this, but there's a chance of a wrapped
554− -- greater-than sign triggering a block quote by accident...
555− -- notFollowedBy' (choice [emailBlockQuoteStart, string ",----"])
556619 notFollowedBy blankline
557− -- parse potential list-starts differently if in a list:
558620 st <- getState
621+ if stateStrict st
622+ then do
623+ notFollowedBy' emailBlockQuoteStart
624+ notFollowedBy' header
625+ else return ()
626+ -- parse potential list-starts differently if in a list:
559627 if (stateParserContext st) == ListItemState
560− then do
561− notFollowedBy' orderedListStart
562− notFollowedBy' bulletListStart
563− else option () pzero
628+ then notFollowedBy' (orderedListStart <|> bulletListStart)
629+ else return ()
564630 return Space)
565631
566632--
@@ -571,8 +637,12 @@ endline = try (do
571637reference = do
572638 char labelStart
573639 notFollowedBy (char noteStart)
574− label <- manyTill inline (char labelEnd)
575− return (normalizeSpaces label)
640+ -- allow for embedded brackets:
641+ label <- manyTill ((do{res <- reference;
642+ return $ [Str "["] ++ res ++ [Str "]"]}) <|>
643+ count 1 inline)
644+ (char labelEnd)
645+ return (normalizeSpaces (concat label))
576646
577647-- source for a link, with optional title
578648source = try (do
@@ -590,8 +660,10 @@ titleWith startChar endChar = try (do
590660 skipEndline -- a title can be on the next line from the source
591661 skipSpaces
592662 char startChar
593− tit <- manyTill (choice [ try (do {char '\\'; char endChar}),
594− (noneOf (endChar:endLineChars)) ]) (char endChar)
663+ tit <- manyTill anyChar (try (do
664+ char endChar
665+ skipSpaces
666+ followedBy' (char ')' <|> newline)))
595667 let tit' = gsub "\"" """ tit
596668 return tit')
597669
@@ -608,19 +680,26 @@ explicitLink = try (do
608680
609681referenceLink = choice [referenceLinkDouble, referenceLinkSingle]
610682
611−-- a link like [this][/url/]
683+-- a link like [this][ref]
612684referenceLinkDouble = try (do
613685 label <- reference
614686 skipSpaces
615687 skipEndline
616688 skipSpaces
617689 ref <- reference
618− return (Link label (Ref ref)))
690+ let ref' = if null ref then label else ref
691+ state <- getState
692+ if ref' `elem` (stateKeysUsed state)
693+ then return () else fail "no corresponding key"
694+ return (Link label (Ref ref')))
619695
620696-- a link like [this]
621697referenceLinkSingle = try (do
622698 label <- reference
623− return (Link label (Ref [])))
699+ state <- getState
700+ if label `elem` (stateKeysUsed state)
701+ then return () else fail "no corresponding key"
702+ return (Link label (Ref label)))
624703
625704-- a link <like.this.com>
626705autoLink = try (do
@@ -645,6 +724,7 @@ noteMarker = try (do
645724 manyTill (noneOf " \t\n") (char labelEnd))
646725
647726noteRef = try (do
727+ failIfStrict
648728 ref <- noteMarker
649729 state <- getState
650730 let identifiers = (stateNoteIdentifiers state) ++ [ref]
@@ -652,6 +732,7 @@ noteRef = try (do
652732 return (NoteRef (show (length identifiers))))
653733
654734inlineNote = try (do
735+ failIfStrict
655736 char noteStart
656737 char labelStart
657738 contents <- manyTill inline (char labelEnd)
@@ -664,3 +745,14 @@ inlineNote = try (do
664745 (Note ref [Para contents]):noteBlocks})
665746 return (NoteRef ref))
666747
748+rawLaTeXInline' = do
749+ failIfStrict
750+ rawLaTeXInline
751+
752+rawHtmlInline' = do
753+ st <- getState
754+ result <- if stateStrict st
755+ then choice [htmlBlockElement, anyHtmlTag, anyHtmlEndTag]
756+ else choice [htmlBlockElement, anyHtmlInlineTag]
757+ return (HtmlInline result)
758+