!7 cmodule TopDownParsing > DynSCP { switchable S input = "Bob paid for Charlie 's college education"; TryPatterns root; transient S selectedGrouped; transient ReliableSingleThread rstProcessInput = dm_rstWithPreDelay(this, 500, r processInput); transient Q calcQ; // Tree has 2 types of nodes: composite and options abstract class Base { L children; L children() { ret children; } abstract S grouped(); } // composite node class AppliedPattern > Base { S pattern; LS arguments; L children() { runInQAndWait(calcQ, r { if (children == null) children = map(arguments, a -> new TryPatterns(a)); }); ret children; } toString { ret stdToStringWithFields(this, 'pattern, 'arguments); } S grouped() { ret format3_round(pattern, toStringArray(arguments)); } } // options node (try different patterns) class TryPatterns > Base { S input; *() {} *(S *input) {} L children() { runInQAndWait(calcQ, r { if (children == null) { children = new L; for (S pat : patterns()) for (LS arguments : matchesToStringLists(flexMatchIC_all(pat, input))) children.add(setAll(new AppliedPattern, pattern := pat, +arguments)); } }); ret children; } toString { ret stdToStringWithFields(this, 'input) + ", n=" + l(children()); } S grouped() { ret empty(children()) ? null : first(children).grouped(); } } start { calcQ = dm_startQ(); dm_watchFieldAndNow input(rstProcessInput); } JComponent visualize2() { JTree tree = jDynamicTree(root, x -> x.children()); onTreeSelectionChanged(tree, r { Base base = cast jtree_selectedUserObject(tree); setField(selectedGrouped := base == null ? null : base.grouped()); }); ret northCenterAndSouthWithMargins( jCenteredSection("Input", dm_centeredTextField input()), tree, jCenteredSection("Grouped", dm_label selectedGrouped())); } LS patterns() { ret dm_getUnclearList("patterns"); } // API void processInput { parse(input); } TryPatterns parse(S s) { setField(input := s); TryPatterns root = new TryPatterns(s); setField(+root); revisualize2(); ret root; } TryPatterns root() { ret root; } }