/**************************************************************** bwb_ops.c Expression Parsing Operations for Bywater BASIC Interpreter Copyright (c) 1993, Ted A. Campbell Bywater Software email: tcamp@delphi.com Copyright and Permissions Information: All U.S. and international rights are claimed by the author, Ted A. Campbell. This software is released under the terms of the GNU General Public License (GPL), which is distributed with this software in the file "COPYING". The GPL specifies the terms under which users may copy and use the software in this distribution. A separate license is available for commercial distribution, for information on which you should contact the author. ****************************************************************/ /*---------------------------------------------------------------*/ /* NOTE: Modifications marked "JBV" were made by Jon B. Volkoff, */ /* 11/1995 (eidetics@cerf.net). */ /*---------------------------------------------------------------*/ #include #include #include #include "bwbasic.h" #include "bwb_mes.h" /* declarations for functions visible in this file only */ #if ANSI_C static int op_oplevel( int level ); static int op_add( int level, int precision ); static int op_subtract( int level, int precision ); static int op_multiply( int level, int precision ); static int op_divide( int level, int precision ); static int op_assign( int level, int precision ); static int op_equals( int level, int precision ); static int op_lessthan( int level, int precision ); static int op_greaterthan( int level, int precision ); static int op_lteq( int level, int precision ); static int op_gteq( int level, int precision ); static int op_notequal( int level, int precision ); static int op_modulus( int level, int precision ); static int op_exponent( int level, int precision ); static int op_intdiv( int level, int precision ); static int op_or( int level, int precision ); static int op_and( int level, int precision ); static int op_not( int level, int precision ); static int op_xor( int level, int precision ); static int op_negation( int level, int precision ); /* JBV */ static int op_islevelstr( int level ); static int op_getprecision( int level ); static int op_isoperator( int operation ); static int op_pulldown( int how_far ); #else static int op_oplevel(); static int op_add(); static int op_subtract(); static int op_multiply(); static int op_divide(); static int op_assign(); static int op_equals(); static int op_lessthan(); static int op_greaterthan(); static int op_lteq(); static int op_gteq(); static int op_notequal(); static int op_modulus(); static int op_exponent(); static int op_intdiv(); static int op_or(); static int op_and(); static int op_not(); static int op_xor(); static int op_negation(); /* JBV */ static int op_islevelstr(); static int op_getprecision(); static int op_isoperator(); static int op_pulldown(); #endif /* ANSI_C for prototypes */ static int op_level; /*************************************************************** FUNCTION: exp_operation() DESCRIPTION: This function performs whatever operations are necessary at the end of function bwb_exp() (i.e., the end of the parsing of an expression; see file bwb_exp.c). ***************************************************************/ #if ANSI_C int exp_operation( int entry_level ) #else int exp_operation( entry_level ) int entry_level; #endif { register int precedence; int operator; #if INTENSIVE_DEBUG sprintf( bwb_ebuf, "in exp_operation(): entered function." ); bwb_debug( bwb_ebuf ); #endif /* cycle through all levels of precedence and perform required operations */ for ( precedence = 0; precedence <= MAX_PRECEDENCE; ++precedence ) { /* Operation loop: cycle through every level above entry level and perform required operations as needed */ op_level = entry_level + 1; while( ( op_level < CURTASK expsc ) && ( op_isoperator( CURTASK exps[ op_level ].operation ) == FALSE )) { ++op_level; } while ( ( op_level > entry_level ) && ( op_level < CURTASK expsc ) ) { /* see if the operation at this level is an operator with the appropriate precedence level by running through the table of operators */ for ( operator = 0; operator < N_OPERATORS; ++operator ) { if ( exp_ops[ operator ].operation == CURTASK exps[ op_level ].operation ) { /* check for appropriate level of precedence */ if ( exp_ops[ operator ].precedence == precedence ) { #if INTENSIVE_DEBUG sprintf( bwb_ebuf, "in exp_operation(): level <%d> operation <%d>", op_level, CURTASK exps[ op_level ].operation ); bwb_debug( bwb_ebuf ); #endif op_oplevel( op_level ); /* perform the operation */ } } } /* advance level if appropriate; one must check, however, since the op_oplevel() function may have decremented CURTASK expsc */ if ( op_level < CURTASK expsc ) { ++op_level; #if INTENSIVE_DEBUG sprintf( bwb_ebuf, "in exp_operation() first increment op_level to <%d>", op_level ); bwb_debug( bwb_ebuf ); #endif while ( ( op_isoperator( CURTASK exps [ op_level ].operation ) == FALSE ) && ( op_level < CURTASK expsc ) ) { ++op_level; #if INTENSIVE_DEBUG sprintf( bwb_ebuf, "in exp_operation() further increment op_level to <%d>", op_level ); bwb_debug( bwb_ebuf ); #endif } } /* end of increment of op_level */ } /* end of for loop for stack levels */ } /* end of for loop for precedence levels */ return TRUE; } /* end of function exp_operation() */ /*************************************************************** FUNCTION: op_oplevel() DESCRIPTION: This function performs a specific operation at a specific level as the expression parser resolves its arguments. ***************************************************************/ #if ANSI_C static int op_oplevel( int level ) #else static int op_oplevel( level ) int level; #endif { int precision; /* set the precision */ if ( ( precision = op_getprecision( level ) ) == OP_ERROR ) { #if PROG_ERRORS sprintf( bwb_ebuf, "exp_operation(): failed to set precision." ); bwb_error( bwb_ebuf ); #else bwb_error( err_mismatch ); /*** ??? ***/ #endif op_pulldown( 2 ); } /* precision is set correctly */ else { #if INTENSIVE_DEBUG sprintf( bwb_ebuf, "in op_oplevel(): level <%d>, precision <%c>", level, precision ); bwb_debug( bwb_ebuf ); #endif switch ( CURTASK exps[ level ].operation ) { case OP_ADD: op_add( level, precision ); break; case OP_SUBTRACT: op_subtract( level, precision ); break; case OP_MULTIPLY: op_multiply( level, precision ); break; case OP_DIVIDE: op_divide( level, precision ); break; case OP_ASSIGN: op_assign( level, precision ); break; case OP_EQUALS: op_equals( level, precision ); break; case OP_LESSTHAN: op_lessthan( level, precision ); break; case OP_GREATERTHAN: op_greaterthan( level, precision ); break; case OP_LTEQ: op_lteq( level, precision ); break; case OP_GTEQ: op_gteq( level, precision ); break; case OP_NOTEQUAL: op_notequal( level, precision ); break; case OP_MODULUS: op_modulus( level, precision ); break; case OP_INTDIVISION: op_intdiv( level, precision ); break; case OP_OR: op_or( level, precision ); break; case OP_AND: op_and( level, precision ); break; case OP_NOT: op_not( level, precision ); break; case OP_XOR: op_xor( level, precision ); break; case OP_EXPONENT: op_exponent( level, precision ); break; case OP_NEGATION: /* JBV */ op_negation( level, precision ); break; default: #if PROG_ERRORS sprintf( bwb_ebuf, "PROGRAMMING ERROR: operator <%d> not (yet) supported.", CURTASK exps[ level ].operation ); op_pulldown( 2 ); bwb_error( bwb_ebuf ); #else bwb_error( err_syntax ); #endif break; } /* end of case statement for operators */ } /* end of else statement, precision set */ return TRUE; } /* end of function op_oplevel() */ /*************************************************************** FUNCTION: op_isoperator() DESCRIPTION: This function detects whether its argument is an operator. ***************************************************************/ #if ANSI_C static int op_isoperator( int operation ) #else static int op_isoperator( operation ) int operation; #endif { register int c; for( c = 0; c < N_OPERATORS; ++c ) { if ( operation == exp_ops[ c ].operation ) { #if INTENSIVE_DEBUG sprintf( bwb_ebuf, "in op_isoperator(): found match <%s>", exp_ops[ c ].symbol ); bwb_debug( bwb_ebuf ); #endif return TRUE; } } /* test failed; return FALSE */ #if INTENSIVE_DEBUG sprintf( bwb_ebuf, "in op_isoperator(): no match found for operation <%d>", operation ); bwb_debug( bwb_ebuf ); #endif return FALSE; } /*************************************************************** FUNCTION: op_add() DESCRIPTION: This function adds two numbers or concatenates two strings. ***************************************************************/ #if ANSI_C static int op_add( int level, int precision ) #else static int op_add( level, precision ) int level; int precision; #endif { int error_condition; static bstring b; /* JBV */ error_condition = FALSE; b.rab = FALSE; /* JBV */ switch( precision ) { case STRING: /* both sides of the operation should be strings for string addition; if not, report an error */ if ( ( op_islevelstr( level - 1 ) != TRUE ) || ( op_islevelstr( level + 1 ) != TRUE ) ) { #if PROG_ERRORS sprintf( bwb_ebuf, "in op_add(): Type mismatch in string addition." ); bwb_error( bwb_ebuf ); #else bwb_error( err_mismatch ); #endif error_condition = TRUE; } /* concatenate the two strings */ if ( error_condition == FALSE ) { #if INTENSIVE_DEBUG sprintf( bwb_ebuf, "in op_add(): try exp_getsval(), level <%d> op <%d> type <%c>:", level - 1, CURTASK exps[ level - 1 ].operation, CURTASK exps[ level - 1 ].type ); bwb_debug( bwb_ebuf ); exp_getsval( &( CURTASK exps[ level - 1 ] )); sprintf( bwb_ebuf, "in op_add(): try exp_getsval(), level <%d> op <%d> type <%c>:", level + 1, CURTASK exps[ level + 1 ].operation, CURTASK exps[ level + 1 ].type ); bwb_debug( bwb_ebuf ); exp_getsval( &( CURTASK exps[ level + 1 ] )); sprintf( bwb_ebuf, "in op_add(): string addition, exp_getsval()s completed" ); bwb_debug( bwb_ebuf ); #endif /* Removed by JBV (incomplete, modifies wrong string variable!) */ /* str_cat( exp_getsval( &( CURTASK exps[ level - 1 ] ) ), exp_getsval( &( CURTASK exps[ level + 1 ] ) ) ); */ /* Added by JBV */ str_btob( &b, exp_getsval( &( CURTASK exps[ level - 1 ] ) ) ); str_cat( &b, exp_getsval( &( CURTASK exps[ level + 1 ] ) ) ); str_btob( &( CURTASK exps[ level - 1 ].sval ), &b ); CURTASK exps[ level - 1 ].operation = CONST_STRING; #if INTENSIVE_DEBUG sprintf( bwb_ebuf, "in op_add(): str_cat() returns <%d>-byte string to level <%d>", exp_getsval( &( CURTASK exps[ level - 1 ] ) )->length, level - 1 ); bwb_debug( bwb_ebuf ); #endif } break; case NUMBER: CURTASK exps[ level - 1 ].nval = exp_getnval( &( CURTASK exps[ level - 1 ] )) + exp_getnval( &( CURTASK exps[ level + 1 ] )); CURTASK exps[ level - 1 ].operation = NUMBER; break; } /* set variable to requested precision */ CURTASK exps[ level - 1 ].type = (char) precision; #if INTENSIVE_DEBUG sprintf( bwb_ebuf, "in op_add() returns with operation <%d> type <%c>", CURTASK exps[ level - 1 ].operation, CURTASK exps[ level - 1 ].type ); bwb_debug( bwb_ebuf ); #endif /* decrement the stack twice */ op_pulldown( 2 ); return TRUE; } /*************************************************************** FUNCTION: op_subtract() DESCRIPTION: This function subtracts the number on the left from the number on the right. ***************************************************************/ #if ANSI_C static int op_subtract( int level, int precision ) #else static int op_subtract( level, precision ) int level; int precision; #endif { switch( precision ) { case STRING: /* both sides of the operation should be numbers for string addition; if not, report an error */ #if PROG_ERRORS sprintf( bwb_ebuf, "Strings cannot be subtracted." ); bwb_error( bwb_ebuf ); #else bwb_error( err_mismatch ); #endif break; case NUMBER: CURTASK exps[ level - 1 ].nval = exp_getnval( &( CURTASK exps[ level - 1 ] )) - exp_getnval( &( CURTASK exps[ level + 1 ] )); break; } /* set variable to requested precision */ CURTASK exps[ level - 1 ].type = (char) precision; CURTASK exps[ level - 1 ].operation = NUMBER; /* decrement the stack twice */ op_pulldown( 2 ); return TRUE; } /*************************************************************** FUNCTION: op_multiply() DESCRIPTION: This function multiplies the number on the left by the number on the right. ***************************************************************/ #if ANSI_C static int op_multiply( int level, int precision ) #else static int op_multiply( level, precision ) int level; int precision; #endif { switch( precision ) { case STRING: /* both sides of the operation should be numbers for string addition; if not, report an error */ #if PROG_ERRORS sprintf( bwb_ebuf, "Strings cannot be multiplied." ); bwb_error( bwb_ebuf ); #else bwb_error( err_mismatch ); #endif break; case NUMBER: CURTASK exps[ level - 1 ].nval = exp_getnval( &( CURTASK exps[ level - 1 ] )) * exp_getnval( &( CURTASK exps[ level + 1 ] )); break; } /* set variable to requested precision */ CURTASK exps[ level - 1 ].type = (char) precision; CURTASK exps[ level - 1 ].operation = NUMBER; /* decrement the stack twice */ op_pulldown( 2 ); return TRUE; } /*************************************************************** FUNCTION: op_divide() DESCRIPTION: This function divides the number on the left by the number on the right. ***************************************************************/ #if ANSI_C static int op_divide( int level, int precision ) #else static int op_divide( level, precision ) int level; int precision; #endif { switch( precision ) { case STRING: /* both sides of the operation should be numbers for division; if not, report an error */ #if PROG_ERRORS sprintf( bwb_ebuf, "Strings cannot be divided." ); bwb_error( bwb_ebuf ); #else bwb_error( err_mismatch ); #endif break; case NUMBER: if ( exp_getnval( &( CURTASK exps[ level + 1 ] )) == (bnumber) 0 ) { CURTASK exps[ level - 1 ].nval = (bnumber) -1.0; op_pulldown( 2 ); #if PROG_ERRORS sprintf( bwb_ebuf, "Divide by 0." ); bwb_error( bwb_ebuf ); #else bwb_error( err_dbz ); #endif return FALSE; } CURTASK exps[ level - 1 ].nval = exp_getnval( &( CURTASK exps[ level - 1 ] )) / exp_getnval( &( CURTASK exps[ level + 1 ] )); break; } /* set variable to requested precision */ CURTASK exps[ level - 1 ].type = (char) precision; CURTASK exps[ level - 1 ].operation = NUMBER; /* decrement the stack twice */ op_pulldown( 2 ); return TRUE; } /*************************************************************** FUNCTION: op_assign() DESCRIPTION: This function assigns the value in the right hand side to the variable in the left hand side. ***************************************************************/ #if ANSI_C static int op_assign( int level, int precision ) #else static int op_assign( level, precision ) int level; int precision; #endif { /* Make sure the position one level below is a variable */ if ( CURTASK exps[ level - 1 ].operation != VARIABLE ) { op_pulldown( 2 ); #if PROG_ERRORS sprintf( bwb_ebuf, "in op_assign(): Assignment must be to variable: level -1 <%d> op <%d>", level - 1, CURTASK exps[ level - 1 ].operation ); bwb_error( bwb_ebuf ); #else bwb_error( err_syntax ); #endif return FALSE; } #if INTENSIVE_DEBUG sprintf( bwb_ebuf, "in op_assign(): entered function level <%d>", level ); bwb_debug( bwb_ebuf ); #endif /* if the assignment is numerical, then the precision should be set to that of the variable on the left-hand side of the assignment */ if ( precision != STRING ) { precision = (int) CURTASK exps[ level - 1 ].type; } switch( precision ) { case STRING: #if INTENSIVE_DEBUG sprintf( bwb_ebuf, "in op_assign(): try exp_getsval(), level <%d> op <%d> type <%c>:", level - 1, CURTASK exps[ level - 1 ].operation, CURTASK exps[ level - 1 ].type ); bwb_debug( bwb_ebuf ); exp_getsval( &( CURTASK exps[ level - 1 ] )); sprintf( bwb_ebuf, "in op_assign(): try exp_getsval(), level <%d> op <%d> type <%c>:", level + 1, CURTASK exps[ level + 1 ].operation, CURTASK exps[ level + 1 ].type ); bwb_debug( bwb_ebuf ); exp_getsval( &( CURTASK exps[ level + 1 ] )); sprintf( bwb_ebuf, "in op_assign(): string addition, exp_getsval()s completed" ); bwb_debug( bwb_ebuf ); #endif str_btob( exp_getsval( &( CURTASK exps[ level - 1 ] )), exp_getsval( &( CURTASK exps[ level + 1 ] )) ); break; case NUMBER: * var_findnval( CURTASK exps[ level - 1 ].xvar, CURTASK exps[ level - 1 ].array_pos ) = CURTASK exps[ level - 1 ].nval = exp_getnval( &( CURTASK exps[ level + 1 ] ) ); break; default: #if PROG_ERRORS sprintf( bwb_ebuf, "in op_assign(): Variable before assignment operator has unidentified type." ); bwb_error( bwb_ebuf ); #else bwb_error( err_mismatch ); #endif return FALSE; } /* set variable to requested precision */ CURTASK exps[ level - 1 ].type = (char) precision; /* decrement the stack twice */ op_pulldown( 2 ); return TRUE; } /*************************************************************** FUNCTION: op_equals() DESCRIPTION: This function compares two values and returns an integer value: TRUE if they are the same and FALSE if they are not. ***************************************************************/ #if ANSI_C static int op_equals( int level, int precision ) #else static int op_equals( level, precision ) int level; int precision; #endif { int error_condition; static bstring b; bstring *bp; error_condition = FALSE; b.rab = FALSE; switch( precision ) { case STRING: /* both sides of the operation should be strings for string addition; if not, report an error */ if ( ( op_islevelstr( level - 1 ) != TRUE ) || ( op_islevelstr( level + 1 ) != TRUE ) ) { #if PROG_ERRORS sprintf( bwb_ebuf, "in op_equals(): Type mismatch in string comparison." ); bwb_error( bwb_ebuf ); #else bwb_error( err_mismatch ); #endif error_condition = TRUE; } /* compare the two strings */ if ( error_condition == FALSE ) { bp = exp_getsval( &( CURTASK exps[ level - 1 ] )); #if OLDWAY b.length = bp->length; b.sbuffer = bp->sbuffer; #endif str_btob( &b, bp ); if ( str_cmp( &b, exp_getsval( &( CURTASK exps[ level + 1 ] )) ) == 0 ) { CURTASK exps[ level - 1 ].nval = (bnumber) TRUE; } else { CURTASK exps[ level - 1 ].nval = (bnumber) FALSE; } } break; case NUMBER: if ( exp_getnval( &( CURTASK exps[ level - 1 ] )) == exp_getnval( &( CURTASK exps[ level + 1 ] )) ) { CURTASK exps[ level - 1 ].nval = (bnumber) TRUE; } else { CURTASK exps[ level - 1 ].nval = (bnumber) FALSE; } break; } /* set variable to integer and operation to NUMBER: this must be done at the end, since at the beginning it might cause op_islevelstr() to return a false error */ CURTASK exps[ level - 1 ].type = NUMBER; CURTASK exps[ level - 1 ].operation = NUMBER; /* decrement the stack */ op_pulldown( 2 ); return TRUE; } /*************************************************************** FUNCTION: op_lessthan() DESCRIPTION: This function compares two values and returns an integer value: TRUE if the left hand value is less than the right, and FALSE if it is not. ***************************************************************/ #if ANSI_C static int op_lessthan( int level, int precision ) #else static int op_lessthan( level, precision ) int level; int precision; #endif { int error_condition; error_condition = FALSE; switch( precision ) { case STRING: /* both sides of the operation should be numbers for string addition; if not, report an error */ if ( ( op_islevelstr( level - 1 ) != TRUE ) || ( op_islevelstr( level + 1 ) != TRUE ) ) { #if PROG_ERRORS sprintf( bwb_ebuf, "Type mismatch in string comparison." ); bwb_error( bwb_ebuf ); #else bwb_error( err_mismatch ); #endif error_condition = TRUE; } /* compare the two strings */ if ( error_condition == FALSE ) { if ( str_cmp( exp_getsval( &( CURTASK exps[ level - 1 ] )), exp_getsval( &( CURTASK exps[ level + 1 ] )) ) < 0 ) { CURTASK exps[ level - 1 ].nval = (bnumber) TRUE; } else { CURTASK exps[ level - 1 ].nval = (bnumber) FALSE; } } break; case NUMBER: if ( exp_getnval( &( CURTASK exps[ level - 1 ] )) < exp_getnval( &( CURTASK exps[ level + 1 ] )) ) { CURTASK exps[ level - 1 ].nval = (bnumber) TRUE; } else { CURTASK exps[ level - 1 ].nval = (bnumber) FALSE; } break; } /* set variable to integer and operation to NUMBER: this must be done at the end, since at the beginning it might cause op_islevelstr() to return a false error */ CURTASK exps[ level - 1 ].type = NUMBER; CURTASK exps[ level - 1 ].operation = NUMBER; /* decrement the stack */ op_pulldown( 2 ); return TRUE; } /*************************************************************** FUNCTION: op_greaterthan() DESCRIPTION: This function compares two values and returns an integer value: TRUE if the left hand value is greater than the right, and FALSE if it is not. ***************************************************************/ #if ANSI_C static int op_greaterthan( int level, int precision ) #else static int op_greaterthan( level, precision ) int level; int precision; #endif { int error_condition; error_condition = FALSE; switch( precision ) { case STRING: /* both sides of the operation should be numbers for string addition; if not, report an error */ if ( ( op_islevelstr( level - 1 ) != TRUE ) || ( op_islevelstr( level + 1 ) != TRUE ) ) { #if PROG_ERRORS sprintf( bwb_ebuf, "Type mismatch in string comparison." ); bwb_error( bwb_ebuf ); #else bwb_error( err_mismatch ); #endif error_condition = TRUE; } /* compare the two strings */ if ( error_condition == FALSE ) { if ( str_cmp( exp_getsval( &( CURTASK exps[ level - 1 ] )), exp_getsval( &( CURTASK exps[ level + 1 ] )) ) > 0 ) { CURTASK exps[ level - 1 ].nval = (bnumber) TRUE; } else { CURTASK exps[ level - 1 ].nval = (bnumber) FALSE; } } break; case NUMBER: if ( exp_getnval( &( CURTASK exps[ level - 1 ] )) > exp_getnval( &( CURTASK exps[ level + 1 ] )) ) { CURTASK exps[ level - 1 ].nval = (bnumber) TRUE; } else { CURTASK exps[ level - 1 ].nval = (bnumber) FALSE; } break; } /* set variable to integer and operation to NUMBER: this must be done at the end, since at the beginning it might cause op_islevelstr() to return a false error */ CURTASK exps[ level - 1 ].type = NUMBER; CURTASK exps[ level - 1 ].operation = NUMBER; /* decrement the stack */ op_pulldown( 2 ); return TRUE; } /*************************************************************** FUNCTION: op_lteq() DESCRIPTION: This function compares two values and returns an integer value: TRUE if the left hand value is less than or equal to the right, and FALSE if it is not. ***************************************************************/ #if ANSI_C static int op_lteq( int level, int precision ) #else static int op_lteq( level, precision ) int level; int precision; #endif { int error_condition; error_condition = FALSE; switch( precision ) { case STRING: /* both sides of the operation should be numbers for string addition; if not, report an error */ if ( ( op_islevelstr( level - 1 ) != TRUE ) || ( op_islevelstr( level + 1 ) != TRUE ) ) { #if PROG_ERRORS sprintf( bwb_ebuf, "Type mismatch in string comparison." ); bwb_error( bwb_ebuf ); #else bwb_error( err_mismatch ); #endif error_condition = TRUE; } /* compare the two strings */ if ( error_condition == FALSE ) { if ( str_cmp( exp_getsval( &( CURTASK exps[ level - 1 ] )), exp_getsval( &( CURTASK exps[ level + 1 ] )) ) <= 0 ) { CURTASK exps[ level - 1 ].nval = (bnumber) TRUE; } else { CURTASK exps[ level - 1 ].nval = (bnumber) FALSE; } } break; case NUMBER: if ( exp_getnval( &( CURTASK exps[ level - 1 ] )) <= exp_getnval( &( CURTASK exps[ level + 1 ] )) ) { CURTASK exps[ level - 1 ].nval = (bnumber) TRUE; } else { CURTASK exps[ level - 1 ].nval = (bnumber) FALSE; } break; } /* set variable to integer and operation to NUMBER: this must be done at the end, since at the beginning it might cause op_islevelstr() to return a false error */ CURTASK exps[ level - 1 ].type = NUMBER; CURTASK exps[ level - 1 ].operation = NUMBER; /* decrement the stack */ op_pulldown( 2 ); return TRUE; } /*************************************************************** FUNCTION: op_gteq() DESCRIPTION: This function compares two values and returns an integer value: TRUE if the left hand value is greater than or equal to the right, and FALSE if it is not. ***************************************************************/ #if ANSI_C static int op_gteq( int level, int precision ) #else static int op_gteq( level, precision ) int level; int precision; #endif { int error_condition; error_condition = FALSE; switch( precision ) { case STRING: /* both sides of the operation should be numbers for string addition; if not, report an error */ if ( ( op_islevelstr( level - 1 ) != TRUE ) || ( op_islevelstr( level + 1 ) != TRUE ) ) { #if PROG_ERRORS sprintf( bwb_ebuf, "Type mismatch in string comparison." ); bwb_error( bwb_ebuf ); #else bwb_error( err_mismatch ); #endif error_condition = TRUE; } /* compare the two strings */ if ( error_condition == FALSE ) { if ( str_cmp( exp_getsval( &( CURTASK exps[ level - 1 ] )), exp_getsval( &( CURTASK exps[ level + 1 ] )) ) >= 0 ) { CURTASK exps[ level - 1 ].nval = (bnumber) TRUE; } else { CURTASK exps[ level - 1 ].nval = (bnumber) FALSE; } } break; case NUMBER: if ( exp_getnval( &( CURTASK exps[ level - 1 ] )) >= exp_getnval( &( CURTASK exps[ level + 1 ] )) ) { CURTASK exps[ level - 1 ].nval = (bnumber) TRUE; } else { CURTASK exps[ level - 1 ].nval = (bnumber) FALSE; } break; } /* set variable to integer and operation to NUMBER: this must be done at the end, since at the beginning it might cause op_islevelstr() to return a false error */ CURTASK exps[ level - 1 ].type = NUMBER; CURTASK exps[ level - 1 ].operation = NUMBER; /* decrement the stack */ op_pulldown( 2 ); return TRUE; } /*************************************************************** FUNCTION: op_notequal() DESCRIPTION: This function compares two values and returns an integer value: TRUE if they are not the same and FALSE if they are. ***************************************************************/ #if ANSI_C static int op_notequal( int level, int precision ) #else static int op_notequal( level, precision ) int level; int precision; #endif { int error_condition; error_condition = FALSE; switch( precision ) { case STRING: /* both sides of the operation should be numbers for string addition; if not, report an error */ if ( ( op_islevelstr( level - 1 ) != TRUE ) || ( op_islevelstr( level + 1 ) != TRUE ) ) { #if PROG_ERRORS sprintf( bwb_ebuf, "Type mismatch in string comparison." ); bwb_error( bwb_ebuf ); #else bwb_error( err_mismatch ); #endif error_condition = TRUE; } /* compare the two strings */ if ( error_condition == FALSE ) { if ( str_cmp( exp_getsval( &( CURTASK exps[ level - 1 ] )), exp_getsval( &( CURTASK exps[ level + 1 ] )) ) != 0 ) { CURTASK exps[ level - 1 ].nval = (bnumber) TRUE; } else { CURTASK exps[ level - 1 ].nval = (bnumber) FALSE; } } break; case NUMBER: if ( exp_getnval( &( CURTASK exps[ level - 1 ] )) != exp_getnval( &( CURTASK exps[ level + 1 ] )) ) { CURTASK exps[ level - 1 ].nval = (bnumber) TRUE; } else { CURTASK exps[ level - 1 ].nval = (bnumber) FALSE; } break; } /* set variable to integer and operation to NUMBER: this must be done at the end, since at the beginning it might cause op_islevelstr() to return a false error */ CURTASK exps[ level - 1 ].type = NUMBER; CURTASK exps[ level - 1 ].operation = NUMBER; /* decrement the stack */ op_pulldown( 2 ); return TRUE; } /*************************************************************** FUNCTION: op_modulus() DESCRIPTION: This function divides the number on the left by the number on the right and returns the remainder. ***************************************************************/ #if ANSI_C static int op_modulus( int level, int precision ) #else static int op_modulus( level, precision ) int level; int precision; #endif { static double iportion; switch( precision ) { case STRING: /* both sides of the operation should be numbers for string addition; if not, report an error */ #if PROG_ERRORS sprintf( bwb_ebuf, "Strings cannot be divided." ); bwb_error( bwb_ebuf ); #else bwb_error( err_syntax ); #endif break; case NUMBER: if ( exp_getnval( &( CURTASK exps[ level + 1 ] )) == (bnumber) 0 ) { CURTASK exps[ level - 1 ].nval = (bnumber) -1; op_pulldown( 2 ); #if PROG_ERRORS sprintf( bwb_ebuf, "Divide by 0." ); bwb_error( bwb_ebuf ); #else bwb_error( err_dbz ); #endif return FALSE; } CURTASK exps[ level ].nval = exp_getnval( &( CURTASK exps[ level - 1 ] )) / exp_getnval( &( CURTASK exps[ level + 1 ] )); modf( (double) CURTASK exps[ level ].nval, &iportion ); CURTASK exps[ level - 1 ].nval = exp_getnval( &( CURTASK exps[ level - 1 ] )) - ( exp_getnval( &( CURTASK exps[ level + 1 ] )) * iportion ); break; } /* set variable to requested precision */ CURTASK exps[ level - 1 ].type = (char) precision; CURTASK exps[ level - 1 ].operation = NUMBER; /* decrement the stack twice */ op_pulldown( 2 ); return TRUE; } /*************************************************************** FUNCTION: op_exponent() DESCRIPTION: This function finds the exponential value of a number (on the left) to the power indicated on the right-hand side. ***************************************************************/ #if ANSI_C static int op_exponent( int level, int precision ) #else static int op_exponent( level, precision ) int level; int precision; #endif { #if INTENSIVE_DEBUG sprintf( bwb_ebuf, "in op_exponent(): entered function level <%d>.", level ); bwb_debug ( bwb_ebuf ); #endif switch( precision ) { case STRING: /* both sides of the operation should be numbers for string addition; if not, report an error */ #if PROG_ERRORS sprintf( bwb_ebuf, "Strings cannot be taken as exponents." ); bwb_error( bwb_ebuf ); #else bwb_error( err_mismatch ); #endif break; case NUMBER: CURTASK exps[ level - 1 ].nval = (bnumber) pow( (double) exp_getnval( &( CURTASK exps[ level - 1 ] )), (double) exp_getnval( &( CURTASK exps[ level + 1 ] )) ); break; } /* set variable to requested precision */ CURTASK exps[ level - 1 ].type = (char) precision; CURTASK exps[ level - 1 ].operation = NUMBER; /* decrement the stack twice */ op_pulldown( 2 ); return TRUE; } /*************************************************************** FUNCTION: op_intdiv() DESCRIPTION: This function divides the number on the left by the number on the right, returning the result as an integer. ***************************************************************/ #if ANSI_C static int op_intdiv( int level, int precision ) #else static int op_intdiv( level, precision ) int level; int precision; #endif { switch( precision ) { case STRING: /* both sides of the operation should be numbers for string addition; if not, report an error */ #if PROG_ERRORS sprintf( bwb_ebuf, "Strings cannot be divided." ); bwb_error( bwb_ebuf ); #else bwb_error( err_mismatch ); #endif break; default: if ( exp_getnval( &( CURTASK exps[ level + 1 ] )) == (bnumber) 0 ) { CURTASK exps[ level - 1 ].nval = (bnumber) -1; op_pulldown( 2 ); #if PROG_ERRORS sprintf( bwb_ebuf, "Divide by 0." ); bwb_error( bwb_ebuf ); #else bwb_error( err_dbz ); #endif return FALSE; } CURTASK exps[ level - 1 ].nval = exp_getnval( &( CURTASK exps[ level - 1 ] )) / exp_getnval( &( CURTASK exps[ level + 1 ] )); break; } /* set variable to requested precision */ CURTASK exps[ level - 1 ].type = NUMBER; CURTASK exps[ level - 1 ].operation = NUMBER; /* decrement the stack twice */ op_pulldown( 2 ); return TRUE; } /*************************************************************** FUNCTION: op_or() DESCRIPTION: This function compares two integers and performs a logical OR on them. ***************************************************************/ #if ANSI_C static int op_or( int level, int precision ) #else static int op_or( level, precision ) int level; int precision; #endif { switch( precision ) { case STRING: /* both sides of the operation should be numbers for logical comparison; if not, report an error */ #if PROG_ERRORS sprintf( bwb_ebuf, "Strings cannot be compared logically." ); bwb_error( bwb_ebuf ); #else bwb_error( err_mismatch ); #endif break; case NUMBER: CURTASK exps[ level - 1 ].nval = (bnumber) ((int) exp_getnval( &( CURTASK exps[ level - 1 ] )) | (int) exp_getnval( &( CURTASK exps[ level + 1 ] ))); break; } /* set variable type to integer */ CURTASK exps[ level - 1 ].type = NUMBER; CURTASK exps[ level - 1 ].operation = NUMBER; /* decrement the stack twice */ op_pulldown( 2 ); return TRUE; } /*************************************************************** FUNCTION: op_and() DESCRIPTION: This function compares two integers and performs a logical AND on them. ***************************************************************/ #if ANSI_C static int op_and( int level, int precision ) #else static int op_and( level, precision ) int level; int precision; #endif { switch( precision ) { case STRING: /* both sides of the operation should be numbers for logical comparison; if not, report an error */ #if PROG_ERRORS sprintf( bwb_ebuf, "Strings cannot be compared logically." ); bwb_error( bwb_ebuf ); #else bwb_error( err_mismatch ); #endif break; case NUMBER: CURTASK exps[ level - 1 ].nval = (bnumber) ((int) exp_getnval( &( CURTASK exps[ level - 1 ] )) & (int) exp_getnval( &( CURTASK exps[ level + 1 ] ))); break; } /* set variable type to integer */ CURTASK exps[ level - 1 ].type = NUMBER; CURTASK exps[ level - 1 ].operation = NUMBER; /* decrement the stack twice */ op_pulldown( 2 ); return TRUE; } /*************************************************************** FUNCTION: op_not() DESCRIPTION: This function performs a logical NOT on the integer to the right. ***************************************************************/ #if ANSI_C static int op_not( int level, int precision ) #else static int op_not( level, precision ) int level; int precision; #endif { switch( precision ) { case STRING: /* both sides of the operation should be numbers for logical comparison; if not, report an error */ #if PROG_ERRORS sprintf( bwb_ebuf, "Strings cannot be compared logically." ); bwb_error( bwb_ebuf ); #else bwb_error( err_mismatch ); #endif break; default: #if INTENSIVE_DEBUG sprintf( bwb_ebuf, "in op_not(): argument is <%d>, precision <%c>", (unsigned int) exp_getnval( &( CURTASK exps[ level + 1 ] )), precision ); bwb_debug( bwb_ebuf ); #endif CURTASK exps[ level ].nval = (bnumber) ~( (int) exp_getnval( &( CURTASK exps[ level + 1 ] )) ); #if INTENSIVE_DEBUG sprintf( bwb_ebuf, "in op_not(): result is <%d>, precision <%c>", (unsigned int) exp_getnval( &( CURTASK exps[ level ] )), precision ); bwb_debug( bwb_ebuf ); #endif break; } /* set variable type to integer */ CURTASK exps[ level ].type = NUMBER; CURTASK exps[ level ].operation = NUMBER; /* decrement the stack once */ op_pulldown( 1 ); #if INTENSIVE_DEBUG sprintf( bwb_ebuf, "in op_not(): CURTASK expsc <%d>, level <%d> result <%d>", CURTASK expsc, level, CURTASK exps[ CURTASK expsc ].nval ); bwb_debug( bwb_ebuf ); #endif return TRUE; } /*************************************************************** FUNCTION: op_xor() DESCRIPTION: This function compares two integers and performs a logical XOR on them. ***************************************************************/ #if ANSI_C static int op_xor( int level, int precision ) #else static int op_xor( level, precision ) int level; int precision; #endif { switch( precision ) { case STRING: /* both sides of the operation should be numbers for logical comparison; if not, report an error */ #if PROG_ERRORS sprintf( bwb_ebuf, "Strings cannot be compared logically." ); bwb_error( bwb_ebuf ); #else bwb_error( err_mismatch ); #endif break; case NUMBER: CURTASK exps[ level - 1 ].nval = (bnumber) ((int) exp_getnval( &( CURTASK exps[ level - 1 ] )) ^ (int) exp_getnval( &( CURTASK exps[ level + 1 ] ))); break; } /* set variable type to integer */ CURTASK exps[ level - 1 ].type = NUMBER; CURTASK exps[ level - 1 ].operation = NUMBER; /* decrement the stack twice */ op_pulldown( 2 ); return TRUE; } /*************************************************************** FUNCTION: op_negation() DESCRIPTION: This function performs a negation on the element to the right. Added by JBV 10/95 ***************************************************************/ #if ANSI_C static int op_negation( int level, int precision ) #else static int op_negation( level, precision ) int level; int precision; #endif { switch( precision ) { case STRING: /* both sides of the operation should be numbers for logical comparison; if not, report an error */ #if PROG_ERRORS sprintf( bwb_ebuf, "Strings cannot be compared logically." ); bwb_error( bwb_ebuf ); #else bwb_error( err_mismatch ); #endif break; default: #if INTENSIVE_DEBUG sprintf( bwb_ebuf, "in op_negation(): argument is <%f>, precision <%c>", exp_getnval( &( CURTASK exps[ level + 1 ] )), precision ); bwb_debug( bwb_ebuf ); #endif CURTASK exps[ level ].nval = (bnumber) -( exp_getnval( &( CURTASK exps[ level + 1 ] )) ); #if INTENSIVE_DEBUG sprintf( bwb_ebuf, "in op_negation(): result is <%f>, precision <%c>", exp_getnval( &( CURTASK exps[ level ] )), precision ); bwb_debug( bwb_ebuf ); #endif break; } /* set variable type to requested precision (JBV) */ CURTASK exps[ level ].type = (char) precision; CURTASK exps[ level ].operation = NUMBER; /* decrement the stack once */ op_pulldown( 1 ); #if INTENSIVE_DEBUG sprintf( bwb_ebuf, "in op_negation(): CURTASK expsc <%d>, level <%d> result <%f>", CURTASK expsc, level, CURTASK exps[ CURTASK expsc ].nval ); bwb_debug( bwb_ebuf ); #endif return TRUE; } /*************************************************************** FUNCTION: op_islevelstr() DESCRIPTION: This function determines whether the operation at a specified level involves a string constant or variable. ***************************************************************/ #if ANSI_C static int op_islevelstr( int level ) #else static int op_islevelstr( level ) int level; #endif { /* first see if the level holds a string constant */ if ( CURTASK exps[ level ].operation == CONST_STRING ) { #if INTENSIVE_DEBUG sprintf( bwb_ebuf, "in op_islevelstr(): string detected at level <%d>.", level ); bwb_debug( bwb_ebuf ); #endif return TRUE; } /* see if the level holds a string variable */ if ( CURTASK exps[ level ].operation == VARIABLE ) { if ( CURTASK exps[ level ].xvar->type == STRING ) { #if INTENSIVE_DEBUG sprintf( bwb_ebuf, "in op_islevelstr(): string detected at level <%d>.", level ); bwb_debug( bwb_ebuf ); #endif return TRUE; } } /* test has failed, return FALSE */ #if INTENSIVE_DEBUG sprintf( bwb_ebuf, "in op_islevelstr(): string not detected at level <%d>.", level ); bwb_debug( bwb_ebuf ); #endif return FALSE; } /*************************************************************** FUNCTION: op_getprecision() DESCRIPTION: This function finds the precision for an operation by comparing the precision at this level and that two levels below. ***************************************************************/ #if ANSI_C static int op_getprecision( int level ) #else static int op_getprecision( level ) int level; #endif { /* first test for string value */ if ( ( CURTASK exps[ level + 1 ].type == STRING ) || ( CURTASK exps[ level - 1 ].type == STRING ) ) { return STRING; } /* Both are numbers, so we should be able to find a suitable precision level by starting with the top and moving down; check first for double precision */ else { return NUMBER; } } /*************************************************************** FUNCTION: op_pulldown() DESCRIPTION: This function pulls the expression stack down a specified number of levels, decrementing the expression stack counter (bycalling dec_esc()) and decrementing the current "level" of operation processing. ***************************************************************/ #if ANSI_C static int op_pulldown( int how_far ) #else static int op_pulldown( how_far ) int how_far; #endif { int level; register int c; #if INTENSIVE_DEBUG sprintf( bwb_ebuf, "in op_pulldown(): pull down e stack <%d> place(s)", how_far ); bwb_debug( bwb_ebuf ); #endif /* first pull down the actual variables themselves */ level = op_level + ( 2 - how_far ); while ( CURTASK expsc >= ( level + how_far ) ) { /*------------------------------------------------------*/ /* But before memcpy, deallocate sbuffer for level, and */ /* afterwards, set sbuffer for level + how_far to NULL! */ /* Else konfusion reigns the next time around... (JBV) */ /*------------------------------------------------------*/ if( CURTASK exps[ level ].sval.sbuffer != NULL ) /* JBV */ FREE( CURTASK exps[ level ].sval.sbuffer, "op_pulldown" ); memcpy( &CURTASK exps[ level ], &CURTASK exps[ level + how_far ], (size_t) ( sizeof( struct exp_ese )) ); CURTASK exps[ level + how_far ].sval.sbuffer = NULL; /* JBV */ ++level; } /* decrement the expression stack counter */ for ( c = 0; c < how_far; ++c ) { if ( dec_esc() == TRUE ) { --op_level; } else { return FALSE; } } return TRUE; }