Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    text
    copied!<p>Some time ago I wrote a Prolog interpreter in C++ (really, my first C++ program), and followed a different approach instead of the (now nearly ubiquitous) WAM. Our teacher at course of languages and compilers construction talked about an ABC algorithm, and I implemented that (I goggled 'Prolog implementation ABC algorithm', <a href="https://cs.uwaterloo.ca/research/tr/1984/CS-84-47.pdf">here</a> a PDF that I found, but I don't know - yet) : here the core solver</p> <pre><code>//-------------------------- // evaluation core of query // use algorithm ABC // int IntlogExec::query(const Clause *q) { unsigned nc = 0; ProofStack::Env *pcn, *pfn; stkpos cn, fn; #define PCN (pcn = ps-&gt;get(cn)) #define PFN (pfn = ps-&gt;get(fn)) UnifyStack us(vs, ts); if (!q) goto C; fn = ps-&gt;push(STKNULL); PFN-&gt;vspos = vs-&gt;reserve(q-&gt;get_nvars()); pfn-&gt;trail = ts-&gt;curr_dim(); pfn-&gt;dbpos = 0; pfn-&gt;call = 0; // current query is empty? A: if (!q-&gt;get_body()) { // search untried calls A1: //fn = ps-&gt;curr_dim() - 1; fn = cn; ProofStack::Env *e = ps-&gt;get(fn); while (e-&gt;father != STKNULL) { if (tr &amp;&amp; e-&gt;call &amp;&amp; tr-&gt;exit(fn, e-&gt;call)) return -1; if (e-&gt;call &amp;&amp; !e-&gt;call-&gt;is_last()) break; e = ps-&gt;get(fn = e-&gt;father); } if (e-&gt;father == STKNULL) return 1; // set call to next untried brother cn = ps-&gt;push(PFN-&gt;father); PCN-&gt;call = pfn-&gt;call-&gt;next(); pcn-&gt;vspos = pfn-&gt;vspos; fn = pfn-&gt;father; } else { cn = ps-&gt;push(fn); PCN-&gt;call = q-&gt;get_body(); } A2: PFN; pcn-&gt;dbpos = 0; cc = pcn-&gt;call; if (nc++ == ncycle) { nc = 0; sighandler(); } // trace the call if (tr &amp;&amp; tr-&gt;call(cn, cc)) return -1; switch (cc-&gt;get_type()) { case CallData::BUILTIN: { BuiltIn *btp = cc-&gt;get_builtin(); pcn-&gt;trail = ts-&gt;curr_dim(); pcn-&gt;vspos = pfn-&gt;vspos; // if evaluate OK if (btp-&gt;eval(cc-&gt;args(), this, 0)) { // if (tr &amp;&amp; tr-&gt;exit(cn, cc)) // return -1; // if (btp-&gt;retry || pcn-&gt;call-&gt;last()) // goto A1; // pcn-&gt;call = pcn-&gt;call-&gt;next(); // goto A2; goto A1; } PCN; if (tr &amp;&amp; tr-&gt;fail(cn, pcn-&gt;call)) return -1; unbind(pcn-&gt;trail); } goto C1; case CallData::CUT: { stkpos gf = PFN-&gt;father; if ( gf != STKNULL &amp;&amp; pfn-&gt;call-&gt;is_last() &amp;&amp; pfn-&gt;call == pcn-&gt;call-&gt;next()) { // tail recursion optimization ProofStack::Env *pgf = ps-&gt;get(gf); pgf-&gt;vspos = pfn-&gt;vspos; ASSERT(!pcn-&gt;call-&gt;is_last()); slist_iter s(tmpt); ElemTmp *t; while ((t = (ElemTmp*)s.next()) != 0 &amp;&amp; t-&gt;spos &gt; fn) t-&gt;spos = fn; CallData *cproc = pcn-&gt;call; cn = ps-&gt;pop(cn - fn) - 1; PCN-&gt;call = cproc-&gt;next(); fn = pcn-&gt;father; goto A2; } pcn-&gt;trail = ts-&gt;curr_dim(); pcn-&gt;vspos = pfn-&gt;vspos; } goto A1; case CallData::DISJUNCT: // replace with catenated try pcn-&gt;vspos = pfn-&gt;vspos; pcn-&gt;trail = ts-&gt;curr_dim(); cn = ps-&gt;push(fn); PCN-&gt;call = cc-&gt;next(); // left side goto A2; case CallData::DBPRED: // initialize DB search pcn-&gt;dbpos = db-&gt;StartProc(cc-&gt;get_dbe()); // DB matching &amp; unification B: if (pcn-&gt;dbpos &amp;&amp; (q = pcn-&gt;dbpos-&gt;get()) != 0) { unsigned nvars = q-&gt;get_nvars(); pcn-&gt;vspos = vs-&gt;reserve(nvars); pcn-&gt;trail = ts-&gt;curr_dim(); /* if (!unify( pfn-&gt;vspos, cc-&gt;args(), pcn-&gt;vspos, q-&gt;h_args(), q-&gt;h_arity())) */ if (q-&gt;h_arity() &gt; 0) { TermArgs pa1 = cc-&gt;args(), pa2 = q-&gt;h_args(); us.clear(); for (int i = q-&gt;h_arity() - 1; i &gt; 0; i--) { UnifyStack::termPair *tp = us.push(); tp-&gt;t1 = pa1.getarg(i); tp-&gt;i1 = pfn-&gt;vspos; tp-&gt;t2 = pa2.getarg(i); tp-&gt;i2 = pcn-&gt;vspos; } us.check_overflow(); if (!us.work( pa1.getarg(0), pfn-&gt;vspos, pa2.getarg(0), pcn-&gt;vspos)) { // undo changes unbind(pcn-&gt;trail); vs-&gt;pop(nvars); // try next match pcn-&gt;dbpos = pcn-&gt;dbpos-&gt;succ(db); goto B; } } fn = cn; goto A; } break; default: ASSERT(0); } if (tr &amp;&amp; PCN-&gt;call &amp;&amp; tr-&gt;fail(cn, cc)) return -1; // backtracking C1: query_fail(ps-&gt;curr_dim() - cn); // resume top query C: cn = ps-&gt;curr_dim() - 1; unbind(PCN-&gt;trail); C2: if ((fn = pcn-&gt;father) == STKNULL) return 0; if ((cc = pcn-&gt;call) == 0) goto C1; switch (cc-&gt;get_type()) { case CallData::CUT: { // change satisfaction path up to father stkpos fvp = PFN-&gt;vspos; query_fail(cn - fn + 1); if ((cn = ps-&gt;curr_dim() - 1) != STKNULL) { unbind(PCN-&gt;trail); vs-&gt;pop(vs-&gt;curr_dim() - fvp); goto C2; } return 0; } case CallData::BUILTIN: { // check builtins retry BuiltIn *btp = cc-&gt;get_builtin(); if (btp-&gt;args &amp; BuiltIn::retry) { if (tr &amp;&amp; tr-&gt;redo(cn, cc)) return -1; // could be resatisfied pcn-&gt;trail = ts-&gt;curr_dim(); pcn-&gt;vspos = PFN-&gt;vspos; // if evaluate OK if (btp-&gt;eval(cc-&gt;args(), this, 1)) goto A1; } // failed goto C1; } case CallData::DISJUNCT: // evaluate right side if (tr &amp;&amp; tr-&gt;redo(cn, cc)) return -1; pcn-&gt;call = cc-&gt;get_orelse(); goto A2; case CallData::DBPRED: // a DB query node to retry if (tr) { // display REDOs (TBD) if (pcn-&gt;dbpos &amp;&amp; pcn-&gt;dbpos-&gt;succ(db) &amp;&amp; tr-&gt;redo(cn, cc)) return -1; } vs-&gt;pop(vs-&gt;curr_dim() - pcn-&gt;vspos); pcn-&gt;dbpos = pcn-&gt;dbpos-&gt;succ(db); PFN; goto B; default: ASSERT(0); } return -1; } </code></pre> <p>now I'm not very proud of that code: instead of ABC I ended up (by means of rather painful debugging) to an A-A1-A2 B C1-C-C2.</p> <p><em>edit</em>: I placed the complete interpreter <a href="https://github.com/CapelliC/IL">sources</a> in github. </p>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload