 /*
  * Khoros: $Id: eval.c,v 1.4 1992/03/25 17:31:04 dkhoros Exp $
  */

#if !defined(lint) && !defined(SABER)
static char rcsid[] = "Khoros: $Id: eval.c,v 1.4 1992/03/25 17:31:04 dkhoros Exp $";
#endif

 /*
  * $Log: eval.c,v $
 * Revision 1.4  1992/03/25  17:31:04  dkhoros
 * VirtualPatch5
 *
  */ 

/*
 *----------------------------------------------------------------------
 *
 * Copyright 1990, University of New Mexico.  All rights reserved.
 * 
 * Permission to copy and modify this software and its documen-
 * tation only for internal use in your organization is hereby
 * granted, provided that this notice is retained thereon and
 * on all copies.  UNM makes no representations as too the sui-
 * tability and operability of this software for any purpose.
 * It is provided "as is" without express or implied warranty.
 * 
 * UNM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FIT-
 * NESS.  IN NO EVENT SHALL UNM BE LIABLE FOR ANY SPECIAL,
 * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY OTHER DAMAGES WHAT-
 * SOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
 * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
 * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PER-
 * FORMANCE OF THIS SOFTWARE.
 * 
 * No other rights, including for example, the right to redis-
 * tribute this software and its documentation or the right to
 * prepare derivative works, are granted unless specifically
 * provided in a separate license agreement.
 *----------------------------------------------------------------------
 */

#include "unmcopyright.h"	 /* Copyright 1990 by UNM */
#include "expr.h"
#include "y.tab.h"


/* >>>>>>>>>>>>>>>>>>>>>>>>>>>>> <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
   >>>>                                                       <<<<
   >>>>	    	    file name: eval.c			      <<<<
   >>>>                                                       <<<<
   >>>>		Various Private Evaluation Routines	      <<<<
   >>>>		For the xvexpr Library			      <<<<
   >>>>                                                       <<<<
   >>>>             _xve_eval_symbol()			      <<<<
   >>>>             _xve_eval_symlist()			      <<<<
   >>>>                                                       <<<<
   >>>>>>>>>>>>>>>>>>>>>>>>>>>>> <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< */



static  SymbolList *eval_function(), *eval_ufunction(), *eval_operator(),
		   *eval_instruction();


/************************************************************
*
*  Routine Name: eval_symlist()
*
*       Purpose: eval_symlist() is called when we want to evaluate
*		 a symbol list.
*
*        Input:  symlist  - the current symbol list to be evaluated
*		 abort	  - whether to abort when an undefined variable
*			    is found.
*
*       Output:  returns the result stored in the "result" symbol
*
*   Written By:  Mark Young
*
*
************************************************************/


static SymbolList *eval_symlist(symlist, result, abort)

SymbolList *symlist;
Symbol	   *result;
int	   abort;
{
	Symbol	*symbol;
	char	error[LENGTH];


	/*
	 *  If the current symlist is NULL then the endlist will become
	 *  the beginning of the symbol list, so we just return it.
	 */
	if (symlist == NULL)
	{
	   sprintf(error,"Error! end of symbol list encountered, but more \
expected.");
	   vexpr_error(error);
	}

	symbol = symlist->symbol;
	switch (symbol->type)
	{
	   case NUMBER:
	   case CONSTANT:
	   case EXPRESSION:
		if (result != NULL)
		{
		   result->type  = NUMBER;
		   result->Value = symbol->Value;
		}
		symlist = symlist->next;
		break;

	   case STRING:
		if (result != NULL)
		{
		   result->type  = STRING;
		   result->String = VStrcpy(symbol->String);
		}
		symlist = symlist->next;
		break;

	   case VARIABLE:
		if (symbol->eval == FALSE)
		{
		   symbol->eval = TRUE;
		   (void) eval_symlist(symbol->symlist, result, abort);
		   symbol->eval = FALSE;
		}
		else if (result != NULL)
		{
		   result->type  = NUMBER;
		   result->Value = symbol->Value;
		}
		symlist = symlist->next;
		break;

	   case FUNCTION:
		symlist = symlist->next;
		symlist = eval_function(symbol, symlist, abort, result);
		break;

	   case UFUNCTION:
		symlist = symlist->next;
		symlist = eval_ufunction(symbol, symlist, abort, result);
		break;

	   case UOPERATOR:
	   case OPERATOR:
		symlist = symlist->next;
		symlist = eval_operator(symbol, symlist, abort, result);
		break;

	   case INSTRUCTION:
		symlist = symlist->next;
		symlist = eval_instruction(symbol, symlist, abort, result);
	        break;

	   case UNDEFINED:
		if (abort)
		{
		   sprintf(error,"Error! Unknown variable or constant '%s'",
				symbol->name);
		   vexpr_error(error);
		}
		symlist = symlist->next;

		if (result != NULL)
		{
		   result->type  = UNDEFINED;
		   result->Value = 0.0;
		}
		break;

	   default:
		(void) sprintf(error,"Error!  Unknown symbol type '%d'",
				symbol->type);
		vexpr_error(error);
		break;
	}
	return(symlist);
}



