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: | 735 / 1488 |
Version history: | 125 change(s) |
Referenced in: | [show references] |