                              Listing 1

                        Listing of FUNCEVAL.C


/**********************************************************
*                                                         *
*  Convert() converts a string containing a math          *
*  function in two variables, X and Y, to a postfix       *
*  notation string with the numeric constants and         *
*  functions converted to one byte symbols.  If any       *
*  syntax errors occur they will be posted in the global  *
*  external variable SyntaxErr.  Definitions are in       *
*  syntxerr.h.                                            *
*                                                         *
*  Evaluate() will substitute the X and Y values passed   *
*  to it and return the value of the function.            *
*                                                         *
*  This program may be freely used for non-profit         *
*  purposes as long as the copyright notice remains       *
*  in the code.                                           *
*                                                         *
***********************************************************
*                                                         *
*  funceval.c v3.0 -- Copyright 1988  Randy C. Finch      *
*                                                         *
**********************************************************/

/*--------------  INCLUDES  ----------------*/

#include <ctype.h>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <float.h>
#include <stdlib.h>
#include "syntxerr.h"


/*----------------  DEFINES  -----------------*/

#define NUMSYM 128     /* Number of constants allowed in function */
#define SYMBASE 128    /* Base value for constants symbols */
#define STACKSIZE 256  /* Stack size */
#define SIN  1         /* Symbol for sine */
#define COS  2         /* Symbol for cosine */
#define TAN  3         /* Symbol for tangent */
#define ASIN 4         /* Symbol for arcsine */
#define ACOS 5         /* Symbol for arccosine */
#define ATAN 6         /* Symbol for arctangent */
#define SINH 7         /* Symbol for hyperbolic sine */
#define COSH 8         /* Symbol for hyperbolic cosine */
#define TANH 9         /* Symbol for hyperbolic tangent */
#define EXP  10        /* Symbol for exponential */
#define SQRT 11        /* Symbol for square root */
#define LN   12        /* Symbol for natural logarithm */
#define LOG  13        /* Symbol for logarithn base 10 */
#define NULL 0         /* Symbol for null */
#define TRUE 1         /* Symbol for true condition */
#define FALSE 0        /* Symbol for false condition */


/*------------  EXTERNAL GLOBALS  ------------*/

unsigned char SyntaxErr;


/*----------------  GLOBALS  ----------------*/

struct CharStack {
  unsigned char c[STACKSIZE];
  long          top;
};

struct NumStack {
  double n[STACKSIZE];
  long   top;
};

static struct CharStack cstack;

static struct NumStack nstack;

static double Constants[NUMSYM];

static unsigned char CurConstant;

static unsigned char NewExpr[256];


/*----------------  FUNCTIONS  ----------------*/

static char CharInStr(s,c)
  unsigned char *s;
  unsigned char c;
{
  while (*s != NULL) {
     if (*s == c) return TRUE;
     ++s;
  }
  return FALSE;
} /* CharInStr */


static void Deposit(num)
  double num;
{
  Constants[CurConstant - SYMBASE] = num;
} /* Deposit */


static void Substitute(symb, ptr, len)
  unsigned char symb;
  unsigned char *ptr;
  unsigned long len;
{
  *ptr = symb;

  if (len > 1) {
     do {
        ++ptr;
        *ptr = *(ptr + len - 1);
     } while (*ptr != NULL);
  }
} /* Substitute */


static void RemoveSpaces(str)
  unsigned char *str;
{
  unsigned char *ptr;

  while (*str != NULL) {
     if (*str == ' ') {
        ptr = str;
        do {
           *ptr = *(ptr + 1);
           ++ptr;
        } while ( *(ptr - 1) != NULL);

        --str;
     }

     ++str;
  }
} /* RemoveSpaces */


static void AddZero(ptr)
  char *ptr;
{
  unsigned long len;
  char *i;

  len = strlen(ptr);

  for (i=ptr+len+1; i>ptr; --i)
     *i = *(i - 1);

  *ptr = '0';
} /* AddZero */


