unbound-0.2: examples/LF.hs
{- Type checker for LF, based on algorithm in Harper and Pfennig, "On
Equivalence and Canonical Forms in the LF Type Theory", ACM
Transactions on Computational Logic, 2000.
-}
{-# LANGUAGE TemplateHaskell
, ScopedTypeVariables
, FlexibleInstances
, MultiParamTypeClasses
, FlexibleContexts
, UndecidableInstances
, TypeSynonymInstances
, TypeFamilies
, GeneralizedNewtypeDeriving
, NoMonomorphismRestriction
#-}
{- TODO:
1. [ ] Fix parser to deal with infix type operators
2. [ ] test on contents of qbf/
3. [ ] tune for speed?
-}
module Main where
import Prelude hiding (lookup)
import Generics.RepLib.Bind.LocallyNameless
import Generics.RepLib.Bind.Fresh (contLFreshM)
import Generics.RepLib
import Text.Parsec hiding ((<|>))
import qualified Text.Parsec.Token as P
import Text.Parsec.Language (haskellDef)
import Text.Parsec.String
import qualified Text.Parsec.Expr as PE
import Text.PrettyPrint (Doc, (<+>), (<>), colon, comma, text, render, empty, integer, nest, vcat, ($+$))
import qualified Text.PrettyPrint as PP
import Control.Monad.Error
import Control.Monad.Reader
import Control.Monad.Identity
import Control.Applicative hiding (many)
import qualified Data.Map as M
import qualified Data.Set as S
import Data.List (sortBy, groupBy)
import Data.Function (on)
import Data.Ord (comparing)
import System.Environment
------------------------------
-- Syntax --------------------
------------------------------
-- Kinds
data Kind = KPi (Bind (Name Tm, Annot Ty) Kind) -- {x:ty} k
| Type -- type
deriving Show
-- Types, also called "Families"
data Ty = TyPi (Bind (Name Tm, Annot Ty) Ty) -- {x:ty} ty
| TyApp Ty Tm -- ty tm
| TyConst (Name Ty) -- a
deriving Show
-- Terms, also called "Objects"
data Tm = Lam (Bind (Name Tm, Annot Ty) Tm) -- [x:ty] tm
| TmApp Tm Tm -- tm tm
| TmVar (Name Tm) -- x
deriving Show
-- Harper and Pfennig distinguish between term *variables* and term
-- *constants*, but for our purposes there is no need to distinguish
-- between them.
$(derive [''Kind, ''Ty, ''Tm])
instance Alpha Kind
instance Alpha Ty
instance Alpha Tm
-- There are no term variables in types or kinds, so we can just
-- use the default structural Subst instances.
instance Subst Tm Kind
instance Subst Tm Ty
-- For Tm we must implement isvar so the Subst instance knows about
-- term variables.
instance Subst Tm Tm where
isvar (TmVar v) = Just (SubstName v)
isvar _ = Nothing
-- A declaration is either
-- * a type constant declaration (a name and a kind),
-- * a term constant declaration (with optional type and definition), or
-- * a fixity/precedence declaration.
data Decl = DeclTy (Name Ty) Kind
| DeclTm (Name Tm) (Maybe Ty) (Maybe Tm)
| DeclInfix Op
deriving Show
data Op = Op Assoc Integer (Name Tm)
deriving Show
data Assoc = L | R
deriving Show
-- A program is a sequence of declarations.
type Prog = [Decl]
--------------------
-- Erasure ---------
--------------------
-- Simple kinds and types (no dependency)
data SKind = SKType
| SKArr STy SKind
deriving (Eq, Show)
data STy = STyConst (Name Ty)
| STyArr STy STy
deriving (Eq, Show) -- structural equality is OK here, since there
-- are no bound variables. Otherwise we could
-- use 'aeq' from
-- Generics.RepLib.Bind.LocallyNameless.
$(derive [''SKind, ''STy])
class Erasable t where
type Erased t :: *
erase :: t -> Erased t
instance Erasable Kind where
type Erased Kind = SKind
erase Type = SKType
erase (KPi b) = SKArr (erase ty) (erase k)
where ((_, Annot ty), k) = unsafeUnbind b
-- this is actually safe since we ignore the name
-- and promise to erase it from k.
instance Erasable Ty where
type Erased Ty = STy
erase (TyPi b) = STyArr (erase t1) (erase t2)
where ((_, Annot t1), t2) = unsafeUnbind b
erase (TyApp ty _) = erase ty
erase (TyConst c) = STyConst c
instance Erasable Tm where
type Erased Tm = Tm
erase t = t
instance (Erasable a, Erasable b) => Erasable (a,b) where
type Erased (a,b) = (Erased a, Erased b)
erase (x,y) = (erase x, erase y)
------------------------------
-- Signatures/contexts -------
------------------------------
data Context tm ty = C (M.Map (Name Tm) tm) (M.Map (Name Ty) ty)
emptyCtx :: Context tm ty
emptyCtx = C M.empty M.empty
extendTm :: Name Tm -> tm -> Context tm ty -> Context tm ty
extendTm x t (C tm ty) = C (M.insert x t tm) ty
extendTy :: Name Ty -> ty -> Context tm ty -> Context tm ty
extendTy x k (C tm ty) = C tm (M.insert x k ty)
lookupTm :: Name Tm -> TcM (Context tm ty) tm
lookupTm x = ask >>= \(C tm _) -> embedMaybe (text $ "Not in scope: term variable " ++ show x)
(M.lookup x tm)
lookupTy :: Name Ty -> TcM (Context tm ty) ty
lookupTy x = ask >>= \(C _ ty) -> embedMaybe (text $ "Not in scope: type constant " ++ show x)
(M.lookup x ty)
embedMaybe :: Doc -> Maybe a -> TcM ctx a
embedMaybe errMsg m = case m of
Just a -> return a
Nothing -> err errMsg
addTrace :: Doc -> TcM ctx String
addTrace msg = do
chks <- getChkContext
trace <- vcat <$> mapM ppr chks
return . PP.render $ msg $+$ trace
embedEither :: (MonadError String m) => Either String a -> m a
embedEither = either throwError return
instance Erasable a => Erasable (M.Map k a) where
type Erased (M.Map k a) = M.Map k (Erased a)
erase = M.map erase
instance (Erasable tm, Erasable ty) => Erasable (Context tm ty) where
type Erased (Context tm ty) = Context (Erased tm) (Erased ty)
erase (C tm ty) = C (erase tm) (erase ty)
instance (Erasable a) => Erasable (Maybe a) where
type Erased (Maybe a) = Maybe (Erased a)
erase = fmap erase
type Ctx = Context (Ty, Maybe Tm) Kind
type SCtx = Erased Ctx
withTmBinding :: (MonadReader (Context (tm, Maybe Tm) ty) m, LFresh m)
=> Name Tm -> tm -> m r -> m r
withTmBinding x b = do
avoid [AnyName x] . local (extendTm x (b, Nothing))
withTmDefn :: (MonadReader (Context tm ty) m, LFresh m)
=> Name Tm -> tm -> m r -> m r
withTmDefn x b = do
avoid [AnyName x] . local (extendTm x b)
withTyBinding :: (MonadReader (Context tm ty) m, LFresh m)
=> Name Ty -> ty -> m r -> m r
withTyBinding x b = do
avoid [AnyName x] . local (extendTy x b)
-----------------------
-- Error reporting ----
-----------------------
-- Keep track of what we're in the middle of checking.
data Check = TyCheck Tm
| KCheck Ty
| SCheck Kind
| TmEq Tm Tm STy
| TyEq Ty Ty SKind
| KEq Kind Kind
| DeclCheck Decl
------------------------------
-- Typechecking monad --------
------------------------------
newtype TcM ctx a = TcM { unTcM :: ErrorT String (ReaderT ctx (ReaderT [Check] LFreshM)) a }
deriving (Functor, Applicative, Monad, MonadReader ctx, MonadPlus, MonadError String, LFresh)
getTcMAvoids :: TcM ctx (S.Set AnyName)
getTcMAvoids = TcM . lift . lift . lift $ getAvoids
getChkContext :: TcM ctx [Check]
getChkContext = TcM . lift . lift $ ask
-- | Continue a TcM computation, given a binding context, a checking
-- context, and a set of names to avoid.
contTcM :: TcM ctx a -> ctx -> [Check] -> S.Set AnyName -> Either String a
contTcM (TcM m) c chks nms = flip contLFreshM nms . flip runReaderT chks . flip runReaderT c . runErrorT $ m
-- | Run a TcM computation in an empty context.
runTcM :: TcM (Context tm ty) a -> Either String a
runTcM m = contTcM m emptyCtx [] S.empty
-- | Run a subcomputation with an erased context.
withErasedCtx :: TcM SCtx a -> TcM Ctx a
withErasedCtx m = do
c <- ask
chks <- getChkContext
nms <- getTcMAvoids
embedEither $ contTcM m (erase c) chks nms
-- | Run a subcomputation with another check pushed on the checking
-- context stack.
whileChecking :: Check -> TcM ctx a -> TcM ctx a
whileChecking chk m = do
c <- ask
chks <- getChkContext
nms <- getTcMAvoids
embedEither $ contTcM m c (chk:chks) nms
ensure errMsg b = if b then return () else errMsg >>= err
err msg = addTrace msg >>= throwError
matchErr :: Pretty a => a -> a -> TcM ctx Doc
matchErr x y = do
x' <- ppr x
y' <- ppr y
return $ text "Cannot match" <+> x' <+> text "with" <+> y'
unTyPi (TyPi bnd) = return bnd
unTyPi t = ppr t >>= \t' -> err $ text "Expected pi type, got" <+> t' <+> text "instead"
unKPi (KPi bnd) = return bnd
unKPi t = ppr t >>= \t' -> err $ text "Expected pi kind, got" <+> t' <+> text "instead"
isType Type = return ()
isType t = ppr t >>= \t' -> err $ text "Expected Type, got" <+> t' <+> text "instead"
------------------------------
-- Weak head reduction -------
------------------------------
-- Reduce a term to weak-head normal form, or return it unchanged if
-- it is not head-reducible. Works in erased or unerased contexts.
whr :: Tm -> TcM (Context (t, Maybe Tm) ty) Tm
whr (TmVar a) = (do
(_, Just defn) <- lookupTm a
whr defn)
`mplus`
return (TmVar a)
whr (TmApp (Lam b) m1) =
lunbind b $ \((x,_),m2) ->
whr $ subst x m1 m2
whr (TmApp m1 m2) = do
m1' <- whr m1
case m1' of
Lam _ -> whr (TmApp m1' m2)
_ -> return $ TmApp m1' m2
whr t = return t
------------------------------
-- Term equality -------------
------------------------------
-- Type-directed term equality. In context Delta, is M <==> N at
-- simple type tau?
tmEq :: Tm -> Tm -> STy -> TcM SCtx ()
tmEq m n t = whileChecking (TmEq m n t) $ tmEqWhr m n t
tmEqWhr :: Tm -> Tm -> STy -> TcM SCtx ()
tmEqWhr m n t = do
m' <- whr m
n' <- whr n
whileChecking (TmEq m' n' t) $ tmEq' m' n' t -- XXX
-- XXX todo: might be nice to have 'lfresh' and 'lfreshen', the
-- first NOT taking an argument
-- XXX todo: need nicer way of doing "string2Name"
-- Type-directed term equality on terms in WHNF
tmEq' :: Tm -> Tm -> STy -> TcM SCtx ()
tmEq' m n (STyArr t1 t2) = do
x <- lfresh (string2Name "_x")
withTmBinding x t1 $
tmEq (TmApp m (TmVar x)) (TmApp n (TmVar x)) t2
tmEq' m n a@(STyConst {}) = do
a' <- tmEqS m n
ensure (matchErr a a') $ a == a'
-- Structural term equality. Check whether two terms in WHNF are
-- structurally equal, and return their "approximate type" if so.
tmEqS :: Tm -> Tm -> TcM SCtx STy
tmEqS (TmVar a) (TmVar b) = do
ensure (matchErr a b) $ a == b
(tyA,_) <- lookupTm a -- XXX
return tyA
tmEqS (TmApp m1 m2) (TmApp n1 n2) = do
ty <- tmEqS m1 n1
case ty of
STyArr t2 t1 -> do
tmEq m2 n2 t2
return t1
_ -> do
ty' <- ppr ty
err $
text "Left-hand side of an application has type" <+> ty' <> text "; expecting an arrow type"
tmEqS t1 t2 = err $ text "Term mismatch"
------------------------------
-- Type equality -------------
------------------------------
-- Kind-directed type equality.
tyEq :: Ty -> Ty -> SKind -> TcM SCtx ()
tyEq ty1 ty2 k = whileChecking (TyEq ty1 ty2 k) $ tyEq' ty1 ty2 k
tyEq' (TyPi bnd1) (TyPi bnd2) SKType = -- XXX
lunbind2 bnd1 bnd2 $ \(Just ((x, Annot a1), a2, (_, Annot b1), b2)) -> do
tyEq a1 b1 SKType
withTmBinding x (erase a1) $ tyEq a2 b2 SKType
tyEq' a b SKType = do
t <- tyEqS a b
ensure (matchErr t SKType) $ t == SKType
tyEq' a b (SKArr t k) = do
x <- lfresh (string2Name "_x")
withTmBinding x t $ tyEq (TyApp a (TmVar x)) (TyApp b (TmVar x)) k
-- Structural type equality.
tyEqS :: Ty -> Ty -> TcM SCtx SKind
tyEqS (TyConst a) (TyConst b) = do
ensure (matchErr a b) $ a == b
lookupTy a
tyEqS (TyApp a m) (TyApp b n) = do
SKArr t k <- tyEqS a b -- XXX
tmEq m n t
return k
tyEqS t1 t2 = do
t1' <- ppr t1
t2' <- ppr t2
err $ text "Types are not equal: " <+> t1' <> comma <+> t2'
------------------------------
-- Kind equality -------------
------------------------------
-- Algorithmic kind equality.
kEq :: Kind -> Kind -> TcM SCtx ()
kEq Type Type = return ()
kEq k1@(KPi bnd1) k2@(KPi bnd2) = whileChecking (KEq k1 k2) $
lunbind bnd1 $ \((x, Annot a), k) ->
lunbind bnd2 $ \((_, Annot b), l) -> do
tyEq a b SKType
withTmBinding x (erase a) $ kEq k l
kEq k1 k2 = do
k1' <- ppr k1
k2' <- ppr k2
err $ text "Kinds are not equal:" <+> k1' <> comma <+> k2'
------------------------------
-- Type checking -------------
------------------------------
-- Compute the type of a term.
tyCheck :: Tm -> TcM Ctx Ty
tyCheck tm = whileChecking (TyCheck tm) $ tyCheck' tm
tyCheck' :: Tm -> TcM Ctx Ty
tyCheck' t@(TmVar x) = liftM fst $ lookupTm x
tyCheck' t@(TmApp m1 m2) = do
bnd <- unTyPi =<< tyCheck m1
a2 <- tyCheck m2
lunbind bnd $ \((x, Annot a2'), a1) -> do
withErasedCtx $ tyEq a2' a2 SKType
return $ subst x m2 a1
tyCheck' t@(Lam bnd) =
lunbind bnd $ \((x, Annot a1), m2) -> do
isType =<< kCheck a1
a2 <- withTmBinding x a1 $ tyCheck m2
return $ TyPi (bind (x, Annot a1) a2)
-- Compute the kind of a type.
kCheck :: Ty -> TcM Ctx Kind
kCheck ty = whileChecking (KCheck ty) $ kCheck' ty
kCheck' :: Ty -> TcM Ctx Kind
kCheck' (TyConst a) = lookupTy a
kCheck' (TyApp a m) = do
bnd <- unKPi =<< kCheck a
b <- tyCheck m
lunbind bnd $ \((x, Annot b'), k) -> do
withErasedCtx $ tyEq b' b SKType
return $ subst x m k
kCheck' (TyPi bnd) =
lunbind bnd $ \((x, Annot a1), a2) -> do
isType =<< kCheck a1
isType =<< (withTmBinding x a1 $ kCheck a2)
return Type
-- Check the validity of a kind.
sortCheck :: Kind -> TcM Ctx ()
sortCheck k = whileChecking (SCheck k) $ sortCheck' k
sortCheck' :: Kind -> TcM Ctx ()
sortCheck' Type = return ()
sortCheck' (KPi bnd) =
lunbind bnd $ \((x, Annot a), k) -> do
isType =<< kCheck a
withTmBinding x a $ sortCheck k
------------------------------------------------------------
-- Parser ------------------------------------------------
------------------------------------------------------------
type OpList = [Op]
mkOp :: Op -> PE.Operator String OpList Identity Tm
mkOp (Op a _ nm) = PE.Infix (TmApp . TmApp (TmVar nm) <$ sym (name2String nm))
(assoc a)
where assoc L = PE.AssocLeft
assoc R = PE.AssocRight
mkOpTable :: OpList -> PE.OperatorTable String OpList Identity Tm
mkOpTable = map (map mkOp) . groupBy ((==) `on` prec) . sortBy (flip $ comparing prec)
where prec (Op _ n _) = n
type LFParser = Parsec String OpList
lfParseTest :: Show a => LFParser a -> String -> IO ()
lfParseTest p = print . runParser p [] ""
------------------------------
-- Lexing --------------------
------------------------------
startStuff = letter <|> oneOf "!#$%&*+/<=>?@\\^|-~"
endStuff = alphaNum <|> oneOf "!#$%&*+/<=>?@\\^|-~"
reservedNames = ["type", "infix", "right", "left"]
++ [":", "=", ".", "->", "%", "{", "}", "(", ")"]
langDef = haskellDef { P.reservedNames = reservedNames
, P.reservedOpNames = reservedNames
, P.identStart = startStuff
, P.identLetter = endStuff
, P.opStart = startStuff
, P.opLetter = endStuff
}
lexer = P.makeTokenParser langDef
parens = P.parens lexer
braces = P.braces lexer
brackets = P.brackets lexer
sym = P.symbol lexer
op = P.reservedOp lexer
reserved = P.reserved lexer
natural = P.natural lexer
var = string2Name <$> P.identifier lexer
------------------------------
-- Terms ---------------------
------------------------------
parseTm :: LFParser Tm
parseTm = parseTmExpr `chainl1` (pure TmApp)
parseTmExpr :: LFParser Tm
parseTmExpr = do
ops <- getState
PE.buildExpressionParser (mkOpTable ops) parseAtom
parseAtom :: LFParser Tm
parseAtom = parens parseTm
<|> TmVar <$> var
<|> Lam <$> (
bind
<$> brackets ((,) <$> var
<*> (Annot <$> (sym ":" *> parseTy))
)
<*> parseTm
)
------------------------------
-- Types ---------------------
------------------------------
parseTy :: LFParser Ty
parseTy =
-- ty ::=
-- [x:ty] ty
TyPi <$> (bind
<$> braces ((,) <$> var
<*> (Annot <$> (sym ":" *> parseTy))
)
<*> parseTy)
-- te -> ty
<|> try (TyPi <$> (bind
<$> ((,) (string2Name "_") . Annot <$> parseTyExpr)
<*> (op "->" *> parseTy)
))
-- te
<|> parseTyExpr
-- XXX this does not handle type expressions built using infix type operators!
parseTyExpr :: LFParser Ty
-- te ::= ta [tm ...]
parseTyExpr = foldl TyApp <$> parseTyAtom <*> many parseAtom
parseTyAtom :: LFParser Ty
parseTyAtom =
-- ta ::=
-- (ty)
parens parseTy
-- x
<|> TyConst <$> var
------------------------------
-- Kinds ---------------------
------------------------------
parseKind :: LFParser Kind
parseKind =
-- k ::=
-- {x:ty} k
KPi <$> (bind
<$> braces ((,) <$> var
<*> (Annot <$> (sym ":" *> parseTy))
)
<*> parseKind)
-- ka -> k
<|> try (KPi <$> (bind
<$> ((,) (string2Name "_") . Annot <$> parseTyExpr)
<*> (op "->" *> parseKind)
))
-- ka
<|> parseKindAtom
parseKindAtom :: LFParser Kind
parseKindAtom =
-- ka ::=
-- (k)
parens parseKind
-- Type
<|> try (Type <$ reserved "type")
------------------------------
-- Declarations --------------
------------------------------
parseDecl :: LFParser Decl
parseDecl = declBody <* sym "."
where
declBody =
DeclInfix <$> (Op <$> (sym "%" *> reserved "infix" *> rl)
<*> natural
<*> var)
<|> try (DeclTy <$> var
<*> (sym ":" *> parseKind))
<|> try (DeclTm <$> var
<*> (sym ":" *> (Just <$> parseTy))
<*> optionMaybe (sym "=" *> parseTm))
<|> DeclTm <$> var
<*> pure Nothing
<*> (sym "=" *> (Just <$> parseTm))
rl = (L <$ reserved "left") <|> (R <$ reserved "right")
------------------------------
-- Programs ------------------
------------------------------
parseProg :: LFParser Prog
parseProg =
-- stop at eof
[] <$ eof
<|> do d <- parseDecl -- parse a single decl
case d of -- add fixity declarations to the state
DeclInfix op -> modifyState (op:)
_ -> return ()
(d:) <$> parseProg -- parse the rest of the program
----------------------------------------
-- Pretty-printing ---------------------
----------------------------------------
class Pretty p where
ppr :: (LFresh m, Functor m) => p -> m Doc
instance Pretty (Name a) where
ppr = return . text . show
dot = text "."
instance Pretty Decl where
ppr (DeclTy t k) = do
t' <- ppr t
k' <- ppr k
return $ t' <+> colon <+> k' <> dot
ppr (DeclTm x mty mdef) = do
x' <- ppr x
tyf <- case mty of
Nothing -> return id
Just ty -> do ty' <- ppr ty
return (<+> (colon <+> ty'))
deff <- case mdef of
Nothing -> return id
Just def -> do def' <- ppr def
return (<+> (text "=" <+> def'))
return $ (deff . tyf $ x') <> dot
ppr (DeclInfix op) = do
op' <- ppr op
return $ op' <> dot
instance Pretty Op where
ppr (Op assoc prec op) = do
op' <- ppr op
return $
text "%infix"
<+> text (case assoc of L -> "left"; R -> "right")
<+> integer prec
<+> op'
instance Pretty Kind where
ppr Type = return $ text "type"
ppr (KPi bnd) = lunbind bnd $ \((x, Annot ty), k) -> do
x' <- ppr x
ty' <- ppr ty
k' <- ppr k
if x `S.member` fv k
then return $ PP.braces (x' <> colon <> ty') <+> k'
else return $ PP.parens ty' <+> text "->" <+> k'
instance Pretty Ty where
ppr (TyApp ty tm) = do
ty' <- ppr ty
tm' <- ppr tm
return $ ty' <+> PP.parens tm'
ppr (TyConst c) = ppr c
ppr (TyPi bnd) = lunbind bnd $ \((x, Annot ty1), ty2) -> do
x' <- ppr x
ty1' <- ppr ty1
ty2' <- ppr ty2
if x `S.member` fv ty2
then return $ PP.braces (x' <> colon <> ty1') <+> ty2'
else return $ PP.parens ty1' <+> text "->" <+> ty2'
instance Pretty STy where
ppr sty = ppr (uneraseTy sty)
uneraseTy (STyConst c) = TyConst c
uneraseTy (STyArr t1 t2) = TyPi (bind (string2Name "_", Annot (uneraseTy t1)) (uneraseTy t2))
uneraseK SKType = Type
uneraseK (SKArr sty sk) = KPi (bind (string2Name "_", Annot (uneraseTy sty)) (uneraseK sk))
instance Pretty SKind where
ppr sk = ppr (uneraseK sk)
instance Pretty Tm where
ppr (TmVar x) = ppr x
ppr (TmApp tm1 tm2) = do
tm1' <- ppr tm1
tm2' <- ppr tm2
return $ tm1' <+> PP.parens tm2'
ppr (Lam bnd) = lunbind bnd $ \((x, Annot ty), tm) -> do
x' <- ppr x
ty' <- ppr ty
tm' <- ppr tm
return $ PP.brackets (x' <> colon <> ty') <+> tm'
instance Pretty Check where
ppr (TyCheck tm) =
(text "While checking the type of:" <+>) <$> ppr tm
ppr (KCheck ty) =
(text "While checking the kind of:" <+>) <$> ppr ty
ppr (SCheck k) =
(text "While checking the sort of:" <+>) <$> ppr k
ppr (TmEq m n ty) = do
m' <- ppr m
n' <- ppr n
ty' <- ppr ty
return $ text "While checking that terms:"
$+$ nest 4 (m' $+$ n')
$+$ nest 2 (text "are equal at type")
$+$ nest 4 ty'
ppr (TyEq t1 t2 k) = do
t1' <- ppr t1
t2' <- ppr t2
k' <- ppr k
return $ text "While checking that types:"
$+$ nest 4 (t1' $+$ t2')
$+$ nest 2 (text "are equal at kind")
$+$ nest 4 k'
ppr (KEq k1 k2) = do
k1' <- ppr k1
k2' <- ppr k2
return $ text "While checking equality of kinds:"
$+$ nest 4 (k1' $+$ k2')
ppr (DeclCheck decl) = do
d' <- ppr decl
return $ text "While checking the declaration:" $+$ nest 4 d'
------------------------------
-- Typechecking programs -----
------------------------------
checkProg :: Prog -> TcM Ctx ()
checkProg [] = return ()
checkProg (DeclInfix _ : ds) = checkProg ds
checkProg (d@(DeclTy nm k) : ds) = do
whileChecking (DeclCheck d) $ sortCheck k
withTyBinding nm k $ checkProg ds
checkProg ((DeclTm nm Nothing Nothing):_) = do
throwError $ "Term " ++ show nm
++ " has no type or definition! (This shouldn't happen.)"
checkProg (d@(DeclTm nm (Just ty) Nothing) : ds) = do
whileChecking (DeclCheck d) $ isType =<< kCheck ty
withTmBinding nm ty $ checkProg ds
checkProg (d@(DeclTm nm Nothing (Just def)) : ds) = do
ty <- whileChecking (DeclCheck d) $ tyCheck def
withTmDefn nm (ty, Just def) $ checkProg ds
checkProg (d@(DeclTm nm (Just ty) (Just def)) : ds) = do
whileChecking (DeclCheck d) $ do
isType =<< kCheck ty
ty' <- tyCheck def
withErasedCtx $ tyEq ty ty' SKType
withTmDefn nm (ty, Just def) $ checkProg ds
checkLF :: [FilePath] -> IO ()
checkLF fileNames = do
files <- mapM readFile fileNames
case runParser parseProg [] "" (concat files) of
Left err -> print err
Right prog -> do
-- putStrLn . unlines . map render . runLFreshM . mapM ppr $ prog
putStrLn . either ("Error: "++) (const "OK!") . runTcM . checkProg $ prog
main = do
fileNames <- getArgs
checkLF fileNames