 /*
  * Khoros: $Id: expr.y,v 1.4 1992/03/20 23:42:10 dkhoros Exp $
  */

 /*
  * $Log: expr.y,v $
 * Revision 1.4  1992/03/20  23:42:10  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.
 *----------------------------------------------------------------------
 */


/**************************************************************
*
*   Written by:   Mark Young
*
**************************************************************/

%{
#include "unmcopyright.h"	 /* Copyright 1990 by UNM */
#include "expr.h"

static int	  num_args;
static SymbolList *symlist;
static void	  check_variable(), check_function();
static SymbolList *add_number(), *add_operator(), *add_instruction(),
		  *add_variable(), *add_string(), *add_symlist(),
		  *add_function(), *copy_symlist();
%}

%union {
	double     value;
	char       *string;
	Symbol     *symbol;
	SymbolList *symlist;
}

%token  <string>   STRING PRINTF
%token  <value>    NUMBER OPERATOR UOPERATOR INSTRUCTION
%token  <symbol>   VARIABLE EXPRESSION FUNCTION UFUNCTION EVALUATE
		   CONSTANT UNDEFINED

%type   <symlist>  expr sexpr arglist str_or_expr var_or_const
%type	<symbol>   assign

%right 	';' '?' ':' '=' AADD ASUB AMUL ADIV AOR AAND AXOR ASL ASR STRCAT
	IF THEN ELSE
%left	OR
%left	AND
%left	NE LT LE GT GE EQ INCREMENT DECREMENT
%left 	'+' '-'
%left	'*' '/'
%left	'|' '&' '%' '^' SL SR
%left	UMINUS CINT CFLOAT CSTRING '~' '!'
%right 	POW
%%

list:   list_types
	| list ';'
	| list ';' list
	| list ';' error
	  {
	     yyerrok;
	  }
	;

list_types:
	| sexpr
	  {
	     _xve_eval_symlist($1, &FinalValue, TRUE);
	     _xve_free_symlist($1);
	  }
	| expr
	  {
	     _xve_eval_symlist($1, &FinalValue, TRUE);
	     _xve_free_symlist($1);
	  }
	| assign
	  {
	     if ($1->type != UFUNCTION) check_variable($1);
	     _xve_eval_symbol($1, &FinalValue, FALSE);
	  }
	;

