module Language.Haskell.TH.Tools (
mapTypesFun,
wrapTypes,
makeTypes,
nameTypes
) where
import Language.Haskell.TH (
Info(TyConI), reify, Name, mkName, newName, nameBase, stringL,
DecsQ, DecQ, Dec(FunD, DataD), cxt, sigD, dataD, funD,
Con(NormalC), normalC, ClauseQ, clause, normalB,
TypeQ, Type, conT, appT, arrowT, conP, varP, wildP, litP,
conE, varE, appE, litE, strictType, notStrict)
import Control.Applicative ((<$>), (<*>))
import Control.Arrow (first, (&&&))
import Control.Monad (replicateM)
import Data.List (isPrefixOf)
import Data.String (fromString)
import Data.Char (toLower, toUpper)
mapTypesFun :: Name -> Name -> (Name -> [Type] -> ClauseQ) -> DecQ
mapTypesFun fname typ f = do
TyConI (DataD _ _ _ cons _) <- reify typ
clauses <- (`mapM` cons) $ \(NormalC n a) -> f n $ map snd a
return $ FunD fname clauses
wrapTypes :: String -> [String] -> (String, [Name]) -> [Name] -> DecsQ
wrapTypes name types other deriv = let upper = map toUpper in fmap (: []) $
flip (dataD (cxt []) (mkName name) []) deriv $ map
(normalC <$> fst <*> map (strictType notStrict . conT) . snd) $
(++ [first mkName other]) $ flip map types $
mkName . (name ++) &&& (: []) . mkName . upper
makeTypes :: String -> Name -> String -> String -> DecsQ
makeTypes name dat preold prenew = do
TyConI (DataD _ _ _ cs _) <- reify dat
let (datN, funN) = mkName &&& mkName . headToLower $ name
((ns, tns), as) = first (id &&& map chpre) $ unzip $
map (\(NormalC n a) -> (n, map return $ init a)) cs
mkClause n a tn = do
t <- replicateM (length a) (newName "typ")
flip (clause [conP n (map varP t ++ [wildP])]) [] $
normalB $ foldl (\c -> appE c . varE) (conE tn) t
dd <- dataD (cxt []) datN [] (zipWith normalC tns as) [''Eq, ''Show]
sd <- sigD funN $ conT dat --> conT datN
fd <- funD funN $ zipWith3 mkClause ns as tns
return [dd, sd, fd]
where chpre = mkName . (prenew ++) . removePrefix preold . nameBase
removePrefix :: String -> String -> String
removePrefix pre str
| pre `isPrefixOf` str = drop (length pre) str
| otherwise = str
nameTypes :: Name -> String -> Name -> Name -> DecsQ
nameTypes typ pre o st = do
TyConI (DataD _ _ _ cons _) <- reify typ
let types = filter (/= o) $ map (\(NormalC n _) -> n) cons
cs = map (removePrefix pre . nameBase) types
(++) <$> nameToType typ cs types o <*> typeToName typ types cs o st
nameToType :: Name -> [String] -> [Name] -> Name -> DecsQ
nameToType typ strs types o = do
str <- newName "str"
let pats = map ((: []) . litP . stringL) strs ++ [[varP str]]
bodys = map normalB $ map conE types ++
[conE o `appE` (varE 'fromString `appE` varE str)]
(\sd fd -> [sd, fd])
<$> sigD fname (conT ''String --> conT typ)
<*> funD fname (zipWith3 clause pats bodys $ repeat [])
where fname = mkName $ ("nameTo" ++) $ nameBase typ
typeToName :: Name -> [Name] -> [String] -> Name -> Name -> DecsQ
typeToName typ ts ss o st = do
str <- newName "str"
let pats = map ((: []) . ($ []) . conP) ts ++ [[conP o [varP str]]]
bodys = map normalB $ map (litE . stringL) ss ++ [varE str]
(\sd fd -> [sd, fd])
<$> sigD fname (conT typ --> conT st)
<*> funD fname (zipWith3 clause pats bodys $ repeat [])
where fname = mkName $ (++ "ToName") $ headToLower $ nameBase typ
(-->) :: TypeQ -> TypeQ -> TypeQ
t1 --> t2 = arrowT `appT` t1 `appT` t2
headToLower :: String -> String
headToLower "" = ""
headToLower (c : cs) = toLower c : cs