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

        bwb_ops.c       Expression Parsing Operations
                        for Bywater BASIC Interpreter

                        Copyright (c) 1992, Ted A. Campbell

                        Bywater Software
                        P. O. Box 4023
                        Duke Station
                        Durham, NC  27706

                        email: tcamp@acpub.duke.edu

        Copyright and Permissions Information:

        All U.S. and international copyrights are claimed by the
        author. The author grants permission to use this code
        and software based on it under the following conditions:
        (a) in general, the code and software based upon it may be
        used by individuals and by non-profit organizations; (b) it
        may also be utilized by governmental agencies in any country,
        with the exception of military agencies; (c) the code and/or
        software based upon it may not be sold for a profit without
        an explicit and specific permission from the author, except
        that a minimal fee may be charged for media on which it is
        copied, and for copying and handling; (d) the code must be
        distributed in the form in which it has been released by the
        author; and (e) the code and software based upon it may not
        be used for illegal activities.

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

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

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

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

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_islevelstr( int level );
static int op_getprecision( int level );
static int op_isoperator( int operation );
static int op_pulldown( int how_far );

static int op_level;

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

        FUNCTION:   exp_operation()

        DESCRIPTION:  This function performs whatever operations
        are necessary at the end of function bwb_exp.

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

int
exp_operation( int entry_level )
   {
   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 < exp_esc )
         && ( op_isoperator( exp_es[ op_level ].operation ) == FALSE ))
         {
         ++op_level;
         }

      while ( ( op_level > entry_level ) && ( op_level < exp_esc ) )
         {

         /* 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 == exp_es[ 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, exp_es[ 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 exp_esc */

         if ( op_level < exp_esc )
            {
            ++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( exp_es [ op_level ].operation ) == FALSE )
               && ( op_level < exp_esc ) )
               {
               ++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.

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

static int
op_oplevel( int level )
   {
   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 ( exp_es[ 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;

         default:
#if PROG_ERRORS
            sprintf( bwb_ebuf, "PROGRAMMING ERROR: operator <%d> not (yet) supported." );
            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.

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

static int
op_isoperator( int operation )
   {
   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.

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

static int
op_add( int level, int precision )
   {
   int error_condition;

   error_condition = 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_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, exp_es[ level - 1 ].operation, exp_es[ level - 1 ].type );
            bwb_debug( bwb_ebuf );
            exp_getsval( &( exp_es[ level - 1 ] ));
            sprintf( bwb_ebuf, "in op_add(): try exp_getsval(), level <%d> op <%d> type <%c>:",
               level + 1, exp_es[ level + 1 ].operation, exp_es[ level + 1 ].type );
            bwb_debug( bwb_ebuf );
            exp_getsval( &( exp_es[ level + 1 ] ));
            sprintf( bwb_ebuf, "in op_add(): string addition, exp_getsval()s completed" );
            bwb_debug( bwb_ebuf );
#endif

            str_cat( exp_getsval( &( exp_es[ level - 1 ] ) ), 
               exp_getsval( &( exp_es[ level + 1 ] ) ) );
            }
         exp_es[ level - 1 ].operation = CONST_STRING;

         break;

      case DOUBLE:
         exp_es[ level - 1 ].dval
            = exp_getdval( &( exp_es[ level - 1 ] ))
            + exp_getdval( &( exp_es[ level + 1 ] ));
         exp_es[ level - 1 ].operation = NUMBER;
         break;

      case SINGLE:

#if INTENSIVE_DEBUG
         sprintf( bwb_ebuf, "in op_add(): single, (level <%d>) <%f> + <%f> (level <%d>",
            level - 1, exp_getfval( &( exp_es[ level - 1 ] )),
            exp_getfval( &( exp_es[ level + 1 ] )), level + 1 );
         bwb_debug( bwb_ebuf );
#endif

         exp_es[ level - 1 ].fval
            = exp_getfval( &( exp_es[ level - 1 ] ))
            + exp_getfval( &( exp_es[ level + 1 ] ));

#if INTENSIVE_DEBUG
         sprintf( bwb_ebuf, "in op_add(): single, = <%f>",
            exp_es[ level - 1 ].fval );
         bwb_debug( bwb_ebuf );
#endif

         exp_es[ level - 1 ].operation = NUMBER;
         break;

      case INTEGER:

#if INTENSIVE_DEBUG
         sprintf( bwb_ebuf, "in op_add(): Integer precision." );
         bwb_debug ( bwb_ebuf );
         sprintf( bwb_ebuf, "in op_add(): precisions: lhs <%d> rhs <%d>.",
            exp_es[ level - 1 ].type,
            exp_es[ level + 1 ].type );
         bwb_debug ( bwb_ebuf );
#endif

         exp_es[ level - 1 ].ival
            = exp_getival( &( exp_es[ level - 1 ] ))
            + exp_getival( &( exp_es[ level + 1 ] ));

#if INTENSIVE_DEBUG
         sprintf( bwb_ebuf, "in op_add(): integer addition, result is <%d>",
            exp_es[ level - 1 ].ival );
         bwb_debug( bwb_ebuf );
#endif

         exp_es[ level - 1 ].operation = NUMBER;
         break;
      }

   /* set variable to requested precision */

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

#if INTENSIVE_DEBUG
   sprintf( bwb_ebuf, "in op_add() returns with operation <%d> type <%c>",
      exp_es[ level - 1 ].operation, exp_es[ 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.


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

static int
op_subtract( int level, int precision )
   {

   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 DOUBLE:
         exp_es[ level - 1 ].dval
            = exp_getdval( &( exp_es[ level - 1 ] ))
            - exp_getdval( &( exp_es[ level + 1 ] ));
         break;

      case SINGLE:

#if INTENSIVE_DEBUG
         sprintf( bwb_ebuf, "in op_subtract(): Single precision." );
         bwb_debug ( bwb_ebuf );
         sprintf( bwb_ebuf, "in op_subtract(): precisions: lhs <%d> rhs <%d>.",
            exp_es[ level - 1 ].type,
            exp_es[ level + 1 ].type );
         bwb_debug ( bwb_ebuf );
         sprintf( bwb_ebuf, "in op_subtract(): values: lhs <%f> rhs <%f>.",
            exp_getfval( &( exp_es[ level - 1 ] )),
            exp_getfval( &( exp_es[ level + 1 ] )) );
         bwb_debug ( bwb_ebuf );
#endif

         exp_es[ level - 1 ].fval
            = exp_getfval( &( exp_es[ level - 1 ] ))
            - exp_getfval( &( exp_es[ level + 1 ] ));

#if INTENSIVE_DEBUG
         sprintf( bwb_ebuf, "in op_subtract(): SINGLE subtraction, result is <%f>",
            exp_es[ level - 1 ].fval );
         bwb_debug( bwb_ebuf );
#endif

         break;

      case INTEGER:

#if INTENSIVE_DEBUG
         sprintf( bwb_ebuf, "in op_subtract(): Integer precision." );
         bwb_debug ( bwb_ebuf );
         sprintf( bwb_ebuf, "in op_subtract(): precisions: lhs <%d> rhs <%d>.",
            exp_es[ level - 1 ].type,
            exp_es[ level + 1 ].type );
         bwb_debug ( bwb_ebuf );
#endif

         exp_es[ level - 1 ].ival
            = exp_getival( &( exp_es[ level - 1 ] ))
            - exp_getival( &( exp_es[ level + 1 ] ));

#if INTENSIVE_DEBUG
         sprintf( bwb_ebuf, "in op_subtract(): integer subtraction, result is <%d>",
            exp_es[ level - 1 ].ival );
         bwb_debug( bwb_ebuf );
#endif

         break;
      }

   /* set variable to requested precision */

   exp_es[ level - 1 ].type = (char) precision;
   exp_es[ 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 from the number on the right.

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

static int
op_multiply( int level, int precision )
   {

   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 DOUBLE:
         exp_es[ level - 1 ].dval
            = exp_getdval( &( exp_es[ level - 1 ] ))
            * exp_getdval( &( exp_es[ level + 1 ] ));
         break;

      case SINGLE:
         exp_es[ level - 1 ].fval
            = exp_getfval( &( exp_es[ level - 1 ] ))
            * exp_getfval( &( exp_es[ level + 1 ] ));
         break;

      case INTEGER:
         exp_es[ level - 1 ].ival
            = exp_getival( &( exp_es[ level - 1 ] ))
            * exp_getival( &( exp_es[ level + 1 ] ));
         break;
      }

   /* set variable to requested precision */

   exp_es[ level - 1 ].type = (char) precision;
   exp_es[ 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.

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

static int
op_divide( int level, int precision )
   {

   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;

      case DOUBLE:
         if ( exp_getdval( &( exp_es[ level + 1 ] ))
            == 0.0 )
            {
            exp_es[ level - 1 ].dval = -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;
            }
         exp_es[ level - 1 ].dval
            = exp_getdval( &( exp_es[ level - 1 ] ))
            / exp_getdval( &( exp_es[ level + 1 ] ));
         break;

      case SINGLE:
         if ( exp_getfval( &( exp_es[ level + 1 ] ))
            == (float) 0.0 )
            {
            exp_es[ level - 1 ].fval = (float) -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;
            }
         exp_es[ level - 1 ].fval
            = exp_getfval( &( exp_es[ level - 1 ] ))
            / exp_getfval( &( exp_es[ level + 1 ] ));
         break;

      case INTEGER:
         if ( exp_getival( &( exp_es[ level + 1 ] ))
            == 0 )
            {
            exp_es[ level - 1 ].ival = -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;
            }
         exp_es[ level - 1 ].ival
            = exp_getival( &( exp_es[ level - 1 ] ))
            / exp_getival( &( exp_es[ level + 1 ] ));
         break;
      }

   /* set variable to requested precision */

   exp_es[ level - 1 ].type = (char) precision;
   exp_es[ 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.

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

static int
op_assign( int level, int precision )
   {
   bstring *s, *d;

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

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

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

      case DOUBLE:
         * var_finddval( exp_es[ level - 1 ].xvar, 
            exp_es[ level - 1 ].xvar->array_pos )  = 
            exp_es[ level - 1 ].dval = 
            exp_getdval( &( exp_es[ level + 1 ] ) );
         break;

      case SINGLE:
         * var_findfval( exp_es[ level - 1 ].xvar, 
            exp_es[ level - 1 ].xvar->array_pos )  = 
            exp_es[ level - 1 ].fval =
            exp_getfval( &( exp_es[ level + 1 ] ) );
#if INTENSIVE_DEBUG
         sprintf( bwb_ebuf, "in op_assign(): SINGLE assignment var <%s> val <%f>",
            exp_es[ level - 1 ].xvar->name, exp_getfval( &( exp_es[ level - 1 ] )) );
         bwb_debug( bwb_ebuf );
#endif
         break;

      case INTEGER:
         * var_findival( exp_es[ level - 1 ].xvar, 
            exp_es[ level - 1 ].xvar->array_pos )  = 
            exp_es[ level - 1 ].ival =
            exp_getival( &( exp_es[ 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 */

   exp_es[ 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.

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

static int
op_equals( int level, int precision )
   {
   int error_condition;
   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( &( exp_es[ level - 1 ] ));
            b.length = bp->length;
            b.buffer = bp->buffer;
            if ( str_cmp( &b,
               exp_getsval( &( exp_es[ level + 1 ] )) ) == 0 )
               {
               exp_es[ level - 1 ].ival = TRUE;
               }
            else
               {
               exp_es[ level - 1 ].ival = FALSE;
               }
            }
         break;

      case DOUBLE:
         if ( exp_getdval( &( exp_es[ level - 1 ] ))
            == exp_getdval( &( exp_es[ level + 1 ] )) )
            {

            exp_es[ level - 1 ].ival = TRUE;
            }
         else
            {
            exp_es[ level - 1 ].ival = FALSE;
            }
         break;

      case SINGLE:
         if ( exp_getfval( &( exp_es[ level - 1 ] ))
            == exp_getfval( &( exp_es[ level + 1 ] )) )
            {
            exp_es[ level - 1 ].ival = TRUE;
            }
         else
            {
            exp_es[ level - 1 ].ival = FALSE;
            }
         break;

      case INTEGER:
         if ( exp_getival( &( exp_es[ level - 1 ] ))
            == exp_getival( &( exp_es[ level + 1 ] )) )
            {
            exp_es[ level - 1 ].ival = TRUE;
            }
         else
            {
            exp_es[ level - 1 ].ival = 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 */

   exp_es[ level - 1 ].type = INTEGER;
   exp_es[ 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.

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

static int
op_lessthan( int level, int precision )
   {
   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( &( exp_es[ level - 1 ] )),
               exp_getsval( &( exp_es[ level + 1 ] )) ) < 0 )
               {
               exp_es[ level - 1 ].ival = TRUE;
               }
            else
               {
               exp_es[ level - 1 ].ival = FALSE;
               }
            }
         break;

      case DOUBLE:
         if ( exp_getdval( &( exp_es[ level - 1 ] ))
            < exp_getdval( &( exp_es[ level + 1 ] )) )
            {
            exp_es[ level - 1 ].ival = TRUE;
            }
         else
            {
            exp_es[ level - 1 ].ival = FALSE;
            }
         break;

      case SINGLE:
         if ( exp_getfval( &( exp_es[ level - 1 ] ))
            < exp_getfval( &( exp_es[ level + 1 ] )) )
            {
            exp_es[ level - 1 ].ival = TRUE;
            }
         else
            {
            exp_es[ level - 1 ].ival = FALSE;
            }
         break;

      case INTEGER:
         if ( exp_getival( &( exp_es[ level - 1 ] ))
            < exp_getival( &( exp_es[ level + 1 ] )) )
            {

            exp_es[ level - 1 ].ival = TRUE;
            }
         else
            {
            exp_es[ level - 1 ].ival = 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 */

   exp_es[ level - 1 ].type = INTEGER;
   exp_es[ 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.

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

static int
op_greaterthan( int level, int precision )
   {
   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( &( exp_es[ level - 1 ] )),
               exp_getsval( &( exp_es[ level + 1 ] )) ) > 0 )
               {
               exp_es[ level - 1 ].ival = TRUE;
               }
            else
               {
               exp_es[ level - 1 ].ival = FALSE;
               }
            }
         break;

      case DOUBLE:
         if ( exp_getdval( &( exp_es[ level - 1 ] ))
            > exp_getdval( &( exp_es[ level + 1 ] )) )
            {
            exp_es[ level - 1 ].ival = TRUE;
            }
         else
            {
            exp_es[ level - 1 ].ival = FALSE;
            }
         break;

      case SINGLE:
         if ( exp_getfval( &( exp_es[ level - 1 ] ))
            > exp_getfval( &( exp_es[ level + 1 ] )) )
            {
            exp_es[ level - 1 ].ival = TRUE;
            }
         else
            {
            exp_es[ level - 1 ].ival = FALSE;
            }
         break;

      case INTEGER:
         if ( exp_getival( &( exp_es[ level - 1 ] ))
            > exp_getival( &( exp_es[ level + 1 ] )) )
            {
            exp_es[ level - 1 ].ival = TRUE;
            }
         else
            {
            exp_es[ level - 1 ].ival = 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 */

   exp_es[ level - 1 ].type = INTEGER;
   exp_es[ 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.

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

static int
op_lteq( int level, int precision )
   {
   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( &( exp_es[ level - 1 ] )),
               exp_getsval( &( exp_es[ level + 1 ] )) ) <= 0 )
               {
               exp_es[ level - 1 ].ival = TRUE;
               }
            else
               {
               exp_es[ level - 1 ].ival = FALSE;
               }
            }
         break;

      case DOUBLE:
         if ( exp_getdval( &( exp_es[ level - 1 ] ))
            <= exp_getdval( &( exp_es[ level + 1 ] )) )
            {
            exp_es[ level - 1 ].ival = TRUE;
            }
         else
            {
            exp_es[ level - 1 ].ival = FALSE;
            }
         break;

      case SINGLE:

         if ( exp_getfval( &( exp_es[ level - 1 ] ))
            <= exp_getfval( &( exp_es[ level + 1 ] )) )
            {
            exp_es[ level - 1 ].ival = TRUE;
            }
         else
            {
            exp_es[ level - 1 ].ival = FALSE;
            }
         break;

      case INTEGER:
         if ( exp_getival( &( exp_es[ level - 1 ] ))
            <= exp_getival( &( exp_es[ level + 1 ] )) )
            {
            exp_es[ level - 1 ].ival = TRUE;
            }
         else
            {
            exp_es[ level - 1 ].ival = 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 */

   exp_es[ level - 1 ].type = INTEGER;
   exp_es[ 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.

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

static int
op_gteq( int level, int precision )
   {
   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( &( exp_es[ level - 1 ] )),
               exp_getsval( &( exp_es[ level + 1 ] )) ) >= 0 )
               {
               exp_es[ level - 1 ].ival = TRUE;
               }
            else
               {
               exp_es[ level - 1 ].ival = FALSE;
               }
            }
         break;

      case DOUBLE:
         if ( exp_getdval( &( exp_es[ level - 1 ] ))
            >= exp_getdval( &( exp_es[ level + 1 ] )) )
            {
            exp_es[ level - 1 ].ival = TRUE;
            }
         else
            {
            exp_es[ level - 1 ].ival = FALSE;
            }
         break;

      case SINGLE:
         if ( exp_getfval( &( exp_es[ level - 1 ] ))
            >= exp_getfval( &( exp_es[ level + 1 ] )) )
            {
            exp_es[ level - 1 ].ival = TRUE;
            }
         else
            {
            exp_es[ level - 1 ].ival = FALSE;
            }
         break;

      case INTEGER:
         if ( exp_getival( &( exp_es[ level - 1 ] ))
            >= exp_getival( &( exp_es[ level + 1 ] )) )
            {
            exp_es[ level - 1 ].ival = TRUE;
            }
         else
            {
            exp_es[ level - 1 ].ival = 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 */

   exp_es[ level - 1 ].type = INTEGER;
   exp_es[ 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.

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

static int
op_notequal( int level, int precision )
   {
   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( &( exp_es[ level - 1 ] )),
               exp_getsval( &( exp_es[ level + 1 ] )) ) != 0 )
               {
               exp_es[ level - 1 ].ival = TRUE;
               }
            else
               {
               exp_es[ level - 1 ].ival = FALSE;
               }
            }
         break;

      case DOUBLE:
         if ( exp_getdval( &( exp_es[ level - 1 ] ))
            != exp_getdval( &( exp_es[ level + 1 ] )) )
            {
            exp_es[ level - 1 ].ival = TRUE;
            }
         else
            {
            exp_es[ level - 1 ].ival = FALSE;
            }
         break;

      case SINGLE:
         if ( exp_getfval( &( exp_es[ level - 1 ] ))
            != exp_getfval( &( exp_es[ level + 1 ] )) )
            {
            exp_es[ level - 1 ].ival = TRUE;
            }
         else
            {
            exp_es[ level - 1 ].ival = FALSE;
            }
         break;

      case INTEGER:
         if ( exp_getival( &( exp_es[ level - 1 ] ))
            != exp_getival( &( exp_es[ level + 1 ] )) )
            {
            exp_es[ level - 1 ].ival = TRUE;
            }
         else
            {
            exp_es[ level - 1 ].ival = 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 */

   exp_es[ level - 1 ].type = INTEGER;
   exp_es[ 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 return the
        remainder.

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

static int
op_modulus( int level, int precision )
   {
   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 DOUBLE:
         if ( exp_getdval( &( exp_es[ level + 1 ] ))
            == 0.0 )
            {
            exp_es[ level - 1 ].dval = -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;
            }
         exp_es[ level ].dval
            = exp_getdval( &( exp_es[ level - 1 ] ))
            / exp_getdval( &( exp_es[ level + 1 ] ));
         modf( exp_es[ level ].dval, &iportion );
         exp_es[ level - 1 ].dval
            = exp_getdval( &( exp_es[ level - 1 ] ))
            - ( exp_getdval( &( exp_es[ level + 1 ] ))
            * iportion );
         break;

      case SINGLE:
         if ( exp_getfval( &( exp_es[ level + 1 ] ))
            == (float) 0.0 )
            {
            exp_es[ level - 1 ].fval = (float) -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;
            }
         exp_es[ level ].fval
            = exp_getfval( &( exp_es[ level - 1 ] ))
            / exp_getfval( &( exp_es[ level + 1 ] ));
         modf( (double) exp_es[ level ].fval, &iportion );

#if INTENSIVE_DEBUG
         sprintf( bwb_ebuf, "in op_modulus(): integer portion is %f",
            iportion );
         bwb_debug( bwb_ebuf );
#endif

         exp_es[ level - 1 ].fval
            = exp_getfval( &( exp_es[ level - 1 ] ))
            - ( exp_getfval( &( exp_es[ level + 1 ] ))
            * (float) iportion );
         break;

      case INTEGER:
         if ( exp_getival( &( exp_es[ level + 1 ] ))
            == 0 )
            {
            exp_es[ level - 1 ].ival = -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;
            }
         exp_es[ level - 1 ].ival
            = exp_getival( &( exp_es[ level - 1 ] ))
            % exp_getival( &( exp_es[ level + 1 ] ));
         break;
      }

   /* set variable to requested precision */

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

   /* decrement the stack twice */

   op_pulldown( 2 );

   return TRUE;

   }

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

        FUNCTION:   op_exponent()

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

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

static int
op_exponent( int level, int precision )
   {

#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 DOUBLE:
         exp_es[ level - 1 ].dval
           = pow( exp_getdval( &( exp_es[ level - 1 ] )),
                  exp_getdval( &( exp_es[ level + 1 ] )) );
         break;

      case SINGLE:
         exp_es[ level - 1 ].fval
           = (float) pow( exp_getdval( &( exp_es[ level - 1 ] )),
                  exp_getdval( &( exp_es[ level + 1 ] )) );
         break;

      case INTEGER:

#if INTENSIVE_DEBUG
         sprintf( bwb_ebuf, "in op_exponent(): Integer precision." );
         bwb_debug ( bwb_ebuf );
         sprintf( bwb_ebuf, "in op_exponent(): lhs <%f> rhs <%f>.",
            exp_getdval( &( exp_es[ level - 1 ] )),
            exp_getdval( &( exp_es[ level + 1 ] )) );
         bwb_debug ( bwb_ebuf );
#endif

         exp_es[ level - 1 ].ival
           = (int) pow( exp_getdval( &( exp_es[ level - 1 ] )),
                  exp_getdval( &( exp_es[ level + 1 ] )) );
         break;
      }

   /* set variable to requested precision */

   exp_es[ level - 1 ].type = (char) precision;
   exp_es[ 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 and returns the
        result as an integer.

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

static int
op_intdiv( int level, int precision )
   {

   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_getival( &( exp_es[ level + 1 ] ))
            == 0 )
            {
            exp_es[ level - 1 ].ival = -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;
            }

#if INTENSIVE_DEBUG
         sprintf( bwb_ebuf, "in op_intdiv(): <%d> / <%d>",
            exp_getival( &( exp_es[ level - 1 ] )),
            exp_getival( &( exp_es[ level + 1 ] )) );
         bwb_debug( bwb_ebuf );
#endif

         exp_es[ level - 1 ].ival
            = exp_getival( &( exp_es[ level - 1 ] ))
            / exp_getival( &( exp_es[ level + 1 ] ));
         break;
      }

   /* set variable to requested precision */

   exp_es[ level - 1 ].type = INTEGER;
   exp_es[ 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 NOT on them, returning the result
        as an integer.

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

static int
op_or( int level, int precision )
   {

   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 DOUBLE:
         exp_es[ level - 1 ].ival
            = exp_getival( &( exp_es[ level - 1 ] ))
            | exp_getival( &( exp_es[ level + 1 ] ));
         break;

      case SINGLE:
         exp_es[ level - 1 ].ival
            = exp_getival( &( exp_es[ level - 1 ] ))
            | exp_getival( &( exp_es[ level + 1 ] ));
         break;

      case INTEGER:
         exp_es[ level - 1 ].ival
            = exp_getival( &( exp_es[ level - 1 ] ))
            | exp_getival( &( exp_es[ level + 1 ] ));
         break;
      }

   /* set variable type to integer */

   exp_es[ level - 1 ].type = INTEGER;
   exp_es[ 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 NOT on them, returning the result
        as an integer.

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

static int
op_and( int level, int precision )
   {

   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 DOUBLE:
         exp_es[ level - 1 ].ival
            = exp_getival( &( exp_es[ level - 1 ] ))
            & exp_getival( &( exp_es[ level + 1 ] ));
         break;

      case SINGLE:
         exp_es[ level - 1 ].ival
            = exp_getival( &( exp_es[ level - 1 ] ))
            & exp_getival( &( exp_es[ level + 1 ] ));
         break;

      case INTEGER:
         exp_es[ level - 1 ].ival
            = exp_getival( &( exp_es[ level - 1 ] ))
            & exp_getival( &( exp_es[ level + 1 ] ));
         break;
      }

   /* set variable type to integer */

   exp_es[ level - 1 ].type = INTEGER;
   exp_es[ level - 1 ].operation = NUMBER;

   /* decrement the stack twice */

   op_pulldown( 2 );

   return TRUE;

   }

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

        FUNCTION:   op_not()

        DESCRIPTION:  This function compares two integers and
        performs a logical NOT on them, returning the result
        as an integer.

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

static int
op_not( int level, int precision )
   {
   unsigned char r;

   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_getival( &( exp_es[ level + 1 ] )), precision );
         bwb_debug( bwb_ebuf );
#endif

         exp_es[ level ].ival =
            ~( exp_getival( &( exp_es[ level + 1 ] )) );

#if INTENSIVE_DEBUG
         sprintf( bwb_ebuf, "in op_not(): result is <%d>, precision <%c>",
            (int) r, precision );
         bwb_debug( bwb_ebuf );
#endif

         break;
      }

   /* set variable type to integer */

   exp_es[ level ].type = INTEGER;
   exp_es[ level ].operation = NUMBER;

   /* decrement the stack once */

   op_pulldown( 1 );

#if INTENSIVE_DEBUG
   sprintf( bwb_ebuf, "in op_not(): exp_esc <%d>, level <%d> result <%d>",
      exp_esc, level, exp_es[ exp_esc ].ival );
   bwb_debug( bwb_ebuf );
#endif

   return TRUE;

   }

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

        FUNCTION:   op_xor()

        DESCRIPTION:  This function compares two integers and
        performs a logical NOT on them, returning the result
        as an integer.

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

static int
op_xor( int level, int precision )
   {

   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 DOUBLE:
         exp_es[ level - 1 ].ival
            = exp_getival( &( exp_es[ level - 1 ] ))
            ^ exp_getival( &( exp_es[ level + 1 ] ));
         break;

      case SINGLE:
         exp_es[ level - 1 ].ival
            = exp_getival( &( exp_es[ level - 1 ] ))
            ^ exp_getival( &( exp_es[ level + 1 ] ));
         break;

      case INTEGER:
         exp_es[ level - 1 ].ival   
            = exp_getival( &( exp_es[ level - 1 ] ))
            ^ exp_getival( &( exp_es[ level + 1 ] ));
         break;
      }

   /* set variable type to integer */

   exp_es[ level - 1 ].type = INTEGER;
   exp_es[ level - 1 ].operation = NUMBER;

   /* decrement the stack twice */

   op_pulldown( 2 );

   return TRUE;

   }

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

        FUNCTION:   op_islevelstr()

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

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

static int
op_islevelstr( int level )
   {

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

   if ( exp_es[ 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 ( exp_es[ level ].operation == VARIABLE )
      {
      if ( exp_es[ 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.

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

static int
op_getprecision( int level )
   {

   /* first test for string value */

   if (  ( exp_es[ level + 1 ].type == STRING )
      || ( exp_es[ 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 */

   if (  ( exp_es[ level + 1 ].type == DOUBLE )
      || ( exp_es[ level - 1 ].type == DOUBLE ) )
      {
      return DOUBLE;
      }

   /* check next for single precision */

   if (  ( exp_es[ level + 1 ].type == SINGLE )
      || ( exp_es[ level - 1 ].type == SINGLE ) )
      {
      return SINGLE;
      }

   /* test integer precision */

   if (  ( exp_es[ level + 1 ].type == INTEGER )
      && ( exp_es[ level - 1 ].type == INTEGER ) )
      {
      return INTEGER;
      }

   /* else error */

#if PROG_ERRORS
   sprintf( bwb_ebuf, "in op_getprecision(): invalid precision level." );
   bwb_error( bwb_ebuf );
#else
   bwb_error( err_syntax );
#endif

   return FALSE;

   }

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

        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.

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

static int
op_pulldown( int how_far )
   {
   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 ( exp_esc >= ( level + how_far ) )
      {

      memcpy( &exp_es[ level ], &exp_es[ level + how_far ],
         (size_t) ( sizeof( struct exp_ese )) );
      ++level;

      }

   /* decrement the expression stack counter */

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

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

      }

   return TRUE;

   }