assign:	  VARIABLE '=' expr
	  {
	     _xve_free_symbol($1);
	     $$->symlist = $1->symlist = $3;
	  }
	| VARIABLE '=' sexpr
	  {
	     _xve_free_symbol($1);
	     $$->symlist = $1->symlist = $3;
	  }
	| VARIABLE ':' '=' expr
	  {
	     _xve_eval_symlist($4, &FinalValue, TRUE);
	     _xve_free_symlist($4);
	     $$->Value = $1->Value = FinalValue.Value;
	  }
	| VARIABLE ':' expr '=' expr
	  {
	     _xve_eval_symlist($3, &FinalValue, TRUE);
	     _xve_free_symlist($3);
	     $$->Value = $1->Value = FinalValue.Value;
	     _xve_free_symbol($1);
	     $$->symlist = $1->symlist = $5;
	  }
	| VARIABLE '=' assign
	  {
	     _xve_free_symbol($1);
	     $$->symlist = $1->symlist = copy_symlist($3->symlist);
	     check_variable($3);
	     _xve_eval_symbol($3, &FinalValue, FALSE);
	  }
	| FUNCTION '(' arglist ')' '=' expr
	  {
	     char error[LENGTH];

	     /*
	      *  Don't allow the user to redefine a predefined function,
	      *  such as cos().  Only allow them to change or add user
	      *  defined functions.
	      */
	     if ($1->type == FUNCTION)
	     {
		sprintf(error,"Error!  '%s' is pre-defined function not a user \
defined one.\nYou are only allowed to define or change user defined functions.",
			$1->name);
		yyerror(error);
	     }

	     /*
	      *  Change the type to be a user defined function.  Also, since
	      *  this is a user defined function we need to change the function
	      *  argument variables.
	      *
	      *  (see check_function() for more details).
	      */
	     _xve_free_symbol($1);
	     $1->type = UFUNCTION;
	     check_function($1, $3, $6);
	     $$->symlist = $1->symlist = add_symlist($3, $6);
	  }
	| VARIABLE AADD expr
	  {
	     if ($1->type == UNDEFINED)
	     {
		_xve_delete_symbol(CURRENT_ID, $1);
	        yyerror("Undefined variable");
	     }
	     symlist = add_operator(NULL, '+', "+", OPERATOR);
	     symlist = add_symlist(symlist, $1->symlist);
	     symlist = add_symlist(symlist, $3);
	     $$->symlist = $1->symlist = symlist;
	  }
	| VARIABLE ASUB expr
	  {
	     if ($1->type == UNDEFINED)
	     {
		_xve_delete_symbol(CURRENT_ID, $1);
	        yyerror("Undefined variable");
	     }
	     symlist = add_operator(NULL, '-', "-", OPERATOR);
	     symlist = add_symlist(symlist, $1->symlist);
	     symlist = add_symlist(symlist, $3);
	     $$->symlist = $1->symlist = symlist;
	  }
	| VARIABLE INCREMENT
	  {
	     if ($1->type == UNDEFINED)
	     {
		_xve_delete_symbol(CURRENT_ID, $1);
	        yyerror("Undefined variable");
	     }
	     symlist = add_operator(NULL, '+', "+", OPERATOR);
	     symlist = add_symlist(symlist, $1->symlist);
	     symlist = add_number(symlist, 1.0);
	     $$->symlist = $1->symlist = symlist;
	  }
	| VARIABLE DECREMENT
	  {
	     if ($1->type == UNDEFINED)
	     {
		_xve_delete_symbol(CURRENT_ID, $1);
	        yyerror("Undefined variable");
	     }
	     symlist = add_operator(NULL, '-', "-", OPERATOR);
	     symlist = add_symlist(symlist, $1->symlist);
	     symlist = add_number(symlist, 1.0);
	     $$->symlist = $1->symlist = symlist;
	  }
	| VARIABLE AMUL expr
	  {
	     if ($1->type == UNDEFINED)
	     {
		_xve_delete_symbol(CURRENT_ID, $1);
	        yyerror("Undefined variable");
	     }
	     symlist = add_operator(NULL, '*', "*", OPERATOR);
	     symlist = add_symlist(symlist, $1->symlist);
	     symlist = add_symlist(symlist, $3);
	     $$->symlist = $1->symlist = symlist;
	  }
	| VARIABLE ADIV expr
	  {
	     if ($1->type == UNDEFINED)
	     {
		_xve_delete_symbol(CURRENT_ID, $1);
	        yyerror("Undefined variable");
	     }
	     symlist = add_operator(NULL, '/', "/", OPERATOR);
	     symlist = add_symlist(symlist, $1->symlist);
	     symlist = add_symlist(symlist, $3);
	     $$->symlist = $1->symlist = symlist;
	  }
	| VARIABLE AOR expr
	  {
	     if ($1->type == UNDEFINED)
	     {
		_xve_delete_symbol(CURRENT_ID, $1);
	        yyerror("Undefined variable");
	     }
	     symlist = add_operator(NULL, OR, "||", OPERATOR);
	     symlist = add_symlist(symlist, $1->symlist);
	     symlist = add_symlist(symlist, $3);
	     $$->symlist = $1->symlist = symlist;
	  }
	| VARIABLE AAND expr
	  {
	     if ($1->type == UNDEFINED)
	     {
		_xve_delete_symbol(CURRENT_ID, $1);
	        yyerror("Undefined variable");
	     }
	     symlist = add_operator(NULL, AND, "&&", OPERATOR);
	     symlist = add_symlist(symlist, $1->symlist);
	     symlist = add_symlist(symlist, $3);
	     $$->symlist = $1->symlist = symlist;
	  }
	| VARIABLE AXOR expr
	  {
	     if ($1->type == UNDEFINED)
	     {
		_xve_delete_symbol(CURRENT_ID, $1);
	        yyerror("Undefined variable");
	     }
	     symlist = add_operator(NULL, '-', "-", OPERATOR);
	     symlist = add_symlist(symlist, $1->symlist);
	     symlist = add_symlist(symlist, $3);
	     $$->symlist = $1->symlist = symlist;
	  }
	| VARIABLE ASL expr
	  {
	     if ($1->type == UNDEFINED)
	     {
		_xve_delete_symbol(CURRENT_ID, $1);
	        yyerror("Undefined variable");
	     }
	     symlist = add_operator(NULL, SL, "<<", OPERATOR);
	     symlist = add_symlist(symlist, $1->symlist);
	     symlist = add_symlist(symlist, $3);
	     $$->symlist = $1->symlist = symlist;
	  }
	| VARIABLE ASR expr
	  {
	     if ($1->type == UNDEFINED)
	     {
		_xve_delete_symbol(CURRENT_ID, $1);
	        yyerror("Undefined variable");
	     }
	     symlist = add_operator(NULL, SR, ">>", OPERATOR);
	     symlist = add_symlist(symlist, $1->symlist);
	     symlist = add_symlist(symlist, $3);
	     $$->symlist = $1->symlist = symlist;
	  }
	;

