/* $Header: /afs/athena.mit.edu/astaff/project/atdev/src/fmax/RCS/eval.c,v 1.3 91/03/05 09:13:02 dot Exp $ */

/*******************************************************************
  Copyright (C) 1990 by the Massachusetts Institute of Technology

   Export of this software from the United States of America is assumed
   to require a specific license from the United States Government.
   It is the responsibility of any person or organization contemplating
   export to obtain such a license before exporting.

WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
distribute this software and its documentation for any purpose and
without fee is hereby granted, provided that the above copyright
notice appear in all copies and that both that copyright notice and
this permission notice appear in supporting documentation, and that
the name of M.I.T. not be used in advertising or publicity pertaining
to distribution of the software without specific, written prior
permission.  M.I.T. makes no representations about the suitability of
this software for any purpose.  It is provided "as is" without express
or implied warranty.

***************************************************************** */

#include <stdio.h>
#include <memory.h>
#include <signal.h>
#include <errno.h>
#include <setjmp.h>

#include "datatypes.h"

#define STACK_DEPTH 200
static Value stack[STACK_DEPTH];
static int sp = -1;	         /* stack pointer */
#define TOS stack[sp]
#define MarkStackFrame() sp
#define RestoreStackFrame(frame) sp = frame
#define LocalVariable(frame,prm) (&stack[frame+prm])


extern 
    f_lnot(),f_bnot(),f_uminus(),f_uplus(),
    f_lor(),f_land(),f_bor(),f_xor(),f_band(),f_eq(),f_ne(),f_gt(),f_lt(),
    f_ge(),f_le(),f_plus(),f_minus(),f_mult(),f_div(),f_mod(),f_power(),
    f_factorial(),f_bool(),
    f_plusover(),f_minusover(),f_multover(), f_divover(),
    f_modover(), f_powerover(), f_lorover(), f_borover(), f_xorover(),
    f_landover(),f_bandover(),f_sizeof(), f_concat();



static int numprms[] = { 
    1,1,1,1,2,2,2,2,2,2,2,2,2,2,
    2,2,2,2,2,2,2,1,1,1,1,1,1,
    1,1,1,1,1,1,1,1,2
};

static funcaddr funcs[] = { 
    f_lnot,	f_bnot,	  f_uminus,	f_uplus, f_lor,
    f_land,	f_bor,	       f_xor,	f_band,	    
    f_eq,	    f_ne,
    f_gt,	    f_lt,	    f_ge,
    f_le,	    f_plus,      f_minus,
    f_mult,	    f_div,        f_mod,
    f_power,   f_factorial,
    f_bool,	   f_plusover,    f_minusover,
    f_multover,   f_divover,     f_modover,
    f_powerover, f_lorover,    f_borover,
    f_xorover,    f_landover,   f_bandover,
    f_sizeof,     f_concat

};



reset_stack()
{
  sp = -1;
}


check_stack()	/* make sure stack's empty */
{
  if (sp != -1)
    fprintf(stderr,"\nwarning:  internal error--stack not empty!\n");
}


Value *pop(x)
Value *x;
{
  if (sp < 0 )
    internalerr("stack underflow");
  *x = stack[sp--];
  return(x);
}


push(x)
Value *x;
{
  if (sp == STACK_DEPTH-1)
    runerr("stack overflow");
  stack[++sp] = *x;
}


