Accessing _all_ downstream annotations of a node in Rascal

174 views Asked by At

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?

1

There are 1 answers

7
Jurgen Vinju On BEST ANSWER

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