str_or_expr:	STRING
          {
             $$ = add_string(NULL, $1);
          }
        | EXPRESSION
          {
             $$ = add_variable(NULL, $1);
          }
	| PRINTF '(' STRING ',' arglist ')'
	  {
	     $$ = add_operator(NULL, PRINTF, "printf", OPERATOR);
             $$ = add_string($$, $3);
	     $$ = add_symlist($$, $5);
	  }
	;

sexpr:  str_or_expr  
	| sexpr str_or_expr
	  {
	     $$ = add_operator(NULL, STRCAT, "", OPERATOR);
	     $$ = add_symlist($$, $1);
	     $$ = add_symlist($$, $2);
	  }
	;

var_or_const: VARIABLE
	  {
	     $$ = add_variable(NULL, $1);
	  }
	| CONSTANT
	  {
	     if ($1->type == UNDEFINED)
	     {
		_xve_delete_symbol(CURRENT_ID, $1);
		yyerror("Undefined constant");
	     }
	     $$ = add_variable(NULL, $1);
	  }
	;
	  
expr:     var_or_const
	| NUMBER
	  {
	     $$ = add_number(NULL, $1);
	  }
	| NUMBER var_or_const %prec '*'
	  {
	     $$ = add_operator(NULL, '*', "*", OPERATOR);
	     $$ = add_number($$, $1);
	     $$ = add_symlist($$, $2);
	  }
	| IF '(' expr ')' expr ELSE expr
	  {
	     $$ = add_instruction(NULL, IF, "if", 3);
	     $$ = add_symlist($$, $3);
	     $$ = add_symlist($$, $5);
	     $$ = add_symlist($$, $7);
	  }
	| expr '?' expr ':' expr
	  {
	     $$ = add_instruction(NULL, IF, "if", 3);
	     $$ = add_symlist($$, $1);
	     $$ = add_symlist($$, $3);
	     $$ = add_symlist($$, $5);
	  }
	| EVALUATE '(' expr ')'
	  {
	     Symbol temp;

	     _xve_eval_symlist($3, &temp, TRUE);
	     if (temp.type == NUMBER)
	        $$ = add_number(NULL, temp.Value);
	     else
		yyerror("Can only evaluate numerical expressions");
	  }
	| FUNCTION '(' arglist ')'
	  {
	     $$ = add_function(NULL, $1, num_args);
	     $$ = add_symlist($$, $3);
	  }
	| expr AND expr
	  {
	     $$ = add_operator(NULL, AND, "&&", OPERATOR);
	     $$ = add_symlist($$, $1);
	     $$ = add_symlist($$, $3);
	  }
	| expr OR  expr
	  {
	     $$ = add_operator(NULL, OR, "||", OPERATOR);
	     $$ = add_symlist($$, $1);
	     $$ = add_symlist($$, $3);
	  }
	| expr EQ  expr
	  {
	     $$ = add_operator(NULL, EQ, "==", OPERATOR);
	     $$ = add_symlist($$, $1);
	     $$ = add_symlist($$, $3);
	  }
	| expr NE  expr
	  {
	     $$ = add_operator(NULL, NE, "!=", OPERATOR);
	     $$ = add_symlist($$, $1);
	     $$ = add_symlist($$, $3);
	  }
	| expr LE  expr
	  {
	     $$ = add_operator(NULL, LE, "<=", OPERATOR);
	     $$ = add_symlist($$, $1);
	     $$ = add_symlist($$, $3);
	  }
	| expr GE  expr
	  {
	     $$ = add_operator(NULL, GE, ">=", OPERATOR);
	     $$ = add_symlist($$, $1);
	     $$ = add_symlist($$, $3);
	  }
	| expr LT  expr
	  {
	     $$ = add_operator(NULL, LT, "<", OPERATOR);
	     $$ = add_symlist($$, $1);
	     $$ = add_symlist($$, $3);
	  }
	| expr GT  expr
	  {
	     $$ = add_operator(NULL, GT, ">", OPERATOR);
	     $$ = add_symlist($$, $1);
	     $$ = add_symlist($$, $3);
	  }
	| expr '+' expr
	  {
	     $$ = add_operator(NULL, '+', "+", OPERATOR);
	     $$ = add_symlist($$, $1);
	     $$ = add_symlist($$, $3);
	  }
	| expr '-' expr
	  {
	     $$ = add_operator(NULL, '-', "-", OPERATOR);
	     $$ = add_symlist($$, $1);
	     $$ = add_symlist($$, $3);
	  }
	| expr '*' expr
	  {
	     $$ = add_operator(NULL, '*', "*", OPERATOR);
	     $$ = add_symlist($$, $1);
	     $$ = add_symlist($$, $3);
	  }
	| expr '/' expr
	  {
	     $$ = add_operator(NULL, '/', "/", OPERATOR);
	     $$ = add_symlist($$, $1);
	     $$ = add_symlist($$, $3);
	  }
	| expr POW expr
	  {
	     $$ = add_operator(NULL, POW, "**", OPERATOR);
	     $$ = add_symlist($$, $1);
	     $$ = add_symlist($$, $3);
	  }
	| expr '%' expr
	  {
	     $$ = add_operator(NULL, '%', "%", OPERATOR);
	     $$ = add_symlist($$, $1);
	     $$ = add_symlist($$, $3);
	  }
	| expr '&' expr
	  {
	     $$ = add_operator(NULL, '&', "&", OPERATOR);
	     $$ = add_symlist($$, $1);
	     $$ = add_symlist($$, $3);
	  }
	| expr '|' expr
	  {
	     $$ = add_operator(NULL, '|', "|", OPERATOR);
	     $$ = add_symlist($$, $1);
	     $$ = add_symlist($$, $3);
	  }
	| expr '^' expr
	  {
	     $$ = add_operator(NULL, '^', "^", OPERATOR);
	     $$ = add_symlist($$, $1);
	     $$ = add_symlist($$, $3);
	  }
	| expr SL expr
	  {
	     $$ = add_operator(NULL, SL, "<<", OPERATOR);
	     $$ = add_symlist($$, $1);
	     $$ = add_symlist($$, $3);
	  }
	| expr SR expr
	  {
	     $$ = add_operator(NULL, SR, ">>", OPERATOR);
	     $$ = add_symlist($$, $1);
	     $$ = add_symlist($$, $3);
	  }
	| '~' expr
	  {
	     $$ = add_operator(NULL, '~', "~", UOPERATOR);
	     $$ = add_symlist($$, $2);
	  }
	| CINT expr
	  {
	     $$ = add_operator(NULL, CINT, "(int)", UOPERATOR);
	     $$ = add_symlist($$, $2);
	  }
	| CFLOAT expr
	  {
	     $$ = add_operator(NULL, CFLOAT, "(float)", UOPERATOR);
	     $$ = add_symlist($$, $2);
	  }
	| CSTRING expr
	  {
	     $$ = add_operator(NULL, CSTRING, "(string)", UOPERATOR);
	     $$ = add_symlist($$, $2);
	  }
	| '!' expr
	  {
	     $$ = add_operator(NULL, '!', "!", UOPERATOR);
	     $$ = add_symlist($$, $2);
	  }
	| '(' expr ')'
	  {
	     $$ = $2;
	  }
	| '-' expr %prec UMINUS
	  {
	     $$ = add_operator(NULL, UMINUS, "-", UOPERATOR);
	     $$ = add_symlist($$, $2);
	  }
	;

