home *** CD-ROM | disk | FTP | other *** search
- /* xlobj - xlisp object functions */
- /* Copyright (c) 1985, by David Michael Betz
- All Rights Reserved
- Permission is granted for unrestricted non-commercial use */
-
- #include "xlisp.h"
-
- /* external variables */
- extern LVAL xlenv,xlfenv,xldenv,xlvalue;
- extern LVAL s_stdout,s_lambda;
-
- /* local variables (jsp: nonstatic because used externally by some apps) */
- LVAL s_self=0,k_new=0,k_isnew=0;
- LVAL k_prin1;
- LVAL cls_class=0,cls_object=0;
-
- /* instance variable numbers for the class 'Class' */
- #define MESSAGES 0 /* list of messages */
- #define IVARS 1 /* list of instance variable names */
- #define CVARS 2 /* list of class variable names */
- #define CVALS 3 /* list of class variable values */
- #define SUPERCLASS 4 /* pointer to the superclass */
- #define IVARCNT 5 /* number of class instance variables */
- #define IVARTOTAL 6 /* total number of instance variables */
- #define PNAME 7 /* print name TAA Mod */
- /* number of instance variables for the class 'Class' */
- #define CLASSSIZE 8
-
- /* forward declarations */
- #ifdef ANSI
- LVAL XNEAR entermsg(LVAL cls, LVAL msg);
- LVAL XNEAR sendmsg(LVAL obj, LVAL cls, LVAL sym);
- LVAL XNEAR evmethod(LVAL obj, LVAL msgcls, LVAL method);
- int XNEAR getivcnt(LVAL cls, int ivar);
- int XNEAR listlength(LVAL list);
- #else
- FORWARD LVAL entermsg();
- FORWARD LVAL sendmsg();
- FORWARD LVAL evmethod();
- #endif
-
- /* $putpatch.c$: "MODULE_XLOBJ_C_GLOBALS" */
-
- /* routine to print an object for PRINx */
- #ifdef ANSI
- static VOID XNEAR xputobj(LVAL fptr, LVAL val)
- #else
- LOCAL VOID xputobj(fptr,val)
- LVAL fptr; LVAL val;
- #endif
- {
- LVAL temp;
- if ((temp = getclass(val)) == cls_class) { /* this is a class */
- if (null(temp = getivar(val,PNAME)) || (ntype(temp) != STRING) ) {
- /* but nameless */
- xlputstr(fptr,"#<class ???: #");
- }
- else {
- #ifdef MEDMEM
- strcpy(buf, "#<class ");
- STRCAT(buf, getstring(temp));
- strcat(buf, ": #");
- #else
- sprintf(buf,"#<class %s: #",getstring(temp));
- #endif
- xlputstr(fptr,buf);
- }
- }
- else { /* not a class */
- if (null(temp = getivar(temp,PNAME)) || (ntype(temp) != STRING) ) {
- /* but nameless */
- xlputstr(fptr,"#<a ??? object: #");
- }
- else {
- #ifdef MEDMEM
- strcpy(buf, "#<a ");
- STRCAT(buf, getstring(temp));
- strcat(buf, ": #");
- #else
- sprintf(buf,"#<a %s: #",getstring(temp));
- #endif
- xlputstr(fptr,buf);
- }
- }
- sprintf(buf,AFMT,val);
- xlputstr(fptr,buf);
- xlputc(fptr,'>');
- }
-
-
- /* xsend - send a message to an object */
- LVAL xsend()
- {
- LVAL obj;
- obj = xlgaobject();
- return (sendmsg(obj,getclass(obj),xlgasymbol()));
- }
-
- /* xsendsuper - send a message to the superclass of an object */
- LVAL xsendsuper()
- {
- LVAL env,p;
- for (env = xlenv; !null(env); env = cdr(env))
- if ((!null(p = car(env))) && objectp(car(p)))
- return (sendmsg(car(p),
- getivar(cdr(p),SUPERCLASS),
- xlgasymbol()));
- xlfail("not in a method");
- return (NIL); /* fake out compiler warning */
- }
-
- /* xlclass - define a class */
- #ifdef ANSI
- static LVAL XNEAR xlclass(char *name, int vcnt)
- #else
- LOCAL LVAL xlclass(name,vcnt)
- char *name; int vcnt;
- #endif
- {
- LVAL sym,cls;
-
- /* create the class */
- sym = xlenter(name);
- cls = newobject(cls_class,CLASSSIZE);
- defconstant(sym,cls); /* TAA MOD -- was setvalue */
-
- /* set the instance variable counts */
- setivar(cls,IVARCNT,cvfixnum((FIXTYPE)vcnt));
- setivar(cls,IVARTOTAL,cvfixnum((FIXTYPE)vcnt));
-
- /* set the class name TAA Mod */
- setivar(cls,PNAME,cvstring(name));
-
- /* set the superclass to 'Object' */
- setivar(cls,SUPERCLASS,cls_object);
-
- /* return the new class */
- return (cls);
- }
-
- /* xladdivar - enter an instance variable */
- #ifdef ANSI
- static VOID XNEAR xladdivar(LVAL cls, char *var)
- #else
- LOCAL VOID xladdivar(cls,var)
- LVAL cls; char *var;
- #endif
- {
- setivar(cls,IVARS,cons(xlenter(var),getivar(cls,IVARS)));
- }
-
- /* xladdmsg - add a message to a class */
- #ifdef ANSI
- static VOID XNEAR xladdmsg(LVAL cls, char *msg, int offset)
- #else
- LOCAL VOID xladdmsg(cls,msg,offset)
- LVAL cls; char *msg; int offset;
- #endif
- {
- extern FUNDEF funtab[];
- LVAL mptr;
-
- /* enter the message selector */
- mptr = entermsg(cls,xlenter(msg));
-
- /* store the method for this message */
- rplacd(mptr,cvsubr(funtab[offset].fd_subr,funtab[offset].fd_type,offset));
- }
-
- /* xlobgetvalue - get the value of an instance variable */
- int xlobgetvalue(pair,sym,pval)
- LVAL pair,sym,*pval;
- {
- LVAL cls,names;
- int ivtotal,n;
-
- /* find the instance or class variable */
- for (cls = cdr(pair); objectp(cls); cls = getivar(cls,SUPERCLASS)) {
-
- /* check the instance variables */
- names = getivar(cls,IVARS);
- ivtotal = getivcnt(cls,IVARTOTAL);
- for (n = ivtotal - getivcnt(cls,IVARCNT); n < ivtotal; ++n) {
- if (car(names) == sym) {
- *pval = getivar(car(pair),n);
- return (TRUE);
- }
- names = cdr(names);
- }
-
- /* check the class variables */
- names = getivar(cls,CVARS);
- for (n = 0; consp(names); ++n) {
- if (car(names) == sym) {
- *pval = getelement(getivar(cls,CVALS),n);
- return (TRUE);
- }
- names = cdr(names);
- }
- }
-
- /* variable not found */
- return (FALSE);
- }
-
- /* xlobsetvalue - set the value of an instance variable */
- int xlobsetvalue(pair,sym,val)
- LVAL pair,sym,val;
- {
- LVAL cls,names;
- int ivtotal,n;
-
- /* find the instance or class variable */
- for (cls = cdr(pair); objectp(cls); cls = getivar(cls,SUPERCLASS)) {
-
- /* check the instance variables */
- names = getivar(cls,IVARS);
- ivtotal = getivcnt(cls,IVARTOTAL);
- for (n = ivtotal - getivcnt(cls,IVARCNT); n < ivtotal; ++n) {
- if (car(names) == sym) {
- setivar(car(pair),n,val);
- return (TRUE);
- }
- names = cdr(names);
- }
-
- /* check the class variables */
- names = getivar(cls,CVARS);
- for (n = 0; consp(names); ++n) {
- if (car(names) == sym) {
- setelement(getivar(cls,CVALS),n,val);
- return (TRUE);
- }
- names = cdr(names);
- }
- }
-
- /* variable not found */
- return (FALSE);
- }
-
- /* obisnew - default 'isnew' method */
- LVAL obisnew()
- {
- LVAL self;
- self = xlgaobject();
- xllastarg();
- return (self);
- }
-
- /* obclass - get the class of an object */
- LVAL obclass()
- {
- LVAL self;
- self = xlgaobject();
- xllastarg();
- return (getclass(self));
- }
-
- /* obshow - show the instance variables of an object */
- LVAL obshow()
- {
- LVAL self,fptr,cls,names;
- int ivtotal,n;
-
- /* get self and the file pointer */
- self = xlgaobject();
- fptr = (moreargs() ? xlgetfile(TRUE) : getvalue(s_stdout));
- xllastarg();
-
- /* get the object's class */
- cls = getclass(self);
-
- /* print the object and class */
- xlputstr(fptr,"Object is ");
- xlprint(fptr,self,TRUE);
- xlputstr(fptr,", Class is ");
- xlprint(fptr,cls,TRUE);
- xlterpri(fptr);
-
- /* print the object's instance variables */
- for (; !null(cls); cls = getivar(cls,SUPERCLASS)) {
- names = getivar(cls,IVARS);
- ivtotal = getivcnt(cls,IVARTOTAL);
- for (n = ivtotal - getivcnt(cls,IVARCNT); n < ivtotal; ++n) {
- xlputstr(fptr," ");
- xlprint(fptr,car(names),TRUE);
- xlputstr(fptr," = ");
- xlprint(fptr,getivar(self,n),TRUE);
- xlterpri(fptr);
- names = cdr(names);
- }
- }
-
- /* return the object */
- return (self);
- }
-
- /* clnew - create a new object instance */
- LVAL clnew()
- {
- LVAL self;
- self = xlgaobject();
- /* $putpatch.c$: "MODULE_XLOBJ_C_CLNEW" */
- return (newobject(self,getivcnt(self,IVARTOTAL)));
- }
-
- /* clisnew - initialize a new class */
- LVAL clisnew()
- {
- LVAL self,ivars,cvars,super;
- int n;
-
- /* get self, the ivars, cvars and superclass */
- self = xlgaobject();
- ivars = xlgalist();
- cvars = (moreargs() ? xlgalist() : NIL);
- super = (moreargs() ? xlgaobject() : cls_object);
- xllastarg();
-
- /* store the instance and class variable lists and the superclass */
- setivar(self,IVARS,ivars);
- setivar(self,CVARS,cvars);
- setivar(self,CVALS,(!null(cvars) ? newvector(listlength(cvars)) : NIL));
- setivar(self,SUPERCLASS,super);
-
- /* compute the instance variable count */
- n = listlength(ivars);
- setivar(self,IVARCNT,cvfixnum((FIXTYPE)n));
- n += getivcnt(super,IVARTOTAL);
- setivar(self,IVARTOTAL,cvfixnum((FIXTYPE)n));
-
- /* return the new class object */
- return (self);
- }
-
- /* clanswer - define a method for answering a message */
- LVAL clanswer()
- {
- LVAL self,msg,fargs,code,mptr;
-
- /* message symbol, formal argument list and code */
- self = xlgaobject();
- msg = xlgasymbol();
- fargs = xlgalist();
- code = xlgalist();
- xllastarg();
-
- /* make a new message list entry */
- mptr = entermsg(self,msg);
-
- /* setup the message node */
- xlprot1(fargs);
- fargs = cons(s_self,fargs); /* add 'self' as the first argument */
- /* The following TAA MOD is by Neils Mayer, at HP */
- /* it sets the lexical environment to be correct (non-global) */
- /* rplacd(mptr,xlclose(msg,s_lambda,fargs,code,NIL,NIL)); */
- rplacd(mptr,xlclose(msg,s_lambda,fargs,code,xlenv,xlfenv));
- xlpop();
-
- /* return the object */
- return (self);
- }
-
- /* entermsg - add a message to a class */
- LOCAL LVAL XNEAR entermsg(cls,msg)
- LVAL cls,msg;
- {
- LVAL lptr,mptr;
-
- /* lookup the message */
- for (lptr = getivar(cls,MESSAGES); !null(lptr); lptr = cdr(lptr))
- if (car(mptr = car(lptr)) == msg)
- return (mptr);
-
- /* allocate a new message entry if one wasn't found */
- xlsave1(mptr);
- mptr = consa(msg);
- setivar(cls,MESSAGES,cons(mptr,getivar(cls,MESSAGES)));
- xlpop();
-
- /* return the symbol node */
- return (mptr);
- }
-
- /* sendmsg - send a message to an object */
- LOCAL LVAL XNEAR sendmsg(obj,cls,sym)
- LVAL obj,cls,sym;
- {
- LVAL msg,msgcls,method,val,p;
-
- /* look for the message in the class or superclasses */
- for (msgcls = cls; !null(msgcls); ) {
-
- /* lookup the message in this class */
- for (p = getivar(msgcls,MESSAGES); !null(p); p = cdr(p))
- if ((!null(msg = car(p))) && car(msg) == sym)
- goto send_message;
-
- /* look in class's superclass */
- msgcls = getivar(msgcls,SUPERCLASS);
- }
-
- /* message not found */
- xlerror("no method for this message",sym);
-
- send_message:
-
- /* insert the value for 'self' (overwrites message selector) */
- *--xlargv = obj;
- ++xlargc;
-
- /* invoke the method */
- if (null(method = cdr(msg)))
- xlerror("bad method",method);
- switch (ntype(method)) {
- case SUBR:
- val = (*getsubr(method))();
- break;
- case CLOSURE:
- if (gettype(method) != s_lambda)
- xlerror("bad method",method);
- val = evmethod(obj,msgcls,method);
- break;
- default:
- xlerror("bad method",method);
- }
-
- /* after creating an object, send it the ":isnew" message */
- if (car(msg) == k_new && !null(val)) {
- xlprot1(val);
- sendmsg(val,getclass(val),k_isnew);
- xlpop();
- }
-
- /* return the result value */
- return (val);
- }
-
- /* evmethod - evaluate a method */
- LOCAL LVAL XNEAR evmethod(obj,msgcls,method)
- LVAL obj,msgcls,method;
- {
- LVAL oldenv,oldfenv,cptr,name,val;
- LVAL olddenv=xldenv;
- CONTEXT cntxt;
-
- /* protect some pointers */
- xlstkcheck(3);
- xlsave(oldenv);
- xlsave(oldfenv);
- xlsave(cptr);
-
- /* create an 'object' stack entry and a new environment frame */
- oldenv = xlenv;
- oldfenv = xlfenv;
- xlenv = cons(cons(obj,msgcls),getenvi(method));
- xlenv = xlframe(xlenv);
- xlfenv = getfenv(method);
-
- /* bind the formal parameters */
- xlabind(method,xlargc,xlargv);
-
- /* setup the implicit block */
- if (!null(name = getname(method)))
- xlbegin(&cntxt,CF_RETURN,name);
-
- /* execute the block */
- if (name && setjmp(cntxt.c_jmpbuf))
- val = xlvalue;
- else
- for (cptr = getbody(method); consp(cptr); cptr = cdr(cptr))
- val = xleval(car(cptr));
-
- /* finish the block context */
- if (!null(name))
- xlend(&cntxt);
-
- /* restore the environment */
- xlenv = oldenv;
- xlfenv = oldfenv;
- xlunbind(olddenv);
-
- /* restore the stack */
- xlpopn(3);
-
- /* return the result value */
- return (val);
- }
-
- /* getivcnt - get the number of instance variables for a class */
- #ifdef ANSI
- static int XNEAR getivcnt(LVAL cls, int ivar)
- #else
- LOCAL int getivcnt(cls,ivar)
- LVAL cls; int ivar;
- #endif
- {
- LVAL cnt;
- if (null(cnt = getivar(cls,ivar)) || !fixp(cnt))
- xlfail("bad value for instance variable count");
- return ((int)getfixnum(cnt));
- }
-
- /* listlength - find the length of a list */
- #ifdef ANSI
- static int XNEAR listlength(LVAL list)
- #else
- LOCAL int listlength(list)
- LVAL list;
- #endif
- {
- int len;
- for (len = 0; consp(list); len++)
- list = cdr(list);
- return (len);
- }
-
- /* obsymbols - initialize symbols */
- VOID obsymbols()
- {
- /* enter the object related symbols */
- s_self = xlenter("SELF");
- k_new = xlenter(":NEW");
- k_isnew = xlenter(":ISNEW");
- k_prin1 = xlenter(":PRIN1");
-
- /* get the Object and Class symbol values */
- cls_object = getvalue(xlenter("OBJECT"));
- cls_class = getvalue(xlenter("CLASS" ));
- /* $putpatch.c$: "MODULE_XLOBJ_C_OBSYMBOLS" */
- }
-
- /* xloinit - object function initialization routine */
- VOID xloinit()
- {
- /* create the 'Class' object */
- cls_class = xlclass("CLASS",CLASSSIZE);
- setelement(cls_class,0,cls_class);
-
- /* create the 'Object' object */
- cls_object = xlclass("OBJECT",0);
-
- /* finish initializing 'class' */
- setivar(cls_class,SUPERCLASS,cls_object);
-
- xladdivar(cls_class,"PNAME"); /* ivar number 7 TAA Mod */
- xladdivar(cls_class,"IVARTOTAL"); /* ivar number 6 */
- xladdivar(cls_class,"IVARCNT"); /* ivar number 5 */
- xladdivar(cls_class,"SUPERCLASS"); /* ivar number 4 */
- xladdivar(cls_class,"CVALS"); /* ivar number 3 */
- xladdivar(cls_class,"CVARS"); /* ivar number 2 */
- xladdivar(cls_class,"IVARS"); /* ivar number 1 */
- xladdivar(cls_class,"MESSAGES"); /* ivar number 0 */
- xladdmsg(cls_class,":NEW",FT_CLNEW);
- xladdmsg(cls_class,":ISNEW",FT_CLISNEW);
- xladdmsg(cls_class,":ANSWER",FT_CLANSWER);
-
- /* finish initializing 'object' */
- setivar( cls_object,SUPERCLASS,NIL);
- xladdmsg(cls_object,":ISNEW",FT_OBISNEW);
- xladdmsg(cls_object,":CLASS",FT_OBCLASS);
- xladdmsg(cls_object,":SHOW",FT_OBSHOW);
- xladdmsg(cls_object,":PRIN1",FT_OBPRIN1);
- /* $putpatch.c$: "MODULE_XLOBJ_C_XLOINIT" */
-
- }
-
-
- /* default :PRIN1 method for objects */
- LVAL obprin1()
- {
- LVAL self,fptr;
-
- /* get self and the file pointer */
- self = xlgaobject();
- fptr = (moreargs() ? xlgetfile(TRUE) : getvalue(s_stdout));
- xllastarg();
-
- /* print it */
- xputobj(fptr,self);
-
- /* return the object */
- return (self);
- }
-
- /* called by xlprint to tell an object to print itself by faking
- a call like (send obj :prin1 fptr) */
- VOID putobj(fptr,obj)
- LVAL fptr,obj;
- {
- FRAMEP oldargv;
- int oldargc;
-
- /* check if there's room for the new call frame (5 slots needed) */
- if (xlsp >= (xlargstktop-5)) xlargstkoverflow();
-
- /* create a new (dummy) call frame. dummy because (1) stack backtraces
- * won't work anyway since if there's an error when PRINTing an object,
- * that error will probably occur again during the backtrace, and
- * (2) sendmsg() trashes the message selector slot.
- */
- *xlsp = cvfixnum((FIXTYPE)(xlsp - xlfp));
- xlfp = xlsp++; /* new frame pointer */
- *xlsp++ = NIL; /* dummy function */
- *xlsp++ = cvfixnum((FIXTYPE) 2); /* we have two arguments */
- *xlsp++ = k_prin1; /* 1st arg: the message (trashed by sendmsg()) */
- *xlsp++ = fptr; /* 2nd arg: the file/stream */
-
- /* save old xlargc and xlargv. set up new ones */
- oldargc = xlargc;
- oldargv = xlargv;
- xlargc = 1; /* one arg to be picked up */
- xlargv = xlfp + 4; /* points at 2nd arg: the file/stream */
-
- /* do it */
- sendmsg(obj,getclass(obj),k_prin1);
-
- /* restore xlargc and xlargv */
- xlargc = oldargc;
- xlargv = oldargv;
-
- /* remove call frame */
- xlsp = xlfp;
- xlfp -= (int)getfixnum(*xlfp);
- }
-
-
-