shithub: pprolog

Download patch

ref: 48da622d4ad0b4acfe9005dd318ac3f20b4e8672
parent: 0f347162b74d945f509955b6c57e506ab800db7b
author: Peter Mikkelsen <[email protected]>
date: Thu Jul 22 17:54:46 EDT 2021

Big commit changing the way the system is loaded at startup.

1) The loader and system modules are loaded by the C directly into the user module
2) The system module is then loaded with the loader from the user module
3) The loader module is then loaded with the loader from the user module
4) The repl is then loaded with the loader from the loader module
5) The user module is cleared

--- a/builtins.c
+++ b/builtins.c
@@ -10,7 +10,7 @@
 #define Throw(What) do{\
 	Goal *g = gmalloc(sizeof(Goal)); \
 	g->goal = What; \
-	g->module = usermodule; \
+	g->module = getmodule(L"user"); \
 	g->catcher = nil; \
 	g->next = goalstack; \
 	goalstack = g; \
@@ -63,7 +63,6 @@
 BuiltinProto(builtincharcode);
 BuiltinProto(builtinchoicestacksize);
 BuiltinProto(builtincollectgarbage);
-BuiltinProto(builtinloadmodulefromfile);
 BuiltinProto(builtinflushoutput);
 BuiltinProto(builtinstreamproperties);
 BuiltinProto(builtinsetstreamposition);
@@ -70,6 +69,8 @@
 BuiltinProto(builtinop);
 BuiltinProto(builtincurrentops);
 BuiltinProto(builtinnewemptymodule);
+BuiltinProto(builtindeletemodule);
+BuiltinProto(builtinactivatesystemmodule);
 BuiltinProto(builtinhalt);
 
 int compareterms(Term *, Term *);
@@ -186,8 +187,6 @@
 		return builtinchoicestacksize;
 	if(Match(L"$collect_garbage", 0))
 		return builtincollectgarbage;
-	if(Match(L"$load_module_from_file", 1))
-		return builtinloadmodulefromfile;
 	if(Match(L"flush_output", 1))
 		return builtinflushoutput;
 	if(Match(L"stream_properties", 1))
@@ -200,6 +199,10 @@
 		return builtincurrentops;
 	if(Match(L"$new_empty_module", 1))
 		return builtinnewemptymodule;
+	if(Match(L"$delete_module", 1))
+		return builtindeletemodule;
+	if(Match(L"$activate_system_module", 0))
+		return builtinactivatesystemmodule;
 	if(Match(L"$halt", 1))
 		return builtinhalt;
 
@@ -1492,27 +1495,6 @@
 }
 
 int
-builtinloadmodulefromfile(Term *goal, Binding **bindings, Module *module)
-{
-	USED(bindings);
-	USED(module);
-	Term *file = goal->children;
-
-	if(file->tag == VariableTerm)
-		Throw(instantiationerror());
-	if(file->tag != AtomTerm)
-		Throw(typeerror(L"atom", file));
-
-	char *filestr = smprint("%S", file->text);
-	Module *m = parsemodule(filestr);
-	free(filestr);
-	if(m)
-		return 1;
-	else
-		return 0;
-}
-
-int
 builtinflushoutput(Term *goal, Binding **bindings, Module *module)
 {
 	USED(bindings);
@@ -1574,7 +1556,7 @@
 	Term *specifier = priority->next;
 	Term *operator = specifier->next;
 
-	if(runestrcmp(operator->text, L",") == 0)
+	if(runestrcmp(operator->text, L",") == 0 && runestrcmp(module->name, L"system") != 0)
 		Throw(permissionerror(L"modify", L"operator", operator));
 
 	int type = 0;
@@ -1650,6 +1632,26 @@
 	USED(module);
 	Rune *name = goal->children->text;
 	addemptymodule(name);
+	return 1;
+}
+
+int
+builtindeletemodule(Term *goal, Binding **bindings, Module *module)
+{
+	USED(bindings);
+	USED(module);
+	Rune *name = goal->children->text;
+	removemodule(name);
+	return 1;
+}
+
+int
+builtinactivatesystemmodule(Term *goal, Binding **bindings, Module *module)
+{
+	USED(bindings);
+	USED(module);
+	USED(goal);
+	systemmoduleloaded = 1;
 	return 1;
 }
 
--- a/dat.h
+++ b/dat.h
@@ -154,7 +154,5 @@
 Choicepoint *choicestack;
 Goal *goalstack;
 Module *modules;
-Module *systemmodule; /* The module for the builtins. Everything has access to those */
-Module *usermodule; /* The default module for user defined predicates */
 uvlong clausenr;
