module Language.KURE.Boilerplate
( kureYourBoilerplate
)
where
import Language.KURE
import Language.Haskell.TH
import Data.Char
import Data.Monoid
import Control.Monad
import System.Environment
kureYourBoilerplate :: Name -> [(Name,Name)] -> Q [Dec]
kureYourBoilerplate gname at_types = do
debug <- runIO $ (do _k_debug <- getEnv "KURE_DEBUG"
return $ True) `catch` (\ _ -> return False)
info <- reify gname
api_tys <- case info of
TyConI (DataD _ _ _ cons _) -> do
let (gcons,tys) = unzip [ (con,argTy) | (NormalC con [(_,argTy)]) <- cons ]
when (length tys /= length cons) $ do
fail $ "Strange type inside Generic datatype: " ++ show gname
mapM_ (pprintTermInstances gname) (zip gcons tys)
return tys
TyConI (TySynD _ [] singleTy) ->
return [singleTy]
_ -> fail $ "problem with generic type name " ++ show gname
runIO $ print ("(API_tys",api_tys)
runIO $ putStrLn "---------------------------------"
api_resolved_tys <- mapM resolveSynomyn api_tys
runIO $ print ("(tyNames",api_resolved_tys)
runIO $ putStrLn "---------------------------------"
(decs,allR',allU') <- liftM unzip3 $ sequence [ kureType debug [ (ConT m,ConT dec) | (m,dec) <- at_types]
(map pprint api_resolved_tys) rty ty
| (rty,ty) <- zip api_resolved_tys api_tys
]
rr <- newName "rr"
let mkPromote prom nm _ty = AppE (VarE prom) (AppE (VarE nm) (VarE rr))
theOptGenericInstance <-
case info of
TyConI (DataD {}) -> do
let choice e1 e2 = InfixE (Just e1) (VarE '(<+)) (Just e2)
let altsR = [ mkPromote 'promoteR nm ty
| (FunD nm _,ty) <- zip allR' api_resolved_tys
]
let altsU = [ mkPromote 'promoteU nm ty
| (FunD nm _,ty) <- zip allU' api_resolved_tys
]
return [ InstanceD []
(foldl AppT (ConT ''Walker) [ConT m,ConT dec,ConT gname])
[ FunD (mkName "allR") [ Clause [VarP rr] (NormalB $ foldl1 choice altsR) allR']
, FunD (mkName "crushU") [ Clause [VarP rr] (NormalB $ foldl1 choice altsU) allU']
]
| (m,dec) <- at_types
]
_ -> return []
let alldecs = concat decs ++ theOptGenericInstance
when debug $ runIO $ do putStrLn $ pprint alldecs
return $ alldecs
kureType :: Bool -> [(Type,Type)] -> [String] -> Type -> Type -> Q ([Dec],Dec,Dec)
kureType debug at_ty tyNames ty@(ConT nm) _orig_type = do
info <- reify nm
cons <- case info of
TyConI (DataD _ _ _ cons _) -> return cons
_ -> fail $ "strange info on name " ++ show nm ++ " : " ++ show info
(decs,consNames,argTypess) <- liftM unzip3 $ sequence [ kureCons debug tyNames con | con <- cons ]
rr <- newName "rr"
let buildFn name suffix extract = FunD name
[ Clause [VarP rr] (NormalB $ foldl1 choice alts) []]
where
choice e1 e2 = InfixE (Just e1) (VarE '(<+)) (Just e2)
alts = [ foldl AppE (VarE (mkName $ consName ++ suffix))
[ mkExtract tyNames extract rr ty2
| ty2 <- argTypes
]
| (consName,argTypes) <- zip consNames argTypess
]
let theInstances = [ InstanceD []
(foldl AppT (ConT ''Walker) [m,dec,ty])
[ buildFn (mkName "allR") "R" R
, buildFn (mkName "crushU") "U" U
]
| (m,dec) <- at_ty
]
allR_nm <- newName "allR"
allU_nm <- newName "allU"
return ( concat decs ++ theInstances
, buildFn allR_nm "R" R
, buildFn allU_nm "U" U
)
kureType _debug at_ty _tyNames ty ty2 = do
rr <- newName "rr"
tup <- newName "x"
let buildFn name suffix extract = FunD name
[ Clause [VarP rr] (NormalB
$ (if suffix == "R" then AppE (VarE 'transparently) else id)
$ AppE (VarE 'translate)
$ LamE [VarP tup]
$ AppE ( AppE (VarE 'apply)
(AppE (VarE extract) (VarE rr))
)
(SigE (VarE tup) ty2)
) []]
let theInstances = [ InstanceD []
(foldl AppT (ConT ''Walker) [m,dec,ty])
[ buildFn (mkName "allR") "R" 'extractR
, buildFn (mkName "crushU") "U" 'extractU
]
| (m,dec) <- at_ty ]
allR_nm <- newName "allR"
allU_nm <- newName "allU"
return ( theInstances
, buildFn allR_nm "R" 'extractR
, buildFn allU_nm "U" 'extractU
)
kureCons :: Bool -> [String] -> Con -> Q ([Dec],String,[Type])
kureCons _debug tyNames (NormalC consName args) = do
let guardName = mkName (combName consName ++ "G")
v <- newName "v"
let guardExpr = AppE (VarE 'acceptR) (LamE [VarP v]
(CaseE (VarE v)
[ Match (RecP consName [])
(NormalB (ConE 'True)) []
, Match WildP (NormalB (ConE 'False)) []
]))
let guardDef = ValD (VarP guardName) (NormalB guardExpr) []
let withName = mkName ("with" ++ nameBase consName)
(f:vs) <- mapM newName ("f": ["v" | _ <- args])
let withDef = FunD withName
[ Clause [VarP f,ConP consName (map VarP vs)] (NormalB (foldl AppE (VarE f) (map VarE vs))) []
, Clause [WildP,WildP] (NormalB (AppE (VarE 'failure) (LitE (StringL (show withName ++ " failed"))))) []
]
let nameR = mkName (combName consName ++ "R")
let isInteresting ty@(VarT {}) _ = error $ "found " ++ pprint ty ++ " as argument to " ++ show consName
isInteresting ty [] | pprint ty `elem` tyNames
= True
isInteresting (ConT _nm) [] = False
isInteresting (ConT nm) [inner_ty]
| nm == ''[] = isInteresting inner_ty []
| nm == ''Maybe = isInteresting inner_ty []
isInteresting (ConT nm) tys
| length tys >= 2 && nm == tupleTypeName (length tys) = or [ isInteresting ty [] | ty <- tys ]
isInteresting (AppT e1 e2) es = isInteresting e1 (e2:es)
isInteresting ty _ = error $ "unsupported type " ++ pprint ty ++ " as argument to " ++ show consName
resolvedArgsTypes <- mapM resolveSynomyn argsTypes
let interestingConsArgs = [ isInteresting ty [] | ty <- resolvedArgsTypes ]
rrs <- mapM newName [ "rr" | True <- interestingConsArgs ]
es <- mapM newName ["e" | _ <- args ]
es' <- sequence [ if interesting
then liftM Just $ newName "e'"
else return $ Nothing
| interesting <- interestingConsArgs ]
let es'' = [ case opt_e' of
Just e' -> e'
_ -> e
| (e,opt_e') <- zip es es'
]
let es'_rrs_es = [ (e',rr,e)
| (rr,(e,e')) <- zip rrs
[ (e,e') | (e,Just e') <- zip es es' ]
]
let nameRExpr = AppE (VarE 'transparently)
(AppE (VarE 'rewrite)
(AppE (VarE withName)
(LamE (map VarP es)
(DoE ( [ BindS (VarP e')
(foldl AppE (VarE 'apply) (map VarE [rr,e]))
| (e',rr,e) <- es'_rrs_es
]
++ [ NoBindS $
AppE (VarE 'return)
$ foldl AppE (ConE consName) (map VarE es'')
])))))
let nameRDef = FunD nameR [ Clause (map VarP rrs) (NormalB nameRExpr) []]
let nameU = mkName (combName consName ++ "U")
let nameUExpr = AppE (VarE 'translate)
(AppE (VarE withName)
(LamE (map VarP es)
(DoE ( [ BindS (VarP e')
(foldl AppE (VarE 'apply) (map VarE [rr,e]))
| (e',rr,e) <- es'_rrs_es
]
++ [ NoBindS $
AppE (VarE 'return)
$ AppE (VarE 'mconcat) (ListE (map VarE [ e' | Just e' <- es']))
]))))
let nameUDef = FunD nameU [ Clause (map VarP rrs) (NormalB nameUExpr) []]
let nameP = mkName (combName consName ++ "P")
the_e <- newName "the_e"
let namePExpr = AppE (VarE 'transparently)
(AppE (VarE 'translate)
(LamE [VarP the_e]
(AppE (AppE (VarE withName)
(LamE (map VarP es)
(AppE (AppE (VarE 'apply)
(foldl AppE (VarE f) (map VarE es))
)
(VarE the_e)
)
)
)
(VarE the_e))
))
let namePDef = FunD nameP [ Clause [VarP f] (NormalB namePExpr) []]
return ( [guardDef,withDef,nameRDef,nameUDef,namePDef]
, combName consName
, [ ty | (True,ty) <- zip interestingConsArgs resolvedArgsTypes ]
)
where
argsTypes = map snd args
kureCons _ _tyNames other = error $ "Unsupported constructor : " ++ show other
mkExtract :: [String] -> ResultStyle -> Name -> Type -> Exp
mkExtract tyNames extract rr ty | pprint ty `elem` tyNames
= AppE (VarE $ theExtract extract) (VarE rr)
mkExtract tyNames extract rr (AppT e1 e2) = mkExtract' tyNames extract rr e1 [e2]
mkExtract _tyNames _extract _rr ty = error $ "failed to make extract for " ++ pprint ty
mkExtract' :: [String] -> ResultStyle -> Name -> Type -> [Type] -> Exp
mkExtract' tyNames extract rr (AppT e1 e2) es = mkExtract' tyNames extract rr e1 (e2:es)
mkExtract' tyNames extract rr (ConT con) [t1,t2]
| con == tupleTypeName 2 = AppE (AppE (VarE $ theTuple2 extract)
(mkExtract tyNames extract rr t1)
) (mkExtract tyNames extract rr t2)
mkExtract' tyNames extract rr (ConT con) [t1]
| con == ''[] = AppE (VarE $ theList extract)
(mkExtract tyNames extract rr t1)
mkExtract' tyNames extract rr (ConT con) [t1]
| con == ''Maybe = AppE (VarE $ theMaybe extract)
(mkExtract tyNames extract rr t1)
mkExtract' _tyNames _extract _rr ty _ = error $ "failed to make extract for " ++ pprint ty
data ResultStyle = R | U
theExtract :: ResultStyle -> Name
theExtract R = 'extractR
theExtract U = 'extractU
theTuple2 :: ResultStyle -> Name
theTuple2 R = 'tuple2R
theTuple2 U = 'tuple2U
theList :: ResultStyle -> Name
theList R = 'listR
theList U = 'listU
theMaybe :: ResultStyle -> Name
theMaybe R = 'maybeR
theMaybe U = 'maybeU
combName :: Name -> String
combName nm = case nameBase nm of
(t:ts) -> toLower t : ts
[] -> ""
resolveSynomyn:: Type -> Q Type
resolveSynomyn ty@(ConT con) = do
info <- reify con
case info of
TyConI (DataD _ _ _ _ _) -> return $ ty
TyConI (NewtypeD _ _ _ _ _) -> return $ ty
TyConI (TySynD _ [] ty2) -> resolveSynomyn ty2
_ -> fail $ "unknown info inside " ++ show con ++ " ( " ++ show info ++ ")"
resolveSynomyn (AppT e1 e2) = do
e1' <- resolveSynomyn e1
e2' <- resolveSynomyn e2
return $ AppT e1' e2'
resolveSynomyn other = fail $ "resolveSynomyn problem : " ++ show other
pprintTermInstances :: Name -> (Name,Type) -> Q ()
pprintTermInstances gnm (nm,ty) =
runIO $ do
putStrLn $ ""
putStrLn $ "--------------------------------------------------"
putStrLn $ "instance Term " ++ pprint ty ++ " where"
putStrLn $ " type Generic " ++ pprint ty ++ " = " ++ nameBase gnm
putStrLn $ " select (" ++ nameBase nm ++ " e) = Just e"
putStrLn $ " select _ = Nothing"
putStrLn $ " inject = " ++ nameBase nm
putStrLn $ "--------------------------------------------------"
putStrLn $ ""