Name Analysis Reference Manual
Recall that when a language allows a programmer to use the same identifier
to represent entities of different kinds in the same region of the program,
the developer is required to use a distinct scope graph for each kind of
identifier
(see Scope graphs).
Often a number of these graphs have the same ranges and the same
relationships among those ranges.
In that case the distinct scope graphs are isomorphic, and
they can all be implemented by a single instantiation of the ScopeGraphs
specification module.
A ScopeGraphs module instantiation always represents a set
of isomorphic scope graphs; by default, that set has a single member.
Ranges are represented by values of type NodeTuplePtr
(see Establishing the Structure of a Scope Graph).
These values are pointers to NodeTuple values, which are arrays of
pointers to the actual scope nodes.
A set of edges that are equivalent under the isomorphism is specified by
giving NodeTuplePtr values for tail and tip; the module then creates
the elements of the set.
There is no additional mechanism needed for a larger set, one need only
provide some additional information about particular identifier occurrences
to specify the kind of identifier involved.
Let's consider a concrete example.
A Java programmer may use the identifier ambig to represent a
package, type, variable, and method in a single range.
This requires four distinct scope graphs, one for each entity that
ambig can represent.
However, the language rules are such that these four scope graphs are
isomorphic.
Thus this set of scope graphs can be implemented by a single instantiation
of the ScopeGraphs module.
The NodeTuple corresponding to each range would be a four-element
array in that implementation.
The number of elements in the set for a given instantiation of the
ScopeGraphs module is given by the integer-valued
*RootScope.*NumberOfIsoGraphs attribute.
This synthesized attribute is set by a module computation to the value 1.
A developer must override that computation when specifying several
isomorphic scope graphs.
The *RootScope role is automatically inherited by the symbol at the
root of the abstract syntax.
The computation of the *NumberOfIsoGraphs attribute must be
overridden by introducing a computation at that symbol.
For example, if Java were the symbol at the root of the abstract
syntax, the computation would be:
TREE SYMBOL Java COMPUTE SYNT.*NumberOfIsoGraphs=4; END;
The elements of the set of scope graphs implemented by a module
instantiation are indexed by values @math{i},
@math{0 \leq i < *NumberOfIsoGraphs }.
A specification is much more understandable if the possible graph indexes
are named.
Here is an appropriate set of graph index names for a Java specification:
enum { packageName, typeName, expressionName, methodName,
packageOrTypeName, expressionOrTypeName, ambiguousName
};
The first line names the kinds of entity that have scope graphs
(an expressionName is a field name or a variable name).
Syntactic context must determine the specific kind of identifier
at its defining occurrence.
An applied occurrence at which the specific kind of identifier can be
determined is called a strong context.
In a weak context, the kind of identifier cannot be determined precisely
although the set of possibilities could be narrowed.
For example, in certain Java contexts an applied occurrence might refer to
either a package or a type, but not to a method.
Integer values @math{j}, @math{j \geq RootScope.NumberOfIsoGraphs }
are used to specify the kind of identifier in a weak context.
Meanings for these larger values are chosen by the developer,
as shown in the second line of the enumeration.
An identifier occurrence's kind is specified to a computational role
by the integer-valued GraphIndex attribute.
This inherited attribute is set by a module computation to the value 0.
The following computational roles provide the GraphIndex attribute:
*IdDefScope
*WLSimpleName
*WLQualName
*WLInsertDef
*GCSimpleName
*GCQualName
When there is more than one graph in the set, the default GraphIndex
computations must be overridden.
Here is a specific example:
TREE SYMBOL MethodIdDef INHERITS IdDefScope COMPUTE
INH.GraphIndex = methodName;
END;
A module computation stores the value of the *IdDefScope.*GraphIndex
attribute as the value of the GraphIndex property of the definition
table key that is the value of *IdDefScope.*Key .
Similarly, module computations establish values for the GraphIndex
properties of the keys that are the values of the *UseKey attributes
of the other roles listed above.
When the GraphIndex property of an applied occurrence indicates
that the context is weak, searches are carried out in all graphs
of the instantiation's set.
If more than one binding is found, then NoKey is returned.
The developer can change this behavior by providing a language-specific
disambiguation function to determine the correct result
(see Implementing Language-Specific Behavior).
Let `MaxIndex' be the maximum value of
*RootScope.*NumberOfIsoGraphs
over all instantiations of the ScopeGraphs module in the specification.
If `MaxIndex' is greater than 1, then the developer must provide a
file named `ScopeGraphs.h' as part of the specification.
`ScopeGraphs.h' must contain the following declaration:
#define MaxIsoGraphs `MaxIndex'
|