/************************************************************
*
*  Routine Name: eval_function()
*
*       Purpose: eval_function() is called when we want to evaluate
*		 a function
*
*        Input:  symbol   - the function symbol
*		 symlist  - the argument symbols for the function
*		 abort	  - whether to abort when an undefined variable
*			    is found.
*
*       Output:  returns the result stored in the "result" symbol,
*		 as well as the resulting symlist.
*
*   Written By:  Mark Young
*
*
************************************************************/


static SymbolList *eval_function(symbol, symlist, abort, result)

int	   abort;
Symbol	   *symbol, *result;
SymbolList *symlist;
{
	Symbol   arg;
	double   vals[20];
	int	 i, num_args;
	register double	value;
	double   (*function)();


	/*
	 *  Evaluate the different argument expressions and then call the
	 *  function with the different evaluated arguments.
	 */
	num_args = symbol->num;
	if (result == NULL)
	{
	   for (i = 0; i < num_args; i++)
	      symlist = eval_symlist(symlist, NULL, abort);

	   return(symlist);
	}

	function = symbol->Function;
	for (i = 0; i < num_args; i++)
	{
	   symlist = eval_symlist(symlist, &arg, abort);
	   vals[i] = arg.Value;
	}

	switch (num_args)
	{
	   case 0:
		value = function();
		break;

	   case 1:
		value = function(vals[0]);
		break;

	   case 2:
		value = function(vals[0], vals[1]);
		break;

	   case 3:
		value = function(vals[0], vals[1], vals[2]);
		break;

	   case 4:
		value = function(vals[0], vals[1], vals[2], vals[3]);
		break;

	   case 5:
		value = function(vals[0], vals[1], vals[2], vals[3], vals[4]);
		break;

	   case 6:
		value = function(vals[0], vals[1], vals[2], vals[3], vals[4],
				vals[5]);
		break;

	   case 7:
		value = function(vals[0], vals[1], vals[2], vals[3], vals[4],
				vals[5], vals[6]);
		break;

	   case 8:
		value = function(vals[0], vals[1], vals[2], vals[3], vals[4],
				vals[5], vals[6], vals[7]);
		break;

	   case 9:
		value = function(vals[0], vals[1], vals[2], vals[3], vals[4],
				vals[5], vals[6], vals[7], vals[8]);
		break;
	}
	result->type  = NUMBER;
	result->Value = value;
	return(symlist);
}



/************************************************************
*
*  Routine Name: eval_ufunction()
*
*       Purpose: eval_ufunction() is called when we want to evaluate
*		 a user defined function
*
*        Input:  symbol   - the function symbol
*		 symlist  - the argument symbols for the function
*		 abort	  - whether to abort when an undefined variable
*			    is found.
*
*       Output:  returns the result stored in the "result" symbol,
*		 as well as the resulting symlist.
*
*   Written By:  Mark Young
*
*
************************************************************/


static SymbolList *eval_ufunction(symbol, symlist, abort, result)

int	   abort;
Symbol	   *symbol, *result;
SymbolList *symlist;
{
	double   values[20];
	SymbolList *arglist;
	int	 i, num_args;
	Symbol   *arg, arg1;


	/*
	 *  Evaluate the different argument expressions and then call the
	 *  eval_symlist() with the different evaluated arguments, but we
	 *  first need to save the argument values so that if we act just
	 *  like a stack would.
	 */
	num_args = symbol->num;
	if (result == NULL)
	{
	   for (i = 0; i < num_args; i++)
	      symlist = eval_symlist(symlist, &arg1, abort);

	   return(symlist);
	}

	arglist  = symbol->symlist;
	for (i = 0; i < num_args; i++)
	{
	   symlist = eval_symlist(symlist, &arg1, abort);

	   arg = arglist->symbol;
	   values[i] = arg->Value;
	   arg->Value = arg1.Value;
	   arglist = arglist->next;
	}
	(void) eval_symlist(arglist, result, abort);

	arglist = symbol->symlist;
	for (i = 0; i < num_args; i++)
	{
	   arg = arglist->symbol;
	   arg->Value = values[i];
	   arglist = arglist->next;
	}
	return(symlist);
}



