-- This module infers the type of given CoreExpr.
-- This is an updated version of the old Infer.lhs
{-# LANGUAGE TemplateHaskell, FlexibleContexts #-}
module MagicHaskeller.TICE where
import MagicHaskeller.CoreLang
import MagicHaskeller.Types
import MagicHaskeller.TyConLib
import MagicHaskeller.Execute(unsafeExecute, unDeBruijn, s, sprime, bprime, cprime, fix)
import MagicHaskeller.PriorSubsts
import qualified MagicHaskeller.PolyDynamic as PD
import MagicHaskeller.MyDynamic(unsafeFromDyn)
import MagicHaskeller.ReadTHType(thTypeToType)
import Data.Array.IArray
import Control.Monad(MonadPlus)
import Language.Haskell.TH(pprint)

import Debug.Trace

type TI = PriorSubsts Maybe

ceToDynamic :: TyConLib -> VarLib -> CoreExpr -> PD.Dynamic
ceToDynamic tcl vl ce = case tiCE tcl vl ce of
                          Nothing -> error ("ceToDynamic: " ++ pprint (exprToTHExp' False vl ce) ++ " type mismatch.")
                          Just ty -> PD.unsafeToDyn tcl ty (unsafeFromDyn $ unsafeExecute vl ce) $ exprToTHExp vl ce

tiCE :: (Functor m, MonadPlus m) => TyConLib -> VarLib -> CoreExpr -> m Type
tiCE tcl vl ce = do t <- runPS (tiEx tcl vl (unDeBruijn ce) >>= applyPS)
                    return $ normalize t
tiEx tcl vl ex = tie ex
    where
      tie (e :$ f) = do
        te <- tie e
        tf <- tie f
        tv <- newTVar
        let t = TV tv
        (if runPS (unify (tf :-> t) te) == Nothing then trace ("unifying " ++ show (tf :-> t) ++ " and " ++ show te) else id)
                     unify (tf :-> t) te
        t' <- applyPS t -- 勝手にこの行入れちゃったけど,いらないの?
        return t'
      tie prim     = freshInst (PD.dynType $ exe prim)
      -- myTypeOf is a version of exe defined in Execute.hs. 
      {-# INLINE exe #-}
      exe :: CoreExpr -> PD.Dynamic
      exe (Primitive n) = vl!n
      exe (PrimCon   n) = vl!n
      exe (Context (Dict pd)) = pd
      exe S = $(PD.dynamic [|defaultTCL|] [| s     :: (b->c->a) -> (b->c) -> b -> a |])
      exe K = $(PD.dynamic [|defaultTCL|] [| const :: a->b->a |])
      exe I = $(PD.dynamic [|defaultTCL|] [| id    :: a->a    |])
      exe B = $(PD.dynamic [|defaultTCL|] [| (.)   :: (c->a) ->    (b->c) -> b -> a |])
      exe C = $(PD.dynamic [|defaultTCL|] [| flip  :: (b->c->a) ->     c  -> b -> a |])
      exe S' = $(PD.dynamic [|defaultTCL|] [| sprime :: (a->b->c)->(d->a)->(d->b)->d->c |])
      exe B' = $(PD.dynamic [|defaultTCL|] [| bprime :: (a->b->c)->    a ->(d->b)->d->c |])
      exe C' = $(PD.dynamic [|defaultTCL|] [| cprime :: (a->b->c)->(d->a)->b->d->c |])
      exe Y  = $(PD.dynamic [|defaultTCL|] [| fix    :: (a->a)->a |])
      exe foo = error ("TICE.tiEx: " ++ show foo ++ " : unknown combinator")