abstract sclass Adapter { void learn(S in, S out) {} S get(S in) { ret in; } O remember() { null; } void rewind(O o) {} } abstract sclass DialogBot { sbool debug, repeat = true; OccTree2 tree; new L nodes; new L rewindStack; new L adapterRewind; Adapter adapter; *(OccTree2 *tree) { nodes.add(tree); } *(OccTree2 tree, Adapter adapter) { this(tree); this.adapter = adapter; } void take(E in) { remember(); if (repeat) setAdd(nodes, tree); new L l; for (OccTree2 node : nodes) for (OccTree2 n : node.next) if (matchE(adapt(n.e), in)) l.add(n); if (adapter != null) for (OccTree2 n : l) adapter.learn(adapt(n.e).text(), in.text()); nodes = l; } E adapt(E e) { if (adapter != null) ret new E(adapter.get(e.text()), e.type()); // TODO: security when rewriting states ret e; } void remember() { if (rewindStack != null) { rewindStack.add(cloneList(nodes)); if (adapter != null) adapterRewind.add(adapter.remember()); } } E getSingleOutput() { remember(); new L l; for (OccTree2 node : nodes) for (OccTree2 n : node.next) if (n.e.a() || isCommand(n.e)) l.add(n); nodes = l; ret nempty(nodes) ? adapt(first(nodes).e) : null; } void rewind() { nodes = popLast(rewindStack); if (adapter != null) adapter.rewind(popLast(adapterRewind)); } abstract bool matchE(E a, E b); bool isCommand(E e) { ret e.state() && matchStart("bot", e.state); } void noRewind() { rewindStack = null; } } sclass LooseBot extends DialogBot { *(OccTree2 node) { super(node); } *(OccTree2 tree, Adapter adapter) { super(tree, adapter); } bool matchE(E a, E b) { // standard NL matching ret eq(a.type(), b.type()) && match(a.text(), b.text()); } } sclass WordAdapter extends Adapter { new Map wordMap; void learn(S in, S out) { L t1 = nlTok2(dropPunctuation2(in)); L t2 = nlTok2(dropPunctuation2(out)); if (l(t1) != l(t2)) ret; for (int i = 1; i < l(t1); i += 2) { S w1 = t1.get(i), w2 = t2.get(i); if (!eqic(w1, w2)) // just overwrite - be flexible! wordMap.put(w1.toLowerCase(), w2.toLowerCase()); } } S get(S s) { L tok = nlTok2(s); for (int i = 1; i < l(tok); i += 2) { S w = wordMap.get(tok.get(i).toLowerCase()); if (nempty(w)) tok.set(i, w); } ret join(tok); } O remember() { ret cloneMap(wordMap); } void rewind(O o) { wordMap = (Map) o; } }