/************************************************************
*
*  Routine Name: eval_operator()
*
*       Purpose: eval_operator() is called when we want to evaluate
*		 an operation.
*
*        Input:  symbol   - the operator symbol
*		 symlist  - the argument symbols for the operator
*		 abort	  - whether to abort when an undefined variable
*			    is found.
*
*       Output:  returns the result stored in the "result" symbol,
*		 as well as the resulting symlist.
*
*
*   Written By:  Mark Young
*
*
************************************************************/

#ifdef CRAY
#define vpow(x,y) ((val1) < 0.0 && (val2) == ((int) (val2)))            ? \
        (((((int) (val2)) % 2 == 0) ? 1 : -1) * pow(fabs(val1),val2))   : \
        pow(val1, val2)
#else
#define vpow pow
#endif


static SymbolList *eval_operator(symbol, symlist, abort, result)

int	   abort;
Symbol	   *symbol, *result;
SymbolList *symlist;
{
	Symbol	 arg1, arg2;
	register double	value, val1, val2;
	char	 str1[LENGTH], str2[LENGTH];


	if (result == NULL)
	{
	   symlist = eval_symlist(symlist, NULL, abort);
	   if (symbol->type == OPERATOR)
	      symlist = eval_symlist(symlist, NULL, abort);

	   return(symlist);
	}

	symlist = eval_symlist(symlist, &arg1, abort);
	val1 = arg1.Value;

	if (symbol->type == OPERATOR)
	{
	   symlist = eval_symlist(symlist, &arg2, abort);
	   val2 = arg2.Value;
	}

	switch (symbol->Operator)
	{
	   case AND:
		value = ((val1 && val2) ? TRUE : FALSE);
		break;

	   case OR:
		value = ((val1 || val2) ? TRUE : FALSE);
		break;

	   case EQ:
		value = ((val1 == val2) ? TRUE : FALSE);
		break;

	   case NE:
		value = ((val1 != val2) ? TRUE : FALSE);
		break;

	   case LE:
		value = ((val1 <= val2) ? TRUE : FALSE);
		break;

	   case GE:
		value = ((val1 >= val2) ? TRUE : FALSE);
		break;

	   case LT:
		value = ((val1 < val2) ? TRUE : FALSE);
		break;

	   case GT:
		value = ((val1 > val2) ? TRUE : FALSE);
		break;

	   case '+':
		value = val1+val2;
		break;

	   case '-':
		value = val1 - val2;
		break;

	   case '*':
		value = val1*val2;
		break;

	   case '/':
		if (abort == TRUE)
		   value = (val2 != 0.0) ? val1/val2 : vexpr_error("Floating \
divide by zero");
		else
		   value = (val2 != 0.0) ? val1/val2 : val1;
		break;

	   case POW:
		value = vpow(val1, val2);
		break;

	   case '%':
		value = ((((int) val2) != 0) ? ((int) val1) % ((int) val2) : 0);
		break;

	   case '&':
		value = ((int) val1) & ((int) val2);
		break;

	   case '|':
		value = ((int) val1) | ((int) val2);
		break;

	   case '^':
		value = ((int) val1) ^ ((int) val2);
		break;

	   case SL:
		value = ((int) val1) << ((int) val2);
		break;

	   case SR:
		value = ((int) val1) >> ((int) val2);
		break;

	   case '~':
		value = ~((int) val1);
		break;

	   case '!':
		value = !val1;
		break;

	   case UMINUS:
		value = -val1;
		break;

	   case CINT:
		value = (int) val1;
		break;

	   case CFLOAT:
		value = (float) val1;
		break;

	   case CSTRING:
		if (arg1.type == NUMBER)
		   (void) sprintf(str1,"%g", arg1.Value);
		else if (arg1.type == STRING)
		   (void) sprintf(str1,"%s", arg1.String);
		else
		   (void) sprintf(str1,"");

		result->type   = STRING;
		result->String = VStrcpy(str1);
		return(symlist);
		break;

	   case STRCAT:
		if (arg1.type == NUMBER)
		   (void) sprintf(str1,"%g", arg1.Value);
		else if (arg1.type == STRING)
		   (void) sprintf(str1,"%s", arg1.String);
		else
		   (void) strcpy(str1,"");

		if (arg2.type == NUMBER)
		   (void) sprintf(str2,"%g", arg2.Value);
		else if (arg2.type == STRING)
		   (void) sprintf(str2,"%s", arg2.String);
		else
		   (void) strcpy(str2,"");

		result->type   = STRING;
		result->String = VStrcat(str1, str2);
		return(symlist);
		break;

	   default:
		break;
	}
	result->type  = NUMBER;
	result->Value = value;
	return(symlist);
}



