-- 
-- (c) Susumu Katayama 2009
--

\begin{code}
{-# OPTIONS -fglasgow-exts -cpp #-}
module MagicHaskeller.ReadTHType(thTypeToType, typeToTHType, showTypeName) where

import MagicHaskeller.Types as Types
import MagicHaskeller.TyConLib
import Language.Haskell.TH as TH
import Data.Array((!), inRange, bounds)
import Data.Char(ord,chr)
import Data.List(nub)
import Data.Map(lookup)

showTypeName = TH.nameBase -- Use the unqualified name to avoid confusion because Data.Typeable.tyConString shows the unqualified name for types defined in the Standard Hierarchical Library (though the qualified name is shown when Typeable is derived).
-- showTypeName = show -- maybe in future, when TypeRep can be shown qualified.

-- MyDynamicでしか使われていないので，ForallTは単に無視する．PolyDynamicのチェックがちょっと緩くなるだけ．
thTypeToType :: TyConLib -> TH.Type -> Types.Type
thTypeToType tcl t = normalize $ thTypeToType' tcl t
thTypeToType' tcl (ForallT _ []    t) = thTypeToType' tcl t
thTypeToType' tcl (ForallT _ (_:_) t) = error "Type classes are not supported yet."
thTypeToType' tcl (TupleT n)      = TC (tuple tcl n)
thTypeToType' tcl ListT           = TC (list tcl)
-- thTypeToType' tcl (HsTyFun ht0 ht1)
--     = thTypeToType' tcl ht0 :-> thTypeToType' tcl ht1
thTypeToType' tcl (AppT (AppT ArrowT      ht0) ht1)                           = thTypeToType' tcl ht0 :-> thTypeToType' tcl ht1
thTypeToType' tcl (AppT (AppT (ConT name) ht0) ht1) | nameBase name == "(->)" = thTypeToType' tcl ht0 :> thTypeToType' tcl ht1
thTypeToType' tcl (AppT ht0 ht1)                                              = TA (thTypeToType' tcl ht0) (thTypeToType' tcl ht1)
thTypeToType' (fm,_) (ConT name)  = let nstr = showTypeName name
                                    in case Data.Map.lookup nstr fm of
	                                 Nothing -> TC $ (-1 - bakaHash nstr) -- error "nameToTyCon: unknown TyCon"
	                                 Just c  -> TC c
{- この辺は単なるコメントアウトでいいんだっけ？ 
thTypeToType' tcl (HsTyCon (Special HsUnitCon)) = TC (unit tcl)
thTypeToType' tcl (HsTyCon (Special HsListCon)) = TC (list tcl)
-}
thTypeToType' _   ArrowT = error "Partially applied (->)."
thTypeToType' _   (VarT name) = nameToVarType name
-- thTypeToType' _   hst = error ("thTypeToType': "++show hst)

nameToVarType name = strToVarType (showTypeName name)

{- tcKindを廃止するので．ま，tcKindがなくてもhigher-order kindでなければトップレベルのkindから推論できるし．
-- copied from svn/MagicHaskeller/memodeb/RandomFilter.hs
typeToTHType :: TyConLib -> Types.Type -> TH.Type
typeToTHType tcl ty = TH.ForallT (map tvToName $ tyvars ty) [] (typeToTHType' tcl ty)
typeToTHType' (_,ar) (TC tc) | tcid >= 0 = TH.ConT (TH.mkName $ fst ((ar ! tcKind tc) !! tcid))
                             where tcid = tcID tc
typeToTHType' tcl    (TV tv) = TH.VarT $ tvToName tv
typeToTHType' tcl (TA t0 t1) = TH.AppT (typeToTHType' tcl t0) (typeToTHType' tcl t1)
typeToTHType' tcl (t0:->t1)  = TH.AppT (TH.AppT TH.ArrowT (typeToTHType' tcl t0)) (typeToTHType' tcl t1)
typeToTHType' tcl (t0:> t1)  = TH.AppT (TH.AppT sectionedArrow (typeToTHType' tcl t0)) (typeToTHType' tcl t1)
tvToName = TH.mkName . return . chr . (+ ord 'a') . tvID
-}
typeToTHType :: TyConLib -> Types.Type -> TH.Type
typeToTHType tcl ty = (case tyvars ty of []  -> id
                                         tvs -> TH.ForallT (map tvToName $ nub tvs) [])
                                                               (typeToTHType' tcl 0 ty)
typeToTHType' (_,ar) k (TC tc) | tc >= 0 = TH.ConT (TH.mkName name)
                             where name = if inRange (bounds ar) k then fst ((ar ! k) !! tc)
                                                                   else 'K':shows k ('I':show tc) -- useful with defaultTCL 
typeToTHType' tcl    _ (TV tv) = TH.VarT $ tvToName tv
typeToTHType' tcl    k (TA t0 t1) = TH.AppT (typeToTHType' tcl (k+1) t0) (typeToTHType' tcl 0 t1)
typeToTHType' tcl    0 (t0:->t1)  = TH.AppT (TH.AppT TH.ArrowT (typeToTHType' tcl 0 t0)) (typeToTHType' tcl 0 t1)
typeToTHType' tcl    0 (t0:> t1)  = TH.AppT (TH.AppT sectionedArrow (typeToTHType' tcl 0 t0)) (typeToTHType' tcl 0 t1)
tvToName = TH.mkName . return . chr . (+ ord 'a')

-- secionedArrow = TH.ConT ''(->) -- 多分こっちでもOK
sectionedArrow = TH.ConT (mkName "GHC.Prim.(->)")

{-
Prelude> :module +Language.Haskell.TH
Prelude Language.Haskell.TH> AppT (AppT (ConT $ mkName "GHC.Prim.(->)") (ConT $ mkName "GHC.Base.Int")) (ConT $ mkName  "Char")
AppT (AppT (ConT GHC.Prim.(->)) (ConT GHC.Base.Int)) (ConT Char)
Prelude Language.Haskell.TH> AppT (AppT ArrowT (ConT $ mkName "GHC.Base.Int")) (ConT $ mkName  "Char")
AppT (AppT ArrowT (ConT GHC.Base.Int)) (ConT Char)
-}

\end{code}
