static char rcsid[] = "$Header:eval.c 12.0$";
#ifndef lint
static char *rcsid =
   "$Header:eval.c 12.0$";
   "$ACIS:eval.c 12.0$";
#endif

/*					-[Thu Aug 18 10:07:22 1983 by jkf]-
 * 	eval.c				$Locker:  $
 * evaluator
 *
 * (c) copyright 1982, Regents of the University of California
 */

#include "global.h"
#include <signal.h>
#include "frame.h"



/*
 *	eval
 * returns the value of the pointer passed as the argument.		
 *
 */

lispval
eval(actarg)
lispval actarg;
{
#define argptr handy
    register lispval a = actarg;
    register lispval handy;
    register struct nament *namptr;
    register struct argent *workp;
    struct nament *oldbnp = bnp;
    int dopopframe = FALSE;
    int type, shortcircuit = TRUE;
    lispval Ifcall(), Iarray();
    Savestack(4);

    /*debugging 
    if (rsetsw && rsetatom->a.clb != nil) {
	printf("Eval:");
	printr(a,stdout);
	printf("\nrsetsw: %d evalhsw: %d\n", rsetsw, evalhsw);
	printf("*rset: ");
	printr(rsetatom->a.clb,stdout);
	printf(" evalhook: ");
	printr(evalhatom->a.clb,stdout);
	printf(" evalhook call flag^G: %d\n", evalhcallsw);
	fflush(stdout); 
    };  
    */

    /* check if an interrupt is pending	 and handle if so */
    if(sigintcnt > 0) sigcall(SIGINT);

    if (rsetsw && rsetatom->a.clb != nil)  /* if (*rset t) has been done */
    {
	pbuf pb;
	shortcircuit = FALSE;
	if (evalhsw != nil && evalhatom->a.clb != nil)
	{
						/*if (sstatus evalhook t)
						    and evalhook non-nil */
	    if (!evalhcallsw)
	    		/*if we got here after calling evalhook, then
			  evalhcallsw will be TRUE, so we want to skip calling
			  the hook function, permitting one form to be
			  evaluated before the hook fires.
			 */
	    {
		/* setup equivalent of (funcall evalhook <arg to eval>) */
		(np++)->val = a;		/* push form on namestack */
		lbot=np;			/* set up args to funcall */
		(np++)->val = evalhatom->a.clb; /* push evalhook's clb */
		(np++)->val = a;		/* eval's arg becomes
					           2nd arg to funcall */
		PUSHDOWN(evalhatom, nil);	/* bind evalhook to nil*/
		PUSHDOWN(funhatom, nil);	/* bind funcallhook to nil*/
		funhcallsw = TRUE;		/* skip any funcall hook */
		handy = Lfuncal();		/* now call funcall */
		funhcallsw = FALSE;
		POP;
		POP;
		Restorestack();
		return(handy);
	    };
	}
	errp = Pushframe(F_EVAL,a,nil);
	dopopframe = TRUE;	/* remember to pop later */
	if(retval == C_FRETURN)
	{
	    Restorestack();
	    errp = Popframe();
	    return(lispretval);
	}
    };
        
    evalhcallsw = FALSE;   /* clear indication that evalhook called */
    
    switch (TYPE(a))
    {
    case ATOM:
	if (rsetsw && rsetatom->a.clb != nil && bptr_atom->a.clb != nil) {

	    struct nament *bpntr, *eval1bptr;
				  /* Both rsetsw and rsetatom for efficiency*/
				    /* bptr_atom set by second arg to eval1 */
	    eval1bptr = (struct nament *) bptr_atom->a.clb->d.cdr;
				    /* eval1bptr is bnp when eval1 was called;
				       if an atom was bound after this,
				       then its clb is valid */
	    for (bpntr = eval1bptr; bpntr < bnp; bpntr++)
		if (bpntr->atm==a) {
		    handy = a->a.clb;
		    goto gotatom;
		};		    /* Value saved in first binding of a,
				       if any, after pointer to eval1,
				       is the valid value, else use its clb */
	    for (bpntr = (struct nament *)bptr_atom->a.clb->d.car;
	      bpntr < eval1bptr; bpntr++)
		if (bpntr->atm==a) {
		    handy=bpntr->val;
		    goto gotatom;   /* Simply no way around goto here */
		};
	};
        handy = a->a.clb;
    gotatom:
        if(handy==CNIL) {
            handy = errorh1(Vermisc,"Unbound Variable:",nil,TRUE,0,a);
        }
	if(dopopframe) errp = Popframe();
	Restorestack();
        return(handy);

    case VALUE:
	if(dopopframe) errp = Popframe();
	Restorestack();
        return(a->l);

    case DTPR:
        (np++)->val = a;		/* push form on namestack */
        lbot = np;			/* define beginning of argstack */
        /* oldbnp = bnp;		   redundant - Mitch Marcus */
        a = a->d.car;			/* function name or lambda-expr */
        for(EVER)
            {
            switch(TYPE(a))
                {
            case ATOM:
					/*  get function binding  */
                if(a->a.fnbnd==nil && a->a.clb!=nil) {
                    a=a->a.clb;
                    if(TYPE(a)==ATOM)
                        a=a->a.fnbnd;
                } else
                    a = a->a.fnbnd;
                break;
            case VALUE:
                a = a->l;		/*  get value  */
                break;
                }

            vtemp = (CNIL-1);       /* sentinel value for error test */

        /*funcal:*/    switch (TYPE(a))
                {
            case BCD:    /* function */
                argptr = actarg->d.cdr;

				    /* decide whether lambda, nlambda or
				       macro and push args onto argstack
				       accordingly.                */

                if(a->bcd.discipline==nlambda) {
                    (np++)->val = argptr;
                    TNP;
                } else if(a->bcd.discipline==macro) {
                    (np++)->val = actarg;
                    TNP;
                } else for(;argptr!=nil; argptr = argptr->d.cdr) {
		    /* short circuit evaluations of ATOM, INT, DOUB
		     * if not in debugging mode
		     */
		    if(shortcircuit
		       && ((type = TYPE(argptr->d.car)) == ATOM)
		       && (argptr->d.car->a.clb != CNIL))
                       (np++)->val = argptr->d.car->a.clb;
		    else if(shortcircuit &&
		    		((type == INT) || (type == STRNG)))
                       (np++)->val = argptr->d.car;
		    else
                       (np++)->val = eval(argptr->d.car);
                    TNP;
                }
                /* go for it */

                if(TYPE(a->bcd.discipline)==STRNG)
                    vtemp = Ifcall(a);
                else
                    vtemp = (*(lispval (*)())(a->bcd.start))();
                break;

            case ARRAY:
                vtemp = Iarray(a,actarg->d.cdr,TRUE);
                break;

            case DTPR:		    /* push args on argstack according to
				       type                */
		protect(a);	/* save function definition in case function
				   is redefined */
		lbot = np;
                argptr = a->d.car;
                if (argptr==lambda) {
                    for(argptr = actarg->d.cdr;
                        argptr!=nil; argptr=argptr->d.cdr) {
                        
                        (np++)->val = eval(argptr->d.car);
                        TNP;
                    }
                } else if (argptr==nlambda) {
                    (np++)->val = actarg->d.cdr;
                    TNP;
                } else if (argptr==macro) {
                    (np++)->val = actarg;
                    TNP;
                } else if (argptr==lexpr) {
                    for(argptr = actarg->d.cdr;
                      argptr!=nil; argptr=argptr->d.cdr) {
                        
                        (np++)->val = eval(argptr->d.car);
                        TNP;
                    }
                    handy = newdot();
                    handy->d.car = (lispval)lbot;
                    handy->d.cdr = (lispval)np;
                    PUSHDOWN(lexpr_atom,handy);
                    lbot = np;
                    (np++)->val = inewint(((lispval *)handy->d.cdr) - (lispval *)handy->d.car);

                } else break;    /* something is wrong - this isn't a proper function */

                argptr = (a->d.cdr)->d.car;
                namptr =  bnp;
                workp = lbot;
                if(bnp + (np - lbot)> bnplim)
                    binderr();
                for(;argptr != (lispval)nil;
                     workp++,argptr = argptr->d.cdr)    /* rebind formal names (shallow) */
                {
                    if(argptr->d.car==nil)
                        continue;
                    /*if(((namptr)->atm = argptr->d.car)==nil)
                        error("Attempt to lambda bind nil",FALSE);*/
                    namptr->atm = argptr->d.car;
                    if (workp < np) {
                        namptr->val = namptr->atm->a.clb;
                        namptr->atm->a.clb = workp->val;
                    } else
                        bnp = namptr,
                        error("Too few actual parameters",FALSE);
                    namptr++;
                }
                bnp = namptr;
                if (workp < np)
                    error("Too many actual parameters",FALSE);

				    /* execute body, implied prog allowed */

                for (handy = a->d.cdr->d.cdr;
                    handy != nil;
                    handy = handy->d.cdr) {
                        vtemp = eval(handy->d.car);
                    }
                }
            if (vtemp != (CNIL-1)) {
				/* if we get here with a believable value, */
				/* we must have executed a function. */
                popnames(oldbnp);

                /* in case some clown trashed t */

                tatom->a.clb = (lispval) tatom;
                if(a->d.car==macro)
		{
		    if(Vdisplacemacros->a.clb && (TYPE(vtemp) == DTPR))
		    {
			actarg->d.car = vtemp->d.car;
			actarg->d.cdr = vtemp->d.cdr;
		    }
		    vtemp = eval(vtemp);
		}
                    /* It is of the most wonderful 
                       coincidence that the offset
                       for car is the same as for
                       discipline so we get bcd macros
                       for free here ! */
		if(dopopframe) errp = Popframe();
		Restorestack();
		return(vtemp);
	    }
            popnames(oldbnp);
            a = (lispval) errorh1(Verundef,"eval: Undefined function ",nil,TRUE,0,actarg->d.car);
            }

        }
    if(dopopframe) errp = Popframe();
    Restorestack();
    return(a);    /* other data types are considered constants */
}

/*
 *    popnames
 * removes from the name stack all entries above the first argument.   
 * routine should usually be used to clean up the name stack as it    
 * knows about the special cases.  bnp is returned pointing to the
 * same place as the argument passed.
 */
lispval
popnames(llimit)
register struct nament *llimit;
{
    register struct nament *rnp;

    for(rnp = bnp; --rnp >= llimit;)
        rnp->atm->a.clb = rnp->val;
    bnp = llimit;
}


/* dumpnamestack
 * utility routine to dump out the namestack.
 * from bottom to 5 above np
 * should be put elsewhere
 */
dumpnamestack()
{
    struct argent *newnp;

    printf("namestack dump:\n");
    for(newnp = orgnp ; (newnp < np + 6) && (newnp < nplim) ; newnp++)
    {
	if(newnp == np) printf("**np:**\n");
	printf("[%d]: ",newnp-orgnp);
	printr(newnp->val,stdout);
	printf("\n");
    }
    printf("end namestack dump\n");
}



lispval
Lapply()
{
    register lispval a;
    register lispval handy;
    lispval vtemp, Ifclosure();
    struct nament *oldbnp = bnp;
    struct argent *oldlbot = lbot; /* Bottom of my frame! */
    struct argent *oldnp = np; /* First free on stack */
    int extrapush;		/* if must save function value */

    a = lbot->val;
    argptr = lbot[1].val;
    if(np-lbot!=2)
        errorh2(Vermisc,"Apply: Wrong number of args.",nil,FALSE,
               999,a,argptr);
    if(TYPE(argptr)!=DTPR && argptr!=nil)
        argptr = errorh1(Vermisc,"Apply: non-list of args",nil,TRUE,
                998,argptr);
    (np++)->val = a;    /* push form on namestack */
    TNP;
    lbot = np;        /* bottom of current frame */
    for(EVER)
        {
	extrapush = 0;
        if (TYPE(a) == ATOM) { a = a->a.fnbnd; extrapush = 1; }
					/* get function definition (unless
					   calling form is itself a lambda-
					   expression) */
        vtemp = CNIL;			/* sentinel value for error test */
        switch (TYPE(a)) {

        case BCD: 
					/* push arguments - value of a */
            if(a->bcd.discipline==nlambda || a->bcd.discipline==macro) {
                (np++)->val=argptr;
                TNP;
            } else for (; argptr!=nil; argptr = argptr->d.cdr) {
                (np++)->val=argptr->d.car;
                TNP;
            }

	    if(TYPE(a->bcd.discipline) == STRNG)
	      vtemp = Ifcall(a);	/* foreign function */
	    else
              vtemp = (*(lispval (*)())(a->bcd.start))(); /* go for it */
            break;

        case ARRAY:
            vtemp = Iarray(a,argptr,FALSE);
            break;


        case DTPR:
            if (a->d.car==nlambda || a->d.car==macro) {
                (np++)->val = argptr;
                TNP;
            } else if (a->d.car==lambda)
                for (; argptr!=nil; argptr = argptr->d.cdr) {
                    (np++)->val = argptr->d.car;
                    TNP;
                }
            else if(a->d.car==lexpr) {
                for (; argptr!=nil; argptr = argptr->d.cdr) {
                    
                    (np++)->val = argptr->d.car;
                    TNP;
                }
                handy = newdot();
                handy->d.car = (lispval)lbot;
                handy->d.cdr = (lispval)np;
                PUSHDOWN(lexpr_atom,handy);
                lbot = np;
                (np++)->val = inewint(((lispval *)handy->d.cdr) - (lispval *)handy->d.car);

            } else break;    /* something is wrong - this isnt a proper function */
            rebind(a->d.cdr->d.car,lbot);

	    if (extrapush == 1) { protect(a); extrapush = 2;}
            for (handy = a->d.cdr->d.cdr;
                handy != nil;
                handy = handy->d.cdr) {
                    vtemp = eval(handy->d.car);    /* go for it */
                }
	    break;
	    
	case VECTOR:
	    /* certain vectors are valid (fclosures) */
	   if(a->v.vector[VPropOff] == fclosure)
	       vtemp = (lispval) Ifclosure(a,FALSE);
	   break;
		
        };
	
	/* pop off extra value if we pushed it before */
	if (extrapush == 2)
	{
	    np--;
	    extrapush = 0;
	};
	
        if (vtemp != CNIL)
				/* if we get here with a believable value, */
				/* we must have executed a function. */
            {
            popnames(oldbnp);

            /* in case some clown trashed t */

            tatom->a.clb = (lispval) tatom;
	    np = oldnp; lbot = oldlbot;
            return(vtemp);
            }
        popnames(oldbnp);
        a = (lispval) errorh1(Verundef,"apply: Undefined Function ",
					      nil,TRUE,0,oldlbot->val);
    }
    /*NOT REACHED*/
}


