
-- ==========================================================--
-- === Add parameters to supercombinators which           ===--
-- === otherwise return functions          EtaAbstract.hs ===--
-- ==========================================================--

module EtaAbstract where
import BaseDefs
import Utils
import MyUtils


-- ==========================================================--
-- Doesn't assume that the tree has been lambda-lifted.
-- It does however assume that all lambda-terms are
-- directly attached to a let-binding.
--
eaEtaAbstract :: AnnExpr Naam TExpr ->
                 AnnExpr Naam TExpr

eaEtaAbstract ae@(tau, AVar v)     = ae
eaEtaAbstract ae@(tau, ANum n)     = ae
eaEtaAbstract ae@(tau, AConstr c)  = ae
eaEtaAbstract ae@(tau, AAp e1 e2)
   = (tau, AAp (eaEtaAbstract e1) (eaEtaAbstract e2))
eaEtaAbstract ae@(tau, ACase sw alts)
   = (tau, ACase (eaEtaAbstract sw)
                 [(n, (ps, eaEtaAbstract rhs)) | (n, (ps, rhs)) <- alts])
eaEtaAbstract ae@(tau, ALam vs e)
   = (tau, ALam vs (eaEtaAbstract e))

eaEtaAbstract ae@(tau, ALet rf defs body)
   = let typeInfo = [eaUncurry ty | (n, (ty, rhs)) <- defs]
         mergedDefs = map2nd mergeLams defs
         fixedDefs = myZipWith2 fixOne mergedDefs typeInfo
         fixOne sc@(n, (tau, ALam vs e)) (argTs, resT)
            | length vs == length argTs  = sc
            | length vs >  length argTs  = panic "eaEtaAbstract"
            | length vs <  length argTs  = eaMain sc argTs resT
         fixOne sc@(n, (tau, non_lam_b)) (argTs, resT)
            | null argTs  = sc
            | otherwise   = eaMain (n, (tau, ALam [] (tau, non_lam_b))) argTs resT
         mergeLams ae@(tau, ALam vs (tau2, ALam vs2 e))
            = mergeLams (tau, ALam (vs++vs2) e)
         mergeLams anyThingElse = anyThingElse
     in (tau, ALet rf fixedDefs (eaEtaAbstract body))


-- ==========================================================--
--
eaMain :: (Naam, AnnExpr Naam TExpr) ->
          [TExpr] ->
          TExpr ->
          (Naam, AnnExpr Naam TExpr)

eaMain (scname, (tau, ALam vs (tau2, rhs))) argTs resT
   = let actualArity  = length vs
         reqdArity    = length argTs
         newArgsReqd  = reqdArity - actualArity
         newArgs      = eaMakeNewArgs newArgsReqd vs
         newArgsTypes = myZip2 newArgs (drop actualArity argTs)
         appArgTLists = map ((flip drop) argTs)
                            (actualArity `myIntsFromTo` (reqdArity-1))
         appTypes     = map (eaCurry resT) appArgTLists
         newBody      = eaMakeApChain (myZip2 newArgsTypes appTypes) (tau2, rhs)
     in (scname, (tau, ALam (vs++newArgs) newBody))


-- ==========================================================--
--
eaMakeApChain :: [((Naam, TExpr), TExpr)] ->
                 AnnExpr Naam TExpr ->
                 AnnExpr Naam TExpr

eaMakeApChain [] app = app
eaMakeApChain (((v, vtype), vaptype):rest) app
   = eaMakeApChain rest (vaptype, AAp app (vtype, AVar v))


-- ==========================================================--
--
eaMakeNewArgs :: Int -> [Naam] -> [Naam]

eaMakeNewArgs n vs
   = let leadingvs = filter (not.null) (map (takeWhile (== 'v')) vs)
         root = last (sort ("":leadingvs)) ++ "v"
         newNames = map f (1 `myIntsFromTo` n)
         f n = root ++ show (n :: Int)
     in newNames


-- ==========================================================--
--
eaCurry :: TExpr -> [TExpr] -> TExpr

eaCurry resT []           = resT
eaCurry resT (argT:argTs) = TArr argT (eaCurry resT argTs)


-- ==========================================================--
--
eaUncurry :: TExpr -> ([TExpr], TExpr)

eaUncurry (TVar tv) = ([], TVar tv)

eaUncurry (TArr t1 t2)
   = let (rest, final) = eaUncurry t2
     in (t1:rest, final)

eaUncurry (TCons tcon targs)
   = ([], TCons tcon targs)


-- ==========================================================--
-- === end                                 EtaAbstract.hs ===--
-- ==========================================================--