/************************************************************
*
*  Routine Name: eval_instruction()
*
*       Purpose: eval_instruction() is called when we want to evaluate
*		 an instruction.
*
*        Input:  symbol   - the operator symbol
*		 symlist  - the argument symbols for the operator
*		 abort	  - whether to abort when an undefined variable
*			    is found.
*
*       Output:  returns the result stored in the "result" symbol,
*		 as well as the resulting symlist.
*
*   Written By:  Mark Young
*
*
************************************************************/


static SymbolList *eval_instruction(symbol, symlist, abort, result)

int	   abort;
Symbol	   *symbol, *result;
SymbolList *symlist;
{
	Symbol	arg;


	if (result == NULL)
	{
	   if (symbol->Instruction == IF)
	   {
	      symlist = eval_symlist(symlist, NULL, abort);
	      symlist = eval_symlist(symlist, NULL, abort);
	      symlist = eval_symlist(symlist, NULL, abort);
	   }
	   return(symlist);
	}

	switch (symbol->Instruction)
	{
	    case IF:
		 symlist = eval_symlist(symlist, &arg, abort);
		 if (arg.Value)
		 {
		    symlist = eval_symlist(symlist, &arg, abort);
		    symlist = eval_symlist(symlist, NULL, abort);
	         }
		 else
		 {
		    symlist = eval_symlist(symlist, NULL, abort);
		    symlist = eval_symlist(symlist, &arg, abort);
		 }
		 break;
	}

	result->type = arg.type;
	if (arg.type == NUMBER)
	   result->Value = arg.Value;
	else
	   result->String = VStrcpy(arg.String);
	return(symlist);
}



/************************************************************
*
*  Routine Name: _xve_eval_symbol()
*
*       Purpose: _xve_eval_symbol() is called when we want to eval-
*		 uate a symbol.
*
*        Input:  symbol   - the current symbol to be evaluated
*		 abort	  - whether to abort when an undefined variable
*			    is found
*
*       Output:  returns the result stored in the "result" symbol
*
*   Written By:  Mark Young
*
*
************************************************************/


_xve_eval_symbol(symbol, result, abort)

Symbol *symbol;
Symbol *result;
int    abort;
{
	int	   i;
	Symbol	   temp_symbol;
	SymbolList list, *symlist;
	char       error[LENGTH];


	/*
	 *  If the current symbol is NULL then print out an error
	 *  and abort.
	 */
	if (symbol == NULL)
	{
	   sprintf(error,"Error! NULL symbol list encountered.\n");
	   vexpr_error(error);
	}
	else if (result == NULL)
	   result = &temp_symbol;

 	if (symbol->type == VARIABLE)
	{
	   symbol->eval = TRUE;
	   eval_symlist(symbol->symlist, result, abort);
	   symbol->Value = result->Value;
	   symbol->eval = FALSE;
	}
	else if (symbol->type == EXPRESSION)
	{
	   eval_symlist(symbol->symlist, result, abort);
	   symbol->Value = result->Value;
	}
	else if (symbol->type == UFUNCTION)
	{
	   symlist = symbol->symlist;
	   for (i = 0; i < symbol->num; i++)
	      symlist = symlist->next;

	   (void) eval_symlist(symlist, result, abort);
	   symbol->Value = result->Value;
	}
	else
	{
	   list.symbol = symbol;
	   list.next   = NULL;
	   (void) eval_symlist(&list, result, abort);
	}
}



/************************************************************
*
*  Routine Name: _xve_eval_symlist()
*
*       Purpose: _xve_eval_symlist() is called when we want to evaluate
*		 a symbol list.
*
*        Input:  symlist  - the current symbol list to be evaluated
*		 abort	  - whether to abort when an undefined variable
*			    is found.
*
*       Output:  returns the result stored in the "result" symbol
*
*   Written By:  Mark Young
*
*
************************************************************/


_xve_eval_symlist(symlist, result, abort)

SymbolList *symlist;
Symbol	   *result;
int	   abort;
{
	char	   error[LENGTH];


	/*
	 *  If the current symlist is NULL then the endlist will become
	 *  the beginning of the symbol list, so we just return it.
	 */
	if (symlist == NULL)
	{
	   sprintf(error,"Error! NULL symbol list encountered.\n");
	   vexpr_error(error);
	}

	if (result == NULL)
	{
	   sprintf(error,"Error! NULL result symbol encountered.\n");
	   vexpr_error(error);
	}
	(void) eval_symlist(symlist, result, abort);
}
