Minor rewrite of tc. Some bugs still left
This commit is contained in:
parent
6218efac20
commit
5d247057f5
6 changed files with 256 additions and 128 deletions
|
|
@ -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 | '_')*) ;
|
||||||
|
|
||||||
|
|
|
||||||
21
src/Main.hs
21
src/Main.hs
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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])
|
||||||
|
|
|
||||||
|
|
@ -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
|
-- Name is only here for proper error messages
|
||||||
infT <- inferExp e
|
RBound num name ->
|
||||||
when (t /= infT) (throwError AnnotatedMismatch)
|
M.lookup num <$> St.gets vars >>= \case
|
||||||
return infT
|
Nothing -> throwError UnboundVar
|
||||||
|
Just t -> return t
|
||||||
|
|
||||||
Old.EConst c -> case c of
|
RFree name -> do
|
||||||
(Old.CInt i) -> return (TMono "Int")
|
M.lookup name <$> St.gets sigs >>= \case
|
||||||
(Old.CStr s) -> return (TMono "String")
|
Nothing -> throwError UnboundVar
|
||||||
|
Just t -> return t
|
||||||
|
|
||||||
Old.EAdd e1 e2 -> do
|
RConst (CInt _) -> return $ TMono "Int"
|
||||||
let int = TMono "Int"
|
RConst (CStr _) -> return $ TMono "Str"
|
||||||
updateBound e1 int
|
|
||||||
updateBound e2 int
|
|
||||||
inf1 <- inferExp e1
|
|
||||||
inf2 <- inferExp e2
|
|
||||||
when (not $ isInt inf1 && isInt inf2) (throwError TypeMismatch)
|
|
||||||
return int
|
|
||||||
|
|
||||||
-- Incomplete and probably wrong
|
-- Currently does not accept using a polymorphic type as the function.
|
||||||
Old.EApp e1 e2 -> do
|
RApp expr1 expr2 -> do
|
||||||
inferExp e1 >>= \case
|
typ1 <- inferExp expr1
|
||||||
TArrow mono@(TMono i) t2 -> do
|
typ2 <- inferExp expr2
|
||||||
t <- inferExp e2
|
fit typ2 typ1
|
||||||
when (t /= mono) (throwError TypeMismatch)
|
|
||||||
return t2
|
|
||||||
|
|
||||||
TArrow poly@(TPoly f) t2 -> do
|
RAdd expr1 expr2 -> do
|
||||||
t <- inferExp e2
|
typ1 <- inferExp expr1
|
||||||
when (not $ t `subtype` t) (throwError TypeMismatch)
|
typ2 <- inferExp expr2
|
||||||
return t2
|
when (not $ (isInt typ1 || isPoly typ1) && (isInt typ2 || isPoly typ2)) (throwError TypeMismatch)
|
||||||
|
specifyType expr1 (TMono "Int")
|
||||||
|
specifyType expr2 (TMono "Int")
|
||||||
|
return (TMono "Int")
|
||||||
|
|
||||||
-- This is not entirely correct. The assumed type can change.
|
RAbs num name expr -> do
|
||||||
Old.EAbs i e -> do
|
insertVars num (TPoly "a")
|
||||||
let assume = (TPoly "a")
|
typ <- inferExp expr
|
||||||
St.modify (M.insert i assume)
|
newTyp <- lookupVars num
|
||||||
infT <- R.local (insertEnv i assume) (inferExp e)
|
return $ TArrow newTyp typ
|
||||||
St.gets (M.lookup i) >>= \case
|
|
||||||
Nothing -> todo
|
|
||||||
Just x -> return (TArrow x infT)
|
|
||||||
|
|
||||||
Old.ELet i e1 e2 -> todo
|
|
||||||
|
|
||||||
-- 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"
|
|
||||||
|
|
|
||||||
|
|
@ -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 ".")
|
||||||
|
]
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1 @@
|
||||||
letters = let x = 1
|
testType f x = f x
|
||||||
in let y = 2
|
|
||||||
in let z = 3
|
|
||||||
in x + y + z
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue