!7 static Map> theSet; static new LinkedHashSet allObjects; static long changes; sclass Updatable { void update {} void setField(S field, O value) { if (eq(get(this, field), value)) ret; set(this, field, value); change(); } } sclass Expectation { S ifClass; Runnable action; *() {} *(S *ifClass, Runnable *action) {} } sclass Word extends Updatable { S text; // or null if unknown new LinkedHashSet prev; new LinkedHashSet next; L constituents; // if group new L expectations; new L fulfilledExpectations; new TreeSet classes; new LinkedHashSet groups; // I am part of new LinkedHashMap traits; void update { // Add direct word classes if (text != null) if (classes.addAll(reverseLookupInMapToSets(theSet, text))) change(); // Make text for group if (isGroup() && text == null) { L l = collect(constituents, 'text); if (!anyNull(l)) setField(text := joinWithSpace(l)); } // Process expectations for (Expectation e : cloneList(expectations)) { print("Checking expected class " + e.ifClass); if (classes.contains(e.ifClass)) { moveElementFromCollectionToCollection(e, expectations, fulfilledExpectations); change(); callF(e.action); } } for (S c : classes) addTraitsForClass(c); for (Trait t : valuesList(traits)) t.update(); } bool isGroup() { ret constituents != null; } void addExpectation(Expectation e) { print("addExpectation " + e); expectations.add(e); change(); } void addTraitsForClass(S c) { if (eq(c, "")) addTrait(LinkWithTo("", "")); if (eq(c, "")) addTrait(LinkWithTo("", "")); } void addTrait(Class c) { if (!traits.containsKey(c)) traits.put(c, nu(c, w := this)); } } static Word makeGroup(Word a, Word b) { print("makeGroup " + a.text + " / " + b.text); L list = ll(a, b); // look for existing group for (Word g : a.groups) if (eq(g.constituents, list)) ret g; // new group new Word g; allObjects.add(g); change(); g.constituents = list; for (Word w : list) w.groups.add(g); for (Word prev : a.prev) prev.next.add(g); for (Word next : b.next) next.prev.add(g); ret g; } sclass Trait extends Updatable { Word w; } sclass Preposition extends Trait { int expectationsSentToNext; void update { if (l(w.next) > expectationsSentToNext) { for (final Word next : dropFirst(expectationsSentToNext, w.next)) next.addExpectation(Expectation("", r { makeGroup(w, next).classes.add(""); })); expectationsSentToNext = l(w.next); } } } sclass LinkWithTo extends Trait { S linkWith, linkTo; // classes int expectationsSentToNext; *() {} *(S *linkWith, S *linkTo) {} void update { if (l(w.next) > expectationsSentToNext) { for (final Word next : dropFirst(expectationsSentToNext, w.next)) next.addExpectation(Expectation(linkWith, r { makeGroup(w, next).classes.add(linkTo) })); expectationsSentToNext = l(w.next); } } } p-exp { S sentence = "In the movies Dracula always wears a cape"; L rawWords = printStruct(words(sentence)); theSet = ai_englishWordCategoriesWithElements(); new L words; for (S w : rawWords) words.add(nu(Word, text := w)); for (int i = 0; i < l(words)-1; i++) linkWords(words.get(i), words.get(i+1)); //printStruct(first(words)); addAll(allObjects, words); long lastChanges; do { lastChanges = changes; print(n2(changes, "change")); for (Updatable w : cloneList(allObjects)) w.update(); } while (lastChanges != changes); for (Word w : words) printStruct(cloneForPrinting(w)); print(); L groups = [Word w : instancesOf(Word, allObjects) | w.constituents != null]; print(n2(groups, "group")); for (Word g : groups) print("Group: " + sfu(cloneForPrinting(g))); } svoid linkWords(Word a, Word b) { a.next.add(b); b.prev.add(a); } static O cloneForPrinting(Word w) { ret cloneWithoutFields(w, 'prev, 'next, 'constituents); } svoid change() { ++changes; }