import Data.Char
import Data.Functor.Identity
+import Control.Monad
+import Language.Haskell.TH
import Language.Haskell.TH.Syntax
import Language.Haskell.TH.Quote
import qualified Text.Parsec.Token as P
import Text.Parsec.Language (haskell)
--
-import Language.GenDSL
+import Language.GenDSL as L
dsl :: QuasiQuoter
dsl = QuasiQuoter
, quoteDec = undefined
}
where
- parseExpr :: MonadFail m => String -> Loc -> m Exp
- parseExpr s loc =
- case runParser p () "" s of
- Left err -> fail $ show err
- Right e -> return e
+ parseExpr :: String -> Loc -> ExpQ
+ parseExpr s loc = either (fail . show) id $ runParser p () file s
where
file = loc_filename loc
(line, col) = loc_start loc
- p = getPosition >>= setPosition . mPos >> whiteSpace *> funOrExpr <* eof
- mPos = (flip setSourceName) file .
- (flip setSourceLine) line .
- (flip setSourceColumn) col
+ p = getPosition >>= setPosition . mPos >> whiteSpace *> expr <* eof
+ mPos p = setSourceName (setSourceLine (setSourceColumn p col) line) file
-- Lexer
identifier,operator :: Parser String
whiteSpace = P.whiteSpace haskell
-- Parser
-funOrExpr :: Parser Exp
-funOrExpr = expr
-
-func :: Parser Exp
-func = many1 ((,) <$> many1 pat <* reservedOp "=" <*> expr) >>= mkFun
- where
- mkFun :: MonadFail m => [([Pat], Exp)] -> m Exp
- mkFun es
- | all ((==1) . length . fst) es = pure $ LamE [VarP (mkName "x")] $ mkCase (VarE (mkName "x")) [(p, e)|([p], e)<-es]
- mkFun _ = fail "Multiple patterns/entries not supported yet"
-
-expr :: Parser Exp
+expr :: Parser ExpQ
expr = buildExpressionParser
--Postfix record selectors
- [ [E.Postfix (fmap (\s e->VarE (selectorName (mkName s)) `AppE` e) $ P.lexeme haskell $ char '.' *> identifier)]
+ [ [E.Postfix (fmap (\s e->varE (selectorName (mkName s)) `appE` e) $ P.lexeme haskell $ char '.' *> identifier)]
, [bin "^" AssocRight]
, [bin "*" AssocLeft, bin "/" AssocLeft]
, [bin "+" AssocLeft, bin "-" AssocLeft]
, [E.Infix (fmap ifx $ lexeme $ char '`' *> identifier <* char '`') AssocRight]
] basic
where
- bin :: String -> Assoc -> Operator String () Identity Exp
+ bin :: String -> Assoc -> Operator String () Identity ExpQ
bin str = E.Infix $ ifx (str++".") <$ sat operator (str==)
(("Expected operator " ++ str ++ " but got ")++)
- basic :: Parser Exp
+ basic :: Parser ExpQ
basic
- = try (AppE . VarE <$> var <*> (TupE <$> parens (commaSep expr)))
- <|> VarE <$> var
- <|> mkCase <$ reserved "case" <*> expr <* reserved "of" <*> many1 ((,) <$> pat <* reservedOp "->" <*> expr)
- <|> (\i t e->VarE (mkName "if'") `AppE` i `AppE` t `AppE` e) <$ reserved "if" <*> expr <* reserved "then" <*> expr <* reserved "else" <*> expr
+ = try (appE . varE <$> var <*> (tupE <$> parens (commaSep expr)))
+ <|> varE <$> var
+ <|> mkCase <$ reserved "case" <*> expr <* reserved "of" <*> many1 ((,) <$> pat <*> body)
+ <|> (\i t e->[|if' $i $t $e|]) <$ reserved "if" <*> expr <* reserved "then" <*> expr <* reserved "else" <*> expr
<|> parens expr
- <|> mkLit . LitE <$> lit
- <|> mkLit . ConE . mkName <$> sat identifier ("True"==) ("Expected True but got: "++)
- <|> mkLit . ConE . mkName <$> sat identifier ("False"==) ("Expected False but got: "++)
+ <|> mkLit . litE <$> lite
+ <|> [|lit True|] <$ sat identifier ("True"==) ("Expected True but got: "++)
+ <|> [|lit False|] <$ sat identifier ("False"==) ("Expected False but got: "++)
+
+ match :: Parser (PatQ, BodyQ)
+ match = (,) <$> pat <*> body
-pat :: Parser Pat
+ body :: Parser BodyQ
+ body = guardedB <$> many1 (liftM2 (,) <$> guarded <* reservedOp "->" <*> expr)
+ <|> normalB <$ reservedOp "->" <*> expr
+ where
+ guarded :: Parser GuardQ
+ guarded = normalG <$ reservedOp "|" <*> expr
+
+pat :: Parser PatQ
pat
- = try (RecP <$> con <*> braces (commaSep $ (,) <$> var <* reservedOp "=" <*> pat))
- <|> ConP <$> con <*> many pat
- <|> try (AsP <$> var <* reservedOp "@" <*> pat)
- <|> VarP <$> var
- <|> WildP <$ symbol "_"
- <|> LitP <$> lit
+ = try (recP <$> con <*> braces (commaSep fieldpat))
+ <|> conP <$> con <*> many pat
+ <|> try (asP <$> var <* reservedOp "@" <*> pat)
+ <|> varP <$> var
+ <|> wildP <$ symbol "_"
+ <|> litP <$> lite
<|> parens pat
+ where fieldpat = liftM2 (,) . pure <$> var <* reservedOp "=" <*> pat
-lit :: Parser Lit
-lit
- = CharL <$> P.charLiteral haskell
- <|> IntegerL <$> P.natural haskell
+lite :: Parser Lit
+lite
+ = charL <$> P.charLiteral haskell
+ <|> integerL <$> P.natural haskell
sat :: Parser a -> (a -> Bool) -> (a -> String) -> Parser a
sat p f msg = try (p >>= \a->if f a then pure a else fail (msg a))
-mkLit :: Exp -> Exp
-mkLit = AppE $ VarE $ mkName "lit"
+mkLit :: ExpQ -> ExpQ
+mkLit x = [|lit $x|]
con,var :: Parser Name
con = mkName <$> sat identifier (isUpper . head) ("Not a data constructor: "++)
var = mkName <$> sat identifier (isLower . head) ("Not a variable identifier: "++)
-- Convert case to if statements
-mkCase :: Exp -> [(Pat, Exp)] -> Exp
-mkCase name cases = foldr (uncurry mkCaseMatch) (VarE (mkName "bottom") `AppE` LitE (StringL "Exhausted case")) cases
+mkCase :: ExpQ -> [(PatQ, BodyQ)] -> ExpQ
+mkCase name cases = foldr (uncurry mkCaseMatch) [|bottom "Exhausted case"|] cases
where
- mkCaseMatch :: Pat -> Exp -> Exp -> Exp
- mkCaseMatch a e rest = case mkCasePred name a of
- [] -> mkCaseLets (mkCaseBinding name a) e
- ps -> VarE (mkName "if'") `AppE` foldl1 (ifx "&.") ps `AppE` (mkCaseLets (mkCaseBinding name a) e) `AppE` rest
-
- mkCaseLets :: [Dec] -> Exp -> Exp
+ mkCaseMatch :: PatQ -> BodyQ -> ExpQ -> ExpQ
+ mkCaseMatch qa qb rest = qb >>= \b->case b of
+ NormalB e -> qa >>= \a->case mkCasePred name a of
+ [] -> ex a e
+ ps -> [|if' $(foldl1 (ifx "&.") ps) $(ex a e) $rest|]
+ GuardedB _ -> fail "Guarded bodies not yet supported"
+ where ex a e = mkCaseLets (mkCaseBinding name a) $ pure e
+
+ mkCaseLets :: [DecQ] -> ExpQ -> ExpQ
mkCaseLets [] e = e
- mkCaseLets defs e = LetE defs e
+ mkCaseLets defs e = letE defs e
conPtoRecP :: Name -> [Pat] -> Pat
conPtoRecP consName = RecP consName . zip (map (adtFieldName consName) [0..])
- mkCasePred :: Exp -> Pat -> [Exp]
- mkCasePred e (LitP l) = [ifx "==." (VarE (mkName "lit") `AppE` LitE l) e]
+ mkCasePred :: ExpQ -> Pat -> [ExpQ]
+ mkCasePred e (LitP l) = [[|lit $(litE l) ==. $e|]]
mkCasePred _ (VarP _) = []
mkCasePred e (ConP cons fs) = mkCasePred e $ conPtoRecP cons fs
mkCasePred e (InfixP l cons r) = mkCasePred e (ConP cons [l,r])
mkCasePred e (BangP p) = mkCasePred e p
mkCasePred e (AsP _ p) = mkCasePred e p
mkCasePred _ WildP = []
- mkCasePred e (RecP cons fs) = VarE (predicateName cons) `AppE` e
- : concatMap (\(n, p)->mkCasePred (VarE (selectorName n) `AppE` e) p) fs
+ mkCasePred e (RecP cons fs) = varE (predicateName cons) `appE` e
+ : concatMap (\(n, p)->mkCasePred (varE (selectorName n) `appE` e) p) fs
mkCasePred _ p = error $ "Unsupported pat: " ++ show p
- mkCaseBinding :: Exp -> Pat -> [Dec]
+ mkCaseBinding :: ExpQ -> Pat -> [DecQ]
mkCaseBinding _ (LitP _) = []
- mkCaseBinding e (VarP v) = [FunD v [Clause [] (NormalB $ e) []]]
+ mkCaseBinding e (VarP v) = [funD v [clause [] (normalB e) []]]
mkCaseBinding e (ConP cons fs) = mkCaseBinding e $ conPtoRecP cons fs
mkCaseBinding e (InfixP l cons r) = mkCaseBinding e (ConP cons [l,r])
mkCaseBinding e (UInfixP l cons r) = mkCaseBinding e (ConP cons [l,r])
mkCaseBinding e (ParensP p) = mkCaseBinding e p
mkCaseBinding e (TildeP p) = mkCaseBinding e p
mkCaseBinding e (BangP p) = mkCaseBinding e p
- mkCaseBinding e (AsP n p) = FunD n [Clause [] (NormalB $ e) []]:mkCaseBinding e p
+ mkCaseBinding e (AsP n p) = funD n [clause [] (normalB e) []]:mkCaseBinding e p
mkCaseBinding _ WildP = []
- mkCaseBinding e (RecP _ fs) = concatMap (\(n, p)->mkCaseBinding (VarE (selectorName n) `AppE` e) p) fs
+ mkCaseBinding e (RecP _ fs) = concatMap (\(n, p)->mkCaseBinding (varE (selectorName n) `appE` e) p) fs
mkCaseBinding _ p = error $ "Unsupported pat: " ++ show p