Selection
5.9 Tool Schema
5.9.2 Tool-specic Classes
The classes that have to be added to the predened schema to complete the denition of a tool schema depend on the syntax and static semantics of the language to be supported by the tool. The syntax determines the dierent node types in the abstract syntax tree. Static semantics determine reference relationships that must be implemented between node types. For each node type, a separate class must be dened. The class must be given a name and be positioned in the class hierarchy. Therefore, the class must be dened as a subclass of one or more predened classes. Then instance variables must be dened and nally method signatures and their implementations must be provided. In the following paragraphs, we discuss guidelines as to how this can be achieved for the tool-specic part of a schema. Moreover, we discuss schema integration that is required for the implementation of inter-document consistency constraint checks and their preservation.
5.9.2.1 Instance Variables
For a non-terminal class, the tool builder must declare instance variables that implement edges to abstract syntax child nodes. Most ODBSs are statically typed and, therefore, types must
be dened for instance variables. For single-valued edges, the type of a variable is simply the class implementing the target node type of the edge. For multi-valued edges, variables may be used whose type is constructed from a base type by list or set type constructors. Type constructors for lists and sets exist by denition in all ODBSs [Cat93].
As an example consider the
O
2C
type declarations given below for nodes of types Functionand ParamList from the abstract syntax graph on Page 21. Function includes a number of
single-valued edges leading to child nodes. ParamList denes an ordered multi-valued edge
to parameters. Class Parameter is in fact an abstract class from which classes InParameter
and InOutParameterwill inherit. By exploiting polymorphism, a function's parameter list may
then contain instances of these two classes.
class Function inherit Increment
type tuple(read name:OpName, class ParamList inherit OptionalIncrement read pl:ParamList, type tuple(read params:list(Parameter))
read type:UsingType, ...
read com:Comment) end;
... end;
Terminal classes do not have outgoing aggregation edges. Instead, an instance variable is required that stores the lexical value, that is the character string, which matches the respective terminal symbol of the grammar. This instance variable is inherited from the predened class
TerminalIncrement.
Instances of both non-terminal and terminal classes may be source or target nodes of reference edges that represent semantic relationships. Then additional instance variables have to be declared for each incoming or outgoing reference edge.
As an example, let us consider classUsingTypefrom the Groupie module interface tool schema.
In the gure on Page 21, reference edges connect nodes of type UsingType with declaration
nodes of typesTypeNameorTypeImport. We, therefore, introduce an abstract class TypeDeclas
super class ofTypeNameand TypeDecland dene instance variables in UsingTypeand TypeDecl
in order to implement this reference edge.
class UsingType inherit Increment class TypeDecl inherit Increment
type tuple(read DefinedIn:TypeDecl) type tuple(read UsedIn:set(UsingType))
... ...
end; end;
Finally, additional instance variables may be added in order to implement node attributes. These node attributes will most often be used for static semantic checks. They, therefore, store symbol tables or type information.
As an example, considerModulenodes from the gure on Page 21. These module nodes carry
an attribute for storing scope information such as the set of identiers that have been dened within the scope of the module. To implement this attribute, an instance variable of class
class Module inherit DocumentVersion type tuple(name:ModName, com:Comment, typ:TypeName, op_list:OperationList, imp:ImportInterface, DefinedNames:SymbolTable) ... end;
In short, instance variables are used to implement syntactic and reference edges, lexical values and attributes required during static semantic checks.
5.9.2.2 Methods
Any class should dene a constructor method, which initialises all instance variables and invokes theinitmethods of all super classes in order to initialise inherited instance variables
as well. The constructor in
O
2C
is theinitmethod, which is implicitly called byO
2. The classshould also declare a destructor methodcollapse, which is invoked when the object is deleted.
This method should delete all child increments, reference edges and free space occupied by instance variables that implement node attributes.
For non-terminal classes the following methods should also be dened:
anexpandmethod that expands the increment from the state where it represents a place
holder to a template where all its child increments are place holders,
a parse method that, for a given increment, examines a string passed as an argument
and returns an abstract syntax tree if the string is syntactically correct,
an unparse method that traverses the abstract syntax tree starting from the increment
and computes a textual representation.
These methods implement access and modication operations to the instance variables that implement syntactic edges. They also access and modify the instance variable that stores the information as to whether or not an increment is a place holder.
For each terminal class the following methods should be dened:
a scanmethod that checks for the lexical correctness of the terminal increment, an unparsemethod that returns the lexical value of the terminal increment.
These two methods provide the access and modication operations to the instance variable
valuewhich is hidden from the outside.
If a class implements a node type whose instances are source nodes of reference edges, two methods have to be added for each of these edges in order to control the value of the instance variables implementing the edge. The rst method set implements an operation that estab-
lishes the edge. The parameter of the method is the increment to which the edge is to be drawn. It not only sets the instance variable to the object passed as a parameter, but also invokes an operation from the object in order to establish the reverse direction of the edge. The second method deleteimplements the operation to delete a reference edge. It sets the
value of the instance variable to the undened valueniland invokes an operation on the target
Each class implementing a node type that participates in static semantics or inter-document consistency constraints must include additional methods that check for violation of the con- straints. The purpose of these methods is to include error descriptors in the error set attribute that any increment inherits from class Increment. In addition, these methods have to control
the existence of reference edges and invoke the methods outlined above for this purpose. An- other aim of these methods is to update symbol tables as soon as new identiers are declared or existing identiers are modied or deleted.
In addition to these standardised methods, a tool builder may freely decide to add particular methods that use the methods suggested above, in order to implement particular operations re- quired from the schema. These methods may then be used to implement particular commands required from the tool.
As an example, consider the methods dened for class Function below. Methods init, collapse, expand, parse and unparse are those common to arbitrary non-terminal classes,
though some of their signatures vary. Methods specic to class Functionare the other meth-
ods which implement modication operations on a Function's child increments.
class Function inherit Increment type tuple(read name:OpName,
read pl:ParamList, read type:UsingType, read com:Comment) method
public init (f : Increment), public collapse, public expand, public parse(Str:string):Function, public unparse:string, public expand_name(Str:string):boolean, public change_name(Str:string):boolean, public expand_type(Str:string):boolean, public change_type(Str:string):boolean, public expand_comment(Str:string):boolean, public change_comment(Str:string):boolean end;
In short, terminal and non-terminal classes must export a number of methods. Some of them such as the constructor and destructor methods exist in arbitrary classes. Others should be available only for terminal (e.g. scan) or non-terminal classes (e.g. parse). In addition to
these methods that should be exported by all classes, there are methods that are specic to particular increment classes. The implementation of method bodies, however, varies from class to class and cannot be dened in advance.