-
+int systemmoduleloaded; /* Is the module "system" ready to be used */
--- a/eval.c
+++ b/eval.c
@@ -14,7 +14,7 @@
 evalquery(Term *query)
 {
 	Binding *replbindings = nil;
-	goalstack = addgoals(goalstack, query, usermodule);
+	goalstack = addgoals(goalstack, query, getmodule(L"user"));
 
 	while(goalstack->goal != nil){
 		Term *goal = goalstack->goal;
@@ -44,6 +44,15 @@
 				goto Backtrack;
 		}else{
 			Predicate *pred = findpredicate(module->predicates, goal);
+			if(pred == nil){
+				/* To to find it in the system module */
+				Module *sysmod;
+				if(systemmoduleloaded)
+					sysmod = getmodule(L"system");
+				else
+					sysmod = getmodule(L"user");
+				pred = findpredicate(sysmod->predicates, goal);
+			}
 			if(pred == nil){
 				Rune *name;
 				int arity;
--- a/fns.h
+++ b/fns.h
@@ -69,9 +69,10 @@
 
 /* module.c */
 void initmodules(void);
-Module *parsemodule(char *);
+int addtousermod(char *);
 Module *getmodule(Rune *);
 Module *addemptymodule(Rune *);
+void removemodule(Rune *);
 Clause *appendclause(Clause *, Clause *);
 Predicate *appendpredicate(Predicate *, Predicate *);
 Operator *getoperator(Rune *, Module *);
--- a/loader.pl
+++ b/loader.pl
@@ -1,14 +1,29 @@
 :- module(loader, []).
 
 start(Args) :-
-	catch((load_module_from_file('/sys/lib/prolog/repl.pl'), ReplLoaded = true), E, (print_exception(E), ReplLoaded = false)),
-	!,
-	( ReplLoaded = true
-	-> repl:repl(Args)
+	( bootstrap([system, loader, repl])
+	-> call(repl:repl(Args))
+	; write('Booting pprolog failed..'), halt
 	).
 
-print_exception(E) :-
-	write('Caught exception while loading /sys/lib/prolog/repl.pl: '),
+bootstrap([]) :- '$delete_module'(user), '$new_empty_module'(user).
+bootstrap([Mod|Mods]) :-
+	system_mod_path(Mod, File),
+	catch(load_module_from_file(File), E, (print_exception(File, E), fail)),
+	( Mod == system 
+	-> '$activate_system_module'
+	; true
+	),
+	bootstrap(Mods).
+
+system_mod_path(Mod, Path) :-
+	atom_concat('/sys/lib/prolog/', Mod, Path0),
+	atom_concat(Path0, '.pl', Path).
+
+print_exception(File, E) :-
+	write('Caught exception while loading '),
+	write(File),
+	write(': '),
 	write(E),
 	nl.
 
@@ -25,7 +40,8 @@
 run_initialization_goals(Module) :-
 	( retract(initialization_goals(Module, Goal)),
 	  catch(Module:Goal, E, print_initialization_goal_error(Module, Goal, E)),
-	  fail % Backtrack to find more goals
+	  !,
+	  run_initialization_goals(Module)
 	; true
 	).
 
@@ -89,7 +105,7 @@
 handle_clause(Head, Body, Singletons, Module) :-
 	functor(Head, Name, Arity),
 	PredicateIndicator = Name / Arity,
-	warn_singletons(PredicateIndicator, Singletons),
+	warn_singletons(PredicateIndicator, Module, Singletons),
 	Module:'$insert_clause'(Head :- Body).
 
 handle_directive(dynamic(PI), Module, Module) :-
@@ -125,15 +141,17 @@
 	write(D),
 	nl.
 
-warn_singletons(_, []).
-warn_singletons(PI, Singles) :-
-	write('Warning: singleton variables in '), 
-	write(PI),
-	write(': '),
+warn_singletons(_, Module, []).
+warn_singletons(PI, Module, Singles) :-
+	write('Warning: singleton variables '),
 	write(Singles),
+	write(' in '),
+	write(Module:PI),
 	write('.'),
 	nl.
 
+
+:- dynamic(ensure_loads/1).
 ensure_loads(_) :- fail.
 
 ensure_load(F) :-
--- a/main.c
+++ b/main.c
@@ -11,7 +11,6 @@
 void
 main(int argc, char *argv[])
 {
-	clausenr = 2; /* Start at two since 0 is for the facts in the database, and 1 is for queries */
 	initflags();
 	initstreams();
 	initmodules();
@@ -37,10 +36,6 @@
 		argc--;
 	}
 	args = mklist(args);
-	Term *mod = mkatom(L"loader");
-	Term *pred = mkcompound(L"start", 1, args);
-
-	mod->next = pred;
-	Term *goal = mkcompound(L":", 2, mod);
-	evalquery(goal);	
+	Term *start = mkcompound(L"start", 1, args);
+	evalquery(start);	
 }
\ No newline at end of file
--- a/mkfile
+++ b/mkfile
@@ -22,7 +22,7 @@
 BIN=/$objtype/bin
 
 PROLOGFILES=\
-	stdlib.pl\
+	system.pl\
 	repl.pl\
 	loader.pl
 
--- a/module.c
+++ b/module.c
@@ -8,56 +8,30 @@
 void
 initmodules(void)
 {
-	systemmodule = parsemodule("/sys/lib/prolog/stdlib.pl");
-	if(systemmodule == nil){
-		print("Can't load /sys/lib/prolog/stdlib.pl\n");
+	addemptymodule(L"user");
+	if(!addtousermod("/sys/lib/prolog/system.pl")){
+		print("Can't load /sys/lib/prolog/system.pl\n");
 		exits(nil);
 	}
-
-	Predicate *p;
-	for(p = systemmodule->predicates; p != nil; p = p->next){
-		p->builtin = 1;
-		p->dynamic = 0;
+	if(!addtousermod("/sys/lib/prolog/loader.pl")){
+		print("Can't load /sys/lib/prolog/loader.pl\n");
+		exits(nil);
 	}
-
-	usermodule = addemptymodule(L"user");
-	parsemodule("/sys/lib/prolog/loader.pl");
 }
 
-Module *
-parsemodule(char *file)
+int
+addtousermod(char *file)
 {
-	Module *m = nil;
-
 	int fd = open(file, OREAD);
 	if(fd < 0)
-		return nil;
+		return 0;
 
+	Module *usermodule = getmodule(L"user");
 	Term *terms = parse(fd, nil, 0);
 
 	if(terms == nil)
-		return nil;
+		return 0;
 
-	/* Actually look at the terms and convert ':-'/2 terms into clauses.
-	   The only directives (terms of type ':-'/1 there should be in the list are
-	   the module specific ones, as the other are handled by parse itself.
-	*/
-	if(terms->tag == CompoundTerm && runestrcmp(terms->text, L":-") == 0 && terms->arity == 1){
-		Term *directive = terms->children;
-		if(directive->tag == CompoundTerm && runestrcmp(directive->text, L"module") == 0 && directive->arity == 2){
-			Term *modulename = directive->children;
-			Term *publiclist = modulename->next;
-			if(modulename->tag != AtomTerm){
-				print("Module name should be an atom in: %S\n", prettyprint(directive, 0, 0, 0, nil));
-				return nil;
-			}
-			if(flagdebug)
-				print("Public list for module '%S': %S\n", modulename->text, prettyprint(publiclist, 0, 0, 0, nil));
-			m = getmodule(modulename->text);
-		}
-		terms = terms->next;
-	}
-
 	Predicate *currentpred = nil;
 	Term *t;
 	for(t = terms; t != nil; t = t->next){
@@ -79,10 +53,7 @@
 
 		/* Figure out if this clause goes into the latest predicate, or if it is the start of a new one */
 		if(currentpred == nil || runestrcmp(cl->head->text, currentpred->name) != 0 || arity != currentpred->arity){
-			if(m)
-				m->predicates = appendpredicate(currentpred, m->predicates);
-			else
-				usermodule->predicates = appendpredicate(currentpred, usermodule->predicates);
+			usermodule->predicates = appendpredicate(currentpred, usermodule->predicates);
 			currentpred = gmalloc(sizeof(Predicate));
 			currentpred->name = cl->head->text;
 			currentpred->arity = arity;
@@ -94,12 +65,8 @@
 		}else
 			currentpred->clauses = appendclause(currentpred->clauses, cl);
 	}
-	if(m)
-		m->predicates = appendpredicate(currentpred, m->predicates);
-	else
-		usermodule->predicates = appendpredicate(currentpred, usermodule->predicates);
-
-	return m;
+	usermodule->predicates = appendpredicate(currentpred, usermodule->predicates);
+	return 1;
 }
 
 Module *
@@ -119,12 +86,11 @@
 	Module *m = gmalloc(sizeof(Module));
 	m->name = name;
 	m->next = modules;
+	m->predicates = nil;
 	memset(m->operators, 0, sizeof(m->operators));
 
-	if(systemmodule == nil)
-		m->predicates = nil;
-	else{
-		m->predicates = systemmodule->predicates; /* Direct access to system clauses for now, but when I figure out imports this will change */
+	Module *systemmodule = getmodule(L"system");
+	if(systemmodule != nil){
 		int level;
 		Operator *op;
 		for(level = 0; level < PrecedenceLevels; level++){
@@ -134,6 +100,24 @@
 	}
 	modules = m;
 	return m;
+}
+
+void
+removemodule(Rune *name)
+{
+	Module *m;
+	Module *prev = nil;
+	for(m = modules; m != nil; m = m->next){
+		if(runestrcmp(m->name, name) != 0)
+			prev = m;
+		else{
+			if(prev == nil)
+				modules = m->next;
+			else
+				prev->next = m->next;
+			return;
+		}
+	}
 }
 
 Clause *
--- a/parser.c
+++ b/parser.c
@@ -70,7 +70,7 @@
 		parsein = bio;
 
 	nexttoken();
-	currentmod = usermodule;
+	currentmod = getmodule(L"user");
 
 	Term *result = prologtext(querymode);
 	if(querymode && result){
@@ -101,12 +101,7 @@
 
 	if(t->tag == CompoundTerm && runestrcmp(t->text, L":-") == 0 && t->arity == 1){
 		Term *body = t->children;
-		if(runestrcmp(body->text, L"module") == 0 && body->arity == 2){
-			handlemoduledirective(body->children);
-			t->next = prologtext(querymode);
-			return t;
-		}
-		else if(runestrcmp(body->text, L"op") == 0 && body->arity == 3)
+		if(runestrcmp(body->text, L"op") == 0 && body->arity == 3)
 			handleopdirective(body->children);
 
 		t = prologtext(querymode);
@@ -576,20 +571,6 @@
 {
 	print("Syntax error: Unexpected %d (%S) token in %s\n", lookahead.tag, lookahead.text, where);
 	exits("syntax error");
-}
-
-void
-handlemoduledirective(Term *args)
-{
-	Term *modulename = args;
-	Term *publiclist = modulename->next;
-	USED(publiclist);
-
-	if(modulename->tag != AtomTerm){
-		print("Module name should be an atom in: %S\n", prettyprint(modulename, 0, 0, 0, currentmod));
-		return;
-	}
-	currentmod = addemptymodule(modulename->text);
 }
 
 void
--- a/prettyprint.c
+++ b/prettyprint.c
@@ -16,7 +16,7 @@
 	Rune *result;
 	Rune *args;
 	if(mod == nil)
-		mod = usermodule;
+		mod = getmodule(L"user");
 
 	switch(t->tag){
 	case CompoundTerm:
--- a/stdlib.pl
+++ /dev/null
@@ -1,699 +1,0 @@
-:-(module(system, [])).
-
-% Insert the standard operators
-
-:-(op(1200, fx, :-)).
-:- op(1200, fx,  ?-).
-:- op(1200, xfx, :-).
-:- op(1200, xfx, -->).
-:- op(1100, xfy, ;).
-:- op(1050, xfy, ->).
-:- op(1000, xfy, ',').
-:- op(900,  fy,  \+).
-:- op(700,  xfx, =).
-:- op(700,  xfx, \=).
-:- op(700,  xfx, ==).
-:- op(700,  xfx, \==).
-:- op(700,  xfx, @<).
-:- op(700,  xfx, @=<).
-:- op(700,  xfx, @>).
-:- op(700,  xfx, @>=).
-:- op(700,  xfx, =..).
-:- op(700,  xfx, is).
-:- op(700,  xfx, =:=).
-:- op(700,  xfx, =\=).
-:- op(700,  xfx, <).
-:- op(700,  xfx, =<).
-:- op(700,  xfx, >).
-:- op(700,  xfx, >=).
-:- op(600,  xfy, :).
-:- op(500,  yfx, +).
-:- op(500,  yfx, -).
-:- op(500,  yfx, /\).
-:- op(500,  yfx, \/).
-:- op(400,  yfx, *).
-:- op(400,  yfx, /).
-:- op(400,  yfx, //).
-:- op(400,  yfx, rem).
-:- op(400,  yfx, mod).
-:- op(400,  yfx, <<).
-:- op(400,  yfx, >>).
-:- op(200,  xfx, **).
-:- op(200,  xfy, ^).
-:- op(200,  fy, -).
-:- op(200,  fy, \).
-
-% Logic and control predicates
-\+ Goal :- call(Goal), !, fail.
-\+ Goal.
-
-once(Goal) :-
-	call(Goal),
-	!.
-
-repeat :- true ; repeat.
-
-% Control structures.
-
-If -> Then :-
-	If, !, Then.
-
-If -> Then ; _ :- 
-	If, !, Then.
-
-_ -> _ ; Else :-
-	!, Else.
-
-If ; _ :-
-	If.
-
-_ ; Else :-
-	Else.
-
-A , B :- A , B.
-
-% Term unification
-A = A.
-
-A \= B :- 
-	\+ A = B.
-
-% Comparison of terms using the standard order
-
-A == B :-
-	compare(=, A, B).
-
-A \== B :-
-	\+ A == B.
-
-A @< B :-
-	compare(<, A, B).
-
-A @=< B :-
-	A == B.
-A @=< B :-
-	A @< B.
-
-A @> B :-
-	compare(>, A, B).
-
-A @>= B :-
-	A == B.
-A @>= B :-
-	A @> B.
-
-% Input output
-
-open(SourceSink, Mode, Stream) :-
-	open(SourceSink, Mode, Stream, []).
-
-close(StreamOrAlias) :-
-	close(StreamOrAlias, []).
-
-flush_output :-
-	current_output(S),
-	flush_output(S).
-
-stream_property(S, P) :-
-	stream_properties(Props),
-	member(prop(S,P), Props).
-
-at_end_of_stream :-
-	current_input(S),
-	stream_property(S, end_of_stream(E)),
-	!,
-	(E = at ; E = past),
-	!.
-
-at_end_of_stream(S_or_a) :-
-	( atom(S_or_a)
-	-> stream_property(S, alias(S_or_a))
-	; S = S_or_a
-	),
-	stream_property(S, end_of_stream(E)),
-	!,
-	(E = at; E = past),
-	!.
-
-% Standard exceptions
-
-instantiation_error :-
-	throw(error(instantiation_error, _)).
-
-type_error(ValidType, Culprit) :-
-	throw(error(type_error(ValidType, Culprit), _)).
-
-domain_error(ValidDomain, Culprit) :-
-	throw(error(domain_error(ValidDomain, Culprit), _)).
-
-existence_error(ObjectType, Culprit) :-
-	throw(error(existence_error(ObjectType, Culprit), _)).
-
-permission_error(Operation, PermissionType, Culprit) :-
-	throw(error(permission_error(Operation, PermissionType, Culprit), _)).
-
-representation_error(Flag) :-
-	throw(error(representation_error(Flag), _)).
-
-evaluation_error(Error) :-
-	throw(error(evaluation_error(Error), _)).
-
-resource_error(Resource) :-
-	throw(error(resource_error(Resource), _)).
-
-syntax_error(Error) :-
-	throw(error(syntax_error(Error), _)).
-
-% Input and output
-parse_read_option(variables(Vs), options(variables, Vs)).
-parse_read_option(variable_names(VNames), option(variable_names, VNames)).
-parse_read_option(singletons(S), options(singletons, S)).
-
-parse_read_options([], []).
-parse_read_options([Op|Rest], [OpParsed|RestParsed]) :-
-	is_nonvar(Op),
-	parse_read_options(Rest, RestParsed),
-	( parse_read_option(Op, OpParsed)
-	-> true
-	; domain_error(read_option, Op)
-	).
-
-read_term(S, Term, Options) :-
-	is_nonvar(Options),
-	is_list(Options),
-	parse_read_options(Options, ParsedOptions),
-	'$read_term'(S, Term, ParsedOptions).
-
-read_term(Term, Options) :-
-	current_input(S),
-	read_term(S, Term, Options).
-
-read(Term) :-
-	current_input(S),
-	read_term(S, Term, []).
-
-read(S, Term) :-
-	read_term(S, Term, []).
-
-parse_write_option(quoted(true), option(quoted, 1)).
-parse_write_option(quoted(false), option(quoted, 0)).
-parse_write_option(ignore_ops(true), option(ignore_ops, 1)).
-parse_write_option(ignore_ops(false), option(ignore_ops, 0)).
-parse_write_option(numbervars(true), option(numbervars, 1)).
-parse_write_option(numbervars(false), option(numbervars, 0)).
-
-parse_write_options([], []).
-parse_write_options([Op|Rest], [OpParsed|RestParsed]) :-
-	is_nonvar(Op),
-	parse_write_options(Rest, RestParsed),
-	( parse_write_option(Op, OpParsed)
-	-> true
-	; domain_error(write_option, Op)
-	).
-write_term(S, Term, Options) :-
-	is_nonvar(Options),
-	is_list(Options),
-	parse_write_options(Options, ParsedOptions),
-	'$write_term'(S, Term, ParsedOptions).
-
-
-write_term(Term, Options) :-
-	current_output(S),
-	write_term(S, Term, Options).
-
-write(Term) :-
-	current_output(S),
-	write_term(S, Term, [numbervars(true)]).
-
-write(S, Term) :-
-	write_term(S, Term, [numbervars(true)]).
-
-writeq(Term) :-
-	current_output(S),
-	write_term(S, Term, [quoted(true), numbervars(true)]).
-
-writeq(S, Term) :-
-	write_term(S, Term, [quoted(true), numbervars(true)]).
-
-write_canonical(Term) :-
-	current_output(S),
-	write_term(S, Term, [quoted(true), ignore_ops(true)]).
-
-write_canonical(S, Term) :-
-	write_term(S, Term, [quoted(true), ignore_ops(true)]).
-
-% Arithmetic comparisons defined in terms of >=. This is not the most effective way,
-% but it is fine for now.
-
-E1 =:= E2 :-
-	E1 >= E2,
-	E2 >= E1.
-
-E1 =\= E2 :-
-	\+ E1 =:= E2.
-
-E1 < E2 :-
-	E2 >= E1,
-	E1 =\= E2.
-
-E1 =< E2 :-
-	E2 >= E1.
-
-E1 > E2 :-
-	E2 < E1.
-
-
-% Clause retrieval and information and removal
-
-clause(Head, Body) :-
-	clause(Head, Body, Clauses),
-	member(clause(Head, Body), Clauses).
-
-current_predicate(PI) :-
-	current_predicate(PI, Predicates),
-	member(PI, Predicates).
-
-retract(Clause) :-
-	copy_term(Clause, ClauseCopy),
-	retract_one(ClauseCopy),
-	( Clause = ClauseCopy
-	; retract(Clause)
-	).
-
-% Basic list predicates
-
-member(X, [X|_]).
-member(X, [_|Tail]) :-
-	member(X, Tail).
-
-append([], Ys, Ys).
-append([X|Xs], Ys, [X|Zs]) :-
-	append(Xs, Ys, Zs).
-
-length([], 0).
-length([_|T], Len) :-
-	length(T, Len0),
-	Len is Len0 + 1.
-
-unique([], []).
-unique([H|T], [H|Rest]) :-
-	findall(_, (member(X, T), X == H), []),
-	!,
-	unique(T, Rest).
-unique([H|T], Rest) :-
-	unique(T, Rest).
-
-union(A, B, C) :-
-	append(A, B, C0),
-	unique(C0, C).
-
-difference(A, B, Diff) :-
-	append(A, B, AB),
-	unique_in(AB, AB, Diff).
-
-unique_in([], _, []).
-unique_in([H|T], L, [H|Rest]) :-
-	findall(_, (member(X, L), X == H), [_]),
-	!,
-	unique_in(T, L, Rest).
-unique_in([H|T], L, Rest) :-
-	unique_in(T, L, Rest).
-
-include(_, [], []).
-include(Goal, [X|Xs], Included) :-
-	Goal =.. L,
-	append(L, [X], L1),
-	G =.. L1,
-	( call(G)
-	-> Included = [X|Included0]
-	;  Included = Included0
-	),
-	include(Goal, Xs, Included0).
-
-% Additional type tests
-
-callable(T) :- atom(T) ; compound(T).
-
-list([]).
-list([_|T]) :- list(T).
-
-partial_list(T) :- var(T).
-partial_list([_|T]) :- partial_list(T).
-
-atomic(T) :- atom(T) ; integer(T) ; float(T).
-
-% type assertions (throws an error if false)
-
-is_atom(T) :- atom(T), ! ; type_error(atom, T).
-
-is_atom_or_var(T) :- (atom(T) ; var(T)), ! ; type_error(atom, T).
-
-is_callable(T) :- callable(T), ! ; type_error(callable, T).
-
-is_nonvar(T) :- nonvar(T), ! ; instantiation_error.
-
-is_list_or_partial_list(T) :- (list(T) ; partial_list(T)), ! ; type_error(list, T).
-
-is_list(T) :- list(T), ! ; type_error(list, T).
-
-is_integer(T) :- integer(T), ! ; type_error(integer, T).
-
-is_predicate_indicator(T) :- (nonvar(T), T = N/A, integer(A), atom(N), !) ; type_error(predicate_indicator, T).
-
-% All solutions
-
-findall(Template, Goal, Instances) :-
-	is_nonvar(Goal),
-	is_callable(Goal),
-	is_list_or_partial_list(Instances),
-	system:asserta('find all'([])),
-	call(Goal),
-	system:asserta('find all'(solution(Template))),
-	fail.
-findall(Template, Goal, Instances) :-
-	findall_collect([], Instances).
-
-findall_collect(Acc, Instances) :-
-	system:retract('find all'(Item)),
-	!,
-	findall_collect(Item, Acc, Instances).
-findall_collect([], Instances, Instances).
-findall_collect(solution(T), Acc, Instances) :-
-	findall_collect([T|Acc], Instances).
-
-bagof(Template, Goal, Instances) :-
-	free_variable_set(Goal, Template, Witness),
-	iterated_goal(Goal, G),
-	findall(Witness+Template, G, S),
-	bagof_loop(Witness, S, Instances).
-
-bagof_loop(Witness, S, Instances) :-
-	[W+T|_] = S,
-	bagof_wt_list(S, W+T, WT_list),
-	bagof_split(WT_list, W_list, T_list),
-	( bagof_unify_list(Witness, W_list), Instances = T_list
-	; bagof_next_s(S, WT_list, S_next), bagof_loop(Witness, S_next, Instances)
-	).
-
-bagof_wt_list([], _, []).
-bagof_wt_list([W+T|Tail], W0+T0, Rest) :-
-	copy_term(W+T, W1+T1),
-	bagof_wt_list(Tail, W0+T0, Rest0),
-	( variant(W1, W0)
-	-> Rest = [W1+T1|Rest0]
-	; Rest = Rest0
-	).
-
-copy_terms_list([], []).
-copy_terms_list([H|T], [HH|TT]) :-
-	copy_term(H, HH),
-	copy_terms_list(T, TT).
-bagof_split([], [], []).
-bagof_split([WW+TT|RestWT], [WW|RestW], [TT|RestT]) :-
-	bagof_split(RestWT, RestW, RestT).
-
-bagof_unify_list(W, []).
-bagof_unify_list(W, [W|T]) :- bagof_unify_list(W, T).
-
-bagof_next_s([], _, []).
-bagof_next_s([H|T], WT_list, Rest) :-
-	bagof_next_s(T, WT_list, Rest0),
-	( findall(_, (member(X, WT_list), variant(X, H)), [])
-	-> Rest = [H|Rest0]
-	; Rest = Rest0
-	).
-
-setof(Template, Goal, Instances) :-
-	bagof(Template, Goal, Instances_list),
-	sort(Instances_list, Instances).
-
-% misc helpers
-
-variable_set(Term, []) :-
-	atomic(Term).
-variable_set(Term, [Term]) :-
-	var(Term).
-variable_set(Term, Vars) :-
-	compound(Term),
-	Term =.. [_|Args],
-	variable_set(Args, [], Vars0),
-	unique(Vars0, Vars).
-
-variable_set([], Acc, Acc).
-variable_set([Arg|Rest], Acc0, Result) :-
-	variable_set(Arg, VarSet),
-	append(Acc0, VarSet, Acc),
-	variable_set(Rest, Acc, Result).
-
-existential_variable_set(Term, []) :-
-	(atomic(Term) ; var(Term)),
-	!.
-existential_variable_set(V^G, Vars) :-
-	!,
-	existential_variable_set(G, Vars0),
-	variable_set(V, Vars1),
-	union(Vars0, Vars1, Vars).
-existential_variable_set(_, []).
-
-free_variable_set(T, V, Vars) :-
-	variable_set(T, TVars),
-	variable_set(V, VVars),
-	existential_variable_set(T, TExVars),
-	union(VVars, TExVars, BV),
-	difference(TVars, BV, Vars).
-
-iterated_goal(Goal, T) :-
-	compound(Goal),
-	_^G = Goal,
-	!,
-	iterated_goal(G, T).
-iterated_goal(G, G).
-
-variant(T1, T2) :-
-	var(T1), var(T2), !.
-variant(T1, T2) :-
-	compound(T1),
-	compound(T2),
-	!,
-	T1 =.. [Name|Args1],
-	T2 =.. [Name|Args2],
-	variant_list(Args1, Args2).
-variant(T1, T2) :-
-	T1 == T2.
-
-variant_list([], []).
-variant_list([H1|T1], [H2|T2]) :-
-	variant(H1, H2),
-	variant_list(T1, T2).
-
-% Sorting, which also removes duplicates (should be implemented in C for speed I think).
-
-sort(Ls0, Ls) :-
-	append(Lefts, [A,B|Rights], Ls0),
-	A @> B,
-	!,
-	append(Lefts, [B,A|Rights], Ls1),
-	sort(Ls1, Ls).
-sort(Ls0, Ls) :-
-	append(Lefts, [A,B|Rights], Ls0),
-	A == B,
-	!,
-	append(Lefts, [A|Rights], Ls1),
-	sort(Ls1, Ls).
-sort(Ls, Ls).
-
-% Atomic term processing
-
-atom_concat(A1, A2, A3) :-
-	is_atom_or_var(A1),
-	is_atom_or_var(A2),
-	is_atom_or_var(A3),
-	atom(A1), atom(A2),
-	!,
-	atom_codes(A1, Codes1),
-	atom_codes(A2, Codes2),
-	append(Codes1, Codes2, Codes),
-	atom_codes(A3, Codes).
-atom_concat(A1, A2, A3) :-
-	is_atom_or_var(A1),
-	is_atom_or_var(A2),
-	is_atom_or_var(A3),
-	atom(A3),
-	!,
-	atom_codes(A3, Codes),
-	append(Codes1, Codes2, Codes),
-	atom_codes(A1, Codes1),
-	atom_codes(A2, Codes2).
-atom_concat(A1, A2, A3) :-
-	instantiation_error.
-
-% Character input/output
-
-get_char(Char) :-
-	current_input(S),
-	get_char(S, Char).
-
-get_code(Code) :-
-	current_input(S),
-	get_code(S, Code).
-
-get_code(S, Code) :-
-	get_char(S, Char),
-	( Char = end_of_file
-	-> Code = -1
-	; char_code(Char, Code)
-	).
-
-peek_char(Char) :-
-	current_input(S),
-	peek_char(S, Char).
-
-peek_code(Code) :-
-	current_input(S),
-	peek_code(S, Code).
-
-peek_code(S, Code) :-
-	peek_char(S, Char),
-	( Char = end_of_file
-	-> Code = -1
-	; char_code(Char, Code)
-	).
-
-put_char(Char) :-
-	current_output(S),
-	put_char(S, Char).
-
-put_code(Code) :-
-	current_output(S),
-	put_code(S, Code).
-
-put_code(S, Code) :-
-	char_code(Char, Code),
-	put_char(S, Char).
-
-nl :-
-	current_output(S),
-	nl(S).
-
-nl(S) :-
-	put_char(S, '
-'). % This should really be \n
-
-% flags
-set_prolog_flag(Flag, Value) :-
-	is_nonvar(Flag),
-	is_nonvar(Value),
-	is_atom(Flag),
-	is_prolog_flag(Flag),
-	is_appropriate_flag_value(Flag, Value),
-	is_modifiable_flag(Flag),
-	'$set_prolog_flag'(Flag, Value).
-
-current_prolog_flag(Flag, Value) :-
-	is_atom_or_var(Flag),
-	( atom(Flag)
-	-> is_prolog_flag(Flag)
-	; true
-	),
-	current_prolog_flags(FlagsAndValues),
-	member(flag(Flag, Value), FlagsAndValues).
-
-is_prolog_flag(Flag) :-
-	member(Flag, 
-		[ bounded
-		, max_integer
-		, min_integer
-		, integer_rounding_function
-		, char_conversion
-		, debug
-		, max_arity
-		, unknown
-		, double_quotes]),
-	!
-	; domain_error(prolog_flag, Flag).
-
-is_modifiable_flag(Flag) :-
-	member(Flag, [char_conversion, debug, unknown, double_quotes]),
-	!
-	; permission_error(modify, flag, Flag).
-
-is_appropriate_flag_value(Flag, Value) :-
-	appropriate_flag_values(Flag, Values), 
-	member(Value, Values),
-	!
-	; domain_error(flag_value, Flag + Value).
-
-appropriate_flag_values(bounded, [true, false]).
-appropriate_flag_values(max_integer, [Val]) :-
-	current_prolog_flag(max_integer, Val).
-appropriate_flag_values(min_integer, [Val]) :-
-	current_prolog_flag(min_integer, Val).
-appropriate_flag_values(integer_rounding_function, [down, toward_zero]).
-appropriate_flag_values(char_conversion, [on, off]).
-appropriate_flag_values(debug, [on, off]).
-appropriate_flag_values(max_arity, [Val]) :-
-	current_prolog_flag(max_arity).
-appropriate_flag_values(unknown, [error, fail, warning]).
-appropriate_flag_values(double_quotes, [chars, codes, atom]).
-
-% Operator table modification and inspection
-
-op(Priority, Op_specifier, Operator) :-
-	is_nonvar(Priority),
-	is_integer(Priority),
-	is_nonvar(Op_specifier),
-	is_atom(Op_specifier),
-	( operator_priority(Priority), !
-	; domain_error(operator_priority, Priority)
-	),
-	( operator_specifier(Op_specifier), !
-	; domain_error(operator_specifier, Op_specifier)
-	),
-	is_nonvar(Operator),
-	( atom(Operator)
-	-> Ops = [Operator]
-	; Ops = Operator
-	),
-	is_list(Ops),
-	op_helper(Priority, Op_specifier, Ops).
-
-op_helper(Priority, Op_specifier, []).
-op_helper(Priority, Op_specifier, [Op|Ops]) :-
-	is_nonvar(Op),
-	is_atom(Op),
-	'$op'(Priority, Op_specifier, Op),
-	op_helper(Priority, Op_specifier, Ops).
-
-operator_priority(P) :-
-	integer(P),
-	P >= 0,
-	P =< 1200.
-
-operator_specifier(S) :-
-	member(S, [xf, yf, xfx, xfy, yfx, fx, fy]).
-
-current_op(Priority, Op_specifier, Operator) :-
-	( (var(Priority) ; operator_priority(Priority)), !
-	; domain_error(operator_priority, Priority)
-	),
-	( (var(Op_specifier) ; operator_specifier(Op_specifier)), !
-	; domain_error(operator_specifier, Op_specifier)
-	),
-	is_atom_or_var(Operator),
-	current_ops(Operators),
-	member(op(Priority, Op_specifier, Operator), Operators).
-
-% Halting
-
-halt(X) :-
-	is_nonvar(X),
-	is_integer(X),
-	'$halt'(X).
-
-halt :-
-	halt(0).
-
-% Loading prolog text
-
-consult(File) :-
-	loader:load_module_from_file(File).
--- /dev/null
+++ b/system.pl
@@ -1,0 +1,699 @@
+:-(module(system, [])).
+
+% Insert the standard operators
+
+:-(op(1200, fx, :-)).
+:- op(1200, fx,  ?-).
+:- op(1200, xfx, :-).
+:- op(1200, xfx, -->).
+:- op(1100, xfy, ;).
+:- op(1050, xfy, ->).
+:- op(1000, xfy, ',').
+:- op(900,  fy,  \+).
+:- op(700,  xfx, =).
+:- op(700,  xfx, \=).
+:- op(700,  xfx, ==).
+:- op(700,  xfx, \==).
+:- op(700,  xfx, @<).
+:- op(700,  xfx, @=<).
+:- op(700,  xfx, @>).
+:- op(700,  xfx, @>=).
+:- op(700,  xfx, =..).
+:- op(700,  xfx, is).
+:- op(700,  xfx, =:=).
+:- op(700,  xfx, =\=).
+:- op(700,  xfx, <).
+:- op(700,  xfx, =<).
+:- op(700,  xfx, >).
+:- op(700,  xfx, >=).
+:- op(600,  xfy, :).
+:- op(500,  yfx, +).
+:- op(500,  yfx, -).
+:- op(500,  yfx, /\).
+:- op(500,  yfx, \/).
+:- op(400,  yfx, *).
+:- op(400,  yfx, /).
+:- op(400,  yfx, //).
+:- op(400,  yfx, rem).
+:- op(400,  yfx, mod).
+:- op(400,  yfx, <<).
+:- op(400,  yfx, >>).
+:- op(200,  xfx, **).
+:- op(200,  xfy, ^).
+:- op(200,  fy, -).
+:- op(200,  fy, \).
+
+% Logic and control predicates
+\+ Goal :- call(Goal), !, fail.
+\+ Goal.
+
+once(Goal) :-
+	call(Goal),
+	!.
+
+repeat :- true ; repeat.
+
+% Control structures.
+
+If -> Then :-
+	If, !, Then.
+
+If -> Then ; _ :- 
+	If, !, Then.
+
+_ -> _ ; Else :-
+	!, Else.
+
+If ; _ :-
+	If.
+
+_ ; Else :-
+	Else.
+
+A , B :- A , B.
+
+% Term unification
+A = A.
+
+A \= B :- 
+	\+ A = B.
+
+% Comparison of terms using the standard order
+
+A == B :-
+	compare(=, A, B).
+
+A \== B :-
+	\+ A == B.
+
+A @< B :-
+	compare(<, A, B).
+
+A @=< B :-
+	A == B.
+A @=< B :-
+	A @< B.
+
+A @> B :-
+	compare(>, A, B).
+
+A @>= B :-
+	A == B.
+A @>= B :-
+	A @> B.
+
+% Input output
+
+open(SourceSink, Mode, Stream) :-
+	open(SourceSink, Mode, Stream, []).
+
+close(StreamOrAlias) :-
+	close(StreamOrAlias, []).
+
+flush_output :-
+	current_output(S),
+	flush_output(S).
+
+stream_property(S, P) :-
+	stream_properties(Props),
+	member(prop(S,P), Props).
+
+at_end_of_stream :-
+	current_input(S),
+	stream_property(S, end_of_stream(E)),
+	!,
+	(E = at ; E = past),
+	!.
+
+at_end_of_stream(S_or_a) :-
+	( atom(S_or_a)
+	-> stream_property(S, alias(S_or_a))
+	; S = S_or_a
+	),
+	stream_property(S, end_of_stream(E)),
+	!,
+	(E = at; E = past),
+	!.
+
+% Standard exceptions
+
+instantiation_error :-
+	throw(error(instantiation_error, _)).
+
+type_error(ValidType, Culprit) :-
+	throw(error(type_error(ValidType, Culprit), _)).
+
+domain_error(ValidDomain, Culprit) :-
+	throw(error(domain_error(ValidDomain, Culprit), _)).
+
+existence_error(ObjectType, Culprit) :-
+	throw(error(existence_error(ObjectType, Culprit), _)).
+
+permission_error(Operation, PermissionType, Culprit) :-
+	throw(error(permission_error(Operation, PermissionType, Culprit), _)).
+
+representation_error(Flag) :-
+	throw(error(representation_error(Flag), _)).
+
+evaluation_error(Error) :-
+	throw(error(evaluation_error(Error), _)).
+
+resource_error(Resource) :-
+	throw(error(resource_error(Resource), _)).
+
+syntax_error(Error) :-
+	throw(error(syntax_error(Error), _)).
+
+% Input and output
+parse_read_option(variables(Vs), options(variables, Vs)).
+parse_read_option(variable_names(VNames), option(variable_names, VNames)).
+parse_read_option(singletons(S), options(singletons, S)).
+
+parse_read_options([], []).
+parse_read_options([Op|Rest], [OpParsed|RestParsed]) :-
+	is_nonvar(Op),
+	parse_read_options(Rest, RestParsed),
+	( parse_read_option(Op, OpParsed)
+	-> true
+	; domain_error(read_option, Op)
+	).
+
+read_term(S, Term, Options) :-
+	is_nonvar(Options),
+	is_list(Options),
+	parse_read_options(Options, ParsedOptions),
+	'$read_term'(S, Term, ParsedOptions).
+
+read_term(Term, Options) :-
+	current_input(S),
+	read_term(S, Term, Options).
+
+read(Term) :-
+	current_input(S),
+	read_term(S, Term, []).
+
+read(S, Term) :-
+	read_term(S, Term, []).
+
+parse_write_option(quoted(true), option(quoted, 1)).
+parse_write_option(quoted(false), option(quoted, 0)).
+parse_write_option(ignore_ops(true), option(ignore_ops, 1)).
+parse_write_option(ignore_ops(false), option(ignore_ops, 0)).
+parse_write_option(numbervars(true), option(numbervars, 1)).
+parse_write_option(numbervars(false), option(numbervars, 0)).
+
+parse_write_options([], []).
+parse_write_options([Op|Rest], [OpParsed|RestParsed]) :-
+	is_nonvar(Op),
+	parse_write_options(Rest, RestParsed),
+	( parse_write_option(Op, OpParsed)
+	-> true
+	; domain_error(write_option, Op)
+	).
+write_term(S, Term, Options) :-
+	is_nonvar(Options),
+	is_list(Options),
+	parse_write_options(Options, ParsedOptions),
+	'$write_term'(S, Term, ParsedOptions).
+
+
+write_term(Term, Options) :-
+	current_output(S),
+	write_term(S, Term, Options).
+
+write(Term) :-
+	current_output(S),
+	write_term(S, Term, [numbervars(true)]).
+
+write(S, Term) :-
+	write_term(S, Term, [numbervars(true)]).
+
+writeq(Term) :-
+	current_output(S),
+	write_term(S, Term, [quoted(true), numbervars(true)]).
+
+writeq(S, Term) :-
+	write_term(S, Term, [quoted(true), numbervars(true)]).
+
+write_canonical(Term) :-
+	current_output(S),
+	write_term(S, Term, [quoted(true), ignore_ops(true)]).
+
+write_canonical(S, Term) :-
+	write_term(S, Term, [quoted(true), ignore_ops(true)]).
+
+% Arithmetic comparisons defined in terms of >=. This is not the most effective way,
+% but it is fine for now.
+
+E1 =:= E2 :-
+	E1 >= E2,
+	E2 >= E1.
+
+E1 =\= E2 :-
+	\+ E1 =:= E2.
+
+E1 < E2 :-
+	E2 >= E1,
+	E1 =\= E2.
+
+E1 =< E2 :-
+	E2 >= E1.
+
+E1 > E2 :-
+	E2 < E1.
+
+
+% Clause retrieval and information and removal
+
+clause(Head, Body) :-
+	clause(Head, Body, Clauses),
+	member(clause(Head, Body), Clauses).
+
+current_predicate(PI) :-
+	current_predicate(PI, Predicates),
+	member(PI, Predicates).
+
+retract(Clause) :-
+	copy_term(Clause, ClauseCopy),
+	retract_one(ClauseCopy),
+	( Clause = ClauseCopy
+	; retract(Clause)
+	).
+
+% Basic list predicates
+
+member(X, [X|_]).
+member(X, [_|Tail]) :-
+	member(X, Tail).
+
+append([], Ys, Ys).
+append([X|Xs], Ys, [X|Zs]) :-
+	append(Xs, Ys, Zs).
+
+length([], 0).
+length([_|T], Len) :-
+	length(T, Len0),
+	Len is Len0 + 1.
+
+unique([], []).
+unique([H|T], [H|Rest]) :-
+	findall(_, (member(X, T), X == H), []),
+	!,
+	unique(T, Rest).
+unique([H|T], Rest) :-
+	unique(T, Rest).
+
+union(A, B, C) :-
+	append(A, B, C0),
+	unique(C0, C).
+
+difference(A, B, Diff) :-
+	append(A, B, AB),
+	unique_in(AB, AB, Diff).
+
+unique_in([], _, []).
+unique_in([H|T], L, [H|Rest]) :-
+	findall(_, (member(X, L), X == H), [_]),
+	!,
+	unique_in(T, L, Rest).
+unique_in([H|T], L, Rest) :-
+	unique_in(T, L, Rest).
+
+include(_, [], []).
+include(Goal, [X|Xs], Included) :-
+	Goal =.. L,
+	append(L, [X], L1),
+	G =.. L1,
+	( call(G)
+	-> Included = [X|Included0]
+	;  Included = Included0
+	),
+	include(Goal, Xs, Included0).
+
+% Additional type tests
+
+callable(T) :- atom(T) ; compound(T).
+
+list([]).
+list([_|T]) :- list(T).
+
+partial_list(T) :- var(T).
+partial_list([_|T]) :- partial_list(T).
+
+atomic(T) :- atom(T) ; integer(T) ; float(T).
+
+% type assertions (throws an error if false)
+
+is_atom(T) :- (atom(T) ; type_error(atom, T)), !.
+
+is_atom_or_var(T) :- (atom(T) ; var(T) ; type_error(atom, T)), !.
+
+is_callable(T) :- (callable(T) ; type_error(callable, T)), !.
+
+is_nonvar(T) :- (nonvar(T) ; instantiation_error), !.
+
+is_list_or_partial_list(T) :- (list(T) ; partial_list(T) ; type_error(list, T)), !.
+
+is_list(T) :- (list(T) ; type_error(list, T)), !.
+
+is_integer(T) :- (integer(T) ; type_error(integer, T)), !.
+
+is_predicate_indicator(T) :- ((nonvar(T), T = N/A, integer(A), atom(N)) ; type_error(predicate_indicator, T)), !.
+
+% All solutions
+
+findall(Template, Goal, Instances) :-
+	is_nonvar(Goal),
+	is_callable(Goal),
+	is_list_or_partial_list(Instances),
+	system:asserta('find all'([])),
+	call(Goal),
+	system:asserta('find all'(solution(Template))),
+	fail.
+findall(Template, Goal, Instances) :-
+	findall_collect([], Instances).
+
+findall_collect(Acc, Instances) :-
+	system:retract('find all'(Item)),
+	!,
+	findall_collect(Item, Acc, Instances).
+findall_collect([], Instances, Instances).
+findall_collect(solution(T), Acc, Instances) :-
+	findall_collect([T|Acc], Instances).
+
+bagof(Template, Goal, Instances) :-
+	free_variable_set(Goal, Template, Witness),
+	iterated_goal(Goal, G),
+	findall(Witness+Template, G, S),
+	bagof_loop(Witness, S, Instances).
+
+bagof_loop(Witness, S, Instances) :-
+	[W+T|_] = S,
+	bagof_wt_list(S, W+T, WT_list),
+	bagof_split(WT_list, W_list, T_list),
+	( bagof_unify_list(Witness, W_list), Instances = T_list
+	; bagof_next_s(S, WT_list, S_next), bagof_loop(Witness, S_next, Instances)
+	).
+
+bagof_wt_list([], _, []).
+bagof_wt_list([W+T|Tail], W0+T0, Rest) :-
+	copy_term(W+T, W1+T1),
+	bagof_wt_list(Tail, W0+T0, Rest0),
+	( variant(W1, W0)
+	-> Rest = [W1+T1|Rest0]
+	; Rest = Rest0
+	).
+
+copy_terms_list([], []).
+copy_terms_list([H|T], [HH|TT]) :-
+	copy_term(H, HH),
+	copy_terms_list(T, TT).
+bagof_split([], [], []).
+bagof_split([WW+TT|RestWT], [WW|RestW], [TT|RestT]) :-
+	bagof_split(RestWT, RestW, RestT).
+
+bagof_unify_list(W, []).
+bagof_unify_list(W, [W|T]) :- bagof_unify_list(W, T).
+
+bagof_next_s([], _, []).
+bagof_next_s([H|T], WT_list, Rest) :-
+	bagof_next_s(T, WT_list, Rest0),
+	( findall(_, (member(X, WT_list), variant(X, H)), [])
+	-> Rest = [H|Rest0]
+	; Rest = Rest0
+	).
+
+setof(Template, Goal, Instances) :-
+	bagof(Template, Goal, Instances_list),
+	sort(Instances_list, Instances).
+
+% misc helpers
+
+variable_set(Term, []) :-
+	atomic(Term).
+variable_set(Term, [Term]) :-
+	var(Term).
+variable_set(Term, Vars) :-
+	compound(Term),
+	Term =.. [_|Args],
+	variable_set(Args, [], Vars0),
+	unique(Vars0, Vars).
+
+variable_set([], Acc, Acc).
+variable_set([Arg|Rest], Acc0, Result) :-
+	variable_set(Arg, VarSet),
+	append(Acc0, VarSet, Acc),
+	variable_set(Rest, Acc, Result).
+
+existential_variable_set(Term, []) :-
+	(atomic(Term) ; var(Term)),
+	!.
+existential_variable_set(V^G, Vars) :-
+	!,
+	existential_variable_set(G, Vars0),
+	variable_set(V, Vars1),
+	union(Vars0, Vars1, Vars).
+existential_variable_set(_, []).
+
+free_variable_set(T, V, Vars) :-
+	variable_set(T, TVars),
+	variable_set(V, VVars),
+	existential_variable_set(T, TExVars),
+	union(VVars, TExVars, BV),
+	difference(TVars, BV, Vars).
+
+iterated_goal(Goal, T) :-
+	compound(Goal),
+	_^G = Goal,
+	!,
+	iterated_goal(G, T).
+iterated_goal(G, G).
+
+variant(T1, T2) :-
+	var(T1), var(T2), !.
+variant(T1, T2) :-
+	compound(T1),
+	compound(T2),
+	!,
+	T1 =.. [Name|Args1],
+	T2 =.. [Name|Args2],
+	variant_list(Args1, Args2).
+variant(T1, T2) :-
+	T1 == T2.
+
+variant_list([], []).
+variant_list([H1|T1], [H2|T2]) :-
+	variant(H1, H2),
+	variant_list(T1, T2).
+
+% Sorting, which also removes duplicates (should be implemented in C for speed I think).
+
+sort(Ls0, Ls) :-
+	append(Lefts, [A,B|Rights], Ls0),
+	A @> B,
+	!,
+	append(Lefts, [B,A|Rights], Ls1),
+	sort(Ls1, Ls).
+sort(Ls0, Ls) :-
+	append(Lefts, [A,B|Rights], Ls0),
+	A == B,
+	!,
+	append(Lefts, [A|Rights], Ls1),
+	sort(Ls1, Ls).
+sort(Ls, Ls).
+
+% Atomic term processing
+
+atom_concat(A1, A2, A3) :-
+	is_atom_or_var(A1),
+	is_atom_or_var(A2),
+	is_atom_or_var(A3),
+	atom(A1), atom(A2),
+	!,
+	atom_codes(A1, Codes1),
+	atom_codes(A2, Codes2),
+	append(Codes1, Codes2, Codes),
+	atom_codes(A3, Codes).
+atom_concat(A1, A2, A3) :-
+	is_atom_or_var(A1),
+	is_atom_or_var(A2),
+	is_atom_or_var(A3),
+	atom(A3),
+	!,
+	atom_codes(A3, Codes),
+	append(Codes1, Codes2, Codes),
+	atom_codes(A1, Codes1),
+	atom_codes(A2, Codes2).
+atom_concat(A1, A2, A3) :-
+	instantiation_error.
+
+% Character input/output
+
+get_char(Char) :-
+	current_input(S),
+	get_char(S, Char).
+
+get_code(Code) :-
+	current_input(S),
+	get_code(S, Code).
+
+get_code(S, Code) :-
+	get_char(S, Char),
+	( Char = end_of_file
+	-> Code = -1
+	; char_code(Char, Code)
+	).
+
+peek_char(Char) :-
+	current_input(S),
+	peek_char(S, Char).
+
+peek_code(Code) :-
+	current_input(S),
+	peek_code(S, Code).
+
+peek_code(S, Code) :-
+	peek_char(S, Char),
+	( Char = end_of_file
+	-> Code = -1
+	; char_code(Char, Code)
+	).
+
+put_char(Char) :-
+	current_output(S),
+	put_char(S, Char).
+
+put_code(Code) :-
+	current_output(S),
+	put_code(S, Code).
+
+put_code(S, Code) :-
+	char_code(Char, Code),
+	put_char(S, Char).
+
+nl :-
+	current_output(S),
+	nl(S).
+
+nl(S) :-
+	put_char(S, '
+'). % This should really be \n
+
+% flags
+set_prolog_flag(Flag, Value) :-
+	is_nonvar(Flag),
+	is_nonvar(Value),
+	is_atom(Flag),
+	is_prolog_flag(Flag),
+	is_appropriate_flag_value(Flag, Value),
+	is_modifiable_flag(Flag),
+	'$set_prolog_flag'(Flag, Value).
+
+current_prolog_flag(Flag, Value) :-
+	is_atom_or_var(Flag),
+	( atom(Flag)
+	-> is_prolog_flag(Flag)
+	; true
+	),
+	current_prolog_flags(FlagsAndValues),
+	member(flag(Flag, Value), FlagsAndValues).
+
+is_prolog_flag(Flag) :-
+	member(Flag, 
+		[ bounded
+		, max_integer
+		, min_integer
+		, integer_rounding_function
+		, char_conversion
+		, debug
+		, max_arity
+		, unknown
+		, double_quotes]),
+	!
+	; domain_error(prolog_flag, Flag).
+
+is_modifiable_flag(Flag) :-
+	member(Flag, [char_conversion, debug, unknown, double_quotes]),
+	!
+	; permission_error(modify, flag, Flag).
+
+is_appropriate_flag_value(Flag, Value) :-
+	appropriate_flag_values(Flag, Values), 
+	member(Value, Values),
+	!
+	; domain_error(flag_value, Flag + Value).
+
+appropriate_flag_values(bounded, [true, false]).
+appropriate_flag_values(max_integer, [Val]) :-
+	current_prolog_flag(max_integer, Val).
+appropriate_flag_values(min_integer, [Val]) :-
+	current_prolog_flag(min_integer, Val).
+appropriate_flag_values(integer_rounding_function, [down, toward_zero]).
+appropriate_flag_values(char_conversion, [on, off]).
+appropriate_flag_values(debug, [on, off]).
+appropriate_flag_values(max_arity, [Val]) :-
+	current_prolog_flag(max_arity).
+appropriate_flag_values(unknown, [error, fail, warning]).
+appropriate_flag_values(double_quotes, [chars, codes, atom]).
+
+% Operator table modification and inspection
+
+op(Priority, Op_specifier, Operator) :-
+	is_nonvar(Priority),
+	is_integer(Priority),
+	is_nonvar(Op_specifier),
+	is_atom(Op_specifier),
+	( operator_priority(Priority), !
+	; domain_error(operator_priority, Priority)
+	),
+	( operator_specifier(Op_specifier), !
+	; domain_error(operator_specifier, Op_specifier)
+	),
+	is_nonvar(Operator),
+	( atom(Operator)
+	-> Ops = [Operator]
+	; Ops = Operator
+	),
+	is_list(Ops),
+	op_helper(Priority, Op_specifier, Ops).
+
+op_helper(Priority, Op_specifier, []).
+op_helper(Priority, Op_specifier, [Op|Ops]) :-
+	is_nonvar(Op),
+	is_atom(Op),
+	'$op'(Priority, Op_specifier, Op),
+	op_helper(Priority, Op_specifier, Ops).
+
+operator_priority(P) :-
+	integer(P),
+	P >= 0,
+	P =< 1200.
+
+operator_specifier(S) :-
+	member(S, [xf, yf, xfx, xfy, yfx, fx, fy]).
+
+current_op(Priority, Op_specifier, Operator) :-
+	( (var(Priority) ; operator_priority(Priority)), !
+	; domain_error(operator_priority, Priority)
+	),
+	( (var(Op_specifier) ; operator_specifier(Op_specifier)), !
+	; domain_error(operator_specifier, Op_specifier)
+	),
+	is_atom_or_var(Operator),
+	current_ops(Operators),
+	member(op(Priority, Op_specifier, Operator), Operators).
+
+% Halting
+
+halt(X) :-
+	is_nonvar(X),
+	is_integer(X),
+	'$halt'(X).
+
+halt :-
+	halt(0).
+
+% Loading prolog text
+
+consult(File) :-
+	loader:load_module_from_file(File).