shithub: pprolog

Download patch

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");