!752 static Map types; p { types = new PersistentMap("types"); } synchronized answer { exceptionToUser { if (!tb()) null; if (match("* is a *", s, m) || match("* is an *", s, m)) { /*if (isQuoted(m.get(0)))*/ { S word = toLowerCase(m.unq(0)); S type = toLowerCase(m.unq(1)); types.put(word, type); ret "OK!"; } } if (match("* is not a *", s, m) || match("* is not an *", s, m)) { S word = m.tlc(0); S type = m.tlc(1); if (eqic(types.get(word), type)) { types.remove(word); ret "Removed!"; } } if (match("type *", s, m) || match("what is *", s, m)) { S word = m.tlc(0); S type = types.get(word); if (nempty(type)) ret fixSentence(quote(word) + " is a " + type); } if (matchStartSimple("pattern", s, m)) { S input = m.rest(); L tok = /*dropPunctuation*/(nlTok(input)); new L l; for (int i = 1; i < l(tok); i += 2) { S word = tok.get(i); S type = getType(word); if (empty(type)) type = "?"; l.add(type); } ret join(" ", l); } if "word types" ret structure(asList(asSet(values(types)))); if "word type *" ret structure(keysForValue(types, m.tlc(0))); }} static S fixformat(S s, O... data) { ret fixSentence(format(s, data)); } static S fixSentence(S s) { L tok = nlTok(s); for (int i = 1; i+2 < l(tok); i += 2) if (eqic(tok.get(i), "a") || eqic(tok.get(i), "an")) tok.set(i, aOrAn(tok.get(i+2))); ret join(tok); } static S aOrAn(S s) { if (!empty(s)) { if (isVowel(s.charAt(0))) ret "an"; if (eqic(s, "honest")) // etc ret "an"; } ret "a"; } static boolean isVowel(char c) { ret containsIgnoreCase("aeiou", c); } static S getType(S word) { if (isQuoted(word)) ret "quoted"; if (isInteger(word)) ret "number"; if (!isExtendedIdentifier(word)) ret "symbol"; ret types.get(tlc(word)); } static boolean matchStartSimple(S prefix, S s, Matches m) { s = s.trim(); if (startsWithIgnoreCase(s, prefix + " ")) { m.m = new S[] { s.substring(l(prefix) + 1).trim() }; ret true; } ret false; }