uu-cco-examples-0.1.0.1: src/CCO/ArithBool/AG/Typing.ag
imports
{
import CCO.Printing
import CCO.SourcePos (SourcePos)
import CCO.Tree (ATerm (App), Tree (fromTree, toTree))
import CCO.Tree.Parser (parseTree, app)
import Control.Applicative (Applicative (pure))
}
-------------------------------------------------------------------------------
-- Syntax
-------------------------------------------------------------------------------
{
-- | Type of types.
-- Join-semilattice with greatest element 'Top'.
data Ty = Nat | Bool | Top deriving (Eq, Show)
instance Tree Ty where
fromTree Nat = App "Nat" []
fromTree Bool = App "Bool" []
fromTree Top = App "Top" []
toTree = parseTree [ app "Nat" (pure Nat )
, app "Bool" (pure Bool)
, app "Top" (pure Top )
]
-- | Retrieves whether two 'Ty's match.
-- Two 'Ty's match if they are the same or if one of them is 'Top'.
match :: Ty -> Ty -> Bool
match Top _ = True
match _ Top = True
match ty1 ty2 = ty1 == ty2
-- | Retrieves the least upper bound of two 'Ty's.
lub :: Ty -> Ty -> Ty
lub ty1 ty2 = if ty1 == ty2 then ty1 else Top
}
-------------------------------------------------------------------------------
-- Typing
-------------------------------------------------------------------------------
attr Tm Tm_
syn ty :: {Ty}
syn tyErrs use {++} {[]} :: {[TyErr]}
sem Tm_
| Num lhs.ty = Nat
| False_ True_ lhs.ty = Bool
| If lhs.ty = @t2.ty `lub` @t3.ty
| Add Sub Mul Div lhs.ty = Nat
| Lt Eq Gt lhs.ty = Bool
sem Tm_
| If lhs.tyErrs = @t1.tyErrs ++ @t2.tyErrs ++ @t3.tyErrs ++
checkTyGuard @t1.pos @t1.ty ++
checkTyBranches @t3.pos @t2.ty @t3.ty
{
-- | Checks the type of the guard of a conditional.
checkTyGuard :: SourcePos -> Ty -> [TyErr]
checkTyGuard _ ty | ty `match` Bool = []
checkTyGuard pos ty = [TyErr pos descr Bool ty]
where
descr = "guard of a conditional should be a boolean"
-- | Checks that both branches of a conditional have the same type.
checkTyBranches :: SourcePos -> Ty -> Ty -> [TyErr]
checkTyBranches pos tyThen tyElse
| tyThen `match` tyElse = []
| otherwise = [TyErr pos descr tyThen tyElse]
where
descr = "branches of a conditional should have the same type"
}
sem Tm_
| Add Sub Mul Div lhs.tyErrs = @t1.tyErrs ++ @t2.tyErrs ++
checkTyArithOp @t1.pos @t1.ty ++
checkTyArithOp @t2.pos @t2.ty
{
-- | Checks the type of an operand of an arithmetic operator.
checkTyArithOp :: SourcePos -> Ty -> [TyErr]
checkTyArithOp _ ty | ty `match` Nat = []
checkTyArithOp pos ty = [TyErr pos descr Nat ty]
where
descr = "operand of an arithmetic operator should be a natural number"
}
sem Tm_
| Lt Eq Gt lhs.tyErrs = @t1.tyErrs ++ @t2.tyErrs ++
checkTyRelOp @t1.pos @t1.ty ++
checkTyRelOp @t2.pos @t2.ty
{
-- | Checks the type of an operand of a relational operator.
checkTyRelOp :: SourcePos -> Ty -> [TyErr]
checkTyRelOp _ ty | ty `match` Nat = []
checkTyRelOp pos ty = [TyErr pos descr Nat ty]
where
descr = "operand of a relational operator should be a natural number"
}
-------------------------------------------------------------------------------
-- Type errors
-------------------------------------------------------------------------------
{
-- | Type of type errors.
data TyErr
= TyErr SourcePos String Ty Ty -- ^ Holds a source position, a description,
-- the expected type and the inferred
-- type.
instance Printable TyErr where
pp = ppTyErr
}
-------------------------------------------------------------------------------
-- Pretty printing type errors
-------------------------------------------------------------------------------
{
-- | Pretty prints a type error message.
ppTyErr :: TyErr -> Doc
ppTyErr (TyErr pos descr expected inferred)
= above [ppHeader, text " ", ppExpected, ppInferred]
where
ppHeader = wrapped $
describeSourcePos pos ++ ": Type error: " ++ descr ++ "."
ppExpected = text "? expected : " >|< showable expected
ppInferred = text "? inferred : " >|< showable inferred
}