Libraryless. Click here for Pure Java version (1407L/9K/32K).
1 | !752 |
2 | |
3 | !include #1002825 // new structure |
4 | |
5 | static class Prolog {
|
6 | int varCount; |
7 | boolean showStuff; |
8 | |
9 | class Var extends Lisp {
|
10 | Lisp instance; |
11 | |
12 | *() {
|
13 | this("V" + (++varCount));
|
14 | } |
15 | |
16 | *(S name) {
|
17 | super(name); |
18 | instance = this; |
19 | } |
20 | |
21 | void reset() { instance = this; }
|
22 | |
23 | public String toString() {
|
24 | if (instance == this) ret getName(); |
25 | ret instance.toString(); |
26 | } |
27 | |
28 | S getName() {
|
29 | ret head; |
30 | } |
31 | } |
32 | |
33 | class Clause {
|
34 | Lisp head; |
35 | Goal body; |
36 | |
37 | *(Lisp *head, Goal *body) {}
|
38 | *(Lisp *head) {}
|
39 | |
40 | Clause copy() {
|
41 | return new Clause(copy2(head), body == null ? null : body.copy()); |
42 | } |
43 | |
44 | public String toString() {
|
45 | //ret head + " :- " + (body == null ? "true" : body); |
46 | ret body == null ? head.toString() : head + " :- " + body; |
47 | } |
48 | }; |
49 | |
50 | static class Program {
|
51 | Clause pcar; |
52 | Program pcdr; |
53 | |
54 | *(Clause *pcar, Program *pcdr) {}
|
55 | *(L<Clause> clauses) {
|
56 | this(clauses, 0); |
57 | } |
58 | |
59 | *(L<Clause> clauses, int i) {
|
60 | pcar = clauses.get(i); |
61 | if (i+1 < l(clauses)) |
62 | pcdr = new Program(clauses, i+1); |
63 | } |
64 | |
65 | *(Clause... clauses) {
|
66 | this(asList(clauses)); |
67 | } |
68 | |
69 | public String toString() {
|
70 | ret pcdr == null ? pcar + "." : pcar + ".\n" + pcdr; |
71 | } |
72 | } |
73 | |
74 | class Trail {
|
75 | Var tcar; |
76 | Trail tcdr; |
77 | |
78 | *(Var *tcar, Trail *tcdr) {}
|
79 | } |
80 | |
81 | Trail sofar = null; |
82 | |
83 | Trail Trail_Note() { return sofar; }
|
84 | void Trail_Push(Var x) { sofar = new Trail(x, sofar); }
|
85 | void Trail_Undo(Trail whereto) {
|
86 | for (; sofar != whereto; sofar = sofar.tcdr) |
87 | sofar.tcar.reset(); |
88 | } |
89 | |
90 | static class TermVarMapping {
|
91 | new L<Var> vars; |
92 | |
93 | *(L<Var> *vars) {}
|
94 | *(Var... vars) { this.vars.addAll(asList(vars)); }
|
95 | |
96 | void showanswer() {
|
97 | print("TRUE.");
|
98 | for (Var v : vars) |
99 | print(" " + v.getName() + " = " + v);
|
100 | } |
101 | } |
102 | |
103 | class Goal {
|
104 | Lisp car; |
105 | Goal cdr; |
106 | |
107 | *(Lisp *car, Goal *cdr) {}
|
108 | *(Lisp *car) {}
|
109 | |
110 | public String toString() {
|
111 | ret cdr == null ? car.toString() : car + "; " + cdr; |
112 | } |
113 | |
114 | void solve(Program p, int level, TermVarMapping map) {
|
115 | if (showStuff) |
116 | print(indent(level) + "solve@" + level + ": " + this); |
117 | |
118 | for (Program q = p; q != null; q = q.pcdr) {
|
119 | Trail t = Trail_Note(); |
120 | Clause c = q.pcar.copy(); |
121 | Trail_Undo(t); |
122 | if (showStuff) |
123 | print(indent(level) + " try:" + c); |
124 | if (unify(car, c.head)) {
|
125 | Goal gdash = c.body == null ? cdr : c.body.append(cdr); |
126 | if (gdash == null) |
127 | map.showanswer(); |
128 | else |
129 | gdash.solve(p, level+1, map); |
130 | } else |
131 | if (showStuff) |
132 | print(indent(level) + " nomatch."); |
133 | Trail_Undo(t); |
134 | } |
135 | } |
136 | |
137 | Goal copy() {
|
138 | return new Goal(copy2(car), |
139 | cdr == null ? null : cdr.copy()); |
140 | } |
141 | |
142 | Goal append(Goal l) {
|
143 | return new Goal(car, cdr == null ? null : cdr.append(l)); |
144 | } |
145 | |
146 | } // class Goal |
147 | |
148 | boolean unify(Lisp thiz, Lisp t) {
|
149 | if (thiz instanceof Var) { // TermVar::unify
|
150 | Var v = cast thiz; |
151 | if (v.instance != v) |
152 | return unify(v.instance, t); |
153 | Trail_Push(v); |
154 | v.instance = t; |
155 | return true; |
156 | } |
157 | |
158 | // TermCons::unify |
159 | return unify2(t, thiz); |
160 | } |
161 | |
162 | boolean unify2(Lisp thiz, Lisp t) {
|
163 | if (thiz instanceof Var) |
164 | return unify(thiz, t); |
165 | |
166 | int arity = thiz.size(); |
167 | if (neq(thiz.head, t.head) || arity != t.size()) |
168 | return false; |
169 | for (int i = 0; i < arity; i++) |
170 | if (!unify(thiz.get(i), t.get(i))) |
171 | return false; |
172 | return true; |
173 | } |
174 | |
175 | Lisp copy(Lisp thiz) {
|
176 | if (thiz instanceof Var) {
|
177 | Var v = cast thiz; |
178 | if (v.instance == v) {
|
179 | Trail_Push(v); |
180 | v.instance = new Var(); |
181 | } |
182 | return v.instance; |
183 | } |
184 | |
185 | ret copy2(thiz); |
186 | } |
187 | |
188 | Lisp newTermCons(Lisp t) {
|
189 | Lisp l = new Lisp(t.head); |
190 | for (Lisp arg : t) |
191 | l.add(copy(arg)); |
192 | ret l; |
193 | } |
194 | |
195 | Lisp copy2(Lisp thiz) {
|
196 | return newTermCons(thiz); |
197 | } |
198 | |
199 | Var newVar() {
|
200 | ret new Var; |
201 | } |
202 | |
203 | Var newVar(S name) {
|
204 | ret new Var(name); |
205 | } |
206 | |
207 | Clause clause(Lisp head, Goal body) {
|
208 | ret prologify(new Clause(head, body)); |
209 | } |
210 | |
211 | Clause clause(Lisp head) {
|
212 | ret prologify(new Clause(head)); |
213 | } |
214 | |
215 | Clause clause(Lisp head, Lisp body) {
|
216 | ret clause(head, new Goal(body)); |
217 | } |
218 | |
219 | Lisp prologify(Lisp term) {
|
220 | ret prologify(term, new HashMap); |
221 | } |
222 | |
223 | Clause prologify(Clause c) {
|
224 | new HashMap vars; |
225 | ret new Clause( |
226 | prologify(c.head, vars), |
227 | prologify(c.body, vars)); |
228 | } |
229 | |
230 | Goal prologify(Goal goal, Map<S, Var> vars) {
|
231 | if (goal == null) ret null; |
232 | ret new Goal( |
233 | prologify(goal.car, vars), |
234 | prologify(goal.cdr, vars)); |
235 | } |
236 | |
237 | Lisp prologify(Lisp term, Map<S, Var> vars) {
|
238 | if (term == null) ret null; |
239 | if (snlIsVar(term)) {
|
240 | Var v = vars.get(term.head); |
241 | if (v == null) |
242 | vars.put(term.head, v = newVar(term.head)); |
243 | ret v; |
244 | } else {
|
245 | Lisp l = new Lisp(term.head); |
246 | for (Lisp arg : term) |
247 | l.add(prologify(arg, vars)); |
248 | ret l; |
249 | } |
250 | } |
251 | |
252 | L<Var> findVars(Lisp term) {
|
253 | new IdentityHashMap<Var, Boolean> map; |
254 | findVars(term, map); |
255 | ret asList(map.keySet()); |
256 | } |
257 | |
258 | void findVars(Lisp term, IdentityHashMap<Var, Boolean> map) {
|
259 | if (term instanceof Var) |
260 | map.put((Var) term, Boolean.TRUE); |
261 | else |
262 | for (Lisp arg : term) |
263 | findVars(arg, map); |
264 | } |
265 | |
266 | static Map<S, Var> makeVarMap(L<Var> vars) {
|
267 | new HashMap<S, Var> map; |
268 | for (Var v : vars) |
269 | map.put(v.getName(), v); |
270 | ret map; |
271 | } |
272 | |
273 | // Executor stack entry |
274 | static class Entry {
|
275 | Goal goal; |
276 | Program q; |
277 | Trail trail; |
278 | boolean trailSet; |
279 | |
280 | *(Program *q, Goal *goal) {}
|
281 | } |
282 | |
283 | Executor executor; |
284 | |
285 | Executor newExecutor(Program p, Goal goal) {
|
286 | if (executor != null) fail("Already got an executor");
|
287 | ret executor = new Executor(p, goal); |
288 | } |
289 | |
290 | // a stackless, step-based executor for Prolog queries |
291 | // only one instance per Prolog instance! |
292 | class Executor {
|
293 | Program p; |
294 | new L<Entry> stack; |
295 | |
296 | *(Program *p, Goal goal) {
|
297 | stack.add(new Entry(p, goal)); |
298 | } |
299 | |
300 | int level() {
|
301 | ret l(stack)-1; |
302 | } |
303 | |
304 | boolean done() {
|
305 | ret empty(stack); |
306 | } |
307 | |
308 | boolean step() {
|
309 | if (done()) fail("done!"); // safety
|
310 | |
311 | Entry e = last(stack); |
312 | |
313 | if (e.trailSet) {
|
314 | Trail_Undo(e.trail); |
315 | e.trailSet = false; |
316 | } |
317 | |
318 | if (e.q == null) { // program loop ends
|
319 | removeLast(stack); |
320 | ret false; |
321 | } |
322 | |
323 | if (showStuff) |
324 | print(indent(level()) + "solve@" + level() + ": " + e.goal); |
325 | |
326 | // now in program loop |
327 | |
328 | e.trail = Trail_Note(); |
329 | e.trailSet = true; |
330 | Clause c = e.q.pcar.copy(); |
331 | e.q = e.q.pcdr; // next clause in program |
332 | Trail_Undo(e.trail); |
333 | if (showStuff) |
334 | print(indent(level()) + " try:" + c); |
335 | if (unify(e.goal.car, c.head)) {
|
336 | Goal gdash = c.body == null ? e.goal.cdr : c.body.append(e.goal.cdr); |
337 | if (gdash == null) |
338 | ret true; |
339 | else {
|
340 | stack.add(new Entry(p, gdash)); |
341 | ret false; |
342 | } |
343 | } else |
344 | if (showStuff) |
345 | print(indent(level()) + " nomatch."); |
346 | |
347 | ret false; |
348 | } |
349 | } |
350 | |
351 | } // class Prolog |
352 | |
353 | p {
|
354 | new Prolog p; |
355 | |
356 | /* A sample test program: append */ |
357 | |
358 | S at_app = "app"; |
359 | S at_cons = "cons"; |
360 | Lisp f_nil = lisp("nil");
|
361 | Lisp f_1 = lisp("1");
|
362 | Lisp f_2 = lisp("2");
|
363 | Lisp f_3 = lisp("3");
|
364 | |
365 | // Try making a clause from pure Lisp |
366 | |
367 | Lisp lhs1 = lisp("app", "nil", "X", "X");
|
368 | Prolog.Clause c1 = p.clause(lhs1); |
369 | print("c1: " + structure_seen(c1));
|
370 | |
371 | Lisp rhs2 = lisp(at_app, "L", "M", "N"); |
372 | Lisp lhs2 = lisp(at_app, lisp(at_cons, "X", "L"), |
373 | "M", |
374 | lisp(at_cons, "X", "N")); |
375 | |
376 | Prolog.Clause c2 = p.clause(lhs2, rhs2); |
377 | |
378 | // Test prologify |
379 | |
380 | Lisp rhs3pre = lisp("app", "I", "J",
|
381 | lisp("cons", "1",
|
382 | lisp("cons", "2",
|
383 | lisp("cons", "3", "nil"))));
|
384 | |
385 | Lisp rhs3 = p.prologify(rhs3pre); |
386 | L<Prolog.Var> vars = p.findVars(rhs3); |
387 | print("Vars: " + structure_seen(vars));
|
388 | Map<S, Prolog.Var> varMap = Prolog.makeVarMap(vars); |
389 | Prolog.Var v_i = varMap.get("I");
|
390 | Prolog.Var v_j = varMap.get("J");
|
391 | assertNotNull(v_i); |
392 | assertNotNull(v_j); |
393 | Prolog.Goal g1 = p.new Goal(rhs3); |
394 | |
395 | Prolog.Program test_p = new Prolog.Program(c1, c2); |
396 | Prolog.Program test_p2 = new Prolog.Program(c2, c1); |
397 | |
398 | Prolog.TermVarMapping var_name_map = new Prolog.TermVarMapping(v_i, v_j); |
399 | |
400 | /* |
401 | print("=======Append with normal clause order:");
|
402 | print(test_p); |
403 | print(); |
404 | g1.solve(test_p, 0, var_name_map); |
405 | print(); |
406 | print("=======Append with reversed normal clause order:");
|
407 | print(test_p2); |
408 | print(); |
409 | g1.solve(test_p2, 0, var_name_map); |
410 | */ |
411 | |
412 | p.showStuff = true; |
413 | Prolog.Executor e = p.newExecutor(test_p, g1); |
414 | int safety = 0; |
415 | while (!e.done() && ++safety < 1000) {
|
416 | if (e.step()) {
|
417 | print("Solution at iteration " + safety + ":");
|
418 | var_name_map.showanswer(); |
419 | } |
420 | } |
421 | print("Steps: " + safety);
|
422 | } |
Began life as a copy of #1002820
download show line numbers debug dex old transpilations
Travelled to 13 computer(s): aoiabmzegqzx, bhatertpkbcr, cbybwowwnfue, cfunsshuasjs, gwrvuhgaqvyk, ishqpsrjomds, lpdgvwnxivlt, mqqgnosmbjvj, pyentgdyhuwx, pzhvpgtvlbxg, tslmcundralx, tvejysmllsmz, vouqrxazstgt
No comments. add comment
| Snippet ID: | #1002823 |
| Snippet name: | Prolog Interpreter (with executor, developing) |
| Eternal ID of this version: | #1002823/1 |
| Text MD5: | 4be912e0b0bb18aa752807baab4f4ffa |
| Transpilation MD5: | da2062398da412689a5d19828683380d |
| Author: | stefan |
| Category: | javax |
| Type: | JavaX source code |
| Public (visible to everyone): | Yes |
| Archived (hidden from active list): | No |
| Created/modified: | 2016-02-29 01:08:30 |
| Source code size: | 9732 bytes / 422 lines |
| Pitched / IR pitched: | No / No |
| Views / Downloads: | 1298 / 1357 |
| Referenced in: | [show references] |