betz (01/06/83)
:::::::::::::: xlobj.c :::::::::::::: /* xlobj - xlisp object functions */ #include "xlisp.h" /* external variables */ extern struct node *xlstack; extern struct node *xlenv; /* external procedures */ extern struct node *xlevarg(); extern struct node *xlevmatch(); /* global variables */ struct node *self; /* the class object pointer */ static struct node *class; static struct node *messages; static struct node *ivars; static struct node *new; static struct node *isnew; static int init; /* forward declarations (the extern hack is because of decusc) */ extern struct node *enterivar(); /* xlmfind - find the message binding for a message to an object */ struct node *xlmfind(obj,msym) struct node *obj,*msym; { struct node *lptr,*msg; /* lookup the message */ for (lptr = enterivar(obj->n_obclass,messages)->n_bndvalue; lptr != NULL; lptr = lptr->n_listnext) if ((msg = lptr->n_listvalue) != NULL && msg->n_msg == msym) return (msg); /* message not found */ return (NULL); } /* xlxsend - send a message to an object */ struct node *xlxsend(obj,msg,args) struct node *obj,*msg,*args; { struct node *oldstk,method,cptr,val,*isnewmsg,*oldenv; /* save the old environment */ oldenv = xlenv; /* create a new stack frame */ oldstk = xlsave(&method,&cptr,&val,NULL); /* get the method for this message */ method.n_ptr = msg->n_msgcode; /* make sure its a function or a subr */ if (method.n_ptr->n_type != SUBR && method.n_ptr->n_type != FUN) xlfail("bad method"); /* bind the symbol self */ xlbind(self,obj); /* evaluate the function call */ if (method.n_ptr->n_type == SUBR) { xlfixbindings(oldenv); val.n_ptr = (*method.n_ptr->n_subr)(args); } else { /* bind the formal arguments */ xlabind(method.n_ptr->n_funargs,args); xlfixbindings(oldenv); /* execute the code */ cptr.n_ptr = method.n_ptr->n_funcode; while (cptr.n_ptr != NULL) val.n_ptr = xlevarg(&cptr.n_ptr); } /* restore the environment */ xlunbind(oldenv); /* after creating an object, send it the "isnew" message */ if (msg->n_msg == new && val.n_ptr != NULL) { if ((isnewmsg = xlmfind(val.n_ptr,isnew)) == NULL) xlfail("no method for the isnew message"); val.n_ptr = xlxsend(val.n_ptr,isnewmsg,args); } /* restore the previous stack frame */ xlstack = oldstk; /* return the result value */ return (val.n_ptr); } /* xlsend - send a message to an object (message in arg list) */ struct node *xlsend(obj,args) struct node *obj,*args; { struct node *msg; /* find the message binding for this message */ if ((msg = xlmfind(obj,xlevmatch(SYM,&args))) == NULL) xlfail("no method for this message"); /* send the message */ return (xlxsend(obj,msg,args)); } /* new - create a new object instance */ static struct node *mnew() { struct node *oldstk,obj; struct node *cls,*lptr,*lnk,*last; /* create a new stack frame */ oldstk = xlsave(&obj,NULL); /* get the class */ cls = self->n_symvalue; /* generate a new object */ obj.n_ptr = newnode(OBJ); obj.n_ptr->n_obclass = cls; /* create a list of instance variables for the new object */ for (lptr = enterivar(cls,ivars)->n_bndvalue, last = NULL; lptr != NULL; lptr = lptr->n_listnext, last = lnk) { lnk = newnode(LIST); if (last == NULL) obj.n_ptr->n_obdata = lnk; else last->n_listnext = lnk; lnk->n_listvalue = newnode(BND); lnk->n_listvalue->n_bndsym = lptr->n_listvalue; } /* restore the previous stack frame */ xlstack = oldstk; /* return the new object */ return (obj.n_ptr); } /* misnew - initialize a new class */ static struct node *misnew(args) struct node *args; { /* make sure there aren't any arguments */ if (args != NULL) xlfail("too many arguments"); /* return the new object */ return (self->n_symvalue); } /* enterivar - enter an instance variable */ static struct node *enterivar(obj,sym) struct node *obj,*sym; { struct node *lptr,*vbnd; /* lookup the instance variable */ for (lptr = obj->n_obdata; lptr != NULL; lptr = lptr->n_listnext) if ((vbnd = lptr->n_listvalue) != NULL && vbnd->n_bndsym == sym) break; /* add the instance variable if it wasn't found */ if (lptr == NULL) { if (!init) printf("can't find \"%s\"\n",sym->n_symname); lptr = newnode(LIST); lptr->n_listnext = obj->n_obdata; obj->n_obdata = lptr; lptr->n_listvalue = vbnd = newnode(BND); vbnd->n_bndsym = sym; } /* return the binding */ return (vbnd); } /* addivar - enter an instance variable */ static addivar(cls,var) struct node *cls; char *var; { struct node *vbnd,*lptr; /* enter the "ivars" instance variable */ vbnd = enterivar(cls,ivars); /* add the instance variable */ lptr = newnode(LIST); lptr->n_listnext = vbnd->n_bndvalue; vbnd->n_bndvalue = lptr; lptr->n_listvalue = xlenter(var); } /* entermsg - add a message to a class */ static struct node *entermsg(cls,msg) struct node *cls,*msg; { struct node *lptr,*mbnd,*mptr; /* lookup the "messages" instance variable */ mbnd = enterivar(cls,messages); /* lookup the message */ for (lptr = mbnd->n_bndvalue; lptr != NULL; lptr = lptr->n_listnext) if ((mptr = lptr->n_listvalue)->n_msg == msg) break; /* allocate a new message entry if one wasn't found */ if (lptr == NULL) { lptr = newnode(LIST); lptr->n_listnext = mbnd->n_bndvalue; mbnd->n_bndvalue = lptr; lptr->n_listvalue = mptr = newnode(MSG); mptr->n_msg = msg; } /* return the symbol node */ return (mptr); } /* answer - define a method for answering a message */ static struct node *answer(args) struct node *args; { struct node *oldstk,arg,msg,fargs,code; struct node *obj,*mptr,*fptr; /* create a new stack frame */ oldstk = xlsave(&arg,&msg,&fargs,&code,NULL); /* initialize */ arg.n_ptr = args; /* message symbol */ msg.n_ptr = xlevmatch(SYM,&arg.n_ptr); /* get the formal argument list */ fargs.n_ptr = xlevmatch(LIST,&arg.n_ptr); /* get the code */ code.n_ptr = xlevmatch(LIST,&arg.n_ptr); /* make sure there aren't any more arguments */ if (arg.n_ptr != NULL) xlfail("too many arguments"); /* get the object node */ obj = self->n_symvalue; /* make a new message list entry */ mptr = entermsg(obj,msg.n_ptr); /* setup the message node */ mptr->n_msgcode = fptr = newnode(FUN); fptr->n_funargs = fargs.n_ptr; fptr->n_funcode = code.n_ptr; /* restore the previous stack frame */ xlstack = oldstk; /* return the object */ return (obj); } /* mivars - define a list of instance variables */ static int mivars(args) struct node *args; { struct node *oldstk,vars,*obj,*vbnd; /* create a new stack frame */ oldstk = xlsave(&vars,NULL); /* get ivar list */ vars.n_ptr = xlevmatch(LIST,&args); /* make sure there aren't any more arguments */ if (args != NULL) xlfail("too many arguments"); /* get the object node */ obj = self->n_symvalue; /* find the ivars instance variable */ vbnd = enterivar(obj,ivars); vbnd->n_bndvalue = vars.n_ptr; /* restore the previous stack frame */ xlstack = oldstk; /* return the object */ return (obj); } /* addmsg - add a message to a class */ static addmsg(cls,msg,code) struct node *cls; char *msg; int (*code)(); { struct node *mptr; /* enter the message symbol */ mptr = entermsg(cls,xlenter(msg)); /* store the code for this message */ mptr->n_msgcode = newnode(SUBR); mptr->n_msgcode->n_subr = code; } /* xloinit - object function initialization routine */ xloinit() { struct node *csym; /* set the initialization flag */ init = TRUE; /* enter the object related symbols */ messages = xlenter("messages"); ivars = xlenter("ivars"); new = xlenter("new"); isnew = xlenter("isnew"); self = xlenter("self"); /* initialize the class object */ csym = xlenter("class"); class = csym->n_symvalue = newnode(OBJ); class->n_obclass = class; addivar(class,"messages"); addivar(class,"ivars"); addmsg(class,"new",mnew); addmsg(class,"answer",answer); addmsg(class,"ivars",mivars); addmsg(class,"isnew",misnew); /* clear the initialization flag */ init = FALSE; } :::::::::::::: xlkmap.c :::::::::::::: /* xlkmap - xlisp key map functions */ #include <stdio.h> #include "xlisp.h" /* external variables */ extern struct node *xlstack; extern struct node *xlenv; /* external procedures */ extern struct node *xlevarg(); extern struct node *xlevmatch(); extern struct node *xlmfind(); extern struct node *xlxsend(); /* local definitions */ #define KMSIZE 256 /* number of characters in a keymap */ #define KMAX 20 /* maximum number of characters in a key sequence */ /* local variables */ static struct node *currentenv; /* keymap - create a new keymap */ static struct node *keymap(args) struct node *args; { /* make sure there aren't any arguments */ if (args != NULL) xlfail("too many arguments"); /* create a keymap node */ return (newnode(KMAP)); } /* newkmap - allocate memory for a new key map vector */ static struct node *(*newkmap())[] { struct node *(*map)[]; /* allocate the vector */ if ((map = calloc(1,sizeof(struct node *) * KMSIZE)) == NULL) { printf("insufficient memory"); exit(); } /* return the new vector */ return (map); } /* key - define a key */ static struct node *key(args) struct node *args; { struct node *oldstk,arg,kmap,kstr,ksym,*kmptr; struct node *(*map)[]; char *sptr; int ch; /* create a new stack frame */ oldstk = xlsave(&arg,&kmap,&kstr,&ksym,NULL); /* initialize */ arg.n_ptr = args; /* get the keymap pointer */ kmap.n_ptr = xlevmatch(KMAP,&arg.n_ptr); /* get the key string */ kstr.n_ptr = xlevmatch(STR,&arg.n_ptr); /* get the key symbol */ ksym.n_ptr = xlevmatch(SYM,&arg.n_ptr); /* make sure there aren't any more arguments */ if (arg.n_ptr != NULL) xlfail("too many arguments"); /* process each character in the key string */ for (kmptr = kmap.n_ptr, sptr = kstr.n_ptr->n_str; *sptr != 0; kmptr = (*map)[ch]) { /* get a character */ ch = *sptr++; /* allocate a key map vector if non currently exists */ if ((map = kmptr->n_kmap) == NULL) map = kmptr->n_kmap = newkmap(); /* check for this being the last character in the string */ if (*sptr == 0) (*map)[ch] = ksym.n_ptr; else if ((*map)[ch] == NULL || (*map)[ch]->n_type != KMAP) { (*map)[ch] = newnode(KMAP); (*map)[ch]->n_kmap = newkmap(); } } /* restore the previous stack frame */ xlstack = oldstk; /* return the key map */ return (kmap.n_ptr); } /* kmprocess - process input characters using a key map */ static struct node *kmprocess(args) struct node *args; { struct node *oldstk,arg,kmap,env,margs,*kmptr,*nptr,*oldenv; struct node *(*map)[]; char keys[KMAX+1]; int ch,kndx; /* create a new stack frame */ oldstk = xlsave(&arg,&kmap,&env,&margs,NULL); /* initialize */ arg.n_ptr = args; /* get the key map */ kmap.n_ptr = xlevmatch(KMAP,&arg.n_ptr); /* get the environment */ env.n_ptr = xlevmatch(LIST,&arg.n_ptr); /* make sure there aren't any more arguments */ if (arg.n_ptr != NULL) xlfail("too many arguments"); /* bind the current environment variable */ oldenv = xlenv; xlbind(currentenv,env.n_ptr); xlfixbindings(oldenv); /* make sure the key map is defined */ if (kmap.n_ptr->n_kmap == NULL) xlfail("empty keymap"); /* create an argument list to send with key messages */ margs.n_ptr = newnode(LIST); margs.n_ptr->n_listvalue = newnode(STR); margs.n_ptr->n_listvalue->n_str = keys; margs.n_ptr->n_listvalue->n_strtype = STATIC; /* character processing loop */ for (kmptr = kmap.n_ptr, kndx = 0; TRUE; ) { /* flush pending output */ fflush(stdout); /* get a character */ if ((ch = kbin()) < 0) break; /* put it in the key sequence */ if (kndx < KMAX) keys[kndx++] = ch; else xlfail("key sequence too long"); /* dispatch on character code */ if ((map = kmptr->n_kmap) == NULL) xlfail("bad keymap"); else if ((nptr = (*map)[ch]) == NULL) { kmptr = kmap.n_ptr; kndx = 0; } else if (nptr->n_type == KMAP) kmptr = (*map)[ch]; else if (nptr->n_type == SYM) { keys[kndx] = 0; sendmsg(nptr,currentenv->n_symvalue,margs.n_ptr); kmptr = kmap.n_ptr; kndx = 0; } else xlfail("bad keymap"); } /* unbind */ xlunbind(oldenv); /* restore the previous stack frame */ xlstack = oldstk; /* return the keymap */ return (kmap.n_ptr); } /* sendmsg - send a message given an environment list */ static sendmsg(msym,env,args) struct node *msym,*env,*args; { struct node *eptr,*obj,*msg; /* look for an object that answers the message */ for (eptr = env; eptr != NULL; eptr = eptr->n_listnext) if ((obj = eptr->n_listvalue) != NULL && obj->n_type == OBJ) if ((msg = xlmfind(obj,msym)) != NULL) { xlxsend(obj,msg,args); break; } } /* xlkmmark - mark a keymap */ xlkmmark(km) struct node *km; { struct node *(*map)[]; int i; /* mark the keymap node */ km->n_flags |= MARK; /* check for a null keymap */ if ((map = km->n_kmap) == NULL) return; /* loop through each keymap entry */ for (i = 0; i < KMSIZE; i++) if (((*map)[i] != NULL) && (*map)[i]->n_type == KMAP) xlkmmark((*map)[i]); } /* xlkmfree - free a keymap */ xlkmfree(km) struct node *km; { struct node *(*map)[]; int i; /* check for a null keymap */ if ((map = km->n_kmap) == NULL) return; /* loop through each keymap entry */ for (i = 0; i < KMSIZE; i++) if (((*map)[i] != NULL) && (*map)[i]->n_type == KMAP) xlkmfree((*map)[i]); /* free this keymap */ free(km->n_kmap); } /* xlkinit - key map function initialization routine */ xlkinit() { /* define the xlisp variables */ currentenv = xlenter("currentenv"); /* define the xlisp functions */ xlsubr("keymap",keymap); xlsubr("key",key); xlsubr("kmprocess",kmprocess); }