module Parsing.ParseInline (inline) where import Data.Maybe import Network.URI (isURI) import Text.Parsec import qualified Data.Map.Strict as M import AST import Parsing.State import Parsing.Text import Parsing.TextUtils import Parsing.Utils import Parsing.ParseHtml nestedBold :: Parser Inline nestedBold = suppressErr (try (string "****")) >> fail "cannot have empty or nested bold nodes" bold :: Parser Inline bold = do state <- getState let currentParserStack = inlineParserStack state failIf $ elem "bold" currentParserStack s <- betweenWithErrors' "**" "**" "bold" $ withModifiedState (many1 inline) $ \s -> s {inlineParserStack=("bold" : currentParserStack)} return $ Bold s -- The bold and italics parsers are tricky because they both use the same special character. italics :: Parser Inline italics = do state <- getState let currentParserStack = inlineParserStack state failIf $ elem "italics" currentParserStack s <- between (try ((char' '*' "\"*\" (italics)") >> suppressErr (notFollowedBy (char '*')))) (char' '*' "closing \"*\" (italics)") ((withModifiedState (many1 inline) $ \s -> s {inlineParserStack=("italics" : currentParserStack)}) if elem "bold" currentParserStack then "content in italics node or extra \"*\" to close bold node" else "content in italics node") return $ Italics s code :: Parser Inline code = fmap Code $ betweenWithErrors' "`" "`" "code" $ many1 $ escapableNoneOf "`" footnoteRef :: Parser Inline footnoteRef = do identifier <- betweenWithErrors' "^[" "]" "footnote reference" $ many1 $ escapableNoneOf "[]" state <- getState let f = footnoteIndices state if M.member identifier f then fail $ "repeated footnote identifier: " ++ identifier else return () let index = M.size f let f' = M.insert identifier index f putState $ state {footnoteIndices=f'} return $ FootnoteRef index image :: Parser Inline image = do alt <- betweenWithErrors' "![" "]" "image" $ many1 $ escapableNoneOf "[]" src <- betweenWithErrors' "(" ")" "image src" $ many $ escapableNoneOf "()" return $ Image {alt=alt, src=src} link :: Parser Inline link = do state <- getState let currentParserStack = inlineParserStack state lookAhead (char '[' "\"[\" (link)") if elem "link" currentParserStack then fail "links cannot be nested" else return () text <- betweenWithErrors' "[" "]" "link" $ withModifiedState (many1 inline) $ \s -> s {inlineParserStack=("link" : currentParserStack)} href <- optionMaybe $ betweenWithErrors' "(" ")" "link href" $ many $ escapableNoneOf "()" if isJust href then return $ Link {text=text, href=fromJust href} else do let text' = unboxPlaintext text where unboxPlaintext [Plaintext p] = p unboxPlaintext _ = "" if isURI text' then return $ Link {text=text, href=text'} else fail "link href is required unless link text is a valid absolute URI" inline :: Parser Inline inline = choice [nestedBold, bold, italics, code, footnoteRef, link, image, fmap InlineHtml html, fmap Plaintext plaintext]