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

        bwb_ops.c       Expression Parsing Operations
                        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"

/* declarations for functions visible in this file only */

#if ANSI_C
static int op_oplevel( int level );
static int op_add( int level, int precision );
static int op_subtract( int level, int precision );
static int op_multiply( int level, int precision );
static int op_divide( int level, int precision );
static int op_assign( int level, int precision );
static int op_equals( int level, int precision );
static int op_lessthan( int level, int precision );
static int op_greaterthan( int level, int precision );
static int op_lteq( int level, int precision );
static int op_gteq( int level, int precision );
static int op_notequal( int level, int precision );
static int op_modulus( int level, int precision );
static int op_exponent( int level, int precision );
static int op_intdiv( int level, int precision );
static int op_or( int level, int precision );
static int op_and( int level, int precision );
static int op_not( int level, int precision );
static int op_xor( int level, int precision );
static int op_negation( int level, int precision ); /* JBV */
static int op_islevelstr( int level );
static int op_getprecision( int level );
static int op_isoperator( int operation );
static int op_pulldown( int how_far );
#else
static int op_oplevel();
static int op_add();
static int op_subtract();
static int op_multiply();
static int op_divide();
static int op_assign();
static int op_equals();
static int op_lessthan();
static int op_greaterthan();
static int op_lteq();
static int op_gteq();
static int op_notequal();
static int op_modulus();
static int op_exponent();
static int op_intdiv();
static int op_or();
static int op_and();
static int op_not();
static int op_xor();
static int op_negation(); /* JBV */
static int op_islevelstr();
static int op_getprecision();
static int op_isoperator();
static int op_pulldown();
#endif				/* ANSI_C for prototypes */

static int op_level;

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

	FUNCTION:       exp_operation()

	DESCRIPTION:    This function performs whatever operations
			are necessary at the end of function bwb_exp()
			(i.e., the end of the parsing of an expression;
			see file bwb_exp.c).

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

#if ANSI_C
int
exp_operation( int entry_level )
#else
int
exp_operation( entry_level )
   int entry_level;
#endif
   {
   register int precedence;
   int operator;

#if INTENSIVE_DEBUG
   sprintf( bwb_ebuf, "in exp_operation(): entered function." );
   bwb_debug( bwb_ebuf );
#endif

   /* cycle through all levels of precedence and perform required
      operations */

   for ( precedence = 0; precedence <= MAX_PRECEDENCE; ++precedence )
      {

      /* Operation loop: cycle through every level above entry level
         and perform required operations as needed */

      op_level = entry_level + 1;
      while( ( op_level < CURTASK expsc )
         && ( op_isoperator( CURTASK exps[ op_level ].operation ) == FALSE ))
         {
         ++op_level;
         }

      while ( ( op_level > entry_level ) && ( op_level < CURTASK expsc ) )
         {

         /* see if the operation at this level is an operator with the
            appropriate precedence level by running through the table
            of operators */

         for ( operator = 0; operator < N_OPERATORS; ++operator )
            {

            if ( exp_ops[ operator ].operation == CURTASK exps[ op_level ].operation )
               {

               /* check for appropriate level of precedence */

               if ( exp_ops[ operator ].precedence == precedence )
                  {

#if INTENSIVE_DEBUG
                  sprintf( bwb_ebuf, "in exp_operation(): level <%d> operation <%d>",
                     op_level, CURTASK exps[ op_level ].operation );
                  bwb_debug( bwb_ebuf );
#endif

                  op_oplevel( op_level );     /* perform the operation */

                  }
               }
            }

         /* advance level if appropriate; one must check, however, since
            the op_oplevel() function may have decremented CURTASK expsc */

         if ( op_level < CURTASK expsc )
            {
            ++op_level;

#if INTENSIVE_DEBUG
            sprintf( bwb_ebuf, "in exp_operation() first increment op_level to <%d>",
               op_level );
            bwb_debug( bwb_ebuf );
#endif

            while ( ( op_isoperator( CURTASK exps [ op_level ].operation ) == FALSE )
               && ( op_level < CURTASK expsc ) )
               {
               ++op_level;

#if INTENSIVE_DEBUG
               sprintf( bwb_ebuf, "in exp_operation() further increment op_level to <%d>",
                  op_level );
               bwb_debug( bwb_ebuf );
#endif

               }
            }                           /* end of increment of op_level */

         }                              /* end of for loop for stack levels */

      }                                 /* end of for loop for precedence levels */

   return TRUE;

   }                                    /* end of function exp_operation() */


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

	FUNCTION:       op_oplevel()

	DESCRIPTION:    This function performs a specific operation
			at a specific level as the expression parser
			resolves its arguments.

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

#if ANSI_C
static int
op_oplevel( int level )
#else
static int
op_oplevel( level )
   int level;
