packages feed

pisigma-0.1.0.2: src/PiSigma/Check.hs

module PiSigma.Check where

import Control.Monad

import PiSigma.Syntax
import PiSigma.Evaluation
import PiSigma.Print
import PiSigma.Equality

--import Debug.Trace
--trace "check\n" $

-- closures are the other way around
-- should be fixed by changing closures.

{-
fog :: CTerm -> Closure
fog (g,t) = (con2scope g,t)
-}

failc :: Env e => Clos Term -> [Msg] -> Eval e a
failc t m = failp ([S "Checking:\t",C t,S "\n"]++m)

-- | Takes a term and an (unevaluated) type. We first
-- handle the cases that may potentially change the
-- environment. If none of those cases match, we can
-- safely evaluate the type and call check'.
check :: Env e => Clos Term -> Clos Type -> Eval e ()
--check (g,t) c | trace ("check\ng ="++(show g)++"\n t="++(show t)++"\nc="++(show c)++"\n") False = undefined

check (Let p t,g) c = 
    do g' <- checkProg (p,g)
       check (t,g') c

check (gt @  (Split t (x,(y,u)),g)) c = 
    do sigab <- infer' (t,g)
       case sigab of
         (VQ Sigma ((a,(z,b)),s)) -> 
             do t'      <- eval (t,g)
                (_,g')  <- tdecl x g (a,s)
                b       <- subst (z,(b,s)) (Var x, g')
                (_,g'') <- tdecl y g' b
                case t' of
                  (Ne (NVar i)) ->
                      letn' i (Pair (Var x,Var y), g'')
                                (check (u,g'') c)
                      -- this is a bit silly since we know the ids.
                  _ -> check (u,g'') c
                      -- instead of failing, we just omit the assignment.
         _ -> failc gt [S "split : Sigma type expected\nFound:\t",V sigab,S "\n(split)\n"]

check gt @ (t'@(Case t lus),g) c = 
    do enum <- infer' (t,g)
       case enum of
         VEnum ls -> 
             let ls' = map fst lus
             in if ls /= ls'
                then do -- pt' <- prt t'
                        failc gt [S ("case: labels don't match"
                                     ++ "\n expected : " ++ show ls
                                     ++ "\n inferred : " ++ show ls'
                                     ++ "\n(match)\n")]
                -- set equivalence would be sufficent
                else do t' <- eval (t,g)
                        case t' of
                          -- if the scrutinee is a variable, we add a constraint
                          -- while checking each of the branches
                          (Ne (NVar i)) ->
                               mapM_ (\ (l,u) -> 
                                      letn' i (label l)
                                            (check (u,g) c)) lus
                          -- if the scrutinee is not a variable, we do not add
                          -- a constraint, but continue
                          _ -> mapM_ (\ (l,u) -> check (u,g) c) lus
         _ -> failc gt [S "case : enum type expected\nFound:\t",V enum,S "\n(case)\n"]
         {- Problem: here and other places: we try to print the term t which isn't yet 
            typechecked and may contain undefined variables which may crash the printer...
            see undef-bug.pi
         -}
check (Force t,g) (a , s) =
    check (t,g) (Lift a , s)

check t a = do a' <- feval a
               check' t a'

check' :: Env e => Clos Term -> Val -> Eval e ()
check' gt (VBox (Boxed a)) = check gt a  -- do we still need this line?
check' (Lam (x,t),g) (VQ Pi ((a,(y,b)),s)) =
    do (i,g') <- tdecl x g (a,s)
       let s' = extendScope s y (i,Nothing)
       check (t,g') (b,s')
check' gt @ (Lam _,g) a = failc gt [S "Expected:\t",V a,S "\n(Lam)\n"]
check' (Pair (t,u),g) (VQ Sigma ((a,(y,b)),s)) =
    do check (t,g) (a,s)
       b <- subst (y,(b,s)) (t,g)
       check (u,g) b
check' gt @ (t @ (Pair _),g) a = failc gt [S "Expected\t",V a,S "\n(Pair)\n"]
-- Labels cannot be inferred because there is no way to know
-- what the other labels are.
check' gt @ (Label l,g) a @ (VEnum ls) | l `elem` ls = return ()
check' gt @ (Label l,g) a = failc gt [S "Expected\t",V a,S "\n(Label)\n"]
-- To check that [sigma] is a type, it's sufficient to know
-- that sigma is a type. The alternative would seem to be to
-- assume that ^Type == Type, but that does not work.
check' (Box a,g) VType =
    check (a,g) ty
check' (Box t,g) (VLift a) =
    check (t,g) a
check' gt @ (Box _,g) a = failc gt [S "Expected\t",V a,S "\n(Box)\n"]
check' t a =
    do b <- infer' t
       catchE (eq a b)
         (\ s -> failc t [S "Expected:\t",V a,
                          S "\nFound:\t",V b,S $ "\n(check)\n"])
-- ++s++"\n"++(show a)++"\n"++(show b)])
-- useful for debugging.

inferVar :: Env e => Clos Name -> Eval e (Clos Type)
inferVar (x,g) =
    case lookupCon g x of
      Just a  -> return a
      Nothing -> fail ("Undefined variable: "++x++"\n(inferVar)\n")

infer :: Env e => Clos Term -> Eval e (Clos Type)
--infer (g,t) | trace ("infer\ng ="++(show g)++"\n t="++(show t)++"\n") False = undefined

infer (Var x,g) = inferVar (x,g)

infer (Let tel t,g) = 
    do g' <- checkProg (tel,g)
       infer (t,g')  

infer (Type,g) = return ty

infer (Q _ (a,(x,b)),g) = 
    do check (a,g) ty
       (_,g') <- tdecl x g (a,g)
       check (b,g') ty
       return ty

infer gt@(t :. u,g) = 
    do piab <- infer' (t,g)
       case piab of 
         (VQ Pi ((a,(x,b)),s)) -> do check (u,g) (a,s)
                                     subst (x,(b,s)) (u,g)
         _ -> failc gt [S "Pi type expected, but found:\t",
                        V piab,
                        S "\n(app)\n"]

infer (Enum _,_) = return ty

infer (Box t,g) =
    liftM (\ (a,s) -> (Lift a,s)) (infer (t,g))

-- TODO: either use infer', or explain why forced eval
-- cannot be used here.
infer gt@(Force t,g) =
    do a  <- infer (t,g)
       a' <- eval a
       case a' of
         VLift b -> return b
         _       -> failc gt [S "Lifted type expected\n(Force)\n"]
infer (Lift a,g) =
    do check (a,g) ty
       return ty

infer gt = failc gt [S "Not inferable\n(infer)\n"]


-- | Infers a type and evaluates it.
infer' :: Env e => Clos Term -> Eval e Val
infer' gt =
    do a <- infer gt
       feval a

checkProg :: Env e => Clos Prog -> Eval e Scope
checkProg ([],g) = return g
checkProg ((Decl x a):tel,g) = 
    do check (a,g) ty 
       (_,g') <- decl x (PrtInfo {name=x, expand=False}) g (Just (a,g))
       checkProg (tel,g')
checkProg ((Defn x t):tel,g) =
    do a <- inferVar (x,g)
       check (t,g) a
       i <- getId x g
       defn' i (t,g)
       checkProg (tel,g)