module Data.Thorn.Internal (
newVar, newVarP, newVarE
, newFunc, newFuncP, newFuncE
, newFmap, newFmapP, newFmapE
, mkNameE, mkNameCE, mkNameP
, Typex(..)
, Conx(..)
, cxtxs
, normalizeType
, apps
) where
import Language.Haskell.TH
import Data.List
import Data.Maybe
import Control.Monad
import Control.Applicative
newVar,newFunc,newFmap :: Int -> Name
newVar n = mkName $ "thornvariant" ++ show n
newVarP = VarP . newVar
newVarE = VarE . newVar
newFunc n = mkName $ "thornfunction" ++ show n
newFuncP = VarP . newFunc
newFuncE = VarE . newFunc
newFmap n = mkName $ "thornfmap" ++ show n
newFmapP = VarP . newFmap
newFmapE = VarE . newFmap
mkNameE = VarE . mkName
mkNameCE = ConE . mkName
mkNameP = VarP . mkName
data Typex =
VarTx Name
| FuncTx (Typex -> TypexQ)
| DataTx Name VarMap [Conx]
| SeenDataTx Name VarMap
| BasicTx Name
| TupleTx [Typex]
| ArrowTx Typex Typex
| ListTx Typex
| SpecialTx Int
type TypexQ = Q Typex
data Conx =
NormalCx Name [Typex]
| InfixCx Name Typex Typex
deriving Eq
cxtxs :: Conx -> [Typex]
cxtxs (NormalCx _ txs) = txs
cxtxs (InfixCx _ txa txb) = [txa,txb]
type VarMap = [(Name,Typex)]
type Datas = [(Name,VarMap)]
instance Eq Typex where
VarTx t == VarTx t' = t==t'
DataTx nm vmp cons == DataTx nm' vmp' cons' = nm==nm'&&vmp==vmp'&&cons==cons'
SeenDataTx nm vmp == SeenDataTx nm' vmp' = nm==nm'&&vmp==vmp'
BasicTx nm == BasicTx nm' = nm==nm'
TupleTx txs == TupleTx txs' = txs==txs'
ArrowTx txa txb == ArrowTx txa' txb' = txa==txa'&&txb==txb'
ListTx tx == ListTx tx' = tx==tx'
SpecialTx n == SpecialTx n' = n==n'
_ == _ = False
instance Show Typex where
show (DataTx _ _ _) = "DataTx"
show (SeenDataTx _ _) = "SeenDataTx"
show _ = "Foo"
normalizeType :: VarMap -> Datas -> Type -> TypexQ
normalizeType vmp dts (ForallT tvs _ t) = normalizeType vmp' dts t
where vmp' = filter (\(nm,_) -> notElem nm (map nameTV tvs)) vmp
normalizeType vmp dts (AppT t u) = do
FuncTx f <- normalizeType vmp dts t
ux <- normalizeType vmp dts u
f ux
normalizeType vmp dts (SigT t _) = normalizeType vmp dts t
normalizeType vmp dts (VarT nm) = case (find (\(nm',_) -> nm==nm') vmp) of
Nothing -> return $ VarTx nm
Just (_,tx) -> return tx
normalizeType vmp dts (ConT nm)
| s == "()" = normalizeType vmp dts (TupleT 0)
| head s == '(' && dropWhile (==',') (tail s) == ")" = normalizeType vmp dts (TupleT (length s 1))
| s == "(->)" = normalizeType vmp dts ArrowT
| s == "[]" = normalizeType vmp dts ListT
| elem s ["Int","Word","Float","Double","Char","Ptr","FunPtr"] = return $ BasicTx nm
| otherwise = reify nm >>= go
where s = nameBase nm
go (TyConI (TySynD _ tvs u)) = ho (length tvs) []
where ho 0 txs = normalizeType (zip (map nameTV tvs) (reverse txs)) dts u
ho n txs = return $ FuncTx $ \tx -> ho (n1) (tx:txs)
go (TyConI (DataD _ _ tvs cons _)) = ho (length tvs) []
where ho 0 txs = fromData nm (zip (map nameTV tvs) (reverse txs)) dts cons
ho n txs = return $ FuncTx $ \tx -> ho (n1) (tx:txs)
go (TyConI (NewtypeD _ _ tvs con _)) = ho (length tvs) []
where ho 0 txs = fromData nm (zip (map nameTV tvs) (reverse txs)) dts [con]
ho n txs = return $ FuncTx $ \tx -> ho (n1) (tx:txs)
go (PrimTyConI _ _ _) = fail "Autofmap doesn't support such primitive types, sorry."
go (FamilyI _ _) = fail "Autofmap doesn't support type families, sorry."
normalizeType vmp dts (TupleT n) = go n []
where go 0 txs = return $ TupleTx (reverse txs)
go n txs = return $ FuncTx $ \tx -> go (n1) (tx:txs)
normalizeType vmp dts ArrowT = return $ FuncTx $ \txa -> return $ FuncTx $ \txb -> return $ ArrowTx txa txb
normalizeType vmp dts ListT = return $ FuncTx $ \tx -> return $ ListTx tx
normalizeType _ _ _ = fail "Autofmap doesn't support such types, sorry."
fromData :: Name -> VarMap -> Datas -> [Con] -> TypexQ
fromData nm vmp dts cons = case find (\(nm',vmp')->nm==nm') dts of
Just (_,vmp')
| vmp == vmp' -> return $ SeenDataTx nm vmp
| otherwise -> fail "Autofmap doesn't support irregular types, sorry."
Nothing -> DataTx nm vmp <$> mapM normalizeCon cons
where dts' = (nm,vmp) : dts
normalizeCon (NormalC nm sts) = NormalCx nm <$> mapM normalizeStrictType sts
normalizeCon (RecC nm vsts) = NormalCx nm <$> mapM normalizeVarStrictType vsts
normalizeCon (InfixC sta nm stb) = InfixCx nm <$> normalizeStrictType sta <*> normalizeStrictType stb
normalizeStrictType (_,t) = normalizeType vmp dts' t
normalizeVarStrictType (_,_,t) = normalizeType vmp dts' t
nameTV :: TyVarBndr -> Name
nameTV (PlainTV nm) = nm
nameTV (KindedTV nm _) = nm
apps e es = foldl (\e es -> AppE e es) e es