Static Semantics and Inter-Document Consistency Constraints: Tool specications must dene static semantics such as scoping and type compatibility rules of a language More-
6.5 Class Specications
6.5.4 Semantic Rules
Attributes and semantic relationships are concepts that can be used for dening data structures for static semantics and inter-document consistency constraints. Changes of attribute values and the creation or deletion of semantic relationships will be dened in tool command de- nitions by invoking methods. These changes, however, usually require a number of follow-on activities in order to check static semantic constraints for related increments.
As an example, consider the creation of a new type import increment in the Groupie module interface tool specication. A command will invoke a method which creates a new type import increment, scans a given string and inserts the type import increment into an import list increment. Then it must be checked to see whether the type import matches with an exported type in the respective imported module. In that case the semantic relationship ImpFrom/ExpTo
must be established between the two increments, otherwise the type import must be considered wrong and an error attribute of the type import increment should include a respective error descriptor. By accessing this error attribute, the tool command can decide if the type import is correct or not. If not, it might then dene whether to tolerate or to reject the error. If the type import is correct or the tool command tolerates the error, the import now extends the set of declared types and the symbol table attribute DefinedNamesof the module must be
updated. This, in turn, might require checking using type increments such as parameter types or function result types. They could have become correct by the change if their lexical value is identical to that of the type import. Then their error attributes will have to be updated in order to reect the correction of this error.
If we did not dene any other concept than methods, tool builders would have to nd valid execution orders to perform the required follow-on actions for all potential attribute and se- mantic relationship changes. We strongly consider this to be at the wrong level of abstraction. Tool builders require instead a declarative concept for dening the correctness of the various static semantic and inter-document consistency constraints. This concept should, in partic- ular, relieve them from worrying about the order in which evaluations are performed. The new concept should also support our structuring paradigm and be dened in terms of incre- ment classes. In addition, the concept must enable the ecient evaluation of static semantic constraints to be carried out as this has to be done on-line, i.e. during the execution of user commands. It must, therefore, meet the time restrictions required in Section 2.3.3. We now dene semantic rules that will meet the above requirements.
Each semantic rule consists of a list of statements called action that is bound to a condition. The condition is specied after the ON clause and the action is dened between ACTION and END ACTIONkeywords. Temporal predicates may be used to specify conditions, namelyCHANGED
and DELETED. A CHANGED predicate becomesTRUEif its argument has been created or changed
since the last execution of the semantic rule. The DELETED expression becomes TRUE if its
argument is about to be removed. Arguments of a CHANGED or DELETED expression may be
attributes or semantic relationships of any other increments. Path expressions are used to determine attributes or semantic relationships of remote increments. A name of an attribute may only occur as the last name in a path expression. Compound conditions can be built by using the OR operator. An EXISTS operator is used in the usual sense of rst order logic to
specify that the rule has to be executed as soon as some other condition holds for an element in a multi-valued syntax component or a multi-valued semantic relationship. This predicate, in fact, resolves the situation for semantic rules that intermediate steps in path expressions cannot be multi-valued.
INCREMENT SPECIFICATION TypeImport; ...
SEMANTIC RULES // Rule 1
ON CHANGED(father.ImpFrom.DefinedNames) OR CHANGED(value)
// symbol table of imported module or lexical value of type import changed VAR i: Increment;
ACTION
i := father.ImpFrom.DefinedNames.increment_at(value);
IF (i == NIL) // Name undefined?
THEN ImpFrom:= NIL; // Type Import not o.k.
ELSE
IF (i.IS_OF_CLASS("TypeName")) // Is Defined Name a TypeName?
THEN ImpFrom := <TypeName>i; // o.k: establish semantic relationship ELSE ImpFrom := NIL; // Type Import not o.k.
ENDIF; ENDIF; END ACTION;
Figure 6.9: Semantic Rule in ClassTypeImport
As a rst example, consider a semantic rule from class TypeImport of the Groupie interface
editor specication that is displayed in Figure 6.9. The condition of this rule denes that the rule will be executed if either the value of attribute DefinedNamesin the symbol table of the
imported module or the lexical value of the type import itself has been changed.
If the condition of a rule becomes true, the list of GTSL statements given in the action is se- quentially executed before attributes and links that are modied by the rule are accessed the next time. The available statements for semantic rule actions are assignments of expressions to attribute values, invocation of methods reading or modifying attribute values, creation of relationships and control ow primitives such asIForFOREACHstatements. In order to facili-
tate encapsulation and avoid side eects, semantic rules of a class may only modify attributes or relationships dened or inherited by that class. Note that actions must, therefore, not invoke methods via path expressions that would modify attributes of remote increments. This is excluded by a static semantic constraint
The purpose of the action in the rst semantic rule in the above example is to determine the existence of semantic relationship ImpFrom. The rst statement reads the DefinedNames
attribute in order to obtain the increment that is associated with the lexical value of the
TypeImport. If there is no such association, then the semantic relationship should not exist
and NIL is assigned to ImpFrom, otherwise the rule checks whether the dynamic type of the
increment really is a type name in order to prevent the import of some other resource, such as an operation that might be exported under the same name. If the association denotes a type name, we can specialise the static type using the cast operator and establish the semantic relationship ImpFrom/ExpTobetween the import and the corresponding export, otherwise the
relationship should not exist, since the increment that is dened in the symbol table is not a type name. A very similar rule is dened for class OpImportin Figure 6.10. It establishes the ImpFrom/ExpTorelationship between operation imports and the respective exported operation.
The two classesTypeImportandOpImportshare the common characteristic that the increments
are considered erroneous whenever the semantic relationship between import and export does not exist. This common behaviour is dened in the common super classImportas displayed in
INCREMENT SPECIFICATION OpImport; ...
SEMANTIC RULES // Rule 1
ON CHANGED(father.ImpFrom.DefinedNames) OR CHANGED(value)
// symbol table of imported module or lexical value of op import changed VAR i: Increment;
ACTION
i := father.ImpFrom.DefinedNames.increment_at(value);
IF (i == NIL) // Name undefined?
THEN ImpFrom:= NIL; // Operation Import not o.k.
ELSE
IF (i.IS_OF_CLASS("OpName")) // Is Defined Name an OpName?
THEN ImpFrom := <OpName>i; // o.k: establish semantic relationship ELSE ImpFrom := NIL; // Type Import not o.k.
ENDIF; ENDIF; END ACTION;
Figure 6.10: Semantic Rule in Class OpImport
INCREMENT SPECIFICATION Import; ...
SEMANTIC RULES // Rule 1
ON CHANGED(ImpFrom) // If link has changed
ACTION
IF (ImpFrom == NIL) // See whether it (still) exists
THEN Errors.append_error(#NotAValidExport);// add respective error ELSE Errors.clear_error(#NotAValidExport); // delete error
ENDIF; END ACTION; END SEMANTIC RULES;
Figure 6.11: Semantic Rule in Abstract ClassImport
Figure 6.11. The rule is, therefore, inherited by both subclasses. The value ofErrorsdepends
on the existence of semantic relationship ImpFrom/ExpTo. The condition of the rule in class Import, therefore, includes a CHANGED expression with the link ImpFrom as the argument. To
be able to dene it this way, we have to dene the link ImpFrom in class Import. It is then
covariantly redened in subclasses to lead to OpName or TypeName, respectively. The rule's
precondition becomes TRUE, if for example the rst semantic rule has modied the link. In
this case, a check is made whether the link exists. If it does not exist then the import is wrong and an error message is added to the Errorsset, otherwise the import is correct. The
corresponding error message is removed because the import might have been incorrect before. Note that we explicitly dene the dependencies between rules and attributes. As with import interfaces, these dependencies might be inferred by some static analysis tool. It is, however, im- portant that the tool builder is aware of the dependencies. As with imports these dependencies should be minimised. The rationale here is to facilitate ecient rule evaluation. Moreover, the explicit declaration of dependencies, as with imports, simplies the impact analysis if attribute or relationship declarations are changed.
As a further example consider the rules below that modify and access theDefinedNamessymbol
table in an ADTModulewhenever elements of the export are changed. These changes must be
reected in the symbol table in order to keep imports consistent. The rst rule enters a type name into the symbol table whenever the value of the exported type is changed. The second rule uses the EXISTS predicate in order to update the symbol table DefinedNames, whenever
the lexical value of an operation name changes. Then the methodassociateof the predened
non-syntactic classSymbolTableis invoked in order to store the operation as an entry with its
new name as the key. If necessary the old key is deleted.
INCREMENT SPECIFICATION ADTModule ... // Rule 1 SEMANTIC RULES ON CHANGED (type.value) ACTION DefinedNames.associate(type.name, type.name.value); END; // Rule 2 ON EXISTS op in opl.op_list:CHANGED(op.name.value) ACTION DefinedNames.associate(op.name, op.name.value); END ACTION; ...
Figure 6.12: Semantic Rule in ClassADTModule
The next rule is dened in class OpNameand uses the symbol table DefinedNames of its root
increment in order to check for the uniqueness of operation names. If the symbol table contains another increment such as a type name under the same key, then an error message is added to theErrorsset, otherwise the message is deleted.
INCREMENT SPECIFICATION OpName; ... // Rule OpName::1 ON CHANGED father.father.father.DefinedNames ACTION IF father.father.father.DefinedNames.is_duplicate(value) THEN Errors.append_error(#NameAlreadyDefined) ELSE Errors.remove_error(#NameAlreadyDefined) ENDIF END ACTION; ...
Figure 6.13: Semantic Rule in Class OpName
The dynamic semantics of semantic rules is informally dened as follows. Rule evaluation is, in fact, divided into two phases. The rst phase is a propagation phase where attributes and semantic relationships are stamped as dirty whenever they are modied. All semantic rules that read dirty attributes or relationships are also stamped as dirty. Then the property of being dirty is transitively propagated to all attributes or relationships that are modied by dirty rules. The propagation phase ends when all aected rules, attributes and relationships have been marked dirty. The second phase evaluates rules and it starts whenever a dirty attribute
or relationship is about to be accessed (e.g. during unparsing). Then all rules are executed according to the propagation path dened in the propagation phase. This execution brings the attribute or relationship back into state clean. Only then is the attribute or relationship really accessed.
The conditions of semantic rules specify static dependencies between dierent semantic rules. We do not want to enable computations or even modications to be dened in these condi- tions. Such modications could produce serious side eects and lead to situations where the rules can no longer be understood by the tool builder. In particular, the tool builder had to know the order in which change predicates are evaluated and it was the main motivation for semantic rules to relieve the tool builder from this burden. Path expressions as dened above include steps that may invoke methods and in these methods side eects could be produced. Method invocations would be required to determine recursive path expressions as they occur, for instance, during specication of nested scoping blocks. Upon increment creation, however, recursive path expressions can always be materialised within auxilliary semantic relationships. Then these relationships can be used in semantic rule conditions. We can, therefore, disable method invocations to occur in steps of path expressions of semantic rule conditions.
In short, we have dened a declarative language for the denition of static semantics and inter- document consistency constraints. Note that we have not dened any execution order between the dierent semantic rules that we have discussed. A valid execution order will be inferred statically from the dependencies between rules, attributes and relationships. Semantic rules enforce well-structured static semantics and inter-document consistency constraint denitions because the language does not enable rule actions to modify attributes or relationships that do not belong to the class. Moreover, subclasses inherit semantic rules from super classes.