------------------------------------------------------------------------------
--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.
------------------------------------------------------------------------------

-- Instance of Text for printing expressions:

instance Text Constant where
    showsPrec p (CNum n)  = shows n
    showsPrec p (CFun n)  = showString n

instance Text (Expr [Char]) where
    showsPrec p (EConst k)  = shows k
    showsPrec p (EVar v)    = showString v

    showsPrec p e@(EAp _ _) = showChar '(' . showsAp e . showChar ')'
                              where showsAp (EAp l r) = showsAp l
                                                          . showChar ' '
                                                          . shows r
                                    showsAp e         = shows e

    showsPrec p (ELet isRec defns body)
                            = showString (if isRec then "letrec" else "let")
                                . showChar ' '
                                . showsDefns defns
                                . showString " in "
                                . shows body

    showsPrec p (ELam binders body)
                            = showString "(\\"
                                . foldr1 (\h t-> h . showChar ' ' . t)
                                         (map showString binders)
                                . showChar '.'
                                . shows body
                                . showChar ')'

showWithSep          :: Text a => String -> [a] -> ShowS
showWithSep s [x]     = shows x
showWithSep s (x:xs)  = shows x . showString s . showWithSep s xs

showsDefns           :: [Defn Name] -> ShowS
showsDefns []         = showString "{}"
showsDefns [d]        = showsDefn d
showsDefns defns      = showChar '{'
                           . foldr1 (\h t-> h . showString "; " . t)
                                    (map showsDefn defns)
                           . showChar '}'

showsDefn            :: Defn Name -> ShowS
showsDefn (x,e)       = showString x . showString " = " . shows e

-- display lists of supercombinators:

showSCs :: [SCDefn] -> String
showSCs  = layn . map showSc
 where showSc (name,args,body)
           = foldr1 (\n ns -> n ++ " " ++ ns) (name:args)
                ++ " = "
                ++ show body

-- Parser for input of expressions: (sorry, this is rather a hack!)

number   :: Parser Int
number    = sp (many1 (sat isDigit) `do` strToNum)
            where strToNum = foldl (\n d->10*n+d) 0 . map (\c->ord c - ord '0')

variable :: Parser String
variable  = sp (sat isLower `seq` many (sat isAlpha) `do` (\(c,cs) -> c:cs))

constant :: Parser String
constant  = sp (sat isUpper `seq` many (sat isAlpha) `do` (\(c,cs) -> c:cs))

expr     :: Parser Expression
expr      = sptok "letrec" `seq` variable `seq` sptok "=" `seq` expr
                         `seq` sptok "in" `seq` expr
              `do` (\(lt,(v,(eq,(def,(inn,rhs)))))-> ELet True [(v,def)] rhs)
                `orelse`
            sptok "let" `seq` variable `seq` sptok "=" `seq` expr
                         `seq` sptok "in" `seq` expr
              `do` (\(lt,(v,(eq,(def,(inn,rhs)))))-> ELet False [(v,def)] rhs)
                `orelse`
            sptok "\\" `seq` listOf variable (sp (okay ())) `seq` sptok "."
                       `seq` expr
              `do` (\(l,(vs,(dot,e))) -> ELam vs e)
                `orelse`
            atomic

atomic  :: Parser Expression
atomic   = sptok "(" `seq` many1 expr `seq` sptok ")"
                  `do` (\(o,(e,c))->foldl1 EAp e) 
               `orelse` 
           variable `do` EVar
               `orelse`
           constant `do` (EConst . CFun)
               `orelse`
           number `do` (EConst . CNum)


inp     :: String -> Expression
inp s    = case expr s of ((p,""):_) -> p
                          _          -> error "Cannot parse input"

-- Examples:

ll, fll  :: Expression -> String
ll        = showSCs . lambdaLift 
fll       = showSCs . fullyLazyLift

example1 :: Expression
example1  = inp "let f = \\x. let g = \\y.(Plus (Times x x) y) in \
                             \(Plus (g 3) (g 4)) \
                \in (f 6)"

{- Results:

   ? ll example1				-- normal lambda lifting
      1) $main = let f = SC1 in (f 6)
      2) SC1 x = let g = (SC0 x) in (Plus (g 3) (g 4))
      3) SC0 x y = (Plus (Times x x) y)
 
   ? fll example1				-- fully lazy version

      1) $main = let f0 = SC1 in (f0 6)
      2) SC1 x1 = let v4 = (Plus (Times x1 x1)) in
                  let g2 = (SC0 v4) in (Plus (g2 3) (g2 4))
      3) SC0 v4 y3 = (v4 y3)
 
-}

example2 :: Expression
example2  = inp "let \
                \   f = \\x. letrec g = \\y. (Cons (Times x x) (g y)) \
                \            in (g 3) \
                \in (f 6)"

{- Results:

   ? ll example2				-- normal lambda lifting
      1) $main = let f = SC1 in (f 6)
      2) SC1 x = letrec g = (SC0 g x) in (g 3)
      3) SC0 g x y = (Cons (Times x x) (g y))

   ? fll example2				-- fully lazy version
      1) $main = let f0 = SC1 in (f0 6)
      2) SC1 x1 = let v4 = (Cons (Times x1 x1)) in
                  letrec g2 = (SC0 g2 v4) in (g2 3)
      3) SC0 g2 v4 y3 = (v4 (g2 y3))

-}