arglist:  {
	     num_args = 0;
	     $$ = NULL;
	  }

	| expr
	  {
	     num_args = 1;
	     $$ = add_symlist(NULL, $1);
	  }

	| expr ',' arglist
	  {
	     num_args += 1;
	     $$ = add_symlist(NULL, $1);
	     $$ = add_symlist($$, $3);
	  }
	;

%%



/************************************************************
*
*  Routine Name: add_number()
*
*       Purpose: add_number() is called when we want to add a
*		 number to the symbol list.  The current symbol
*		 list is passed in and the number is then tacked
*		 onto the end of the symbol list.  If the
*		 symbol list is currently NULL then we will
*		 create a new list and add the number to it.
*
*        Input:  symlist  - the current symbol list
*		 number   - the number to be added to the list
*
*       Output:  returns the new symbol list.
*
*   Written By:  Mark Young
*
*
************************************************************/

static SymbolList *add_number(symlist, number)

SymbolList *symlist;
double	   number;
{
	Symbol	   *symbol;
	SymbolList *list, *entry;
	char	   error[LENGTH];


	/*
	 *  Allocate the entry for our new number
	 */
	if ((entry = (SymbolList *) malloc(sizeof(SymbolList))) == NULL)
	{
	   (void) sprintf(error, "Error! Ran out of memory trying to allocate \
reference list for number '%g'", number);
	   yyerror(error);
	}

	/*
	 *  Allocate the symbol to store our number
	 */
	if ((symbol = (Symbol *) calloc(1, sizeof(Symbol))) == NULL)
	{
	   (void) sprintf(error, "Error! Ran out of memory trying to allocate \
symbol for number '%g'", number); free(entry);
	   yyerror(error);
	}
	symbol->type  = NUMBER;
	symbol->Value = number;
	entry->symbol = symbol;
	entry->next   = NULL;

	/*
	 *  If the current symlist is NULL then return entry as our symlist.
	 */
	if (symlist == NULL)
	{
	   return(entry);
	}

	/*
	 *  The current symbol list is not empty so add the entry to the
	 *  end of the current list.
	 */
	list = symlist;
	while (list->next != NULL)
	   list = list->next;

	list->next = entry;
	return(symlist);
}