#endif
   {
   int precision;

   /* set the precision */

   if ( ( precision = op_getprecision( level ) ) == OP_ERROR )
      {
#if PROG_ERRORS
      sprintf( bwb_ebuf, "exp_operation(): failed to set precision." );
      bwb_error( bwb_ebuf );
#else
      bwb_error( err_mismatch );			/*** ??? ***/
#endif
      op_pulldown( 2 );
      }

   /* precision is set correctly */

   else
      {
#if INTENSIVE_DEBUG
      sprintf( bwb_ebuf, "in op_oplevel(): level <%d>, precision <%c>",
         level, precision );
      bwb_debug( bwb_ebuf );
#endif

      switch ( CURTASK exps[ level ].operation )
         {
         case OP_ADD:
            op_add( level, precision );
            break;

         case OP_SUBTRACT:
            op_subtract( level, precision );
            break;

         case OP_MULTIPLY:
            op_multiply( level, precision );
            break;

         case OP_DIVIDE:
            op_divide( level, precision );
            break;

         case OP_ASSIGN:
            op_assign( level, precision );
            break;

         case OP_EQUALS:
            op_equals( level, precision );
            break;

         case OP_LESSTHAN:
            op_lessthan( level, precision );
            break;

         case OP_GREATERTHAN:
            op_greaterthan( level, precision );
            break;

         case OP_LTEQ:
            op_lteq( level, precision );
            break;

         case OP_GTEQ:
            op_gteq( level, precision );
            break;

         case OP_NOTEQUAL:
            op_notequal( level, precision );
            break;

         case OP_MODULUS:
            op_modulus( level, precision );
            break;

         case OP_INTDIVISION:
            op_intdiv( level, precision );
            break;

         case OP_OR:
            op_or( level, precision );
            break;

         case OP_AND:
            op_and( level, precision );
            break;

         case OP_NOT:
            op_not( level, precision );
            break;

         case OP_XOR:
            op_xor( level, precision );
            break;

         case OP_EXPONENT:
            op_exponent( level, precision );
            break;

         case OP_NEGATION: /* JBV */
            op_negation( level, precision );
            break;

         default:
#if PROG_ERRORS
            sprintf( bwb_ebuf, "PROGRAMMING ERROR: operator <%d> not (yet) supported.", CURTASK exps[ level ].operation );
            op_pulldown( 2 );
            bwb_error( bwb_ebuf );
#else
            bwb_error( err_syntax );
#endif
            break;
         }                              /* end of case statement for operators */
      }                                 /* end of else statement, precision set */

   return TRUE;

   }                                    /* end of function op_oplevel() */

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

	FUNCTION:       op_isoperator()

	DESCRIPTION:    This function detects whether its argument
			is an operator.

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

#if ANSI_C
static int
op_isoperator( int operation )
#else
static int
op_isoperator( operation )
   int operation;
#endif
   {
   register int c;

   for( c = 0; c < N_OPERATORS; ++c )
      {
      if ( operation == exp_ops[ c ].operation )
         {

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

         return TRUE;
         }
      }

   /* test failed; return FALSE */

#if INTENSIVE_DEBUG
   sprintf( bwb_ebuf, "in op_isoperator(): no match found for operation <%d>",
      operation );
   bwb_debug( bwb_ebuf );
#endif

   return FALSE;

   }

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

        FUNCTION:   	op_add()

        DESCRIPTION:  	This function adds two numbers or
        		concatenates two strings.

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

#if ANSI_C
static int
op_add( int level, int precision )
#else
static int
op_add( level, precision )
   int level;
   int precision;
#endif
   {
   int error_condition;
   static bstring b; /* JBV */

   error_condition = FALSE;
   b.rab = FALSE; /* JBV */

   switch( precision )
      {
      case STRING:

         /* both sides of the operation should be strings for
            string addition; if not, report an error */

         if (  ( op_islevelstr( level - 1 ) != TRUE )
            || ( op_islevelstr( level + 1 ) != TRUE ) )
            {
#if PROG_ERRORS
            sprintf( bwb_ebuf, "in op_add(): Type mismatch in string addition." );
            bwb_error( bwb_ebuf );
#else
            bwb_error( err_mismatch );
#endif
            error_condition = TRUE;
            }

         /* concatenate the two strings */

         if ( error_condition == FALSE )
            {

#if INTENSIVE_DEBUG
            sprintf( bwb_ebuf, "in op_add(): try exp_getsval(), level <%d> op <%d> type <%c>:",
               level - 1, CURTASK exps[ level - 1 ].operation, CURTASK exps[ level - 1 ].type );
            bwb_debug( bwb_ebuf );
            exp_getsval( &( CURTASK exps[ level - 1 ] ));
            sprintf( bwb_ebuf, "in op_add(): try exp_getsval(), level <%d> op <%d> type <%c>:",
               level + 1, CURTASK exps[ level + 1 ].operation, CURTASK exps[ level + 1 ].type );
            bwb_debug( bwb_ebuf );
            exp_getsval( &( CURTASK exps[ level + 1 ] ));
            sprintf( bwb_ebuf, "in op_add(): string addition, exp_getsval()s completed" );
            bwb_debug( bwb_ebuf );
#endif

            /* Removed by JBV (incomplete, modifies wrong string variable!) */
            /* str_cat( exp_getsval( &( CURTASK exps[ level - 1 ] ) ), 
               exp_getsval( &( CURTASK exps[ level + 1 ] ) ) ); */

            /* Added by JBV */
            str_btob( &b, exp_getsval( &( CURTASK exps[ level - 1 ] ) ) );
            str_cat( &b, exp_getsval( &( CURTASK exps[ level + 1 ] ) ) );
            str_btob( &( CURTASK exps[ level - 1 ].sval ), &b );
            CURTASK exps[ level - 1 ].operation = CONST_STRING;

#if INTENSIVE_DEBUG
            sprintf( bwb_ebuf, "in op_add(): str_cat() returns <%d>-byte string to level <%d>",
               exp_getsval( &( CURTASK exps[ level - 1 ] ) )->length, level - 1 );
            bwb_debug( bwb_ebuf );
#endif
            }

         break;

      case NUMBER:
         CURTASK exps[ level - 1 ].nval
            = exp_getnval( &( CURTASK exps[ level - 1 ] ))
            + exp_getnval( &( CURTASK exps[ level + 1 ] ));
         CURTASK exps[ level - 1 ].operation = NUMBER;
         break;

      }

   /* set variable to requested precision */

   CURTASK exps[ level - 1 ].type = (char) precision;

#if INTENSIVE_DEBUG
   sprintf( bwb_ebuf, "in op_add() returns with operation <%d> type <%c>",
      CURTASK exps[ level - 1 ].operation, CURTASK exps[ level - 1 ].type );
   bwb_debug( bwb_ebuf );
#endif

   /* decrement the stack twice */

   op_pulldown( 2 );

   return TRUE;

   }

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

	FUNCTION:       op_subtract()

	DESCRIPTION:    This function subtracts the number on
			the left from the number on the right.

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