DoActionTab(at)
register ActionTab *at;
{
    register int i;
    register Action *a;
    register Value *f;
    register int frame;
    char msg[100];

    frame = MarkStackFrame();

    for(i = 0; i<at->size;) {
	a = &at->action[i];
	switch (a->op) {
	  case Push:            /* push a global variable on the stack */
	    if (a->arg.variable->value.type == undef) {
		sprintf(msg,"Variable `%s' is undefined.",
			a->arg.variable->name);
		runerr(msg);
	    }
	    push(&a->arg.variable->value);
	    i++;
	    break;
	  case Pushc:
	    push(&a->arg.constant);
	    i++;
	    break;
	  case Pushl:
	    push(LocalVariable(frame,a->arg.frameoff));
	    i++;
	    break;
	  case Call:
	    f = a->arg.function;
	    if (f->type == primfunc) {
		switch (f->pf.numprms) {
		  case 0:
		    (*f->pf.func)(&stack[++sp]);
		    break;
		  case 1:
		    (*f->pf.func)(&stack[sp],&stack[sp]);
		    break;
		  case 2:
		    (*f->pf.func)(&stack[sp-1],&stack[sp],&stack[sp-1]);
		    sp--;
		    break;
		  case 3:
		    (*f->pf.func)(&stack[sp-2],&stack[sp-1],
				  &stack[sp],&stack[sp-2]);
		    sp -= 2;
		    break;
		  case 4:
		    (*f->pf.func)(&stack[sp-3],&stack[sp-2],&stack[sp-1],
				  &stack[sp],&stack[sp-3]);
		    sp -= 3;
		    break;
		  default:
		    internalerr("Too many args to a primitive function");
		    break;
		}
	    }
	    else if (f->type != undef) {   /* f->type == deffunc */
/*
                DoActionTab(f->df.actiontab);
		stack[sp - f->df.numprms] = stack[sp];
		sp -= f->df.numprms;
*/		
	    }
	    else { /* function is undefined */
		char msgbuf[80];
		sprintf(msgbuf,"function %s is undefined",GetNameOfValue(f));
		runerr(msgbuf);
	    }
	    i++;
	    break;
	  case Index:
	    if (!isarray(&stack[sp-1])) runerr("Not an array.  Can't index");
	    if (!isintorreal(&stack[sp]))
		runerr("Type Mismatch: array index must be integer or real");
	    if (isreal(&stack[sp])) f_int(&stack[sp],&stack[sp]);
	    if (stack[sp].i.i < 0) runerr("Index too small");
	    if (stack[sp].i.i >= stack[sp-1].a.N) runerr("Index too large");
	    stack[sp-1] = stack[sp-1].a.vals[stack[sp].i.i];
	    sp--;
	    i++;
	    break;
	  case Jump:
	    i += a->arg.jumpoff;
	    break;
	  case Jumpz:
	    i += ((stack[sp--].i.i == 0) ? (a->arg.jumpoff) : 1);
	    break;
	  case Jumpnz:
	    i += ((stack[sp--].i.i != 0) ? (a->arg.jumpoff) : 1);
	    break;
	  case Jumpland:
	    i += ((stack[sp].i.i == 0) ? (a->arg.jumpoff) : (sp--, 1));
	    break;
	  case Jumplor:
	    i += ((stack[sp].i.i != 0) ? (a->arg.jumpoff) : (sp--, 1));
	    break;
	  case Pop:
	    sp--;
	    i++;
	    break;
	  default:     /* call a unary or binary operator */
	    if (numprms[(int)a->op] == 1) 
	      (*funcs[(int)a->op])(&stack[sp],&stack[sp]);
	    else {
	      (*funcs[(int)a->op])(&stack[sp-1],&stack[sp],&stack[sp-1]);
	      sp--;
	    }
	    i++;
	    break;
	}
    }
}



extern int errno;

static jmp_buf fpe_env;

fpe()
{
    (void) signal(SIGFPE, fpe);
    longjmp(fpe_env, TRUE);
}


Value *EvalActionTab(at,val)
ActionTab *at;
Value *val;
{

    errno = 0;
    reset_stack();
    if (setjmp(fpe_env)) {  /* bail out on errors */
	val->type = undef;
	return val;		  
    }

    (void) signal(SIGFPE, fpe); /* catch core dumps on FPEs */

    DoActionTab(at);

    (void) signal(SIGFPE, SIG_DFL);

    (void) pop(val);
    if (errno == EDOM || errno == ERANGE) 
	val->type = undef;

    return val;
}


