Not logged in.  Login/Logout/Register | List snippets | | Create snippet | Upload image | Upload data

1126
LINES

< > BotCompany Repo | #1002884 // class Prolog with shared clauses (LIVE)

JavaX fragment (include)

1  
// do NOT initialize in static field
2  
static final class Prolog {
3  
  boolean upperCaseVariables = false; // true for SNL, false for NL
4  
  long varCount;
5  
  boolean showStuff;
6  
  new L<Entry> stack;
7  
  Trail sofar = null;
8  
  new L<Clause> program;
9  
  new L<L<Clause>> programByArity;
10  
  long steps;
11  
  L<S> log;
12  
  int maxLogSize = 1000; // lines
13  
  int maxLevel = 10000; // max stack size
14  
  int timeLimit = 20; // 20 seconds
15  
  long startTime;
16  
  boolean allowEval = true;
17  
  int defaultRewriteMax = 100;
18  
  
19  
  // name of theory to memorize stuff to
20  
  S outTheory;
21  
  
22  
  // stats
23  
  int maxLevelSeen; // maximum stack level reached during computation
24  
  long topUnifications, exceptions;
25  
26  
  static interface INative {
27  
    public boolean yo(Prolog p, Lisp term);
28  
  }
29  
30  
  // CENTRAL method for comparing terms
31  
  // change for modifying case-sensitivity.
32  
  boolean headeq(S a, S b) {
33  
    ret isQuoted (a) ? eq(a, b) : eqic(a, b);
34  
  }
35  
  
36  
  static class Var extends Lisp {
37  
    long id;
38  
    Lisp instance;
39  
    
40  
    *(S name) {
41  
      super(name);
42  
      instance = this;
43  
    }
44  
    
45  
    *(long id) {
46  
      super("___");
47  
      this.id = id;
48  
      instance = this;
49  
    }
50  
    
51  
    void reset() { instance = this; }
52  
    
53  
    public String toString() {
54  
      if (instance != this)
55  
        ret instance.toString();
56  
      ret prettyName();
57  
    }
58  
    
59  
    S prettyName() {
60  
      ret isUserVar() ? getName() : "_" + id;
61  
    }
62  
    
63  
    boolean isUserVar() {
64  
      ret id == 0;
65  
    }
66  
    
67  
    S getName() {
68  
      ret head;
69  
    }
70  
    
71  
    Lisp getValue() {
72  
      Lisp l = instance;
73  
      while (l instanceof Var) {
74  
        Var v = cast l;
75  
        if (v.instance == v)
76  
          ret v;
77  
        l = v.instance;
78  
      }
79  
      ret l;
80  
    }
81  
  }
82  
  
83  
  static class LCollector extends Lisp {
84  
    new L<Lisp> solutions;
85  
    
86  
    *() { super("___"); }
87  
    
88  
    public S toString() {
89  
      ret "<collector>";
90  
    }
91  
  }
92  
  
93  
  static class Clause {
94  
    Lisp head;
95  
    S loadedFrom; // theory name
96  
    boolean used;
97  
    
98  
    // Either body or nat will be set (or none)
99  
    INative nat;
100  
    Goal body;
101  
    
102  
    *(Lisp *head, INative *nat) {}
103  
    *(Lisp *head, Goal *body) {}
104  
    *(Lisp *head, Goal *body, INative *nat) {}
105  
    *(Lisp *head) {}
106  
    
107  
    Clause copy(Prolog p) {
108  
      ret new Clause(p.copy(head), body == null ? null : body.copy(p), nat);
109  
    }
110  
    
111  
    public String toString() {
112  
      //ret head + " :- " + (body == null ? "true" : body);
113  
      ret nat != null ? head + " :- native" :
114  
        (body == null ? head.toString() : head + " :- " + body);
115  
    }
116  
    
117  
    // fails if clause is a rule
118  
    Lisp getStatement() {
119  
      if (nat != null || body != null) fail("Not a statement: " + this);
120  
      ret head;
121  
    }
122  
  }
123  
  
124  
  class Trail {
125  
    Var tcar;
126  
    Trail tcdr;
127  
    
128  
    *(Var *tcar, Trail *tcdr) {}
129  
  }
130  
  
131  
  Trail Trail_Note() { return sofar; }
132  
  void Trail_Set(Var x, Lisp value) {
133  
    sofar = new Trail(x, sofar);
134  
    x.instance = value;
135  
    /*if (showStuff)
136  
      log(indent(level()) + "  Push " + x.prettyName() + " (" + x + ")");*/
137  
  }
138  
  void Trail_Undo(Trail whereto) {
139  
    for (; sofar != whereto; sofar = sofar.tcdr) {
140  
      /*if (showStuff)
141  
        log(indent(level()) + "  Resetting variable " + sofar.tcar.prettyName() + " (" + sofar.tcar + ")");*/
142  
      sofar.tcar.reset();
143  
    }
144  
  }
145  
  
146  
  static class TermVarMapping {
147  
    new L<Var> vars;
148  
    
149  
    *(L<Var> *vars) {}
150  
    *(Var... vars) { this.vars.addAll(asList(vars)); }
151  
    
152  
    void showanswer() {
153  
      print("TRUE.");
154  
      for (Var v : vars)
155  
        print("  " + v.prettyName() + " = " + v);
156  
    }
157  
  }
158  
      
159  
  static class Goal {
160  
    Lisp car;
161  
    Goal cdr;
162  
    
163  
    *(Lisp *car, Goal *cdr) {}
164  
    *(Lisp *car) {}
165  
    
166  
    public String toString() {
167  
      ret cdr == null ? car.toString() : car + "; " + cdr;
168  
    }
169  
    
170  
    Goal copy(Prolog p) {
171  
      return new Goal(p.copy(car),
172  
        cdr == null ? null : cdr.copy(p));
173  
    }
174  
    
175  
    Goal append(Goal l) {
176  
      return new Goal(car, cdr == null ? l : cdr.append(l));
177  
    }
178  
    
179  
  } // class Goal
180  
  
181  
  boolean unifyOrRollback(Lisp a, Lisp b) {
182  
    Trail t = Trail_Note();
183  
    if (unify(a, b)) ret true;
184  
    Trail_Undo(t);
185  
    ret false;
186  
  }
187  
  
188  
  boolean unify(Lisp thiz, Lisp t) {
189  
    if (thiz == null) fail("thiz=null");
190  
    if (t == null) fail("t=null");
191  
    
192  
    if (thiz instanceof Var) { // TermVar::unify
193  
      Var v = cast thiz;
194  
      if (v.instance != v)
195  
        return unify(v.instance, t);
196  
      Trail_Set(v, t);
197  
      return true;
198  
    }
199  
    
200  
    // TermCons::unify
201  
    return unify2(t, thiz);
202  
  }
203  
  
204  
  boolean unify2(Lisp thiz, Lisp t) { 
205  
    if (thiz instanceof Var)
206  
      return unify(thiz, t);
207  
   
208  
    int arity = thiz.size();
209  
    if (!headeq(thiz.head, t.head) || arity != t.size())
210  
      return false;
211  
    for (int i = 0; i < arity; i++)
212  
      if (!unify(thiz.get(i), t.get(i)))
213  
        return false;
214  
    return true;
215  
  }
216  
  
217  
  Lisp copy(Lisp thiz) {
218  
    if (thiz instanceof Var) {
219  
      Var v = cast thiz;
220  
      if (v.instance == v) {
221  
        Trail_Set(v, newVar());
222  
      }
223  
      return v.instance;
224  
    }
225  
    
226  
    // copy2 (copy non-var)
227  
    Lisp l = new Lisp(thiz.head);
228  
    for (Lisp arg : thiz)
229  
      l.add(copy(arg));
230  
    ret l;
231  
  }
232  
  
233  
  Var newVar() {
234  
    ret new Var(++varCount);
235  
  }
236  
  
237  
  Var newVar(S name) {
238  
    ret new Var(name);
239  
  }
240  
  
241  
  Clause clause(Lisp head, Goal body) {
242  
    ret prologify(new Clause(head, body));
243  
  }
244  
  
245  
  // primary processor for freshly parsed rules
246  
  
247  
  Clause clause(Lisp rule) {
248  
    L<Lisp> ops = snlSplitOps(rule);
249  
    /*if (showStuff)
250  
      log("clause(Lisp): " + rule + " => " + structure(ops)); */
251  
      
252  
    // expand rule shortcuts (say/memorize/...)
253  
    
254  
    for (int i = 0; i < l(ops)-1; i++)
255  
      if (ops.get(i).is("memorize *"))
256  
        ops.set(i, lisp("and *", lisp("[]", "memorize", ops.get(i))));
257  
    
258  
    if (!empty(ops) && last(ops).is("say *"))
259  
      ops.set(l(ops)-1, lisp("then *", lisp("[]", "say", last(ops).get(0))));
260  
      
261  
    rule = empty(ops) ? rule : snlJoinOps(ops);
262  
263  
    if (allowEval)
264  
      rule = new EvalTransform().evalTransformRule(rule);
265  
      
266  
    ops = snlSplitOps(rule);
267  
    if (!empty(ops) && last(ops).is("then *")) {
268  
      Lisp head = last(ops).get(0);
269  
      Goal goal = null;
270  
      
271  
      // TODO: check the actual words (if/and/...)
272  
      for (int i = l(ops)-2; i >= 0; i--)
273  
        goal = new Goal(ops.get(i).get(0), goal);
274  
        
275  
      ret clause(head, goal);
276  
    } else
277  
      ret clause(rule, (Lisp) null);
278  
  }
279  
  
280  
  Clause clause(Lisp head, Lisp body) {
281  
    ret clause(head, body == null ? null : new Goal(body));
282  
  }
283  
  
284  
  Lisp prologify(Lisp term) {
285  
    ret prologify(term, new HashMap);
286  
  }
287  
  
288  
  Clause prologify(Clause c) {
289  
    new HashMap vars;
290  
    c = new Clause(
291  
      prologify(c.head, vars),
292  
      prologify(c.body, vars),
293  
      c.nat);
294  
    /*if (showStuff)
295  
      log("Clause made: " + structure_seen(c));*/
296  
    ret c;
297  
  }
298  
  
299  
  Goal prologify(Goal goal, Map<S, Var> vars) {
300  
    if (goal == null) ret null;
301  
    ret new Goal(
302  
      prologify(goal.car, vars),
303  
      prologify(goal.cdr, vars));
304  
  }
305  
  
306  
  // Note: only for un-prologified
307  
  boolean isVar(Lisp term) {
308  
    ret upperCaseVariables ? snlIsVar(term) : nlIsVar(term);
309  
  }
310  
  
311  
  // for prologified (e.g. in native clauses)
312  
  boolean isLiveVar(Lisp term) {
313  
    ret term instanceof Var;
314  
  }
315  
  
316  
  Lisp prologify(Lisp term, Map<S, Var> vars) {
317  
    if (term == null) ret null;
318  
    if (isVar(term)) {
319  
      Var v = vars.get(term.head);
320  
      if (v == null)
321  
        vars.put(term.head, v = newVar(term.head));
322  
      ret v;
323  
    } else {
324  
      Lisp l = new Lisp(term.head);
325  
      for (Lisp arg : term)
326  
        l.add(prologify(arg, vars));
327  
      ret l;
328  
    }
329  
  }
330  
  
331  
  L<Var> findVars(Goal g) {
332  
    new IdentityHashMap<Var, Boolean> map;
333  
    while (g != null) {
334  
      findVars(g.car, map);
335  
      g = g.cdr;
336  
    }
337  
    ret asList(map.keySet());
338  
  }
339  
  
340  
  L<Var> findVars(Lisp term) {
341  
    new IdentityHashMap<Var, Boolean> map;
342  
    findVars(term, map);
343  
    ret asList(map.keySet());
344  
  }
345  
  
346  
  void findVars(Lisp term, IdentityHashMap<Var, Boolean> map) {
347  
    if (term instanceof Var)
348  
      map.put((Var) term, Boolean.TRUE);
349  
    else
350  
      for (Lisp arg : term)
351  
        findVars(arg, map);
352  
  }
353  
  
354  
  static Map<S, Var> makeVarMap(L<Var> vars) {
355  
    new HashMap<S, Var> map;
356  
    for (Var v : vars)
357  
      map.put(v.getName(), v);
358  
    ret map;
359  
  }
360  
  
361  
  new L<Clause> emptyProgram;
362  
  
363  
  // Executor stack entry
364  
  class Entry {
365  
    Goal goal;
366  
    L<Clause> program; // full program or filtered by arity
367  
    int programIdx = 0;
368  
    Trail trail;
369  
    boolean trailSet;
370  
371  
    *(Goal *goal) {
372  
      Lisp car = resolve1(goal.car);
373  
      if (car instanceof Var) {
374  
        if (showStuff)
375  
          log("Weird: Goal is variable: " + car);
376  
        program = Prolog.this.program;
377  
      } else {
378  
        int arity = car.size();
379  
        program = arity >= l(programByArity) ? emptyProgram : programByArity.get(arity);
380  
        if (showStuff)
381  
          log(indent(level()) + "Goal arity " + arity + ": " + render(car) + ", have " + n2(program, "fact") + " of this arity");
382  
      }
383  
    }
384  
  }
385  
  
386  
  void start(S goal) {
387  
    start(nlParse(goal));
388  
  }
389  
  
390  
  void start(Lisp goal) {
391  
    start(new Goal(prologify(goal)));
392  
  }
393  
  
394  
  S render(Lisp term) {
395  
    ret nlUnparse(resolve(term));
396  
  }
397  
  
398  
  S render(Goal goal) {
399  
    ret goal == null ? "-" : render(goal.car);
400  
  }
401  
  
402  
  S render(Clause c) {
403  
    ret c == null ? "-" : render(c.head);
404  
  }
405  
  
406  
  // warning: doesn't prologify the goal
407  
  void start(Goal goal) {
408  
    if (showStuff)
409  
      log("Starting on goal: " + render(goal));
410  
    steps = 0;
411  
    stack = new L;
412  
    Trail_Undo(null);
413  
    stackAdd(new Entry(goal));
414  
    startTime = sysNow();
415  
  }
416  
  
417  
  void log(S s) {
418  
    if (log != null) {
419  
      if (l(log) == maxLogSize) {
420  
        log.add("[Log overflow]");
421  
        showStuff = false;
422  
      }
423  
      else if (l(log) < maxLogSize)
424  
        log.add(s);
425  
    } else if (showStuff)
426  
      print("prolog: " + s);
427  
  }
428  
    
429  
  int level() {
430  
    ret l(stack)-1;
431  
  }
432  
  
433  
  boolean done() {
434  
    boolean result = empty(stack);
435  
    /*if (showStuff && result)
436  
      log("Done with goal!");*/
437  
    ret result;
438  
  }
439  
440  
  boolean gnext(Goal g) {  
441  
    Goal gdash = g.cdr;
442  
    if (gdash == null) {
443  
      if (showStuff)
444  
        log("gnext true");
445  
      ret true;
446  
    } else {
447  
      stackAdd(new Entry(gdash));
448  
      ret false;
449  
    }
450  
  }
451  
  
452  
  void stackPop() {
453  
    Entry e = popLast(stack);
454  
    if (e.trailSet)
455  
      Trail_Undo(e.trail);
456  
  }
457  
  
458  
  void backToCutPoint(int n) {
459  
    if (showStuff)
460  
      log("back to cut point " + n);
461  
    while (l(stack) >= n) {
462  
      if (showStuff)
463  
        log("cut: dropping " + structure(last(stack).goal));
464  
      stackPop();
465  
    }
466  
  }
467  
468  
  boolean step() {
469  
    ping();
470  
    if (done()) fail("done!"); // safety
471  
    if (sysNow() >= startTime+timeLimit*1000)
472  
      fail("time limit reached: " + timeLimit + " s");
473  
    
474  
    ++steps;
475  
    Entry e = last(stack);
476  
    
477  
    if (e.trailSet) {
478  
      Trail_Undo(e.trail);
479  
      e.trailSet = false;
480  
    }
481  
    
482  
    e.trail = Trail_Note();
483  
    e.trailSet = true;
484  
    
485  
    // cut operator - suceeds first time
486  
    if (e.goal.car.is("!", 1)) {
487  
      if (showStuff)
488  
        log("cut " + e.programIdx + ". " + structure(e.goal));
489  
      if (e.programIdx == -1) {
490  
        ++e.programIdx;
491  
        ret gnext(e.goal);
492  
      } else if (e.programIdx == 0) {
493  
        ++e.programIdx;
494  
        // fails 2nd time and cuts
495  
        //e.goal.car.head = "false"; // super-hack :D
496  
        backToCutPoint(parseInt(e.goal.car.get(0).raw()));
497  
        ret false;
498  
      } else {
499  
        stackPop();
500  
        ret false;
501  
      }
502  
    }
503  
504  
    if (e.programIdx >= l(e.program)) { // program loop ends
505  
      removeLast(stack);
506  
      ret false;
507  
    }
508  
      
509  
    // now in program loop - get next clause to try
510  
    
511  
    Clause cc = e.program.get(e.programIdx);
512  
    ++e.programIdx;
513  
    
514  
    // copy the clause; synchronize on it because it may come
515  
    // from a shared Prolog interpreter
516  
    
517  
    Clause c;
518  
    synchronized(cc) {
519  
      c = cc.copy(this);
520  
      Trail_Undo(e.trail);
521  
    }
522  
    
523  
    S text = null;
524  
    if (showStuff)
525  
      //text = "  Goal: " + e.goal + ". Got clause: " + c;
526  
      text = "Got clause: " + render(c);
527  
    ++topUnifications;
528  
    if (unify(e.goal.car, c.head)) {
529  
      cc.used = true; // mark clause used
530  
      if (showStuff) {
531  
        log(indent(level()) + text);
532  
        log(indent(level()) + "  Clause unifies to: " + render(c));
533  
      }
534  
      Goal gdash;
535  
      if (c.nat != null) {
536  
        if (showStuff)
537  
          log(indent(level()) + " Clause is native.");
538  
        if (!c.nat.yo(this, c.head)) {
539  
          if (showStuff)
540  
            log(indent(level()) + "Native clause fails");
541  
          ret false;
542  
        }
543  
        gdash = e.goal.cdr;
544  
      } else
545  
        gdash = c.body == null ? e.goal.cdr : resolveCut(c.body).append(e.goal.cdr);
546  
      if (showStuff)
547  
        log(indent(level()) + "  gdash: " + render(gdash));
548  
        
549  
      if (gdash == null) {
550  
        if (showStuff)
551  
          log("SUCCESS!");
552  
        ret true;
553  
      } else {
554  
        Entry e2 = new Entry(gdash);
555  
        /*if (showStuff)
556  
          log(indent(level()) + "New goal: " + render(gdash));*/
557  
        stackAdd(e2);
558  
        ret false;
559  
      }
560  
    } /*else
561  
      if (showStuff)
562  
        log(indent(level()) + "No match for clause.");*/
563  
        
564  
    ret false;
565  
  }
566  
  
567  
  // resolve cut in clause body
568  
  Goal resolveCut(Goal g) {
569  
    if (g.car.is("!", 0))
570  
      ret fixCut(g);
571  
    if (g.cdr == null)
572  
      ret g;
573  
    ret new Goal(g.car, resolveCut(g.cdr));
574  
  }
575  
  
576  
  // note stack length in cut
577  
  Goal fixCut(Goal g) {
578  
    ret new Goal(lisp("!", lstr(stack)), g.cdr); // max. one cut per clause
579  
  }
580  
  
581  
  void stackAdd(Entry e) {
582  
    stack.add(e);
583  
    int n = l(stack);
584  
    if (n > maxLevel) fail("Maximum stack level reached: " + n);
585  
    if (n > maxLevelSeen) maxLevelSeen = n;
586  
  }
587  
  
588  
  void addClause(S text) {
589  
    addClause(nlParse(text));
590  
  }
591  
  
592  
  void addClauses(L<Lisp> l) {
593  
    for (Lisp clause : unnull(l))
594  
      addClause(clause);
595  
  }
596  
  
597  
  // synonym of addClause
598  
  void addStatement(S text) {
599  
    addClause(text);
600  
  }
601  
  
602  
  void addClause(Lisp c) {
603  
    addClause(clause(c));
604  
  }
605  
  
606  
  void addClause(Lisp c, S name) {
607  
    addClause(clause(c), name);
608  
  }
609  
  
610  
  void addClause(Clause c) {
611  
    addClause(c, null);
612  
  }
613  
  
614  
  void addClause(Clause c, S name) {
615  
    c.loadedFrom = name;
616  
    program.add(c);
617  
    if (c.head instanceof Var) {
618  
      if (showStuff)
619  
        log("WARNING: Clause is variable, will not be executed right now: " + c);
620  
      ret;
621  
    }
622  
    int arity = c.head.size();
623  
    growArityList(arity);
624  
    programByArity.get(arity).add(c);
625  
  }
626  
  
627  
  void growArityList(int arity) {
628  
    while (arity >= l(programByArity))
629  
      programByArity.add(new L);
630  
  }
631  
  
632  
  boolean canSolve(Lisp goal) {
633  
    ret canSolve(new Goal(prologify(goal)));
634  
  }
635  
  
636  
  boolean canSolve(S goal) {
637  
    ret canSolve(nlParse(goal));
638  
  }
639  
  
640  
  boolean canSolve(Goal goal) {
641  
    start(goal);
642  
    while (!done())
643  
      if (step())
644  
        ret true;
645  
    ret false;
646  
  }
647  
  
648  
  S solveAsText(S goal, S var) {
649  
    Map<S, Lisp> solution = solve(goal);
650  
    ret solution == null ? null : nlUnparse(solution.get(addPrefix("$", var)));
651  
  }
652  
  
653  
  // return variable map or null if unsolved
654  
  Map<S, Lisp> solve(Lisp goal) {
655  
    start(goal);
656  
657  
    ret nextSolution();
658  
  }
659  
  
660  
  Map<S, Lisp> solve(S text) {
661  
    ret solve(nlParse(text));
662  
  }
663  
  
664  
  Map<S, Lisp> getUserVarMap() {
665  
    Goal g = stack.get(0).goal;
666  
    if (showStuff)
667  
      log("UserVarMap goal: " + g);
668  
    new HashMap<S, Lisp> map;
669  
    for (Var v : findVars(g))
670  
      if (v.isUserVar()) {
671  
        Lisp term = resolve(v);
672  
        boolean ok = !(term instanceof Var) || (term != v && ((Var) term).isUserVar());
673  
        if (showStuff)
674  
          log("UserVarMap var " + v + " ok: " + ok);
675  
        if (ok)
676  
          map.put(v.getName(), term);
677  
      }
678  
    ret map;
679  
  }
680  
  
681  
  Map<S, Lisp> nextSolution() {
682  
    if (showStuff)
683  
      log("nextSolution");
684  
    int n = 0;
685  
    while (!done()) {
686  
      ++n;
687  
      if (step()) {
688  
        if (showStuff)
689  
          log("  solution found in step " + n);
690  
        ret getUserVarMap();
691  
      }
692  
    }
693  
    if (showStuff)
694  
      log("\nNo solution");
695  
    ret null;
696  
  }
697  
  
698  
  void code(S text) {
699  
    addTheory(text, "ad hoc");
700  
  }
701  
  
702  
  void addTheory(S text, S name) {
703  
    for (Clause c : parseProgram(text))
704  
      addClause(c, name);
705  
  }
706  
    
707  
  void addTheory(S text, Lisp tree) {
708  
    addTheory(text, tree, null);
709  
  }
710  
  
711  
  void addTheory(S text, Lisp tree, S name) {
712  
    for (Clause c : parseProgram(text, tree))
713  
      addClause(c, name);
714  
  }
715  
  
716  
  L<Clause> parseProgram(S text) {
717  
    ret parseProgram(text, nlParse(text));
718  
  }
719  
  
720  
  L<Clause> parseProgram(S text, Lisp tree) {
721  
    new L<Clause> l;
722  
    if (nlIsMultipleStatements(text))
723  
      for (Lisp part : tree)
724  
        l.add(clause(part));
725  
    else
726  
      l.add(clause(tree));
727  
    ret l;
728  
  }
729  
  
730  
  L<Lisp> parseStatements(S text) {
731  
    ret map(func(Clause c) { c.getStatement() }, parseProgram(text));
732  
  }
733  
    
734  
  // Resolve top-level only
735  
  Lisp resolve1(Lisp term) {
736  
    if (term instanceof Var)
737  
      ret ((Var) term).getValue();
738  
    ret term;
739  
  }
740  
  
741  
  // resolve all variables
742  
  Lisp resolve(Lisp term) {
743  
    term = resolve1(term);
744  
      
745  
    // smart recurse
746  
    for (int i = 0; i < term.size(); i++) {
747  
      Lisp l = term.get(i);
748  
      Lisp r = resolve(l);
749  
      if (l != r) {
750  
        Lisp x = new Lisp(term.head);
751  
        for (int j = 0; j < i; j++)
752  
          x.add(term.get(j));
753  
        x.add(r);
754  
        for (i++; i < term.size(); i++)
755  
          x.add(resolve(term.get(i)));
756  
        ret x;
757  
      }
758  
    }
759  
    
760  
    ret term;
761  
  }
762  
  
763  
  // looks for a bodyless rule in the program that matches the term
764  
   // todo: eqic
765  
  boolean containsStatement(Lisp term) {
766  
    for (Clause c : program)
767  
      if (c.body == null && c.nat == null && eq(c.head, term))
768  
        ret true;
769  
    ret false;
770  
  }
771  
  
772  
  // closed == contains no variables
773  
  boolean isClosedTerm(Lisp term) {
774  
    if (term instanceof Var)
775  
      ret false;
776  
    else
777  
      for (Lisp arg : term)
778  
        if (!isClosedTerm(arg))
779  
          ret false;
780  
    ret true;
781  
  }
782  
  
783  
  void addNative(BaseNative n) {
784  
    addClause(prologify(new Clause(nlParse(n.pat), n)), "nat");
785  
  }
786  
  
787  
  void addNatives(BaseNative... l) {
788  
    for (BaseNative n : l)
789  
      addNative(n);
790  
  }
791  
    
792  
  void addNatives(L<BaseNative> l) {
793  
    for (BaseNative n : l)
794  
      addNative(n);
795  
  }
796  
  
797  
  L<Lisp> getStackTerms() {
798  
    new L<Lisp> l;
799  
    for (Entry e : stack)
800  
      l.add(e.goal.car);
801  
    ret l;
802  
  }
803  
  
804  
  void logOn() {
805  
    log = new L;
806  
    showStuff = true;
807  
  }
808  
809  
  static abstract class BaseNative implements INative {
810  
    S pat;
811  
    Prolog p;
812  
    Map<S, Lisp> m;
813  
    
814  
    *(S *pat) {}
815  
    
816  
    abstract boolean yo();
817  
    
818  
    public boolean yo(Prolog p, Lisp term) {
819  
      m = new HashMap;
820  
      this.p = p;
821  
      try {
822  
        // Terms are always resolved for native code!
823  
        ret nlMatch(pat, p.resolve(term), m) && yo();
824  
      } catch (Exception e) {
825  
        ++p.exceptions;
826  
        if (p.showStuff)
827  
          p.log("Exception in native class " + getClass().getName() + ": " + getStackTrace(e));
828  
        ret false;
829  
      } finally {
830  
        this.p = null;
831  
      }
832  
    }
833  
    
834  
    boolean unify(S var, Lisp term) {
835  
      ret set(var, term);
836  
    }
837  
    
838  
    boolean set(S var, Lisp term) {
839  
      Lisp v = m.get(var);
840  
      if (v == null)
841  
        fail("Variable " + var + " not found");
842  
      ret p.unify(v, term);
843  
    }
844  
845  
    boolean unifyForeign(IdentityHashMap<Lisp, S> varMap, Map<S, Lisp> solution) {
846  
      if (p.showStuff)
847  
        p.log("unifyForeign with: " + structure(solution));
848  
      for (Lisp v : varMap.keySet()) {
849  
        S name = varMap.get(v);
850  
        Lisp value = solution.get(name);
851  
        if (value == null) continue;
852  
        if (!p.unify(v, escapeVariables(value))) {
853  
          ret false; // rollback?
854  
        }
855  
      }
856  
      ret true;
857  
    }
858  
    
859  
    // fills varMap
860  
    Lisp exportVars(Lisp tree, IdentityHashMap<Lisp, S> varMap) {
861  
      if (tree instanceof Var) {
862  
        S name = varMap.get(tree);
863  
        if (name == null) {
864  
          name = "$_" + (varMap.size()+1);
865  
          varMap.put(tree, name);
866  
        }
867  
        ret lisp(name);
868  
      }
869  
      
870  
      Lisp lisp = new Lisp(tree.head);
871  
      for (Lisp sub : tree)
872  
        lisp.add(exportVars(sub, varMap));
873  
      ret lisp;
874  
    }
875  
    
876  
    // var = without $
877  
    Lisp get(S var) {
878  
      ret m.get(var);
879  
    }
880  
    
881  
    S unq(S var) {
882  
      ret m.get(var).unq();
883  
    }
884  
  } // BaseNative
885  
  
886  
  // Prolog constructor
887  
  *() {
888  
    addClause(lisp("true"));
889  
    addBaseNatives();
890  
  }
891  
  
892  
  *(Prolog p) {
893  
    copyProgramFrom(p);
894  
  }
895  
  
896  
  static class State {
897  
    Trail trail;
898  
    L<Entry> stack;
899  
  }
900  
  
901  
  State saveState() {
902  
    new State s;
903  
    s.trail = sofar;
904  
    s.stack = stack;
905  
    sofar = null;
906  
    ret s;
907  
  }
908  
  
909  
  void restoreState(State s) {
910  
    sofar = s.trail;
911  
    stack = s.stack;
912  
  }
913  
  
914  
  // auto-rewrite with all clauses in DB
915  
  L<Lisp> rewrite() {
916  
    ret rewriteWith(program, defaultRewriteMax);
917  
  }
918  
  
919  
  L<Lisp> rewrite(int max) {
920  
    ret rewriteWith(program, max);
921  
  }
922  
  
923  
  // Note: rewriteTheory is not searched for facts (only rules)
924  
  L<Lisp> rewriteWith(Prolog rewriteTheory) {
925  
    ret rewriteWith(rewriteTheory.program());
926  
  }
927  
  
928  
  L<Lisp> rewriteWith(L<Clause> rewriteTheory) {
929  
    ret rewriteWith(rewriteTheory, defaultRewriteMax);
930  
  }
931  
  
932  
  L<Lisp> rewriteWith(L<Clause> rewriteTheory, int max) {
933  
    new L<Lisp> l;
934  
    rewriteWithInto(rewriteTheory, limitedListCollector(l, max));
935  
    ret l;
936  
  }
937  
  
938  
  void rewriteInto(Collector<Lisp> collector) {
939  
    rewriteWithInto(program, collector);
940  
  }
941  
  
942  
  void rewriteWithInto(L<Clause> rewriteTheory, Collector<Lisp> collector) {
943  
    if (collector.full()) ret;
944  
    for (Prolog.Clause clause : rewriteTheory) {
945  
      if (clause.body == null) continue; // need conditions to rewrite anything
946  
      
947  
      // usual safe clause copying
948  
      
949  
      Trail t = Trail_Note();
950  
      Clause c;
951  
      synchronized(clause) {
952  
        c = clause.copy(this);
953  
        Trail_Undo(t);
954  
      }
955  
      
956  
      State state = saveState();
957  
      try {
958  
        start(c.body);
959  
        while (!done()) {
960  
          if (step()) {
961  
            Lisp term = resolve(c.head);
962  
            if (!isClosedTerm(term)) {
963  
              if (showStuff)
964  
                log("Not a closed term, skipping: " + term);
965  
              continue;
966  
            }
967  
            
968  
            if (containsStatement(term)) {
969  
              if (showStuff)
970  
                log("Statement exists, skipping: " + term);
971  
              continue;
972  
            }
973  
            
974  
            if (collector.contains(term))
975  
              continue;
976  
              
977  
            if (showStuff)
978  
              log("Found new statement: " + term);
979  
            collector.add(term);
980  
            if (collector.full()) {
981  
              log("Collector full");
982  
              break;
983  
            }
984  
          }
985  
        }
986  
      } finally {
987  
        restoreState(state);
988  
      }
989  
    }
990  
  }
991  
  
992  
  static class NewCollector extends BaseNative {
993  
    *() { super("$x = new collector"); }
994  
    
995  
    boolean yo() {
996  
      ret set("x", new LCollector);
997  
    }
998  
  }
999  
  
1000  
  class Save extends BaseNative {
1001  
    *() { super("saveTo($x, $collector)"); }
1002  
    
1003  
    boolean yo() {
1004  
      LCollector collector = (LCollector) get("collector");
1005  
      Lisp x = get("x");
1006  
      collector.solutions.add(resolve(x));
1007  
      ret true;
1008  
    }
1009  
  }
1010  
  
1011  
  static class Retrieve extends BaseNative {
1012  
    *() { super("$x = retrieve($collector)"); }
1013  
    
1014  
    boolean yo() {
1015  
      print("Retrieve: collector = " + get("collector"));
1016  
      LCollector collector = (LCollector) get("collector");
1017  
      ret set("x", nlList(collector.solutions));
1018  
    }
1019  
  }
1020  
  
1021  
  void addBaseNatives() {
1022  
    addNatives(new NewCollector, new Save, new Retrieve);
1023  
  }
1024  
  
1025  
  static Lisp escapeVariables(Lisp tree) {
1026  
    if (tree instanceof Var) {
1027  
      S name = ((Var) tree).prettyName();
1028  
      ret lisp("[]", "var", name);
1029  
    }
1030  
    
1031  
    Lisp lisp = new Lisp(tree.head);
1032  
    for (Lisp sub : tree)
1033  
      lisp.add(escapeVariables(sub));
1034  
    ret lisp;
1035  
  }
1036  
  
1037  
  boolean addClauseIfNew(Lisp clause, S name) {
1038  
    if (containsStatement(clause)) ret false;
1039  
    addClause(clause, name);
1040  
    ret true;
1041  
  }
1042  
  
1043  
  L<Clause> getUsedClauses() {
1044  
    new L<Clause> l;
1045  
    for (Clause c : program)
1046  
      if (c.used)
1047  
        l.add(c);
1048  
    ret l;
1049  
  }
1050  
  
1051  
  L<S> getUsedTheories() {
1052  
    new TreeSet<S> theories;
1053  
    for (Clause c : getUsedClauses())
1054  
      theories.add(or(c.loadedFrom, "?"));
1055  
    ret asList(theories);
1056  
  }
1057  
  
1058  
  void think(Lisp x) {
1059  
    addClauseIfNew(x, "thought");  // TODO: vars and stuff?
1060  
  }
1061  
  
1062  
  L<Lisp> getMemorizedStatements() {
1063  
    new L<Lisp> l;
1064  
    for (Clause c : program)
1065  
      if (c.body == null && c.nat == null && c.head.is("[]", 2) && c.head.get(0).is("memorized"))
1066  
        l.add(c.head);
1067  
    ret l;
1068  
  }
1069  
  
1070  
  S showClause(Clause c) {
1071  
    ret nlUnparse(c.head) + (c.body == null ? "" : " :- ...");
1072  
  }
1073  
  
1074  
  S showProgram() {
1075  
    new L<S> l;
1076  
    for (Clause c : program)
1077  
      l.add(showClause(c));
1078  
    ret n(l(program), "statement") + " " + slackSnippet(joinLines(l  ));
1079  
  }
1080  
  
1081  
  void copyProgramFrom(Prolog p) {
1082  
    program.addAll(p.program);
1083  
    growArityList(l(p.programByArity));
1084  
    for (int i = 0; i < l(p.programByArity); i++)
1085  
      programByArity.get(i).addAll(p.programByArity.get(i));
1086  
  }
1087  
  
1088  
  void addProgram(L<Clause> clauses, S loadedFrom) {
1089  
    for (Clause c : clauses)
1090  
      addClause(c, loadedFrom);
1091  
  }
1092  
  
1093  
  void addClauses(S text) {
1094  
    addTheory(text, fsI(programID()));
1095  
  }
1096  
  
1097  
  L<S> statementsAsText() {
1098  
    ret statementsAsText(0);
1099  
  }
1100  
  
1101  
  L<S> statementsAsText(int startingFromIndex) {
1102  
    new L<S> l;
1103  
    for (int i = startingFromIndex; i < l(program); i++) {
1104  
      Clause c = program.get(i);
1105  
      if (c.nat == null) {
1106  
        if (c.body != null)
1107  
          l.add("[if ? then " + nlUnparse(c.head, false) + "]"); // TODO
1108  
        else
1109  
          l.add(nlUnparse(c.head, false));
1110  
      }
1111  
    }
1112  
    ret l;
1113  
  }
1114  
  
1115  
  int mark() {
1116  
    ret l(program);
1117  
  }
1118  
  
1119  
  L<Clause> program() { ret program; }
1120  
  
1121  
  L<Lisp> rewriteAndSave() {
1122  
    L<Lisp> l = rewrite();
1123  
    addClauses(l);
1124  
    ret l;
1125  
  }
1126  
} // class Prolog

Author comment

Began life as a copy of #1002868

download  show line numbers  debug dex  old transpilations   

Travelled to 14 computer(s): aoiabmzegqzx, bhatertpkbcr, cbybwowwnfue, cfunsshuasjs, gwrvuhgaqvyk, ishqpsrjomds, lpdgvwnxivlt, mqqgnosmbjvj, onxytkatvevr, pyentgdyhuwx, pzhvpgtvlbxg, tslmcundralx, tvejysmllsmz, vouqrxazstgt

No comments. add comment

Snippet ID: #1002884
Snippet name: class Prolog with shared clauses (LIVE)
Eternal ID of this version: #1002884/13
Text MD5: 8b1ba8ab8d3ec6f37ccefe88809e7462
Author: stefan
Category: javax
Type: JavaX fragment (include)
Public (visible to everyone): Yes
Archived (hidden from active list): No
Created/modified: 2018-05-02 09:53:58
Source code size: 27122 bytes / 1126 lines
Pitched / IR pitched: No / No
Views / Downloads: 691 / 1929
Version history: 12 change(s)
Referenced in: [show references]