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

        bwb_exp.c       Expression Parser
                        for Bywater BASIC Interpreter

                        Copyright (c) 1993, Ted A. Campbell
                        Bywater Software

                        email: tcamp@delphi.com

        Copyright and Permissions Information:

        All U.S. and international rights are claimed by the author,
        Ted A. Campbell.

	This software is released under the terms of the GNU General
	Public License (GPL), which is distributed with this software
	in the file "COPYING".  The GPL specifies the terms under
	which users may copy and use the software in this distribution.

	A separate license is available for commercial distribution,
	for information on which you should contact the author.

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

/*---------------------------------------------------------------*/
/* NOTE: Modifications marked "JBV" were made by Jon B. Volkoff, */
/* 11/1995 (eidetics@cerf.net).                                  */
/*---------------------------------------------------------------*/

#include <stdio.h>
#include <ctype.h>
#include <math.h>

#include "bwbasic.h"
#include "bwb_mes.h"

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

        FUNCTION:	bwb_exp()

        DESCRIPTION:	This is the function by which the expression
			parser is called.

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

#if ANSI_C
struct exp_ese *
bwb_exp( char *expression, int assignment, int *position )
#else
struct exp_ese *
bwb_exp( expression, assignment, position )
   char *expression;
   int assignment;
   int *position;