#if ANSI_C
static int
op_subtract( int level, int precision )
#else
static int
op_subtract( level, precision )
   int level;
   int precision;
#endif
   {

   switch( precision )
      {
      case STRING:

         /* both sides of the operation should be numbers for
            string addition; if not, report an error */

#if PROG_ERRORS
         sprintf( bwb_ebuf, "Strings cannot be subtracted." );
         bwb_error( bwb_ebuf );
#else
         bwb_error( err_mismatch );
#endif

         break;

      case NUMBER:
         CURTASK exps[ level - 1 ].nval
            = exp_getnval( &( CURTASK exps[ level - 1 ] ))
            - exp_getnval( &( CURTASK exps[ level + 1 ] ));
         break;

      }

   /* set variable to requested precision */

   CURTASK exps[ level - 1 ].type = (char) precision;
   CURTASK exps[ level - 1 ].operation = NUMBER;

   /* decrement the stack twice */

   op_pulldown( 2 );

   return TRUE;

   }

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

	FUNCTION:       op_multiply()

	DESCRIPTION:    This function multiplies the number on
			the left by the number on the right.

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

#if ANSI_C
static int
op_multiply( int level, int precision )
#else
static int
op_multiply( level, precision )
   int level;
   int precision;
#endif
   {

   switch( precision )
      {
      case STRING:

         /* both sides of the operation should be numbers for
            string addition; if not, report an error */

#if PROG_ERRORS
         sprintf( bwb_ebuf, "Strings cannot be multiplied." );
         bwb_error( bwb_ebuf );
#else
         bwb_error( err_mismatch );
#endif

         break;

      case NUMBER:
         CURTASK exps[ level - 1 ].nval
            = exp_getnval( &( CURTASK exps[ level - 1 ] ))
            * exp_getnval( &( CURTASK exps[ level + 1 ] ));
         break;

      }

   /* set variable to requested precision */

   CURTASK exps[ level - 1 ].type = (char) precision;
   CURTASK exps[ level - 1 ].operation = NUMBER;

   /* decrement the stack twice */

   op_pulldown( 2 );

   return TRUE;

   }

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

	FUNCTION:       op_divide()

	DESCRIPTION:    This function divides the number on
			the left by the number on the right.

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

#if ANSI_C
static int
op_divide( int level, int precision )
#else
static int
op_divide( level, precision )
   int level;
   int precision;
#endif
   {

   switch( precision )
      {
      case STRING:

         /* both sides of the operation should be numbers for
            division; if not, report an error */

#if PROG_ERRORS
         sprintf( bwb_ebuf, "Strings cannot be divided." );
         bwb_error( bwb_ebuf );
#else
         bwb_error( err_mismatch );
#endif

         break;

      case NUMBER:
         if ( exp_getnval( &( CURTASK exps[ level + 1 ] ))
            == (bnumber) 0 )
            {
            CURTASK exps[ level - 1 ].nval = (bnumber) -1.0;
            op_pulldown( 2 );
#if PROG_ERRORS
            sprintf( bwb_ebuf, "Divide by 0." );
            bwb_error( bwb_ebuf );
#else
            bwb_error( err_dbz );
#endif
            return FALSE;
            }
         CURTASK exps[ level - 1 ].nval
            = exp_getnval( &( CURTASK exps[ level - 1 ] ))
            / exp_getnval( &( CURTASK exps[ level + 1 ] ));
         break;
      }

   /* set variable to requested precision */

   CURTASK exps[ level - 1 ].type = (char) precision;
   CURTASK exps[ level - 1 ].operation = NUMBER;

   /* decrement the stack twice */

   op_pulldown( 2 );

   return TRUE;

   }

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

	FUNCTION:       op_assign()

	DESCRIPTION:    This function assigns the value in the
			right hand side to the variable in the
			left hand side.

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

#if ANSI_C
static int
op_assign( int level, int precision )
#else
static int
op_assign( level, precision )
   int level;
   int precision;
#endif
   {

   /* Make sure the position one level below is a variable */

   if ( CURTASK exps[ level - 1 ].operation != VARIABLE )
      {
      op_pulldown( 2 );
#if PROG_ERRORS
      sprintf( bwb_ebuf, "in op_assign(): Assignment must be to variable: level -1 <%d> op <%d>",
         level - 1, CURTASK exps[ level - 1 ].operation );
      bwb_error( bwb_ebuf );
#else
      bwb_error( err_syntax );
#endif
      return FALSE;
      }

#if INTENSIVE_DEBUG
   sprintf( bwb_ebuf, "in op_assign(): entered function level <%d>",
      level );
   bwb_debug( bwb_ebuf );
#endif

  /* if the assignment is numerical, then the precision should be set
     to that of the variable on the left-hand side of the assignment */

   if ( precision != STRING )
      {
      precision = (int) CURTASK exps[ level - 1 ].type;
      }

   switch( precision )
      {
      case STRING:

#if INTENSIVE_DEBUG
         sprintf( bwb_ebuf, "in op_assign(): try exp_getsval(), level <%d> op <%d> type <%c>:",
            level - 1, CURTASK exps[ level - 1 ].operation, CURTASK exps[ level - 1 ].type );
         bwb_debug( bwb_ebuf );
         exp_getsval( &( CURTASK exps[ level - 1 ] ));
         sprintf( bwb_ebuf, "in op_assign(): try exp_getsval(), level <%d> op <%d> type <%c>:",
            level + 1, CURTASK exps[ level + 1 ].operation, CURTASK exps[ level + 1 ].type );
         bwb_debug( bwb_ebuf );
         exp_getsval( &( CURTASK exps[ level + 1 ] ));
         sprintf( bwb_ebuf, "in op_assign(): string addition, exp_getsval()s completed" );
         bwb_debug( bwb_ebuf );
#endif

         str_btob( exp_getsval( &( CURTASK exps[ level - 1 ] )),
                   exp_getsval( &( CURTASK exps[ level + 1 ] )) );
         break;

      case NUMBER:
         * var_findnval( CURTASK exps[ level - 1 ].xvar, 
            CURTASK exps[ level - 1 ].array_pos ) =
            CURTASK exps[ level - 1 ].nval = 
            exp_getnval( &( CURTASK exps[ level + 1 ] ) );
         break;

      default:
#if PROG_ERRORS
         sprintf( bwb_ebuf, "in op_assign(): Variable before assignment operator has unidentified type." );
         bwb_error( bwb_ebuf );
#else
         bwb_error( err_mismatch );
#endif
         return FALSE;

      }

   /* set variable to requested precision */

   CURTASK exps[ level - 1 ].type = (char) precision;

   /* decrement the stack twice */

   op_pulldown( 2 );

   return TRUE;

   }

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

	FUNCTION:       op_equals()

	DESCRIPTION:    This function compares two values and
			returns an integer value: TRUE if they are
			the same and FALSE if they are not.

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