static unsigned char CPop()
{
  if (cstack.top == 0) return 0;
  else {
     --cstack.top;
     return cstack.c[cstack.top + 1];
  }
} /* CPop */


static char CPush(c)
  unsigned char c;
{
  if (cstack.top == STACKSIZE) return FALSE;
  else {
     ++cstack.top;
     cstack.c[cstack.top] = c;
     return TRUE;
  }
} /* CPush */


static unsigned char CTopOfStack()
{
  return cstack.c[cstack.top];
} /* CTopOfStack */


static double NPop()
{
  --nstack.top;
  return nstack.n[nstack.top + 1];
} /* NPop */


static void NPush(n)
  double n;
{
  ++nstack.top;
  nstack.n[nstack.top] = n;
} /* NPush */


static char IsFunction(c)
  unsigned char c;
{
  if ( (c >= SIN) && (c <= LOG) )
     return TRUE;
  else
     return FALSE;
} /* IsFunction */


static char IsSymbol(c)
  unsigned char c;
{
  if ((c >= SYMBASE) && (c < SYMBASE+NUMSYM))
     return TRUE;
  else
     return FALSE;
} /* IsSymbol */


static char Precedence(c1,c2)
  unsigned char c1,c2;
{
  if ( (CharInStr("+-*/",c1)) && (c2 == '^') )
     return FALSE;

  else if ( (CharInStr("+-",c1)) && (CharInStr("*/",c2)) )
     return FALSE;

  else if ( ((c1 == '(') && (c2 != ')')) || (c2 == '(') )
     return FALSE;

  else if ( (CharInStr("+-*/^",c1)) && (IsFunction(c2)) )
     return FALSE;

  else
     return TRUE;
} /* Precedence */


