/****************************************************************
  
        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).                                  */
/*                                                               */
/* Those additionally marked with "DD" were at the suggestion of */
/* Dale DePriest (daled@cadence.com).                            */
/*                                                               */
/* Version 3.00 by Howard Wulf, AF5NE                            */
/*                                                               */
/*---------------------------------------------------------------*/


#include "bwbasic.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_imp(int level, int precision);
static int
op_eqv(int level, int precision);
static int
op_negation(int level, int precision); /* JBV */
static int
op_posation(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()
         (i.e., the end of the parsing of an expression;
         see file bwb_exp.c).
  
***************************************************************/

int
exp_operation(int entry_level)
{
   register int    precedence;
   int             operator;

   bwx_DEBUG(__FUNCTION__);

   /* 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 < NUM_OPERATORS; ++operator)
         {

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

               /* check for appropriate level of
                * precedence */

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


                  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;


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


            }
         }  /* 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.
  
***************************************************************/

static int
op_oplevel(int level)
{
   int             precision;

   bwx_DEBUG(__FUNCTION__);

   /* set the precision */

   if ((precision = op_getprecision(level)) == OP_ERROR)
   {
      op_pulldown(2);
      sprintf(bwb_ebuf, "exp_operation(): failed to set precision.");
      bwb_error(bwb_ebuf);
      return FALSE;
   }
   /* precision is set correctly */

   else
   {

      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_IMPLIES:
         op_imp(level, precision);
         break;

      case OP_EQUIV:
         op_eqv(level, precision);
         break;

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

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

      case OP_POSATION:
         op_posation(level, precision);
         break;

      default:
         sprintf(bwb_ebuf, "PROGRAMMING ERROR: operator <%d> not (yet) supported.", CURTASK exps[level].operation);
         op_pulldown(2);
         bwb_error(bwb_ebuf);
         return FALSE;
         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;

   bwx_DEBUG(__FUNCTION__);

   for (c = 0; c < NUM_OPERATORS; ++c)
   {
      if (operation == exp_ops[c].operation)
      {
         return TRUE;
      }
   }

   /* test failed; return FALSE */


   return FALSE;

}

/***************************************************************
  
        FUNCTION:    op_add()
  
        DESCRIPTION:    This function adds two numbers or
            concatenates two strings.
  
***************************************************************/

static int
op_add(int level, int precision)
{
   bwx_DEBUG(__FUNCTION__);

   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))
      {
         sprintf(bwb_ebuf, "in op_add(): Type mismatch in string addition.");
         bwb_error(bwb_ebuf);
         return FALSE;
      }
      /* concatenate the two strings */

      {
         static bstring  b;   /* JBV */
         b.rab = FALSE; /* 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;

      }
      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;


   /* 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)
{
   bwx_DEBUG(__FUNCTION__);

   switch (precision)
   {
   case STRING:

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

      sprintf(bwb_ebuf, "Strings cannot be subtracted.");
      bwb_error(bwb_ebuf);
      return FALSE;

      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.
  
***************************************************************/

static int
op_multiply(int level, int precision)
{
   bwx_DEBUG(__FUNCTION__);

   switch (precision)
   {
   case STRING:

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

      sprintf(bwb_ebuf, "Strings cannot be multiplied.");
      bwb_error(bwb_ebuf);
      return FALSE;

      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.
  
***************************************************************/

static int
op_divide(int level, int precision)
{
   bwx_DEBUG(__FUNCTION__);

   switch (precision)
   {
   case STRING:

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

      sprintf(bwb_ebuf, "Strings cannot be divided.");
      bwb_error(bwb_ebuf);
      return FALSE;

      break;

   case NUMBER:

      if (exp_getnval(&(CURTASK exps[level + 1]))
          == 0)
      {
         /* - Evaluation of an expression results in division
          * by zero (nonfatal, the recommended recovery
          * procedure is to supply machine infinity with the
          * sign of the numerator and continue) */

         if (exp_getnval(&(CURTASK exps[level - 1])) < 0)
         {
            /* NEGATIVE */
            CURTASK         exps[level - 1].nval = -(DBL_MAX); /* NEGATIVE INFINITY */
         }
         else
         {
            /* POSITIVE  */
            CURTASK         exps[level - 1].nval = (DBL_MAX);  /* POSITIVE INFINITY */
         }
         bwb_Warning_Overflow("*** WARNING:  Divide by 0 ***");
      }
      else
      {
         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;

}


BasicNumberType
VerifyNumeric(BasicNumberType Value)
{
   bwx_DEBUG(__FUNCTION__);
   /* check Value */
   if (isnan(Value))
   {
      /*** FATAL - INTERNAL ERROR - SHOULD NEVER HAPPEN ***/
      bwb_error("NOT A NUMBER");
      return 0;
   }
   else
   if (isinf(Value))
   {
      /* - Evaluation of an expression results in an overflow
       * (nonfatal, the recommended recovery procedure is to supply
       * machine in- finity with the algebraically correct sign and
       * continue). */
      if (Value < 0)
      {
         Value = -DBL_MAX;
      }
      else
      {
         Value = DBL_MAX;
      }
      bwb_Warning_Overflow("*** Arithmetic Overflow ***");
   }
   return Value;
}


/***************************************************************
  
   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)
{
   bwx_DEBUG(__FUNCTION__);

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

   if (CURTASK exps[level - 1].operation != VARIABLE)
   {
      op_pulldown(2);
      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);
      return FALSE;
   }
   if (CURTASK exps[level - 1].type != CURTASK exps[level + 1].type)
   {
      bwb_error("Type Mismatch");
      return FALSE;
   }
   /* 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:


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

   case NUMBER:
      if (TRUE)
      {
         double          Value;
         Value = exp_getnval(&(CURTASK exps[level + 1]));
         Value = VerifyNumeric(Value);
         /* assign Value */
         *var_findnval(CURTASK exps[level - 1].xvar,
                  CURTASK exps[level - 1].array_pos) =
            CURTASK exps[level - 1].nval =
            Value;
      }
      break;

   default:
      sprintf(bwb_ebuf, "in op_assign(): Variable before assignment operator has unidentified type.");
      bwb_error(bwb_ebuf);
      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.
  
***************************************************************/

static int
op_equals(int level, int precision)
{
   bwx_DEBUG(__FUNCTION__);

   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))
      {
         sprintf(bwb_ebuf, "in op_equals(): Type mismatch in string comparison.");
         bwb_error(bwb_ebuf);
         return FALSE;
      }
      /* compare the two strings */

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

   case NUMBER:
      if (exp_getnval(&(CURTASK exps[level - 1]))
          == exp_getnval(&(CURTASK exps[level + 1])))
      {
         CURTASK         exps[level - 1].nval = TRUE;
      }
      else
      {
         CURTASK         exps[level - 1].nval = 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.
  
***************************************************************/

static int
op_lessthan(int level, int precision)
{
   bwx_DEBUG(__FUNCTION__);

   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))
      {
         sprintf(bwb_ebuf, "Type mismatch in string comparison.");
         bwb_error(bwb_ebuf);
         return FALSE;
      }
      /* compare the two strings */

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

   case NUMBER:
      if (exp_getnval(&(CURTASK exps[level - 1]))
          < exp_getnval(&(CURTASK exps[level + 1])))
      {
         CURTASK         exps[level - 1].nval = TRUE;
      }
      else
      {
         CURTASK         exps[level - 1].nval = 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.
  
***************************************************************/

static int
op_greaterthan(int level, int precision)
{
   bwx_DEBUG(__FUNCTION__);

   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))
      {
         sprintf(bwb_ebuf, "Type mismatch in string comparison.");
         bwb_error(bwb_ebuf);
         return FALSE;
      }
      /* compare the two strings */

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

   case NUMBER:
      if (exp_getnval(&(CURTASK exps[level - 1]))
          > exp_getnval(&(CURTASK exps[level + 1])))
      {
         CURTASK         exps[level - 1].nval = TRUE;
      }
      else
      {
         CURTASK         exps[level - 1].nval = 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.
  
***************************************************************/

static int
op_lteq(int level, int precision)
{
   bwx_DEBUG(__FUNCTION__);

   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))
      {
         sprintf(bwb_ebuf, "Type mismatch in string comparison.");
         bwb_error(bwb_ebuf);
         return FALSE;
      }
      /* compare the two strings */

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

   case NUMBER:
      if (exp_getnval(&(CURTASK exps[level - 1]))
          <= exp_getnval(&(CURTASK exps[level + 1])))
      {
         CURTASK         exps[level - 1].nval = TRUE;
      }
      else
      {
         CURTASK         exps[level - 1].nval = 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.
  
***************************************************************/

static int
op_gteq(int level, int precision)
{
   bwx_DEBUG(__FUNCTION__);

   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))
      {
         sprintf(bwb_ebuf, "Type mismatch in string comparison.");
         bwb_error(bwb_ebuf);
         return FALSE;
      }
      /* compare the two strings */

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

   case NUMBER:
      if (exp_getnval(&(CURTASK exps[level - 1]))
          >= exp_getnval(&(CURTASK exps[level + 1])))
      {
         CURTASK         exps[level - 1].nval = TRUE;
      }
      else
      {
         CURTASK         exps[level - 1].nval = 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.
  
***************************************************************/

static int
op_notequal(int level, int precision)
{
   bwx_DEBUG(__FUNCTION__);

   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))
      {
         sprintf(bwb_ebuf, "Type mismatch in string comparison.");
         bwb_error(bwb_ebuf);
         return FALSE;
      }
      /* compare the two strings */

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

   case NUMBER:
      if (exp_getnval(&(CURTASK exps[level - 1]))
          != exp_getnval(&(CURTASK exps[level + 1])))
      {
         CURTASK         exps[level - 1].nval = TRUE;
      }
      else
      {
         CURTASK         exps[level - 1].nval = 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.
  
***************************************************************/

static int
op_modulus(int level, int precision)
{
   static double   iportion;

   bwx_DEBUG(__FUNCTION__);

   switch (precision)
   {
   case STRING:

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

      sprintf(bwb_ebuf, "Strings cannot be divided.");
      bwb_error(bwb_ebuf);
      return FALSE;

      break;

   case NUMBER:
      {
         /* N = X MOD Y */
         if (exp_getnval(&(CURTASK exps[level + 1]))
             == 0)
         {
            CURTASK         exps[level - 1].nval = -1;
            op_pulldown(2);
            sprintf(bwb_ebuf, "Modulus by 0.");
            bwb_error(bwb_ebuf);
            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.
  
***************************************************************/

static int
op_exponent(int level, int precision)
{

   bwx_DEBUG(__FUNCTION__);


   switch (precision)
   {
   case STRING:

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

      sprintf(bwb_ebuf, "Strings cannot be taken as exponents.");
      bwb_error(bwb_ebuf);
      return FALSE;

      break;

   case NUMBER:
      if (TRUE)
      {
         /* N = X ^ Y */
         BasicNumberType X;
         BasicNumberType Y;
         BasicNumberType N;

         X = exp_getnval(&(CURTASK exps[level - 1]));
         Y = exp_getnval(&(CURTASK exps[level + 1]));

         if (X < 0 && Y != (int) Y)
         {
            /*** FATAL ***/
            /* - Evaluation of the operation of
             * involution results in a nega- tive number
             * being raised to a non-integral power
             * (fatal). */
            N = 0;
            bwb_error("NEGATIVE QUANTITY RAISED TO A NON-INTEGRAL POWER");
            return FALSE;
         }
         else
         if (X == 0 && Y < 0)
         {
            /* - Evaluation of the operation of
             * involution results in a zero be- ing
             * raised to a negative value (nonfatal, the
             * recommended re- covery procedure is to
             * supply positive machine infinity and
             * continue). */

            N = (DBL_MAX);
            bwb_Warning_Overflow("*** Arithmetic Overflow ***");
         }
         else
         {
            N = pow(X, Y);
         }
         CURTASK         exps[level - 1].nval = N;
      }
      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.
  
***************************************************************/

static int
op_intdiv(int level, int precision)
{
   bwx_DEBUG(__FUNCTION__);

   switch (precision)
   {
   case STRING:

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

      sprintf(bwb_ebuf, "Strings cannot be divided.");
      bwb_error(bwb_ebuf);
      return FALSE;

      break;

   default:
      {
         /* N = X \ Y  */
         long            X;
         long            Y;
         long            N;
         X = exp_getival(&(CURTASK exps[level - 1]));
         Y = exp_getival(&(CURTASK exps[level + 1]));
         if (Y == 0)
         {
            sprintf(bwb_ebuf, "Integer Divide by 0.");
            bwb_error(bwb_ebuf);
            return FALSE;
         }
         N = X / Y;
         CURTASK         exps[level - 1].nval = N;
      }
      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.
  
***************************************************************/

static int
op_or(int level, int precision)
{
   bwx_DEBUG(__FUNCTION__);

   switch (precision)
   {
   case STRING:

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

      sprintf(bwb_ebuf, "Strings cannot be compared logically.");
      bwb_error(bwb_ebuf);
      return FALSE;

      break;

   case NUMBER:
      {
         /* N = X OR Y  */
         long            X;
         long            Y;
         long            N;
         X = exp_getival(&(CURTASK exps[level - 1]));
         Y = exp_getival(&(CURTASK exps[level + 1]));
         N = X | Y;
         CURTASK         exps[level - 1].nval = N;
      }
      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.
  
***************************************************************/

static int
op_and(int level, int precision)
{
   bwx_DEBUG(__FUNCTION__);

   switch (precision)
   {
   case STRING:


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

      sprintf(bwb_ebuf, "Strings cannot be compared logically.");
      bwb_error(bwb_ebuf);
      return FALSE;

      break;

   case NUMBER:
      {
         /* N = X AND Y  */
         long            X;
         long            Y;
         long            N;
         X = exp_getival(&(CURTASK exps[level - 1]));
         Y = exp_getival(&(CURTASK exps[level + 1]));
         N = X & Y;
         CURTASK         exps[level - 1].nval = N;
      }
      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.
  
***************************************************************/

static int
op_not(int level, int precision)
{
   bwx_DEBUG(__FUNCTION__);

   switch (precision)
   {
   case STRING:


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

      sprintf(bwb_ebuf, "Strings cannot be compared logically.");
      bwb_error(bwb_ebuf);
      return FALSE;

      break;

   default:
      {
         /* N = NOT X  */
         long            X;
         long            N;
         X = exp_getival(&(CURTASK exps[level + 1]));
         N = ~X;
         CURTASK         exps[level /*- 1*/ ].nval = N;
      }
      break;
   }

   /* set variable type to integer */

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

   /* decrement the stack once */

   op_pulldown(1);


   return TRUE;

}

/***************************************************************
  
   FUNCTION:       op_xor()
  
   DESCRIPTION:    This function compares two integers and
         performs a logical XOR on them.
  
***************************************************************/

static int
op_xor(int level, int precision)
{
   bwx_DEBUG(__FUNCTION__);

   switch (precision)
   {
   case STRING:

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

      sprintf(bwb_ebuf, "Strings cannot be compared logically.");
      bwb_error(bwb_ebuf);
      return FALSE;

      break;

   case NUMBER:
      {
         /* N = X XOR Y  */
         long            X;
         long            Y;
         long            N;
         X = exp_getival(&(CURTASK exps[level - 1]));
         Y = exp_getival(&(CURTASK exps[level + 1]));
         N = X ^ Y;
         CURTASK         exps[level - 1].nval = N;
      }
      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_eqv()
  
   DESCRIPTION:    This function compares two integers and
         performs a logical EQV on them.
  
***************************************************************/

static int
op_eqv(int level, int precision)
{
   bwx_DEBUG(__FUNCTION__);

   switch (precision)
   {
   case STRING:

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

      sprintf(bwb_ebuf, "Strings cannot be compared logically.");
      bwb_error(bwb_ebuf);
      return FALSE;

      break;

   case NUMBER:
      {
         /* N = X EQV Y = NOT ( X XOR Y ) */
         long            X;
         long            Y;
         long            N;
         X = exp_getival(&(CURTASK exps[level - 1]));
         Y = exp_getival(&(CURTASK exps[level + 1]));
         N = ~(X ^ Y);
         CURTASK         exps[level - 1].nval = N;
      }
      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_imp()
  
   DESCRIPTION:    This function compares two integers and
         performs a logical IMP on them.
  
***************************************************************/

static int
op_imp(int level, int precision)
{
   bwx_DEBUG(__FUNCTION__);

   switch (precision)
   {
   case STRING:

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

      sprintf(bwb_ebuf, "Strings cannot be compared logically.");
      bwb_error(bwb_ebuf);
      return FALSE;

      break;

   case NUMBER:
      {
         /* N = X IMP Y  = (X AND Y) OR (NOT X) */
         long            X;
         long            Y;
         long            N;
         X = exp_getival(&(CURTASK exps[level - 1]));
         Y = exp_getival(&(CURTASK exps[level + 1]));
         N = (X & Y) | (~X);
         CURTASK         exps[level - 1].nval = N;
      }
      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
  
***************************************************************/

static int
op_negation(int level, int precision)
{
   bwx_DEBUG(__FUNCTION__);

   switch (precision)
   {
   case STRING:


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

      sprintf(bwb_ebuf, "Strings cannot be compared logically.");
      bwb_error(bwb_ebuf);
      return FALSE;

      break;

   default:


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

      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);


   return TRUE;

}

static int
op_posation(int level, int precision)
{
   bwx_DEBUG(__FUNCTION__);

   switch (precision)
   {
   case STRING:


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

      sprintf(bwb_ebuf, "Strings cannot be compared logically.");
      bwb_error(bwb_ebuf);
      return FALSE;

      break;

   default:


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


      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);


   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)
{

   bwx_DEBUG(__FUNCTION__);

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

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


      return TRUE;
   }
   /* see if the level holds a string variable */

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


         return TRUE;
      }
   }
   /* test has failed, return FALSE */


   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)
{
   bwx_DEBUG(__FUNCTION__);

   /* 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.
  
***************************************************************/

static int
op_pulldown(int how_far)
{
   int             level;
   register int    c;

   bwx_DEBUG(__FUNCTION__);

   /* 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;

}

/* EOF */