#if ANSI_C
static int
op_equals( int level, int precision )
#else
static int
op_equals( level, precision )
   int level;
   int precision;
#endif
   {
   int error_condition;
   static bstring b;
   bstring *bp;

   error_condition = FALSE;
   b.rab = FALSE;

   switch( precision )
      {
      case STRING:

         /* both sides of the operation should be strings for
            string addition; if not, report an error */

         if (  ( op_islevelstr( level - 1 ) != TRUE )
            || ( op_islevelstr( level + 1 ) != TRUE ) )
            {
#if PROG_ERRORS
            sprintf( bwb_ebuf, "in op_equals(): Type mismatch in string comparison." );
            bwb_error( bwb_ebuf );
#else
            bwb_error( err_mismatch );
#endif
            error_condition = TRUE;
            }

         /* compare the two strings */

         if ( error_condition == FALSE )
            {
            bp = exp_getsval( &( CURTASK exps[ level - 1 ] ));
#if OLDWAY
	    b.length = bp->length;
	    b.sbuffer = bp->sbuffer;
#endif
	    str_btob( &b, bp );

            if ( str_cmp( &b,
               exp_getsval( &( CURTASK exps[ level + 1 ] )) ) == 0 )
               {
               CURTASK exps[ level - 1 ].nval = (bnumber) TRUE;
               }
            else
               {
               CURTASK exps[ level - 1 ].nval = (bnumber) FALSE;
               }
            }
         break;

      case NUMBER:
         if ( exp_getnval( &( CURTASK exps[ level - 1 ] ))
            == exp_getnval( &( CURTASK exps[ level + 1 ] )) )
            {
            CURTASK exps[ level - 1 ].nval = (bnumber) TRUE;
            }
         else
            {
            CURTASK exps[ level - 1 ].nval = (bnumber) FALSE;
            }
         break;

      }

   /* set variable to integer and operation to NUMBER:
      this must be done at the end, since at the beginning it
      might cause op_islevelstr() to return a false error */

   CURTASK exps[ level - 1 ].type = NUMBER;
   CURTASK exps[ level - 1 ].operation = NUMBER;

   /* decrement the stack */

   op_pulldown( 2 );

   return TRUE;

   }

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

	FUNCTION:       op_lessthan()

	DESCRIPTION:    This function compares two values and
			returns an integer value: TRUE if the
			left hand value is less than the right,
			and FALSE if it is not.

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

#if ANSI_C
static int
op_lessthan( int level, int precision )
#else
static int
op_lessthan( level, precision )
   int level;
   int precision;
#endif
   {
   int error_condition;

   error_condition = FALSE;

   switch( precision )
      {
      case STRING:

         /* both sides of the operation should be numbers for
            string addition; if not, report an error */

         if (  ( op_islevelstr( level - 1 ) != TRUE )
            || ( op_islevelstr( level + 1 ) != TRUE ) )
            {
#if PROG_ERRORS
            sprintf( bwb_ebuf, "Type mismatch in string comparison." );
            bwb_error( bwb_ebuf );
#else
            bwb_error( err_mismatch );
#endif
            error_condition = TRUE;
            }

         /* compare the two strings */

         if ( error_condition == FALSE )
            {
            if ( str_cmp( exp_getsval( &( CURTASK exps[ level - 1 ] )),
               exp_getsval( &( CURTASK exps[ level + 1 ] )) ) < 0 )
               {
               CURTASK exps[ level - 1 ].nval = (bnumber) TRUE;
               }
            else
               {
               CURTASK exps[ level - 1 ].nval = (bnumber) FALSE;
               }
            }
         break;

      case NUMBER:
         if ( exp_getnval( &( CURTASK exps[ level - 1 ] ))
            < exp_getnval( &( CURTASK exps[ level + 1 ] )) )
            {
            CURTASK exps[ level - 1 ].nval = (bnumber) TRUE;
            }
         else
            {
            CURTASK exps[ level - 1 ].nval = (bnumber) FALSE;
            }
         break;

      }

   /* set variable to integer and operation to NUMBER:
      this must be done at the end, since at the beginning it
      might cause op_islevelstr() to return a false error */

   CURTASK exps[ level - 1 ].type = NUMBER;
   CURTASK exps[ level - 1 ].operation = NUMBER;

   /* decrement the stack */

   op_pulldown( 2 );

   return TRUE;

   }

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

	FUNCTION:       op_greaterthan()

	DESCRIPTION:    This function compares two values and
			returns an integer value: TRUE if the
			left hand value is greater than the right,
			and FALSE if it is not.

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

#if ANSI_C
static int
op_greaterthan( int level, int precision )
#else
static int
op_greaterthan( level, precision )
   int level;
   int precision;