/************************************************************
*
*  Routine Name: add_operator()
*
*       Purpose: add_operator() is called when we want to add a
*		 operator to the symbol list.  The current symbol
*		 list is passed in and the operator is then tacked
*		 onto the end of the symbol list.  If the
*		 symbol list is currently NULL then we will
*		 create a new list and add the operator to it.
*
*        Input:  symlist  - the current symbol list
*		 operator - the operator to be added to the list
*		 name     - the string that represents that operator
*		 type     - the type of operator, can be OPERATOR or
*			    OPERATOR.  The operator type takes two
*			    arguments, but the unary operator takes one
*			    argument.
*
*       Output:  returns the new symbol list.
*
*   Written By:  Mark Young
*
*
************************************************************/

static SymbolList *add_operator(symlist, operator, name, type)

SymbolList *symlist;
int	   operator, type;
char	   *name;
{
	Symbol	   *symbol;
	SymbolList *list, *entry;
	char	   error[LENGTH];


	/*
	 *  Allocate the entry for our new operator
	 */
	if ((entry = (SymbolList *) malloc(sizeof(SymbolList))) == NULL)
	{
	   (void) sprintf(error, "Error! Ran out of memory trying to allocate \
reference list for operator '%c' ", operator);
	   yyerror(error);
	}

	/*
	 *  Allocate the symbol to store our operator
	 */
	if ((symbol = (Symbol *) calloc(1,sizeof(Symbol))) == NULL)
	{
	   (void) sprintf(error, "Error! Ran out of memory trying to allocate \
symbol for operator '%c' ", operator);
	   yyerror(error);
	}
	symbol->type     = type;
	symbol->Operator = operator;
	symbol->name	 = VStrcpy(name);
	entry->symbol    = symbol;
	entry->next	 = NULL;

	/*
	 *  If the current symlist is NULL then return entry as our symlist.
	 */
	if (symlist == NULL)
	{
	   return(entry);
	}

	/*
	 *  The current symbol list is not empty so add the entry to the
	 *  end of the current list.
	 */
	list = symlist;
	while (list->next != NULL)
	   list = list->next;

	list->next = entry;
	return(symlist);
}



/************************************************************
*
*  Routine Name: add_instruction()
*
*       Purpose: add_instruction() is called when we want to add a
*		 instruction to the symbol list.  The current symbol
*		 list is passed in and the instruction is then tacked
*		 onto the end of the symbol list.  If the
*		 symbol list is currently NULL then we will
*		 create a new list and add the instruction to it.
*
*        Input:  symlist     - the current symbol list
*		 instruction - the operator to be added to the list
*		 name     - the string that represents that instruction
*		 num      - the number of expression used by the
*			    instruction.
*
*       Output:  returns the new symbol list.
*
*   Written By:  Mark Young
*
*
************************************************************/

static SymbolList *add_instruction(symlist, instruction, name, num)

