I read this question while trying to do something similar. The answer given there does not solve my problem.
I want to use a visit statement to determine the 'mass' of each subtree, so for each node I want to sum the masses of all descendants. For example, a visiting step which encounters this node expression with a list in it:
\anode([\bnode()[@mass=1], \bnode()[@mass=2]], \cnode()[@mass=5])
should yield this:
\anode([\bnode()[@mass=1], \bnode()[@mass=2]], \cnode()[@mass=5])[@mass=8]
So I do not just want to filter out the non-nodes, but actually traverse them. Including a case for lists in my visit statement does not work (at least not obviously) because lists cannot have annotations.
Is there a natural way to propagate the complete annotation information up a tree?
This first solution is precise and useful if the number of nodes with container children is not so large:
x = \anode([\bnode()[@mass=1], \bnode()[@mass=2]], \cnode()[@mass=5]);
visit(x) {
case n:\anode(l) => n[@mass=(0 | it + e@mass | e <- l)]
}
(You'd probably also abstract the reducer expression in a local function)
The second solution would be to abstract from the node type, in case you have many of these:
visit(x) {
case n:str _(l) => n[@mass=(0 | it + e@mass | e <- l)]
}
The third solution would work best in case the annotations are nested even deeper, as in lists of lists etc, and you have many different types of nodes:
import Node;
int computeMass(list[value] x) {
mass = 0;
top-down-break visit(x) {
case node x : mass += (x@mass?) ? x@mass : 0;
}
return mass;
}
visit(x) {
case node n => n[@mass=computeMass(getChildren(n))]
}
I prefer the very first solution because it is most precise.
Note that we are replacing the annotations feature by "keyword parameters" in the near future; with almost the same semantics, different syntax, like \cnode(mass=2)
for \code()[@mass=2]
. With the new keyword parameters you would also have a lazily computed default value for the mass field, like so:
data N()
= anode(list[N] children, int mass=(0 | it + c.mass | c <- children))
| cnode(int mass=0)
;
anode([cnode(mass=1),cnode(mass=2)]).mass == 3