ref: 44ab8a339c78bcc3460d44b2f435116f21faa60a
parent: 3f26a0f2a1f699e628136ec5be6178b5ab40fc44
author: Peter Mikkelsen <peter@pmikkelsen.com>
date: Mon Jul 5 12:27:38 EDT 2021
First step on modules. Still very very rough.
--- a/TODO
+++ b/TODO
@@ -5,4 +5,4 @@
* Right now we copy and allocate a lot, but almost never free stuff.
* Many builtins should really throw an error, but they just fail for now.
* Exceptions (throw, catch)
-* Modules
+* Modules (I try to do something like SWI prolog for now, but I know there is also an iso standard)
\ No newline at end of file
--- a/builtins.c
+++ b/builtins.c
@@ -5,7 +5,7 @@
#include "dat.h"
#include "fns.h"
-#define BuiltinProto(name) int name(Term *, Term *, Binding **)
+#define BuiltinProto(name) int name(Term *, Binding **)
#define Match(X, Y) (runestrcmp(name, X) == 0 && arity == Y)
#define Throw(What) do{\
Goal *g = malloc(sizeof(Goal)); \
@@ -128,9 +128,8 @@
}
int
-builtinfail(Term *database, Term *goal, Binding **bindings)
+builtinfail(Term *goal, Binding **bindings)
{
- USED(database);
USED(goal);
USED(bindings);
return 0;
@@ -137,9 +136,8 @@
}
int
-builtincall(Term *database, Term *goal, Binding **bindings)
+builtincall(Term *goal, Binding **bindings)
{
- USED(database);
USED(bindings);
Goal *g = malloc(sizeof(Goal));
@@ -152,9 +150,8 @@
}
int
-builtincut(Term *database, Term *goal, Binding **bindings)
+builtincut(Term *goal, Binding **bindings)
{
- USED(database);
USED(bindings);
Choicepoint *cp = choicestack;
@@ -169,9 +166,8 @@
}
int
-builtinvar(Term *database, Term *goal, Binding **bindings)
+builtinvar(Term *goal, Binding **bindings)
{
- USED(database);
USED(bindings);
Term *arg = goal->children;
return (arg->tag == VariableTerm);
@@ -178,9 +174,8 @@
}
int
-builtinatom(Term *database, Term *goal, Binding **bindings)
+builtinatom(Term *goal, Binding **bindings)
{
- USED(database);
USED(bindings);
Term *arg = goal->children;
return (arg->tag == AtomTerm);
@@ -187,9 +182,8 @@
}
int
-builtininteger(Term *database, Term *goal, Binding **bindings)
+builtininteger(Term *goal, Binding **bindings)
{
- USED(database);
USED(bindings);
Term *arg = goal->children;
return (arg->tag == NumberTerm && arg->numbertype == NumberInt);
@@ -196,9 +190,8 @@
}
int
-builtinfloat(Term *database, Term *goal, Binding **bindings)
+builtinfloat(Term *goal, Binding **bindings)
{
- USED(database);
USED(bindings);
Term *arg = goal->children;
return (arg->tag == NumberTerm && arg->numbertype == NumberFloat);
@@ -205,9 +198,8 @@
}
int
-builtinatomic(Term *database, Term *goal, Binding **bindings)
+builtinatomic(Term *goal, Binding **bindings)
{
- USED(database);
USED(bindings);
Term *arg = goal->children;
return (arg->tag == AtomTerm || arg->tag == NumberTerm);
@@ -214,9 +206,8 @@
}
int
-builtincompound(Term *database, Term *goal, Binding **bindings)
+builtincompound(Term *goal, Binding **bindings)
{
- USED(database);
USED(bindings);
Term *arg = goal->children;
return (arg->tag == CompoundTerm);
@@ -223,9 +214,8 @@
}
int
-builtinnonvar(Term *database, Term *goal, Binding **bindings)
+builtinnonvar(Term *goal, Binding **bindings)
{
- USED(database);
USED(bindings);
Term *arg = goal->children;
return (arg->tag != VariableTerm);
@@ -232,9 +222,8 @@
}
int
-builtinnumber(Term *database, Term *goal, Binding **bindings)
+builtinnumber(Term *goal, Binding **bindings)
{
- USED(database);
USED(bindings);
Term *arg = goal->children;
return (arg->tag == NumberTerm);
@@ -296,9 +285,8 @@
}
int
-builtincompare(Term *database, Term *goal, Binding **bindings)
+builtincompare(Term *goal, Binding **bindings)
{
- USED(database);
Term *order = goal->children;
Term *t1 = order->next;
Term *t2 = t1->next;
@@ -317,9 +305,8 @@
}
int
-builtinfunctor(Term *database, Term *goal, Binding **bindings)
+builtinfunctor(Term *goal, Binding **bindings)
{
- USED(database);
Term *term = goal->children;
Term *name = term->next;
@@ -354,9 +341,8 @@
}
int
-builtinarg(Term *database, Term *goal, Binding **bindings)
+builtinarg(Term *goal, Binding **bindings)
{
- USED(database);
Term *n = goal->children;
Term *term = n->next;
@@ -390,9 +376,8 @@
}
int
-builtinuniv(Term *database, Term *goal, Binding **bindings)
+builtinuniv(Term *goal, Binding **bindings)
{
- USED(database);
Term *term = goal->children;
Term *list = term->next;
@@ -463,9 +448,8 @@
}
int
-builtinis(Term *database, Term *goal, Binding **bindings)
+builtinis(Term *goal, Binding **bindings)
{
- USED(database);
Term *result = goal->children;
Term *expr = result->next;
@@ -478,9 +462,8 @@
}
int
-builtincatch(Term *database, Term *goal, Binding **bindings)
+builtincatch(Term *goal, Binding **bindings)
{
- USED(database);
USED(bindings);
Term *catchgoal = goal->children;
@@ -503,9 +486,8 @@
}
int
-builtinthrow(Term *database, Term *goal, Binding **bindings)
+builtinthrow(Term *goal, Binding **bindings)
{
- USED(database);
USED(bindings);
Term *ball = goal->children;
@@ -543,9 +525,8 @@
}
int
-builtincurrentprologflag(Term *database, Term *goal, Binding **bindings)
+builtincurrentprologflag(Term *goal, Binding **bindings)
{
- USED(database);
USED(goal);
USED(bindings);
return 0;
@@ -552,9 +533,8 @@
}
int
-builtinsetprologflag(Term *database, Term *goal, Binding **bindings)
+builtinsetprologflag(Term *goal, Binding **bindings)
{
- USED(database);
USED(bindings);
Term *key = goal->children;
Term *value = key->next;
@@ -572,9 +552,8 @@
}
int
-builtinopen(Term *database, Term *goal, Binding **bindings)
+builtinopen(Term *goal, Binding **bindings)
{
- USED(database);
USED(bindings);
Term *sourcesink = goal->children;
@@ -608,9 +587,8 @@
}
int
-builtinclose(Term *database, Term *goal, Binding **bindings)
+builtinclose(Term *goal, Binding **bindings)
{
- USED(database);
USED(bindings);
Term *stream = goal->children;
@@ -634,9 +612,8 @@
}
int
-builtincurrentinput(Term *database, Term *goal, Binding **bindings)
+builtincurrentinput(Term *goal, Binding **bindings)
{
- USED(database);
USED(bindings);
Term *stream = goal->children;
@@ -648,9 +625,8 @@
}
int
-builtincurrentoutput(Term *database, Term *goal, Binding **bindings)
+builtincurrentoutput(Term *goal, Binding **bindings)
{
- USED(database);
USED(bindings);
Term *stream = goal->children;
@@ -662,9 +638,8 @@
}
int
-builtinsetinput(Term *database, Term *goal, Binding **bindings)
+builtinsetinput(Term *goal, Binding **bindings)
{
- USED(database);
USED(bindings);
Term *stream = goal->children;
@@ -685,9 +660,8 @@
}
int
-builtinsetoutput(Term *database, Term *goal, Binding **bindings)
+builtinsetoutput(Term *goal, Binding **bindings)
{
- USED(database);
USED(bindings);
Term *stream = goal->children;
@@ -708,9 +682,8 @@
}
int
-builtinreadterm(Term *database, Term *goal, Binding **bindings)
+builtinreadterm(Term *goal, Binding **bindings)
{
- USED(database);
USED(bindings);
Term *stream = goal->children;
@@ -739,11 +712,10 @@
}
int
-builtinwriteterm(Term *database, Term *goal, Binding **bindings)
+builtinwriteterm(Term *goal, Binding **bindings)
{
- USED(database);
USED(bindings);
-
+
Term *stream = goal->children;
Term *term = stream->next;
Term *options = term->next;
@@ -763,4 +735,3 @@
writeterm(stream, options, term);
return 1;
}
-
--- a/dat.h
+++ b/dat.h
@@ -2,7 +2,9 @@
typedef struct Binding Binding;
typedef struct Goal Goal;
typedef struct Choicepoint Choicepoint;
-typedef int (*Builtin)(Term *, Term *, Binding **);
+typedef struct Clause Clause;
+typedef struct Module Module;
+typedef int (*Builtin)(Term *, Binding **);
struct Term
{
@@ -36,11 +38,29 @@
struct Choicepoint
{
Goal *goalstack;
- Term *retryclause;
+ Clause *retryclause;
uvlong id; /* Unique number for each clause. Used to know where to cut to. */
+ Module *currentmodule;
Choicepoint *next;
};
+struct Clause
+{
+ Term *head;
+ Term *body;
+ uvlong clausenr;
+ int public;
+ Clause *next;
+};
+
+struct Module
+{
+ /* What about imports */
+ Rune *name;
+ Clause *clauses;
+ Module *next;
+};
+
/* Sorted so that a lower value means it comes earlier in the standard ordering */
enum {
VariableTerm,
@@ -55,7 +75,6 @@
};
int debug;
-Term *initgoals;
/* Flags */
enum {
@@ -66,6 +85,9 @@
int flagdoublequotes;
-/* Staate of the running system */
+/* State of the running system */
Choicepoint *choicestack;
-Goal *goalstack;
\ No newline at end of file
+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 */
--- a/eval.c
+++ b/eval.c
@@ -6,7 +6,7 @@
#include "fns.h"
Goal *addgoals(Goal *, Term *);
-Term *findclause(Term *, Term *, Binding **);
+Clause *findclause(Clause *, Term *, Binding **);
int equalterms(Term *, Term *);
Goal *copygoals(Goal *);
Builtin findbuiltin(Term *);
@@ -14,8 +14,9 @@
static uvlong clausenr;
int
-evalquery(Term *database, Term *query, Binding **resultbindings)
+evalquery(Term *query, Binding **resultbindings)
{
+ static Module *currentmodule = nil;
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.
@@ -37,42 +38,60 @@
goalstack = addgoals(goalstack, query);
clausenr = 2; /* Start at two since 0 is for the facts in the database, and 1 is for queries */
+
+ currentmodule = usermodule;
}else{
goto Backtrack;
}
while(goalstack->goal != nil){
- Term *dbstart;
+ Clause *startclause;
Term *goal;
Goal *oldgoalstack;
- dbstart = database;
+ startclause = nil; /* Where to start looking for a matching clause. Used by backtracking */
Retry:
- print("Loop run\n");
goal = goalstack->goal;
oldgoalstack = goalstack;
goalstack = goalstack->next;
- if(oldgoalstack->catcher){
- print("Was catchframe\n");
+ if(oldgoalstack->catcher)
continue;
- }
if(debug)
print("Working goal: %S\n", prettyprint(goal, 0, 0, 0));
+ if(startclause == nil && goal->tag == CompoundTerm && goal->arity == 2 && runestrcmp(goal->text, L":") == 0){
+ Term *module = goal->children;
+ if(module->tag == AtomTerm){
+ Module *m = getmodule(module->text);
+ if(m == nil)
+ goal = existenceerror(L"module", module);
+ else{
+ goal = module->next;
+ currentmodule = m;
+ startclause = m->clauses;
+ oldgoalstack->goal = goal;
+ }
+ }else
+ goal = typeerror(L"module", module);
+ }
+
+ if(startclause == nil)
+ startclause = currentmodule->clauses;
+
Binding *bindings = nil;
- Term *clause = nil;
+ Clause *clause = nil;
/* Try to see if the goal can be solved using a builtin first */
Builtin builtin = findbuiltin(goal);
if(builtin != nil){
- int success = builtin(database, goal, &bindings);
+ int success = builtin(goal, &bindings);
if(!success)
goto Backtrack;
}else{
/* Find a clause where the head unifies with the goal */
- clause = findclause(dbstart, goal, &bindings);
+ clause = findclause(startclause, goal, &bindings);
if(clause != nil){
if(clause->next != nil){
/* Add a choicepoint. Note we create a choicepoint every time, so there is room for improvement. */
@@ -81,6 +100,7 @@
cp->next = choicestack;
cp->retryclause = clause->next;
cp->id = clause->clausenr;
+ cp->currentmodule = currentmodule;
choicestack = cp;
}
}else{
@@ -93,7 +113,8 @@
choicestack = cp->next;
/* freegoals(goals) */
goalstack = cp->goalstack;
- dbstart = cp->retryclause;
+ currentmodule = cp->currentmodule;
+ startclause = cp->retryclause;
goto Retry;
}
}
@@ -106,8 +127,8 @@
}
/* Add clause body as goals, with bindings applied */
- if(clause != nil && clause->tag == CompoundTerm && clause->arity == 2 && runestrcmp(clause->text, L":-") == 0){
- Term *subgoal = copyterm(clause->children->next, nil);
+ if(clause != nil && clause->body != nil){
+ Term *subgoal = copyterm(clause->body, nil);
applybinding(subgoal, bindings);
goalstack = addgoals(goalstack, subgoal);
}
@@ -133,21 +154,18 @@
return goals;
}
-Term *
-findclause(Term *database, Term *goal, Binding **bindings)
+Clause *
+findclause(Clause *clauses, Term *goal, Binding **bindings)
{
- Term *clause;
- Term *head;
- for(; database != nil; database = database->next){
- clause = copyterm(database, &clausenr);
- clausenr++;
- clause->next = database->next;
- if(clause->tag == CompoundTerm && runestrcmp(clause->text, L":-") == 0 && clause->arity == 2)
- head = clause->children;
- else
- head = clause;
+ Clause *clause;
+ for(; clauses != nil; clauses = clauses->next){
+ if(!clauses->public)
+ continue;
- if(unify(head, goal, bindings))
+ clause = copyclause(clauses, &clausenr);
+ clausenr++;
+ clause->next = clauses->next;
+ if(unify(clause->head, goal, bindings))
return clause;
}
return nil;
--- a/example.pl
+++ b/example.pl
@@ -1,3 +1,5 @@
+:- module(example, []).
+
math(A,B,C,D) :- D is A + B + C * A.
parentest :-
--- a/fns.h
+++ b/fns.h
@@ -14,14 +14,15 @@
Term *mknumber(int, vlong, double);
Term *mkstring(Rune *);
Term *mklist(Term *);
+Clause *copyclause(Clause *, uvlong *);
/* eval.c */
-int evalquery(Term *, Term *, Binding **);
+int evalquery(Term *, Binding **);
int unify(Term *, Term *, Binding **);
void applybinding(Term *, Binding *);
/* repl.c */
-void repl(Term *);
+void repl(void);
/* builtins.c */
Builtin findbuiltin(Term *);
@@ -55,4 +56,9 @@
int istextstream(Term *);
int isbinarystream(Term *);
int readterm(Term *, Term *, Term **);
-void writeterm(Term *, Term *, Term *);
\ No newline at end of file
+void writeterm(Term *, Term *, Term *);
+
+/* module.c */
+void initmodules(void);
+Module *parsemodule(char *);
+Module *getmodule(Rune *);
\ No newline at end of file
--- a/main.c
+++ b/main.c
@@ -10,50 +10,25 @@
void
main(int argc, char *argv[])
{
- char *parsetestfile = nil;
-
ARGBEGIN{
case 'd':
debug = 1;
break;
- case 'f':
- parsetestfile = EARGF(usage());
- break;
default:
usage();
}ARGEND
- if(argc != 0)
- usage();
-
initflags();
initstreams();
+ initmodules();
- int fd = open("./stdlib.pl", OREAD);
- if(fd < 0){
- print("Can't open ./stdlib.pl\n");
- exits("open");
+ while(argc != 0){
+ parsemodule(argv[0]);
+ argc--;
+ argv++;
}
- Term *database = parse(fd, nil, 0);
- close(fd);
-
- if(parsetestfile){
- int fd = open(parsetestfile, OREAD);
- if(fd < 0)
- exits("open");
- Term *clauses = parse(fd, nil, 0);
- database = appendterm(database, clauses);
-
- Term *goal;
- for(goal = initgoals; goal != nil; goal = goal->next){
- Binding *bindings = nil;
- evalquery(database, goal, &bindings);
- }
- }
-
- repl(database);
-
+ repl();
exits(nil);
}
@@ -60,6 +35,6 @@
void
usage(void)
{
- fprint(2, "Usage: pprolog [-d]\n");
+ fprint(2, "Usage: pprolog [-d] modulefiles\n");
exits("Usage");
}
\ No newline at end of file
--- a/misc.c
+++ b/misc.c
@@ -132,3 +132,21 @@
return mkcompound(L".", 2, t);
}
}
+
+Clause *
+copyclause(Clause *orig, uvlong *clausenr)
+{
+ Clause *new = malloc(sizeof(Clause));
+ new->head = copyterm(orig->head, clausenr);
+ if(orig->body)
+ new->body = copyterm(orig->body, clausenr);
+ else
+ new->body = nil;
+ if(clausenr)
+ new->clausenr = *clausenr;
+ else
+ new->clausenr = orig->clausenr;
+ new->public = orig->public;
+ new->next = nil;
+ return new;
+}
\ No newline at end of file
--- a/mkfile
+++ b/mkfile
@@ -12,7 +12,8 @@
repl.$O\
flags.$O\
error.$O\
- streams.$O
+ streams.$O\
+ module.$O
HFILES=dat.h fns.h
--- /dev/null
+++ b/module.c
@@ -1,0 +1,116 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+
+#include "dat.h"
+#include "fns.h"
+
+Module *addemptymodule(Rune *);
+Clause *appendclause(Clause *, Clause *);
+
+void
+initmodules(void)
+{
+ systemmodule = parsemodule("./stdlib.pl");
+ if(systemmodule == nil){
+ print("Can't load ./stdlib.pl\n");
+ exits(nil);
+ }
+
+ usermodule = addemptymodule(L"user");
+}
+
+Module *
+parsemodule(char *file)
+{
+ Module *m = nil;
+
+ int fd = open(file, OREAD);
+ if(fd < 0)
+ return nil;
+ Term *terms = parse(fd, nil, 0);
+
+ if(terms == nil)
+ return nil;
+
+ /* 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));
+ return nil;
+ }
+ print("Public list for module '%S': %S\n", modulename->text, prettyprint(publiclist, 0, 0, 0));
+ m = addemptymodule(modulename->text);
+ }
+ terms = terms->next;
+ }
+
+ Term *t;
+ for(t = terms; t != nil; t = t->next){
+ Clause *cl = malloc(sizeof(Clause));
+ cl->clausenr = 0;
+ cl->public = 1; /* everything is public for now */
+ cl->next = nil;
+ if(t->tag == CompoundTerm && runestrcmp(t->text, L":-") == 0 && t->arity == 2){
+ cl->head = t->children;
+ cl->body = t->children->next;
+ }else{
+ cl->head = t;
+ cl->body = nil;
+ }
+
+ if(m == nil)
+ usermodule->clauses = appendclause(usermodule->clauses, cl);
+ else
+ m->clauses = appendclause(m->clauses, cl);
+ }
+
+ return m;
+}
+
+Module *
+getmodule(Rune *name)
+{
+ Module *m;
+ for(m = modules; m != nil; m = m->next){
+ if(runestrcmp(m->name, name) == 0)
+ return m;
+ }
+ return nil;
+}
+
+Module *
+addemptymodule(Rune *name)
+{
+ Module *m = malloc(sizeof(Module));
+ m->name = name;
+ m->next = modules;
+
+ if(systemmodule == nil)
+ m->clauses = nil;
+ else
+ m->clauses = systemmodule->clauses; /* Direct access to system clauses for now, but when I figure out imports this will change */
+ modules = m;
+ return m;
+}
+
+Clause *
+appendclause(Clause *clauses, Clause *new)
+{
+ Clause *tmp;
+
+ if(clauses == nil)
+ return new;
+
+ for(tmp = clauses; tmp->next != nil; tmp = tmp->next);
+
+ tmp->next = new;
+ return clauses;
+}
\ No newline at end of file
--- a/parser.c
+++ b/parser.c
@@ -91,7 +91,6 @@
}else
parsein = bio;
- initgoals = nil;
initoperators();
nexttoken();
@@ -125,12 +124,10 @@
if(t->tag == CompoundTerm && runestrcmp(t->text, L":-") == 0 && t->arity == 1){
Term *body = t->children;
print("Got directive: %S\n", prettyprint(body, 0, 0, 0));
- if(body->tag == CompoundTerm && body->arity == 1 && runestrcmp(body->text, L"initialization") == 0){
- Term *tmp = initgoals;
- initgoals = body->children;
- initgoals->next = tmp;
- }
- t = prologtext(querymode);
+ if(runestrcmp(body->text, L"module") == 0 && body->arity == 2)
+ t->next = prologtext(querymode);
+ else
+ t = prologtext(querymode);
}else if(t->tag == CompoundTerm && runestrcmp(t->text, L":-") == 0 && t->arity == 2){
t->next = prologtext(querymode);
}else if(t->tag == AtomTerm || t->tag == CompoundTerm){
@@ -391,6 +388,7 @@
addoperator(700, Xfx, L">");
addoperator(700, Xfx, L">=");
addoperator(700, Xfx, L"=..");
+ addoperator(600, Xfy, L":");
addoperator(500, Yfx, L"+");
addoperator(400, Yfx, L"*");
addoperator(400, Yfx, L"/");
--- a/repl.c
+++ b/repl.c
@@ -8,7 +8,7 @@
Rune parsefindmore(int);
void
-repl(Term *database)
+repl(void)
{
int fd = 0; /* Standard input */
while(1){
@@ -19,7 +19,7 @@
goalstack = nil; /* should free old choicestack and goalstack */
int success;
FindMore:
- success = evalquery(database, query, &bindings);
+ success = evalquery(query, &bindings);
if(success == 0)
print("false.\n");
else{
--- a/stdlib.pl
+++ b/stdlib.pl
@@ -1,3 +1,5 @@
+:- module(system, []).
+
% Logic and control predicates
\+ Goal :- call(Goal), !, fail.
\+ Goal.
@@ -57,17 +59,6 @@
A == B.
A @>= B :-
A @> B.
-
-% List predicates
-
-length([], 0).
-length([_|Tail], Length) :-
- length(Tail, Length0),
- Length is Length0 + 1.
-
-member(X, [X|_]).
-member(X, [_|Tail]) :-
- member(X, Tail).
% Input output