!7 abstract sclass Node { new LinkedHashSet children; toString { ret shortClassName(this) + ": " + toString2(); } abstract S toString2(); void add(Node n) { children.add(n); } } sclass TextNode > Node { S text; *() {} *(S *text) {} S toString2() { ret quote(text); } } sclass TokNode > Node { L tok; *() {} *(LS *tok) {} S toString2() { ret sfu(tok); } public bool equals(O o) { ret stdEq(this, o, 'tok); } public int hashCode() { ret stdHash(this, 'tok); } } sclass ObjectNode > Node { O obj; *() {} *(O *obj) {} S toString2() { ret sfu(obj); } public bool equals(O o) { ret stdEq(this, o, 'obj); } public int hashCode() { ret stdHash(this, 'obj); } } p-exp { S input = first(mL("Monolog examples")); TextNode root = new(input); processNode_recursive(root); showFrame(jDynamicTree(root, func(Node n) -> Collection { n.children })); } svoid processNode_recursive(Node n) { if (n == null) ret; processNode(n); for (Node child : n.children) processNode(child); } svoid processNode(Node n) { if (n instanceof TextNode) { TextNode tn = cast n; L tok = javaTokPlusCurly_dontKeep(tn.text); n.add(TokNode(tok)); } if (n instanceof TokNode) { TokNode tn = cast n; if (containsSubList(tn.tok, ll("=", "", ">"))) n.add(ObjectNode(splitAtDoubleArrow(tn.tok))); } }