SymbolList *symlist;
int	   instruction, num;
char	   *name;
{
	Symbol	   *symbol;
	SymbolList *list, *entry;
	char	   error[LENGTH];


	/*
	 *  Allocate the entry for our new operator
	 */
	if ((entry = (SymbolList *) malloc(sizeof(SymbolList))) == NULL)
	{
	   (void) sprintf(error, "Error! Ran out of memory trying to allocate \
reference list for instruction '%s' ", name);
	   yyerror(error);
	}

	/*
	 *  Allocate the symbol to store our operator
	 */
	if ((symbol = (Symbol *) calloc(1,sizeof(Symbol))) == NULL)
	{
	   (void) sprintf(error, "Error! Ran out of memory trying to allocate \
symbol for instruction '%s' ", name);
	   yyerror(error);
	}
	symbol->num	    = num;
	symbol->type	    = INSTRUCTION;
	symbol->Instruction = instruction;
	symbol->name	    = VStrcpy(name);
	entry->symbol       = symbol;
	entry->next	    = NULL;

	/*
	 *  If the current symlist is NULL then return entry as our symlist.
	 */
	if (symlist == NULL)
	{
	   return(entry);
	}

	/*
	 *  The current symbol list is not empty so add the entry to the
	 *  end of the current list.
	 */
	list = symlist;
	while (list->next != NULL)
	   list = list->next;

	list->next = entry;
	return(symlist);
}



/************************************************************
*
*  Routine Name: add_variable()
*
*       Purpose: add_variable() is called when we want to add a
*		 variable, or constant to the symbol list.
*		 The variable symbol does not need to be copied, since
*		 we want to share variables within the system.  The
*		 current symbol list is passed in and the number is
*		 then tacked onto the end of the symbol list.  If the
*		 symbol list is currently NULL then we will
*		 create a new list and add the variable to it.
*
*        Input:  symlist  - the current symbol list
*		 variable - the symbol to be added to the list
*
*       Output:  returns the new symbol list.
*
*   Written By:  Mark Young
*
*
************************************************************/

static SymbolList *add_variable(symlist, variable)

SymbolList *symlist;
Symbol	   *variable;
{
	char	   error[LENGTH];
	SymbolList *list, *entry;


	/*
	 *  Allocate the entry for our new variable
	 */
	if ((entry = (SymbolList *) malloc(sizeof(SymbolList))) == NULL)
	{
	   (void) sprintf(error, "Error! Ran out of memory trying to allocate \
reference list for variable '%s'", variable->name);
	   yyerror(error);
	}
	entry->symbol = variable;
	entry->next   = NULL;

	/*
	 *  If the current symlist is NULL then return entry as our symlist.
	 */
	if (symlist == NULL)
	{
	   return(entry);
	}

	/*
	 *  The current symbol list is not empty so add the entry to the
	 *  end of the current list.
	 */
	list = symlist;
	while (list->next != NULL)
	   list = list->next;

	list->next = entry;
	return(symlist);
}



/************************************************************
*
*  Routine Name: add_function()
*
*       Purpose: add_function() is called when we want to add a
*		 function to the symbol list.  The function symbol
*		 does not need to be copied, since we want to share
*		 functions within the system.  The current symbol list
*		 is passed in and the number is then tacked onto the
*		 end of the symbol list.  If the symbol list is currently
*		 NULL then we will create a new list and add the variable
*		 to it.
*
*        Input:  symlist    - the current symbol list
*		 function   - the symbol to be added to the list
*		 num_args   - the number of arguments specified with
*			      the function.
*
*       Output:  returns the new symbol list.
*
*   Written By:  Mark Young
*
*
************************************************************/

static SymbolList *add_function(symlist, function, num_args)

SymbolList *symlist;
Symbol	   *function;
int	   num_args;
{
	char	   error[LENGTH];
	SymbolList *list, *entry;


	/*
	 *  Allocate the entry for our new function
	 */
	if (function->num != num_args && function->type != UNDEFINED)
	{
	   (void) sprintf(error, "Error! Incorrect number of arguments \
specified.  Function '%s' takes %d arguments, but you specified %d.",
			function->name, (int) function->num, num_args);
	   yyerror(error);
	}

	/*
	 *  Allocate the entry for our new function
	 */
	if ((entry = (SymbolList *) malloc(sizeof(SymbolList))) == NULL)
	{
	   (void) sprintf(error, "Error! Ran out of memory trying to allocate \
reference list for function '%s'", function->name);
	   yyerror(error);
	}
	entry->symbol = function;
	entry->next   = NULL;

	/*
	 *  If the current symlist is NULL then return entry as our symlist.
	 */
	if (symlist == NULL)
	{
	   return(entry);
	}

	/*
	 *  The current symbol list is not empty so add the entry to the
	 *  end of the current list.
	 */
	list = symlist;
	while (list->next != NULL)
	   list = list->next;

	list->next = entry;
	return(symlist);
}



/************************************************************
*
*  Routine Name: add_string()
*
*       Purpose: add_string() is called when we want to add a
*		 string to the symbol list.  The current symbol
*		 list is passed in and the string is then tacked
*		 onto the end of the symbol list.  If the
*		 symbol list is currently NULL then we will
*		 create a new list and add the string to it.
*
*        Input:  symlist  - the current symbol list
*		 string   - the string to be added to the list
*
*       Output:  returns the new symbol list.
*
*   Written By:  Mark Young
*
*
************************************************************/

