------------------------------------------------------------------------------
--The files in this directory are based on the programs described in:
--
--    A Modular fully-lazy lambda lifter in Haskell
--    Simon L. Peyton Jones and David Lester
--    Software -- Practice and Experience
--    Vol 21(5), pp.479-506
--    MAY 1991
--
--See the Readme file for more details.
------------------------------------------------------------------------------

-- 3.3 A data type for compilation -- a happy ending:

data Constant           = CNum Int | CBool Bool | CFun Name
type Name               = String

data Expr binder        = EVar   Name                              |
                          EConst Constant                          |
                          EAp    (Expr binder) (Expr binder)       |
                          ELet   IsRec [Defn binder] (Expr binder) |
                          ELam   [binder] (Expr binder)

type Defn binder        = (binder, Expr binder)

type Expression         = Expr Name

type IsRec              = Bool
recursive               = True
nonRecursive            = False

type AnnExpr  binder a  = (a, AnnExpr' binder a)
data AnnExpr' binder a  = AVar   Name                                        |
                          AConst Constant                                    |
                          AAp    (AnnExpr binder a) (AnnExpr binder a)       |
                          ALet   IsRec [AnnDefn binder a] (AnnExpr binder a) |
                          ALam   [binder] (AnnExpr binder a)

type AnnDefn binder a   = (binder, AnnExpr binder a)

bindersOf              :: [(binder,rhs)] -> [binder]
bindersOf defns         = [ binder | (binder,rhs) <- defns ]

rhssOf                 :: [(binder,rhs)] -> [rhs]
rhssOf defns            = [ rhs | (binder, rhs) <- defns ]

-- 4 Lambda lifting:

lambdaLift :: Expression -> [SCDefn]
lambdaLift  = collectSCs . abstract . freeVars

type SCDefn = (Name, [Name], Expression)

-- 4.2 Free variables:

freeVars            :: Expression -> AnnExpr Name (Set Name)

freeVars (EConst k)  = (setEmpty, AConst k)
freeVars (EVar v)    = (setSingleton v, AVar v)
freeVars (EAp e1 e2) = (setUnion (freeVarsOf e1') (freeVarsOf e2'),AAp e1' e2')
                        where e1' = freeVars e1
                              e2' = freeVars e2

freeVars (ELam args body)
  = (setDifference (freeVarsOf body') (setFromList args), ALam args body')
    where body' = freeVars body

freeVars (ELet isRec defns body)
  = (setUnion defnsFree bodyFree, ALet isRec defns' body')
    where binders        = bindersOf defns
          binderSet      = setFromList binders
          values'        = map freeVars (rhssOf defns)
          defns'         = zip binders values'
          freeInValues   = foldr setUnion setEmpty (map freeVarsOf values')
          defnsFree
             | isRec     = setDifference freeInValues binderSet
             | not isRec = freeInValues
          body'          = freeVars body
          bodyFree       = setDifference (freeVarsOf body') binderSet

freeVarsOf                 :: AnnExpr Name (Set Name) -> Set Name
freeVarsOf (freeVars, expr) = freeVars

-- 4.3 Generating supercombinators:

abstract               :: AnnExpr Name (Set Name) -> Expression
abstract (_, AVar v)    = EVar v
abstract (_, AConst k)  = EConst k
abstract (_, AAp e1 e2) = EAp (abstract e1) (abstract e2)
abstract (free, ALam args body)
                        = foldl EAp sc (map EVar fvList)
                          where fvList = setToList free
                                sc     = ELam (fvList++args) (abstract body)
abstract (_,ALet isRec defns body)
                        = ELet isRec
                               [(name,abstract body) | (name,body) <- defns]
                               (abstract body)

-- 4.4 Collecting supercombinators:

collectSCs   :: Expression -> [SCDefn]
collectSCs e  = [("$main",[],e')] ++ bagToList scs
                where (_, scs, e') = collectSCs_e initialNameSupply e

collectSCs_e :: NameSupply -> Expression -> (NameSupply,Bag SCDefn,Expression)
collectSCs_e ns (EConst k)  = (ns, bagEmpty, EConst k)
collectSCs_e ns (EVar v)    = (ns, bagEmpty, EVar v)
collectSCs_e ns (EAp e1 e2) = (ns'', bagUnion scs1 scs2, EAp e1' e2')
                              where (ns',  scs1, e1') = collectSCs_e ns  e1
                                    (ns'', scs2, e2') = collectSCs_e ns' e2

collectSCs_e ns (ELam args body)
 = (ns'', bagInsert (name,args,body') bodySCs, EConst (CFun name))
   where (ns', bodySCs,body') = collectSCs_e ns body
         (ns'',name)          = newName ns' "SC"

collectSCs_e ns (ELet isRec defns body)
 = (ns'', scs, ELet isRec defns' body')
   where ((ns'',scs),defns')   = mapAccuml collectSCs_d (ns',bodySCs) defns
         (ns', bodySCs, body') = collectSCs_e ns body

         collectSCs_d (ns,scs) (name,value)
                              = ((ns',bagUnion scs scs'), (name, value'))
                                where (ns',scs',value') = collectSCs_e ns value

