Libraryless. Click here for Pure Java version (6624L/43K).
1 | sclass PhilosophyBot1 { |
2 | srecord LogicRule(lhs, rhs) {} |
3 | srecord And(a, b) {} |
4 | srecord If(condition, thenBlock, elseBlock) {} |
5 | srecord For(var, condition, body) {} |
6 | srecord While(condition, body) {} |
7 | |
8 | // body takes variable mapping |
9 | // body can return |
10 | // Bool => immediate result (ok or fail) |
11 | // SS => variable mapping |
12 | // null => not applicable |
13 | srecord NativePredicate(S head, IF1<SS, O> body) {} |
14 | |
15 | srecord WithAlternative(IF0<O> alternative, O result) {} |
16 | |
17 | replace Proc with L. // procedures are a list of statements |
18 | |
19 | transient S program; |
20 | transient int maxRounds = 1000; |
21 | transient Set<S> facts = linkedCISet(); |
22 | transient Set<S> originalFacts; |
23 | transient new LinkedHashSet<LogicRule> logicRules; |
24 | transient new AllOnAll<LogicRule, S> rulesOnFacts; |
25 | transient new L<Proc> proceduresToRun; |
26 | // parsed procedures |
27 | transient long proceduresExecuted; |
28 | transient new L<NativePredicate> nativePredicates; |
29 | transient bool debugNativeCalls = true, debugAllCmds = true; |
30 | |
31 | transient Set<S> vars = litciset("x", "y", "z"); |
32 | |
33 | void addLogicRule(LogicRule rule) { |
34 | if (logicRules.add(rule)) { |
35 | print("Got logic rule", rule); |
36 | rulesOnFacts.newA(rule); // to combine it with the facts |
37 | } |
38 | } |
39 | |
40 | void addFact(S fact) { |
41 | fact = trim(fact); |
42 | if (empty(fact)) ret; |
43 | fact = tok_deRoundBracket(fact); |
44 | // Check if it's a procedure |
45 | LS tok = javaTokWithBrackets(fact); |
46 | if (countCodeTokens(tok) == 2 && eqic(getCodeToken(tok, 0), "proc") |
47 | && isCurlyBracketed(getCodeToken(tok, 1))) pcall { |
48 | // It's a procedure! |
49 | S proc = uncurly_keepSpaces(getCodeToken(tok, 1)); |
50 | if (proceduresToRun.add(parseProcedure(proc))) { |
51 | print("Got procedure:"); |
52 | print(indentx("> ", proc)); |
53 | } |
54 | } |
55 | else // It's a fact, not a procedure |
56 | if (facts.add(fact)) { |
57 | print("Got fact: " + fact); |
58 | rulesOnFacts.newB(fact); // to combine it with the rules |
59 | } |
60 | } |
61 | |
62 | void runProcedure(S proc) pcall { |
63 | print("Running procedure."); |
64 | runParsedProcedure(parseProcedure(proc)); |
65 | } |
66 | |
67 | void runParsedProcedure(Proc commands) { |
68 | ++proceduresExecuted; |
69 | L remainingCommands = cloneLinkedList(commands); |
70 | O cmd; |
71 | while not null (cmd = popFirst(remainingCommands)) { |
72 | if (cmd cast L) continue with runParsedProcedure(cmd); |
73 | if (debugAllCmds) |
74 | print("Running cmd: " + sfu(cmd)); |
75 | if cmd is If(O condition, O thenBlock, O elseBlock) { |
76 | O blockToRun = checkCondition(condition) ? thenBlock : elseBlock; |
77 | runParsedProcedure(ll(blockToRun)); |
78 | } else if cmd is For(O var, O condition, O body) { |
79 | // make a new logic rule and add it |
80 | // assume the variable is globally declared as a variable |
81 | addLogicRule(new LogicRule(condition, "proc {\n" + body + "\n}")); |
82 | } else if cmd is While(O condition, O body) { |
83 | bool b = checkCondition(condition); |
84 | if (!b) ret; |
85 | proceduresToRun.add(ll(body, cmd)); |
86 | } else if (cmd cast S) { |
87 | O result = runNativePredicate(cmd); |
88 | if (result != null) { |
89 | if (result instanceof WithAlternative) { |
90 | print("Have alternative"); |
91 | result = ((WithAlternative) result).result; |
92 | } |
93 | if (isFalse(result)) ret; |
94 | if (isTrueOpt(result)) continue; |
95 | SS mapping = cast result; // assume it's a variable mapping |
96 | // apply to all remaining commands and continue |
97 | L remainingCommands2 = mapToLinkedList(remainingCommands, |
98 | c -> replaceVars(c, mapValues optRound(mapping))); |
99 | print("Applying var mapping " + mapping + " to " + remainingCommands |
100 | + " => " + remainingCommands2); |
101 | remainingCommands = remainingCommands2; |
102 | } else |
103 | addFact(cmd); |
104 | } else if (cmd != null) |
105 | fail("Unimplemented command: " + cmd); |
106 | } |
107 | } |
108 | |
109 | // return var mapping (SS), Bool or null for no matching predicate |
110 | O runNativePredicate(S s) { |
111 | for (NativePredicate np : nativePredicates) { |
112 | SS map = zipIt(np.head, s); |
113 | if (map != null) { |
114 | O result = np.body.get(mapValues tok_deRoundBracket(map)); |
115 | if (debugNativeCalls) |
116 | print("Native predicate result: " + np.head + " => " + result); |
117 | if (result instanceof Map && nempty(map)) { |
118 | result = mapKeys((SS) result, var -> lookupOrKeep(map, var)); |
119 | if (debugNativeCalls) |
120 | print("Rewrote native predicate result: " + result); |
121 | } |
122 | try object result; |
123 | } |
124 | } |
125 | null; |
126 | } |
127 | |
128 | bool checkCondition(O o) { |
129 | if (o cast S) { |
130 | if (contains(facts, o)) true; |
131 | O result = runNativePredicate(o); |
132 | if (result cast Bool) ret result; |
133 | if (result instanceof Map) true; // TODO |
134 | } |
135 | print("Ignoring condition: " + o); |
136 | false; |
137 | } |
138 | |
139 | Proc parseProcedure(S proc) { |
140 | //printStruct(proc); |
141 | proc = withoutLinesEmptyAfterTrim(proc); |
142 | //printStruct(proc); |
143 | proc = autoUnindent(proc); |
144 | //printStruct(proc); |
145 | print(indentx("> ", proc)); |
146 | |
147 | LS l = groupPythonStyleIndents(proc); |
148 | pnl("unpythonized ", l); |
149 | |
150 | new L out; |
151 | for i over l: { |
152 | S s = l.get(i); |
153 | LS tok = javaTokWithBrackets(s); |
154 | if (eqic(firstCodeToken(tok), "if")) { |
155 | assertEquals(s, ":", getCodeToken(tok, 2)); |
156 | out.add(new If(deRoundBracket(getCodeToken(tok, 1)), |
157 | parseProcedure(joinSubList(tok, 3*2)), null)); |
158 | } else if (eqic(firstCodeToken(tok), "while")) { |
159 | assertEquals(s, ":", getCodeToken(tok, 2)); |
160 | out.add(new While(deRoundBracket(getCodeToken(tok, 1)), |
161 | parseProcedure(joinSubList(tok, 3*2)))); |
162 | } else if (eqic(firstCodeToken(tok), "else")) { |
163 | O last = last(out); |
164 | if (!last instanceof If) fail("Else without if"); |
165 | assertEquals(s, ":", getCodeToken(tok, 1)); |
166 | ((If) last).elseBlock = joinSubList(tok, 2*2); |
167 | } else if (eqic(firstCodeToken(tok), "for")) { |
168 | assertEquals(s, ":", getCodeToken(tok, 2)); |
169 | S cond = getCodeToken(tok, 1); |
170 | // cond looks like: "(y | x has a y)" |
171 | cond = deRoundBracket(cond); |
172 | LS tok2 = javaTok(cond); |
173 | assertEquals(cond, "|", getCodeToken(tok2, 1)); |
174 | S var = assertIdentifier(cond, getCodeToken(tok2, 0)); |
175 | S actualCondition = trimJoinSubList(tok2, 2*2+1); |
176 | out.add(new For(var, actualCondition, parseProcedure(joinSubList(tok, 3*2)))); |
177 | } else |
178 | out.add(s); |
179 | } |
180 | pnl("Parsed procedure ", out); |
181 | ret out; |
182 | } |
183 | |
184 | O splitAtAmpersand2(S s) { |
185 | LS l = tok_splitAtAmpersand(s); |
186 | if (l(l) == 1) ret s; |
187 | ret new And(first(l), splitAtAmpersand2(join(" & ", dropFirst(l)))); |
188 | } |
189 | |
190 | // "zip" a condition with a fact (match word-by-word) |
191 | SS zipIt(S cond, S fact) { |
192 | SS map = gazelle_zip(cond, fact); |
193 | if (map == null) null; // no match |
194 | print("gazelle zip => " + map); |
195 | |
196 | // are only variables changed? |
197 | if (!allKeysAreInSet(map, vars)) |
198 | null; /*with print("Non-variable changes, exiting")*/; |
199 | |
200 | ret map; |
201 | } |
202 | |
203 | O replaceVars(O o, SS map) { |
204 | if (empty(map)) ret o; |
205 | // TODO: non-string cases |
206 | ret join(replaceCodeTokensUsingMap(javaTok((S) o), map)); |
207 | } |
208 | |
209 | void applyLogicRuleToFact(LogicRule rule, S fact) { |
210 | O lhs = rule.lhs, rhs = rule.rhs; |
211 | O cond, remaining = null; |
212 | if lhs is And(O a, O b) { |
213 | cond = a; |
214 | remaining = b; |
215 | } else |
216 | cond = lhs; |
217 | |
218 | // now we match the condition with the fact |
219 | SS map = zipIt((S) cond, fact); |
220 | if (map == null) ret; // no match |
221 | |
222 | // Now we have a proper mapping with the keys being variables! |
223 | print("Match."); |
224 | |
225 | // drop round brackets |
226 | // XXX? map = mapValues tok_deRoundBracket(map); |
227 | |
228 | // Apply mapping to right hand side |
229 | S rhs_replaced = cast replaceVars(rhs, map); |
230 | print(+rhs_replaced); |
231 | |
232 | if (remaining == null) { |
233 | // Add as fact |
234 | addFact(rhs_replaced); |
235 | } else { |
236 | // Apply mapping to remaning condition |
237 | S remaining_replaced = cast replaceVars(remaining, map); |
238 | addLogicRule(new LogicRule(remaining_replaced, rhs_replaced)); |
239 | } |
240 | } |
241 | |
242 | run { |
243 | parseProgram(); |
244 | think(); |
245 | } |
246 | |
247 | void parseProgram { |
248 | // split into paragraphs and unindent |
249 | |
250 | LS paragraphs = map autoUnindent(map rtrim(splitAtEmptyLines(program))); |
251 | print("Got " + n2(paragraphs, "parapraph")); |
252 | |
253 | // print the parapraphs |
254 | print(joinWithEmptyLines(map(s -> indentx("> ", s), paragraphs))); |
255 | |
256 | // throw away comment-only and quoted paragraphs (assume it's a title) |
257 | LS paragraphs2 = antiFilter(paragraphs, s -> |
258 | isSingleLine(trim(s)) && isQuoted(trim(s)) || countJavaTokens(s) == 0 |
259 | || endsWith(rtrim(s), "----")); |
260 | print("Got " + n2(paragraphs2, "filtered paragraph")); |
261 | print(joinWithEmptyLines(map(s -> indentx("> ", s), paragraphs2))); |
262 | |
263 | // find fact paragraphs |
264 | |
265 | print(map allLinesAreUnindented(paragraphs2)); |
266 | Pair<LS> p1 = filterAntiFilter(s -> |
267 | !isSingleLine(trim(s)) && allLinesAreUnindented(s), paragraphs2); |
268 | LS multiFactParagraphs = p1.a, paragraphs3 = p1.b; |
269 | |
270 | for (S para : multiFactParagraphs) |
271 | for (S s : tlft(para)) |
272 | addFact(s); |
273 | |
274 | // find logic rules |
275 | |
276 | new LS paragraphs4; |
277 | for (S para : paragraphs3) { |
278 | PairS p = splitAtDoubleArrow_pair(para); |
279 | if (p == null) continue with paragraphs4.add(para); |
280 | addLogicRule(new LogicRule(splitAtAmpersand2(p.a), splitAtAmpersand2(p.b))); |
281 | } |
282 | |
283 | pnlStruct("Unparsed - assuming facts", paragraphs4); |
284 | |
285 | // assume the unparsed stuff consists of facts |
286 | for (S para : paragraphs4) |
287 | addFact(para); |
288 | |
289 | originalFacts = cloneSet(facts); |
290 | } |
291 | |
292 | bool doSomeLogic() { |
293 | bool anyAction; |
294 | Pair<LogicRule, S> p; |
295 | while not null (p = rulesOnFacts.next()) { |
296 | set anyAction; |
297 | //print("Combination: " + p); |
298 | applyLogicRuleToFact(p.a, p.b); |
299 | } |
300 | ret anyAction; |
301 | } |
302 | |
303 | // indicator for end of thought process (when this stays stable) |
304 | long size() { |
305 | ret l(logicRules) + l(facts) + proceduresExecuted; |
306 | } |
307 | |
308 | void think { |
309 | int round = 0; |
310 | |
311 | while (round++ < maxRounds) { |
312 | long lastSize = size(); |
313 | print("Logic round " + round + ", size: " + lastSize); |
314 | while (doSomeLogic() && round++ < maxRounds) {} |
315 | |
316 | for (Proc proc : getAndClearList(proceduresToRun)) |
317 | runParsedProcedure(proc); |
318 | |
319 | if (size() == lastSize) { |
320 | print("No changes, exiting"); |
321 | break; |
322 | } |
323 | } |
324 | |
325 | // We're done logicking, so print all the facts gathered |
326 | |
327 | LS factsToPrint = listMinusList(facts, originalFacts); |
328 | pnlWithHeading("Facts I deduced", factsToPrint); |
329 | |
330 | // Print the actual output |
331 | |
332 | new LS output; |
333 | for (S fact : factsToPrint) { |
334 | LS tok = javaTokWithBrackets(fact); |
335 | if (countCodeTokens(tok) == 2 && eqic(getCodeToken(tok, 0), "print")) |
336 | // For the user, we print without all the round brackets |
337 | output.add(tok_dropRoundBrackets(getCodeToken(tok, 1))); |
338 | } |
339 | |
340 | pnlWithHeading("Bot Output", output); |
341 | } |
342 | |
343 | void addNativePredicate(S head, IF0 body) { |
344 | nativePredicates.add(new NativePredicate(head, map -> body!)); |
345 | } |
346 | |
347 | void addNativePredicate(S head, IF1<SS, O> body) { |
348 | nativePredicates.add(new NativePredicate(head, body)); |
349 | } |
350 | |
351 | // for backtracking in native predicates |
352 | WithAlternative withAlternative(IF0<O> alternative, O result) { |
353 | ret new WithAlternative(alternative, result); |
354 | } |
355 | } |
Began life as a copy of #1025597
download show line numbers debug dex old transpilations
Travelled to 6 computer(s): bhatertpkbcr, mqqgnosmbjvj, pyentgdyhuwx, pzhvpgtvlbxg, tvejysmllsmz, vouqrxazstgt
No comments. add comment
Snippet ID: | #1025607 |
Snippet name: | PhilosophyBot1 backup before backtracking |
Eternal ID of this version: | #1025607/1 |
Text MD5: | e1670695d1a19d50a8b46ceaccaae02e |
Transpilation MD5: | 76eebc65135d53eba4a6a4a96f58c156 |
Author: | stefan |
Category: | |
Type: | JavaX fragment (include) |
Public (visible to everyone): | Yes |
Archived (hidden from active list): | No |
Created/modified: | 2019-10-08 12:40:27 |
Source code size: | 11766 bytes / 355 lines |
Pitched / IR pitched: | No / No |
Views / Downloads: | 279 / 347 |
Referenced in: | [show references] |