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:

  1. 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. 
  2. 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. 
  3. 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. 
  4. 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:
  1. 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. 
  2. 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. 
  3. 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:
 

  1. 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.
  2. 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.
  3. AggregateType: This class defines a new type for storing aggregates. This is helpful in the semantic analysis of aggregates.
  4. 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. 
  5. 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:

  1. Stores the functions and predicates in the HashMaps funMap and predMap.
  2. Whenever it encounters a declaration, it puts the name and it's type into the TypeEnvironment. 
  3. 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. 
  4. 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:
  1. If the declared name has already been declared earlier (although this might be disabled to allow over-rides). 
  2. If a name is encountered which is not defined anywhere. 
  3. 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).