Forwarding equations
Forwarding is a mechanism for providing default values for attributes that we introduced to attribute grammars that is especially useful for defining language extensions.
A production can specify a semantically equivalent tree that it will provide default values for any attributes that are not explicitly defined on that production. Thus, the type of the forwarded-to tree must be the same as the nonterminal on the left-hand side of the production.
Any inherited attributes assigned to the forwarding-tree are automatically copied to the forwarded-to tree.
Example: An example will help to clarify. Consider the definition of a greater-than-or-equal operator
>=
found in theExpr.sv
file of the Simple tutorial’s abstract syntax.
abstract production gte
e::Expr ::= l::Expr r::Expr
{
e.pp = "(" ++ l.pp ++ " >= " ++ r.pp ++ ")" ;
forwards to not(lt(l,r)) ;
}
This productions defines the value of its
pp
attribute but does not have a definition of the attributec_code
to define its translation to C code. When a node in the syntax tree constructed by thegte
production is queried for the value of itspp
attribute it will compute it based on the explicit definition. If that node is queried for the value of itsc_code
attribute however it will “forward” that query to the treenot(lt(l,r)
. This tree is the semantic equivalent of thegte
tree ((l >= r) = -(l < r)
).
If inherited attributes, for example
env
are used in the computation ofc_code
, then those attributes must be available on the forwards to tree. This is accomplished automatically as any inherited attribute that decorates nodee
is copied to the forwards to node.
A production can specify the values of inherited attributes for the forwarded to tree. To do so, the forwards clause is augmented with attribute definitions and has the following form
forwards to <expression> with { ( <name> = <expression> ; )* } ;
The primary purpose of forwarding is to enable safe extension of a host language. Forwarding permits one extension to safely add new attributes to the host language, while another extension adds new productions. This is accomplished by simple evaluating the attribute on the forwarded-to tree for those new productions.
But, forwarding is often used in a few other common ways:
Single-dispatch based on types, for example, is easy to implement using forwarding. Types can be given an attribute like the following:
synthesized attribute lengthDispatcher :: (Expr ::= Decorated Expr);
Using the inheritance method of the previous section, the default type can set this attribute to an error production, and relevant types (such as strings and lists) can override that with a production specialized to their types.
Then the syntax itself can be implemented as follows:
abstract production lengthFunction
top::Expr ::= arg::Expr
{
forwards to arg.type.lengthDispatcher(arg);
}
Note: Although it is unlikely that calls to length would be nested too deeply, see Decorated vs Undecorated Examples for an explanation for why the production attribute has a decorated child.
Complex extensions will often forward to a higher order attribute. For example,
synthesized attribute hostTranslation :: Expr;
abstract production extensionMagic
top::Expr ::= 'magic' m::ComplexNewSyntax
{
forwards to m.hostTranslation;
}
Here, the “semantically equivalent” tree is computed based on the code.
Part of this page should be on the forwarding concept page, and this trimmed down to just reference material.