static SymbolList *add_string(symlist, string)

SymbolList *symlist;
char	   *string;
{
	Symbol	   *symbol;
	SymbolList *list, *entry;
	char	   error[LENGTH];


	/*
	 *  Allocate the entry for our new string
	 */
	if ((entry = (SymbolList *) malloc(sizeof(SymbolList))) == NULL)
	{
	   (void) sprintf(error, "Error! Ran out of memory trying to allocate \
reference list for string '%s' ", string);
	   yyerror(error);
	}

	/*
	 *  Allocate the symbol to store our string
	 */
	if ((symbol = (Symbol *) calloc(1,sizeof(Symbol))) == NULL)
	{
	   (void) sprintf(error, "Error! Ran out of memory trying to allocate \
symbol for string '%s' ", string);
	   yyerror(error);
	}
	symbol->type     = STRING;
	symbol->String   = VStrcpy(string);
	entry->symbol    = symbol;
	entry->next	 = NULL;

	/*
	 *  If the current symlist is NULL then return entry as our symlist.
	 */
	if (symlist == NULL)
	{
	   return(entry);
	}

	/*
	 *  The current symbol list is not empty so add the entry to the
	 *  end of the current list.
	 */
	list = symlist;
	while (list->next != NULL)
	   list = list->next;

	list->next = entry;
	return(symlist);
}



/************************************************************
*
*  Routine Name: add_symlist()
*
*       Purpose: add_string() is called when we want to add a
*		 string to the symbol list.  The current symbol
*		 list is passed in and the symlist is then tacked
*		 onto the end of the symbol list.  If the
*		 symbol list is currently NULL then we will
*		 return the other list.
*
*        Input:  symlist  - the current symbol list
*		 endlist  - the symlist to be added to the current list
*
*       Output:  returns the new symbol list.
*
*   Written By:  Mark Young
*
*
************************************************************/

static SymbolList *add_symlist(symlist, endlist)

SymbolList *symlist, *endlist;
{
	SymbolList *list;


	/*
	 *  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)
	   return(endlist);

	/*
	 *  The current symbol list is not empty so add the endlist to the
	 *  end of the current list.
	 */
	list = symlist;
	while (list->next != NULL)
	   list = list->next;

	list->next = endlist;
	return(symlist);
}



/************************************************************
*
*  Routine Name: check_variable()
*
*       Purpose: check_variable() is called when we want to
*		 determine if a variable is an expression or
*		 a dependent variable expression.  When defining
*		 a variable the user may want that variable to
*		 actually be the value of that expression, but
*		 sometimes they may want it to be symbols that
*		 represent that variable.  For instance,
*
*		    x = 10     (EXPRESSION)
*
*	 	 this is an expression, since it is not dependent
*		 on any other variables.  But if we had used
*
*		    x = y + z  (VARIABLE)
*
*		 then we say it is a variable dependent expression,
*		 since if y or z were to change then we would like
*		 x to also change.
*		
*
*        Input:  variable   - the current variable symbol
*
*       Output:  None.  Set the type of variable depending if
*		 the symbol is an expression or variable dependent
*		 expression.
*
*   Written By:  Mark Young
*
*
************************************************************/

static void check_variable(variable)

Symbol	   *variable;
{
	int	   type;
	Symbol	   *symbol;
	SymbolList *symlist;


	/*
	 *  If the current variable is NULL then just return
	 */
	if (variable == NULL)
	   return;

	/*
	 *  Race thru the variable's symbol list and see if the variable
	 *  is an expression or dependent expression.
	 */
	type = EXPRESSION;
	symlist = variable->symlist;
	while (symlist != NULL)
	{
	   symbol = symlist->symbol;
	   if (symbol == variable)
	   {
	      variable->type = EXPRESSION;
	      return;
	   }
	   else if ((symbol->type == VARIABLE || symbol->type == EXPRESSION ||
		symbol->type == UNDEFINED) && variable != symbol)
	   {
	      type = VARIABLE;
	   }
	   symlist = symlist->next;
	}
	variable->type = type;
}



/************************************************************
*
*  Routine Name: check_function()
*
*       Purpose: check_function() is called when we want to
*		 create a user defined function.  The main
*		 purpose of this routine is make the function's
*		 argument list be local to only the function and
*		 not global to the rest of the system.
*
*        Input:  symbol   - the current variable symbol
*
*       Output:  None.  Make the functions symlist have only local
*		 variables and then update the function expression
*		 to use these local variables.
*
*   Written By:  Mark Young
*
*
************************************************************/