#endif
   {
   struct exp_ese *rval;			/* return value */
   int entry_level, main_loop, err_condition;
   char *e;                                     /* pointer to current string */
   int r;                                       /* return value from functions */
   register int c;                              /* quick counter */
#if OLD_WAY
   int adv_loop;
#endif

#if INTENSIVE_DEBUG
   sprintf( bwb_ebuf, "entered bwb_exp(): expression <%s> assignment <%d> level <%d>",
      & ( expression[ *position ] ), assignment, CURTASK expsc );
   bwb_debug( bwb_ebuf );
#endif

   /* save the entry level of the expression stack in order to
      check it at the end of this function */

   entry_level = CURTASK expsc;
   err_condition = FALSE;

   /* advance past whitespace or beginning of line segment */

#if MULTISEG_LINES
   if ( expression[ *position ] == ':' )
      {
      ++( *position );
      }
#endif
   adv_ws( expression, position );
#if MULTISEG_LINES
   if ( expression[ *position ] == ':' )
      {
      ++( *position );
      adv_ws( expression, position );
      }
#endif

   /* increment the expression stack counter to get a new level */

   inc_esc();

   /* check to be sure there is a legitimate expression
      and set initial parameters for the main loop */

   if ( is_eol( expression, position ) == TRUE )
      {
      main_loop = FALSE;    /* break out of loop */
      }
   else
      {
      main_loop = TRUE;
      CURTASK exps[ CURTASK expsc ].pos_adv = 0;
      }

#if OLDWAY
   adv_loop = TRUE;
   while( adv_loop == TRUE )
      {
      switch( expression[ *position ] )
         {
         case ' ':                           /* whitespace */
         case '\t':
            ++(*position);
            break;
         case '\0':                          /* end of string */
         case '\r':
         case '\n':
            main_loop = adv_loop = FALSE;    /* break out of loop */
            break;
         default:
            adv_loop = FALSE;
            main_loop = TRUE;
            CURTASK exps[ CURTASK expsc ].pos_adv = 0;
            break;
         }
      }
#endif

   /* main parsing loop */

   while ( main_loop == TRUE )
      {

      /* set variable <e> to the start of the expression */

      e = &( expression[ *position ] );

#if INTENSIVE_DEBUG
      sprintf( bwb_ebuf, "in bwb_exp(): main loop, level <%d> element <%s> ",
         CURTASK expsc, e );
      bwb_debug( bwb_ebuf );
#endif

      /* detect the operation required at this level */

      CURTASK exps[ CURTASK expsc ].operation = exp_findop( e );

#if INTENSIVE_DEBUG
      sprintf( bwb_ebuf, "in bwb_exp(): exp_findop() returned <%d>",
         CURTASK exps[ CURTASK expsc ].operation );
      bwb_debug( bwb_ebuf );
#endif

      /* perform actions specific to the operation */

      switch( CURTASK exps[ CURTASK expsc ].operation )
         {
         case OP_ERROR:
            main_loop = FALSE;
            err_condition = TRUE;
            break;

	 case OP_TERMINATE:                /* terminate at THEN, ELSE, TO */
#if INTENSIVE_DEBUG
	    bwb_debug( "in bwb_exp(): Found OP_TERMINATE" );
#endif
         case OP_STRJOIN:                  /* string join or tab */
         case OP_STRTAB:
	    main_loop = FALSE;
	    err_condition = FALSE;
	    dec_esc();
	    break;

	 case OP_ADD:                      /* in the case of any numerical operation, */
         case OP_SUBTRACT:
         case OP_MULTIPLY:
         case OP_DIVIDE:
         case OP_MODULUS:
         case OP_EXPONENT:
         case OP_INTDIVISION:
         case OP_GREATERTHAN:
         case OP_LESSTHAN:
         case OP_GTEQ:
         case OP_LTEQ:
         case OP_NOTEQUAL:
         case OP_NOT:
         case OP_AND:
         case OP_OR:
         case OP_XOR:
         case OP_IMPLIES:
         case OP_EQUIV:
         case OP_NEGATION: /* JBV */

#if INTENSIVE_DEBUG
            sprintf( bwb_ebuf, "in bwb_exp(): operator detected." );
            bwb_debug( bwb_ebuf );
#endif

            CURTASK exps[ CURTASK expsc ].pos_adv = -1;             /* set to strange number */

            /* cycle through operator table to find match */

            for ( c = 0; c < N_OPERATORS; ++c )
               {
               if ( exp_ops[ c ].operation == CURTASK exps[ CURTASK expsc ].operation )
                  {
                  CURTASK exps[ CURTASK expsc ].pos_adv = strlen( exp_ops[ c ].symbol );
                  }
               }

            if ( CURTASK exps[ CURTASK expsc ].pos_adv == -1 )      /* was a match found? */
               {
               CURTASK exps[ CURTASK expsc ].pos_adv = 0;           /* no -- set to 0 */
               }
            break;                         /* and move on */

         case OP_EQUALS:

#if INTENSIVE_DEBUG
            sprintf( bwb_ebuf, "in bwb_exp(): equal sign detected." );
            bwb_debug( bwb_ebuf );
#endif

            if ( assignment == TRUE )
               {
               CURTASK exps[ CURTASK expsc ].operation = OP_ASSIGN;
               }
            CURTASK exps[ CURTASK expsc ].pos_adv = 1;
            break;

         case PARENTHESIS:
            r = exp_paren( e );
            break;

	 case CONST_STRING:
            r = exp_strconst( e );
            break;

	 case CONST_NUMERICAL:
            r = exp_numconst( e );
#if INTENSIVE_DEBUG
            sprintf( bwb_ebuf, "in bwb_exp(): return from exp_numconst(), r = <%d>",
               r );
            bwb_debug( bwb_ebuf );
#endif
            break;

         case FUNCTION:

#if INTENSIVE_DEBUG
            sprintf( bwb_ebuf, "in bwb_exp(): calling exp_function(), expression <%s>",
               e );
            bwb_debug( bwb_ebuf );
#endif

            r = exp_function( e );
            break;

         case OP_USERFNC:

#if INTENSIVE_DEBUG
            sprintf( bwb_ebuf, "in bwb_exp(): calling exp_ufnc(), expression <%s>",
               e );
            bwb_debug( bwb_ebuf );
#endif

	    r = exp_ufnc( e );

#if INTENSIVE_DEBUG
	    sprintf( bwb_ebuf, "in bwb_exp(): return from exp_ufnc(), buffer <%s>",
	       &( expression[ *position ] ) );
	    bwb_debug( bwb_ebuf );
#endif

            break;

         case VARIABLE:
            r = exp_variable( e );
            break;

	 default:
            err_condition = TRUE;
            main_loop = FALSE;
#if PROG_ERRORS
            sprintf( bwb_ebuf, "in bwb_exp.c:bwb_exp(): unidentified operation (%d).",
               CURTASK exps[ CURTASK expsc ].operation );
            bwb_error( bwb_ebuf );
#else
            bwb_error( err_syntax );
#endif
            break;
         }

      /* increment *position counter based on previous actions */

      *position += CURTASK exps[ CURTASK expsc ].pos_adv;
      CURTASK exps[ CURTASK expsc ].pos_adv = 0;            /* reset advance counter */

#if INTENSIVE_DEBUG
      sprintf( bwb_ebuf, "in bwb_exp(): advanced position; r <%d> err_c <%d>",
         r, err_condition );
      bwb_debug( bwb_ebuf );
#endif

#if INTENSIVE_DEBUG
      if ( CURTASK exps[ CURTASK expsc ].operation == OP_EQUALS )
         {
         sprintf( bwb_ebuf, "in bwb_exp(): with OP_EQUALS: finished case" );
         bwb_debug( bwb_ebuf );
         }
#endif

      /* check for end of string */

      if ( is_eol( expression, position ) == TRUE )
         {
         main_loop = FALSE;    /* break out of loop */         
         }

#if OLDWAY
      adv_loop = TRUE;
      while( adv_loop == TRUE )
         {
         switch( expression[ *position ] )
            {
            case ' ':                           /* whitespace */
            case '\t':
               ++(*position);
               break;
            case '\0':                          /* end of string */
            case '\r':
            case '\n':
            case ':':
               main_loop = adv_loop = FALSE;    /* break out of loop */
               break;
            default:
               adv_loop = FALSE;
               break;
            }
         }
#endif

      /* get a new stack level before looping */

      if ( main_loop == TRUE )
         {
         r = inc_esc();
#if INTENSIVE_DEBUG
         sprintf( bwb_ebuf, "in bwb_exp(): increment esc, r <%d>, err_c <%d>",
            r, err_condition );
         bwb_debug( bwb_ebuf );
#endif
         }

      /* check for error return */

      if ( r == OP_ERROR )
         {
#if INTENSIVE_DEBUG
         sprintf( bwb_ebuf, "in bwb_exp(): found r == OP_ERROR." );
         bwb_debug( bwb_ebuf );
#endif
         main_loop = FALSE;
         err_condition = TRUE;
         }
      else
         {
         r = TRUE;
         }

      }                                 /* end of main parsing loop */

#if INTENSIVE_DEBUG
   sprintf( bwb_ebuf, "in bwb_exp(): breakout from main parsing loop, r <%d> err_c <%d>",
      r, err_condition );
   bwb_debug( bwb_ebuf );
#endif

   /* check error condition */

   if ( err_condition == TRUE )
      {

#if INTENSIVE_DEBUG
      sprintf( bwb_ebuf, "error detected in expression parser" );
      bwb_debug( bwb_ebuf );
#endif

      /* decrement the expression stack counter until it matches entry_level */

      while( CURTASK expsc > entry_level )
         {
         dec_esc();
         }

#if PROG_ERRORS
      bwb_error( "in bwb_exp():  Error detected in parsing expression" );
#else
      bwb_error( err_syntax );
#endif
      }

   /* no error; normal exit from function */

   else
      {

      /* are any more operations needed? if we are still at entry level,
         then they are not */

      /* try operations */

      exp_operation( entry_level );

      /* see what is on top of the stack */

      if ( CURTASK expsc > ( entry_level + 1 ))
         {
         switch( CURTASK exps[ CURTASK expsc ].operation )
            {
            case OP_STRJOIN:
               if ( CURTASK expsc != ( entry_level + 2 ))
                  {
#if PROG_ERRORS
                  sprintf( bwb_ebuf, "in bwb_exp(): OP_STRJOIN in wrong position." );
                  bwb_error( bwb_ebuf );
#else
                  bwb_error( err_syntax );
#endif
                  }
               break;
            default:
#if PROG_ERRORS
               sprintf( bwb_ebuf, "in bwb_exp(): incomplete expression." );
               bwb_error( bwb_ebuf );
#else
               bwb_error( err_syntax );
#endif
               break;
            }

         /* decrement the expression stack counter */

#if INTENSIVE_DEBUG
         sprintf( bwb_ebuf, "in bwb_exp(): before dec_esc type is <%c>",
            CURTASK exps[ CURTASK expsc ].type );
         bwb_debug( bwb_ebuf );
#endif

         dec_esc();

         }

      /* assign rvar to the variable for the current level */

      rval = &( CURTASK exps[ CURTASK expsc ] );

      /* decrement the expression stack counter */

      dec_esc();

      /* check the current level before exit */

      if ( entry_level != CURTASK expsc )
         {
#if PROG_ERRORS
         sprintf( bwb_ebuf, "in bwb_exp(): exit stack level (%d) does not match entry stack level (%d)",
            CURTASK expsc, entry_level );
         bwb_error( bwb_ebuf );
#else
         bwb_error( err_overflow );
#endif
         }

      }

   /* return a pointer to the last stack level */

   return rval;

   }

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

	FUNCTION:       exp_findop()

	DESCRIPTION:    This function reads the expression to find
			what operation is required at its stack level.

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

