Libraryless. Click here for Pure Java version (9584L/64K).
1 | sclass PhilosophyBot1 { |
2 | static transformable record LogicRule(lhs, rhs) { |
3 | int n; // when added |
4 | O trail; |
5 | |
6 | toString { |
7 | ret (n == 0 ? "" : "[" + n + "] ") + lhs + " => " + rhs; |
8 | } |
9 | } |
10 | static transformable record And(a, b) {} |
11 | static transformable record If(condition, thenBlock, elseBlock) {} |
12 | static transformable record For(var, condition, body) {} // don't need var actually |
13 | static transformable record ForIn(var, expr, body) {} |
14 | static transformable record Assignment(var, expr) {} |
15 | static transformable record While(condition, body) {} |
16 | |
17 | replace NPRet with O. // native predicate return type (Bool/SS/null) |
18 | |
19 | // like a native predicate, but doesn't return anything |
20 | sclass CodeFragment { |
21 | S head; |
22 | IVF2<SS, Env> body; |
23 | bool keepBrackets; |
24 | |
25 | *(S *head, IVF2<SS, Env> *body, bool *keepBrackets) {} |
26 | *(S *head, IVF2<SS, Env> *body) {} |
27 | |
28 | toString { ret stdToString(this); } |
29 | } |
30 | |
31 | sclass Env { |
32 | bool wantAlternatives; |
33 | |
34 | bool wantAlternatives() { ret wantAlternatives; } |
35 | } |
36 | |
37 | srecord WithAlternative(IF0<O> alternative, O result) {} |
38 | |
39 | // body takes variable mapping |
40 | // body can return |
41 | // Bool => immediate result (ok or fail) |
42 | // SS => variable mapping |
43 | // WithAlternative |
44 | // Iterator<NPRet> |
45 | // null => not applicable |
46 | // For "for $x in ..." statements, return a Iterable<stringable>? |
47 | srecord NativePredicate(S head, IF2<SS, Env, NPRet> body) {} |
48 | |
49 | replace ProcedureToRun with Proc. |
50 | replace Proc with L. // procedures are a list of statements |
51 | |
52 | // trail of a transformed fact |
53 | srecord TTransformed(S fact, IF1<S> transformer) {} |
54 | |
55 | transient S program; |
56 | transient bool programLoaded; |
57 | transient int maxRounds = 1000; |
58 | transient int maxFacts = 1000; |
59 | transient Set<S> facts = syncLinkedCISet(); |
60 | transient Set<S> originalFacts = ciSet(); |
61 | transient new LinkedHashSet<LogicRule> logicRules; // modified with sync |
62 | transient new AllOnAll<LogicRule, S> rulesOnFacts; |
63 | transient new AllOnAll<CodeFragment, S> codeOnFacts; |
64 | transient new AllOnAllWithUpdates<IVF1<S>, S> anyCodeOnFacts; |
65 | transient new AllOnAll/*WithUpdates*/<IVF1<LogicRule>, LogicRule> anyCodeOnRules; |
66 | transient new L<ProcedureToRun> proceduresToRun; |
67 | // parsed procedures |
68 | transient long proceduresExecuted; |
69 | transient new L<NativePredicate> nativePredicates; |
70 | transient bool debugNativeCalls = true, debugAllCmds = true; |
71 | transient bool verbose = true; |
72 | transient bool printStackTraces; |
73 | transient new L onProcedureEnded; |
74 | transient new L<IVF1<LogicRule>> onLogicRuleAdded; |
75 | transient new L<IVF1<S>> onFactAdded; |
76 | transient bool printNonMatches, debugContradictionChecks; |
77 | transient bool standardImportsLoaded; |
78 | transient bool curryAllRules = true; // rewrite "a & b => c" to a => b => c |
79 | transient int logicRulesCounter; |
80 | transient bool thoughtBegun; |
81 | transient bool printFactsAfterThinking = true; |
82 | transient bool noteSimplificationsAsFacts; // not a good idea - leads to endless loops |
83 | transient bool parsing; |
84 | |
85 | // return true when you handled adding the rule |
86 | transient new L<IPred<LogicRule>> logicRulePreprocessors; |
87 | |
88 | // return true when you handled adding the fact |
89 | transient new L<IPred<S>> factPreprocessors; |
90 | |
91 | // extra stuff that is done every round |
92 | transient new L<Runnable> extraLogicOperations; |
93 | |
94 | transient Set<S> vars = litciset("x", "y", "z"); |
95 | |
96 | // phrase replacements that are made in every fact. |
97 | // functionality is enabled by philosophyBot1_addDeepReplacement |
98 | transient SS deepReplacements; |
99 | |
100 | transient L<IF1<S>> deepTransformers; |
101 | transient L<IF1<S, WithTrail<S>>> trailEnabledDeepTransformers; |
102 | |
103 | // fact -> trail. to enable, initialize with ciMap() |
104 | transient MapSO trails; |
105 | |
106 | Map extensions; |
107 | |
108 | *() { |
109 | // find contradictions |
110 | anyCodeOnFacts.newA(fact -> { |
111 | Bool b = checkNativeCondition(fact); |
112 | if (debugContradictionChecks) |
113 | print("Fact check: " + b + " - " + fact); |
114 | if (isFalse(b)) |
115 | addFactWithTrail("contradiction", trails == null ? null : fact); |
116 | }); |
117 | |
118 | _standardHandlers(); |
119 | } |
120 | *(S *program) { this(); } |
121 | |
122 | void addRule(S s) { |
123 | PairS p = splitAtFirstDoubleArrow(javaTokWithBrackets(s)); |
124 | if (p == null) if (verbose) print("addRule failed: " + s); |
125 | addLogicRuleWithTrail(new LogicRule(splitAtAmpersand2(p.a), p.b), |
126 | litorderedmap(op := "addRule", +s)); |
127 | } |
128 | |
129 | void addRules(Iterable<S> l) { |
130 | fOr (S s : l) addRule(s); |
131 | } |
132 | |
133 | O splitRuleRHS(O rhs) { |
134 | if (rhs cast S) { |
135 | PairS p = splitAtFirstDoubleArrow(javaTokWithBrackets(rhs)); |
136 | if (p != null) ret splitRuleRHS(new LogicRule(splitAtAmpersand2(p.a), splitRuleRHS(p.b))); |
137 | |
138 | ret splitAtAmpersand2(rhs); |
139 | } |
140 | ret rhs; |
141 | } |
142 | |
143 | LogicRule curryLHS(LogicRule rule) { |
144 | while licensed { |
145 | O lhs = rule.lhs; |
146 | if lhs is And(O a, O b) { |
147 | LogicRule r = rule; |
148 | rule = new LogicRule(a, new LogicRule(b, rule.rhs)); |
149 | rule.trail = r.trail; |
150 | } |
151 | else break; |
152 | } |
153 | ret rule; |
154 | } |
155 | |
156 | void addLogicRuleWithTrail(LogicRule rule, O trail) { |
157 | rule.trail = trail; |
158 | addLogicRule(rule); |
159 | } |
160 | |
161 | void addLogicRule(LogicRule rule, S originalText) { |
162 | //print("originalText=" + originalText + " for " + rule); |
163 | addLogicRuleWithTrail(rule, originalText); |
164 | } |
165 | |
166 | void addLogicRule(LogicRule rule) { |
167 | rule.rhs = splitRuleRHS(rule.rhs); |
168 | |
169 | if (curryAllRules) |
170 | rule = curryLHS(rule); |
171 | |
172 | for (IPred<LogicRule> p : logicRulePreprocessors) |
173 | if (p.get(rule)) ret; // has been preprocessed |
174 | |
175 | rule.n = ++logicRulesCounter; |
176 | |
177 | if (printStackTraces) printStackTrace(str(rule)); |
178 | if (trails != null && rule.trail == null) |
179 | if (parsing) |
180 | rule.trail = "initial rule"; |
181 | else |
182 | fail("No trail for rule: " + rule); |
183 | |
184 | // is LHS a native predicate? then eval immediately |
185 | // TODO: multiple conditions |
186 | Bool b = checkConditionOpt(rule.lhs); |
187 | if (isFalse(b)) ret; // drop rule |
188 | if (isTrue(b)) |
189 | addRewrittenRHS(rule.rhs, rule); |
190 | |
191 | if (syncAdd(logicRules, rule)) { |
192 | if (verbose) print("Got logic rule", rule); |
193 | pcallFAll(onLogicRuleAdded, rule); |
194 | anyCodeOnRules.newB(rule); |
195 | rulesOnFacts.newA(rule); // to combine it with the facts |
196 | } |
197 | } |
198 | |
199 | void addFacts(Iterable<S> l) { |
200 | fOr (S fact : l) addFact(fact); |
201 | } |
202 | |
203 | void addFactWithTrail(WithTrail<S> fact) { |
204 | if (fact != null) addFactWithTrail(fact!, fact.trail); |
205 | } |
206 | |
207 | void addFactWithTrail(S fact, O trail) { |
208 | S cleaned = addFact(fact); |
209 | setTrail(cleaned, trail); |
210 | } |
211 | |
212 | void setTrail(S fact, O trail) { |
213 | mapPut(trails, fact, trail); |
214 | } |
215 | |
216 | // return cleaned version |
217 | S addFact(S fact) { |
218 | ping(); |
219 | fact = trim(fact); |
220 | if (empty(fact)) null; |
221 | |
222 | if (l(facts) >= maxFacts) null; |
223 | |
224 | fact = tok_deRoundBracket(fact); |
225 | |
226 | if (printStackTraces) printStackTrace("Adding fact: " + fact); |
227 | |
228 | if (parsing && getTrail(fact) == null) setTrail(fact, "initial fact"); |
229 | |
230 | for (IPred<S> p : factPreprocessors) |
231 | if (p.get(fact)) null; // has been preprocessed |
232 | |
233 | // Check if it's a procedure |
234 | LS tok = mainTokenize(fact); |
235 | if (countCodeTokens(tok) == 2 && firstTokenEqic(tok, "proc") |
236 | && isCurlyBracketed(getCodeToken(tok, 1))) pcall { |
237 | // It's a procedure! |
238 | S proc = uncurly_keepSpaces(getCodeToken(tok, 1)); |
239 | if (proceduresToRun.add(parseProcedure(proc))) { |
240 | if (verbose) print("Got procedure:"); |
241 | if (verbose) print(indentx("> ", proc)); |
242 | } |
243 | } /*else if (countCodeTokens(tok) == 2 && firstTokenEqic(tok, "java") |
244 | && isCurlyBracketed(getCodeToken(tok, 1))) pcall { |
245 | // It's Java code |
246 | |
247 | }*/ else // It's a fact, not a procedure |
248 | if (facts.add(fact)) { |
249 | if (verbose) print("Got fact: " + fact); |
250 | pcallFAll(onFactAdded, fact); |
251 | rulesOnFacts.newB(fact); // to combine it with the rules |
252 | codeOnFacts.newB(fact); |
253 | anyCodeOnFacts.newB(fact); |
254 | } |
255 | |
256 | ret fact; |
257 | } |
258 | |
259 | void addRewrittenRHS(O o, O trail) { |
260 | if (o cast LogicRule) |
261 | addLogicRuleWithTrail(o, trail); |
262 | else if o is And(O a, O b) { |
263 | addRewrittenRHS(a, o); |
264 | addRewrittenRHS(b, o); |
265 | } else if (o != null) |
266 | addFactWithTrail((S) o, trail); |
267 | } |
268 | |
269 | void addFactFromProgram(S fact) { |
270 | if (countJavaTokens(fact) == 0) ret; |
271 | fact = javaDropAllComments(fact); |
272 | originalFacts.add(fact); |
273 | addFact(fact); |
274 | } |
275 | |
276 | void runProcedure(S proc) pcall { |
277 | if (verbose) print("Running procedure."); |
278 | runParsedProcedure(parseProcedure(proc)); |
279 | } |
280 | |
281 | void runParsedProcedure(Proc commands) { |
282 | runParsedProcedure(commands, proceduresToRun); |
283 | } |
284 | |
285 | void runParsedProcedure(Proc commands, L<Proc> whereToPostCode) { |
286 | ++proceduresExecuted; |
287 | new Env env; |
288 | L remainingCommands = cloneLinkedList(commands); |
289 | O cmd; |
290 | while not null (cmd = popFirst_ping(remainingCommands)) { |
291 | if (cmd cast L) continue with runParsedProcedure(cmd); |
292 | if (cmd cast Runnable) continue with cmd.run(); |
293 | if (debugAllCmds) |
294 | print("Running cmd: " + sfu(cmd)); |
295 | if cmd is If(O condition, O thenBlock, O elseBlock) { |
296 | O blockToRun = checkCondition(condition) ? thenBlock : elseBlock; |
297 | runParsedProcedure(ll(blockToRun)); |
298 | } else if cmd is For(O var, O condition, O body) { |
299 | // make a new logic rule and add it |
300 | // assume the variable is globally declared as a variable |
301 | addLogicRuleWithTrail(new LogicRule(condition, "proc {\n" + body + "\n}"), litorderedmap(op := "for")); |
302 | } else if cmd is While(O condition, O body) { |
303 | bool b = checkCondition(condition); |
304 | if (!b) continue; |
305 | whereToPostCode.add(ll(body, cmd)); |
306 | } else if cmd is Assignment(S var, S expr) { |
307 | S result = cast runNativePredicate(expr, new Env); |
308 | SS mapping = litcimap(var, result); |
309 | remainingCommands = mapToLinkedList(remainingCommands, |
310 | c -> replaceVars(c, mapValues optRound(mapping))); |
311 | } else if cmd is ForIn(S var, S expr, O body) { |
312 | // XXX |
313 | O result = runNativePredicate(expr, new Env); |
314 | if (!result instanceof Iterable) { |
315 | if (verbose) print("Warning: result of " + expr + " not iterable (" + shortClassName(result) + ")"); |
316 | continue; |
317 | } |
318 | Iterator it = iterator((Iterable) result); |
319 | Runnable step = r { |
320 | if (!it.hasNext()) ret; |
321 | S value = str(it.next()); |
322 | SS map = litcimap(var, value); |
323 | O body2 = replaceVars(body, map); |
324 | //print("ForIn: " + map + " => " + body2); |
325 | whereToPostCode.add(ll(body2, this)); |
326 | }; |
327 | step.run(); |
328 | } else if (cmd cast S) { |
329 | O result = runNativePredicate(cmd, env); |
330 | if (result != null) { |
331 | result = unpackWithAlternativeOrIterator(result); |
332 | if (isFalse(result)) ret; |
333 | if (isTrueOpt(result)) continue; |
334 | if (result cast S) |
335 | continue with addFact(result); |
336 | SS mapping = cast result; // assume it's a variable mapping |
337 | // apply to all remaining commands and continue |
338 | L remainingCommands2 = mapToLinkedList(remainingCommands, |
339 | c -> replaceVars(c, mapValues optRound(mapping))); |
340 | if (verbose) print("Applying var mapping " + mapping + " to " + remainingCommands |
341 | + " => " + remainingCommands2); |
342 | remainingCommands = remainingCommands2; |
343 | } else |
344 | addFact(cmd); |
345 | } else if (cmd != null) |
346 | fail("Unimplemented command: " + cmd); |
347 | } |
348 | pcallFAll(onProcedureEnded, commands); // notify listeners |
349 | } |
350 | |
351 | // return var mapping (SS), Bool or null for no matching predicate |
352 | // or result verbatim (e.g. Iterable) |
353 | O runNativePredicate(S s, Env env) { |
354 | for (NativePredicate np : nativePredicates) { |
355 | SS map = zipIt(np.head, s); |
356 | if (map != null) { |
357 | try { |
358 | O result = np.body.get(mapValues tok_deRoundBracket(map), env); |
359 | if (debugNativeCalls) |
360 | print("Native predicate result: " + np.head + " => " + result); |
361 | if (result instanceof Map && nempty(map)) { |
362 | result = mapKeys((SS) result, var -> lookupOrKeep(map, var)); |
363 | if (debugNativeCalls) |
364 | print("Rewrote native predicate result: " + result); |
365 | } |
366 | try object result; |
367 | } catch e { handleException(e); } |
368 | } else |
369 | if (printNonMatches) |
370 | print("Non-match: " + quote(np.head) + " / " + quote(s)); |
371 | } |
372 | null; |
373 | } |
374 | |
375 | // returns false if unknown |
376 | bool checkCondition(O o) { |
377 | ret isTrue(checkConditionOpt(o)); |
378 | } |
379 | |
380 | // returns null if unknown |
381 | Bool checkConditionOpt(O o) { |
382 | if (o cast S) { |
383 | if (contains(facts, o)) true; |
384 | try object Bool b = checkNativeCondition(o); |
385 | } |
386 | //print("Ignoring condition: " + o); |
387 | null; |
388 | } |
389 | |
390 | Bool checkNativeCondition(S o) { |
391 | O result = runNativePredicate(o, new Env); |
392 | result = unpackWithAlternativeOrIterator(result); |
393 | if (result cast Bool) ret result; |
394 | if (result instanceof Map) true; // TODO |
395 | null; |
396 | } |
397 | |
398 | !include #1025614 // parsePythonesqueProcedure |
399 | |
400 | Proc parseProcedure(S s) { |
401 | ret parsePythonesqueProcedure(s); |
402 | } |
403 | |
404 | O splitAtAmpersand2(S s) { |
405 | LS l = tok_splitAtAmpersand(s); |
406 | if (l(l) == 1) ret s; |
407 | ret new And(first(l), splitAtAmpersand2(join(" & ", dropFirst(l)))); |
408 | } |
409 | |
410 | // "zip" a condition with a fact (match word-by-word) |
411 | SS zipIt(S cond, S fact) { |
412 | SS map = zipIt_keepBrackets(cond, fact); |
413 | if (map == null) null; // no match |
414 | map = mapValues tok_deRoundOrCurlyBracket(map); |
415 | // handle special case, e.g. "i am x", "i am (x)" |
416 | removeFromMapWhere(map, (k, v) -> eqic(k, v)); |
417 | ret map; |
418 | } |
419 | |
420 | SS zipIt_deBracket(S pat, S s) { |
421 | SS map = zipIt(pat, s); |
422 | ret map == null ? null : mapValues tok_deRoundOrCurlyBracket(map); |
423 | } |
424 | |
425 | // "zip" a condition with a fact (match word-by-word) |
426 | SS zipIt_keepBrackets(S cond, S fact) { |
427 | SS map = gazelle_deepZip_keepBrackets(cond, fact); |
428 | if (map == null) null; // no match |
429 | if (!all(keys(map), s -> isVar(s))) null; /*with print("Non-variable changes, exiting")*/; |
430 | ret map; |
431 | } |
432 | |
433 | bool isVar(S s) { |
434 | ret s != null && |
435 | (vars.contains(s) || s.startsWith("var_") || isDollarVar(s)); |
436 | } |
437 | |
438 | O replaceVars(O o, SS map) { |
439 | if (empty(map)) ret o; |
440 | ret transform(x -> replaceVars_base(x, map), o); |
441 | } |
442 | |
443 | O replaceVars_base(O o, SS map) { |
444 | if (o cast S) |
445 | ret replaceCodeTokensUsingMap(o, map); |
446 | null; |
447 | } |
448 | |
449 | S format(S s, SS map) { |
450 | ret replaceCodeTokensUsingMap(s, mapValues optRound(map)); |
451 | } |
452 | |
453 | // if f returns null, go through structure |
454 | O transform(IF1 f, O o) { |
455 | ret transform_understandsTransformableAndList(f, o); |
456 | } |
457 | |
458 | void applyLogicRuleToFact(LogicRule rule, S fact) { |
459 | O lhs = rule.lhs, rhs = rule.rhs; |
460 | O cond, remaining = null; |
461 | if lhs is And(O a, O b) { |
462 | cond = a; |
463 | remaining = b; |
464 | } else |
465 | cond = lhs; |
466 | |
467 | // now we match the condition with the fact |
468 | SS map = zipIt_keepBrackets((S) cond, fact); |
469 | if (map == null) { |
470 | if (printNonMatches) |
471 | print("Non-match: " + quote(cond) + " / " + quote(fact)); |
472 | ret; // no match |
473 | } |
474 | |
475 | // Now we have a proper mapping with the keys being variables! |
476 | if (verbose) print("Match: " + quote(cond) + " / " + quote(fact)); |
477 | |
478 | // drop round brackets |
479 | // XXX? map = mapValues tok_deRoundBracket(map); |
480 | |
481 | // Apply mapping to right hand side |
482 | O rhs_replaced = replaceVars(rhs, map); |
483 | if (verbose) print(+rhs_replaced); |
484 | |
485 | Map trail = litorderedmap(op := "applyLogicRuleToFact", |
486 | +rule, +fact, +rhs_replaced, +remaining); |
487 | |
488 | if (remaining == null) { |
489 | // No more conditions. Add as fact / new rule |
490 | addRewrittenRHS(rhs_replaced, trail); |
491 | } else { |
492 | // Apply mapping to remaining condition |
493 | O remaining_replaced = replaceVars(remaining, map); |
494 | trail.put(+remaining_replaced); |
495 | addLogicRuleWithTrail(new LogicRule(remaining_replaced, rhs_replaced), trail); |
496 | } |
497 | } |
498 | |
499 | run { think(); } |
500 | |
501 | !include #1025615 // smartParser1 |
502 | |
503 | void parseProgram { |
504 | if (programLoaded) ret; |
505 | set programLoaded; |
506 | loadProgram(program); |
507 | } |
508 | |
509 | void loadProgram(S program) { |
510 | set parsing; |
511 | try { |
512 | smartParser1(program); |
513 | } finally { |
514 | parsing = false; |
515 | } |
516 | } |
517 | |
518 | bool doSomeLogic() { |
519 | bool anyAction; |
520 | Pair<LogicRule, S> p; |
521 | while not null (p = rulesOnFacts.next()) { |
522 | ping(); |
523 | set anyAction; |
524 | //print("Combination: " + p); |
525 | applyLogicRuleToFact(p.a, p.b); |
526 | } |
527 | Pair<CodeFragment, S> p2; |
528 | while not null (p2 = codeOnFacts.next()) { |
529 | ping(); |
530 | set anyAction; |
531 | //print("Combination: " + p2); |
532 | applyCodeFragmentToFact(p2.a, p2.b); |
533 | } |
534 | Pair<IVF1<S>, S> p3; |
535 | while not null (p3 = anyCodeOnFacts.next()) { |
536 | ping(); |
537 | set anyAction; |
538 | //print("Combination: " + p3); |
539 | pcallF(p3.a, p3.b); |
540 | } |
541 | Pair<IVF1<LogicRule>, LogicRule> p4; |
542 | while not null (p4 = anyCodeOnRules.next()) { |
543 | ping(); |
544 | set anyAction; |
545 | pcallF(p4.a, p4.b); |
546 | } |
547 | ret anyAction; |
548 | } |
549 | |
550 | void applyCodeFragmentToFact(CodeFragment cf, S fact) { |
551 | SS map = cf.keepBrackets ? zipIt_keepBrackets(cf.head, fact) : zipIt(cf.head, fact); |
552 | if (map != null) |
553 | cf.body.get(mapValues tok_deRoundBracket(map), new Env); |
554 | } |
555 | |
556 | // indicator for end of thought process (when this stays stable) |
557 | long size() { |
558 | ret l(logicRules) + l(facts) + proceduresExecuted; |
559 | } |
560 | |
561 | void think { |
562 | parseProgram(); |
563 | |
564 | set thoughtBegun; |
565 | |
566 | int round = 0; |
567 | |
568 | while ping (round++ < maxRounds) { |
569 | long lastSize = size(); |
570 | if (verbose) print("Logic round " + round + ", size: " + lastSize); |
571 | while (doSomeLogic() && round++ < maxRounds && l(facts) < maxFacts) {} |
572 | |
573 | for ping (Proc proc : getAndClearList(proceduresToRun)) |
574 | runParsedProcedure(proc); |
575 | |
576 | callFAll(extraLogicOperations); |
577 | |
578 | if (size() == lastSize) { |
579 | if (verbose) print("No changes, exiting"); |
580 | break; |
581 | } |
582 | } |
583 | |
584 | // We're done logicking, so print all the facts gathered |
585 | |
586 | Cl<S> madeFacts = factsDeduced(); |
587 | if (printFactsAfterThinking) |
588 | pnlWithHeading("Facts I deduced", madeFacts); |
589 | |
590 | // Print say () and print () separately |
591 | |
592 | new LS output; |
593 | for (S fact : madeFacts) { |
594 | LS tok = mainTokenize(fact); |
595 | if (countCodeTokens(tok) == 2 && eqicOneOf(getCodeToken(tok, 0), "print", "say")) |
596 | // For the user, we print without all the round brackets |
597 | output.add(tok_dropRoundBrackets(getCodeToken(tok, 1))); |
598 | } |
599 | |
600 | pnlWithHeadingIfNempty("Bot Output", output); |
601 | } |
602 | |
603 | void addNativePredicate(S head, IF0 body) { |
604 | nativePredicates.add(new NativePredicate(head, (map, env) -> body!)); |
605 | } |
606 | |
607 | void addNativePredicate(S head, IF1<SS, O> body) { |
608 | nativePredicates.add(new NativePredicate(head, (map, env) -> body.get(map))); |
609 | } |
610 | |
611 | void addNativePredicate(S head, IF2<SS, Env, NPRet> body) { |
612 | nativePredicates.add(new NativePredicate(head, body)); |
613 | } |
614 | |
615 | // when you only need one result |
616 | O unpackWithAlternativeOrIterator(O result) { |
617 | if (result instanceof Iterator) ret first((Iterator) result); |
618 | if (result instanceof WithAlternative) ret ((WithAlternative) result).result; |
619 | ret result; |
620 | } |
621 | |
622 | void onFactDo(S head, IVF2<SS, Env> body) { |
623 | codeOnFacts.newA(new CodeFragment(head, body)); |
624 | } |
625 | |
626 | void onFactDo_keepBrackets(S head, IVF2<SS, Env> body) { |
627 | codeOnFacts.newA(new CodeFragment(head, body, true)); |
628 | } |
629 | |
630 | LS filterByPattern(S pat, Iterable<S> items) { |
631 | ret filter(items, i -> zipIt(pat, i) != null); |
632 | } |
633 | |
634 | // pat = pattern with variables |
635 | // results are mappings with debracketed values |
636 | L<SS> matchFacts(S pat) { |
637 | ret matchStrings(pat, facts); |
638 | } |
639 | |
640 | SS matchString(S pat, S input) { |
641 | ret zipIt_deBracket(pat, input); |
642 | } |
643 | |
644 | L<SS> matchStrings(S pat, Iterable<S> items) { |
645 | new L<SS> out; |
646 | for (S s : items) { |
647 | SS map = zipIt_deBracket(pat, s); |
648 | if (map != null) |
649 | out.add(map); |
650 | } |
651 | ret out; |
652 | } |
653 | |
654 | LPair<S, SS> matchFacts2(S pat) { |
655 | ret matchStrings2(pat, facts); |
656 | } |
657 | |
658 | // returns items too |
659 | LPair<S, SS> matchStrings2(S pat, Iterable<S> items) { |
660 | new LPair<S, SS> out; |
661 | for (S s : items) { |
662 | SS map = zipIt_deBracket(pat, s); |
663 | if (map != null) |
664 | out.add(pair(s, map)); |
665 | } |
666 | ret out; |
667 | } |
668 | |
669 | LS matchFacts(S var, S pat) { |
670 | ret map(matchFacts(pat), map -> map.get(var)); |
671 | } |
672 | |
673 | // pat = pattern with variables |
674 | // results are mappings with debracketed values |
675 | L<SS> matchFacts_keepBrackets(S pat) { |
676 | new L<SS> out; |
677 | for (S fact : facts) { |
678 | SS map = zipIt_keepBrackets(pat, fact); |
679 | if (map != null) |
680 | out.add(map); |
681 | } |
682 | ret out; |
683 | } |
684 | |
685 | void openAllTheories { |
686 | for (SS map : matchFacts_keepBrackets("theory $x $y")) |
687 | openTheory(tok_deRoundOrCurlyBracket(map.get("$x")), map.get("$y")); |
688 | } |
689 | |
690 | void openTheory(S name) { |
691 | for (SS map : matchFacts_keepBrackets("theory $x $y")) |
692 | if (eqic(properUnquote(tok_deRoundBracket($x(map))), name)) |
693 | ret with openTheory(name, $y(map)); |
694 | fail("Theory not defined: " + quote(name)); |
695 | } |
696 | |
697 | void openTheory(S name, S body) { |
698 | //print("Raw theory: " + quote(s)); |
699 | loadProgram(withoutLeadingLinesEmptyAfterTrim_autoUnindent(tok_deRoundOrCurlyBracket_keepFirstSpacing(body))); |
700 | if (verbose) print("Opened theory " + name); |
701 | } |
702 | |
703 | void autoOpenTheories { |
704 | onFactDo_keepBrackets("theory $x $y", (map, env) -> openTheory(tok_deRoundOrCurlyBracket(map.get("$x")), map.get("$y"))); |
705 | } |
706 | |
707 | // returns number of expectations checked |
708 | int checkExpectations() { |
709 | int n = 0; |
710 | // check if all expect (...) facts are met |
711 | for (SS map : matchFacts("expect $x")) { |
712 | assertContains(facts, firstValue(map)); |
713 | ++n; |
714 | } |
715 | // check if all don't expect (...) facts are met |
716 | for (SS map : matchFacts("don't expect $x")) { |
717 | assertDoesntContain(facts, firstValue(map)); |
718 | ++n; |
719 | } |
720 | ret n; |
721 | } |
722 | |
723 | void standardImports() { |
724 | if (standardImportsLoaded) ret; |
725 | set standardImportsLoaded; |
726 | registerImport("math", () -> philosophyBot1_math(this)); |
727 | registerImport("bool", () -> philosophyBot1_bool(this)); |
728 | registerImport("or", () -> philosophyBot1_orHandler(this)); |
729 | registerImport("iota", () -> philosophyBot1_iotaHandler(this)); |
730 | registerImport("tlft_honoringBrackets", () -> |
731 | addNativePredicate("tlft_honoringBrackets $x", |
732 | map -> printIf(verbose, "tlft output", |
733 | tlft_honoringBrackets($x(map))))); |
734 | addNativePredicate("printNonMatches", () -> { printNonMatches = true; true; }); |
735 | |
736 | philosophyBot1_enableAddSimplifier_withTrails(this); |
737 | } |
738 | |
739 | void addFactPreprocessor(IPred<S> f) { |
740 | factPreprocessors.add(f); |
741 | } |
742 | |
743 | void addLogicRulePreprocessor(IPred<LogicRule> f) { |
744 | logicRulePreprocessors.add(f); |
745 | } |
746 | |
747 | void registerImport(S name, Runnable handler) { |
748 | S line = "import " + name; |
749 | factPreprocessors.add(s -> { |
750 | if (eqic(s, line)) { |
751 | if (verbose) print("Importing " + name); |
752 | handler.run(); |
753 | true; |
754 | } |
755 | false; |
756 | }); |
757 | } |
758 | |
759 | bool hasFact(S fact) { |
760 | ret contains(facts, fact); |
761 | } |
762 | |
763 | bool hasContradiction() { ret hasFact("contradiction"); } |
764 | |
765 | LS mainTokenize(S s) { |
766 | ret javaTokWithBrackets(s); |
767 | } |
768 | |
769 | // sanitize untrusted input - overly safe version |
770 | S sanitizeInput(S s) { |
771 | ret joinWithSpace(antiFilter(words2_plusApostrophe(s), w -> isVar(w))); |
772 | } |
773 | |
774 | void deleteFacts(Iterable<S> l) { |
775 | Set<S> set = asCISet(l); |
776 | facts = filterCISet(facts, f -> !contains(set, f)); |
777 | } |
778 | |
779 | Cl<S> factsDeduced() { |
780 | ret listMinusList(facts, originalFacts); |
781 | } |
782 | |
783 | Cl<S> deducedFacts() { |
784 | ret factsDeduced(); |
785 | } |
786 | |
787 | Cl<S> factsDeducedAfterThought() { |
788 | think(); |
789 | ret deducedFacts(); |
790 | } |
791 | |
792 | |
793 | bool containsDollarVars(O o) { |
794 | // abuse transform function |
795 | new Flag flag; |
796 | withCancelPoint(cp -> { |
797 | transform(x -> { |
798 | if (x cast S) { |
799 | if (main containsDollarVars(x)) { |
800 | flag.raise(); |
801 | cancelTo(cp); |
802 | } |
803 | ret x; |
804 | } |
805 | null; |
806 | }, o); |
807 | }); |
808 | ret flag!; |
809 | } |
810 | |
811 | L<LogicRule> allLogicRulesWithoutLHSVars() { |
812 | ret filter(logicRules, r -> !containsDollarVars(leftmostCondition(r.lhs))); |
813 | } |
814 | |
815 | O leftmostCondition(O o) { |
816 | while (o instanceof And) o = ((And) o).a; |
817 | ret o; |
818 | } |
819 | |
820 | void _standardHandlers { |
821 | philosophyBot_autoOpenTheoriesHandler(this); |
822 | addFactPreprocessor(s -> { if (eqic(s, "standard imports")) ret true with standardImports(); false; }); |
823 | } |
824 | |
825 | void runAndCheckExpectations { |
826 | run(); |
827 | checkExpectations(); |
828 | } |
829 | |
830 | void addDeepTransformer(IF1<S> transformer) { |
831 | if (deepTransformers == null) { |
832 | deepTransformers = new L; |
833 | // enable transformers |
834 | anyCodeOnFacts.newA(fact -> |
835 | addFactWithTrail(gazelle_deepTransform( |
836 | s -> firstTransformersResult(deepTransformers, s), fact), |
837 | trails == null ? null : new TTransformed(fact, transformer))); |
838 | } |
839 | deepTransformers.add(transformer); |
840 | } |
841 | |
842 | void addTrailEnabledDeepTransformer(IF1<S, WithTrail<S>> transformer) { |
843 | if (trailEnabledDeepTransformers == null) { |
844 | trailEnabledDeepTransformers = new L; |
845 | // enable transformers |
846 | anyCodeOnFacts.newA(fact -> { |
847 | WithTrail<S> simplified = gazelle_deepTransform_withTrail( |
848 | s -> firstTransformersResult_trailEnabled(trailEnabledDeepTransformers, s), fact); |
849 | if (simplified == null) ret; |
850 | addFactWithTrail(simplified); |
851 | if (noteSimplificationsAsFacts) |
852 | addFact(format("$x simplifies $y", litmap($x := fact, $y := simplified!))); |
853 | }); |
854 | } |
855 | trailEnabledDeepTransformers.add(transformer); |
856 | } |
857 | |
858 | O getExtension(O key) { ret mapGet(extensions, key); } |
859 | |
860 | void addExtension(O key, O value) { |
861 | if (extensions == null) extensions = new Map; |
862 | extensions.put(key, value); |
863 | } |
864 | |
865 | void enableTrails() { if (trails == null) trails = ciMap(); } |
866 | |
867 | O getTrail(S fact) { ret mapGet(trails, fact); } |
868 | |
869 | // get trail for arbitrary object (fact/rule) |
870 | O getTrail(O o) { |
871 | if (o cast LogicRule) ret o.trail; |
872 | if (o cast S) |
873 | ret mapGet(trails, o); |
874 | null; |
875 | } |
876 | |
877 | void printDeducedFactsWithTrails() { |
878 | printAsciiHeading("Deduced facts with trails"); |
879 | for (S fact : factsDeduced()) { |
880 | print(fact); |
881 | printUnlessNull(" << ", getTrail(fact)); |
882 | } |
883 | } |
884 | |
885 | L<WithTrail<S>> deducedFactsWithTrails() { |
886 | ret map(factsDeduced(), s -> WithTrail(getTrail(s), s)); |
887 | } |
888 | |
889 | void setTrailForAllLogicRules(O trail) { |
890 | for (LogicRule r : logicRules) |
891 | r.trail = trail; |
892 | } |
893 | |
894 | L<LogicRule> logicRules() { |
895 | ret cloneList(logicRules); |
896 | } |
897 | |
898 | Pair<Set<LogicRule>, Set<S>> rulesAndFactsUsedForMaking(O factOrLogicRule) { |
899 | Set l = thingsUsedForMaking(factOrLogicRule); |
900 | ret pair(instancesOfAsSet LogicRule(l), setIntersection(asCISet(stringsOnly(l)), facts)); |
901 | } |
902 | |
903 | Set thingsUsedForMaking(O factOrLogicRule) { |
904 | ret philosophyBot1_visitTrailsTree_gen(this, null, factOrLogicRule); |
905 | } |
906 | |
907 | void addExtraLogicOperation(Runnable r) { |
908 | extraLogicOperations.add(r); |
909 | } |
910 | |
911 | void addCodeOnFact(IVF1<S> f) { |
912 | anyCodeOnFacts.newA(f); |
913 | } |
914 | |
915 | void updateCodeOnFact(IVF1<S> f) { |
916 | anyCodeOnFacts.updateA(f); |
917 | } |
918 | |
919 | void addCodeOnRule(IVF1<LogicRule> f) { |
920 | anyCodeOnRules.newA(f); |
921 | } |
922 | |
923 | /*void updateCodeOnRule(IVF1<LogicRule> f) { |
924 | anyCodeOnRules.updateA(f); |
925 | }*/ |
926 | |
927 | void handleException(Throwable e) { |
928 | print("BOT", e); |
929 | } |
930 | } |
Began life as a copy of #1025576
download show line numbers debug dex old transpilations
Travelled to 8 computer(s): bhatertpkbcr, mqqgnosmbjvj, pyentgdyhuwx, pzhvpgtvlbxg, tvejysmllsmz, vouqrxazstgt, whxojlpjdney, xrpafgyirdlv
No comments. add comment
Snippet ID: | #1025597 |
Snippet name: | PhilosophyBot1 (LIVE) |
Eternal ID of this version: | #1025597/302 |
Text MD5: | dfbe0bae82c887f0a85d81c1ce1aebb5 |
Transpilation MD5: | c182a034605304c1003fea5f1aa4aee8 |
Author: | stefan |
Category: | |
Type: | JavaX fragment (include) |
Public (visible to everyone): | Yes |
Archived (hidden from active list): | No |
Created/modified: | 2020-04-26 01:28:23 |
Source code size: | 28551 bytes / 930 lines |
Pitched / IR pitched: | No / No |
Views / Downloads: | 863 / 2804 |
Version history: | 301 change(s) |
Referenced in: | [show references] |