{-# LANGUAGE GADTs, ExistentialQuantification #-}

module Main where

succeed :: a -> Maybe a
succeed = return

data V s t where
  Z :: V (t,m) t
  S :: V m t -> V (x,m) t

data Exp s t where
  IntC  :: Int -> Exp s Int                     -- 5
  BoolC :: Bool -> Exp s Bool                   -- True
  Plus  :: Exp s Int -> Exp s Int -> Exp s Int  -- x + 3
  Lteq  :: Exp s Int -> Exp s Int -> Exp s Bool -- x <= 3
  Var   :: V s t -> Exp s t                     -- x

data Com s where
  Set :: V s t -> Exp s t -> Com s              -- x := e
  Seq :: Com s -> Com s -> Com s                -- { s1; s2; }
  If :: Exp s Bool -> Com s -> Com s -> Com s   -- if e then x else y
  While :: Exp s Bool -> Com s -> Com s         -- while e do s
  Declare :: Exp s t -> Com (t,s) -> Com s      -- { int x = 5; s }

update :: (V s t) -> t -> s -> s
update Z n (x,y) = (n,y)
update (S v) n (x,y) = (x,update v n y)

eval :: Exp s t -> s -> t
eval (IntC n) s = n
eval (BoolC b) s = b
eval (Plus x y) s = (eval x s) + (eval y s)
eval (Lteq x y) s = (eval x s) <= (eval y s)
eval (Var Z) (x,y) = x
eval (Var (S v)) (x,y) = eval (Var v) y


exec :: (Com st) -> st -> st
exec (Set v e) s = update v (eval e s) s
exec (Seq x y) s = exec y (exec x s)
exec (If test x1 x2) s =
  if (eval test s) then exec x1 s else exec x2 s
exec (While test body) s = loop s
  where loop s = if (eval test s)
                    then loop (exec body s)
                    else s
exec (Declare e body) s = store
  where (_,store) = (exec body (eval e s,s))

v0 = Z
v1 = S Z
v2 = S (S Z)
v3 = S (S (S Z))

e2 = Lteq (Plus (Var v0)(Var v1)) (Plus (Var v0) (IntC 1))

sum_var = Z
x = S Z

prog :: Com (Int,(Int,a))
prog =
 Seq (Set sum_var (IntC 0))
     (Seq (Set x (IntC 1))
     (While (Lteq (Var x) (IntC 5))
            (Seq (Set sum_var (Plus (Var sum_var)(Var x)))
                 (Set x (Plus (Var x) (IntC 1))))))

ans = exec prog (34,(12,1))
main = print ans
{-
{ sum = 0 ;
  x = 1;
  while (x <= 5)
  { sum = sum + x;
    x = x + 1;
  }
}
-}


---------------------------------------------------
-- Untyped Annotated AST

data TyAst = I | B | P TyAst TyAst

data TypeR t where
  IntR :: TypeR Int
  BoolR :: TypeR Bool
  PairR :: TypeR a -> TypeR b -> TypeR (a,b)

-- Judgments for Types
data TJudgment = forall t . TJ (TypeR t)

checkT :: TyAst -> TJudgment
checkT I = TJ IntR
checkT B = TJ BoolR
checkT (P x y) =
   case (checkT x,checkT y) of
     (TJ a, TJ b) -> TJ(PairR a b)

----------------------------------------------------
-- Equality Proofs and Type representations
data Equal a b where
  EqProof :: Equal a a

match :: TypeR a -> TypeR b -> Maybe (Equal a b)
match IntR IntR = succeed EqProof
match BoolR BoolR = succeed EqProof
match (PairR a b) (PairR c d) =
  do { EqProof <- match a c
     ; EqProof <- match b d
     ; succeed EqProof }
match _ _ = fail "match fails"


----------------------------------------------
-- checking Variables are consistent

checkV :: Int -> TypeR t -> TypeR s -> Maybe(V s t)
checkV 0 t1 (PairR t2 p) =
  do { EqProof <- match t1 t2
     ; return Z }
checkV n t1 (PairR ty p) =
  do { v <- checkV (n-1) t1 p; return(S v)}
checkV n t1 sr = Nothing


-----------------------------------------------------
data ExpAst
  = IntCA Int
  | BoolCA Bool
  | PlusA ExpAst ExpAst
  | LteqA ExpAst ExpAst
  | VarA Int TyAst

-- Judgments for Expressions
data EJudgment s = forall t . EJ (TypeR t) (Exp s t)

checkE :: ExpAst -> TypeR s -> Maybe (EJudgment s)
checkE (IntCA n) sr = succeed(EJ IntR (IntC n))
checkE (BoolCA b) sr = succeed(EJ BoolR (BoolC b))
checkE (PlusA x y) sr =
  do { EJ t1 e1 <- checkE x sr
     ; EqProof <- match t1 IntR
     ; EJ t2 e2 <- checkE y sr
     ; EqProof <- match t2 IntR
     ; succeed(EJ IntR (Plus e1 e2))}
checkE (VarA n ty) sr =
  do { TJ t <- succeed(checkT ty)
     ; v <- checkV n t sr
     ; return(EJ t (Var v)) }

-----------------------------------------------------
data ComAst
  = SetA Int TyAst ExpAst
  | SeqA ComAst ComAst
  | IfA ExpAst ComAst ComAst
  | WhileA ExpAst ComAst
  | DeclareA TyAst ExpAst ComAst

data CJudgment s = EC (Com s)

checkC :: ComAst -> TypeR s -> Maybe(CJudgment s)
checkC (SetA n ty e) sr =
  do { TJ t1 <- succeed(checkT ty)
     ; v <- checkV n t1 sr
     ; EJ t2 e1 <- checkE e sr
     ; EqProof <- match t1 t2
     ; return(EC (Set v e1))}
checkC (SeqA x y) sr =
  do { EC c1 <- checkC x sr
     ; EC c2 <- checkC y sr
     ; return(EC (Seq c1 c2)) }
checkC (IfA e x y) sr =
  do { EJ t1 e1 <- checkE e sr
     ; EqProof <- match t1 BoolR
     ; EC c1 <- checkC x sr
     ; EC c2 <- checkC y sr
     ; return(EC(If e1 c1 c2)) }
checkC (WhileA e x) sr =
  do { EJ t1 e1 <- checkE e sr
     ; EqProof <- match t1 BoolR
     ; EC c1 <- checkC x sr
     ; return(EC(While e1 c1)) }
checkC (DeclareA ty e c) sr =
  do { TJ t1 <- succeed(checkT ty)
     ; EJ t2 e2 <- checkE e sr
     ; EqProof <- match t1 t2
     ; EC c2 <- checkC c (PairR t1 sr)
     ; return(EC(Declare e2 c2)) }

--------------------------------------------------------------

e1 = Lteq (Plus (Var sum_var)(Var x)) (Plus (Var x) (IntC 1))

{-
data Store s
  = M (Code s)
  | forall a b . N (Code a) (Store b) where s = (a,b)

eval2 :: Exp s t -> Store s -> Code t
eval2 (IntC n) s = lift n
eval2 (BoolC b) s = lift b
eval2 (Plus x y) s = [| $(eval2 x s) + $(eval2 y s) |]
eval2 (Lteq x y) s = [| $(eval2 x s) <= $(eval2 y s) |]
eval2 (Var Z) (N a b) = a
eval2 (Var (S v)) (N a b) = eval2 (Var v) b
eval2 (Var Z) (M x) = [| fst $x |]
eval2 (Var (S v)) (M x) = eval2 (Var v) (M [| snd $x |])


test e = [| \ (x,(y,z)) -> $(eval2 e (N [|x|] (N [|y|] (M [|z|])))) |]

-- test e1 --->  [| \ (x,(y,z)) -> x + y <= y + 1 |]
-}