#if ANSI_C
int
exp_findop( char *expression )
#else
int
exp_findop( expression )
   char *expression;
#endif
   {
   register int c;                              /* character counter */
   int carry_on;                                /* boolean: control while loop */
   int rval;                                    /* return value */
   char cbuf[ MAXSTRINGSIZE + 1 ];              /* capitalized expression */
   char nbuf[ MAXSTRINGSIZE + 1 ];              /* non-capitalized expression */
   int position;                                /* position in the expression */
   int adv_loop;                                /* control loop to build expression */

#if INTENSIVE_DEBUG
   sprintf( bwb_ebuf, "in exp_findop(): received <%s>", expression );
   bwb_debug( bwb_ebuf );
#endif

   /* set return value to OP_NULL initially */

   rval = OP_NULL;

   /* assign local pointer to expression to begin reading */

   position = 0;

   /* advance to the first significant character */

   adv_ws( expression, &position );

#if INTENSIVE_DEBUG
   sprintf( bwb_ebuf, "in exp_findop(): expression after advance <%s>",
      &( expression[ position ] ) );
   bwb_debug( bwb_ebuf );
#endif

   /* we now have the first significant character and can begin parsing */

   /* check the first character for an indication of a parenthetical
      expression, a string constant, or a numerical constant that begins
      with a digit (numerical constants beginning with a plus or minus
      sign or hex/octal/binary constants will have to be detected by
      exp_isnc() */

   carry_on = TRUE;
   switch ( expression[ position ] )
      {
      case '\"':                /* this should indicate a string constant */
         rval = CONST_STRING;
         break;
      case '(':                 /* this will indicate a simple parenthetical expression */
         rval = PARENTHESIS;
         break;

#if MULTISEG_LINES
      case ':':                 /* terminate processing */
#endif
      case ')':			/* end of argument list? */
         rval = OP_TERMINATE;
         break;

      case '0':                 /* these will indicate a numerical constant */
      case '1':
      case '2':
      case '3':
      case '4':
      case '5':
      case '6':
      case '7':
      case '8':
      case '9':
      case '.':
      case '&':                 /* designator for hex or octal constant */
         rval = CONST_NUMERICAL;
         break;
      }

#if INTENSIVE_DEBUG
   sprintf( bwb_ebuf, "in exp_findop(): rval pos 1 is <%d>", rval );
   bwb_debug( bwb_ebuf );
#endif

   /* String constants, numerical constants, open parentheses, and
      the plus and minus operators have been checked at this point;
      but if the return value is still OP_NULL, other possibilities
      must be checked, namely, other operators, function names, and
      variable names.  The function adv_element cannot be used here
      because it will stop, e.g., with certain operators and not
      include them in the returned element. */

   /* get a character string to be interpreted */

   adv_loop = TRUE;
   cbuf[ 0 ] = '\0';
   nbuf[ 0 ] = '\0';
   c = 0;
   while ( adv_loop == TRUE )
      {

#if INTENSIVE_DEBUG
      sprintf( bwb_ebuf, "in exp_findop() loop position <%d> char 0x%x",
	 c, expression[ position ] );
      bwb_debug( bwb_ebuf );
#endif

      switch( expression[ position ] )
	 {
	 case ' ':              /* whitespace */
	 case '\t':
	 case '\r':             /* end of line */
	 case '\n':
	 case '\0':             /* end of string */
	 case '(':              /* parenthesis terminating function name */
	    adv_loop = FALSE;
	    break;
	 default:
	    nbuf[ c ] = cbuf[ c ] = expression[ position ];
	    ++c;
	    nbuf[ c ] = cbuf[ c ] = '\0';
	    ++position;
	    break;
	 }

      if ( c >= MAXSTRINGSIZE )
	 {
	 adv_loop = FALSE;
	 }

      }
   bwb_strtoupper( cbuf );

#if INTENSIVE_DEBUG
   sprintf( bwb_ebuf, "in exp_findop(): cbuf element is <%s>", cbuf );
   bwb_debug( bwb_ebuf );
#endif

   /* check for numerical constant */

   if ( rval == OP_NULL )
      {
      rval = exp_isnc( cbuf );
      }

   /* check for other operators */

   if ( rval == OP_NULL )
      {
      rval = exp_isop( cbuf );
      }

   /* check for user-defined function */

   if ( rval == OP_NULL )
      {
      rval = exp_isufn( nbuf );
      }

   /* check for function name */

   if ( rval == OP_NULL )
      {
      rval = exp_isfn( nbuf );
      }

   /* check for a BASIC command, esp. to catch THEN or ELSE */

   if ( rval == OP_NULL )
      {
      rval = exp_iscmd( cbuf );
      }

   /* last: check for variable name, and assign it if there
      is not already one */

   if ( rval == OP_NULL )
      {
      rval = exp_isvn( nbuf );
      }

   /* return the value assigned (or OP_ERROR if none assigned) */

   if ( rval == OP_NULL )
      {
      return OP_ERROR;
      }
   else
      {
      return rval;
      }

   }

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

	FUNCTION:       exp_isnc()

	DESCRIPTION:    This function reads the expression to find
			if a numerical constant is present at this
			point.

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

