ref: a37ae2f0170499be1a95031d24ff86aac5cf46f1
parent: d2a0828140c31514c514b8e4fb9a4d52c389d8fe
author: Peter Mikkelsen <peter@pmikkelsen.com>
date: Fri Jul 9 16:09:22 EDT 2021
Add asserta/1, assertz/1, retract/1, abolish/1 (and retract_one/1, which is retract/1 but doesn't backtrack)
--- a/builtins.c
+++ b/builtins.c
@@ -17,6 +17,7 @@
return 1; \
}while(0)
+BuiltinProto(builtintrue);
BuiltinProto(builtinfail);
BuiltinProto(builtincall);
BuiltinProto(builtincut);
@@ -49,6 +50,10 @@
BuiltinProto(builtingeq);
BuiltinProto(builtinclause);
BuiltinProto(builtincurrentpredicate);
+BuiltinProto(builtinasserta);
+BuiltinProto(builtinassertz);
+BuiltinProto(builtinretractone);
+BuiltinProto(builtinabolish);
int compareterms(Term *, Term *);
@@ -72,6 +77,8 @@
}
/* Rewrite this so its not just a long if chain */
+ if(Match(L"true", 0))
+ return builtintrue;
if(Match(L"fail", 0))
return builtinfail;
if(Match(L"call", 1))
@@ -136,11 +143,28 @@
return builtinclause;
if(Match(L"current_predicate", 2))
return builtincurrentpredicate;
+ if(Match(L"asserta", 1))
+ return builtinasserta;
+ if(Match(L"assertz", 1))
+ return builtinassertz;
+ if(Match(L"retract_one", 1))
+ return builtinretractone;
+ if(Match(L"abolish", 1))
+ return builtinabolish;
return nil;
}
int
+builtintrue(Term *goal, Binding **bindings, Module *module)
+{
+ USED(goal);
+ USED(bindings);
+ USED(module);
+ return 1;
+}
+
+int
builtinfail(Term *goal, Binding **bindings, Module *module)
{
USED(goal);
@@ -832,8 +856,6 @@
if(clauselist->tag != VariableTerm)
Throw(typeerror(L"variable", clauselist));
- print("Attempting to find clauses in module %S where head unifies with %S\n", module->name, prettyprint(head, 0, 0, 0));
-
Predicate *pred = findpredicate(module->predicates, head);
if(pred == nil)
return 0;
@@ -902,4 +924,187 @@
}
Term *reallist = mklist(pilist);
return unify(list, reallist, bindings);
+}
+
+int
+assertclause(Term *clause, Module *module, int after)
+{
+ /* If after=0 then this is asserta, else it is assertz */
+ Term *head;
+ Term *body;
+
+ if(clause->tag == CompoundTerm && runestrcmp(clause->text, L":-") == 0 && clause->arity == 2){
+ head = clause->children;
+ body = head->next;
+ }else{
+ head = clause;
+ body = mkatom(L"true");
+ }
+
+ if(body->tag == VariableTerm)
+ body = mkcompound(L"call", 1, body);
+
+ if(head->tag == VariableTerm)
+ Throw(instantiationerror());
+ if(head->tag != AtomTerm && head->tag != CompoundTerm)
+ Throw(typeerror(L"callable", head));
+ if(body->tag != AtomTerm && body->tag != CompoundTerm)
+ Throw(typeerror(L"callable", body));
+
+ Rune *name = head->text;
+ int arity;
+ if(head->tag == CompoundTerm)
+ arity = head->arity;
+ else
+ arity = 0;
+
+ uvlong id = 0;
+ Clause *cl = gmalloc(sizeof(Clause));
+ cl->head = copyterm(head, &id);
+ cl->body = copyterm(body, &id);
+ cl->clausenr = id;
+ cl->next = nil;
+
+ Predicate *p;
+ for(p = module->predicates; p != nil; p = p->next){
+ if(p->arity == arity && runestrcmp(p->name, name) == 0){
+ if(!p->dynamic){
+ Term *t = mkatom(name);
+ t->next = mkinteger(arity);
+ Term *pi = mkcompound(L"/", 2, t);
+ Throw(permissionerror(L"modify", L"static_procedure", pi));
+ }
+ if(after)
+ p->clauses = appendclause(p->clauses, cl);
+ else
+ p->clauses = appendclause(cl, p->clauses);
+ return 1;
+ }
+ }
+
+ /* If we get here, create a new predicate in the module */
+ p = gmalloc(sizeof(Predicate));
+ p->name = name;
+ p->arity = arity;
+ p->clauses = cl;
+ p->public = 1;
+ p->builtin = 0;
+ p->dynamic = 1;
+ p->next = nil;
+ module->predicates = appendpredicate(module->predicates, p);
+
+ return 1;
+}
+
+int
+builtinasserta(Term *goal, Binding **bindings, Module *module)
+{
+ USED(bindings);
+ return assertclause(goal->children, module, 0);
+}
+
+int
+builtinassertz(Term *goal, Binding **bindings, Module *module)
+{
+ USED(bindings);
+ return assertclause(goal->children, module, 1);
+}
+
+int
+builtinretractone(Term *goal, Binding **bindings, Module *module)
+{
+ Term *clause = goal->children;
+ Term *head;
+ Term *body;
+
+ if(clause->tag == CompoundTerm && runestrcmp(clause->text, L":-") == 0 && clause->arity == 2){
+ head = clause->children;
+ body = head->next;
+ }else{
+ head = clause;
+ body = mkatom(L"true");
+ }
+
+ if(head->tag == VariableTerm)
+ Throw(instantiationerror());
+ if(head->tag != AtomTerm && head->tag != CompoundTerm)
+ Throw(typeerror(L"callable", head));
+
+ Predicate *pred = findpredicate(module->predicates, head);
+ if(pred == nil)
+ return 0;
+ if(!pred->dynamic){
+ Rune *name = head->text;
+ int arity = 0;
+ if(head->tag == CompoundTerm)
+ arity = head->arity;
+ Term *t = mkatom(name);
+ t->next = mkinteger(arity);
+ Term *pi = mkcompound(L"/", 2, t);
+ Throw(permissionerror(L"access", L"static_procedure", pi));
+ }
+
+ Clause *cl;
+ for(cl = pred->clauses; cl != nil; cl = cl->next){
+ if(!unify(cl->head, head, bindings))
+ continue;
+ if(!unify(cl->body, body, bindings))
+ continue;
+
+ if(cl == pred->clauses)
+ pred->clauses = cl->next;
+ else{
+ Clause *tmp;
+ for(tmp = pred->clauses; tmp->next != cl; tmp = tmp->next);
+ tmp->next = tmp->next->next;
+ }
+ return 1;
+ }
+ return 0;
+}
+
+int
+builtinabolish(Term *goal, Binding **bindings, Module *module)
+{
+ USED(goal);
+ USED(bindings);
+ USED(module);
+ Term *pi = goal->children;
+
+ if(pi->tag == VariableTerm)
+ Throw(instantiationerror());
+ if(pi->tag != CompoundTerm || runestrcmp(pi->text, L"/") != 0 || pi->arity != 2)
+ Throw(typeerror(L"predicate_indicator", pi));
+
+ Term *nameterm = pi->children;
+ Term *arityterm = nameterm->next;
+ if(nameterm->tag == VariableTerm || arityterm->tag == VariableTerm)
+ Throw(instantiationerror());
+ if(arityterm->tag != IntegerTerm)
+ Throw(typeerror(L"integer", arityterm));
+ if(nameterm->tag != AtomTerm)
+ Throw(typeerror(L"atom", nameterm));
+ Rune *name = nameterm->text;
+ int arity = arityterm->ival;
+
+ if(arity < 0)
+ Throw(domainerror(L"not_less_than_zero", arityterm));
+
+ Predicate *p = module->predicates;
+ if(p->arity == arity && runestrcmp(p->name, name) == 0){
+ module->predicates = p->next;
+ return 1;
+ }
+ for(p = module->predicates; p != nil; p = p->next){
+ if(p->arity != arity || runestrcmp(p->name, name) != 0)
+ continue;
+ if(p == module->predicates)
+ module->predicates = p->next;
+ else{
+ Predicate *tmp;
+ for(tmp = module->predicates; tmp->next != p; tmp = tmp->next);
+ tmp->next = tmp->next->next;
+ }
+ }
+ return 1;
}
\ No newline at end of file
--- a/dat.h
+++ b/dat.h
@@ -60,6 +60,7 @@
int arity;
int public;
int builtin; /* All the predicates from the system module are builtin */
+ int dynamic;
Clause *clauses;
Predicate *next;
};
--- a/fns.h
+++ b/fns.h
@@ -66,6 +66,8 @@
void initmodules(void);
Module *parsemodule(char *);
Module *getmodule(Rune *);
+Clause *appendclause(Clause *, Clause *);
+Predicate *appendpredicate(Predicate *, Predicate *);
/* types.c */
int islist(Term *);
--- a/module.c
+++ b/module.c
@@ -6,8 +6,6 @@
#include "fns.h"
Module *addemptymodule(Rune *);
-Clause *appendclause(Clause *, Clause *);
-Predicate *appendpredicate(Predicate *, Predicate *);
void
initmodules(void)
@@ -21,6 +19,7 @@
Predicate *p;
for(p = systemmodule->predicates; p != nil; p = p->next){
p->builtin = 1;
+ p->dynamic = 0;
}
usermodule = addemptymodule(L"user");
@@ -70,7 +69,7 @@
cl->body = t->children->next;
}else{
cl->head = t;
- cl->body = nil;
+ cl->body = mkatom(L"true");
}
if(cl->head->tag == AtomTerm)
arity = 0;
@@ -89,6 +88,7 @@
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);
--- a/stdlib.pl
+++ b/stdlib.pl
@@ -10,8 +10,7 @@
repeat :- true ; repeat.
-% Control structures.
-true.
+% Control structures.
If -> Then :-
If, !, Then.
@@ -150,7 +149,7 @@
E2 < E1.
-% Clause retrieval and information
+% Clause retrieval and information and removal
clause(Head, Body) :-
clause(Head, Body, Clauses),
@@ -159,6 +158,13 @@
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|_]).