shithub: pprolog

ref: c85de58a2047c4858825d03977e490db6168fbe3
dir: /module.c/

View raw version
#include <u.h>
#include <libc.h>
#include <bio.h>

#include "dat.h"
#include "fns.h"

void handleopdirective(Term *, Module *);

void
initmodules(void)
{
	addemptymodule(L"user");
	if(!addtousermod("/sys/lib/prolog/system.pl")){
		print("Can't load /sys/lib/prolog/system.pl\n");
		exits(nil);
	}
	if(!addtousermod("/sys/lib/prolog/loader.pl")){
		print("Can't load /sys/lib/prolog/loader.pl\n");
		exits(nil);
	}
}

int
addtousermod(char *file)
{
	Biobuf *bio = Bopen(file, OREAD);
	if(bio == nil)
		return 0;

	Module *usermodule = getmodule(L"user");
	Predicate *currentpred = nil;

	Term *t;
	while(t = parse(bio, usermodule)){
		Clause *cl = gmalloc(sizeof(Clause));
		int arity;
		cl->clausenr = 0;
		cl->next = nil;
		if(t->tag == CompoundTerm && runestrcmp(t->text, L":-") == 0 && t->arity == 1){
			Term *body = t->children;
			if(runestrcmp(body->text, L"op") == 0 && body->arity == 3)
				handleopdirective(body->children, usermodule);
			continue;
		}else 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 = mkatom(L"true");
		}
		if(cl->head->tag == AtomTerm)
			arity = 0;
		else
			arity = cl->head->arity;

		/* 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){
			usermodule->predicates = appendpredicate(currentpred, usermodule->predicates);
			currentpred = gmalloc(sizeof(Predicate));
			currentpred->name = cl->head->text;
			currentpred->arity = arity;
			currentpred->clauses = cl;
			currentpred->public = 1; /* everything is public for now */
			currentpred->builtin = 0;
			currentpred->dynamic = 1; /* everything is dynamic for now */
			currentpred->next = nil;
		}else
			currentpred->clauses = appendclause(currentpred->clauses, cl);
	}
	usermodule->predicates = appendpredicate(currentpred, usermodule->predicates);

	Bterm(bio);
	return 1;
}

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 = gmalloc(sizeof(Module));
	m->name = name;
	m->next = modules;
	m->predicates = nil;
	memset(m->operators, 0, sizeof(m->operators));

	Module *systemmodule = getmodule(L"system");
	if(systemmodule != nil){
		int level;
		Operator *op;
		for(level = 0; level < PrecedenceLevels; level++){
			for(op = systemmodule->operators[level]; op != nil; op = op->next)
				addoperator(op->level, op->type, op->spelling, m);
		}
	}
	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 *
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;
}

Predicate *
appendpredicate(Predicate *preds, Predicate *new)
{
	Predicate *tmp;

	if(preds == nil)
		return new;

	for(tmp = preds; tmp->next != nil; tmp = tmp->next);

	tmp->next = new;
	return preds;
}

Operator *
getoperator(Rune *spelling, Module *mod)
{
	Operator *op = nil;
	int level;

	if(spelling == nil || mod == nil)
		return nil;

	for(level = 0; level < PrecedenceLevels && op == nil; level++){
		Operator *tmp;
		for(tmp = mod->operators[level]; tmp != nil; tmp = tmp->next){
			if(runestrcmp(tmp->spelling, spelling) == 0){
				if(op == nil){
					op = gmalloc(sizeof(Operator));
					memcpy(op, tmp, sizeof(Operator));
				}else
					op->type |= tmp->type;
			}
		}
	}
	return op;
}

void
addoperator(int level, int type, Rune *spelling, Module *mod)
{
	if(mod == nil)
		return;

	/* the operator table is never garbage collected, so just use normal malloc */
	Operator *op = malloc(sizeof(Operator));
	op->type = type;
	op->level = level;
	op->spelling = spelling;
	op->next = mod->operators[level-1];
	mod->operators[level-1] = op;
}

void
handleopdirective(Term *args, Module *mod)
{
	Term *levelt = args;
	Term *typet = levelt->next;
	Term *opt = typet->next;
	if(levelt->tag == IntegerTerm 
	    && levelt->ival >= 0 
	    && levelt->ival <= PrecedenceLevels
	    && typet->tag == AtomTerm
	    && opt->tag == AtomTerm){
		int level = levelt->ival;
		Rune *spelling = opt->text;
		int type = 0;
		if(runestrcmp(typet->text, L"xf") == 0)
			type = Xf;
		else if(runestrcmp(typet->text, L"yf") == 0)
			type = Yf;
		else if(runestrcmp(typet->text, L"xfx") == 0)
			type = Xfx;
		else if(runestrcmp(typet->text, L"xfy") == 0)
			type = Xfy;
		else if(runestrcmp(typet->text, L"yfx") == 0)
			type = Yfx;
		else if(runestrcmp(typet->text, L"fy") == 0)
			type = Fy;
		else if(runestrcmp(typet->text, L"fx") == 0)
			type = Fx;
		if(type != 0){
			addoperator(level, type, spelling, mod);
			return;
		}
	}
	print("Malformed op directive with level=%S, type=%S, op=%S\n",
		prettyprint(levelt, 0, 0, 0, mod),
		prettyprint(typet, 0, 0, 0, mod),
		prettyprint(opt, 0, 0, 0, mod));
}