#endif
   {
   int error_condition;

   error_condition = FALSE;

   switch( precision )
      {
      case STRING:

         /* both sides of the operation should be numbers for
            string addition; if not, report an error */

         if (  ( op_islevelstr( level - 1 ) != TRUE )
            || ( op_islevelstr( level + 1 ) != TRUE ) )
            {
#if PROG_ERRORS
            sprintf( bwb_ebuf, "Type mismatch in string comparison." );
            bwb_error( bwb_ebuf );
#else
            bwb_error( err_mismatch );
#endif
            error_condition = TRUE;
            }

         /* compare the two strings */

         if ( error_condition == FALSE )
            {
            if ( str_cmp( exp_getsval( &( CURTASK exps[ level - 1 ] )),
               exp_getsval( &( CURTASK exps[ level + 1 ] )) ) > 0 )
               {
               CURTASK exps[ level - 1 ].nval = (bnumber) TRUE;
               }
            else
               {
               CURTASK exps[ level - 1 ].nval = (bnumber) FALSE;
               }
            }
         break;

      case NUMBER:
         if ( exp_getnval( &( CURTASK exps[ level - 1 ] ))
            > exp_getnval( &( CURTASK exps[ level + 1 ] )) )
            {
            CURTASK exps[ level - 1 ].nval = (bnumber) TRUE;
            }
         else
            {
            CURTASK exps[ level - 1 ].nval = (bnumber) FALSE;
            }
         break;

      }

   /* set variable to integer and operation to NUMBER:
      this must be done at the end, since at the beginning it
      might cause op_islevelstr() to return a false error */

   CURTASK exps[ level - 1 ].type = NUMBER;
   CURTASK exps[ level - 1 ].operation = NUMBER;

   /* decrement the stack */

   op_pulldown( 2 );

   return TRUE;

   }

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

	FUNCTION:       op_lteq()

	DESCRIPTION:    This function compares two values and
			returns an integer value: TRUE if the
			left hand value is less than or equal
			to the right, and FALSE if it is not.

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

#if ANSI_C
static int
op_lteq( int level, int precision )
#else
static int
op_lteq( level, precision )
   int level;
   int precision;
#endif
   {
   int error_condition;

   error_condition = FALSE;

   switch( precision )
      {
      case STRING:

         /* both sides of the operation should be numbers for
            string addition; if not, report an error */

         if (  ( op_islevelstr( level - 1 ) != TRUE )
            || ( op_islevelstr( level + 1 ) != TRUE ) )
            {
#if PROG_ERRORS
            sprintf( bwb_ebuf, "Type mismatch in string comparison." );
            bwb_error( bwb_ebuf );
#else
            bwb_error( err_mismatch );
#endif
            error_condition = TRUE;
            }

         /* compare the two strings */

         if ( error_condition == FALSE )
            {
            if ( str_cmp( exp_getsval( &( CURTASK exps[ level - 1 ] )),
               exp_getsval( &( CURTASK exps[ level + 1 ] )) ) <= 0 )
               {
               CURTASK exps[ level - 1 ].nval = (bnumber) TRUE;
               }
            else
               {
               CURTASK exps[ level - 1 ].nval = (bnumber) FALSE;
               }
            }
         break;

      case NUMBER:
         if ( exp_getnval( &( CURTASK exps[ level - 1 ] ))
            <= exp_getnval( &( CURTASK exps[ level + 1 ] )) )
            {
            CURTASK exps[ level - 1 ].nval = (bnumber) TRUE;
            }
         else
            {
            CURTASK exps[ level - 1 ].nval = (bnumber) FALSE;
            }
         break;

      }

   /* set variable to integer and operation to NUMBER:
      this must be done at the end, since at the beginning it
      might cause op_islevelstr() to return a false error */

   CURTASK exps[ level - 1 ].type = NUMBER;
   CURTASK exps[ level - 1 ].operation = NUMBER;

   /* decrement the stack */

   op_pulldown( 2 );

   return TRUE;

   }

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

	FUNCTION:       op_gteq()

	DESCRIPTION:    This function compares two values and
			returns an integer value: TRUE if the
			left hand value is greater than or equal
			to the right, and FALSE if it is not.

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

#if ANSI_C
static int
op_gteq( int level, int precision )
#else
static int
op_gteq( level, precision )
   int level;
   int precision;
#endif
   {
   int error_condition;

   error_condition = FALSE;

   switch( precision )
      {
      case STRING:

         /* both sides of the operation should be numbers for
            string addition; if not, report an error */

         if (  ( op_islevelstr( level - 1 ) != TRUE )
            || ( op_islevelstr( level + 1 ) != TRUE ) )
            {
#if PROG_ERRORS
            sprintf( bwb_ebuf, "Type mismatch in string comparison." );
            bwb_error( bwb_ebuf );
#else
            bwb_error( err_mismatch );
#endif
            error_condition = TRUE;
            }

         /* compare the two strings */

         if ( error_condition == FALSE )
            {
            if ( str_cmp( exp_getsval( &( CURTASK exps[ level - 1 ] )),
               exp_getsval( &( CURTASK exps[ level + 1 ] )) ) >= 0 )
               {
               CURTASK exps[ level - 1 ].nval = (bnumber) TRUE;
               }
            else
               {
               CURTASK exps[ level - 1 ].nval = (bnumber) FALSE;
               }
            }
         break;

      case NUMBER:
         if ( exp_getnval( &( CURTASK exps[ level - 1 ] ))
            >= exp_getnval( &( CURTASK exps[ level + 1 ] )) )
            {
            CURTASK exps[ level - 1 ].nval = (bnumber) TRUE;
            }
         else
            {
            CURTASK exps[ level - 1 ].nval = (bnumber) FALSE;
            }
         break;

      }

   /* set variable to integer and operation to NUMBER:
      this must be done at the end, since at the beginning it
      might cause op_islevelstr() to return a false error */

   CURTASK exps[ level - 1 ].type = NUMBER;
   CURTASK exps[ level - 1 ].operation = NUMBER;

   /* decrement the stack */

   op_pulldown( 2 );

   return TRUE;

   }

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

	FUNCTION:       op_notequal()

	DESCRIPTION:    This function compares two values and
			returns an integer value: TRUE if they
			are not the same and FALSE if they are.

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