/*
 * Rebind -- rebind formal names
 */
rebind(argptr,workp)
register lispval argptr;        /* argptr points to list of atoms */
register struct argent * workp;        /* workp points to position on stack
                       where evaluated args begin */
{
    register struct nament *namptr = bnp;

    for(;argptr != (lispval)nil;
         workp++,argptr = argptr->d.cdr)  /* rebind formal names (shallow) */
    {
        if(argptr->d.car==nil)
            continue;
        namptr->atm = argptr->d.car;
        if (workp < np) {
            namptr->val = namptr->atm->a.clb;
            namptr->atm->a.clb = workp->val;
        } else
            bnp = namptr,
            error("Too few actual parameters",FALSE);
        namptr++;
        if(namptr > bnplim)
            binderr();
    }
    bnp = namptr;
    if (workp < np)
        error("Too many actual parameters",FALSE);
}

/* the argument to Lfuncal is now mandatory since nargs
 * wont work on RISC. If it is given  then it is 
 * the name of the function to call and lbot points to the first arg.
 * if it is not given, then lbot points to the function to call
 */
lispval
Ifuncal(fcn)
lispval fcn;
{
    register lispval a;
    register lispval handy; 
    struct nament *oldbnp = bnp;	/* MUST be first local for evalframe */
    lispval fcncalled;
    lispval Ifcall(),Llist(),Iarray(), Ifclosure();
    lispval vtemp;
    int typ, dopopframe = FALSE, extrapush;
    extern lispval end[];
    Savestack(3);

    /*if(nargs()==1)			/* function I am evaling.    */
	a = fcncalled = fcn;
    /*else { a = fcncalled = lbot->val; lbot++; }*/

    /*debugging 
    if (rsetsw && rsetatom->a.clb != nil) {
	printf("funcall:");
	printr(a,stdout);
	printf("\nrsetsw: %d evalhsw: %d\n", rsetsw, evalhsw);
	printf("*rset: ");
	printr(rsetatom->a.clb,stdout);
	printf(" funhook: ");
	printr(funhatom->a.clb,stdout);
	printf(" funhook call flag^G: %d\n",funhcallsw);
	fflush(stdout); 
    };  
    */

    /* check if exception pending */
    if(sigintcnt > 0 ) sigcall(SIGINT);

    if (rsetsw && rsetatom->a.clb != nil)  /* if (*rset t) has been done */
    {    	
	pbuf pb;
	if (evalhsw != nil && funhatom->a.clb != nil)
	{
						/*if (sstatus evalhook t)
						    and evalhook non-nil */
	    if (!funhcallsw)
			/*if we got here after calling funcallhook, then
			  funhcallsw will be TRUE, so we want to skip calling
			  the hook function, permitting one form to be
			  evaluated before the hook fires.
			 */
	    {
		/* setup equivalent of (funcall funcallhook <args to eval>) */
		protect(a);
		a = fcncalled = funhatom->a.clb; /* new function to funcall */
		PUSHDOWN(funhatom, nil);	/* lambda-bind 
						 * funcallhook to nil
						 */
		PUSHDOWN(evalhatom, nil);	
	     /* printf(" now will funcall ");
		printr(a,stdout);
		putchar('\n');
		fflush(stdout); */
	    };
	}
	errp = Pushframe(F_FUNCALL,a,nil);
	dopopframe = TRUE;	/* remember to pop later */
	if(retval == C_FRETURN)
	{
	    popnames(oldbnp);
	    errp = Popframe();
	    Restorestack();
	    return(lispretval);
	}
    };
    
    funhcallsw = FALSE;	/* so recursive calls to funcall will cause hook
    			   to fire */
    for(EVER)
    {
     top:
        extrapush = 0;
	
        typ = TYPE(a);
        if (typ == ATOM)
	{   /* get function defn (unless calling form */
            /* is itself a lambda-expr) */
	    a = a->a.fnbnd;
	    typ = TYPE(a);
	    extrapush = 1;	/* must protect this later */
	}
        vtemp = CNIL-1;            /* sentinel value for error test */
        switch (typ) {
        case ARRAY:
	    protect(a);			/* stack array descriptor on top */
	    a = a->ar.accfun;		/* now funcall access function */
	    goto top;
        case BCD:
            if(a->bcd.discipline==nlambda)
                {   if(np==lbot) protect(nil);  /* default is nil */
                while(np-lbot!=1 || (lbot->val != nil &&
                      TYPE(lbot->val)!=DTPR)) {

			    lbot->val = errorh1(Vermisc,"Bad funcall arg(s) to fexpr.",
						 nil,TRUE,0,lbot->val);
			    
                    np = lbot+1;
                    }
                }
            /* go for it */

            if(TYPE(a->bcd.discipline)==STRNG)
                vtemp = Ifcall(a);
            else
                vtemp = (*(lispval (*)())(a->bcd.start))();
            if(a->bcd.discipline==macro)
                vtemp = eval(vtemp);
            break;


        case DTPR:
            if (a->d.car == lambda) {
                ;/* VOID */
            } else if (a->d.car == nlambda || a->d.car==macro) {
                if( np==lbot ) protect(nil);    /* default */
                while(np-lbot!=1 || (lbot->val != nil &&
                          TYPE(lbot->val)!=DTPR)) {
                    lbot->val = error("Bad funcall arg(s) to fexpr.",TRUE);
                    np = lbot+1;
                    }
            } else if (a->d.car == lexpr) {
                handy = newdot();
                handy->d.car = (lispval) lbot;
                handy->d.cdr = (lispval) np;
                PUSHDOWN(lexpr_atom,handy);
                lbot = np;
                (np++)->val = inewint(((lispval *)handy->d.cdr) - (lispval *)handy->d.car);
            } else break;        /* something is wrong - this isn't a proper function */
            rebind(a->d.cdr->d.car,lbot);

	    /* since the actual arguments are bound to their formal params
	     * we can pop them off the stack.  However if we are doing
	     * debugging (that is if we've pushed a frame on the stack)
	     * then we must not pop off the actual args since they must
	     * be visible for evalframe to work
	     */
            if(!dopopframe) np = lbot;
	    if (extrapush == 1) {protect(a);  extrapush = 2;}
            for (handy = a->d.cdr->d.cdr;
                handy != nil;
                handy = handy->d.cdr) {
                    vtemp = eval(handy->d.car);    /* go for it */
                }
            if(a->d.car==macro)
                vtemp = eval(vtemp);
	    break;
	    
	case VECTOR:
	   /* A fclosure represented as a vector with the property 'fclosure' */
	   if(a->v.vector[VPropOff] == fclosure)
	       vtemp = (lispval) Ifclosure(a,TRUE);
	   break;
	   
        }
	
	/* pop off extra value if we pushed it before */
	if(extrapush == 2) { np-- ; extrapush = 0; }
	
        if (vtemp != CNIL-1)
            /* if we get here with a believable value, */
            /* we must have executed a function. */
            {
            popnames(oldbnp);

            /* in case some clown trashed t */

            tatom->a.clb = (lispval) tatom;

	    if(dopopframe) errp = Popframe();
	    Restorestack();
            return(vtemp);
            }
        popnames(oldbnp);
	    a = fcncalled = (lispval) errorh1(Verundef,"funcall: Bad function",
					       nil,TRUE,0,fcncalled);
    }
    /*NOT REACHED*/
}
lispval   /* this version called from lisp */
Lfuncal()
{
	lispval handy;
	Savestack(0);
	
	switch(np-lbot)
	{
	    case 0: argerr("funcall");
	    	    break;
	}
	handy = lbot++->val;
	handy = Ifuncal(handy);
	Restorestack();
	return(handy);
}

