- edited description
Remove Side Effects in Aspect Weaving
Aspect introductions are woven into the AST (JastAdd's internal AST, not the generated AST) by directly modifying the AST. These modifications are mixed with attribute evaluations, meaning that the attributes compute values based on a partially woven AST. For most attributes this is OK in practice, but it might break stuff we don't know about yet because some attributes are producing wrong results. For example: the parents of an AST class depend on synthesized NTAs. The test case inh/nta_04p
in jastadd-test demonstrates this:
// grammar: { A; B; }
aspect Test {
syn nta B A.b() = new B();
inh A C.a();
eq A.b().a() = this;
}
The class A
is a parent of B
, however you wouldn't know this just from looking at the abstract grammar. The synthesized NTA A.b()
makes A
a parent of B
, thus the parent information depends on attribute weaving. The parentMap()
attribute is used to figure out the parents of AST classes, and so this attribute depends on attribute weaving.
It gets more complicated since attributes can be introduced into an AST class not only by direct attribute declarations, but also through interfaces. For example (syn/interface01
):
aspect Test {
interface I { }
Node implements I;
syn boolean I.attr();
eq Node.attr() = false;
}
Interface weaving is done after normal attribute weaving and it depends on refinement processing.
A large downside to the current side-effect driven attribute weaving is that the parentMap()
attribute can not be memoized, due to it producing incorrect results in the first example above. However, parentMap()
can take a long time to evaluate and it gets exponentially worse for larger JastAdd projects.
Possible Solution
One possible solution is to add NTAs to compute attributes of different types for an AST class. Instead of iterating over attribute declarations and copying them into the target AST class, the AST class could query a global map of attributes to find attributes that should be woven into that class.
A sketch to this approach:
syn lazy Map<String, Collection<SynDecl>> Grammar.synDeclMap(String name) {
Map<String, Collection<SynDecl>> map = new HashMap<>();
for (TypeDecl type : getTypeDeclList()) {
map.put(type.name(), new ArrayList<>());
}
for (SynDecl decl : grammar.synDecls) {
map.get(decl.hostName).add(decl);
}
return map;
}
syn nta List<SynDecl> ASTDecl.synDecls() {
List<SynDecl> decls = new List<>();
for (SynDecl decl : grammar().synDeclMap().get(name())) {
decls.add(decl);
}
return decls;
}
Comments (6)
-
reporter -
reporter - edited description
-
reporter - edited description
-
reporter Attribute refinements are difficult to process in a declarative attribute-driven way because they insert new body declarations into existing AST classes. If refinements are handled via attributes, then mostly all other weaving needs to be converted at once.
-
reporter Declarative synthesized attribute weaving
This replaces the imperative weaving transformation for synthesized attribute declarations by a nonterminal attribute that computes all existing synthesized attribute declarations.
see #268
→ <<cset 164477ab8d3c>>
-
reporter Declarative synthesized NTA lookup
Replaced the SynthesizedNta list child of ASTDecl by a synthesized attribute ASTDecl.synNtaDecls() in order to avoid a weaving side effect adding SynthesizedNta nodes to ASTDecl.
see #268
→ <<cset 0fcf3e9d3b6a>>
- Log in to comment