{-# LANGUAGE TemplateHaskell #-} {-# LANGUAGE TupleSections #-} -- | Quasiquotation for rewriting a match clause. module Control.Egison.QQ ( mc, ) where import Control.Egison.Core import Data.List import Data.List.Split import Data.Map (Map) import Data.Maybe (fromMaybe) import Language.Haskell.Meta import Language.Haskell.TH hiding (match) import Language.Haskell.TH.Quote import Language.Haskell.TH.Syntax import Text.Regex -- | A quasiquoter for rewriting a match clause. -- -- * /Wildcards/ -- -- A match clause that contains a wildcard -- -- > [mc| _ => "Matched" |] -- -- is rewrited to -- -- > MatchClause Wildcard -- > (\HNil -> "Matched") -- -- * /Pattern variables/ -- -- A match clause that contains a pattern variable -- -- > [mc| $x => x |] -- -- is rewrited to -- -- > MatchClause (PatVar "x") -- > (\HCons x HNil -> x) -- -- * /Value patterns/ -- -- A match clause that contains a value pattern -- -- > [mc| cons $x (cons $y (cons #(x + 1) (cons $z nil))) => (x, y, z) |] -- -- is rewrited to -- -- > MatchClause (cons (PatVar "x") (cons (PatVar "y") (cons (ValuePat (\HCons x (HCons (y HNil)) -> x + 1)) (cons (PatVar "z") nil)))) -- > (\HCons x (HCons (y (HCons z HNil))) -> (x, y, z)) -- -- * /And-patterns/ -- -- A match clause that contains an and-pattern -- -- > [mc| (& (cons _ _) $x) => x |] -- -- is rewrited to -- -- > MatchClause (AndPat (cons Wildcard Wildcard) (PatVar "x")) -- > (\HCons x HNil -> x) -- -- * /Or-patterns/ -- -- A match clause that contains an or-pattern -- -- > [mc| (| nil (cons _ _)) => "Matched" |] -- -- is rewrited to -- -- > MatchClause (OrPat nil (cons Wildcard Wildcard)) -- > (\HNil -> "Matched") mc :: QuasiQuoter mc = QuasiQuoter { quoteExp = \s -> do let [pat, exp] = splitOn "=>" s e1 <- case parseExp (changeNotPat (changeOrPat (changeAndPat (changeValuePat (changePatVar (changeWildcard pat)))))) of Left _ -> fail "Could not parse pattern expression." Right exp -> return exp e2 <- case parseExp exp of Left _ -> fail "Could not parse expression." Right exp -> return exp mcChange e1 e2 , quotePat = undefined , quoteType = undefined , quoteDec = undefined } changeWildcard :: String -> String changeWildcard pat = subRegex (mkRegex " _") pat " Wildcard" changePatVar :: String -> String changePatVar pat = subRegex (mkRegex "\\$([a-zA-Z0-9]+)") pat "(PatVar \"\\1\")" changeValuePat :: String -> String changeValuePat pat = subRegex (mkRegex "\\#(\\([^)]+\\)|\\[[^)]+\\]|[a-zA-Z0-9]+)") pat "(valuePat \\1)" changeAndPat :: String -> String changeAndPat pat = subRegex (mkRegex "\\(\\&") pat "(AndPat" changeOrPat :: String -> String changeOrPat pat = subRegex (mkRegex "\\(\\|") pat "(OrPat" changeNotPat :: String -> String changeNotPat pat = subRegex (mkRegex "\\(not ") pat "(NotPat " mcChange :: Exp -> Exp -> Q Exp mcChange pat expr = do let (vars, xs) = extractPatVars [pat] [] [| (MatchClause $(fst <$> changePat pat (map (`take` vars) xs)) $(changeExp vars expr)) |] -- extract patvars from pattern extractPatVars :: [Exp] -> [String] -> ([String], [Int]) extractPatVars [] vars = (vars, []) extractPatVars (ParensE x:xs) vars = extractPatVars (x:xs) vars extractPatVars (AppE (ConE name) p:xs) vars | nameBase name == "PatVar" = case p of (LitE (StringL s)) -> extractPatVars xs (vars ++ [s]) | nameBase name == "PredicatePat" = let (vs, ns) = extractPatVars xs vars in (vs, length vars:ns) | nameBase name == "LaterPat" = let (vs1, ns1) = extractPatVars xs vars in let (vs2, ns2) = extractPatVars [p] vs1 in (vs2, ns2 ++ ns1) | otherwise = extractPatVars (p:xs) vars extractPatVars (AppE (VarE name) p:xs) vars | nameBase name == "valuePat" = let (vs, ns) = extractPatVars xs vars in (vs, length vars:ns) | otherwise = extractPatVars (p:xs) vars extractPatVars (AppE a b:xs) vars = extractPatVars (a:b:xs) vars extractPatVars (SigE x typ:xs) vs = extractPatVars (x:xs) vs extractPatVars (_:xs) vars = extractPatVars xs vars -- change ValuePat e to \(HCons x HNil) -> e -- change PredicatePat (\x -> e) to \(HCons x HNil) -> (\x -> e) changePat :: Exp -> [[String]] -> Q (Exp, [[String]]) changePat e@(AppE (ConE name) p) vs | nameBase name == "PredicatePat" = do let (vars:varss) = vs (, varss) <$> appE (conE 'PredicatePat) (changeExp vars p) | otherwise = do (e', vs') <- changePat p vs (, vs') <$> appE (conE name) (return e') changePat e@(AppE (VarE name) p) vs | nameBase name == "valuePat" = do let (vars:varss) = vs (, varss) <$> appE (varE name) (changeExp vars p) | otherwise = do (e', vs') <- changePat p vs (, vs') <$> appE (varE name) (return e') changePat (AppE e1 e2) vs = do (e1', vs') <- changePat e1 vs (e2', vs'') <- changePat e2 vs' (, vs'') <$> appE (return e1') (return e2') changePat (ParensE x) vs = changePat x vs changePat (SigE x typ) vs = changePat x vs changePat e vs = return (e, vs) -- change e to \(HCons x HNil) -> e changeExp :: [String] -> Exp -> Q Exp changeExp vars expr = do vars' <- mapM newName vars vars'' <- mapM (\s -> newName $ s ++ "'") vars return $ LamE [f vars'] expr -- \[x, y] -> HCons x (HCons y HNil) f :: [Name] -> Pat f [] = ConP 'HNil [] f (x:xs) = InfixP (VarP x) 'HCons $ f xs