#if ANSI_C
static int
op_notequal( int level, int precision )
#else
static int
op_notequal( level, precision )
   int level;
   int precision;
#endif
   {
   int error_condition;

   error_condition = FALSE;

   switch( precision )
      {
      case STRING:

         /* both sides of the operation should be numbers for
            string addition; if not, report an error */

         if (  ( op_islevelstr( level - 1 ) != TRUE )
            || ( op_islevelstr( level + 1 ) != TRUE ) )
            {
#if PROG_ERRORS
            sprintf( bwb_ebuf, "Type mismatch in string comparison." );
            bwb_error( bwb_ebuf );
#else
            bwb_error( err_mismatch );
#endif
            error_condition = TRUE;
            }

         /* compare the two strings */

         if ( error_condition == FALSE )

            {
            if ( str_cmp( exp_getsval( &( CURTASK exps[ level - 1 ] )),
               exp_getsval( &( CURTASK exps[ level + 1 ] )) ) != 0 )
               {
               CURTASK exps[ level - 1 ].nval = (bnumber) TRUE;
               }
            else
               {
               CURTASK exps[ level - 1 ].nval = (bnumber) FALSE;
               }
            }
         break;

      case NUMBER:
         if ( exp_getnval( &( CURTASK exps[ level - 1 ] ))
            != exp_getnval( &( CURTASK exps[ level + 1 ] )) )
            {
            CURTASK exps[ level - 1 ].nval = (bnumber) TRUE;
            }
         else
            {
            CURTASK exps[ level - 1 ].nval = (bnumber) FALSE;
            }
         break;

      }

   /* set variable to integer and operation to NUMBER:
      this must be done at the end, since at the beginning it
      might cause op_islevelstr() to return a false error */

   CURTASK exps[ level - 1 ].type = NUMBER;
   CURTASK exps[ level - 1 ].operation = NUMBER;

   /* decrement the stack */

   op_pulldown( 2 );

   return TRUE;

   }

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

	FUNCTION:       op_modulus()

	DESCRIPTION:    This function divides the number on
			the left by the number on the right
			and returns the remainder.

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

#if ANSI_C
static int
op_modulus( int level, int precision )
#else
static int
op_modulus( level, precision )
   int level;
   int precision;
#endif
   {
   static double iportion;

   switch( precision )
      {
      case STRING:

         /* both sides of the operation should be numbers for
            string addition; if not, report an error */

#if PROG_ERRORS
         sprintf( bwb_ebuf, "Strings cannot be divided." );
         bwb_error( bwb_ebuf );
#else
         bwb_error( err_syntax );
#endif

         break;

      case NUMBER:
         if ( exp_getnval( &( CURTASK exps[ level + 1 ] ))
            == (bnumber) 0 )
            {
            CURTASK exps[ level - 1 ].nval = (bnumber) -1;
            op_pulldown( 2 );
#if PROG_ERRORS
            sprintf( bwb_ebuf, "Divide by 0." );
            bwb_error( bwb_ebuf );
#else
            bwb_error( err_dbz );
#endif
            return FALSE;
            }
         CURTASK exps[ level ].nval
            = exp_getnval( &( CURTASK exps[ level - 1 ] ))
            / exp_getnval( &( CURTASK exps[ level + 1 ] ));
         modf( (double) CURTASK exps[ level ].nval, &iportion );
         CURTASK exps[ level - 1 ].nval
            = exp_getnval( &( CURTASK exps[ level - 1 ] ))
            - ( exp_getnval( &( CURTASK exps[ level + 1 ] ))
            * iportion );
         break;

      }

   /* set variable to requested precision */

   CURTASK exps[ level - 1 ].type = (char) precision;
   CURTASK exps[ level - 1 ].operation = NUMBER;

   /* decrement the stack twice */

   op_pulldown( 2 );

   return TRUE;

   }

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

	FUNCTION:       op_exponent()

	DESCRIPTION:    This function finds the exponential value
			of a number (on the left) to the power
			indicated on the right-hand side.

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

#if ANSI_C
static int
op_exponent( int level, int precision )
#else
static int
op_exponent( level, precision )
   int level;
   int precision;
#endif
   {

#if INTENSIVE_DEBUG
   sprintf( bwb_ebuf, "in op_exponent(): entered function level <%d>.",
      level );
   bwb_debug ( bwb_ebuf );
#endif

   switch( precision )
      {
      case STRING:

         /* both sides of the operation should be numbers for
            string addition; if not, report an error */

#if PROG_ERRORS
         sprintf( bwb_ebuf, "Strings cannot be taken as exponents." );
         bwb_error( bwb_ebuf );
#else
         bwb_error( err_mismatch );
#endif

         break;

      case NUMBER:
         CURTASK exps[ level - 1 ].nval
           = (bnumber) pow( (double) exp_getnval( &( CURTASK exps[ level - 1 ] )),
                  (double) exp_getnval( &( CURTASK exps[ level + 1 ] )) );
         break;

      }

   /* set variable to requested precision */

   CURTASK exps[ level - 1 ].type = (char) precision;
   CURTASK exps[ level - 1 ].operation = NUMBER;

   /* decrement the stack twice */

   op_pulldown( 2 );

   return TRUE;

   }

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

	FUNCTION:       op_intdiv()

	DESCRIPTION:    This function divides the number on
			the left by the number on the right,
			returning the result as an integer.

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

#if ANSI_C
static int
op_intdiv( int level, int precision )
#else
static int
op_intdiv( level, precision )
   int level;
   int precision;
