3

I want to create a tree (using a Node or ADT) in which every node has an annotation pointing back to its parent. Below is an example with a simple linked list data structure:

import util::Math;
import IO;
import Node;

anno LinkedList LinkedList@parent;
anno int LinkedList@something;

data LinkedList = item(int val, LinkedList next)
                | last(int val)
                ;

public LinkedList linkedList =  item(5,
                                    item(4,
                                        item(3,
                                            item(2,
                                                last(1)[@something=99]
                                            )[@something=99]
                                        )[@something=99]
                                    )[@something=99]
                                )[@something=99];

public LinkedList addParentAnnotations(LinkedList n) {
    return top-down visit (n) {
        case LinkedList x: {
            /* go through all children whose type is LinkedList */
            for (LinkedList cx <- getChildren(x), LinkedList ll := cx) {
                /* setting the annotation like this doesn't seem to work */
                cx@parent = x;
                // DOESN'T WORK EITHER: setAnnotation(cx, getAnnotations(cx) + ("parent": x));
            }
        }
    }
}

Executing addParentAnnotations(linkedList) yields the following result:

rascal>addParentAnnotations(linkedList);
LinkedList: item(
  5,
  item(
    4,
    item(
      3,
      item(
        2,
        last(1)[
          @something=99
        ])[
        @something=99
      ])[
      @something=99
    ])[
    @something=99
  ])[
  @something=99
]
Jurgen Vinju
  • 6,393
  • 1
  • 15
  • 26
pancake
  • 1,923
  • 2
  • 21
  • 42

1 Answers1

3

The thing is that Rascal data is immutable, so you can not update anything using an assignment. The assignment will simply give you a new binding for cx with the annotation set, but will not change the original tree.

To change the original tree, you can either use the => operator for case statements, or the insert statement as follows:

 case LinkedList x => x[@parent=...] // replace x by a new x that is annotated

or:

 case LinkedList x : {
    ...
    x@parent= ...;
    insert x; // replace original x by new x in tree
 }

Some other tip, in the Traversal.rsc library you can find a function called getTraversalContext() which produces a list of parents of the currently visited node if called from the body of a case:

import Traversal;

visit (...) {
    case somePattern: {
        parents = getTraversalContext();
        parent = parents[1];
    }
}
Jurgen Vinju
  • 6,393
  • 1
  • 15
  • 26
  • That looks very promising, except that I can't seem to import the Traversal module. It can't be found according to eclipse. – pancake Sep 26 '13 at 14:29
  • After a little researching I see a difference between the latest version of the git repository and the eclipse plugin. `Traversal.rsc` is not present in the eclipse plugin. I'm assuming the eclipse plugin from the update site does not have this feature yet. I tried installing the plugin from the "unstable" update site, but it fails: it requires `org.eclipse.jetty.http 8.1.3`, but it can't be found. I'm using eclipse 3.7 for RCP/RAP developers. – pancake Sep 26 '13 at 15:20
  • Problem solved, after installing eclipse Juno (4.2) for RCP/RAP developers. And I forgot to thank you for your solution. Helped me out very much, thanks!! Except now I get the root node as parent all the time, and any other index than -1 (e.g. 1 or -2). I need the direct parent of the currently evaluated node. – pancake Sep 26 '13 at 16:59
  • I see now how the traversal context is built up. I need index `1`, not `-1`. My solution: `if ([_, LinkedList parent, _*] := parents) { x@parent = parent; }` – pancake Sep 26 '13 at 17:14
  • Aha! I did not test the code I put here. Thanks for the fix. Can you mark the answer as accepted too? Cheers! – Jurgen Vinju Sep 27 '13 at 10:54
  • Something that I don't get yet, is why you need the match? Is that to account for empty parent lists? – Jurgen Vinju Sep 27 '13 at 10:57
  • 1
    I need the match to account for the top level item that does not have a parent (and only has 1 item in the parents list, namely itself, otherwise I get an "index out of bounds" exception when I use parents[1]). Thanks again! – pancake Sep 27 '13 at 16:57
  • 1
    You could say `parent = parents[ (size(parents) > 1) ? 1 : 0 ]` if you always want to get the immediate parent or, if there is none, the current node. Then you would not need the match. – Mark Hills Oct 03 '13 at 18:25