#if ANSI_C
int
exp_isnc( char *expression )
#else
int
exp_isnc( expression )
   char *expression;
#endif
   {
   char tbuf[ MAXVARNAMESIZE + 1 ]; /* JBV */

   switch( expression[ 0 ] )
      {
      case '0':                 /* these will indicate a numerical constant */
      case '1':
      case '2':
      case '3':
      case '4':
      case '5':
      case '6':
      case '7':
      case '8':
      case '9':
      case '&':                 /* indicator for hex or octal constant */
         return CONST_NUMERICAL;
      case '+':
      case '-':

         /* if the previous stack level was a numerical value or a string,
            then this is certainly not one; return OP_NULL here
            and let the next function call to exp_isop() determine
            the (plus or minus) operator */

         if (  ( CURTASK exps[ CURTASK expsc - 1 ].operation == NUMBER )
            || ( CURTASK exps[ CURTASK expsc - 1 ].operation == VARIABLE )
            || ( CURTASK exps[ CURTASK expsc - 1 ].operation == CONST_STRING ) )
            {

#if INTENSIVE_DEBUG
            sprintf( bwb_ebuf, "in exp_isnc(): previous function is a number or string" );
            bwb_debug( bwb_ebuf );
#endif

            return OP_NULL;
            }

         /* similarly, if the previous stack level was a variable
            with a numerical value (not a string), then this level
            must be an operator, not a numerical constant */

         if ( ( CURTASK exps[ CURTASK expsc - 1 ].operation == VARIABLE )
            && ( CURTASK exps[ CURTASK expsc - 1 ].type != STRING ))
            {
            return OP_NULL;
            }

         /*--------------------------------------------------------*/
         /* Check for unary minus sign added by JBV.               */
         /* Could be prefixing a parenthetical expression or a     */
         /* variable name.                                         */
         /* But parentheses won't show up in expression (cbuf), so */
         /* just check for expression and variable name lengths.   */
         /*--------------------------------------------------------*/
         if (expression[0] == '-')
         {
             if (strlen(expression) == 1) return OP_NEGATION;
             exp_getvfname(&expression[1], tbuf);
             if (strlen(tbuf) != 0) return OP_NEGATION;
         }

         /* failing these tests, the argument must be a numerical
            constant preceded by a plus or minus sign */

         return CONST_NUMERICAL;

      default:
         return OP_NULL;
      }

   }

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

	FUNCTION:       exp_isop()

	DESCRIPTION:    This function reads the expression to find
			if a logical or mathematical operation is
			required at this point.

        This function presupposes that a numerical constant with
        affixed plus or minus sign has been ruled out.

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

