summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPeter Mikkelsen <peter@pmikkelsen.com>2021-06-30 14:04:15 +0000
committerPeter Mikkelsen <peter@pmikkelsen.com>2021-06-30 14:04:15 +0000
commitee4298a2cfbbd9e015cfc775d9d714a9f5035846 (patch)
tree8d6f4b139abf1a645e9e4fe0fba811f64b03839f
parent79d1fe1cf2eb6748e2c12ffe9c36a678655302b1 (diff)
Add a basic repl
-rw-r--r--dat.h10
-rw-r--r--eval.c67
-rw-r--r--example.pl8
-rw-r--r--fns.h7
-rw-r--r--main.c10
-rw-r--r--mkfile2
-rw-r--r--parser.c24
7 files changed, 70 insertions, 58 deletions
diff --git a/dat.h b/dat.h
index e4ddc74..d5dbfef 100644
--- a/dat.h
+++ b/dat.h
@@ -1,4 +1,6 @@
typedef struct Term Term;
+typedef struct Binding Binding;
+
struct Term
{
int tag;
@@ -13,6 +15,14 @@ struct Term
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 {
CompoundTerm,
AtomTerm,
diff --git a/eval.c b/eval.c
index c755cf5..f3a1e15 100644
--- 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 @@ Goal *copygoals(Goal *);
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 @@ evalquery(Term *database, Term *query)
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 @@ Retry:
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 @@ Retry:
goto Retry;
}
}
- print("Success.\n");
+ goals = goals->next;
+ unify(query, goals->goal, resultbindings);
+ return 1;
}
Goal *
diff --git a/example.pl b/example.pl
index 74387b1..d9ffc2a 100644
--- 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 @@ curly(A) :- A = {one,two,three}.
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
diff --git a/fns.h b/fns.h
index 92794bf..b8e92ca 100644
--- 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 *mkcompound(Rune *, int, Term *);
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
diff --git a/main.c b/main.c
index 76d8e04..416e832 100644
--- a/main.c
+++ b/main.c
@@ -29,11 +29,15 @@ main(int argc, char *argv[])
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);
diff --git a/mkfile b/mkfile
index 0196090..5191d06 100644
--- 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
diff --git a/parser.c b/parser.c
index 63da254..18573b7 100644
--- a/parser.c
+++ b/parser.c
@@ -76,10 +76,10 @@ Term *compound(void);
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 @@ parse(int fd)
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 @@ prologtext(void)
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");