Minor rewrite of tc. Some bugs still left

This commit is contained in:
sebastianselander 2023-02-14 22:03:56 +01:00
parent 6218efac20
commit 5d247057f5
6 changed files with 256 additions and 128 deletions

View file

@ -3,7 +3,7 @@ Program. Program ::= [Bind] ;
Bind. Bind ::= Ident [Ident] "=" Exp ; Bind. Bind ::= Ident [Ident] "=" Exp ;
EAnn. Exp5 ::= Exp5 ":" Type ; EAnn. Exp5 ::= "(" Exp ":" Type ")" ;
EId. Exp4 ::= Ident ; EId. Exp4 ::= Ident ;
EConst. Exp4 ::= Const ; EConst. Exp4 ::= Const ;
EApp. Exp3 ::= Exp3 Exp4 ; EApp. Exp3 ::= Exp3 Exp4 ;
@ -16,8 +16,9 @@ CStr. Const ::= String ;
TMono. Type ::= "Mono" Ident ; TMono. Type ::= "Mono" Ident ;
TPoly. Type ::= "Poly" Ident ; TPoly. Type ::= "Poly" Ident ;
TArrow. Type ::= Type "->" Type1 ; TArrow. Type ::= Type1 "->" Type ;
-- This doesn't seem to work so we'll have to live with ugly keywords for now
-- token Upper (upper (letter | digit | '_')*) ; -- token Upper (upper (letter | digit | '_')*) ;
-- token Lower (lower (letter | digit | '_')*) ; -- token Lower (lower (letter | digit | '_')*) ;

View file

@ -5,8 +5,9 @@ import Grammar.Par (myLexer, pProgram)
import Grammar.Print (printTree) import Grammar.Print (printTree)
import System.Environment (getArgs) import System.Environment (getArgs)
import System.Exit (exitFailure, exitSuccess) import System.Exit (exitFailure, exitSuccess)
import TypeChecker.TypeChecker (typecheck) import TypeChecker.TypeChecker (typecheck)
import Renamer.Renamer (rename) import Renamer.Renamer (rename)
import Grammar.Print (prt)
main :: IO () main :: IO ()
main = getArgs >>= \case main = getArgs >>= \case
@ -19,10 +20,14 @@ main = getArgs >>= \case
putStrLn err putStrLn err
exitFailure exitFailure
Right prg -> case rename prg of Right prg -> case rename prg of
Right prg -> do
putStrLn "RENAME SUCCESSFUL"
putStrLn $ printTree prg
Left err -> do Left err -> do
putStrLn "FAILED RENAMING" putStrLn "FAILED RENAMING"
putStrLn . show $ err putStrLn . show $ err
exitFailure exitFailure
Right prg -> case typecheck prg of
Left err -> do
putStrLn "TYPECHECK ERROR"
putStrLn . show $ err
exitFailure
Right prg -> do
putStrLn . printTree $ prg

View file