#endif
   {

   switch( precision )
      {
      case STRING:

         /* both sides of the operation should be numbers for
            string addition; if not, report an error */

#if PROG_ERRORS
         sprintf( bwb_ebuf, "Strings cannot be divided." );
         bwb_error( bwb_ebuf );
#else
         bwb_error( err_mismatch );
#endif

         break;

      default:
         if ( exp_getnval( &( CURTASK exps[ level + 1 ] ))
            == (bnumber) 0 )
            {
            CURTASK exps[ level - 1 ].nval = (bnumber) -1;
            op_pulldown( 2 );
#if PROG_ERRORS
            sprintf( bwb_ebuf, "Divide by 0." );
            bwb_error( bwb_ebuf );
#else
            bwb_error( err_dbz );
#endif
            return FALSE;
            }

         CURTASK exps[ level - 1 ].nval
            = exp_getnval( &( CURTASK exps[ level - 1 ] ))
            / exp_getnval( &( CURTASK exps[ level + 1 ] ));
         break;
      }

   /* set variable to requested precision */

   CURTASK exps[ level - 1 ].type = NUMBER;
   CURTASK exps[ level - 1 ].operation = NUMBER;

   /* decrement the stack twice */

   op_pulldown( 2 );

   return TRUE;

   }

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

	FUNCTION:       op_or()

	DESCRIPTION:    This function compares two integers and
			performs a logical OR on them.

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

#if ANSI_C
static int
op_or( int level, int precision )
#else
static int
op_or( level, precision )
   int level;
   int precision;
#endif
   {

   switch( precision )
      {
      case STRING:

         /* both sides of the operation should be numbers for
            logical comparison; if not, report an error */

#if PROG_ERRORS
         sprintf( bwb_ebuf, "Strings cannot be compared logically." );
         bwb_error( bwb_ebuf );
#else
         bwb_error( err_mismatch );
#endif

         break;

      case NUMBER:
         CURTASK exps[ level - 1 ].nval
            = (bnumber) ((int) exp_getnval( &( CURTASK exps[ level - 1 ] ))
            | (int) exp_getnval( &( CURTASK exps[ level + 1 ] )));
         break;

      }

   /* set variable type to integer */

   CURTASK exps[ level - 1 ].type = NUMBER;
   CURTASK exps[ level - 1 ].operation = NUMBER;

   /* decrement the stack twice */

   op_pulldown( 2 );

   return TRUE;

   }

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

	FUNCTION:       op_and()

	DESCRIPTION:    This function compares two integers and
			performs a logical AND on them.

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

#if ANSI_C
static int
op_and( int level, int precision )
#else
static int
op_and( level, precision )
   int level;
   int precision;
#endif
   {

   switch( precision )
      {
      case STRING:


         /* both sides of the operation should be numbers for
            logical comparison; if not, report an error */

#if PROG_ERRORS
         sprintf( bwb_ebuf, "Strings cannot be compared logically." );
         bwb_error( bwb_ebuf );
#else
         bwb_error( err_mismatch );
#endif

         break;

      case NUMBER:
         CURTASK exps[ level - 1 ].nval
            = (bnumber) ((int) exp_getnval( &( CURTASK exps[ level - 1 ] ))
            & (int) exp_getnval( &( CURTASK exps[ level + 1 ] )));
         break;

      }

   /* set variable type to integer */

   CURTASK exps[ level - 1 ].type = NUMBER;
   CURTASK exps[ level - 1 ].operation = NUMBER;

   /* decrement the stack twice */

   op_pulldown( 2 );

   return TRUE;

   }

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

	FUNCTION:       op_not()

	DESCRIPTION:    This function performs a logical NOT on
			the integer to the right.

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

#if ANSI_C
static int
op_not( int level, int precision )
#else
static int
op_not( level, precision )
   int level;
   int precision;
#endif
   {

   switch( precision )
      {
      case STRING:


         /* both sides of the operation should be numbers for
            logical comparison; if not, report an error */

#if PROG_ERRORS
         sprintf( bwb_ebuf, "Strings cannot be compared logically." );
         bwb_error( bwb_ebuf );
#else
         bwb_error( err_mismatch );
#endif

         break;

      default:

#if INTENSIVE_DEBUG
         sprintf( bwb_ebuf, "in op_not(): argument is <%d>, precision <%c>",
            (unsigned int) exp_getnval( &( CURTASK exps[ level + 1 ] )), precision );
         bwb_debug( bwb_ebuf );
#endif

         CURTASK exps[ level ].nval = (bnumber)
            ~( (int) exp_getnval( &( CURTASK exps[ level + 1 ] )) );

#if INTENSIVE_DEBUG
         sprintf( bwb_ebuf, "in op_not(): result is <%d>, precision <%c>",
            (unsigned int) exp_getnval( &( CURTASK exps[ level ] )), precision );
         bwb_debug( bwb_ebuf );
#endif

         break;
      }

   /* set variable type to integer */

   CURTASK exps[ level ].type = NUMBER;
   CURTASK exps[ level ].operation = NUMBER;

   /* decrement the stack once */

   op_pulldown( 1 );

#if INTENSIVE_DEBUG
   sprintf( bwb_ebuf, "in op_not(): CURTASK expsc <%d>, level <%d> result <%d>",
      CURTASK expsc, level, CURTASK exps[ CURTASK expsc ].nval );
   bwb_debug( bwb_ebuf );
#endif

   return TRUE;

   }

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

	FUNCTION:       op_xor()

	DESCRIPTION:    This function compares two integers and
			performs a logical XOR on them.

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

#if ANSI_C
static int
op_xor( int level, int precision )
#else
static int
op_xor( level, precision )
   int level;
   int precision;
#endif
   {

   switch( precision )
      {
      case STRING:

         /* both sides of the operation should be numbers for
            logical comparison; if not, report an error */

#if PROG_ERRORS
         sprintf( bwb_ebuf, "Strings cannot be compared logically." );
         bwb_error( bwb_ebuf );
#else
         bwb_error( err_mismatch );
#endif

         break;

      case NUMBER:
         CURTASK exps[ level - 1 ].nval
            = (bnumber) ((int) exp_getnval( &( CURTASK exps[ level - 1 ] ))
            ^ (int) exp_getnval( &( CURTASK exps[ level + 1 ] )));
         break;

      }

   /* set variable type to integer */

   CURTASK exps[ level - 1 ].type = NUMBER;
   CURTASK exps[ level - 1 ].operation = NUMBER;

   /* decrement the stack twice */

   op_pulldown( 2 );

   return TRUE;

   }

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

	FUNCTION:       op_negation()

	DESCRIPTION:    This function performs a negation on the
			element to the right.
			Added by JBV 10/95

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

