module Decl1 where

import Val
import Conv
import Core.Abs
import Exp1
import Cont
import Check

-- we can define only the last declared variable

-- data Decl = Var Name Exp | Def Name Exp Exp | Data Name Tel [(Name,Exp)] | DefRec Name Exp Exp

checkDecl :: Decl -> Cont -> G Cont

checkDecl (Var (s@(Ident n)) a) con =
 do
  checkT con a
  v <- return (evalCon con a)
  return (upCon s (mconst n v) v con)

checkDecl (Def s a e) con =
 do
  checkT con a
  v <- return (evalCon con a)
  check con v e
  u <- return (evalCon con e)
  return (upCon s u v con)

checkDecl (DefRec (s@(Ident n)) a e) con =
 do
  checkT con a
  v <- return (evalCon con a)
  check (upCon s (mconst n v) v con) v e
  return (mNewCon con v s e)

checkDecl (Data (s@(Ident n)) tel nas) con =
 do
  checkTel con tel
  v <- return (mSetTel (envCon con) tel)
  con1 <- return (upCon s (mPrim n v) v con)
  mVTCs tel con1 nas

mSetTel env [] = Set
mSetTel env ((Vcon x a):as) =
 Fun (eval env a) (\ u -> mSetTel (update env x u) as)

mNewCon con1 v (s@(Ident n)) e =
 newcon
  where
    w = evalBodyCon newcon (mconst n v) e
    newcon = upCon s w v con1

-- mDrop s k ((x1:A1,...,xk:Ak) -> A) is Fun (\ u1 -> ... \ uk -> mconst s A[u1,...,uk])
-- for representing the values of constructors with paramaters

mDrop (Ident n) 0 v = mPrim n v
mDrop s k (Fun _ f) = Lam (\ u -> mDrop s (k-1) (f u))

-- given c tel a build the value and the type of c
-- not efficient because we check again tel

mValTypCon :: Tel -> Cont -> Ident -> Exp -> G Cont

mValTypCon t con c a =
 do
  b <- return (mExpTel t a)
  checkT con b
  v <- return (evalCon con b)
  return (upCon c (mDrop c (length t) v) v con)

-- given tel and a list [(Name,Exp)] update the environment

mVTCs :: Tel -> Cont -> [ConstrDecl] -> G Cont

mVTCs t con [] = return con
mVTCs t con ((CDcon c a):rest) =
 do
  con1 <- mValTypCon t con c a
  mVTCs t con1 rest

type Tel = [VDecl]    -- telescopes

-- check telescopes

checkTel con [] = return ()
checkTel con ((Vcon x a):as) =
 do
  checkT con a
  v <- return (evalCon con a)
  u <- genCon con x v
  checkTel (upCon x u v con) as

-- mExpTel (a1,...,an) b = (x1:a1) -> (x2:a2) -> ... -> b

mExpTel [] b = b
mExpTel ((Vcon x a):as) b = EFun x a (mExpTel as b)


--- the main function: check a list of declarations

checkDs :: [Decl] -> Cont -> G Cont
checkDs [] con = return con
checkDs (d:ds) con =
 do
  con1 <- checkDecl d con
  checkDs ds con1