@ -1,6 +1,12 @@
{-# LANGUAGE LambdaCase #-} {-# LANGUAGE LambdaCase #-}
module Renamer.RenamerIr (module Grammar.Abs, RExp (..), RBind (..), RProgram (..)) where module Renamer.RenamerIr ( RExp (..)
, RBind (..)
, RProgram (..)
, Const (..)
, Ident (..)
, Type (..)
) where
import Grammar.Abs ( import Grammar.Abs (
Bind (..), Bind (..),
@ -43,6 +49,7 @@ instance Print RBind where
instance Print RExp where instance Print RExp where
prt i = \case prt i = \case
RAnn e t -> prPrec i 2 (concatD [prt 0 e, doc (showString ":"), prt 1 t])
RBound n _ -> prPrec i 3 (concatD [prt 0 ("var" ++ show n)]) RBound n _ -> prPrec i 3 (concatD [prt 0 ("var" ++ show n)])
RFree id -> prPrec i 3 (concatD [prt 0 id]) RFree id -> prPrec i 3 (concatD [prt 0 id])
RConst n -> prPrec i 3 (concatD [prt 0 n]) RConst n -> prPrec i 3 (concatD [prt 0 n])

View file

@ -1,6 +1,6 @@
{-# LANGUAGE LambdaCase, OverloadedStrings, OverloadedRecordDot #-} {-# LANGUAGE LambdaCase, OverloadedStrings, OverloadedRecordDot #-}
module TypeChecker.TypeChecker (typecheck) where module TypeChecker.TypeChecker where
import Control.Monad (when, void) import Control.Monad (when, void)
import Control.Monad.Except (ExceptT, throwError, runExceptT) import Control.Monad.Except (ExceptT, throwError, runExceptT)
@ -13,15 +13,11 @@ import qualified Control.Monad.State as St
import Data.Functor.Identity (Identity, runIdentity) import Data.Functor.Identity (Identity, runIdentity)
import Data.Map (Map) import Data.Map (Map)
import qualified Data.Map as M import qualified Data.Map as M
import Data.Set (Set)
import qualified Data.Set as S
import Data.Bool (bool)
import qualified Grammar.Abs as Old
import Grammar.ErrM (Err) import Grammar.ErrM (Err)
import TypeChecker.TypeCheckerIr import TypeChecker.TypeCheckerIr
data Ctx = Ctx { env :: Map Ident Type data Ctx = Ctx { vars :: Map Integer Type
, sigs :: Map Ident Type , sigs :: Map Ident Type
} }
deriving Show deriving Show
@ -34,121 +30,165 @@ programmer.
-} -}
type Check = StateT (Map Ident Type) (ReaderT Ctx (ExceptT Error Identity)) type Infer = StateT Ctx (ExceptT Error Identity)
initEnv :: Ctx initEnv :: Ctx
initEnv = initEnv = Ctx mempty mempty
Ctx { env = mempty
, sigs = mempty
}
run :: Check Type -> Either Error Type run :: Infer a -> Either Error a
run = runIdentity . runExceptT . flip R.runReaderT initEnv . flip St.evalStateT mempty run = runIdentity . runExceptT . flip St.evalStateT initEnv
typecheck :: Old.Program -> Either Error () typecheck :: RProgram -> Either Error TProgram
typecheck = todo typecheck = run . inferPrg
inferPrg :: Old.Program -> Check () inferPrg :: RProgram -> Infer TProgram
inferPrg (Program [x]) = void $ inferBind x inferPrg (RProgram xs) = do
xs' <- mapM inferBind xs
return $ TProgram xs'
inferBind :: Old.Bind -> Check () inferBind :: RBind -> Infer TBind
inferBind (Bind _ _ e) = void $ inferExp e inferBind (RBind name e) = do
t <- inferExp e
e' <- toTExpr e
return $ TBind name t e'
inferExp :: Old.Exp -> Check Type toTExpr :: RExp -> Infer TExp
toTExpr = \case
re@(RAnn e t) -> do
t <- inferExp re
e' <- toTExpr e
return $ TAnn e' t
re@(RBound num name) -> do
t <- inferExp re
return $ TBound num name t
re@(RFree name) -> do
t <- inferExp re
return $ TFree name t
re@(RConst con)-> do
t <- inferExp re
return $ TConst con t
re@(RApp e1 e2) -> do
t <- inferExp re
e1' <- toTExpr e1
e2' <- toTExpr e2
return $ TApp e1' e2' t
re@(RAdd e1 e2)-> do
t <- inferExp re
e1' <- toTExpr e1
e2' <- toTExpr e2
return $ TAdd e1' e2' t
re@(RAbs num name e) -> do
t <- inferExp re
e' <- toTExpr e
return $ TAbs num name e' t
inferExp :: RExp -> Infer Type
inferExp = \case inferExp = \case
-- TODO: Fix bound variable lookup RAnn expr typ -> do
Old.EId i -> do exprT <- inferExp expr
st <- St.get when (not (exprT == typ || isPoly exprT)) (throwError AnnotatedMismatch)
case lookupBound i st of return typ
Just t -> return t
Nothing -> do
ctx <- R.ask
case lookupEnv i ctx of
Just t -> return t
Nothing -> case lookupSigs i ctx of
Just t -> return t
Nothing -> throwError UnboundVar
Old.EAnn e t -> do
infT <- inferExp e
when (t /= infT) (throwError AnnotatedMismatch)
return infT
Old.EConst c -> case c of -- Name is only here for proper error messages
(Old.CInt i) -> return (TMono "Int") RBound num name ->
(Old.CStr s) -> return (TMono "String") M.lookup num <$> St.gets vars >>= \case
Nothing -> throwError UnboundVar
Just t -> return t
Old.EAdd e1 e2 -> do RFree name -> do
let int = TMono "Int" M.lookup name <$> St.gets sigs >>= \case
updateBound e1 int Nothing -> throwError UnboundVar
updateBound e2 int Just t -> return t
inf1 <- inferExp e1
inf2 <- inferExp e2
when (not $ isInt inf1 && isInt inf2) (throwError TypeMismatch)
return int
-- Incomplete and probably wrong RConst (CInt _) -> return $ TMono "Int"
Old.EApp e1 e2 -> do RConst (CStr _) -> return $ TMono "Str"
inferExp e1 >>= \case
TArrow mono@(TMono i) t2 -> do
t <- inferExp e2
when (t /= mono) (throwError TypeMismatch)
return t2
TArrow poly@(TPoly f) t2 -> do -- Currently does not accept using a polymorphic type as the function.
t <- inferExp e2 RApp expr1 expr2 -> do
when (not $ t `subtype` t) (throwError TypeMismatch) typ1 <- inferExp expr1
return t2 typ2 <- inferExp expr2
fit typ2 typ1
-- This is not entirely correct. The assumed type can change. RAdd expr1 expr2 -> do
Old.EAbs i e -> do typ1 <- inferExp expr1
let assume = (TPoly "a") typ2 <- inferExp expr2
St.modify (M.insert i assume) when (not $ (isInt typ1 || isPoly typ1) && (isInt typ2 || isPoly typ2)) (throwError TypeMismatch)
infT <- R.local (insertEnv i assume) (inferExp e) specifyType expr1 (TMono "Int")
St.gets (M.lookup i) >>= \case specifyType expr2 (TMono "Int")
Nothing -> todo return (TMono "Int")
Just x -> return (TArrow x infT)
Old.ELet i e1 e2 -> todo RAbs num name expr -> do
insertVars num (TPoly "a")
typ <- inferExp expr
newTyp <- lookupVars num
return $ TArrow newTyp typ
-- Aux -- Aux
-- Double check this function. It's bad and maybe wrong
subtype :: Type -> Type -> Bool
subtype (TMono t1) (TMono t2) = t1 == t2
subtype (TMono t1) (TPoly t2) = True
subtype (TPoly t2) (TMono t1) = False
subtype (TArrow t1 t2) (TArrow t3 t4) = t1 `subtype` t3 && t2 `subtype` t4
subtype _ _ = False
lookupEnv :: Ident -> Ctx -> Maybe Type
lookupEnv i = M.lookup i . env
lookupSigs :: Ident -> Ctx -> Maybe Type
lookupSigs i = M.lookup i . sigs
insertEnv :: Ident -> Type -> Ctx -> Ctx
insertEnv i t c = Ctx { env = M.insert i t c.env
, sigs = c.sigs
}
updateBound :: Old.Exp -> Type -> Check ()
updateBound (Old.EId i) t = St.modify (M.insert i t)
updateBound _ _ = return ()
isBound :: Old.Exp -> Check Bool
isBound (Old.EId i) = (M.member i) <$> St.get
isBound _ = return False
lookupBound :: Ident -> Map Ident Type -> Maybe Type
lookupBound = M.lookup
isInt :: Type -> Bool isInt :: Type -> Bool
isInt (TMono "Int") = True isInt (TMono "Int") = True
isInt (TPoly _) = True
isInt _ = False isInt _ = False
isArrow :: Type -> Bool
isArrow (TArrow _ _) = True
isArrow _ = False
isPoly :: Type -> Bool
isPoly (TPoly _) = True
isPoly _ = False
fit :: Type -> Type -> Infer Type
fit (TArrow t1 (TArrow t2 t3)) t4
| t1 == t4 = return $ TArrow t2 t3
| otherwise = fit (TArrow (TArrow t1 t2) t3) t4
fit (TArrow t1 t2) t3
| t1 == t3 = return t2
| otherwise = throwError TypeMismatch
fit _ _ = throwError TypeMismatch
-- a -> (b -> (c -> d))
-- a -> b
-- | Specify the type of a bound variable
-- Because in lambdas we have to assume a general type and update it
specifyType :: RExp -> Type -> Infer ()
specifyType (RBound num name) typ = do
insertVars num typ
return ()
specifyType _ _ = return ()
lookupVars :: Integer -> Infer Type
lookupVars i = do
st <- St.gets vars
case M.lookup i st of
Just t -> return t
Nothing -> throwError UnboundVar
lookupSigs :: Ident -> Infer Type
lookupSigs i = do
st <- St.gets sigs
case M.lookup i st of
Just t -> return t
Nothing -> throwError UnboundVar
insertVars :: Integer -> Type -> Infer ()
insertVars i t = do
st <- St.get
St.put ( Ctx { vars = M.insert i t st.vars, sigs = st.sigs } )
{-# WARNING todo "TODO IN CODE" #-}
todo :: a
todo = error "TODO in code"
data Error data Error
= TypeMismatch = TypeMismatch
| NotNumber | NotNumber
@ -161,18 +201,17 @@ data Error
-- Tests -- Tests
number :: Old.Exp lambda = RAbs 0 "x" (RAdd (RBound 0 "x") (RBound 0 "x"))
number = Old.EConst (CInt 3) lambda2 = RAbs 0 "x" (RAnn (RBound 0 "x") (TArrow (TMono "Int") (TMono "String")))
aToInt :: Old.Exp pp :: Type -> String
aToInt = Old.EAbs (Old.Ident "x") (Old.EAdd (Old.EConst (Old.CInt 3)) (Old.EConst (Old.CInt 3))) pp (TMono (Ident x)) = x
pp (TPoly (Ident x)) = x
pp (TArrow t1 t2) = pp t1 <> " -> " <> pp t2
intToInt :: Old.Exp int,str :: Type
intToInt = Old.EAbs (Old.Ident "x") (Old.EAdd (Old.EId $ Ident "x") (Old.EConst (Old.CInt 3))) int = TMono "Int"
str = TMono "Str"
addLambda :: Old.Exp arrow :: Type -> Type -> Type
addLambda = Old.EAbs (Old.Ident "x") (Old.EAdd (Old.EId $ Ident "x") (Old.EId $ Ident "x")) arrow = TArrow
{-# WARNING todo "TODO IN CODE" #-}
todo :: a
todo = error "TODO in code"

View file

@ -1,12 +1,25 @@
{-# LANGUAGE LambdaCase #-} {-# LANGUAGE LambdaCase #-}
module TypeChecker.TypeCheckerIr where module TypeChecker.TypeCheckerIr ( TProgram(..)
, TBind(..)
, TExp(..)
, RProgram(..)
, RBind(..)
, RExp(..)
, Type(..)
, Const(..)
, Ident(..)
)
where
import Renamer.RenamerIr import Renamer.RenamerIr
import Grammar.Print
data TProgram = TProgram [TBind] data TProgram = TProgram [TBind]
deriving (Eq, Show, Read, Ord)
data TBind = TBind Ident Type TExp data TBind = TBind Ident Type TExp
deriving (Eq, Show, Read, Ord)
data TExp data TExp
= TAnn TExp Type = TAnn TExp Type
@ -17,3 +30,69 @@ data TExp
| TAdd TExp TExp Type | TAdd TExp TExp Type
| TAbs Integer Ident TExp Type | TAbs Integer Ident TExp Type
deriving (Eq, Ord, Show, Read) deriving (Eq, Ord, Show, Read)
instance Print TProgram where
prt i = \case
TProgram defs -> prPrec i 0 (concatD [prt 0 defs])
instance Print TBind where
prt i = \case
TBind x t e ->
prPrec i 0 $
concatD
[ prt 0 x
, doc (showString ":")
, prt 0 t
, doc (showString "=")
, prt 0 e
]
instance Print TExp where
prt i = \case
TAnn e t -> prPrec i 2 $ concatD
[ prt 0 e
, doc (showString ":")
, prt 1 t
]
TBound _ u t -> prPrec i 3 $ concatD
[ doc (showString "(")
, prt 0 u
, doc (showString ":")
, prt 0 t
, doc (showString ")")
]
TFree u t -> prPrec i 3 $ concatD
[ doc (showString "(")
, prt 0 u
, doc (showString ":")
, prt 0 t
, doc (showString ")")
]
TConst c _ -> prPrec i 3 (concatD [prt 0 c])
TApp e e1 t -> prPrec i 2 $ concatD
[ doc (showString "(")
, prt 2 e
, prt 3 e1
, doc (showString ")")
, doc (showString ":")
, prt 0 t
]
TAdd e e1 t -> prPrec i 1 $ concatD
[ doc (showString "(")
, prt 1 e
, doc (showString "+")
, prt 2 e1
, doc (showString ")")
, doc (showString ":")
, prt 0 t
]
TAbs _ u e t -> prPrec i 0 $ concatD
[ doc (showString "(")
, doc (showString "\\")
, prt 0 u
, doc (showString ".")
, prt 0 e
, doc (showString ")")
, doc (showString ":")
, prt 0 t, doc (showString ".")
]

View file

@ -1,4 +1 @@
letters = let x = 1 testType f x = f x
in let y = 2
in let z = 3
in x + y + z