Value *EvalTree(t,v,prms)
Tree *t;
Value *v;
Value *prms;
{
    Value v1,v2,v3,v4,v5;
    char errbuf[100];
    int i;

    switch (t->op) {
    case Push:
	if (t->v.v.s->value.type == undef) {
	    sprintf(errbuf,"%s is undefined.",t->v.v.s->name);
	    runerr(errbuf);
	}
	*v = t->v.v.s->value;
	break;
    case Pushl:
	*v = prms[t->v.lv.num];
	break;
    case Pushc:
	*v = t->v;
	break;
    case Pushlval:
	v->type = lvalue;
	v->lval.lval = &t->v.v.s->value;
	break;
    case Call:
	EvalTree(t->args[0],&v1,prms);
	if ((v1.type != primfunc) && (v1.type != deffunc))
	    runerr("Not a function; can't call it.");
	if (v1.type == primfunc) {
	    if (t->v.i.i != v1.pf.numprms) {
		sprintf(errbuf,
			"Wrong number of arguments passed; expecting %d.",
			v1.pf.numprms);
		runerr(errbuf);
	    }
	    switch (v1.pf.numprms) {
	    case 0:
		(*v1.pf.func)(v);
		break;
	    case 1:
		(*v1.pf.func)(EvalTree(t->args[1],&v2,prms),v);
		break;
	    case 2:
		(*v1.pf.func)(EvalTree(t->args[1],&v2,prms),
			      EvalTree(t->args[2],&v3,prms),v);
		break;
	    case 3:
		(*v1.pf.func)(EvalTree(t->args[1],&v2,prms),
			      EvalTree(t->args[2],&v3,prms),
			      EvalTree(t->args[3],&v4,prms),v);
		break;
	    case 4:
		(*v1.pf.func)(EvalTree(t->args[1],&v2,prms),
			      EvalTree(t->args[2],&v3,prms),
			      EvalTree(t->args[3],&v4,prms),
			      EvalTree(t->args[4],&v5,prms),v);
		break;
	    default:
		internalerr("Too many args to a primitive function");
		break;
	    }
	}
	else  {  /* a def func */
#define MAXARGS 25	    
	    Value callargs[MAXARGS];
	    int i;

	    /* error checking on number of parameters */
	    if (t->v.i.i != v1.df.numprms) {
		sprintf(errbuf,
			"Wrong number of arguments passed; expecting %d.",
			v1.pf.numprms);
		runerr(errbuf);
	    }
	    if (t->v.i.i > MAXARGS) {
	        sprintf(errbuf,"Too many arguments.  Maximum is %d.",MAXARGS);
		runerr(errbuf);
	    }

	    /* evaluate all the arguments */
	    for (i=0; i < t->v.i.i; i++) 
		EvalTree(t->args[i+1],&callargs[i],prms);

	    /* now evaluate the function, with the arguments */
	    EvalTree(v1.df.body,v,callargs);
	}
	break;
    case Index:
	EvalTree(t->args[0],&v1,prms);
	if (v1.type != array) 
	    runerr("Not an array; can't index.");
	EvalTree(t->args[1],&v2,prms);
	if (v2.type != integer)
	    runerr("Array index must be an integer.");
	if (v2.i.i < 0)
	    runerr("Array index must be >= 0.");
	if (v2.i.i >= v1.a.N)
	    runerr("Array index is too big.");
	*v = v1.a.vals[v2.i.i];
	break;
    case Indexlval:
	EvalTree(t->args[0],&v1,prms);
	if (v1.type != lvalue)
	    internalerr("Non-lvalue in Indexlval.");
	if (v1.lval.lval->type != array) 
	    runerr("Not an array; can't index.");
	EvalTree(t->args[1],&v2,prms);
	if (v2.type != integer)
	    runerr("Array index must be an integer.");
	if (v2.i.i < 0)
	    runerr("Array index must be >= 0.");
	if (v2.i.i >= v1.lval.lval->a.N)
	    runerr("Array index is too big.");
	v->type = lvalue;
	v->lval.lval = &(v1.lval.lval->a.vals[v2.i.i]);
	break;
    case Comma:
	for (i=0; i<t->v.i.i; i++) 
	     EvalTree(t->args[i],v,prms);
	break;
    case Assign:
	EvalTree(t->args[0],&v1,prms);
	if (v1.type != lvalue)
	    internalerr("Non-lvalue in Assign.");
	EvalTree(t->args[1],&v2,prms);
	*v = *v1.lval.lval = v2;
	break;
    default:     /* call a unary or binary operator */
	if (numprms[(int)t->op] == 1) 
	    (*funcs[(int)t->op])(EvalTree(t->args[0],v,prms),v);
	else {
	    (*funcs[(int)t->op])(EvalTree(t->args[0],&v1,prms),
				 EvalTree(t->args[1],&v2,prms),
				 v);
	}
	i++;
	break;
    }

    return v;
}
    