static unsigned char *CheckSyntax(str)
  unsigned char *str;
{
  int numLP = 0,
      numRP = 0;

  if ( (CharInStr("/*^E)",*str)) && (strncmp(str,"EXP",3) != 0) ) {
     if (CharInStr("/*^",*str)) {
        SyntaxErr = MISPLACEDOP;
        return str;
     }
     else if (*str == 'E') {
        SyntaxErr = ILLEGALEXP;
        return str;
     }
     else {
        SyntaxErr = MISSINGLP;
        return str;
     }
  } /* if */

  for (;;) {   /* forever */

     if (*str == '(') {
        ++numLP;
        ++str;
        if ( (CharInStr("*/^E",*str)) && (strncmp(str,"EXP",3) != 0) ) {
           if (*str == 'E') {
              SyntaxErr = ILLEGALEXP;
              return str;
           }
           else {
              SyntaxErr = MISPLACEDOP;
              return str;
           }
        } /* if */
        if ( (*str == ')') || (*str == NULL) ) {
           SyntaxErr = MISSINGPARM;
           return str;
        }
     } /* if */

     else if (*str == ')') {
        ++numRP;
        ++str;
        if (numRP > numLP) {
           SyntaxErr = MISSINGLP;
           return (str-1);
        }
        else if ( (!CharInStr(")+-*/^",*str)) && (*str != NULL) ) {
           SyntaxErr = MISSINGOP;
           return str;
        }
     } /* else if */

     else if ( (isdigit(*str)) || (*str == '.') ) {
        char ExitFlag = FALSE,
             OneDecimal = FALSE,
             OneE = FALSE;

        if (*str == '.') OneDecimal = TRUE;

        ++str;

        if ( (OneDecimal == TRUE) && (!isdigit(*str)) ) {
           SyntaxErr = LONEDECIMAL;
           return (str - 1);
        }

        while ( ( (isdigit(*str)) || (CharInStr(".E)-+",*str))
                || (*str == NULL) ) && !ExitFlag ) {

           if (*str == '.') {
              ++str;
              if (OneE) {
                 SyntaxErr = ILLEGALEXP;
                 return (str-1);
              }
              else if (OneDecimal) {
                 SyntaxErr = EXTRADECIMAL;
                 return (str-1);
              }
              else if (strncmp(str,"EXP",3) == 0) {
                 SyntaxErr = MISSINGOP;
                 return str;
              }
              else if ( (!CharInStr("+-*/^E)",*str)) && (!isdigit(*str)) ) {
                 SyntaxErr = ILLEGALCHAR;
                 return str;
              }
              else {
                 OneDecimal = TRUE;
              }
           } /* if */

           else if (*str == 'E') {
              ++str;
              if (OneE) {
                 SyntaxErr = EXTRAE;
                 return (str-1);
              }
              else if ( (!CharInStr("+-",*str)) && (!isdigit(*str)) ) {
                 SyntaxErr = ILLEGALEXP;
                 return str;
              }
              else {
                 OneE = TRUE;
              }
           } /* else if */

           else if (CharInStr("+-",*str)) {
              if ( *(str-1) == 'E' )
                 ++str;
              else if ( !OneE || (OneE && isdigit(*(str-1))) )
                 ExitFlag = TRUE;
              else {
                 SyntaxErr = MISPLACEDOP;
                 return str;
              }
           } /* else if */

           else if ( (*str == ')') || (*str == NULL) ) {
              if (CharInStr("+-E",*(str-1))) {
                 SyntaxErr = ILLEGALEXP;
                 return str;
              }
              else {
                 ExitFlag = TRUE;
              }
           } /* else if */

           else {
              ++str;
           } /* else */

        } /* while */

        if( !CharInStr("+-*/)", *str) && (*str != NULL) ) {
           SyntaxErr = MISSINGOP;
           return str;
        }

     } /* else if */

     else if (CharInStr("+-*/^",*str)) {
        ++str;
        if ( (CharInStr(")E+-*/^",*str)) || (*str == NULL) ) {
           if (strncmp(str,"EXP",3) != 0) {
              SyntaxErr = MISPLACEDOP;
              return (str-1);
           }
        }
     } /* else if */

     else if (CharInStr("XY",*str)) {
        ++str;
        if ( (!CharInStr(")+-*/^",*str)) && (*str != NULL) ) {
           SyntaxErr = MISSINGOPRP;
           return str;
        }
     } /* else if */

     else if (isupper(*str)) {
        if (strncmp(str,"LN",2) == 0) str += 2;
        else if (strncmp(str,"SINH",4) == 0) str += 4;
        else if (strncmp(str,"COSH",4) == 0) str += 4;
        else if (strncmp(str,"TANH",4) == 0) str += 4;
        else if (strncmp(str,"SIN",3) == 0) str += 3;
        else if (strncmp(str,"COS",3) == 0) str += 3;
        else if (strncmp(str,"TAN",3) == 0) str += 3;
        else if (strncmp(str,"EXP",3) == 0) str += 3;
        else if (strncmp(str,"LOG",3) == 0) str += 3;
        else if (strncmp(str,"SQRT",4) == 0) str += 4;
        else if (strncmp(str,"ASIN",4) == 0) str += 4;
        else if (strncmp(str,"ACOS",4) == 0) str += 4;
        else if (strncmp(str,"ATAN",4) == 0) str += 4;
        else {
           SyntaxErr = ILLEGALFUNC;
           return str;
        }

        if (*str != '(') {
           SyntaxErr = MISSINGLP;
           return str;
        }
     } /* else if */

     else if (*str == NULL) {
        if (numLP < numRP) {
           SyntaxErr = MISSINGLP;
           return str;
        }
        else if (numLP > numRP) {
           SyntaxErr = MISSINGRP;
           return str;
        }
        else {
           SyntaxErr = FALSE;
           return 0L;
        }
     } /* else if */

     else {
        SyntaxErr = ILLEGALCHAR;
        return str;
     }

  } /* for */

} /* CheckSyntax */


