ruin-0.1.0.0: Data/Ruin/QQ/Parser.hs
module Data.Ruin.QQ.Parser (
QQ(..),
pQQ,
) where
import Data.Char (isSpace)
import Data.Functor (void)
import Data.Maybe (fromMaybe)
import Text.Parsec
import Text.Parsec.String (Parser)
data QQ = MkQQ
(Maybe String)
[(Bool,String,String)]
deriving Show
pQQ :: Parser QQ
pQQ = do
optional gap
typename <- optionMaybe (pTypename <* gap)
affixes <- optionMaybe (pAffixes <* gap)
case affixes of
Just (Nothing,Nothing) -> fail "Refusing a degenerate affix specification: remove (...)."
_ -> return ()
binders <- go
eof
pure (MkQQ typename (map (interpretAffixes affixes) binders))
where
go = ((:) <$> pBinder <*> go1) <|> pure []
go1 = (gap *> go) <|> pure []
interpretAffixes ::
Maybe (Maybe String,Maybe String)
-> (b,Maybe String,String) -> (b,String,String)
interpretAffixes x (b,mvar,field) = (b,var,field)
where
var = case fromMaybe field mvar of
"_" -> "_" -- do not apply affixes to _
o -> maybe id affix x o
affix (pre,suf) = maybe id (++) pre . maybe id (flip (++)) suf
gap :: Parser ()
gap = void $ many1 $ satisfy isSpace
rest :: Parser Char
rest = char '_' <|> char '\'' <|> alphaNum
pTypename :: Parser String
pTypename = (:) <$> upper <*> many rest <?> "type name"
pAffixes :: Parser (Maybe String,Maybe String)
pAffixes =
(<?> "affix spec")
$ between (char '(') (char ')')
$ (,) <$> optionMaybe pPrefix <* ellipsis <*> optionMaybe pSuffix
ellipsis :: Parser ()
ellipsis = () <$ string "..."
pPrefix :: Parser String
pPrefix = pVar
pSuffix :: Parser String
pSuffix = many1 $ char '_' <|> char '\'' <|> alphaNum
pBinder :: Parser (Bool,Maybe String,String)
pBinder = try (char '!' *> pAt True) <|> pAt False <?> "binder"
pAt :: Bool -> Parser (Bool,Maybe String,String)
pAt b = (,,) b <$> optionMaybe (try (pVar <* char '@')) <*> pField
pVar :: Parser String
pVar = (:) <$> (char '_' <|> lower) <*> many rest <?> "Haskell variable name"
pField :: Parser String
pField = (many1 $ satisfy $ not . isSpace) <?> "field name"