/* xlstruct.c - the defstruct facility */
/* Copyright (c) 1989, by David Michael Betz. */
/* You may give out copies of this software; for conditions see the file */
/* COPYING included with this distribution. */
#include "xlisp.h"
/* forward declarations */
LOCAL VOID addslot P5H(LVAL, LVAL, int, LVAL *, LVAL *);
LOCAL VOID updateslot P3H(LVAL, LVAL, LVAL);
/* local variables */
static char prefix[STRMAX+1];
static char makestr[] = "MAKE-%s";
/* xmkstruct - the '%make-struct' function */
LVAL xmkstruct(V)
{
LVAL type,val;
int i;
/* get the structure type */
type = xlgasymbol();
/* make the structure */
val = newstruct(type,xlargc);
/* store each argument */
for (i = 1; moreargs(); ++i)
setelement(val,i,nextarg());
xllastarg();
/* return the structure */
return (val);
}
/* xcpystruct - the '%copy-struct' function */
LVAL xcpystruct(V)
{
LVAL str,val;
int size,i;
str = xlgastruct();
xllastarg();
size = getsize(str);
val = newstruct(getelement(str,0),size-1);
for (i = 1; i < size; ++i)
setelement(val,i,getelement(str,i));
return (val);
}
/* xstrref - the '%struct-ref' function */
LVAL xstrref(V)
{
LVAL str,val;
int i;
str = xlgastruct();
val = xlgafixnum(); i = (int)getfixnum(val);
xllastarg();
if (i >= getsize(str)) /* wrong structure TAA MOD fix*/
xlerror("bad structure reference",str);
return (getelement(str,i));
}
/* xstrset - the '%struct-set' function */
LVAL xstrset(V)
{
LVAL str,val;
int i;
str = xlgastruct();
val = xlgafixnum(); i = (int)getfixnum(val);
val = xlgetarg();
xllastarg();
if (i >= getsize(str)) /* wrong structure TAA MOD fix*/
xlerror("bad structure reference",str);
setelement(str,i,val);
return (val);
}
/* xstrtypep - the '%struct-type-p' function */
LVAL xstrtypep(V)
{
LVAL type,val;
type = xlgasymbol();
val = xlgetarg();
xllastarg();
if (structp(val)) {
for (val = getelement(val,0);
! null(val);
val = xlgetprop(val,s_strinclude))
if (val == type) return(s_true);
return(NIL);
}
else return(NIL);
}
/* xdefstruct - the 'defstruct' special form */
LVAL xdefstruct(V)
{
LVAL structname,slotname,defexpr,sym,tmp,args,body;
LVAL options,oargs,slots,constrsym,predsym;
char *pname = NULL;
int slotn, has_include;
/* protect some pointers */
xlstkcheck(6);
xlsave(structname);
xlsave(slotname);
xlsave(defexpr);
xlsave(args);
xlsave(body);
xlsave(tmp);
/* initialize */
args = body = NIL;
slotn = 0;
has_include = FALSE;
constrsym = NULL;
predsym = NULL;
/* get the structure name */
tmp = xlgetarg();
if (symbolp(tmp)) {
structname = tmp;
pname = getstring(getpname(structname));
sprintf(prefix, "%s-", pname);
}
/* get the structure name and options */
else if (consp(tmp) && symbolp(car(tmp))) {
structname = car(tmp);
pname = getstring(getpname(structname));
sprintf(prefix, "%s-", pname);
/* handle the list of options */
for (options = cdr(tmp); consp(options); options = cdr(options)) {
/* get the next argument */
tmp = car(options);
/* handle options that don't take arguments */
if (symbolp(tmp)) {
xlerror("unknown option",tmp);
}
/* handle options that take arguments */
else if (consp(tmp) && symbolp(car(tmp))) {
oargs = cdr(tmp);
/* check for the :CONC-NAME keyword */
if (car(tmp) == k_concname) {
/* get the name of the structure to include */
if (!consp(oargs) || !symbolp(car(oargs)))
xlerror("expecting a symbol",oargs);
/* save the prefix */
if (null(car(oargs)))
STRCPY(prefix, "");
else
STRCPY(prefix,getstring(getpname(car(oargs))));
}
/* check for the :INCLUDE keyword */
else if (car(tmp) == k_include) {
LVAL parent;
if (has_include)
xlfail("only one :INCLUDE option allowed");
else
has_include = TRUE;
/* get the name of the structure to include */
if (!consp(oargs) || !symbolp(car(oargs)))
xlerror("expecting a structure name",oargs);
parent = tmp = car(oargs);
oargs = cdr(oargs);
/* add each slot from the included structure */
slots = xlgetprop(tmp,s_sslots);
for (; consp(slots); slots = cdr(slots)) {
if (consp(car(slots)) && consp(cdr(car(slots)))) {
/* get the next slot description */
tmp = car(slots);
/* create the slot access functions */
addslot(car(tmp),car(cdr(tmp)),++slotn,&args,&body);
}
}
/* handle slot initialization overrides */
for (; consp(oargs); oargs = cdr(oargs)) {
tmp = car(oargs);
if (symbolp(tmp)) {
slotname = tmp;
defexpr = NIL;
}
else if (consp(tmp) && symbolp(car(tmp))) {
slotname = car(tmp);
defexpr = (consp(cdr(tmp)) ? car(cdr(tmp)) : NIL);
}
else
xlerror("bad slot description",tmp);
updateslot(args,slotname,defexpr);
}
xlputprop(structname,parent,s_strinclude);
}
/* check for :PRINT-FUNCTION option (Ken Whedbee) */
else if (car(tmp) == k_prntfunc) {
if (!consp(oargs) || !symbolp(car(oargs)))
xlerror("expecting a print function name",oargs);
xlputprop(structname,car(oargs),s_prntfunc);
}
else if (car(tmp) == k_construct) {
if (!consp(oargs) || !symbolp(car(oargs)))
xlerror("expecting a constructor function name",oargs);
if (consp(cdr(oargs)))
xlfail("BOA constructors not supported");
constrsym = car(oargs);
if (! symbolp(constrsym)) xlbadtype(constrsym);
xlputprop(structname,constrsym, s_strconstruct);
}
else if (car(tmp) == k_predicate) {
if (!consp(oargs) || !symbolp(car(oargs)))
xlerror("expecting a predicate function name",oargs);
predsym = car(oargs);
if (! symbolp(predsym)) xlbadtype(predsym);
}
else
xlerror("unknown option",tmp);
}
else
xlerror("bad option syntax",tmp);
}
}
/**** need to add documentation string */
/* flush documentation string */
if (moreargs() && stringp(*xlargv)) (void)(nextarg());
/* get each of the structure members */
while (moreargs()) {
/* get the slot name and default value expression */
tmp = xlgetarg();
if (symbolp(tmp)) {
slotname = tmp;
defexpr = NIL;
}
else if (consp(tmp) && symbolp(car(tmp))) {
slotname = car(tmp);
defexpr = (consp(cdr(tmp)) ? car(cdr(tmp)) : NIL);
}
else
xlerror("bad slot description",tmp);
/* create a closure for non-trivial default expressions */
if (defexpr != NIL) {
tmp = newclosure(NIL,s_lambda,xlenv,xlfenv);
setbody(tmp,cons(defexpr,NIL));
tmp = cons(tmp,NIL);
defexpr = tmp;
}
/* create the slot access functions */
addslot(slotname,defexpr,++slotn,&args,&body);
}
/* store the slotnames and default expressions */
xlputprop(structname,args,s_sslots);
/* enter the MAKE-xxx symbol */
if (constrsym == NULL) {
sprintf(buf, makestr, pname);
#ifdef PACKAGES
constrsym = xlintern(buf, getvalue(s_package));
#else
constrsym = xlenter(buf);
#endif /* PACKAGES */
xlputprop(structname,constrsym, s_strconstruct);
}
/* make the MAKE-xxx function */
if (! null(constrsym)) {
args = cons(lk_key,args);
tmp = cons(structname,NIL);
tmp = cons(s_quote,tmp);
body = cons(tmp,body);
body = cons(s_mkstruct,body);
body = cons(body,NIL);
setfunction(constrsym,
xlclose(constrsym,s_lambda,args,body,xlenv,xlfenv));
}
/* enter the xxx-P symbol */
if (predsym == NULL) {
sprintf(buf,"%s-P", pname);
#ifdef PACKAGES
predsym = xlintern(buf, getvalue(s_package));
#else
predsym = xlenter(buf);
#endif /* PACKAGES */
}
/* make the xxx-P function */
if (! null(predsym)) {
args = cons(s_x,NIL);
body = cons(s_x,NIL);
tmp = cons(structname,NIL);
tmp = cons(s_quote,tmp);
body = cons(tmp,body);
body = cons(s_strtypep,body);
body = cons(body,NIL);
setfunction(predsym,
xlclose(predsym,s_lambda,args,body,NIL,NIL));
}
/* enter the COPY-xxx symbol */
sprintf(buf,"COPY-%s", pname);
#ifdef PACKAGES
sym = xlintern(buf, getvalue(s_package));
#else
sym = xlenter(buf);
#endif /* PACKAGES */
/* make the COPY-xxx function */
args = cons(s_x,NIL);
body = cons(s_x,NIL);
body = cons(s_cpystruct,body);
body = cons(body,NIL);
setfunction(sym,
xlclose(sym,s_lambda,args,body,NIL,NIL));
/* restore the stack */
xlpopn(6);
/* return the structure name */
return (structname);
}
/* xlrdstruct - convert a list to a structure (used by the reader) */
/* Modified by TAA to quote arguments and accept leading colons on keys */
LVAL xlrdstruct P1C(LVAL, list)
{
LVAL structname,slotname,expr,last,val;
/* protect the new structure */
xlsave1(expr);
/* get the structure name */
if (!consp(list) || !symbolp(car(list)))
xlerror("bad structure initialization list",list);
structname = car(list);
list = cdr(list);
/* initialize the constructor function call expression */
expr = consa(xlgetprop(structname, s_strconstruct));
last = expr;
/* turn the rest of the initialization list into keyword arguments */
while (consp(list) && consp(cdr(list))) {
/* get the slot keyword name */
slotname = car(list);
if (!symbolp(slotname))
xlerror("expecting a slot name",slotname);
/* add the slot keyword */
#ifdef PACKAGES
rplacd(last,consa(xlintern(getstring(getpname(slotname)), xlkeypack)));
#else
if (*(getstring(getpname(slotname))) != ':') { /* add colon */
sprintf(buf,":%s",getstring(getpname(slotname)));
rplacd(last,cons(xlenter(buf),NIL));
}
else {
rplacd(last,cons(slotname,NIL));
}
#endif /* PACKAGES */
last = cdr(last);
list = cdr(list);
/* add the value expression -- QUOTED (TAA MOD) */
rplacd(last,cons(NIL,NIL));
last = cdr(last);
rplaca(last, (slotname = cons(s_quote,NIL)));
rplacd(slotname, cons(car(list), NIL));
list = cdr(list);
}
/* make sure all of the initializers were used */
if (consp(list))
xlerror("bad structure initialization list",list);
/* invoke the creation function */
val = xleval(expr);
/* restore the stack */
xlpop();
/* return the new structure */
return (val);
}
/* xlprstruct - print a structure (used by printer) */
VOID xlprstruct P4C(LVAL, fptr, LVAL, vptr, FIXTYPE, plevel, int, flag)
{
LVAL next;
int i,n;
FRAMEP newfp;
{
LVAL type = getelement(vptr,0);
next = xlgetprop(type, s_prntfunc);
while (null(next) && ! null(type)) {
type = xlgetprop(type,s_strinclude);
next = xlgetprop(type, s_prntfunc);
}
}
if (!null(next)) { /* Ken Whedbee addition */
newfp = xlsp;
pusharg(cvfixnum((FIXTYPE)(newfp - xlfp)));
pusharg(symbolp(next) ? xlgetfunction(next) : next);
pusharg(cvfixnum((FIXTYPE) 3));
pusharg(vptr);
pusharg(fptr);
pusharg(cvfixnum(plevel));
xlfp = newfp;
xlapply(3);
}
else {
xlputstr(fptr,"#S("); /* TAA MOD */
xlprint(fptr,getelement(vptr,0),flag);
next = xlgetprop(getelement(vptr,0), s_sslots);
for (i = 1, n = getsize(vptr) - 1; i <= n && consp(next); ++i) {
if (consp(car(next))) { /* should always succeed */
xlputc(fptr,' '); /* Alternate, could print " :" */
xlprint(fptr,car(car(next)),flag);
xlputc(fptr,' ');
xlprint(fptr,getelement(vptr,i),flag);
}
next = cdr(next);
}
xlputc(fptr,')');
}
}
/* addslot - make the slot access functions */
LOCAL VOID addslot P5C(LVAL, slotname, LVAL, defexpr, int, slotn, LVAL *, pargs, LVAL *, pbody)
{
LVAL sym,args,body,tmp;
/* protect some pointers */
xlstkcheck(4);
xlsave(sym);
xlsave(args);
xlsave(body);
xlsave(tmp);
/* construct the update function name */
sprintf(buf,"%s%s",prefix,getstring(getpname(slotname)));
#ifdef PACKAGES
sym = xlintern(buf, getvalue(s_package));
#else
sym = xlenter(buf);
#endif /* PACKAGES */
/* make the access function */
args = cons(s_s,NIL);
body = cons(cvfixnum((FIXTYPE)slotn),NIL);
body = cons(s_s,body);
body = cons(s_strref,body);
body = cons(body,NIL);
setfunction(sym,
xlclose(sym,s_lambda,args,body,NIL,NIL));
/* make the update function */
args = cons(s_x,NIL);
args = cons(s_s,args);
body = cons(s_x,NIL);
body = cons(cvfixnum((FIXTYPE)slotn),body);
body = cons(s_s,body);
body = cons(s_strset,body);
body = cons(body,NIL);
xlputprop(sym,
xlclose(NIL,s_lambda,args,body,NIL,NIL),
s_setf);
/* add the slotname to the make-xxx keyword list */
tmp = cons(defexpr,NIL);
tmp = cons(slotname,tmp);
tmp = cons(tmp,NIL);
if ((args = *pargs) == NIL)
*pargs = tmp;
else {
while (consp(cdr(args)))
args = cdr(args);
rplacd(args,tmp);
}
/* add the slotname to the %make-xxx argument list */
tmp = cons(slotname,NIL);
if ((body = *pbody) == NIL)
*pbody = tmp;
else {
while (consp(cdr(body)))
body = cdr(body);
rplacd(body,tmp);
}
/* restore the stack */
xlpopn(4);
}
/* updateslot - update a slot definition */
LOCAL VOID updateslot P3C(LVAL, args, LVAL, slotname, LVAL, defexpr)
{
LVAL tmp;
for (; consp(args); args = cdr(args))
if (slotname == car(car(args))) {
if (defexpr != NIL) {
xlsave1(tmp);
tmp = newclosure(NIL,s_lambda,xlenv,xlfenv);
setbody(tmp,cons(defexpr,NIL));
tmp = cons(tmp,NIL);
defexpr = tmp;
xlpop();
}
rplaca(cdr(car(args)),defexpr);
break;
}
if (args == NIL)
xlerror("unknown slot name",slotname);
}
syntax highlighted by Code2HTML, v. 0.9.1