ref: ee4298a2cfbbd9e015cfc775d9d714a9f5035846
parent: 79d1fe1cf2eb6748e2c12ffe9c36a678655302b1
author: Peter Mikkelsen <peter@pmikkelsen.com>
date: Wed Jun 30 10:04:15 EDT 2021
Add a basic repl
--- a/dat.h
+++ b/dat.h
@@ -1,4 +1,6 @@
typedef struct Term Term;
+typedef struct Binding Binding;
+
struct Term
{
int tag;
@@ -11,6 +13,14 @@
vlong ival;
double dval;
uvlong clausenr;
+};
+
+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;
};
enum {
--- a/eval.c
+++ b/eval.c
@@ -4,18 +4,9 @@
#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;
@@ -38,15 +29,33 @@
static uvlong clausenr;
-void
-evalquery(Term *database, Term *query)
+int
+evalquery(Term *database, Term *query, Binding **resultbindings)
{
- Goal *goals = addgoals(nil, query);
+ Goal *goals;
Choicepoint *choicestack = nil;
clausenr = 0;
- while(goals != nil){
+ /*
+ The goal stack has the original query at the very bottom, protected by a goal there the ->goal field is nil.
+ This makes it so that we can continue until we hit the protective goal, at which point we have solved everything
+ and to get the result we can unify the original query with the one at the bottom of the stack, to get the bindings
+ applied.
+ */
+
+ goals = malloc(sizeof(Goal));
+ goals->goal = copyterm(query, nil);
+ goals->next = nil;
+ Goal *protector = malloc(sizeof(Goal));
+ protector->goal = nil;
+ protector->next = goals;
+ goals = protector;
+
+ /* Now add the actual goals */
+ goals = addgoals(goals, query);
+
+ while(goals->goal != nil){
Term *dbstart;
Term *goal;
@@ -54,13 +63,6 @@
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);
@@ -74,28 +76,23 @@
choicestack = cp;
}
goals = goals->next;
- /* Apply bindings to all goals on the top of the stack, down to the "bodystart" goal */
+
+ /* Apply bindings to all goals on the stack. */
Goal *g;
- for(g = goals; g != nil && g->goal != nil; g = g->next)
- applybinding(g->goal, bindings);
+ for(g = goals; g != nil; g = g->next){
+ if(g->goal != nil)
+ 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");
+ if(choicestack == nil)
+ return 0;
Choicepoint *cp = choicestack;
choicestack = cp->next;
/* freegoals(goals) */
@@ -105,7 +102,9 @@
goto Retry;
}
}
- print("Success.\n");
+ goals = goals->next;
+ unify(query, goals->goal, resultbindings);
+ return 1;
}
Goal *
--- a/example.pl
+++ b/example.pl
@@ -1,5 +1,3 @@
-:- dynamic(math/4).
-
math(A,B,C,D) :- D is A + B + C * A.
parentest :-
@@ -28,9 +26,3 @@
length([], zero).
length([Head|Tail], suc(Length)) :-
length(Tail, Length).
-
-:- initialization(could_be_friends(bob, sam)).
-
-:- initialization(length([a,b,c,d], Len)).
-
-:- initialization(length(_,_)).
\ No newline at end of file
--- a/fns.h
+++ b/fns.h
@@ -1,5 +1,5 @@
/* parser.c */
-Term *parse(int);
+Term *parse(int, int);
/* prettyprint.c */
Rune *prettyprint(Term *);
@@ -14,4 +14,7 @@
Term *mknumber(int, vlong, double);
/* eval.c */
-void evalquery(Term *, Term *);
\ No newline at end of file
+int evalquery(Term *, Term *, Binding **);
+
+/* repl.c */
+void repl(Term *);
\ No newline at end of file
--- a/main.c
+++ b/main.c
@@ -29,11 +29,15 @@
int fd = open(parsetestfile, OREAD);
if(fd < 0)
exits("open");
- Term *prog = parse(fd);
+ Term *database = parse(fd, 0);
Term *goal;
- for(goal = initgoals; goal != nil; goal = goal->next)
- evalquery(prog, goal);
+ for(goal = initgoals; goal != nil; goal = goal->next){
+ Binding *bindings = nil;
+ evalquery(database, goal, &bindings);
+ }
+
+ repl(database);
}
exits(nil);
--- a/mkfile
+++ b/mkfile
@@ -2,7 +2,7 @@
TARG=pprolog
-OFILES=main.$O parser.$O eval.$O prettyprint.$O misc.$O
+OFILES=main.$O parser.$O eval.$O prettyprint.$O misc.$O repl.$O
HFILES=dat.h fns.h
--- a/parser.c
+++ b/parser.c
@@ -76,10 +76,10 @@
Term *parseoperators(Term *);
void match(int);
void syntaxerror(char *);
-Term *prologtext(void);
+Term *prologtext(int);
Term *
-parse(int fd)
+parse(int fd, int querymode)
{
parsein = Bfdopen(fd, OREAD);
if(parsein == nil){
@@ -90,21 +90,25 @@
initoperators();
nexttoken();
- return prologtext();
+ return prologtext(querymode);
}
Term *
-prologtext(void)
+prologtext(int querymode)
{
if(lookahead.tag == EofTok)
return nil;
Term *t = fullterm(AtomTok, L".", nil);
- if(lookahead.tag == AtomTok && runestrcmp(lookahead.text, L".") == 0)
- match(AtomTok);
- else
+ if(lookahead.tag == AtomTok && runestrcmp(lookahead.text, L".") == 0){
+ if(!querymode)
+ match(AtomTok);
+ }else
syntaxerror("prologtext");
+ if(querymode)
+ return t;
+
if(t->tag == CompoundTerm && runestrcmp(t->text, L":-") == 0 && t->arity == 1){
Term *body = t->children;
print("Got directive: %S\n", prettyprint(body));
@@ -113,11 +117,11 @@
initgoals = body->children;
initgoals->next = tmp;
}
- t = prologtext();
+ t = prologtext(querymode);
}else if(t->tag == CompoundTerm && runestrcmp(t->text, L":-") == 0 && t->arity == 2){
- t->next = prologtext();
+ t->next = prologtext(querymode);
}else if(t->tag == AtomTerm || t->tag == CompoundTerm){
- t->next = prologtext();
+ t->next = prologtext(querymode);
}else{
print("Expected directive or clause as toplevel\n");
syntaxerror("prologtext");