General Information
Tutorials
Reference Manuals
Libraries
Translation Tasks
Tools
Administration
|
Tutorial for Name Analysis Using ScopeGraphsSelecting Acceptable Bindingsclash.nl[123]== import fsm.*; import random.*; { int count = 0; state = 0; while (state != 7) { if (ran() < 0.25) advance(1); else if (ran() < 0.8) advance(2); else advance(3); count = count + 1; } } package fsm; int state; void advance(int input) { if (input == 1) state = state - 1; else if (input == 2) state = state + 1; } package random; int state = 100001; float ran() { state = state * 125; state = state - (state / 2796203) * 2796203; return state / 2796203.0; } This macro is attached to a non-product file.
Two entities named
File `clash.nl' illustrates a weakness of the package facility.
The variable
This situation typifies an aspect of the name analysis process that we call
acceptability:
There are two common kinds of additional rules that prevent an available binding from being acceptable in a particular context: access rules and position rules. Access rules define some directive that a user can attach to a declaration to specify the contexts in which the binding it creates is acceptable. Position rules involve the relative locations of the defining and applied occurrences in the source text. We will cover each in the remainder of this chapter.
Access rules
As language designers, we can solve the problem of `clash.nl'
by introducing a Phrase structure[124]== Declaration: 'private' VarDecl. Declaration: 'private' MethodDecl. Declaration: 'private' ClassDecl. This macro is defined in definitions 2, 25, 30, 32, 41, 50, 55, 67, 76, 97, 118, and 124. This macro is invoked in definition 3.
The following access rules describe the effect of a
private directive is used to avoid the ambiguity that
occurred in `clash.nl':
private.nl[125]== import fsm.*; import random.*; { int count = 0; state = 0; while (state != 7) { if (ran() < 0.25) advance(1); else if (ran() < 0.8) advance(2); else advance(3); count = count + 1; } } package fsm; int state; void advance(int input) { if (input == 1) state = state - 1; else if (input == 2) state = state + 1; } package random; private int state = 100001; float ran() { state = state * 125; state = state - (state / 2796203) * 2796203; return state / 2796203.0; } This macro is attached to a non-product file.
The access rules describing the
def argument is the available binding, and app is the
UseKey of the applied occurrence
(see Applied Occurrences of Name Analysis Reference Manual).
A default version of each of these functions, which finds any binding
acceptable, is available in the library; the appropriate default
version will be used unless the developer overrides it by specifying
a C-coded function.
As developers, we implement the access rules describing the effect of a
Recall that the first two arguments to these functions are definition table keys. Those keys must have properties that will allow the functions to make the appropriate decision (see The Definition Table Module of Property Definition Language). Properties and property computations[126]== InPackage: DefTableKey; /* Enclosing package */ IsPrivate: int; /* 1 if the available binding is private */ This macro is defined in definitions 79, 94, 126, 132, 143, and 148. This macro is invoked in definition 80.
If a binding is found for a simple name in an initial node or a
node reached by following a parent edge, then the applied occurrence
lies in the range of the defining occurrence.
The range of a defining occurrence does not cross a package boundary,
so the binding will always be acceptable.
We can therefore use the default
If the initial node is a qualifier, a binding found for the
qualified identifier will not be acceptable if the applied occurrence
is not in the same package as the defining occurrence.
This condition is checked by the following computation, which is common to
both Common private access check[127]== if (!GetIsPrivate(def, 0)) return AcceptBinding; defpkg = GetInPackage(def, NoKey); apppkg = GetInPackage(app, NoKey); if (defpkg != apppkg) return IgnoreSkipPath; This macro is invoked in definitions 128 and 130.
If the available binding isn't private, then it is acceptable.
If the available binding and the applied occurrence are not in the same
package, then the binding is not acceptable.
The function returns At the end of this computation, we know that the entity is private, and that its defining occurrence is in the same package as the applied occurrence. That information is sufficient to accept a binding for a qualified identifier at the initial node: Check acceptability of a qualified identifier[128]== int isAcceptableQualified (DefTableKey def, DefTableKey app) { DefTableKey defpkg, apppkg; Common private access check[127] return AcceptBinding; } This macro is invoked in definition 138. Let's see how this works on a contrived example: accessPack.nl[129]== { } package P; class A { private int x, y; } /* package access */ class B { class C extends A { void f() { x = y; } /* legal */ } class D extends Q.F { void g() { x = A.y; } /* illegal x */ } } package Q; class F extends P.A { void h() { x = P.A.y; } /* illegal x, y */ } This macro is attached to a non-product file.
First consider the qualified name
IsPrivate attribute of def = 1 InPackage attribute of def = P InPackage attribute of app = PYou should verify that the result would be AcceptBinding .
Now consider the qualified name
All of the applied occurrences of
Consider the applied occurrence of
An inheritance path consists of a sequence of scope graph nodes
owned by classes.
The scope graphs module provides a function,
In order to verify accessibility of a binding that is neither
accepted nor rejected by the common private access check,
Check acceptability at the tip of a path edge[130]== int isAcceptablePath (DefTableKey def, DefTableKey app, int lab) { DefTableKey defpkg, apppkg; Common private access check[127] pkg = defpkg; /* defpkg is set by the Common private access check */ if (!CheckPathsNsp( GetInClassNode(app, NoNodeTuple), GetInClassNode(def, NoNodeTuple), lab, ChkClass)) return IgnoreSkipPath; return AcceptBinding; } This macro is invoked in definition 138.
Call-back function to verify inheritance paths[131]== int ChkClass(DefTableKey cls) { if (GetInPackage(cls, NoKey) == pkg) return 1; return 0; } This macro is invoked in definition 138.
In order to specify the beginning of the inheritance path for a
particular binding, we need to know the scope graph node owned by the
class containing the applied occurrence of the identifier being
sought; the end of that inheritance path is the scope graph node
containing the binding.
The necessary information can be provided by an Properties and property computations[132]== InClassNode: NodeTuplePtr; This macro is defined in definitions 79, 94, 126, 132, 143, and 148. This macro is invoked in definition 80.
Let us now consider how the properties that we have defined are set.
We will use the class symbol Abstract syntax tree[133]== SYMBOL DeclContext: private: int; SYMBOL DeclContext COMPUTE SYNT.private = 0; END; SYMBOL Declaration INHERITS DeclContext END; SYMBOL DeclStmt INHERITS DeclContext END; RULE: Declaration ::= 'private' VarDecl COMPUTE Declaration.private = 1; END; RULE: Declaration ::= 'private' MethodDecl COMPUTE Declaration.private = 1; END; RULE: Declaration ::= 'private' ClassDecl COMPUTE Declaration.private = 1; END; This macro is defined in definitions 10, 18, 22, 38, 47, 52, 53, 65, 69, 71, 88, 89, 92, 93, 95, 96, 101, 105, 108, 110, 111, 112, 113, 122, 133, 134, 135, 136, 137, 144, and 153. This macro is invoked in definition 11.
For every defining occurrence that might be modified by a Abstract syntax tree[134]== SYMBOL VarDefName INHERITS DeclaredName END; SYMBOL MethodDefName INHERITS DeclaredName END; SYMBOL ClassDefName INHERITS DeclaredName END; SYMBOL DeclaredName COMPUTE SYNT.GotDefKeyProp += ResetIsPrivate(THIS.Key, INCLUDING DeclContext.private); END; This macro is defined in definitions 10, 18, 22, 38, 47, 52, 53, 65, 69, 71, 88, 89, 92, 93, 95, 96, 101, 105, 108, 110, 111, 112, 113, 122, 133, 134, 135, 136, 137, 144, and 153. This macro is invoked in definition 11.
Note the use of an accumulating computation for the void attribute
A computation at each declaration sets the Abstract syntax tree[135]== SYMBOL DeclaredName COMPUTE SYNT.GotDefKeyProp += ResetInPackage( THIS.Key, INCLUDING (PackageBody.ScopeKey, Collection.ScopeKey)); END; This macro is defined in definitions 10, 18, 22, 38, 47, 52, 53, 65, 69, 71, 88, 89, 92, 93, 95, 96, 101, 105, 108, 110, 111, 112, 113, 122, 133, 134, 135, 136, 137, 144, and 153. This macro is invoked in definition 11.
Four symbols in our abstract syntax represent applied occurrences.
In every one of these four contexts
we need to set the Abstract syntax tree[136]== SYMBOL AppliedName COMPUTE SYNT.GotUseKeyProp += ResetInPackage( THIS.UseKey, INCLUDING (PackageBody.ScopeKey, Collection.ScopeKey)); END; SYMBOL SimpleName INHERITS AppliedName END; SYMBOL QualifiedId INHERITS AppliedName END; SYMBOL SimpleWLName INHERITS AppliedName END; SYMBOL QualifiedWLId INHERITS AppliedName END; This macro is defined in definitions 10, 18, 22, 38, 47, 52, 53, 65, 69, 71, 88, 89, 92, 93, 95, 96, 101, 105, 108, 110, 111, 112, 113, 122, 133, 134, 135, 136, 137, 144, and 153. This macro is invoked in definition 11.
The computations to set the Abstract syntax tree[137]== SYMBOL DeclaredName COMPUTE SYNT.GotDefKeyProp += ResetInClassNode( THIS.Key, INCLUDING (ClassBody.Env, Collection.Env)); END; SYMBOL AppliedName COMPUTE SYNT.GotUseKeyProp += ResetInClassNode( THIS.UseKey, INCLUDING (ClassBody.Env, Collection.Env)); END; This macro is defined in definitions 10, 18, 22, 38, 47, 52, 53, 65, 69, 71, 88, 89, 92, 93, 95, 96, 101, 105, 108, 110, 111, 112, 113, 122, 133, 134, 135, 136, 137, 144, and 153. This macro is invoked in definition 11.
The C functions must be combined and the global variable AccCtl.c[138]== #include "LangSpecFct.h" #include "err.h" DefTableKey pkg; Check acceptability of a qualified identifier[128] Call-back function to verify inheritance paths[131] Check acceptability at the tip of a path edge[130] This macro is attached to a product file. `AccCtl.c' is a specification file: Specification files[139]== AccCtl.c This macro is defined in definitions 12, 16, 23, 27, 74, 78, 81, 91, 117, 121, 139, 142, 146, and 152. This macro is invoked in definition 13.
ExercisesThese exercises are based on files defined in the Tutorial. To obtain copies of those files in your current directory, enter Eli and give the following command:
-> $elipkg/Name/LearnSG%AccCtl > . None of these files will have write permission in your current directory. You will need to add write permission in order to do the exercises.
Position controlA method body or block consists of a sequence of statements and variable declarations. The rules of NameLan only allow a reference to a variable declared in such a sequence to take the form of a simple name; it cannot be inherited or used in a qualified name. This means that there is no mechanism by which the variable can be accessed outside of the method body or block in which it was declared. We therefore call these variables local variables. Local variable declarations may appear at arbitrary locations in a method body or block. How should we interpret their meaning in relation to each other and to the statements in that method body or block? The programmer might well consider the variable name to be unknown before its declaration. As language designers, we could formalize that intuition:
VarDefName occurrences still obey the scope rules stated
in earlier chapters.
The second rule applies to all VarDefName occurrences.
It prevents multiple variable declarations in a range
(see Error reporting).
If a scope ends at the end of a cscope.nl[140]== int i; { i = -42; float i; i = 42; } This macro is attached to a non-product file.
There are two variables named The search for a binding for the applied occurrence on the second line begins in the scope graph node for the block. That scope graph node contains a defining occurrence on the third line, which must be checked for acceptability. This binding is not acceptable because the applied occurrence does not follow the defining occurrence in the text, and is therefore not in the scope of that defining occurrence. The search must continue in the scope node for the complete text, where an acceptable binding is available. The search for a binding for the applied occurrence on the fourth line also begins in the scope graph node for the program block. In this case the binding is acceptable, because the applied occurrence follows the defining occurrence in the text and is therefore in the scope of that defining occurrence.
In order to compare positions of defining and applied occurrences,
we use values of type
Module computations set the PosCtl.c[141]== #include "LangSpecFct.h" int isAcceptableSimple (DefTableKey def, DefTableKey app) { if (GetIsLocal(def, 0)) { CoordPtr defcoord = GetCoord(def, NoPosition); CoordPtr appcoord = GetCoord(app, NoPosition); if (!earlier(defcoord, appcoord)) return IgnoreContinue; } return AcceptBinding; } This macro is attached to a product file.
See Source Text Coordinates and Error Reporting of The Eli Library, for the details of the `PosCtl.c' is an additional specification: Specification files[142]== PosCtl.c This macro is defined in definitions 12, 16, 23, 27, 74, 78, 81, 91, 117, 121, 139, 142, 146, and 152. This macro is invoked in definition 13.
Properties and property computations[143]== IsLocal: int; This macro is defined in definitions 79, 94, 126, 132, 143, and 148. This macro is invoked in definition 80.
Here are computations that will establish the value of the Abstract syntax tree[144]== SYMBOL DeclContext: IsLocal: int; SYMBOL Declaration COMPUTE SYNT.IsLocal=0; END; SYMBOL DeclStmt COMPUTE SYNT.IsLocal=1; END; SYMBOL VarDefName COMPUTE SYNT.GotDefKeyProp += ResetIsLocal(THIS.Key, INCLUDING DeclContext.IsLocal); END; This macro is defined in definitions 10, 18, 22, 38, 47, 52, 53, 65, 69, 71, 88, 89, 92, 93, 95, 96, 101, 105, 108, 110, 111, 112, 113, 122, 133, 134, 135, 136, 137, 144, and 153. This macro is invoked in definition 11.
ExercisesThese exercises are based on files defined in the Tutorial. To obtain copies of those files in your current directory, enter Eli and give the following command:
-> $elipkg/Name/LearnSG%PosCtl > . None of these files will have write permission in your current directory.
|