static char ConvertConstants(str)
  unsigned char *str;
{
  unsigned char *ptr;

  ptr = str;
  if ( CharInStr("+-",*ptr) ) {
     AddZero(str);
     ptr += 2;
  }

  while ( *ptr != NULL ) {
     if ( (CharInStr("+-",*ptr)) && (*(ptr-1) == '(') )
        AddZero(ptr);

     ++ptr;

  } /* while */

#if DEBUG
  printf("\nAddZero: %s\n", str);
#endif

  {  /* begin block */
  unsigned long j;
  unsigned char numstr[80];
  double number;

  ptr = str;

  CurConstant = SYMBASE;

  while ( *ptr != NULL) {
     if ( (*ptr == '.') || (isdigit(*ptr)) ) {
        unsigned long lennum = 1;

        while ( (CharInStr(".E-+",*(ptr+lennum))) || (isdigit(*(ptr+lennum))) ) {
           if( (CharInStr("-+",*(ptr+lennum))) && (*(ptr+lennum-1) != 'E') )
              break;
           ++lennum;
        }

        for (j=0; j<lennum; ++j)
           *(numstr+j) = *(ptr+j);

        *(numstr+j) = NULL;

        number = atof(numstr);
        Deposit(number);
        Substitute(CurConstant, ptr, lennum);

        ++CurConstant;
        if (CurConstant >= SYMBASE+NUMSYM) {
           SyntaxErr = TOOMANYCONST;
           return FALSE;
        }

     } /* if */

     ++ptr;

  } /* while */

  }  /* end block */

  return TRUE;

} /* ConvertConstants */


static void ConvertFunctions(str)
  unsigned char *str;
{
  while ( *str != NULL ) {
     if ( (isupper(*str)) && (!CharInStr("XY",*str)) ) {

        if (strncmp(str,"LN",2) == 0) Substitute(LN,str,2L);
        else if (strncmp(str,"SINH",4) == 0) Substitute(SINH,str,4L);
        else if (strncmp(str,"COSH",4) == 0) Substitute(COSH,str,4L);
        else if (strncmp(str,"TANH",4) == 0) Substitute(TANH,str,4L);
        else if (strncmp(str,"SIN",3) == 0) Substitute(SIN,str,3L);
        else if (strncmp(str,"COS",3) == 0) Substitute(COS,str,3L);
        else if (strncmp(str,"TAN",3) == 0) Substitute(TAN,str,3L);
        else if (strncmp(str,"EXP",3) == 0) Substitute(EXP,str,3L);
        else if (strncmp(str,"LOG",3) == 0) Substitute(LOG,str,3L);
        else if (strncmp(str,"SQRT",4) == 0) Substitute(SQRT,str,4L);
        else if (strncmp(str,"ASIN",4) == 0) Substitute(ASIN,str,4L);
        else if (strncmp(str,"ACOS",4) == 0) Substitute(ACOS,str,4L);
        else if (strncmp(str,"ATAN",4) == 0) Substitute(ATAN,str,4L);

     } /* if */

     ++str;

  } /* while */

} /* ConvertFunctions */


static char InfixToPostfix(str)
  unsigned char *str;
{
  unsigned long i1=0, i2=0;
  unsigned char NextChar, TopSymbol;

  cstack.top = 0;       /* Initialize stack */
  NewExpr[0] = NULL;    /* Initialize expression */

  while ( *(str+i1) != NULL ) {

     NextChar = *(str+i1);

     if ( (IsSymbol(NextChar)) || (NextChar == 'X') || (NextChar == 'Y') ) {
        NewExpr[i2] = NextChar;
        ++i2;
     }
     else {
        for (;;) {   /* Forever */

           if ( (cstack.top == 0) || (!Precedence(CTopOfStack(),NextChar)) )
              break;

           if ((TopSymbol = CPop()) == 0) {
              SyntaxErr = STACKUNDERFLOW;
              return FALSE;
           }

           if (cstack.top != 0) {
              if ( (IsFunction( CTopOfStack() )) && (NextChar == ')') ) {
                 TopSymbol = CPop();
                 NewExpr[i2] = TopSymbol;
                 ++i2;
                 break;
              } /* if */
           } /* if */

           if ( (TopSymbol == '(') && (NextChar == ')') )
              break;

           if (TopSymbol != '(') {
              NewExpr[i2] = TopSymbol;
              ++i2;
           }

        } /* for */

        if (NextChar != ')') {
           if (CPush(NextChar) == FALSE) {
              SyntaxErr = STACKOVERFLOW;
              return FALSE;
           }
        }

     } /* if */

     ++i1;

  } /* while */

  while (cstack.top != 0) {
     TopSymbol = CPop();
     if (TopSymbol != '(') {
        NewExpr[i2] = TopSymbol;
        ++i2;
     }
  }

  NewExpr[i2] = NULL;

  return TRUE;

} /* InfixToPostfix */


