/*
* Expression.java - Parse and evaluate expressions for BASIC.
*
* Copyright (c) 1996 Chuck McManis, All Rights Reserved.
*
* Permission to use, copy, modify, and distribute this software
* and its documentation for NON-COMMERCIAL purposes and without
* fee is hereby granted provided that this copyright notice
* appears in all copies.
*
* CHUCK MCMANIS MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE
* SUITABILITY OF THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING
* BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT. CHUCK MCMANIS
* SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT
* OF USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES.
*/
package basic;
import java.io.PrintStream;
import java.util.Vector;
import util.RedBlackTree;
/**
* This is the base class for BASIC expressions.
*
* Expressions are parsed by the class ParseExpression and creates
* a parse tree using objects of type expression. The subclasses
* ConstantExpression, VariableExpression, and FunctionExpression
* hold specific types of indivisable elements. The class BooleanExpression is
* used to point to boolean expressions. This distinction on booleans allows us to do
* some syntax checking and statements like the IF statement can verify their expression
* is a boolean one.
*
* See the class ParseExpression for the grammar and precidence rules.
*/
class Expression {
Expression arg1, arg2;
int oper;
/**
* These are the valid operator types.
*/
final static int OP_ADD = 1; // Addition '+'
final static int OP_SUB = 2; // Subtraction '-'
final static int OP_MUL = 3; // Multiplication '*'
final static int OP_DIV = 4; // Division '/'
final static int OP_EXP = 5; // Exponentiation '**'
final static int OP_AND = 6; // Bitwise AND '&'
final static int OP_IOR = 7; // Bitwise inclusive OR '|'
final static int OP_XOR = 8; // Bitwise exclusive OR '^'
final static int OP_NOT = 9; // Unary negation '!'
final static int OP_EQ = 10; // Equality '='
final static int OP_NE = 11; // Inequality '<>'
final static int OP_LT = 12; // Less than '<'
final static int OP_LE = 13; // Less than or equal '<='
final static int OP_GT = 14; // Greater than '>'
final static int OP_GE = 15; // Greater than or equal '>='
final static int OP_BAND = 16; // Boolean AND '.AND.'
final static int OP_BIOR = 17; // Boolean inclusive or '.OR.'
final static int OP_BXOR = 18; // Boolean exclusive or '.XOR.'
final static int OP_BNOT = 19; // Boolean negation '.NOT.'
final static int OP_NEG = 20; // Unary minus
final static String opVals[] = {
"", "+", "-", "*", "/", "**", "&", "|", "^", "!", "=", "<>",
"<", "<=", ">", ">=", ".AND.", ".OR.", ".XOR.", ".NOT.", "-",
};
protected Expression() {
}
final static String typeError =
"Expression: cannot combine boolean term with arithmetic term.";
/**
* Create a new expression.
*/
protected Expression(int op, Expression a, Expression b) throws BASICSyntaxError {
arg1 = a;
arg2 = b;
oper = op;
/*
* If the operator is a boolean, both arguments must be boolean.
*/
if (op > OP_GE) {
if ( (! (arg1 instanceof BooleanExpression)) ||
(! (arg2 instanceof BooleanExpression)) )
throw new BASICSyntaxError(typeError);
} else {
if ((arg1 instanceof BooleanExpression) || (arg2 instanceof BooleanExpression))
throw new BASICSyntaxError(typeError);
}
}
/**
* Create a unary expression.
*/
protected Expression(int op, Expression a) throws BASICSyntaxError {
arg2 = a;
oper = op;
if ((oper == OP_BNOT) && (! (arg2 instanceof BooleanExpression)))
throw new BASICSyntaxError(typeError);
}
void print(PrintStream p) {
p.print("(");
// unary expressions don't have an arg1.
if (arg1 != null)
arg1.print(p);
p.print(opVals[oper]);
arg2.print(p);
p.print(")");
return;
}
public String toString() {
StringBuffer sb = new StringBuffer();
sb.append("(");
if (arg1 != null)
sb.append(arg1.toString());
sb.append(opVals[oper]);
sb.append(arg2.toString());
sb.append(")");
return sb.toString();
}
String unparse() {
StringBuffer sb = new StringBuffer();
sb.append("(");
if (arg1 != null) {
sb.append(arg1.unparse());
sb.append(" "+opVals[oper]+" ");
}
sb.append(arg2.unparse());
sb.append(")");
return sb.toString();
}
/**
* Generate a set of trace records for this expression. All of the
* variables in the expression are added to the tracer vector.
*/
void trace(RedBlackTree tracer) {
if (arg1 != null)
arg1.trace(tracer);
if (arg2 != null)
arg2.trace(tracer);
}
/**
* This method evaluates the expression in the context of the
* passed in program. It throws runtime errors for things like
* no such variable and divide by zero.
*
* Note that for boolean operations the value 1.0 == true and
* the value 0.0 is equivalent to false.
*/
double value(Program pgm) throws BASICRuntimeError {
switch (oper) {
case OP_ADD :
return arg1.value(pgm) + arg2.value(pgm);
case OP_SUB :
return arg1.value(pgm) - arg2.value(pgm);
case OP_MUL :
return arg1.value(pgm) * arg2.value(pgm);
case OP_DIV :
if (arg2.value(pgm) == 0) {
throw new BASICRuntimeError("divide by zero!");
}
return arg1.value(pgm) / arg2.value(pgm);
case OP_XOR :
return (double)(((long) arg1.value(pgm)) ^ ((long) arg2.value(pgm)));
case OP_IOR :
return (double)(((long) arg1.value(pgm)) | ((long) arg2.value(pgm)));
case OP_AND :
return (double)(((long) arg1.value(pgm)) & ((long) arg2.value(pgm)));
case OP_EXP :
return (Math.pow(arg1.value(pgm), arg2.value(pgm)));
case OP_EQ :
return (arg1.value(pgm) == arg2.value(pgm)) ? 1.0 : 0.0;
case OP_NE :
return (arg1.value(pgm) != arg2.value(pgm)) ? 1.0 : 0.0;
case OP_LT :
return (arg1.value(pgm) < arg2.value(pgm)) ? 1.0 : 0.0;
case OP_LE :
return (arg1.value(pgm) <= arg2.value(pgm)) ? 1.0 : 0.0;
case OP_GT :
return (arg1.value(pgm) > arg2.value(pgm)) ? 1.0 : 0.0;
case OP_GE :
return (arg1.value(pgm) >= arg2.value(pgm)) ? 1.0 : 0.0;
case OP_BAND :
return ((arg1.value(pgm) == 1.0) && (arg2.value(pgm) == 1.0)) ? 1.0 : 0.0;
case OP_BIOR :
return ((arg1.value(pgm) == 1.0) || (arg2.value(pgm) == 1.0)) ? 1.0 : 0.0;
case OP_BXOR :
return ((arg1.value(pgm) == 1.0) ^ (arg2.value(pgm) == 1.0)) ? 1.0 : 0.0;
case OP_BNOT :
return (arg2.value(pgm) == 1.0) ? 0.0 : 1.0;
case OP_NOT :
return (double)(~((long)(arg2.value(pgm))));
case OP_NEG :
return 0 - arg2.value(pgm);
default:
throw new BASICRuntimeError("Illegal operator in expression!");
}
}
String stringValue(Program pgm, int c) throws BASICRuntimeError {
System.out.println("This is not a string expression?");
throw new BASICRuntimeError("No String representation for this.");
}
String stringValue(Program pgm) throws BASICRuntimeError {
throw new BASICRuntimeError("No String representation for this.");
}
boolean isString() { return false; }
}