/* The following must be the next "function" after Lfuncal, for the
sake of Levalf.  */
fchack () {}


/*
 * Llexfun  :: lisp function lexpr-funcall
 * lexpr-funcall is a cross between funcall and apply.
 * the last argument is nil or a list of the rest of the arguments.
 * we push those arguments on the stack and call funcall
 *
 */
lispval
Llexfun()
{
    register lispval handy;
    
    switch(np-lbot)
    {
	case 0: argerr("lexpr-funcall");	/* need at least one arg */
		break;
	case 1: return(Lfuncal());	 /* no args besides function */
    }
    /* have at least one argument past the function to funcall */
    handy = np[-1].val;		/* get last value */
    np--;			/* pop it off stack */
    
    while((handy != nil) && (TYPE(handy) != DTPR))
    	handy = errorh1(Vermisc,"lexpr-funcall: last argument is not a list ",
			nil,TRUE,0,handy);

    /* stack arguments */
    for( ; handy != nil ; handy = handy->d.cdr) protect(handy->d.car);

    return(Lfuncal());
}

	
#undef protect
	
/* protect 
 * pushes the first argument onto namestack, thereby protecting from gc
 */
lispval
protect(a)
lispval a;
{
    (np++)->val = a;
       if (np >=  nplim)
        namerr();
}

/* unprot
 * returns the top thing on the name stack.  Underflow had better not
 * occur.
 */
lispval
unprot()
    {
    return((--np)->val);
    }

lispval
linterp()
    {
    error("BYTE INTERPRETER CALLED ERRONEOUSLY",FALSE);
    }

/* Undeff - called from qfuncl when it detects a call to a undefined
    function from compiled code, we print out a message and
    will continue only if returned a symbol (ATOM in C parlance).
*/
lispval
Undeff(atmn)
lispval atmn;
{
    do {atmn =errorh1(Verundef,"Undefined function called from compiled code ",
				      nil,TRUE,0,atmn);}
	while(TYPE(atmn) != ATOM);
    return(atmn);		      
}

/* VARARGS1 */
bindfix(firstarg)
lispval firstarg;
{
    register lispval *argp = &firstarg;
    register struct nament *mybnp = bnp;
    while(*argp != nil) {
        mybnp->atm = *argp++;
        mybnp->val = mybnp->atm->a.clb;
        mybnp->atm->a.clb = *argp++;
        bnp = mybnp++;
    }
}

