Copyright | © 2019 Elias Castegren and Kiko Fernandez-Reyes |
---|---|
License | MIT |
Stability | experimental |
Portability | portable |
Safe Haskell | None |
This module includes everything you need to get started type checking a program. To build the Abstract Syntax Tree (AST), please import and build Applicative.AST.
The main entry point to the type checker is the combinator tcProgram
, which
takes an AST and returns either a list of errors, or the typed program.
For example, for the following program (using a made up syntax):
class C val f: Foo
should be parsed to generate this AST:
testClass1 = ClassDef {cname = "C" ,fields = [FieldDef {fmod = Val, fname = "f", ftype = ClassType "Foo"}] ,methods = []}
To type check the AST, run the tcProgram
combinator as follows:
tcProgram testClass1
This is an increment on top of the Typechecker
module,
that refactors the type checker to be able to throw multiple errors.
Synopsis
- data Except err a
- throwError :: TCError -> Except TCErrors a
- newtype TCErrors = TCErrors (NonEmpty TCError)
- data TCError
- = UnknownClassError Name
- | UnknownFieldError Name
- | UnknownMethodError Name
- | UnboundVariableError Name
- | TypeMismatchError Type Type
- | ImmutableFieldError Expr
- | NonLValError Expr
- | PrimitiveNullError Type
- | NonClassTypeError Type
- | NonArrowTypeError Type
- | ConstructorCallError Type
- | UninferrableError Expr
- | MultipleErrors [TCError]
- data Env = Env {}
- emptyEnv :: Env
- lookupClass :: Env -> Name -> Except TCErrors ClassDef
- lookupField :: Env -> Type -> Name -> Except TCErrors FieldDef
- lookupMethod :: Env -> Type -> Name -> Except TCErrors MethodDef
- lookupVar :: Env -> Name -> Except TCErrors Type
- genEnv :: Program -> Env
- addVariable :: Env -> Name -> Type -> Env
- addParameters :: Env -> [Param] -> Env
- tcProgram :: Program -> Except TCErrors Program
- class Typecheckable a where
- hasType :: Env -> Expr -> Type -> Except TCErrors Expr
- testClass1 :: ClassDef
- testClass2 :: ClassDef
- testProgram :: Program
Documentation
Define our own Exception datatype to be able to redefine the Applicative interface
Declaration of type checking errors. An error will (usually) be
created using the helper function throwError
. As an example:
throwError $ DuplicateClassError (Name "Foo")
throws an error that indicates that the class is defined multiple times.
Declaration of a type checking errors
UnknownClassError Name | Reference of a class that does not exists |
UnknownFieldError Name | Reference of a field that does not exists |
UnknownMethodError Name | Reference of a method that does not exists |
UnboundVariableError Name | Unbound variable |
TypeMismatchError Type Type | Type mismatch error, the first |
ImmutableFieldError Expr | Immutable field error, used when someone violates immutability |
NonLValError Expr | Error to indicate that a one cannot assign a value to expression |
PrimitiveNullError Type | Error indicating that the return type cannot be |
NonClassTypeError Type | Used to indicate that |
NonArrowTypeError Type | Expecting a function (arrow) type but got another type instead. |
ConstructorCallError Type | Tried to call a constructor outside of instantiation |
UninferrableError Expr | Cannot infer type of |
MultipleErrors [TCError] | Contains multiple errors during type checking |
Environment. The Env
is used during type checking, and is updated as
the type checker runs. Most likely, one uses the Reader
monad to hide details
of how the environment is updated, via the common local
function.
lookupMethod :: Env -> Type -> Name -> Except TCErrors MethodDef Source #
Find a method declaration by its Type
(ty
) and field name f
addVariable :: Env -> Name -> Type -> Env Source #
Add a variable name and its type to the environment Env
.
tcProgram :: Program -> Except TCErrors Program Source #
Main entry point of the type checker. This function type checks an AST returning either a list of errors or a well-typed program. For instance, assuming the following made up language: > > class C > val f: Foo >
it should be parsed to generate the following AST:
testClass1 = ClassDef {cname = "C" ,fields = [FieldDef {fmod = Val, fname = "f", ftype = ClassType "Foo"}] ,methods = []}
To type check the AST, run the tcProgram
combinator as follows:
tcProgram testClass1
which either returns a list of errors or the resulting typed AST.
class Typecheckable a where Source #
The type class defines how to type check an AST node.
hasType :: Env -> Expr -> Type -> Except TCErrors Expr Source #
This combinator is used whenever a certain type is expected. This function is quite important. Here follows an example:
doTypecheck mdef@(MethodDef {mparams, mbody, mtype}) = do -- typecheck the well-formedness of types of method parameters mparams' <- mapM typecheck mparams mtype' <- typecheck mtype -- extend environment with method parameters and typecheck body mbody' <- local (addParameters mparams) $ hasType mbody mtype' ...
in the last line, because we are type checking a method declaration,
it is statically known what should be the return type of the function body. In these
cases, one should use the hasType
combinator.
testClass1 :: ClassDef Source #
Class definition for didactic purposes. This AST represents the following
class, which is named C
, contains an immutable field f
of type Foo
:
class C: val f: Foo
This class is ill-typed, as there is no declaration of Foo
anywhere.
To check how to type checker catches this error, run:
tcProgram (Program [testClass1])
testClass2 :: ClassDef Source #
Test program with a class, field, method, and variable access. The class Bar
does not exist in the environment. The variable access is unbound.
This program is the AST equivalent of the following syntax:
class D val g: Bar def m(): Int x