#if ANSI_C
static int
op_negation( int level, int precision )
#else
static int
op_negation( level, precision )
   int level;
   int precision;
#endif
   {

   switch( precision )
      {
      case STRING:


         /* both sides of the operation should be numbers for
            logical comparison; if not, report an error */

#if PROG_ERRORS
         sprintf( bwb_ebuf, "Strings cannot be compared logically." );
         bwb_error( bwb_ebuf );
#else
         bwb_error( err_mismatch );
#endif

         break;

      default:

#if INTENSIVE_DEBUG
         sprintf( bwb_ebuf, "in op_negation(): argument is <%f>, precision <%c>",
            exp_getnval( &( CURTASK exps[ level + 1 ] )), precision );
         bwb_debug( bwb_ebuf );
#endif

         CURTASK exps[ level ].nval = (bnumber)
            -( exp_getnval( &( CURTASK exps[ level + 1 ] )) );

#if INTENSIVE_DEBUG
         sprintf( bwb_ebuf, "in op_negation(): result is <%f>, precision <%c>",
            exp_getnval( &( CURTASK exps[ level ] )), precision );
         bwb_debug( bwb_ebuf );
#endif

         break;
      }

   /* set variable type to requested precision (JBV) */

   CURTASK exps[ level ].type = (char) precision;
   CURTASK exps[ level ].operation = NUMBER;

   /* decrement the stack once */

   op_pulldown( 1 );

#if INTENSIVE_DEBUG
   sprintf( bwb_ebuf, "in op_negation(): CURTASK expsc <%d>, level <%d> result <%f>",
      CURTASK expsc, level, CURTASK exps[ CURTASK expsc ].nval );
   bwb_debug( bwb_ebuf );
#endif

   return TRUE;

   }

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

	FUNCTION:       op_islevelstr()

	DESCRIPTION:    This function determines whether the
			operation at a specified level involves a
			string constant or variable.

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

#if ANSI_C
static int
op_islevelstr( int level )
#else
static int
op_islevelstr( level )
   int level;
#endif
   {

   /* first see if the level holds a string constant */

   if ( CURTASK exps[ level ].operation == CONST_STRING )
      {

#if INTENSIVE_DEBUG
      sprintf( bwb_ebuf, "in op_islevelstr(): string detected at level <%d>.",
         level );
      bwb_debug( bwb_ebuf );
#endif

      return TRUE;
      }

   /* see if the level holds a string variable */

   if ( CURTASK exps[ level ].operation == VARIABLE )
      {
      if ( CURTASK exps[ level ].xvar->type == STRING )
         {

#if INTENSIVE_DEBUG
         sprintf( bwb_ebuf, "in op_islevelstr(): string detected at level <%d>.",
            level );
         bwb_debug( bwb_ebuf );
#endif

         return TRUE;
         }
      }

   /* test has failed, return FALSE */

#if INTENSIVE_DEBUG
   sprintf( bwb_ebuf, "in op_islevelstr(): string not detected at level <%d>.",
      level );
   bwb_debug( bwb_ebuf );
#endif

   return FALSE;

   }

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

	FUNCTION:       op_getprecision()

	DESCRIPTION:    This function finds the precision for
			an operation by comparing the precision
			at this level and that two levels below.

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

#if ANSI_C
static int
op_getprecision( int level )
#else
static int
op_getprecision( level )
   int level;
#endif
   {

   /* first test for string value */

   if (  ( CURTASK exps[ level + 1 ].type == STRING )
      || ( CURTASK exps[ level - 1 ].type == STRING ) )
      {
      return STRING;
      }

   /* Both are numbers, so we should be able to find a suitable
      precision level by starting with the top and moving down;
      check first for double precision */

   else
      {
      return NUMBER;
      }

   }

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

	FUNCTION:       op_pulldown()

	DESCRIPTION:    This function pulls the expression stack
			down a specified number of levels, decrementing
			the expression stack counter (bycalling dec_esc())
			and decrementing the current "level" of operation
			processing.

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

#if ANSI_C
static int
op_pulldown( int how_far )
#else
static int
op_pulldown( how_far )
   int how_far;
#endif
   {
   int level;
   register int c;

#if INTENSIVE_DEBUG
   sprintf( bwb_ebuf, "in op_pulldown(): pull down e stack <%d> place(s)",
      how_far );
   bwb_debug( bwb_ebuf );
#endif

   /* first pull down the actual variables themselves */

   level = op_level + ( 2 - how_far );
   while ( CURTASK expsc >= ( level + how_far ) )
      {

      /*------------------------------------------------------*/
      /* But before memcpy, deallocate sbuffer for level, and */
      /* afterwards, set sbuffer for level + how_far to NULL! */
      /* Else konfusion reigns the next time around... (JBV)  */
      /*------------------------------------------------------*/

      if( CURTASK exps[ level ].sval.sbuffer != NULL ) /* JBV */
         FREE( CURTASK exps[ level ].sval.sbuffer, "op_pulldown" );
      memcpy( &CURTASK exps[ level ], &CURTASK exps[ level + how_far ],
         (size_t) ( sizeof( struct exp_ese )) );
      CURTASK exps[ level + how_far ].sval.sbuffer = NULL; /* JBV */

      ++level;

      }

   /* decrement the expression stack counter */

   for ( c = 0; c < how_far; ++c )
      {

      if ( dec_esc() == TRUE )
         {
         --op_level;
         }
      else
         {
         return FALSE;
         }

      }

   return TRUE;

   }