#if ANSI_C
int
exp_isop( char *expression )
#else
int
exp_isop( expression )
   char *expression;
#endif
   {
   register int c;                              /* counter */

#if INTENSIVE_DEBUG
   sprintf( bwb_ebuf, "in exp_isop(): expression is <%s>", expression );
   bwb_debug( bwb_ebuf );
#endif

   /* compare the initial characters of the string with the table
      of operators */

   for ( c = 0; c < N_OPERATORS; ++c )
      {
      if ( strncmp( expression, exp_ops[ c ].symbol,
         (size_t) strlen( exp_ops[ c ].symbol ) ) == 0 )
         {

#if INTENSIVE_DEBUG
         sprintf( bwb_ebuf, "in exp_isop(): match <%s>, number <%d>.",
            exp_ops[ c ].symbol, c );
         bwb_debug( bwb_ebuf );
#endif

         return exp_ops[ c ].operation;
         }
      }

   /* search failed; return OP_NULL */

   return OP_NULL;

   }

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

	FUNCTION:       exp_iscmd()

	DESCRIPTION:    This function reads the expression to find
			if a BASIC command name is present; if so,
			it returns OP_TERMINATE to terminate expression
			parsing.  This is critical, for example, in
			parsing a conditional following IF where THEN,
			ELSE, and other BASIC commands may follow.

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

#if ANSI_C
int
exp_iscmd( char *expression )
#else
int
exp_iscmd( expression )
   char *expression;
