ref: 67e8b85a9f9b42acc896b44eb2ea1310e749f03a
dir: /eval.c/
#include <u.h> #include <libc.h> #include "dat.h" #include "fns.h" typedef struct Binding Binding; typedef struct Goal Goal; typedef struct Choicepoint Choicepoint; struct Binding { Rune *name; uvlong nr; /* Unique number for each clause. Every time a clause is used, it gets a new number. */ Term *value; Binding *next; }; struct Goal { Term *goal; Goal *next; }; struct Choicepoint { Goal *goalstack; Term *retryclause; Choicepoint *next; }; Goal *addgoals(Goal *, Term *); Term *findclause(Term *, Term *, Binding **); int unify(Term *, Term *, Binding **); int equalterms(Term *, Term *); void applybinding(Term *, Binding *); Goal *copygoals(Goal *); static uvlong clausenr; void evalquery(Term *database, Term *query) { Goal *goals = addgoals(nil, query); Choicepoint *choicestack = nil; clausenr = 0; while(goals != nil){ Term *dbstart; Term *goal; dbstart = database; Retry: goal = goals->goal; if(goal == nil){ goals = goals->next; continue; } print("Solving goal %S\n", prettyprint(goal)); /* Find a clause where the head unifies with the goal */ Binding *bindings = nil; Term *clause = findclause(dbstart, goal, &bindings); if(clause != nil){ if(clause->next != nil){ /* Add a choicepoint. Note we create a choicepoint every time, so there is room for improvement. */ Choicepoint *cp = malloc(sizeof(Choicepoint)); cp->goalstack = copygoals(goals); cp->next = choicestack; cp->retryclause = clause->next; choicestack = cp; } goals = goals->next; /* Apply bindings to all goals on the top of the stack, down to the "bodystart" goal */ Goal *g; for(g = goals; g != nil && g->goal != nil; g = g->next) applybinding(g->goal, bindings); /* Add clause body as goals, with bindings applied */ if(clause->tag == CompoundTerm && clause->arity == 2 && runestrcmp(clause->text, L":-") == 0){ Goal *bodystart = malloc(sizeof(Goal)); bodystart->goal = nil; bodystart->next = goals; goals = bodystart; Term *subgoal = copyterm(clause->children->next, nil); applybinding(subgoal, bindings); goals = addgoals(goals, subgoal); } }else{ if(choicestack == nil){ print("Fail\n"); return; } print("Backtracking...\n"); Choicepoint *cp = choicestack; choicestack = cp->next; /* freegoals(goals) */ goals = cp->goalstack; dbstart = cp->retryclause; goto Retry; } } print("Success.\n"); } Goal * addgoals(Goal *goals, Term *t) { if(t->tag == CompoundTerm && runestrcmp(t->text, L",") == 0 && t->arity == 2){ goals = addgoals(goals, t->children->next); goals = addgoals(goals, t->children); }else{ Goal *g = malloc(sizeof(Goal)); g->goal = t; g->next = goals; goals = g; } return goals; } Term * findclause(Term *database, Term *goal, Binding **bindings) { Term *clause; Term *head; for(; database != nil; database = database->next){ clausenr++; clause = copyterm(database, &clausenr); clause->next = database->next; if(clause->tag == CompoundTerm && runestrcmp(clause->text, L":-") == 0 && clause->arity == 2) head = clause->children; else head = clause; if(unify(head, goal, bindings)) return clause; } return nil; } int unify(Term *a, Term *b, Binding **bindings) { Term *leftstack; Term *rightstack; Term *left; Term *right; *bindings = nil; leftstack = copyterm(a, nil); rightstack = copyterm(b, nil); while(leftstack != nil && rightstack != nil){ left = leftstack; leftstack = left->next; right = rightstack; rightstack = right->next; if(equalterms(left, right)) continue; else if(left->tag == VariableTerm || right->tag == VariableTerm){ if(right->tag == VariableTerm){ Term *tmp = left; left = right; right = tmp; } if(runestrcmp(left->text, L"_") == 0) continue; /* _ doesn't introduce a new binding */ Binding *b = malloc(sizeof(Binding)); b->name = left->text; b->nr = left->clausenr; b->value = right; b->next = *bindings; *bindings = b; Term *t; for(t = leftstack; t != nil; t = t->next) applybinding(t, b); for(t = rightstack; t != nil; t = t->next) applybinding(t, b); Binding *tmpb; for(tmpb = *bindings; tmpb != nil; tmpb = tmpb->next) applybinding(tmpb->value, b); }else if(left->tag == CompoundTerm && right->tag == CompoundTerm && left->arity == right->arity && runestrcmp(left->text, right->text) == 0){ Term *leftchild = left->children; Term *rightchild = right->children; while(leftchild != nil && rightchild != nil){ Term *t1 = copyterm(leftchild, nil); t1->next = leftstack; leftstack = t1; leftchild = leftchild->next; Term *t2 = copyterm(rightchild, nil); t2->next = rightstack; rightstack = t2; rightchild = rightchild->next; } }else return 0; /* failure */ } return 1; } int equalterms(Term *a, Term *b) { /* Check that two non-compound terms are identical */ if(a->tag != b->tag) return 0; switch(a->tag){ case AtomTerm: case VariableTerm: case StringTerm: return !runestrcmp(a->text, b->text); case NumberTerm: if(a->numbertype != b->numbertype) return 0; if(a->numbertype == NumberInt && a->ival == b->ival) return 1; if(a->numbertype == NumberFloat && a->dval == b->dval) return 1; return 0; default: return 0; } } void applybinding(Term *t, Binding *bindings) { if(t->tag == VariableTerm){ Binding *b; for(b = bindings; b != nil; b = b->next){ if(runestrcmp(t->text, b->name) == 0 && t->clausenr == b->nr){ Term *next = t->next; memcpy(t, b->value, sizeof(Term)); t->next = next; return; } } }else if(t->tag == CompoundTerm){ Term *child; for(child = t->children; child != nil; child = child->next) applybinding(child, bindings); } } Goal * copygoals(Goal *goals) { if(goals != nil){ Goal *g = malloc(sizeof(Goal)); if(goals->goal) g->goal = copyterm(goals->goal, nil); else g->goal = nil; g->next = copygoals(goals->next); return g; }else return nil; }