abstract sclass CTNode { new LinkedHashSet children; toString { ret joinNemptiesWithColonSpace(shortClassName(this), toString2()); } S toString2() { ret ""; } // override me bool add(CTNode n) { ret children.add(n); } } sclass ObjectNode extends CTNode { A obj; *() {} *(A *obj) {} S toString2() { ret sfu(obj); } public bool equals(O o) { ret stdEq(this, o, 'obj); } public int hashCode() { ret stdHash(this, 'obj); } } sclass OpNode > CTNode { S op; bool haveResult; O result; // this is the result coming back up from the operation *() {} *(S *op) {} S toString2() { ret haveResult ? result + " <- " + op : op; } public bool equals(O o) { ret stdEq(this, o, 'op); } public int hashCode() { ret stdHash(this, 'op); } void setResult(O o) { result = o; haveResult = true; } }