shithub: pprolog

Download patch

ref: 480de114963ecee700ece5b8793916726c04b9ab
parent: ee65a81ee5b0112ba4480619ca672c569fb28b45
author: Peter Mikkelsen <peter@pmikkelsen.com>
date: Fri Jul 16 11:25:01 EDT 2021

Replace the C repl with one written in prolog :)

--- a/builtins.c
+++ b/builtins.c
@@ -60,6 +60,8 @@
 BuiltinProto(builtinpeekchar);
 BuiltinProto(builtinputchar);
 BuiltinProto(builtincharcode);
+BuiltinProto(builtinchoicestacksize);
+BuiltinProto(builtincollectgarbage);
 
 int compareterms(Term *, Term *);
 
@@ -169,6 +171,10 @@
 		return builtinputchar;
 	if(Match(L"char_code", 2))
 		return builtincharcode;
+	if(Match(L"$choicestack_size", 1))
+		return builtinchoicestacksize;
+	if(Match(L"$collect_garbage", 0))
+		return builtincollectgarbage;
 
 	return nil;
 }
@@ -569,10 +575,10 @@
 builtinthrow(Term *goal, Binding **bindings, Module *module)
 {
 	USED(bindings);
+	USED(module);
 
 	Term *ball = goal->children;
 
-	print("Throwing: %S\n", prettyprint(ball, 0, 0, 0, module));
 	Goal *g;
 	for(g = goalstack; g != nil; g = g->next){
 		if(g->catcher == nil)
@@ -579,27 +585,20 @@
 			continue;
 
 		if(unify(g->catcher, ball, bindings)){
-			if(g->goal == nil){
-				/* As soon as we have print facilities as builtins, we can avoid this by having the protector frame have a unhandled exception handler*/
-				print("Unhandled exception: %S\n", prettyprint(ball, 0, 0, 0, module));
-				exits("exception");
-				return 0;
-			}else{
-				goalstack = g->next;
-				Goal *newgoal = gmalloc(sizeof(Goal));
-				newgoal->goal = copyterm(g->goal, nil);
-				newgoal->module = module;
-				newgoal->catcher = nil;
-				newgoal->next = goalstack;
-				goalstack = newgoal;
-				applybinding(newgoal->goal, *bindings);
+			goalstack = g->next;
+			Goal *newgoal = gmalloc(sizeof(Goal));
+			newgoal->goal = copyterm(g->goal, nil);
+			newgoal->module = g->module;
+			newgoal->catcher = nil;
+			newgoal->next = goalstack;
+			goalstack = newgoal;
+			applybinding(newgoal->goal, *bindings);
 
-				Choicepoint *cp = choicestack;
-				while(cp != nil && cp->id >= goal->clausenr)
-					cp = cp->next;
-				choicestack = cp;
-				return 1;
-			}
+			Choicepoint *cp = choicestack;
+			while(cp != nil && cp->id >= goal->clausenr)
+				cp = cp->next;
+			choicestack = cp;
+			return 1;
 		}
 	}
 	return 0;
@@ -1404,3 +1403,29 @@
 	}
 }
 
+int
+builtinchoicestacksize(Term *goal, Binding **bindings, Module *module)
+{
+	USED(bindings);
+	USED(module);
+	Term *size = goal->children;
+	
+	vlong i = 0;
+	Choicepoint *cp;
+	for(cp = choicestack; cp != nil; cp = cp->next)
+		i++;
+	Term *realsize = mkinteger(i);
+	return unify(size, realsize, bindings);
+}
+
+int
+builtincollectgarbage(Term *goal, Binding **bindings, Module *module)
+{
+	USED(goal);
+	USED(bindings);
+	USED(module);
+	vlong amount = collectgarbage();
+	if(amount != 0 & debug)
+		print("Collected %lld bytes of garbage\n", amount);
+	return 1;
+}
\ No newline at end of file
--- a/dat.h
+++ b/dat.h
@@ -123,5 +123,4 @@
 Module *systemmodule; /* The module for the builtins. Everything has access to those */
 Module *usermodule; /* The default module for user defined predicates */
 uvlong clausenr;
-Binding *replbindings; /* The bindings used by the repl */
-Term *replquery; /* The currently active repl query */
\ No newline at end of file
+
--- a/eval.c
+++ b/eval.c
@@ -11,34 +11,11 @@
 void addchoicepoints(Clause *, Term *, Goal *, Module *);
 
 int
