// expects a "good tree" now abstract sclass DialogBot { sbool debug, repeat = true; OccTree tree, node; *(OccTree *tree) { node = tree; } void take(E e) { selectMatching(e); } E getSingleOutput() { E e = nextBotOutput(); if (e != null) select(node, e); ret e; } void rewind() { node = node.parent; } E findMatch(OccTree node, E in) { for (E e : keys(node.followUp)) if (matchE(e, in)) ret e; null; } bool selectMatching(E in) { E e = findMatch(node, in); bool rep = false; if (repeat && e == null && node != tree) { e = findMatch(tree, in); rep = true; } if (e != null) { if (debug) { if (rep) print("Repeat!"); print("Selecting " + struct(e) + " matching " + struct(in)); } select(rep ? tree : node, e); true; } if (debug) print("No match for " + structure(in) + " in " + structure(node.followUpKeys())); false; } abstract bool matchE(E a, E b); E nextBotOutput() { new L l; Collection follows = keys(node.followUp); for (E e : follows) if (e.a() || isCommand(e)) l.add(e); if (empty(l)) null; // fail("no good bot output: " + struct(node.followUpKeys())); if (l(l) > 1) fail("unclear bot output: " + struct(follows)); ret first(l); } void select(OccTree n, E e) { node = n.followUp(e); if (node == null) fail("can't select " + structure(e)); } bool isCommand(E e) { ret e.state() && matchStart("bot", e.state); } } sclass LooseBot extends DialogBot { *(OccTree node) { super(node); } bool matchE(E a, E b) { // standard NL matching ret eq(a.type(), b.type()) && match(a.text(), b.text()); } }