#endif
   {
   register int n;

#if INTENSIVE_DEBUG
   sprintf( bwb_ebuf, "in exp_iscmd(): expression received <%s>",
      expression );
   bwb_debug( bwb_ebuf );
#endif

   /* first check for THEN or ELSE statements */

   if ( strcmp( expression, CMD_THEN ) == 0 )
      {
#if INTENSIVE_DEBUG
      sprintf( bwb_ebuf, "in exp_iscmd(): match found, <%s>",
	 expression );
      bwb_debug( bwb_ebuf );
#endif
      return OP_TERMINATE;
      }

#if STRUCT_CMDS
   if ( strcmp( expression, CMD_TO ) == 0 )
      {
#if INTENSIVE_DEBUG
      sprintf( bwb_ebuf, "in exp_iscmd(): match found, <%s>",
	 expression );
      bwb_debug( bwb_ebuf );
#endif
      return OP_TERMINATE;
      }
#endif

   if ( strcmp( expression, CMD_ELSE ) == 0 )
      {
#if INTENSIVE_DEBUG
      sprintf( bwb_ebuf, "in exp_iscmd(): match found, <%s>",
	 expression );
      bwb_debug( bwb_ebuf );
#endif
      return OP_TERMINATE;
      }

   /* run through the command table and search for a match */

   for ( n = 0; n < COMMANDS; ++n )
      {
      if ( strcmp( expression, bwb_cmdtable[ n ].name ) == 0 )
         {
#if INTENSIVE_DEBUG
         sprintf( bwb_ebuf, "in exp_iscmd(): match found, <%s>",
            expression );
         bwb_debug( bwb_ebuf );
#endif
         return OP_TERMINATE;
         }
#if INTENSIVE_DEBUG
      else
         {
         sprintf( bwb_ebuf, "in exp_iscmd(): No match, <%s> and <%s>; returns %d",
            expression, bwb_cmdtable[ n ].name,
            strcmp( expression, bwb_cmdtable[ n ].name ) );
         bwb_debug( bwb_ebuf );
         }
#endif
      }

   /* search failed, return NULL */

   return OP_NULL;

   }

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

        FUNCTION:   	exp_isufn()

        DESCRIPTION:  	This function reads the expression to find
        		if a user-defined function name is present
			at this point.

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

#if ANSI_C
int
exp_isufn( char *expression )
#else
int
exp_isufn( expression )
   char *expression;
#endif
   {
   struct fslte *f;
   char tbuf[ MAXVARNAMESIZE + 1 ];

   exp_getvfname( expression, tbuf );

   for ( f = CURTASK fslt_start.next; f != &CURTASK fslt_end; f = f->next )
      {
      if ( strcmp( f->name, tbuf ) == 0 )
         {
#if INTENSIVE_DEBUG
         sprintf( bwb_ebuf, "in exp_isufn(): found user function <%s>",
            tbuf );
         bwb_debug( bwb_ebuf );
#endif

         /* a user function name was found: but is it the local variable
            name for the user function? If so, return OP_NULL and the
            name will be read as a variable */

         if ( var_islocal( tbuf ) != NULL )
            {
            return OP_NULL;
            }
         else
            {

#if INTENSIVE_DEBUG
            sprintf( bwb_ebuf, "in exp_isufn(): found function <%s> not a local variable, EXEC level <%d>",
               tbuf, CURTASK exsc );
            bwb_debug( bwb_ebuf );
            getchar();
#endif

            return OP_USERFNC;
            }
         }
      }

   return OP_NULL;

   }

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

	FUNCTION:       exp_isfn()

	DESCRIPTION:    This function reads the expression to find
			if a function name is present at this point.

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

#if ANSI_C
int
exp_isfn( char *expression )
#else
int
exp_isfn( expression )
   char *expression;
#endif
   {

   /* Block out the call to exp_getvfname() if exp_isvn() is called
      after exp_isfn() */

   exp_getvfname( expression, CURTASK exps[ CURTASK expsc ].string );

#if INTENSIVE_DEBUG
   sprintf( bwb_ebuf, "in exp_isfn(): search for function <%s>",
      expression );
   bwb_debug( bwb_ebuf );
#endif

   if ( fnc_find( CURTASK exps[ CURTASK expsc ].string ) == NULL )
      {
#if INTENSIVE_DEBUG
      sprintf( bwb_ebuf, "in exp_isfn(): failed to find function <%s>",
         expression );
      bwb_debug( bwb_ebuf );
#endif
      return OP_NULL;
      }
   else
      {
#if INTENSIVE_DEBUG
      sprintf( bwb_ebuf, "in exp_isfn(): found function <%s>",
         expression );
      bwb_debug( bwb_ebuf );
#endif
      return FUNCTION;
      }

   }

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

	FUNCTION:       exp_isvn()

	DESCRIPTION:    This function reads the expression to find
			if a variable name at this point.

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

#if ANSI_C
int
exp_isvn( char *expression )
#else
int
exp_isvn( expression )
   char *expression;
