| Home
Introduction
Operation Schemas
User Manual
Directory Structure
Parser
Model Extractor
Type Checker
Modifications
Extending the
compiler
Outstanding Issues
Current status
References and Links
Javadoc |
Type Checker
The classes for type checking are in the package
lglepfl.ocl.check, along with some interfaces and classes for the representation
of types which are in lglepfl.ocl.check.types.
The main contents of the lglepfl.ocl.check.types
package are as follows:
-
The interface Type which is implemented by all classes
which represent a type. The main functions that this interface defines
are navigateQualified() and navigateParametrized(). The first is used to
obtain the types of qualified features of a class (i.e. attributes and
associations) while the second is used to obtain the type of parametrized
features (i.e. feature calls). This interface is directly implemented by
only the lglepfl.ocl.check.types.Collection class, which encapsulates collection
types. All the others implement the sub-interface Any. This mechanism helps
in differentiating collection types from non-collection types.
-
The interface Any which extends Type but does not define
any new features. This is implemented by all the non-collection types and
represents a convenient way of differentiating the collection from the
non-collection types. The classes implementing this interface are lglepfl.ocl.check.types.OclState
and lglepfl.ocl.check.types.OclType representing the OclState and OclType
types respectively, lglepfl.ocl.check.types.Basic representing the OCL
basic types, lglepfl.ocl.check.types.testfacade.TestClassifier and lglepfl.ocl.check.types.testfacade.BankClassifier
representing two classes in the RoyLoy test model and lglepfl.ocl.check.types.xmifacade.ModelClass.java
representing a class in the XMI generated model.
-
The interface ModelFacade, with the method Type getClassifier(String)
which needs to be implemented by a new model source. This is implemented
by lglepfl.ocl.check.types.testfacade.TestModelFacade and lglepfl.ocl.check.types.xmifacade.Model.
-
The interface TypeFactory which returns objects representing
the pre-defined types, checks the conformance of types etc.
The package lglepfl.ocl.check contains the following
major components:
-
The interface NameBoundQueryable, which has methods
for finding if any particular name is bound to a type in the current environment.
This is implemented by lglepfl.ocl.check.TypeChecker and lglepfl.ocl.OclTree.
However, OclTree encapsulates a TypeChecker instance, and all it does is
call the appropriate method on the TypeChecker.
-
The interface TypeQueryable which has methods for finding
the types of strings or AST nodes. This is implemented by lglepfl.ocl.check.TypeChecker
and lglepfl.ocl.OclTree. However, OclTree encapsulates a TypeChecker instance,
and all it does is call the appropriate method on the TypeChecker.
-
The interface QueryableFactory, which has methods for
returning NameBoundQueryable and TypeQueryable objects. The only implementing
class is TypeCheckerFactory, which returns instances of TypeChecker for
both interfaces.
To implement a completely new type system, all the three
interfaces above will have to be implemented.
In the current implementation, the only class that
performs the actual type checking by traversing the tree is TypeChecker.
Since OclTree encapsulates an AST, it supports the two type-checking interfaces
above to simplify querying the types, but it simply calls the appropriate
method on a TypeChecker instance which is passed to it in the constructor.
The sequence of execution that leads to the type-checking
is as follows: ConstraintEvaluation, which is the GUI maintenance class,
calls OclTree's factory method createTree(), passing it the constraint
and a ModelFacade object (which implementation depends on the model source
chosen by the user). OclTree.createTree() then creates an OclParser instance,
parses the constraint, and returns an OclTree object with it's AST field
set to the root of the AST returned by the parser and the model information
source appropriately set. The ConstraintEvaluation class now creates a
Visualizer object and passes OclTree to it, to be displayed on the screen.
In the process of displaying, Visualizer needs to know the type of each
node, and so it asks the OclTree for the type of each node in turn. The
first time that OclTree is asked this, it gets a new TypeChecker instance,
and runs it on the tree, obtaining the type of each node that can be obtained.
When Visualizer asks the types of further nodes, OclTree simply asks the
TypeChecker instance it has for the stored type information.
To help in the process of TypeChecking, TypeChecker
makes use of the following classes:
-
TypeEnvironment: This class maps a name, in the form
of a String, to a Type. So, at a particular node in the tree, if the type
for a name is needed, the TypeEnvironment associated with that node is
obtained first, and it is then queried for the type of that name. If the
TypeEnvironment does not contain an entry for the name, the Model information
is queried.
-
FunAndPred: This class stores the result type, names of parameters and
their types of a given function/predicate. Functions are stored in funMap
and predicates are stored in predMap.
-
AggregateType: This class defines a new type for storing aggregates. This
is helpful in the semantic analysis of aggregates.
-
NodeEnvironmentMap: This class maps nodes to TypeEnvironments.
It is used to obtain the TypeEnvironment associated with a node. If there
is no map for the node under consideration, the TypeEnvironment for the
parent is found, and so on, till a mapping is obtained for some ancestor.
Note that a mapping will ultimately be obtained, because the root of the
tree (AopnSchema) is always mapped to a TypeEnvironment.
-
NodeTypeName: This stores the type for each node, and
is used by Visualizer to display the type information.
In addition. TypeEnvironment has a HashMap called defaultContexts.
This maps a node to a context. The context is usually "self" (which in
turn is mapped in the TypeEnvironment associated with the root to the class
type to which the Operation belongs). However, in an iterator, the defaultContext
is the name of the iterator, or a new, unique name created by lglepfl.ocl.NameCreator
if it is an implicit iterator. Inside an iterating method, the nodes are
mapped to the iterator name in the defaultContexts. The iterator name in
turn is mapped to a type in the TypeEnvironment. So, if the model needs
to be queried for the type of a particular name (i.e., the name is not
local to the Schema), then the name of the defaultContext is first obtained,
this is then used to get the type of the Context, and the model is then
queried with the appropriate context to see if the name is defined there.
The TypeChecker works by carrying out the following
three basic steps during it’s depth first traversal of the AST:
-
Stores the functions and predicates in the HashMaps
funMap and predMap.
-
Whenever it encounters a declaration, it puts the name
and it's type into the TypeEnvironment.
-
Whenever it gets to a node which is either a variable
name or a type name, it finds out the type of the name (from the TypeEnvironment
or if that fails from the Model), and assigns that type of the node.
-
Whenever it exits a sub-tree, if it is appropriate for
the root of that sub-tree to have a type (if for example it is an AExpression
node) then it assigns it a type based on the type of it’s children.
At each of these stages, the following type errors might
be present:
-
If the declared name has already been declared earlier
(although this might be disabled to allow over-rides).
-
If a name is encountered which is not defined anywhere.
-
If a sub-tree does not evaluate to it's proper type
(for example, in an assignment, one side is a Boolean, and the other a
String).
|