Day 7
Trash
It is important that you run Eclipse 3.7 with an unstable built of Spoofax. All other combinations are likely not to work.
Before you start this assignment, you need to
update the following files in your project:
- assignment1/MiniJava.tbl
- lib/nbl-library.custom
- trans/minijava.str
Name Binding
Spoofax' new Name Binding Language (NBL) is a declarative metalanguage for the specification of name bindings in terms of namespaces, definition sites, use sites, and scoping rules.
Namespaces
In NBL, a namespace is a collection of names and is not necessarily connected to a specific language concept.
Different concepts can contribute names to a single namespace.
For example, in MiniJava variables and parameters contribute to the same namespace.
Namespaces are declared in a
namespace
section.
module names
imports
assignment1/MiniJava
namespaces Class
You can copy this content into a file
trans/name.nd
.
Next, you should extend the
namespace
section with all namespaces of MiniJava.
Definition and Use Sites
Once you defined namespaces, you can define name bindings with rules of the form
pattern : clause*
, where
pattern
is a term pattern (like in rewrite rules) and
clause*
is a list of name binding declarations about the language construct that matches with pattern.
For example, the following rule declares definition sites for class names:
rules
Class(c, _, _, _): defines Class c
Use sites which refer to definition sites of names can be declared similarly.
For example, the following rule declares use sites of class names:
Parent(c): refers to Class c
Identify definition and use sites of names in MiniJava and define the corresponding rules.
Editor Integration
When you save
trans/names.nd
, Spoofax generates automatically a file
trans/names.str
. This file contains Stratego code implementing name analysis based on the rules you provided.
To use this implementation, you should import it into
trans/minijava.str
.
When you open a MiniJava program and inspect its analysed abstract syntax, you will find definition and use sites of names annotated with URIs.
For example,
Class("c", ...)
will become
Class("c"{[Class(), c]}, ...)
.
In
trans/minijava.str
you can also find boilerplate code for reference resolution (
editor-resolve
) and code completion (
editor-complete
).
You can register these strategies in as reference resolver in
editor/MiniJava-References.esv
and completion proposer in
editor/MiniJava-Completions.esv
.
After a build, you can try these editor services in a MiniJava editor.
Scopes
In the current state, all names are globally visible in your project.
Thus, you can refer to classes in other MiniJava programs, which reside in the same Eclipse project.
Scopes restrict the visibility of definition sites.
For example, a class scopes fields that are not visible from outside the class.
This can be specified in NBL by extending the rule for classes with a
scopes
clause:
Class(c, _, _, _):
defines Class c
scopes Field
Scopes can be nested and name resolution typically looks for definition sites from inner to outer scopes.
As in the class example, scopes are often also definition sites.
However, this is not a requirement. For example, a program has no name, but scopes its classes:
Program(_, _):
scopes Class
Extend your name binding rules with
scopes
clauses to scope all definition sites.
Name Checking
Similar to type checking, you can realise name checking in terms of constraints, which are implemented as rewrite rules.
The Index
Spoofax stores all definitions and references in an in-memory data structure called the index.
By collecting all this summary information about files in a project together, it ensures fast access to global information.
The index is updated automatically with changes to the file system (e.g., files being deleted or removed) and is persisted as Eclipse exits.
All entries in the index have a URI.
Index entries can be represented as terms and accessed by an API.
For example, =Def([Class(),"c", Anon("12")]) is a definition entry for a class.
Internally, index entries are stored in tables for efficient random access. They also contain meta-data such as the file name and line number of the definition.
With this meta-data, Spoofax can provide editor services such as reference resolving.
Naming Constraints
You can use the index API to detect duplicate definitions, missing definitions, and unused definitions:
constraint-error:
Class(c, _, _, _) -> (c, $[Multiple declarations for class [c]])
where [_, _|_] := <index-lookup-all> c
constraint-error:
Parent(c) -> (c, $[Class [c] is not defined])
where <index-is-unresolved> c
constraint-warning:
Class(c, _, _, _) -> (c, $[Unused class [c]])
where <index-is-unused> c
Interaction with Type Analysis
Types of Definition Sites
Spoofax can also store type information about the definition sites of names in the index.
For example, to determine the type of variable references, Spoofax needs to store the type of the corresponding declarations.
Consider the following name binding rules which also involve type information:
Var(t, v): defines Variable v of type t
Spoofax uses this rule to determine the type of a variable declaration.
This type is stored as an information about the variable name.
Types of Use Sites
Stored type information can then be used in the typing rules for variable references:
type-of: VarRef(x) -> <index-type-of> x
Types can also determine the context, in which a name should be resolved.
For example, in a method call, the type of the first expression determines the callee.
This can be expressed in NBL as follows:
Call(e, m, _):
refers to Method m in Class c
where e has type ClassType(c)
At this point, you can complete your type system and add missing type constraints.
%GS% Challenge: Try to type this . First, pretend that This() refers to a field This() . Second, define a typing rule which determines the type based on the annotated URI. You can retrieve the path of the URI with index-uri-path .
|
Inheritance
Inheritance defines a subtyping relation between super- and subclasses.
%GS% Challenge: Use the index to implement a subtyping relation. First, store inheritance relations as type information. Second, implement a strategy parent , which rewrites a class name to the name of its parent class. Next, implement a strategy ancestor , which rewrites a class name to a list of its ancestors' names. Make sure that this strategy does not cycle, even in the presence of cyclic inheritance. Finally, implement a strategy subtype , which succeeds on a pair of types, if the first type is a subtype of the second type.
|
Furthermore, it makes fields of the superclass available to the subclass.
This can be expressed in NBL in terms of an import:
Class(c, Parent(p), _, ms):
defines Class c
scopes Field
refers to Class p
imports Field from Class p {transitive}
In the current version of NBL, it is important to specify the import on the class level. This might require an adaptation of your existing name binding rules for classes.
You can now check inheritance-related constraints.
Therefor, you should use import-related strategies from the index API. For example, you can check for overriding fields:
constraint-error:
Field(_, f) -> (f, $[Overriding field [f]])
where <index-lookup-imported> f
--
GuidoWachsmuth - 11 Oct 2012