exon-1.7.3.0: lib/Exon/Parse.hs
{-# options_haddock prune #-}
-- | Description: The parser for the quasiquote body, using parsec.
module Exon.Parse where
import Data.Char (isSpace)
import Prelude hiding ((<|>))
import Text.Parsec as Parsec (
Parsec,
anyChar,
char,
choice,
eof,
getState,
lookAhead,
modifyState,
putState,
runParser,
satisfy,
string,
try,
(<|>),
)
import Exon.Data.RawSegment (RawSegment (AutoExpSegment, ExpSegment, StringSegment, WsSegment))
type Parser = Parsec String Int
ws :: Parser Char
ws =
satisfy isSpace
whitespace :: Parser RawSegment
whitespace =
WsSegment <$> some ws
expr :: Parser String
expr =
choice [try opening, try closing, anyChars]
where
opening = do
char '{'
modifyState (1 +)
e <- expr
pure ('{' : e)
closing = do
void $ lookAhead (char '}')
getState >>= \case
0 -> pure ""
cur -> do
putState (cur - 1)
char '}'
e <- expr
pure ('}' : e)
anyChars = do
c <- anyChar
e <- expr
pure (c : e)
autoInterpolation :: Parser RawSegment
autoInterpolation =
string "##{" *> (AutoExpSegment <$> expr) <* char '}'
verbatimInterpolation :: Parser RawSegment
verbatimInterpolation =
string "#{" *> (ExpSegment <$> expr) <* char '}'
interpolations :: Parser RawSegment
interpolations =
try autoInterpolation <|> try verbatimInterpolation
verbatimStep :: Bool -> Parser Bool
verbatimStep =
lookAhead . \case
True -> False <$ ws <|> basic
False -> basic
where
basic =
False <$ (try (string "##{") <|> try (string "#{"))
<|>
False <$ eof
<|>
pure True
verbatimWith :: Parser Bool -> Parser String
verbatimWith step =
step >>= \case
False -> fail "Empty verbatim segment"
True -> go
where
go =
step >>= \case
False -> pure ""
True -> do
h <- anyChar
t <- go
pure (h : t)
verbatim :: Parser String
verbatim =
verbatimWith (verbatimStep False)
verbatimWs :: Parser String
verbatimWs =
verbatimWith (verbatimStep True)
text :: Parser RawSegment
text =
StringSegment <$> verbatim
textWs :: Parser RawSegment
textWs =
StringSegment <$> verbatimWs
segment :: Parser RawSegment
segment =
interpolations <|> text
segmentWs :: Parser RawSegment
segmentWs =
try whitespace <|> interpolations <|> textWs
parser :: Parser [RawSegment]
parser =
many segment <* eof
parserWs :: Parser [RawSegment]
parserWs =
many segmentWs <* eof
parseWith :: Parser [RawSegment] -> String -> Either Text [RawSegment]
parseWith p =
first (unwords . lines . show) . runParser p 0 ""
parse :: String -> Either Text [RawSegment]
parse =
parseWith parser
parseWs :: String -> Either Text [RawSegment]
parseWs =
parseWith parserWs