static void check_function(function, arglist, symlist)

Symbol	   *function;
SymbolList *arglist, *symlist;
{
	SymbolList *list;
	Symbol	   *symbol, *temp;
	int	   num_args = 0;
	char	   error[LENGTH];


	/*
	 *  If the current function is NULL then just return
	 */
	if (function == NULL)
	   return;

	/*
	 *  Change all function arguments from global to local variables.
	 *  At the same time change the expression to use these local
	 *  variables.
	 */
	while (arglist != NULL)
	{
	   symbol = arglist->symbol;
	   if (symbol->type == VARIABLE || symbol->type == EXPRESSION ||
	       symbol->type == UNDEFINED)
	   {
	      /*
	       *  Allocate the symbol to store our local variable
	       */
	      if ((temp = (Symbol *) calloc(1,sizeof(Symbol))) == NULL)
	      {
	         (void) sprintf(error, "Error! Ran out of memory trying to \
allocate local argument '%s' for function '%s' ", symbol->name, function->name);
		 yyerror(error);
	      }
	      temp->Value = 0.0;
	      temp->type  = EXPRESSION;
	      temp->name  = VStrcpy(symbol->name);
	      arglist->symbol = temp;

	      list = symlist;
	      while (list != NULL)
	      {
		 if (list->symbol == symbol)
		    list->symbol = temp;

	         list = list->next;
	      }
	   }
	   arglist = arglist->next; num_args++;
	}
	function->num = num_args;
}



/************************************************************
*
*  Routine Name: copy_symlist()
*
*       Purpose: copy_symlist() is called when we want to copy
*		 the contents of a symbol list.
*
*        Input:  symlist - the current symbol to be copied
*
*       Output:  None.
*
*   Written By:  Mark Young
*
*
************************************************************/


static SymbolList *copy_symlist(symlist)

SymbolList   *symlist;
{
	Symbol	     *symbol;
	SymbolList   *list, *temp;
	char	     error[LENGTH];


	/*
	 *  If the current symlist is NULL then just return
	 */
	if (symlist == NULL)
	   return(NULL);

	/*
	 *  copy all the resources that are allocated with the symbol,
	 *  ie). the symlist and the string (if the symbol is of type
	 *  string).
	 */
	list = NULL;
	while (symlist != NULL)
	{
	   symbol = symlist->symbol;
	   switch (symbol->type)
	   {
	      case STRING:
		   list = add_string(list, symbol->String);
		   break;

	      case NUMBER:
		   list = add_number(list, symbol->Value);
		   break;

	      case OPERATOR:
	      case UOPERATOR:
		   list = add_operator(list, symbol->Operator, symbol->name,
				symbol->type);
		   break;

	      case VARIABLE:
	      case EXPRESSION:
	      case FUNCTION:
	      case UFUNCTION:
		   if ((temp = (SymbolList *) malloc(sizeof(SymbolList)))==NULL)
		   {
		      (void) sprintf(error, "Error! Ran out of memory trying \
to allocate reference list for symbol '%s'", symbol->name);
		      yyerror(error);
		   }
		   temp->symbol = symbol;
		   temp->next   = list;
		   list	= temp;
		   break;

	      default:
		   (void) sprintf(error, "Error! Unknown symbol type '%d' to \
be copied.\n", symbol->type);
		   yyerror(error);
		   break;
	   }
	   symlist = symlist->next;
	}
	return(list);
}



/************************************************************
*
*  Routine Name: yyerror()
*
*       Purpose: called when an error has been detected on the
*		 expression string.
*
*        Input:  error  - the error that occured
*
*       Output:  formats an error message into the *xve_error
*		 array and long jumps back to the beginning
*		 of _xve_eval_expression().
*
*   Written By:  Mark Young
*
*
************************************************************/


yyerror(error)

char *error;
{
	int	i;
	char	temp[512];


	sprintf(xve_error,"\nfunction string is = \"%s\"\n", string_array);
	sprintf(temp,"                     ");
	strcat(xve_error, temp);
	for (i = 0; i < (sptr - string_array); i++)
	{
	    strcat(xve_error," ");
	}
	sprintf(temp,"^\n%s...\n",error);
	strcat(xve_error, temp);

	/*
	 * error out to calling routine get_function().
	 */
	longjmp(jump_dest, 1);
}
