
module Eval () where 

import Language.Haskell.Liquid.Prelude (liquidError)

-- "Classic" GADT 
-- 
-- data Expr a where
--   I :: Int -> Expr Int
--   B :: Bool -> Expr Bool
--   Eq :: Expr a -> Expr a -> Expr Bool
--   Pl :: Expr Int -> Expr Int -> Expr Int
-- 
-- eval :: Expr a -> a
-- eval (I i)      = i
-- eval (B b)      = b
-- eval (Eq e1 e2) = (eval e1) == (eval e2)
-- eval (Pl e1 e2) = (eval e1) + (eval e2)

data Ty   = TInt 
          | TBool

data Expr = I     Int
          | B     Bool
          | Equal Expr Expr
          | Plus  Expr Expr
          deriving (Eq, Show)


{-@ check          :: e:ValidExpr  -> {v:Ty | (v = (eType e))} @-}
check (I _)        = TInt
check (B _)        = TBool
check (Plus e1 e2) = TInt
check (Equal _ _)  = TBool

{-@ lazy eval @-}

{-@ eval           :: e:ValidExpr  -> {v:ValidExpr | ((isValue v) && (((eType e) = (eType v))))} @-}
eval e@(I _)       = e
eval e@(B _)       = e
eval (Plus e1 e2)  = (eval e1) `plus` (eval e2)
eval (Equal e1 e2) = (eval e1) `equal` (eval e2)

plus (I i) (I j)   = I (i + j)
plus _       _     = liquidError "don't worry, its impossible" 

equal (I i) (I j)  = B (i == j)
equal (B x) (B y)  = B (x == y)
equal _       _    = liquidError "don't worry, its impossible" 

-- | The next two are silly, for scraping quals. Yuck. Should scrape from measure-DEFS etc.

{-@ toInt :: IntExpr -> Int @-}
toInt (I i) = i
toInt _     = liquidError "impossible"

{-@ toBool :: BoolExpr -> Bool @-}
toBool (B b) = b
toBool _     = liquidError "impossible"


{-@ predicate IsTInt X   = ((eType X) = TInt)  @-}
{-@ predicate IsTBool X  = ((eType X) = TBool) @-}


{-@ type ValidExpr     = {v: Expr | (isValid v)}                @-}
{-@ type IntExpr       = {v: Expr | ((isValue v) && (IsTInt  v))} @-}
{-@ type BoolExpr      = {v: Expr | ((isValue v) && (IsTBool v))} @-}


{-@ measure isValue       :: Expr -> Bool
    isValue (I i)         = true
    isValue (B b)         = true
    isValue (Equal e1 e2) = false 
    isValue (Plus e1 e2)  = false
  @-}  

{-@ measure eType       :: Expr -> Ty 
    eType (I i)         = TInt  
    eType (Plus  e1 e2) = TInt 
    eType (B b)         = TBool 
    eType (Equal e1 e2) = TBool 
  @-}

{-@ measure isValid       :: Expr -> Bool
    isValid (I i)         = true
    isValid (B b)         = true
    isValid (Equal e1 e2) = (((eType e1) = (eType e2)) && (isValid e1) && (isValid e2))
    isValid (Plus e1 e2)  = ((IsTInt e1) && (IsTInt e2) && (isValid e1) && (isValid e2))
  @-}