#endif
   {

   /* Block out the call to exp_getvfname() if exp_isfn() is called
      after exp_isvn() */

   /* exp_getvfname( expression, CURTASK exps[ CURTASK expsc ].string ); */

   /* rule out null name */

   if ( strlen( CURTASK exps[ CURTASK expsc ].string ) == 0 )
      {
      return OP_NULL;
      }

#if INTENSIVE_DEBUG
   sprintf( bwb_ebuf, "in exp_isvn(): search for variable <%s>",
      CURTASK exps[ CURTASK expsc ].string );
   bwb_debug( bwb_ebuf );
#endif

   if ( var_find( CURTASK exps[ CURTASK expsc ].string ) == NULL )
      {
#if INTENSIVE_DEBUG
      sprintf( bwb_ebuf, "in exp_isvn(): failed to find variable <%s>",
         expression );
      bwb_debug( bwb_ebuf );
#endif
      return OP_NULL;
      }
   else
      {
#if INTENSIVE_DEBUG
      sprintf( bwb_ebuf, "in exp_isvn(): found variable <%s>",
         CURTASK exps[ CURTASK expsc ].string );
      bwb_debug( bwb_ebuf );
#endif
      return VARIABLE;
      }

   }

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

	FUNCTION:       exp_getvfname()

	DESCRIPTION:    This function reads the expression to find
			a variable or function name at this point.

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

#if ANSI_C
int
exp_getvfname( char *source, char *destination )
#else
int
exp_getvfname( source, destination )
   char *source;
   char *destination;
