annotation origin :: Origin; annotation tag<a> :: a;
Recall the distinction between decorated and undecorated types. Attributes are computed on decorated nodes. To obtain a decorated node, an undecorated node is supplied with inherited attributes. This makes the synthesized attributes on that node (which might need to use those incoming inherited attributes) computable.
Annotations are very different from attributes. They are values that are supplied to create undecorated nodes — similar to a production’s children. Unlike children, however, they are not something you would supply inherited attributes to, and an annotation appears uniformly on all productions for a nonterminal.
Annotation declaration looks very similar to attribute declarations:
annotation foo<a> :: a; annotation origin :: Origin; annotation location :: Location;
Again, the syntax looks very much like for attribute occurrence declarations:
annotation location occurs on Expr;
And annotations can also be listed along side attributes in nonterminal
nonterminal Expr with location, foo<String>;
The syntax looks just like attribute access.
There’s an important semantic difference, however: annotations are accessed from the undecorated node, whereas to evaluate an attribute you require a decorated node.
Annotations are supplied via named arguments when a node is created. The syntax looks as follows:
and(l.ast, r.ast, location=lhs.location)
The named arguments must come after the ordered arguments.
In the standard library there is a
location annotation that the parser has special understanding of.
Normally, the parser would not know how to supply an annotation value for the concrete syntax it is parsing, but
location is special and will be automatically filled in during parsing.
This should make obtaining location much easier.
Annotations are a feature that’s not fully matured, and so sometimes there are missing features we’d like. A wishlist is tracked on GitHub.
The idea for annotations was shamelessly stolen from Rascal. Yoink!