#ifndef lint static char *rcsid = "$Header: eval2.c,v 1.8 85/03/24 11:03:02 sklower Exp $"; #endif /* -[Sat May 7 23:38:37 1983 by jkf]- * eval2.c $Locker: $ * more of the evaluator * * (c) copyright 1982, Regents of the University of California */ #include "global.h" #include "frame.h" /* Iarray - handle array call. * fun - array object * args - arguments to the array call , most likely subscripts. * evalp - flag, if TRUE then the arguments should be evaluated when they * are stacked. */ lispval Iarray(fun,args,evalp) register lispval fun,args; { Savestack(2); lbot = np; protect(fun->ar.accfun); for ( ; args != nil ; args = args->d.cdr) /* stack subscripts */ if(evalp) protect(eval(args->d.car)); else protect(args->d.car); protect(fun); vtemp = Lfuncal(); Restorestack(); return(vtemp); } dumpmydata(thing) int thing; { register int *ip = &thing; register int *lim = ip + nargs(); printf("Dumpdata got %d args:\n",nargs()); while(ip < lim) printf("%x\n",*ip++); return(0); } /* Ifcall :: call foreign function/subroutine * Ifcall is handed a binary object which is the function to call. * This function has already been determined to be a foreign function * by noticing that its discipline field is a string. * The arguments to pass have already been evaluated and stacked. We * create on the stack a 'callg' type argument list to give to the * function. What is passed to the foreign function depends on the * type of argument. Certain args are passes directly, others must be * copied since the foreign function my want to change them. * When the foreign function returns, we may have to box the result, * depending on the type of foreign function. */ lispval Ifcall(a) lispval a; { char *alloca(); long callg_(); register int *arglist; register int index; register struct argent *mynp; register lispval ltemp; pbuf pb; int nargs = np - lbot, kind, mysize, *ap; Keepxs(); /* put a frame on the stack which will save np and lbot in a easy to find place in a standard way */ errp = Pushframe(F_TO_FORT,nil,nil); mynp = lbot; kind = (((char *)a->bcd.discipline)[0]); /* dispatch according to whether call by reference or value semantics */ switch(kind) { case 'f': case 'i': case 's': case 'r': arglist = (int *) alloca((nargs + 1) * sizeof(int)); *arglist = nargs; for(index = 1; index <= nargs; index++) { switch(TYPE(ltemp=mynp->val)) { /* fixnums and flonums must be reboxed */ case INT: stack(0); arglist[index] = (int) sp(); *(int *) arglist[index] = ltemp->i; break; case DOUB: stack(0); stack(0); arglist[index] = (int) sp(); *(double *) arglist[index] = ltemp->r; break; /* these cause only part of the structure to be sent */ case ARRAY: arglist[index] = (int) ltemp->ar.data; break; case BCD: arglist[index] = (int) ltemp->bcd.start; break; /* anything else should be sent directly */ default: arglist[index] = (int) ltemp; break; } mynp++; } break; case 'v': while(TYPE(mynp->val)!=VECTORI) mynp->val = error( "First arg to c-function-returning-vector must be of type vector-immediate", TRUE); nargs--; mynp++; lbot++; case 'c': case 'd': /* make one pass over args calculating size of arglist */ while(mynp < np) switch(TYPE(ltemp=mynp++->val)) { case DOUB: nargs += ((sizeof(double)/sizeof(int))-1); break; case VECTORI: if(ltemp->v.vector[-1]==Vpbv) { nargs += -1+VecTotSize(ltemp->vl.vectorl[-2]); } } arglist = (int *) alloca((nargs+1)*sizeof(int)); *arglist = nargs; ap = arglist + 1; /* make another pass over the args actually copying the arguments */ for(mynp = lbot; mynp < np; mynp++) switch(TYPE(ltemp=mynp->val)) { case INT: *ap++ = ltemp->i; break; case DOUB: *(double *)ap = ltemp->r; ap += (sizeof (double)) / (sizeof (long)); break; case VECTORI: if(ltemp->v.vector[-1]==Vpbv) { mysize = ltemp->vl.vectorl[-2]; mysize = sizeof(long) * VecTotSize(mysize); xbcopy(ap,ltemp,mysize); ap = (long *) (mysize + (int) ap); break; } default: *ap++ = (long) ltemp; } } switch(kind) { case 'i': /* integer-function */ case 'c': /* C-function */ ltemp = inewint(callg_(a->bcd.start,arglist)); break; case 'r': /* real-function*/ case 'd': /* C function declared returning double */ { double result = (* ((double (*)()) callg_))(a->bcd.start,arglist); ltemp = newdoub(); ltemp->r = result; } break; case 'f': /* function */ ltemp = (lispval) callg_(a->bcd.start,arglist); break; case 'v': /* C function returning a structure */ ap = (long *) callg_(a->bcd.start,arglist); ltemp = (--lbot)->val; mysize = ltemp->vl.vectorl[-2]; mysize = sizeof(long) * VecTotSize(mysize); xbcopy(ltemp,ap,mysize); break; default: case 's': /* subroutine */ callg_(a->bcd.start,arglist); ltemp = tatom; } errp = Popframe(); Freexs(); return(ltemp); } xbcopy(to,from,size) register char *to, *from; register size; { while(--size >= 0) *to++ = *from++; } lispval ftolsp_(arg1) lispval arg1; { int count; register lispval *ap = &arg1; lispval save; pbuf pb; Savestack(1); if((count = nargs())==0) return;; if(errp->class==F_TO_FORT) np = errp->svnp; errp = Pushframe(F_TO_LISP,nil,nil); lbot = np; for(; count > 0; count--) np++->val = *ap++; save = Lfuncal(); errp = Popframe(); Restorestack(); return(save); } lispval ftlspn_(func,arglist) lispval func; register long *arglist; { int count; lispval save; pbuf pb; Savestack(1); if(errp->class==F_TO_FORT) np = errp->svnp; errp = Pushframe(F_TO_LISP,nil,nil); lbot = np; np++->val = func; count = *arglist++; for(; count > 0; count--) np++->val = (lispval) (*arglist++); save = Lfuncal(); errp = Popframe(); Restorestack(); return(save); } /* Ifclosure :: evaluate a fclosure (new version) * the argument clos is a vector whose property is the atom fclosure * the form of the vector is * 0: function to run * then for each symbol there is on vector entry containing a * pointer to a sequence of two list cells of this form: * (name value . count) * name is the symbol name to close over * value is the saved value of the closure * (if the closure is 'active', the current value will be in the * symbol itself) * count is a fixnum box (which can be destructively modified safely) * it is normally 0. Each time the variable is put on the stack, it is * incremented. It is decremented each time the the closure is left. * If the closure is invoked recusively without a rebinding of the * closure variable X, then the count will not be incremented. * * when entering a fclosure, for each variable there are three * possibities: * (a) this is the first instance of this closed variable * (b) this is the second or greater recursive instance of * this closure variable, however it hasn't been normally lambda * bound since the last closure invocation * (c) like (b) but it has been lambda bound before the most recent * closure. * * case (a) can be determined by seeing if the count is 0. * if the count is >0 then we must scan from the top of the stack down * until we find either the closure or a lambda binding of the variable * this determines whether it is case (b) or (c). * * There are three actions to perform in this routine: * 1. determine the closure type (a,b or c) and do any binding necessary * 2. call the closure function * 3. unbind any necessary closure variables. * * Now, the details of those actions: * 1. for case (b), do nothing as we are still working with the correct * value * for case (a), pushdown the symbol and give it the value from * the closure, inc the closure count * push a closure marker on the bindstack too. * for case (c), must locate the correct value to set by searching * for the last lambda binding before the previous closure. * pushdown the symbol and that value, inc the closure count * push a closure marker on the bindstack too. * a closure marker has atom == int:closure-marker and value pointing * to the closure list. This will be noticed when unbinding. * * 3. unbinding is just like popnames except if a closure marker is * seen, then this must be done: * if the count is 1, just store the symbol's value in the closure * and decrement the count. * if the count is >1, then search up the stack for the last * lambda before the next occurance of this closure variable * and set its value to the current value of the closure. * decrement the closure count. * * clos is the fclosure, funcallp is TRUE if this is called from funcall, * otherwise it is called from apply */ #define Case_A 0 #define Case_B 1 #define Case_C 2 lispval Ifclosure(clos,funcallp) register lispval clos; { struct nament *oldbnp = bnp, *lbnp, *locatevar(); register int i; register lispval vect; int numvars, vlength, tcase, foundc; lispval handy, atm_dtpr, value_dtpr, Ifuncal(), Lapply(); Savestack(3); /* bind variables to their values given in the fclosure */ vlength = VecTotSize(clos->vl.vectorl[VSizeOff]); /* vector length must be positive (it has to have a function at least) */ if (vlength < 1) errorh1(Vermisc,"funcall: fclosure has wrong size ",nil,FALSE,0, clos); numvars = (vlength - 1); /* number of varibles */ for (i = 1 ; i < vlength ; i += 1) { atm_dtpr = clos->v.vector[i]; /* car is symbol name */ value_dtpr = atm_dtpr->d.cdr; /* car: value, cdr: fixnum count */ if(value_dtpr->d.cdr->i == 0) tcase = Case_A; /* first call */ else { lbnp = locatevar(atm_dtpr,&foundc,bnp-1); if (!foundc) { /* didn't find the expected closure, count must be wrong, correct it and assume case (a) */ tcase = Case_A; value_dtpr->d.cdr->i = 0; } else if(lbnp) tcase = Case_C ; /* found intermediate lambda bnd*/ else tcase = Case_B; /* no intermediate lambda bind */ } /* now bind the value if necessary */ switch(tcase) { case Case_A: PUSHDOWN(atm_dtpr->d.car,value_dtpr->d.car); PUSHVAL(clos_marker,atm_dtpr); value_dtpr->d.cdr->i += 1; break; case Case_B: break; /* nothing to do */ case Case_C: /* push first bound value after last close */ PUSHDOWN(atm_dtpr->d.car,lbnp->val); PUSHVAL(clos_marker,atm_dtpr); value_dtpr->d.cdr->i += 1; break; } } if(funcallp) handy = Ifuncal(clos->v.vector[0]); else { handy = lbot[-2].val; /* get args to apply. This is hacky and may fail if apply is changed */ lbot = np; protect(clos->v.vector[0]); protect(handy); handy = Lapply(); } xpopnames(oldbnp); /* pop names with consideration for closure markers */ if(!funcallp) Restorestack(); return(handy); } /* xpopnames :: pop values from bindstack, but look out for * closure markers. This is used (instead of the faster popnames) * when we know there will be closure markers or when we can't * be sure that there won't be closure markers (eg. in non-local go's) */ xpopnames(llimit) register struct nament *llimit; { register struct nament *rnp, *lbnp; lispval atm_dtpr, value_dtpr; int foundc; for(rnp = bnp; --rnp >= llimit;) { if(rnp->atm == clos_marker) { atm_dtpr = rnp->val; value_dtpr = atm_dtpr->d.cdr; if(value_dtpr->d.cdr->i <= 1) { /* this is the only occurance of this closure variable * just restore current value to this closure. */ value_dtpr->d.car = atm_dtpr->d.car->a.clb; } else { /* locate the last lambda before the next occurance of * this closure and store the current symbol's value * there */ lbnp = locatevar(atm_dtpr,&foundc,rnp-2); if(!foundc) { /* strange, there wasn't a closure to be found. * well, we will fix things up so the count is * right. */ value_dtpr->d.car = atm_dtpr->d.car->a.clb; value_dtpr->d.cdr->i = 1; } else if (lbnp) { /* note how the closures value isn't necessarily * stored in the closure, it may be stored on * the bindstack */ lbnp->val = atm_dtpr->d.car->a.clb; } /* the case where lbnp is 0 should never happen, but if it does, we can just do nothing safely */ } value_dtpr->d.cdr->i -= 1; } else rnp->atm->a.clb = rnp->val; /* the normal case */ } bnp = llimit; } struct nament * locatevar(clos,foundc,rnp) struct nament *rnp; lispval clos; int *foundc; { register struct nament *retbnp; lispval symb; retbnp = (struct nament *) 0; *foundc = 0; symb = clos->d.car; for( ; rnp >= orgbnp ; rnp--) { if((rnp->atm == clos_marker) && (rnp->val == clos)) { *foundc = 1; /* found the closure */ return(retbnp); } if(rnp->atm == symb) retbnp = rnp; } return(retbnp); } lispval LIfss() { register lispval atm_dtpr, value_dtpr; struct nament *oldbnp = bnp, *lbnp; int tcase, foundc = 0; lispval newval; int argc = 1; Savestack(2); switch(np-lbot) { case 2: newval = np[-1].val; argc++; case 1: atm_dtpr = lbot->val; value_dtpr = atm_dtpr->d.cdr; break; default: argerr("int:fclosure-symbol-stuff"); } /* this code is copied from Ifclosure */ if(value_dtpr->d.cdr->i==0) tcase = Case_A; /* closure is not active */ else { lbnp = locatevar(atm_dtpr,&foundc,bnp-1); if (!foundc) { /* didn't find closure, count must be wrong, correct it and assume case (a).*/ tcase = Case_A; value_dtpr->d.cdr->i = 0; } else if(lbnp) tcase = Case_C; /* found intermediate lambda*/ else tcase = Case_B; } switch(tcase) { case Case_B: if(argc==2) return(atm_dtpr->d.car->a.clb = newval); return(atm_dtpr->d.car->a.clb); case Case_A: if(argc==2) return(value_dtpr->d.car = newval); return(value_dtpr->d.car); case Case_C: if(argc==2) return(lbnp->val = newval); return(lbnp->val); } /*NOTREACHED*/ }