-evalquery(Term *query, Binding **resultbindings)
+evalquery(Term *query)
 {
-	if(choicestack == nil){
-	/*
-		The goal stack has the original query at the very bottom, protected by a catch frame where 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.
-	*/
-		goalstack = gmalloc(sizeof(Goal));
-		goalstack->goal = copyterm(query, nil);
-		goalstack->module = usermodule;
-		goalstack->catcher = nil;
-		goalstack->next = nil;
-		Goal *protector = gmalloc(sizeof(Goal));
-		protector->goal = nil;
-		protector->module = usermodule;
-		protector->catcher = mkvariable(L"catch-var");
-		protector->next = goalstack;
-		goalstack = protector;
+	Binding *replbindings = nil;
+	goalstack = addgoals(goalstack, query, usermodule);
 
-		/* Now add the actual goals */
-		goalstack = addgoals(goalstack, query, usermodule);
-
-	}else{
-		goto Backtrack;
-	}
-
 	while(goalstack->goal != nil){
 		Term *goal = goalstack->goal;
 		Term *catcher = goalstack->catcher;
@@ -101,7 +78,7 @@
 		}
 	}
 	goalstack = goalstack->next;
-	unify(query, goalstack->goal, resultbindings);
+	unify(query, goalstack->goal, &replbindings);
 	return 1;
 }
 
--- a/fns.h
+++ b/fns.h
@@ -18,15 +18,12 @@
 Clause *copyclause(Clause *, uvlong *);
 
 /* eval.c */
-int evalquery(Term *, Binding **);
+int evalquery(Term *);
 int unify(Term *, Term *, Binding **);
 void applybinding(Term *, Binding *);
 Goal *addgoals(Goal *, Term *, Module *);
 Predicate *findpredicate(Predicate *, Term *);
 Clause *findclause(Clause *, Term *, Binding **);
-
-/* repl.c */
-void repl(void);
 
 /* builtins.c */
 Builtin findbuiltin(Term *);
--- a/garbage.c
+++ b/garbage.c
@@ -66,14 +66,10 @@
 		1) The modules
 		2) The goalstack
 		3) The choicestack
-		4) The replbindings
-		5) The replquery
 	*/
 	markmodules();
 	markgoalstack(goalstack);
 	markchoicestack();
-	markbindings(replbindings);
-	markterm(replquery);
 
 	/* Free the allocations that were not marked as reachable */
 	for(i = 0; i < TableSize; i++){
--- a/main.c
+++ b/main.c
@@ -6,6 +6,7 @@
 #include "fns.h"
 
 void usage(void);
+void repl(int, char **);
 
 void
 main(int argc, char *argv[])
@@ -22,14 +23,8 @@
 	initflags();
 	initstreams();
 	initmodules();
+	repl(argc, argv);
 
-	while(argc != 0){
-		parsemodule(argv[0]);
-		argc--;
-		argv++;
-	}
-
-	repl();
 	exits(nil);
 }
 
@@ -38,4 +33,16 @@
 {
 	fprint(2, "Usage: pprolog [-d] modulefiles\n");
 	exits("Usage");
+}
+
+void
+repl(int argc, char *argv[])
+{
+	USED(argc);
+	USED(argv);
+	Term *mod = mkatom(L"repl");
+	Term *pred = mkatom(L"repl");
+	mod->next = pred;
+	Term *goal = mkcompound(L":", 2, mod);
+	evalquery(goal);	
 }
\ No newline at end of file
--- a/mkfile
+++ b/mkfile
@@ -9,7 +9,6 @@
 	builtins.$O\
 	prettyprint.$O\
 	misc.$O\
-	repl.$O\
 	flags.$O\
 	error.$O\
 	streams.$O\
--- a/module.c
+++ b/module.c
@@ -21,6 +21,7 @@
 	}
 
 	usermodule = addemptymodule(L"user");
+	parsemodule("./repl.pl");
 }
 
 Module *
