BSD 4_3_Tahoe development
[unix-history] / usr / src / ucb / lisp / franz / eval2.c
#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*/
}