4

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?

Community
  • 1
  • 1
Anson
  • 55
  • 6
  • I guess you are thinking attribute grammars? – Jurgen Vinju Jun 18 '15 at 05:08
  • Yes. I did not realize so at first, but that is what I am thinking of. I read [here](http://www.homepages.cwi.nl/~paulk/publications/rascal-lncs.pdf) that Rascal generalizes functionality of attribute grammar tools. Does that include the above? – Anson Jun 18 '15 at 05:32
  • (or something to the same effect) – Anson Jun 18 '15 at 05:40

1 Answers1

2

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
Jurgen Vinju
  • 6,393
  • 1
  • 15
  • 26
  • BTW, the above solutions only sums up _direct_ descendants. You could also do _all_ descendants using the deep match operator `/node n` to find all nested nodes recursively in the reducer which iterates over the children. – Jurgen Vinju Jun 18 '15 at 05:43
  • Thank you! The number of constructors with container children was large, and there were also direct descendants, so I opted for the third option. The 'x has mass' syntax did not detect the annotation when I used it. I hope you do not mind if I edit the code in your answer slightly. – Anson Jun 18 '15 at 07:01
  • Yes, it does! That's nicer. – Anson Jun 18 '15 at 12:31
  • @Anson could you please explain the edit: http://stackoverflow.com/review/suggested-edits/8492055 Is it a syntax improvement or you fixed a mistake? I approved the edit, but it is still unclear without an explanation (which could be in the edit summary field). – Nick Volynkin Jun 18 '15 at 13:02
  • @Anson by the way, you can still open the edit, change summary and/or post and confirm it again. Future reviewers will see the updated version. – Nick Volynkin Jun 18 '15 at 13:03
  • Oh, I understood it from comments. Ok, ping me, if it gets rejected ) – Nick Volynkin Jun 18 '15 at 13:04
  • Hm, I thought I added a summary. Apparently something went wrong there. – Anson Jun 18 '15 at 13:08