module Text.Jira.Parser.Core
(
JiraParser
, ParserState (..)
, defaultState
, parseJira
, withStateFlag
, updateLastStrPos
, notAfterString
, endOfPara
, notFollowedBy'
, blankline
, skipSpaces
, blockNames
) where
import Control.Monad (join, void)
import Data.Text (Text)
import Text.Parsec
type JiraParser = Parsec Text ParserState
data ParserState = ParserState
{ stateInLink :: Bool
, stateInList :: Bool
, stateInTable :: Bool
, stateLastStrPos :: Maybe SourcePos
}
defaultState :: ParserState
defaultState = ParserState
{ stateInLink = False
, stateInList = False
, stateInTable = False
, stateLastStrPos = Nothing
}
withStateFlag :: (Bool -> ParserState -> ParserState)
-> JiraParser a
-> JiraParser a
withStateFlag flagSetter parser = try $
let setFlag = modifyState . flagSetter
in setFlag True *> parser <* setFlag False
updateLastStrPos :: JiraParser ()
updateLastStrPos = do
pos <- getPosition
modifyState $ \st -> st { stateLastStrPos = Just pos }
notAfterString :: JiraParser Bool
notAfterString = do
curPos <- getPosition
prevPos <- stateLastStrPos <$> getState
return (Just curPos /= prevPos)
parseJira :: JiraParser a -> Text -> Either ParseError a
parseJira parser = runParser parser defaultState ""
skipSpaces :: JiraParser ()
skipSpaces = skipMany (char ' ')
blankline :: JiraParser ()
blankline = try $ skipSpaces *> void newline
endOfPara :: JiraParser ()
endOfPara = eof
<|> lookAhead blankline
<|> lookAhead headerStart
<|> lookAhead listItemStart
<|> lookAhead tableStart
<|> lookAhead panelStart
where
headerStart = void $ char 'h' *> oneOf "123456" <* char '.'
listItemStart = void $ many1 (oneOf "#*-") *> char ' '
tableStart = void $ skipSpaces *> many1 (char '|') *> char ' '
panelStart = void $ char '{' *> choice (map string blockNames)
blockNames :: [String]
blockNames = ["code", "noformat", "panel", "quote"]
notFollowedBy' :: Show a => JiraParser a -> JiraParser ()
notFollowedBy' p =
let failIfSucceeds = unexpected . show <$> try p
unitParser = return (return ())
in try $ join (failIfSucceeds <|> unitParser)