// should now be persistable sclass RuleEngine2 { sclass SimplifyWithRule extends Rule { transient O _function; // function name or F1. can return S or LS bool isSplitRule; // splitter rather than simplifier bool callOnTree; // function takes GazelleTree rather than S O function() { if (_function == null) { gazelle_parseInstruction(this); assertNotNull(_function); } ret _function; } } abstract sclass QuickCheck { abstract bool check(S input); } sclass CicQuickCheck extends QuickCheck { S string; bool check(S input) { ret cic(input, string); } } sclass Rule { S globalID; LS in; S out, outType; LS comments; Set vars; L insWithType; L inputMassagers; // func(S input) -> S for every in L checkerFunctions; // func(S input) -> SS for every in S text; // original text QuickCheck applicabilityQuickCheck; Set dontChangeWords; // (optional) massage the variable map after matching // L SS newVarMap> L mapMassagers; S asText() { if (l(in) > 1 && eq(last(in), out)) ret join(" \n+ ", dropLast(in)) + " \n+=> " + out; ret join(" \n+ ", in) + " \n=> " + out; } void addMapMassager(O massager) { if (massager != null) mapMassagers = addToOrCreateList(mapMassagers, massager); } } int minScore = 50; // minimal percentage score for rule matching int maxLevel = 100, maxOutLines = 10000; new L rules; bool printMappings, printGroupedData, printRejectedVars; Set facts = ciSet(); transient F2 callFunctionOnString = func(S function, S s) -> O { callAndMake(function, s) }; *() {} *(LPair rulesWithComments) { for (PairS p : unnull(rulesWithComments)) addRule(p.a, splitAtDoubleArrow_pair(p.a), p.b, null); } void copyRulesFrom(RuleEngine engine) { if (engine != null) rules.addAll(engine.rules); } void addRules2(L> rulesWithCommentsAndID) { new Matches m; for (T3 p : unnull(rulesWithCommentsAndID)) { PairS p2 = splitAtDoubleArrow_pair(p.a); if (p2 != null) continue with addRule(p.a, p2, p.b, p.c); new SimplifyWithRule r; r.globalID = p.c; r.text = p.a; r.comments = lines(p.b); S s = p.a; gazelle_parseInstruction(r); if (r._function == null) continue; rules.add(r); } } void addRule(S text, PairS rule, S comments, S globalID) { if (rule == null) ret; rule = ai_rule_pair_expandPlusArrow(rule); LS conditions = tok_splitAtPlusAtBeginningOfLine(rule.a); new Rule r; r.globalID = globalID; r.text = text; r.comments = lines(comments); r.in = conditions; r.insWithType = map(conditions, func(S s) -> TextAndType { TextAndType(s, "standard") }); for (int i = 0; i < l(r.in); i++) { S s = r.in.get(i); continue unless endsWith(s, ""); if (eq(s, "")) r.checkerFunctions = listSetWithCreate(r.checkerFunctions, i, func(S s) -> SS { nempty(s) ? litmap() : null }); else if (jmatch("* says: ", javaTokWithBrackets(s))) { print("Made input massager"); r.inputMassagers = listSetWithCreate(r.inputMassagers, i, func(S s) -> S { new Matches m; ret jMatchStart("* says:", javaTokWithBrackets(s), m) ? m.get(0) + " says: " : s; }); } } if (jmatchAny(ll("in = dialog", "fact + event => replacement fact"), r.comments)) for (int i = 0; i < l(r.insWithType); i++) r.insWithType.get(i).type = "dialog-" + (l(r.insWithType)-i); new Matches m; for (S s : r.comments) { if (jMatchStart("in * =", s, m) && isInteger($1) && startsWith($2, 'statement)) set(get(r.insWithType, parseInt($1)-1), type := $2); if "don't change *" r.dontChangeWords = createOrAddToCISet(r.dontChangeWords, $1); if (jMatchStart("out = ", s, m)) r.outType = m.rest(); } r.out = rule.b; r.vars = ai_wordsInBothSidesOfPair_uncurly(rule); rules.add(r); } /* LS addFacts(Collection facts) { ret addAllAndReturnNew(this.facts, facts); } void addFact(S fact) { facts.add(fact); } void addAndInterpretFacts(Collection facts) { addFacts(recursiveInterpretations(addFacts(facts))); } void processInput(S input) { print("\nInput: " + tok_dropCurlyBrackets(input)); temp tempIndent(); for (S in : interpretations(input)) { print("\nInterpretation: " + formatForUser(in)); Set out = litciset(); interpretRecursively(in, 5, out); } } Set recursiveInterpretations(S in) { ret recursiveInterpretations(ll(in)); } Set recursiveInterpretations(Iterable in) { Set inSet = asCISet(in); Set out = litciset(); new LinkedHashSet queue; addAll(queue, inSet); while (l(out) < maxOutLines && nempty(queue)) { LS intp = interpretations(popFirst(queue)); for (S i : intp) if (!inSet.contains(i) && !out.contains(i)) { out.add(i); queue.add(i); if (l(out) >= maxOutLines) break; } } ret out; } void interpretRecursively(S input, int level, Set out) { if (level <= 0) ret; LS interpretations = interpretations(input); temp tempIndent(); for (S in : addAllAndReturnNew(out, interpretations)) { print(" => " + formatForUser(in)); for (S s : lithashset(in, ai_superSimpleVerbCorrector(in))) interpretRecursively(s, level-1, out); } } LS defaultParse(S s) { ret codeTokens_lazy_uncurly(javaTokNPunctuationWithBrackets_cached(s)); } LS interpretations(S input) { LS tokI = defaultParse(input); //print("Raw parse: " + tokI); new LS interpretations; for (Rule rule : rules) { continue unless l(rule.in) == 1; LS tokR = defaultParse(first(rule.in)); final SS map = ciMapWithoutKeysEqualToValues(zipTwoListsToCIMap_strict(tokR, tokI)); if (map == null) continue; // Found matching rule int score = l(tokR)-l(map); L nonVars = withoutDollarVars(listMinusSet(keys(map), rule.vars)); if (printRejectedVars && nempty(nonVars)) print("Rejected vars: " + nonVars); if (nempty(nonVars)) score = 0; int percentScore = ratioToIntPercent(score, l(tokR)); if (printMappings) print(" " + percentScore + "% " + reverseMapCI_joinDuplicatesWithPlus(map) + " | " + rule.in); if (percentScore < minScore) continue; // Make consequence S c = rule.out; c = join(mapCodeTokens(javaTok(c), func(S s) -> S { getOrKeep_tryUncurlied(map, s) })); c = join(mapCodeTokens(javaTokWithBrackets(c), func(S s) -> S { getOrKeep_tryUncurlied(map, s) })); //c = javaTokWithBrackets_recursiveTransform(func(S s) -> S { getOrKeep(map, s) }, c); interpretations.add(c); } ret interpretations; } S formatForUser(S s) { ret tok_dropCurlyBrackets(s); }*/ LPair rulesAsPairs() { ret map(rules, func(Rule r) -> Pair { pair(r.in, r.out) }); } L splitterRules() { ret (L) [Rule r : rules | r instanceof SimplifyWithRule && ((SimplifyWithRule) r).isSplitRule]; } void dropRulesWhere(IF1 pred) { rules = antiFilter(pred, rules); } }