--- a/repl.c
+++ /dev/null
@@ -1,87 +1,0 @@
-#include <u.h>
-#include <libc.h>
-#include <bio.h>
-
-#include "dat.h"
-#include "fns.h"
-
-Rune parsefindmore(int);
-void dogc(void);
-
-void
-repl(void)
-{
-	int fd = 0; /* Standard input */
-	while(1){
-		print("?- ");
-		replquery = parse(fd, nil, 1);
-		replbindings = nil;
-		choicestack = nil;
-		goalstack = nil;
-		int success;
-		int firsttime = 1;
-FindMore:
-		success = evalquery(replquery, &replbindings);
-		dogc();
-		if(firsttime){
-			print(" ");
-			firsttime = 0;
-		}
-		if(success == 0)
-			print("  false.\n");
-		else{
-			if(replbindings == nil)
-				print("  true");
-			else{
-				while(replbindings){
-					print("  %S = %S%s", 
-						replbindings->name, 
-						prettyprint(replbindings->value, 0, 0, 0, nil), 
-						replbindings->next ? ",\n " : "");
-					replbindings = replbindings->next;
-				}
-			}
-			if(choicestack != nil){
-				print("\n");
-				if(parsefindmore(fd) == L';'){
-					print(";");
-					goto FindMore;
-				}else
-					print(".\n");
-			}else{
-				print(".\n");
-			}
-		}
-	}
-}
-
-Rune
-parsefindmore(int fd)
-{
-	int consctl = open("/dev/consctl", OWRITE);
-	if(consctl > 0)
-		write(consctl, "rawon", 5);
-	else{
-		print("Could not open /dev/consctl\n");
-		exits("open");
-	}
-
-	fd = dup(fd, -1);
-	Biobuf *input = Bfdopen(fd, OREAD);
-	Rune peek = Bgetrune(input);
-	Bterm(input);
-
-	if(consctl > 0){
-		write(consctl, "rawoff", 6);
-		close(consctl);
-	}
-	return peek;
-}
-
-void
-dogc(void)
-{
-	vlong amount = collectgarbage();
-	if(amount != 0 && debug)
-		print("Collected %lld bytes of garbage\n", amount);
-}
\ No newline at end of file
--- /dev/null
+++ b/repl.pl
@@ -1,0 +1,77 @@
+:- module(repl, []).
+
+repl :-
+	catch(read_eval_print, E, print_exception(E)),
+	'$collect_garbage',
+	repl.
+
+read_eval_print :-
+	write('?- '),
+	asserta(found_a_solution :- (!, fail)),
+	read_term(Term, [variable_names(Vars)]),
+	'$choicestack_size'(Choicecount),
+	eval_and_print(Term, Vars, Choicecount),
+	!,
+	abolish(found_a_solution/0).
+
+eval_and_print(Goal, Vars, Choicecount) :-
+	user:call(Goal),
+	abolish(found_a_solution/0),
+	asserta(found_a_solution :- !),
+	'$choicestack_size'(ChoicecountNew),
+	( ChoicecountNew > Choicecount + 1
+	-> write_result(Vars, more),
+	   get_raw_char(Char),
+	   ( Char = ';'
+	   -> put_char(Char),
+	      nl,
+	      '$collect_garbage',
+	      asserta(found_a_solution :- (!, fail)),
+              fail % backtrack and call G again
+	   ; put_char('.'), nl
+	   )
+	; write_result(Vars, end)
+	).
+eval_and_print(Goal, _, _) :-
+	\+ found_a_solution,
+	write('false.'),
+	nl.
+
+write_state(end) :- write('.'), nl.
+write_state(more).
+
+write_result([], State) :- write('true'), write_state(State).
+write_result([B|Bs], State) :- write_bindings([B|Bs]), write_state(State).
+
+write_bindings([]).
+write_bindings([B|Bs]) :-
+	write(B),
+	( Bs = []
+	-> true
+	; put_char(','), nl
+	),
+	write_bindings(Bs).
+
+print_exception(E) :-
+	write('Unhandled exception: '),
+	write(E),
+	nl.
+
+whitespace(' ').
+whitespace('	').
+whitespace('
+').
+
+get_raw_char(Char) :-
+	open('/dev/consctl', write, S),
+	write(S, rawon),
+	get_one_char(Char),
+	write(S, rawoff),
+	close(S).
+
+get_one_char(Char) :-
+	get_char(C),
+	( whitespace(C)
+	-> get_one_char(Char)
+	; Char = C
+	).