static double Calculate(s,n2,n1)
  unsigned char s;
  double n1,n2;
{
  switch (s) {
     case '+':
        return (n1 + n2);
     case '-':
        return (n1 - n2);
     case '*':
        return (n1 * n2);
     case '/':
        return (n1 / n2);
     case '^':
        return ( exp(n2*log(n1)) );
     case SIN:
        return ( sin(n2) );
     case COS:
        return ( cos(n2) );
     case TAN:
        return ( tan(n2) );
     case EXP:
        return ( exp(n2) );
     case SQRT:
        return ( sqrt(n2) );
     case LN:
        return ( log(n2) );
     case LOG:
        return ( log10(n2) );
     case ASIN:
        return ( asin(n2) );
     case ACOS:
        return ( acos(n2) );
     case ATAN:
        return ( atan(n2) );
     case SINH:
        return ( sinh(n2) );
     case COSH:
        return ( cosh(n2) );
     case TANH:
        return ( tanh(n2) );

  } /* switch */

} /* Calculate */


unsigned char *Convert(FunctionString)
  unsigned char *FunctionString;
{
  unsigned char fstr[512];
  unsigned char *ptr;

  SyntaxErr = FALSE;

  RemoveSpaces(FunctionString);

#if DEBUG
  printf("\nRemoveSpaces: %s\n", FunctionString);
#endif

  strupr(FunctionString);

#if DEBUG
  printf("\nstrupr: %s\n", FunctionString);
#endif

  if ((ptr = CheckSyntax(FunctionString)) != 0) return ptr;

  strcpy(fstr,FunctionString);

#if DEBUG
  printf("\nCheckSyntax: %s\n", fstr);
#endif

  if (!ConvertConstants(fstr)) return fstr;

#if DEBUG
  printf("\nConvertConstants: ");

  for(ptr = fstr; *ptr != NULL; ++ptr)
     printf("%d ", *ptr);

  printf("\n");
#endif

  ConvertFunctions(fstr);

#if DEBUG
  printf("\nConvertFunctions: ");

  for(ptr = fstr; *ptr != NULL; ++ptr)
     printf("%d ", *ptr);

  printf("\n");
#endif

  if (!InfixToPostfix(fstr)) return fstr;

#if DEBUG
  {
  unsigned char *ptr;

  printf("\nInfixToPostfix: ");

  for(ptr = NewExpr; *ptr != NULL; ++ptr)
     printf("%d ", *ptr);

  printf("\n");
  }
#endif

  return NewExpr;

} /* Convert */


double Evaluate(x,y)
  double x, y;
{
  unsigned char symbol;
  long i = 0;

  nstack.top = 0;  /* Initialize stack */

  while (NewExpr[i] != NULL) {
     symbol = NewExpr[i];

     if (symbol == 'X')
        NPush(x);

     else if (symbol == 'Y')
        NPush(y);

     else if (IsSymbol(symbol)) {
        NPush( Constants[symbol-SYMBASE] );
     }

     else if (IsFunction(symbol)) {
        NPush( Calculate(symbol, NPop(), 0.0) );
     }

     else {
        NPush( Calculate(symbol, NPop(), NPop()) );
     }

     ++i;

  } /* while */

  return NPop();

} /* Evaluate */

