ref: d5ce41f05bc322fa2fb4d0eee66080b3b3004853
dir: /eval.c/
#include <u.h>
#include <libc.h>
#include "dat.h"
#include "fns.h"
typedef struct Binding Binding;
typedef struct Goal Goal;
struct Binding
{
Rune *name;
Term *value;
Binding *next;
};
struct Goal
{
Term *goal;
Goal *next;
};
Goal *addgoals(Goal *, Term *);
Term *findclause(Term *, Term *, Binding **);
int unify(Term *, Term *, Binding **);
int equalterms(Term *, Term *);
void applybinding(Term *, Binding *);
void
evalquery(Term *database, Term *query)
{
Goal *goals = addgoals(nil, query);
while(goals != nil){
Term *goal = goals->goal;
goals = goals->next;
if(goal == nil){
print("Done with body\n");
continue;
}
print("Solving goal %S\n", prettyprint(goal));
/* Find a clause where the head unifies with the goal */
Binding *bindings = nil;
Term *clause = findclause(database, goal, &bindings);
if(clause != nil){
print("Solving it using clause %S with new bindings: ", prettyprint(clause));
Binding *b;
for(b = bindings; b != nil; b = b->next){
print("%S = %S ", b->name, prettyprint(b->value));
}
print("\n");
/* Apply bindings to all goals on the top of the stack, down to the "bodystart" goal */
Goal *g;
for(g = goals; g != nil && g->next != 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);
applybinding(subgoal, bindings);
goals = addgoals(goals, subgoal);
}
}else{
print("No clause unifies with the goal, backtracking...(not really yet haha)\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;
print("Added goal: %S\n", prettyprint(t));
goals = g;
}
return goals;
}
Term *
findclause(Term *database, Term *goal, Binding **bindings)
{
Term *clause;
for(clause = database; clause != nil; clause = clause->next){
Term *head;
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);
rightstack = copyterm(b);
while(leftstack != nil && rightstack != nil){
left = leftstack;
leftstack = left->next;
right = rightstack;
rightstack = right->next;
print("Unifying:\n\t%S\n\t%S\n", prettyprint(left), prettyprint(right));
if(equalterms(left, right))
continue;
else if(left->tag == VariableTerm || right->tag == VariableTerm){
if(right->tag == VariableTerm){
Term *tmp = left;
left = right;
right = tmp;
}
Binding *b = malloc(sizeof(Binding));
b->name = left->text;
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);
t1->next = leftstack;
leftstack = t1;
leftchild = leftchild->next;
Term *t2 = copyterm(rightchild);
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){
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);
}
}