sclass NLLogicChecker_v2 {
S input;
new L<S> facts;
new L<IfThen> rules;
StringMatcher matcher = ai_standardMatcher();
new L<S> posted;
new ThreadLocal<IfThen> checkingRule;
VF2<O, Matching> onChecking; // first arg: IfThen or Exp
VF3<O, Matching, O> onChecked; // first arg: IfThen or Exp, last arg: Bool or Throwable
static bool staticVerbose;
bool useIterate, allowUnsafeEvals = true;
new HashMap<S, FuncChecker> funcCheckers;
new HashMap<S, FuncIterator> funcIterators;
S followingUpOn;
List<Map> recentHistory;
L<S> entities;
F2<Exp, Matching, O> evaluator = func(Exp e, Matching m) {
S code = e.text();
if (!allowUnsafeEvals && !isSafeCodeFragment(code)) null;
ret nlLogic_evalExp(NLLogicChecker_v2.this, code, m);
};
abstract sclass FuncChecker {
abstract bool check(Func e, Matching m);
}
abstract sclass FuncIterator {
abstract void iterate(Func e, Matching m, Runnable onMatch);
}
sclass Matching {
new VarMatches matches; // var -> string value
new L<Exp> output;
//L<RuleFailInfo> ruleFails;
bool verbose = staticVerbose;
toString { ret sfu(matches) + "/" + sfu(output); }
}
bool checkRule(final IfThen rule, final Matching m) {
if (rule == null) false;
ret logCheck(rule, m, func -> Bool { checkRule_impl(rule, m) });
}
bool checkRule_impl(IfThen rule, Matching m) {
temp tempSetThreadLocal(checkingRule, rule);
if (!checkExpression(rule.in, m)) false;
m.output.add(apply(rule.out, m));
true;
}
// returns (success, remaining conditions)
Pair<Bool, Exp> checkFirstCondition(IfThen rule, Matching m) {
temp tempSetThreadLocal(checkingRule, rule);
ret checkFirstCondition(rule.in, m);
}
Pair<Bool, Exp> checkFirstCondition(Exp exp, Matching m) {
Pair<Exp> p = nlLogic_extractFirstCondition(exp);
if (p == null) ret pair(true, null);
if (!checkExpression(p.a, m)) ret pair(false, null);
ret pair(true, p.b);
}
IfThen apply(IfThen r, Matching m) {
ret r == null ? null : IfThen(apply(r.in, m), apply(r.out, m));
}
Exp apply(Exp e, Matching m) {
ret apply(e, m.matches);
}
Exp apply(Exp e, SS m) {
if (e == null) null;
if (e cast And)
ret And(apply(e.a, m), apply(e.b, m));
if (e cast ExpNot)
ret ExpNot(apply(e.a, m));
if (e cast Func) {
if (contains(e.options, 'javaTokNoQuotes) && e.arg instanceof Sentence2)
ret Func(e.name, Sentence2(join(replaceVars_withDollarQ(javaTokNoQuotes(e.arg.text()), m))));
else
ret Func(e.name, apply(e.arg, m));
}
if (e cast Sentence)
ret Sentence(javaTok(matcher.apply(e.text(), m)));
if (e cast Sentence2)
ret Sentence2(matcher.apply(e.text(), m));
ret e;
}
<A> A logCheck(O o, Matching m, F0<A> f) {
callF(onChecking, o, m);
try {
A result = callF(f);
callF(onChecked, o, m, result);
ret result;
} catch e {
callF(onChecked, o, m, e);
throw rethrow(e);
}
}
final bool checkExpression(final Exp e, final Matching m) {
ret logCheck(e, m, func -> Bool { checkExpression_impl(e, m) });
}
bool checkExpression_impl_1(Exp e, Matching m) {
if (e cast And)
ret checkExpression(e.a, m) && checkExpression(e.b, m);
ret checkExpression_single(e, m);
}
bool checkExpression_single(Exp e, Matching m) {
if (e cast ExpNot) {
temp tempBackupMatches(m);
ret !checkExpression(e.a, m);
}
if (e cast Func) {
if (eq(e.name, 'input) && !cic(e.options, 'flexMatch)) {
S pat = nlLogic_text(e.arg);
if (contains(e.options, 're)) // regular expression
ret regexpFindIC(pat, input);
ret nlLogic_matchVBarPattern(this, pat, input, m);
} else if (eq(e.name, "fact")) {
S pat = nlLogic_text(e.arg);
if (contains(e.options, "random"))
ret nlLogic_matchRandomFact(this, pat, m);
else
ret nlLogic_matchAnyFact(this, pat, m);
} else if (eq(e.name, "allFacts")) {
S pat = nlLogic_text(e.arg);
L<VarMatches> l = nlLogic_matchAllFacts(this, pat, m);
for i over l: {
SS vars = reverseGet(l, i); // facts are normally reversed, so re-reverse to get them in order
int n = i+1;
for (S var, value : vars) {
if (!strictPutIC(m.matches, var + n, value)) false;
}
}
true;
}
}
false;
}
AutoCloseable tempBackupMatches(final Matching m) {
final VarMatches oldMatches = new (m.matches);
ret autocloseable { m.matches = oldMatches; };
}
O evalExp(Exp e, Matching m) {
ret callF(evaluator, e, m);
}
bool checkExpression_impl(Exp e, Matching m) {
new Matches mm;
if (e cast Func) {
// FUNCTION CONDITIONS
FuncChecker checker = funcCheckers.get(e.name);
if (checker != null && checker.check(e, m)) true;
if (eq(e.name, "singular"))
ret nlLogic_stringFunction(f singular, e, m.matches);
else if (eq(e.name, 'entity)) {
if (entities == null) {
long time = sysNow();
fS switched = switcheroo(input);
print("Switched >> " + switched);
entities = evalWithTimeoutOrNull(5000, func -> LS {
mapMethod('text, ai_extractEntities_v1(switched))
});
print("Entities (" + elapsedMS(time) + " ms): " + joinWithComma(entities));
if (entities == null) entities = new L;
}
for (S entity : entities)
if (matcher.match(nlLogic_text(e.arg), entity, m.matches)) true;
} else if (startsWith(e.name, "line", mm) && isInteger(mm.rest()) && checkingRule! != null) {
int n = parseInt(mm.rest())-nlLogic_numberOfLinesReferenced(checkingRule->in);
S line = n == 0 ? input : getString(get(recentHistory, l(recentHistory)+n), 'text);
//print("Recent: " + recentHistory);
S pat = e.arg.text();
//print("n=" + n + ", Matching " + e + " with " + line);
ret matcher.match(pat, line, m.matches);
} else if (eq(e.name, 'iSaid)) {
S line = getString(last(recentHistory), 'text);
if (line == null) false;
ret matcher.match(e.arg.text(), line, m.matches);
} else if (eq(e.name, 'unknownIf)) {
S statement = nlLogic_text(apply(e.arg, m));
//print("Checking statement: " + statement);
ret !cic(facts, statement) && !cic(facts, "Untrue: " + statement);
} else if (eq(e.name, 'inputContainsTokens))
ret jcontains(input, nlLogic_text(e.arg));
else if (eq(e.name, 'inputStartsWith))
ret startsWith(input, nlLogic_text(e.arg));
else if (eq(e.name, 'anyInput))
ret nempty(input);
else if (eq(e.name, 'preciseInput))
ret eq(nlLogic_text(apply(e.arg, m)), input); // Incomplete var support
else if (eq(e.name, 'followingUpOn)) {
S text = nlLogic_text(e.arg);
if (eq(text, followingUpOn)) true;
Map prevPrev = nextToLast(recentHistory);
S msgID = getString(prevPrev, 'globalID);
S input = getString(prevPrev, 'text);
S ruleID = null, dollarInput = null;
// e.g. "lhnshnhcklhabvmu with input=$input"
if (match("* with input=*", text, mm) && !isDollarVar(mm.get(1))) {
ruleID = mm.unq(0); dollarInput = mm.get(1);
} else
ruleID = text;
//print("followingUpOn: msgID=" + msgID + ", input=" + input);
//printStruct(+prevPrev);
S pat = format("Rule * fired on message * ", ruleID, msgID);
//print("pat=" + pat);
for (S ruleFired : mL("Telegram Rule Fires"))
if (swic(ruleFired, pat)) {
if (dollarInput != null && !strictPutIC(m.matches, dollarInput, input)) false;
L<S> tok = javaTokWithBrackets2(ruleFired);
if (find3("with vars *", tok, mm)) {
SS vars = safeUnstructMap(mm.get(0));
if (!addMapToMapWithoutOverwritingIC(m.matches, vars)) false;
}
true;
}
false;
} else if (eq(e.name, 'debug))
ret true with print("[debug] " + apply(e.arg, m));
}
if (e cast Eq) {
// EQUATION CONDITIONS
Exp r = e.right;
S var = nlLogic_text(e.left);
if (endsWith(var, "!")) { // $x != bla
temp tempBackupMatches(m);
ret !matcher.match(dropSuffixTrim("!", var), r.text(), m.matches);
}
if (r cast Func) pcall {
if (eq(r.name, 'eval)) {
O result = evalExp(r.arg, m);
if (result != null) { // Not allowing null results anymore
LS vars = tok_splitAtComma(var);
Bool x = ai_matchObjectWithMultipleVars(vars, result, m.matches);
if (x != null) ret x;
S sResult = str(result);
StringMatcher _matcher = matcher;
if (isDollarVar(var)) _matcher = new NLStringMatcher_singleDollarVar;
else if (contains(var, ',')) { // TODO: use Pair instead - there is only like one rule using this
if (allDollarVars(vars)
&& ai_matchMulti(vars, tok_splitAtComma(sResult), m.matches)) true;
}
ret _matcher.match(var, sResult, m.matches);
}
}
}
}
if (e cast Sentence2) {
// SENTENCE CONDITIONS
S text = e.text();
if (eq(text, 'true))
true;
}
ret checkExpression_impl_1(e, m);
}
void iterate(final Exp e, final Matching m, final Runnable onMatch) {
if (e cast And) {
iterate(e.a, m, r { iterate(e.b, m, onMatch) });
ret;
}
if (e cast ExpNot) {
if (m.verbose) print("iterate not: " + e);
final new Flag flag;
withCancelPoint(voidfunc(final CancelPoint cp) {
iterate(e.a, m, r {
if (m.verbose) print("iterate not: raising flag");
flag.raise();
cancelTo(cp);
});
});
if (!flag.isUp())
callF(onMatch);
ret;
}
iterate_single(e, m, onMatch);
}
void iterate_single(final Exp e, final Matching m, final Runnable onMatch) {
if (e cast Func) {
//print("Checking func iterators: " + e.name);
FuncIterator it = funcIterators.get(e.name);
if (it != null)
it.iterate(e, m, onMatch);
if (eq(e.name, 'fact)) {
S pat = nlLogic_text(e.arg);
VarMatches oldMatches = new(m.matches);
for (S fact : facts) {
if (m.verbose)
print("iterate matching: " + pat + " with " + fact + " - " + sfu(m.matches));
for (S pat2 : tok_splitAtVerticalBar(pat))
if (matcher.match(pat2, fact, m.matches)) {
if (m.verbose)
print("iterate match: " + pat + " / " + fact + " - " + m);
try { callF(onMatch); } finally { m.matches = new VarMatches(oldMatches); }
}
}
ret;
} else if (eq(e.name, "input") && cic(e.options, 'flexMatch)) {
S pat = nlLogic_text(e.arg);
if (m.verbose)
print("input flexMatch " + pat);
VarMatches oldMatches = new(m.matches);
nlLogic_flexMatch_iterate_vbar(pat, input, m, onMatch, oldMatches);
ret;
}
}
if (e instanceof Sentence2 && contains(e.text(), " in ")) {
L<S> tok = javaTokPlusAllThreeBrackets(e.text());
S var, var2;
new Matches mm;
if (m.verbose)
print("iterate in eval tok: " + sfu(tok));
if (jmatch("* in eval *", tok, mm) && isDollarVar(var = mm.get(0)) && isRoundBracketed(mm.get(1))) {
if (m.verbose)
print("iterate in eval: " + mm.get(1));
Iterable l = cast evalExp(Sentence2(deRoundBracket(mm.get(1))), m);
VarMatches oldMatches = new(m.matches);
for (O element : unnull(l)) {
continue unless element != null;
S s = str(element);
if (m.verbose) print("iterate in eval got element: " + s);
if (strictPutIC(m.matches, var, s))
try { callF(onMatch); } finally { m.matches = new VarMatches(oldMatches); }
}
}
if (jmatch("*, * in eval *", tok, mm) && isDollarVar(var = mm.get(0)) && isDollarVar(var2 = mm.get(1)) && isRoundBracketed(mm.get(2))) {
LS vars = ll(var, var2);
Iterable l = cast evalExp(Sentence2(deRoundBracket(mm.get(2))), m);
VarMatches oldMatches = new(m.matches);
for (O element : unnull(l)) {
continue unless element != null;
if (m.verbose) print("iterate in eval got element: " + element);
if (isTrue(ai_matchObjectWithMultipleVars(vars, element, m.matches)))
try { callF(onMatch); } finally { m.matches = new VarMatches(oldMatches); }
}
}
}
temp tempBackupMatches(m);
if (m.verbose)
print("iterate fallback: " + sfu(e));
if (checkExpression(e, m)) {
if (m.verbose)
print("iterate fallback match: " + e);
callF(onMatch);
}
}
bool checkHelper(Exp e, Matching m) {
ret nlLogic_checkHelper(e, m.matches);
}
*() {
initCheckers();
initIterators();
}
void initCheckers {
mapPutMultipleKeys(funcCheckers, new FuncChecker {
bool check(Func e, Matching m) {
ret nlLogic_stringFunction(f ai_verbPhraseFromThirdPerson, e, m.matches);
}
}, 'verbPhraseFromThirdPerson, 'verbFromThirdPerson);
funcCheckers.put('eval, new FuncChecker {
bool check(Func e, Matching m) {
ret eq("true", str(evalExp(e.arg, m)));
}
});
}
void initIterators {
funcIterators.put('phrase, new FuncIterator {
void iterate(Func e, Matching m, Runnable onMatch) {
//if (!contains(e.options, 'words)) ret;
S pat = e.argText();
LS patTok = standardTok(pat);
LS inputTok = standardTok(input);
// optimizable
for (LS inputPart : tok_marchPatternThroughInput(patTok, inputTok))
if (matcher.match(pat, join(inputPart), m.matches))
callF(onMatch);
}
});
}
// TODO: cache
LS standardTok(S s) {
ret javaTokWithAllBrackets(s);
}
}Began life as a copy of #1017580
download show line numbers debug dex old transpilations
Travelled to 14 computer(s): aoiabmzegqzx, bhatertpkbcr, cbybwowwnfue, cfunsshuasjs, gwrvuhgaqvyk, irmadwmeruwu, ishqpsrjomds, lpdgvwnxivlt, mqqgnosmbjvj, pyentgdyhuwx, pzhvpgtvlbxg, tslmcundralx, tvejysmllsmz, vouqrxazstgt
No comments. add comment
| Snippet ID: | #1017587 |
| Snippet name: | NLLogicChecker_v2 |
| Eternal ID of this version: | #1017587/126 |
| Text MD5: | 8c896eea64cfd6f47c89910275787351 |
| Author: | stefan |
| Category: | javax / a.i. |
| Type: | JavaX fragment (include) |
| Public (visible to everyone): | Yes |
| Archived (hidden from active list): | No |
| Created/modified: | 2018-12-04 17:06:30 |
| Source code size: | 14832 bytes / 431 lines |
| Pitched / IR pitched: | No / No |
| Views / Downloads: | 1231 / 1980 |
| Version history: | 125 change(s) |
| Referenced in: | #1017728 - NLLogicChecker_v3 - now same as v2 #1034167 - Standard Classes + Interfaces (LIVE, continuation of #1003674) |