1 | sclass NLLogicChecker_v2 {
|
2 | S input; |
3 | new L<S> facts; |
4 | new L<IfThen> rules; |
5 | StringMatcher matcher = ai_standardMatcher(); |
6 | new L<S> posted; |
7 | new ThreadLocal<IfThen> checkingRule; |
8 | VF2<O, Matching> onChecking; // first arg: IfThen or Exp |
9 | VF3<O, Matching, O> onChecked; // first arg: IfThen or Exp, last arg: Bool or Throwable |
10 | static bool staticVerbose; |
11 | bool useIterate, allowUnsafeEvals = true; |
12 | new HashMap<S, FuncChecker> funcCheckers; |
13 | new HashMap<S, FuncIterator> funcIterators; |
14 | S followingUpOn; |
15 | |
16 | List<Map> recentHistory; |
17 | L<S> entities; |
18 | F2<Exp, Matching, O> evaluator = func(Exp e, Matching m) {
|
19 | S code = e.text(); |
20 | if (!allowUnsafeEvals && !isSafeCodeFragment(code)) null; |
21 | ret nlLogic_evalExp(NLLogicChecker_v2.this, code, m); |
22 | }; |
23 | |
24 | abstract sclass FuncChecker {
|
25 | abstract bool check(Func e, Matching m); |
26 | } |
27 | |
28 | abstract sclass FuncIterator {
|
29 | abstract void iterate(Func e, Matching m, Runnable onMatch); |
30 | } |
31 | |
32 | sclass Matching {
|
33 | new VarMatches matches; // var -> string value |
34 | new L<Exp> output; |
35 | //L<RuleFailInfo> ruleFails; |
36 | bool verbose = staticVerbose; |
37 | |
38 | toString { ret sfu(matches) + "/" + sfu(output); }
|
39 | } |
40 | |
41 | bool checkRule(final IfThen rule, final Matching m) {
|
42 | if (rule == null) false; |
43 | ret logCheck(rule, m, func -> Bool { checkRule_impl(rule, m) });
|
44 | } |
45 | |
46 | bool checkRule_impl(IfThen rule, Matching m) {
|
47 | temp tempSetThreadLocal(checkingRule, rule); |
48 | if (!checkExpression(rule.in, m)) false; |
49 | m.output.add(apply(rule.out, m)); |
50 | true; |
51 | } |
52 | |
53 | // returns (success, remaining conditions) |
54 | Pair<Bool, Exp> checkFirstCondition(IfThen rule, Matching m) {
|
55 | temp tempSetThreadLocal(checkingRule, rule); |
56 | ret checkFirstCondition(rule.in, m); |
57 | } |
58 | |
59 | Pair<Bool, Exp> checkFirstCondition(Exp exp, Matching m) {
|
60 | Pair<Exp> p = nlLogic_extractFirstCondition(exp); |
61 | if (p == null) ret pair(true, null); |
62 | if (!checkExpression(p.a, m)) ret pair(false, null); |
63 | ret pair(true, p.b); |
64 | } |
65 | |
66 | IfThen apply(IfThen r, Matching m) {
|
67 | ret r == null ? null : IfThen(apply(r.in, m), apply(r.out, m)); |
68 | } |
69 | |
70 | Exp apply(Exp e, Matching m) {
|
71 | ret apply(e, m.matches); |
72 | } |
73 | |
74 | Exp apply(Exp e, SS m) {
|
75 | if (e == null) null; |
76 | if (e cast And) |
77 | ret And(apply(e.a, m), apply(e.b, m)); |
78 | if (e cast ExpNot) |
79 | ret ExpNot(apply(e.a, m)); |
80 | if (e cast Func) {
|
81 | if (contains(e.options, 'javaTokNoQuotes) && e.arg instanceof Sentence2) |
82 | ret Func(e.name, Sentence2(join(replaceVars_withDollarQ(javaTokNoQuotes(e.arg.text()), m)))); |
83 | else |
84 | ret Func(e.name, apply(e.arg, m)); |
85 | } |
86 | |
87 | if (e cast Sentence) |
88 | ret Sentence(javaTok(matcher.apply(e.text(), m))); |
89 | if (e cast Sentence2) |
90 | ret Sentence2(matcher.apply(e.text(), m)); |
91 | |
92 | ret e; |
93 | } |
94 | |
95 | <A> A logCheck(O o, Matching m, F0<A> f) {
|
96 | callF(onChecking, o, m); |
97 | try {
|
98 | A result = callF(f); |
99 | callF(onChecked, o, m, result); |
100 | ret result; |
101 | } catch e {
|
102 | callF(onChecked, o, m, e); |
103 | throw rethrow(e); |
104 | } |
105 | } |
106 | |
107 | final bool checkExpression(final Exp e, final Matching m) {
|
108 | ret logCheck(e, m, func -> Bool { checkExpression_impl(e, m) });
|
109 | } |
110 | |
111 | bool checkExpression_impl_1(Exp e, Matching m) {
|
112 | if (e cast And) |
113 | ret checkExpression(e.a, m) && checkExpression(e.b, m); |
114 | ret checkExpression_single(e, m); |
115 | } |
116 | |
117 | bool checkExpression_single(Exp e, Matching m) {
|
118 | if (e cast ExpNot) {
|
119 | temp tempBackupMatches(m); |
120 | ret !checkExpression(e.a, m); |
121 | } |
122 | if (e cast Func) {
|
123 | if (eq(e.name, 'input) && !cic(e.options, 'flexMatch)) {
|
124 | S pat = nlLogic_text(e.arg); |
125 | if (contains(e.options, 're)) // regular expression |
126 | ret regexpFindIC(pat, input); |
127 | |
128 | ret nlLogic_matchVBarPattern(this, pat, input, m); |
129 | } else if (eq(e.name, "fact")) {
|
130 | S pat = nlLogic_text(e.arg); |
131 | if (contains(e.options, "random")) |
132 | ret nlLogic_matchRandomFact(this, pat, m); |
133 | else |
134 | ret nlLogic_matchAnyFact(this, pat, m); |
135 | } else if (eq(e.name, "allFacts")) {
|
136 | S pat = nlLogic_text(e.arg); |
137 | L<VarMatches> l = nlLogic_matchAllFacts(this, pat, m); |
138 | for i over l: {
|
139 | SS vars = reverseGet(l, i); // facts are normally reversed, so re-reverse to get them in order |
140 | int n = i+1; |
141 | for (S var, value : vars) {
|
142 | if (!strictPutIC(m.matches, var + n, value)) false; |
143 | } |
144 | } |
145 | true; |
146 | } |
147 | } |
148 | false; |
149 | } |
150 | |
151 | AutoCloseable tempBackupMatches(final Matching m) {
|
152 | final VarMatches oldMatches = new (m.matches); |
153 | ret autocloseable { m.matches = oldMatches; };
|
154 | } |
155 | |
156 | O evalExp(Exp e, Matching m) {
|
157 | ret callF(evaluator, e, m); |
158 | } |
159 | |
160 | bool checkExpression_impl(Exp e, Matching m) {
|
161 | new Matches mm; |
162 | if (e cast Func) {
|
163 | // FUNCTION CONDITIONS |
164 | |
165 | FuncChecker checker = funcCheckers.get(e.name); |
166 | if (checker != null && checker.check(e, m)) true; |
167 | |
168 | if (eq(e.name, "singular")) |
169 | ret nlLogic_stringFunction(f singular, e, m.matches); |
170 | else if (eq(e.name, 'entity)) {
|
171 | if (entities == null) {
|
172 | long time = sysNow(); |
173 | fS switched = switcheroo(input); |
174 | print("Switched >> " + switched);
|
175 | entities = evalWithTimeoutOrNull(5000, func -> LS {
|
176 | mapMethod('text, ai_extractEntities_v1(switched))
|
177 | }); |
178 | print("Entities (" + elapsedMS(time) + " ms): " + joinWithComma(entities));
|
179 | if (entities == null) entities = new L; |
180 | } |
181 | for (S entity : entities) |
182 | if (matcher.match(nlLogic_text(e.arg), entity, m.matches)) true; |
183 | } else if (startsWith(e.name, "line", mm) && isInteger(mm.rest()) && checkingRule! != null) {
|
184 | int n = parseInt(mm.rest())-nlLogic_numberOfLinesReferenced(checkingRule->in); |
185 | S line = n == 0 ? input : getString(get(recentHistory, l(recentHistory)+n), 'text); |
186 | //print("Recent: " + recentHistory);
|
187 | S pat = e.arg.text(); |
188 | //print("n=" + n + ", Matching " + e + " with " + line);
|
189 | ret matcher.match(pat, line, m.matches); |
190 | } else if (eq(e.name, 'iSaid)) {
|
191 | S line = getString(last(recentHistory), 'text); |
192 | if (line == null) false; |
193 | ret matcher.match(e.arg.text(), line, m.matches); |
194 | } else if (eq(e.name, 'unknownIf)) {
|
195 | S statement = nlLogic_text(apply(e.arg, m)); |
196 | //print("Checking statement: " + statement);
|
197 | ret !cic(facts, statement) && !cic(facts, "Untrue: " + statement); |
198 | } else if (eq(e.name, 'inputContainsTokens)) |
199 | ret jcontains(input, nlLogic_text(e.arg)); |
200 | else if (eq(e.name, 'inputStartsWith)) |
201 | ret startsWith(input, nlLogic_text(e.arg)); |
202 | else if (eq(e.name, 'anyInput)) |
203 | ret nempty(input); |
204 | else if (eq(e.name, 'preciseInput)) |
205 | ret eq(nlLogic_text(apply(e.arg, m)), input); // Incomplete var support |
206 | else if (eq(e.name, 'followingUpOn)) {
|
207 | S text = nlLogic_text(e.arg); |
208 | if (eq(text, followingUpOn)) true; |
209 | |
210 | Map prevPrev = nextToLast(recentHistory); |
211 | S msgID = getString(prevPrev, 'globalID); |
212 | S input = getString(prevPrev, 'text); |
213 | |
214 | S ruleID = null, dollarInput = null; |
215 | |
216 | // e.g. "lhnshnhcklhabvmu with input=$input" |
217 | if (match("* with input=*", text, mm) && !isDollarVar(mm.get(1))) {
|
218 | ruleID = mm.unq(0); dollarInput = mm.get(1); |
219 | } else |
220 | ruleID = text; |
221 | |
222 | //print("followingUpOn: msgID=" + msgID + ", input=" + input);
|
223 | //printStruct(+prevPrev); |
224 | S pat = format("Rule * fired on message * ", ruleID, msgID);
|
225 | //print("pat=" + pat);
|
226 | for (S ruleFired : mL("Telegram Rule Fires"))
|
227 | if (swic(ruleFired, pat)) {
|
228 | if (dollarInput != null && !strictPutIC(m.matches, dollarInput, input)) false; |
229 | L<S> tok = javaTokWithBrackets2(ruleFired); |
230 | if (find3("with vars *", tok, mm)) {
|
231 | SS vars = safeUnstructMap(mm.get(0)); |
232 | if (!addMapToMapWithoutOverwritingIC(m.matches, vars)) false; |
233 | } |
234 | true; |
235 | } |
236 | false; |
237 | } else if (eq(e.name, 'debug)) |
238 | ret true with print("[debug] " + apply(e.arg, m));
|
239 | } |
240 | |
241 | if (e cast Eq) {
|
242 | // EQUATION CONDITIONS |
243 | |
244 | Exp r = e.right; |
245 | S var = nlLogic_text(e.left); |
246 | |
247 | if (endsWith(var, "!")) { // $x != bla
|
248 | temp tempBackupMatches(m); |
249 | ret !matcher.match(dropSuffixTrim("!", var), r.text(), m.matches);
|
250 | } |
251 | |
252 | if (r cast Func) pcall {
|
253 | if (eq(r.name, 'eval)) {
|
254 | O result = evalExp(r.arg, m); |
255 | if (result != null) { // Not allowing null results anymore
|
256 | LS vars = tok_splitAtComma(var); |
257 | |
258 | Bool x = ai_matchObjectWithMultipleVars(vars, result, m.matches); |
259 | if (x != null) ret x; |
260 | |
261 | S sResult = str(result); |
262 | StringMatcher _matcher = matcher; |
263 | if (isDollarVar(var)) _matcher = new NLStringMatcher_singleDollarVar; |
264 | else if (contains(var, ',')) { // TODO: use Pair instead - there is only like one rule using this
|
265 | if (allDollarVars(vars) |
266 | && ai_matchMulti(vars, tok_splitAtComma(sResult), m.matches)) true; |
267 | } |
268 | |
269 | ret _matcher.match(var, sResult, m.matches); |
270 | } |
271 | } |
272 | } |
273 | } |
274 | |
275 | if (e cast Sentence2) {
|
276 | // SENTENCE CONDITIONS |
277 | |
278 | S text = e.text(); |
279 | if (eq(text, 'true)) |
280 | true; |
281 | } |
282 | |
283 | ret checkExpression_impl_1(e, m); |
284 | } |
285 | |
286 | void iterate(final Exp e, final Matching m, final Runnable onMatch) {
|
287 | if (e cast And) {
|
288 | iterate(e.a, m, r { iterate(e.b, m, onMatch) });
|
289 | ret; |
290 | } |
291 | |
292 | if (e cast ExpNot) {
|
293 | if (m.verbose) print("iterate not: " + e);
|
294 | final new Flag flag; |
295 | withCancelPoint(voidfunc(final CancelPoint cp) {
|
296 | iterate(e.a, m, r {
|
297 | if (m.verbose) print("iterate not: raising flag");
|
298 | flag.raise(); |
299 | cancelTo(cp); |
300 | }); |
301 | }); |
302 | if (!flag.isUp()) |
303 | callF(onMatch); |
304 | ret; |
305 | } |
306 | |
307 | iterate_single(e, m, onMatch); |
308 | } |
309 | |
310 | void iterate_single(final Exp e, final Matching m, final Runnable onMatch) {
|
311 | if (e cast Func) {
|
312 | //print("Checking func iterators: " + e.name);
|
313 | FuncIterator it = funcIterators.get(e.name); |
314 | if (it != null) |
315 | it.iterate(e, m, onMatch); |
316 | |
317 | if (eq(e.name, 'fact)) {
|
318 | S pat = nlLogic_text(e.arg); |
319 | VarMatches oldMatches = new(m.matches); |
320 | for (S fact : facts) {
|
321 | if (m.verbose) |
322 | print("iterate matching: " + pat + " with " + fact + " - " + sfu(m.matches));
|
323 | for (S pat2 : tok_splitAtVerticalBar(pat)) |
324 | if (matcher.match(pat2, fact, m.matches)) {
|
325 | if (m.verbose) |
326 | print("iterate match: " + pat + " / " + fact + " - " + m);
|
327 | try { callF(onMatch); } finally { m.matches = new VarMatches(oldMatches); }
|
328 | } |
329 | } |
330 | ret; |
331 | } else if (eq(e.name, "input") && cic(e.options, 'flexMatch)) {
|
332 | S pat = nlLogic_text(e.arg); |
333 | if (m.verbose) |
334 | print("input flexMatch " + pat);
|
335 | |
336 | VarMatches oldMatches = new(m.matches); |
337 | nlLogic_flexMatch_iterate_vbar(pat, input, m, onMatch, oldMatches); |
338 | ret; |
339 | } |
340 | } |
341 | |
342 | if (e instanceof Sentence2 && contains(e.text(), " in ")) {
|
343 | L<S> tok = javaTokPlusAllThreeBrackets(e.text()); |
344 | S var, var2; |
345 | new Matches mm; |
346 | if (m.verbose) |
347 | print("iterate in eval tok: " + sfu(tok));
|
348 | |
349 | if (jmatch("* in eval *", tok, mm) && isDollarVar(var = mm.get(0)) && isRoundBracketed(mm.get(1))) {
|
350 | if (m.verbose) |
351 | print("iterate in eval: " + mm.get(1));
|
352 | Iterable l = cast evalExp(Sentence2(deRoundBracket(mm.get(1))), m); |
353 | VarMatches oldMatches = new(m.matches); |
354 | |
355 | for (O element : unnull(l)) {
|
356 | continue unless element != null; |
357 | S s = str(element); |
358 | if (m.verbose) print("iterate in eval got element: " + s);
|
359 | if (strictPutIC(m.matches, var, s)) |
360 | try { callF(onMatch); } finally { m.matches = new VarMatches(oldMatches); }
|
361 | } |
362 | } |
363 | |
364 | if (jmatch("*, * in eval *", tok, mm) && isDollarVar(var = mm.get(0)) && isDollarVar(var2 = mm.get(1)) && isRoundBracketed(mm.get(2))) {
|
365 | LS vars = ll(var, var2); |
366 | Iterable l = cast evalExp(Sentence2(deRoundBracket(mm.get(2))), m); |
367 | VarMatches oldMatches = new(m.matches); |
368 | |
369 | for (O element : unnull(l)) {
|
370 | continue unless element != null; |
371 | if (m.verbose) print("iterate in eval got element: " + element);
|
372 | if (isTrue(ai_matchObjectWithMultipleVars(vars, element, m.matches))) |
373 | try { callF(onMatch); } finally { m.matches = new VarMatches(oldMatches); }
|
374 | } |
375 | } |
376 | } |
377 | |
378 | temp tempBackupMatches(m); |
379 | if (m.verbose) |
380 | print("iterate fallback: " + sfu(e));
|
381 | if (checkExpression(e, m)) {
|
382 | if (m.verbose) |
383 | print("iterate fallback match: " + e);
|
384 | callF(onMatch); |
385 | } |
386 | } |
387 | |
388 | bool checkHelper(Exp e, Matching m) {
|
389 | ret nlLogic_checkHelper(e, m.matches); |
390 | } |
391 | |
392 | *() {
|
393 | initCheckers(); |
394 | initIterators(); |
395 | } |
396 | |
397 | void initCheckers {
|
398 | mapPutMultipleKeys(funcCheckers, new FuncChecker {
|
399 | bool check(Func e, Matching m) {
|
400 | ret nlLogic_stringFunction(f ai_verbPhraseFromThirdPerson, e, m.matches); |
401 | } |
402 | }, 'verbPhraseFromThirdPerson, 'verbFromThirdPerson); |
403 | |
404 | funcCheckers.put('eval, new FuncChecker {
|
405 | bool check(Func e, Matching m) {
|
406 | ret eq("true", str(evalExp(e.arg, m)));
|
407 | } |
408 | }); |
409 | } |
410 | |
411 | void initIterators {
|
412 | funcIterators.put('phrase, new FuncIterator {
|
413 | void iterate(Func e, Matching m, Runnable onMatch) {
|
414 | //if (!contains(e.options, 'words)) ret; |
415 | S pat = e.argText(); |
416 | LS patTok = standardTok(pat); |
417 | LS inputTok = standardTok(input); |
418 | |
419 | // optimizable |
420 | for (LS inputPart : tok_marchPatternThroughInput(patTok, inputTok)) |
421 | if (matcher.match(pat, join(inputPart), m.matches)) |
422 | callF(onMatch); |
423 | } |
424 | }); |
425 | } |
426 | |
427 | // TODO: cache |
428 | LS standardTok(S s) {
|
429 | ret javaTokWithAllBrackets(s); |
430 | } |
431 | } |
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: | 948 / 1716 |
| Version history: | 125 change(s) |
| Referenced in: | [show references] |