#endif
   {
   int s_pos, d_pos;                    /* source, destination positions */

#if INTENSIVE_DEBUG
   sprintf( bwb_ebuf, "in exp_getvfname(): source buffer <%s>", source );
   bwb_debug( bwb_ebuf );
#endif

   s_pos = d_pos = 0;
   destination[ 0 ] = '\0';
   while( source[ s_pos ] != '\0' )
      {

      /* all alphabetical characters are acceptable */

      if ( isalpha( source[ s_pos ] ) != 0 )

         {
         destination[ d_pos ] = source[ s_pos ];

         ++d_pos;
         ++s_pos;
         destination[ d_pos ] = '\0';
         }

      /* numerical characters are acceptable but not in the first position */

      else if (( isdigit( source[ s_pos ] ) != 0 ) && ( d_pos != 0 ))
         {
         destination[ d_pos ] = source[ s_pos ];
         ++d_pos;
         ++s_pos;
         destination[ d_pos ] = '\0';
         }

      /* other characters will have to be tried on their own merits */

      else
         {
         switch( source[ s_pos ] )
            {

            case '.':                           /* tolerated non-alphabetical characters */
            case '_':
               destination[ d_pos ] = source[ s_pos ];
               ++d_pos;
               ++s_pos;
               destination[ d_pos ] = '\0';
               break;

	    case STRING:                        /* terminating characters */
	    case '#':                           /* Microsoft-type double precision */
	    case '!':                           /* Microsoft-type single precision */

	       destination[ d_pos ] = source[ s_pos ];
               ++d_pos;
               ++s_pos;
               destination[ d_pos ] = '\0';

               return TRUE;

            case '(':				/* begin function/sub name */
               return TRUE;

            default:                            /* anything else is non-tolerated */
               return FALSE;
            }
         }
      }

#if INTENSIVE_DEBUG
   sprintf( bwb_ebuf, "in exp_getvfname(): found name <%s>", destination );
   bwb_debug( bwb_ebuf );
#endif

   return TRUE;                         /* exit after coming to the end */

   }

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

	FUNCTION:       exp_validarg()

	DESCRIPTION:    This function reads the expression to
			determine whether it is a valid argument (to be
			read recursively by bwb_exp() and passed to a
			function.

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

#if ANSI_C
int
exp_validarg( char *expression )
#else
int
exp_validarg( expression )
   char *expression;
#endif
   {
   register int c;

#if INTENSIVE_DEBUG
   sprintf( bwb_ebuf, "in exp_validarg(): expression <%s>.",
      expression );
   bwb_debug( bwb_ebuf );
#endif

   c = 0;
   while ( TRUE )
      {
      switch( expression[ c ] )
         {
         case ' ':
         case '\t':
            ++c;
            break;
         case '\0':
            return FALSE;
         default:
            return TRUE;
         }
      }

   }

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

        FUNCTION:   	exp_getnval()

	DESCRIPTION:    This function returns the numerical value
			contain in the expression-stack element
			pointed to by 'e'.

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

#if ANSI_C
bnumber
exp_getnval( struct exp_ese *e )
#else
bnumber
exp_getnval( e )
   struct exp_ese *e;
#endif
   {

   /* check for variable */

   if ( e->operation == VARIABLE )
      {
      switch( e->type )
         {
         case NUMBER:
            return (* var_findnval( e->xvar, e->array_pos ));
         default:
            bwb_error( err_mismatch );
            return (bnumber) 0.0;
         }
      }

   /* must be a numerical value */

   if ( e->operation != NUMBER )
      {
#if PROG_ERRORS
      sprintf( bwb_ebuf, "in exp_getnval(): operation <%d> is not a number",
         e->operation );
      bwb_error( bwb_ebuf );
#else
      bwb_error( err_syntax );
#endif
      return (bnumber) 0.0;
      }

   /* return specific values */

   switch( e->type )
      {
      case NUMBER:
         return e->nval;
      default:
#if PROG_ERRORS
         sprintf( bwb_ebuf, "in exp_getnval(): type is <%c>",
            e->type );
         bwb_error( bwb_ebuf );
#else
         bwb_error( err_syntax );
#endif
         return (bnumber) 0.0;
      }

   }

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

	FUNCTION:       exp_getsval()

	DESCRIPTION:    This function returns a pointer to the
			BASIC string structure pointed to by
			expression-stack element 'e'.

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

#if ANSI_C
bstring *
exp_getsval( struct exp_ese *e )
#else
bstring *
exp_getsval( e )
   struct exp_ese *e;
#endif
   {
   static bstring b;
#if TEST_BSTRING
   static int init = FALSE;

   if ( init == FALSE )
      {
      sprintf( b.name, "<exp_getsval() bstring>" );
      }
#endif

   b.rab = FALSE;

   /* return based on operation type */

   switch( e->operation )
      {
      case CONST_STRING:
      case OP_STRJOIN:
         return &( e->sval );
      case VARIABLE:
        switch( e->type )
            {
	    case STRING:
               return var_findsval( e->xvar, e->array_pos );
            case NUMBER:
               sprintf( bwb_ebuf, "%lf ", (double) exp_getnval( e ) );
               str_ctob( &b, bwb_ebuf );
               return &b;
            default:
#if PROG_ERRORS
               sprintf( bwb_ebuf, "in exp_getsval(): type <%c> inappropriate for NUMBER",
                  e->type );
               bwb_error( bwb_ebuf );
#else
               bwb_error( err_syntax );
#endif
               return NULL;
            }
	 break;

      case NUMBER:
        switch( e->type )
            {
	    case NUMBER:
               sprintf( bwb_ebuf, "%lf ", (double) exp_getnval( e ) );
               str_ctob( &b, bwb_ebuf );
               return &b;
            default:
#if PROG_ERRORS
               sprintf( bwb_ebuf, "in exp_getsval(): type <%c> inappropriate for NUMBER",
                  e->type );
               bwb_error( bwb_ebuf );
#else
               bwb_error( err_syntax );
#endif
               return NULL;
            }
	 break;
      default:
#if PROG_ERRORS
         sprintf( bwb_ebuf, "in exp_getsval(): operation <%d> inappropriate",
            e->operation );
         bwb_error( bwb_ebuf );
#else
         bwb_error( err_syntax );
#endif
         return NULL;
      }

   /* this point may not be reached */

   return NULL;

   }

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

	FUNCTION:       inc_esc()

	DESCRIPTION:    This function increments the expression
			stack counter.

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

#if ANSI_C
int
inc_esc( void )
#else
int
inc_esc()
#endif
   {

#if INTENSIVE_DEBUG
   sprintf( bwb_ebuf, "in inc_esc(): prev level <%d>",
      CURTASK expsc );
   bwb_debug ( bwb_ebuf );
#endif

   ++CURTASK expsc;
   if ( CURTASK expsc >= ESTACKSIZE )
      {
      --CURTASK expsc;
#if PROG_ERRORS
      sprintf( bwb_ebuf, "in inc_esc(): Maximum expression stack exceeded <%d>",
         CURTASK expsc );
      bwb_error( bwb_ebuf );
#else
      bwb_error( err_overflow );
#endif
      return OP_NULL;
      }

#if INTENSIVE_DEBUG
   sprintf( CURTASK exps[ CURTASK expsc ].string, "New Expression Stack Level %d", CURTASK expsc );
#endif

   CURTASK exps[ CURTASK expsc ].type = NUMBER;
   CURTASK exps[ CURTASK expsc ].operation = OP_NULL;
   CURTASK exps[ CURTASK expsc ].pos_adv = 0;

   return TRUE;
   }

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

	FUNCTION:       dec_esc()

	DESCRIPTION:    This function decrements the expression
			stack counter.

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

#if ANSI_C
int
dec_esc( void )
#else
int
dec_esc()
#endif
   {
   --CURTASK expsc;
   if ( CURTASK expsc < 0 )
      {
      CURTASK expsc = 0;
#if PROG_ERRORS
      sprintf( bwb_ebuf, "in dec_esc(): Expression stack counter < 0." );
      bwb_error( bwb_ebuf );
#else
      bwb_error( err_overflow );
#endif
      return OP_NULL;
      }

   return TRUE;
   }

