{-# LANGUAGE TupleSections, OverloadedStrings #-} {-# LANGUAGE DeriveAnyClass #-} module Elab where import Control.Monad.Reader import Control.Exception import qualified Data.Map.Strict as Map import Data.Typeable import Elab.Monad import Elab.Eval import qualified Presyntax.Presyntax as P import Syntax infer :: P.Expr -> ElabM (Term, NFType) infer (P.Span ex a b) = do env <- ask liftIO $ runElab (infer ex) env `catches` [ Handler $ \e@WhileChecking{} -> throwIO e , Handler $ \e -> throwIO (WhileChecking a b e) ] infer (P.Var t) = do name <- getNameFor t case name of Builtin _ wi -> elabWiredIn wi name _ -> do nft <- getNfType name pure (Ref name, nft) infer (P.App p f x) = do (f, f_ty) <- infer f (d, r, w) <- isPiType p f_ty x <- check x d x_nf <- eval x pure (App p (w f) x, r x_nf) infer (P.Pi p s d r) = do d <- check d VType d_nf <- eval d assume (Bound s) d_nf $ do r <- check r VType pure (Pi p s d r, VType) infer (P.Sigma s d r) = do d <- check d VType d_nf <- eval d assume (Bound s) d_nf $ do r <- check r VType pure (Sigma s d r, VType) infer (P.Proj1 x) = do (tm, ty) <- infer x (d, _, wp) <- isSigmaType ty pure (Proj1 (wp tm), d) infer (P.Proj2 x) = do (tm, ty) <- infer x tm_nf <- eval tm (_, r, wp) <- isSigmaType ty pure (Proj2 (wp tm), r (vProj1 tm_nf)) infer exp = do t <- newMeta VType tm <- switch $ check exp t pure (tm, t) check :: P.Expr -> NFType -> ElabM Term check (P.Span ex a b) ty = do env <- ask liftIO $ runElab (check ex ty) env `catches` [ Handler $ \e@WhileChecking{} -> throwIO e , Handler $ \e -> throwIO (WhileChecking a b e) ] check (P.Lam p var body) (VPi p' dom (Closure _ rng)) | p == p' = assume (Bound var) dom $ Lam p var <$> check body (rng (VVar (Bound var))) check tm (VPi P.Im dom (Closure var rng)) = assume (Bound var) dom $ Lam P.Im var <$> check tm (rng (VVar (Bound var))) check (P.Lam p v b) ty = do (d, r, wp) <- isPiType p ty assume (Bound v) d $ wp . Lam P.Im v <$> check b (r (VVar (Bound v))) check (P.Pair a b) ty = do (d, r, wp) <- isSigmaType ty a <- check a d a_nf <- eval a b <- check b (r a_nf) pure (wp (Pair a b)) check exp ty = do (tm, has) <- switch $ infer exp unify has ty pure tm elabWiredIn :: WiredIn -> Name -> ElabM (Term, NFType) elabWiredIn WiType _ = pure (Type, VType) isPiType :: P.Plicity -> NFType -> ElabM (Value, NFType -> NFType, Term -> Term) isPiType p (VPi p' d (Closure _ k)) | p == p' = pure (d, k, id) isPiType p t = do dom <- newMeta VType name <- newName assume (Bound name) dom $ do rng <- newMeta VType wp <- isConvertibleTo t (VPi p dom (Closure name (const rng))) pure (dom, const rng, wp) isSigmaType :: NFType -> ElabM (Value, NFType -> NFType, Term -> Term) isSigmaType (VSigma d (Closure _ k)) = pure (d, k, id) isSigmaType t = do dom <- newMeta VType name <- newName assume (Bound name) dom $ do rng <- newMeta VType wp <- isConvertibleTo t (VSigma dom (Closure name (const rng))) pure (dom, const rng, wp) identityTy :: NFType identityTy = VPi P.Im VType (Closure "A" $ \t -> VPi P.Ex t (Closure "_" (const t))) checkStatement :: P.Statement -> ElabM a -> ElabM a checkStatement (P.Decl name ty) k = do ty <- check ty VType ty_nf <- eval ty assume (Defined name) ty_nf k checkStatement (P.Defn name rhs) k = do ty <- asks (Map.lookup (Defined name) . getEnv) case ty of Nothing -> do (tm, ty) <- infer rhs tm_nf <- eval tm define (Defined name) ty tm_nf k Just (ty_nf, nm) -> do unless (nm == VVar (Defined name)) . liftIO . throwIO $ Redefinition (Defined name) rhs <- check rhs ty_nf rhs_nf <- eval rhs define (Defined name) ty_nf rhs_nf k checkProgram :: [P.Statement] -> ElabM ElabEnv checkProgram [] = ask checkProgram (st:sts) = checkStatement st $ checkProgram sts newtype Redefinition = Redefinition { getRedefName :: Name } deriving (Show, Typeable, Exception) data WhileChecking = WhileChecking { startPos :: P.Posn, endPos :: P.Posn, exc :: SomeException } deriving (Show, Typeable, Exception)