/* * Expression.java * * Copyright (c) 1997 Cornell University. * Copyright (c) 1997 Sun Microsystems, Inc. * * See the file "license.terms" for information on usage and * redistribution of this file, and for a DISCLAIMER OF ALL * WARRANTIES. * * Included in SQLite3 port to C# for use in testharness only; 2008 Noah B Hart * * RCS @(#) $Id: Expression.java,v 1.10 2003/02/04 00:35:41 mdejong Exp $ * */ using System; using System.Collections; namespace tcl.lang { /// This class handles Tcl expressions. class Expression { // The token types are defined below. In addition, there is a // table associating a precedence with each operator. The order // of types is important. Consult the code before changing it. internal const int VALUE = 0; internal const int OPEN_PAREN = 1; internal const int CLOSE_PAREN = 2; internal const int COMMA = 3; internal const int END = 4; internal const int UNKNOWN = 5; // Binary operators: internal const int MULT = 8; internal const int DIVIDE = 9; internal const int MOD = 10; internal const int PLUS = 11; internal const int MINUS = 12; internal const int LEFT_SHIFT = 13; internal const int RIGHT_SHIFT = 14; internal const int LESS = 15; internal const int GREATER = 16; internal const int LEQ = 17; internal const int GEQ = 18; internal const int EQUAL = 19; internal const int NEQ = 20; internal const int BIT_AND = 21; internal const int BIT_XOR = 22; internal const int BIT_OR = 23; internal const int AND = 24; internal const int OR = 25; internal const int QUESTY = 26; internal const int COLON = 27; // Unary operators: internal const int UNARY_MINUS = 28; internal const int UNARY_PLUS = 29; internal const int NOT = 30; internal const int BIT_NOT = 31; internal const int EQ = 32; internal const int NE = 33; // Precedence table. The values for non-operator token types are ignored. internal static int[] precTable = new int[] { 0, 0, 0, 0, 0, 0, 0, 0, 12, 12, 12, 11, 11, 10, 10, 9, 9, 9, 9, 8, 8, 7, 6, 5, 4, 3, 2, 1, 13, 13, 13, 13 }; // Mapping from operator numbers to strings; used for error messages. internal static string[] operatorStrings = new string[] { "VALUE", "(", ")", ",", "END", "UNKNOWN", "6", "7", "*", "/", "%", "+", "-", "<<", ">>", "<", ">", "<=", ">=", "==", "!=", "&", "^", "|", "&&", "||", "?", ":", "-", "+", "!", "~", "eq", "ne" }; internal Hashtable mathFuncTable; /// The entire expression, as originally passed to eval et al. private string m_expr; /// Length of the expression. private int m_len; /// Type of the last token to be parsed from the expression. /// Corresponds to the characters just before expr. /// internal int m_token; /// Position to the next character to be scanned from the expression /// string. /// private int m_ind; /// Evaluate a Tcl expression. /// /// /// the context in which to evaluate the expression. /// /// expression to evaluate. /// /// the value of the expression. /// /// TclException for malformed expressions. /// internal TclObject eval( Interp interp, string inString ) { ExprValue value = ExprTopLevel( interp, inString ); switch ( value.type ) { case ExprValue.INT: return TclInteger.newInstance( (int)value.intValue ); case ExprValue.DOUBLE: return TclDouble.newInstance( value.doubleValue ); case ExprValue.STRING: return TclString.newInstance( value.stringValue ); default: throw new TclRuntimeError( "internal error: expression, unknown" ); } } /// Evaluate an Tcl expression. /// the context in which to evaluate the expression. /// /// expression to evaluate. /// /// TclException for malformed expressions. /// /// the value of the expression in boolean. /// internal bool evalBoolean( Interp interp, string inString ) { ExprValue value = ExprTopLevel( interp, inString ); switch ( value.type ) { case ExprValue.INT: return ( value.intValue != 0 ); case ExprValue.DOUBLE: return ( value.doubleValue != 0.0 ); case ExprValue.STRING: return Util.getBoolean( interp, value.stringValue ); default: throw new TclRuntimeError( "internal error: expression, unknown" ); } } /// Constructor. internal Expression() { mathFuncTable = new Hashtable(); // rand -- needs testing // srand -- needs testing // hypot -- needs testing // fmod -- needs testing // try [expr fmod(4.67, 2.2)] // the answer should be .27, but I got .2699999999999996 SupportClass.PutElement( mathFuncTable, "atan2", new Atan2Function() ); SupportClass.PutElement( mathFuncTable, "pow", new PowFunction() ); SupportClass.PutElement( mathFuncTable, "acos", new AcosFunction() ); SupportClass.PutElement( mathFuncTable, "asin", new AsinFunction() ); SupportClass.PutElement( mathFuncTable, "atan", new AtanFunction() ); SupportClass.PutElement( mathFuncTable, "ceil", new CeilFunction() ); SupportClass.PutElement( mathFuncTable, "cos", new CosFunction() ); SupportClass.PutElement( mathFuncTable, "cosh", new CoshFunction() ); SupportClass.PutElement( mathFuncTable, "exp", new ExpFunction() ); SupportClass.PutElement( mathFuncTable, "floor", new FloorFunction() ); SupportClass.PutElement( mathFuncTable, "fmod", new FmodFunction() ); SupportClass.PutElement( mathFuncTable, "hypot", new HypotFunction() ); SupportClass.PutElement( mathFuncTable, "log", new LogFunction() ); SupportClass.PutElement( mathFuncTable, "log10", new Log10Function() ); SupportClass.PutElement( mathFuncTable, "rand", new RandFunction() ); SupportClass.PutElement( mathFuncTable, "sin", new SinFunction() ); SupportClass.PutElement( mathFuncTable, "sinh", new SinhFunction() ); SupportClass.PutElement( mathFuncTable, "sqrt", new SqrtFunction() ); SupportClass.PutElement( mathFuncTable, "srand", new SrandFunction() ); SupportClass.PutElement( mathFuncTable, "tan", new TanFunction() ); SupportClass.PutElement( mathFuncTable, "tanh", new TanhFunction() ); SupportClass.PutElement( mathFuncTable, "abs", new AbsFunction() ); SupportClass.PutElement( mathFuncTable, "double", new DoubleFunction() ); SupportClass.PutElement( mathFuncTable, "int", new IntFunction() ); SupportClass.PutElement( mathFuncTable, "round", new RoundFunction() ); SupportClass.PutElement( mathFuncTable, "wide", new WideFunction() ); m_expr = null; m_ind = 0; m_len = 0; m_token = UNKNOWN; } /// Provides top-level functionality shared by procedures like ExprInt, /// ExprDouble, etc. /// /// the context in which to evaluate the expression. /// /// the expression. /// /// TclException for malformed expressions. /// /// the value of the expression. /// private ExprValue ExprTopLevel( Interp interp, string inString ) { // Saved the state variables so that recursive calls to expr // can work: // expr {[expr 1+2] + 3} string m_expr_saved = m_expr; int m_len_saved = m_len; int m_token_saved = m_token; int m_ind_saved = m_ind; try { m_expr = inString; m_ind = 0; m_len = inString.Length; m_token = UNKNOWN; ExprValue val = ExprGetValue( interp, -1 ); if ( m_token != END ) { SyntaxError( interp ); } return val; } finally { m_expr = m_expr_saved; m_len = m_len_saved; m_token = m_token_saved; m_ind = m_ind_saved; } } internal static void IllegalType( Interp interp, int badType, int Operator ) { throw new TclException( interp, "can't use " + ( ( badType == ExprValue.DOUBLE ) ? "floating-point value" : "non-numeric string" ) + " as operand of \"" + operatorStrings[Operator] + "\"" ); } internal void SyntaxError( Interp interp ) { throw new TclException( interp, "syntax error in expression \"" + m_expr + "\"" ); } internal static void DivideByZero( Interp interp ) { interp.setErrorCode( TclString.newInstance( "ARITH DIVZERO {divide by zero}" ) ); throw new TclException( interp, "divide by zero" ); } internal static void IntegerTooLarge( Interp interp ) { interp.setErrorCode( TclString.newInstance( "ARITH IOVERFLOW {integer value too large to represent}" ) ); throw new TclException( interp, "integer value too large to represent" ); } internal static void WideTooLarge( Interp interp ) { interp.setErrorCode( TclString.newInstance( "ARITH IOVERFLOW {wide value too large to represent}" ) ); throw new TclException( interp, "wide value too large to represent" ); } internal static void DoubleTooLarge( Interp interp ) { interp.setErrorCode( TclString.newInstance( "ARITH OVERFLOW {floating-point value too large to represent}" ) ); throw new TclException( interp, "floating-point value too large to represent" ); } internal static void DoubleTooSmall( Interp interp ) { interp.setErrorCode( TclString.newInstance( "ARITH UNDERFLOW {floating-point value too small to represent}" ) ); throw new TclException( interp, "floating-point value too small to represent" ); } internal static void DomainError( Interp interp ) { interp.setErrorCode( TclString.newInstance( "ARITH DOMAIN {domain error: argument not in valid range}" ) ); throw new TclException( interp, "domain error: argument not in valid range" ); } /// Given a string (such as one coming from command or variable /// substitution), make a Value based on the string. The value /// be a floating-point or integer, if possible, or else it /// just be a copy of the string. /// /// /// the context in which to evaluate the expression. /// /// the string to parse. /// /// TclException for malformed expressions. /// /// the value of the expression. /// private ExprValue ExprParseString( Interp interp, string s ) { int len = s.Length; /* System.out.println("now to ExprParseString ->" + s + "<- of length " + len);*/ // Take shortcut when string is of length 0, as there is // only a string rep for an empty string (no int or double rep) // this will happend a lot so this shortcut will speed things up! if ( len == 0 ) { return new ExprValue( s ); } // The strings "0" and "1" are going to occure a lot // it might be wise to include shortcuts for these cases int i; if ( looksLikeInt( s, len, 0 ) ) { //System.out.println("string looks like an int"); // Note: use strtoul instead of strtol for integer conversions // to allow full-size unsigned numbers, but don't depend on // strtoul to handle sign characters; it won't in some // implementations. for ( i = 0; System.Char.IsWhiteSpace( s[i] ); i++ ) { // Empty loop body. } StrtoulResult res; if ( s[i] == '-' ) { i++; res = Util.strtoul( s, i, 0 ); res.value = -res.value; } else if ( s[i] == '+' ) { i++; res = Util.strtoul( s, i, 0 ); } else { res = Util.strtoul( s, i, 0 ); } if ( res.errno == 0 ) { // We treat this string as a number if all the charcters // following the parsed number are a whitespace char // E.g.: " 1", "1", "1 ", and " 1 " are all good numbers bool trailing_blanks = true; for ( i = res.index; i < len; i++ ) { if ( !System.Char.IsWhiteSpace( s[i] ) ) { trailing_blanks = false; } } if ( trailing_blanks ) { //System.out.println("string is an Integer of value " + res.value); m_token = VALUE; return new ExprValue( res.value ); } } else if ( res.errno == TCL.INTEGER_RANGE ) { IntegerTooLarge( interp ); } /* if (res.index == len) { // We treat this string as a number only if the number // ends at the end of the string. E.g.: " 1", "1" are // good numbers but "1 " is not. if (res.errno == TCL.INTEGER_RANGE) { IntegerTooLarge(interp); } else { m_token = VALUE; return new ExprValue(res.value); } }*/ } else { //System.out.println("string does not look like an int, checking for Double"); StrtodResult res = Util.strtod( s, 0 ); if ( res.errno == 0 ) { // Trailing whitespaces are treated just like the Integer case bool trailing_blanks = true; for ( i = res.index; i < len; i++ ) { if ( !System.Char.IsWhiteSpace( s[i] ) ) { trailing_blanks = false; } } if ( trailing_blanks ) { //System.out.println("string is a Double of value " + res.value); m_token = VALUE; return new ExprValue( res.value ); } } else if ( res.errno == TCL.DOUBLE_RANGE ) { if ( res.value != 0 ) { DoubleTooLarge( interp ); } else { DoubleTooSmall( interp ); } } // if res.errno is any other value (like TCL.INVALID_DOUBLE) // just fall through and use the string rep /* if (res.index == len) { if (res.errno == 0) { //System.out.println("string is a Double of value " + res.value); m_token = VALUE; return new ExprValue(res.value); } else if (res.errno == TCL.DOUBLE_RANGE) { DoubleTooLarge(interp); } }*/ } //System.out.println("string is not a valid number, returning as string"); // Not a valid number. Save a string value (but don't do anything // if it's already the value). return new ExprValue( s ); } /// Parse a "value" from the remainder of the expression. /// /// /// the context in which to evaluate the expression. /// /// treat any un-parenthesized operator with precedence /// <= this as the end of the expression. /// /// TclException for malformed expressions. /// /// the value of the expression. /// private ExprValue ExprGetValue( Interp interp, int prec ) { int Operator; bool gotOp = false; // True means already lexed the // operator (while picking up value // for unary operator). Don't lex // again. ExprValue value, value2; // There are two phases to this procedure. First, pick off an // initial value. Then, parse (binary operator, value) pairs // until done. value = ExprLex( interp ); if ( m_token == OPEN_PAREN ) { // Parenthesized sub-expression. value = ExprGetValue( interp, -1 ); if ( m_token != CLOSE_PAREN ) { SyntaxError( interp ); } } else { if ( m_token == MINUS ) { m_token = UNARY_MINUS; } if ( m_token == PLUS ) { m_token = UNARY_PLUS; } if ( m_token >= UNARY_MINUS ) { // Process unary operators. Operator = m_token; value = ExprGetValue( interp, precTable[m_token] ); if ( interp.noEval == 0 ) { switch ( Operator ) { case UNARY_MINUS: if ( value.type == ExprValue.INT ) { value.intValue = -value.intValue; } else if ( value.type == ExprValue.DOUBLE ) { value.doubleValue = -value.doubleValue; } else { IllegalType( interp, value.type, Operator ); } break; case UNARY_PLUS: if ( ( value.type != ExprValue.INT ) && ( value.type != ExprValue.DOUBLE ) ) { IllegalType( interp, value.type, Operator ); } break; case NOT: if ( value.type == ExprValue.INT ) { if ( value.intValue != 0 ) { value.intValue = 0; } else { value.intValue = 1; } } else if ( value.type == ExprValue.DOUBLE ) { if ( value.doubleValue == 0.0 ) { value.intValue = 1; } else { value.intValue = 0; } value.type = ExprValue.INT; } else { IllegalType( interp, value.type, Operator ); } break; case BIT_NOT: if ( value.type == ExprValue.INT ) { value.intValue = ~value.intValue; } else { IllegalType( interp, value.type, Operator ); } break; } } gotOp = true; } else if ( m_token == CLOSE_PAREN ) { // Caller needs to deal with close paren token. return null; } else if ( m_token != VALUE ) { SyntaxError( interp ); } } if ( value == null ) { SyntaxError( interp ); } // Got the first operand. Now fetch (operator, operand) pairs. if ( !gotOp ) { value2 = ExprLex( interp ); } while ( true ) { Operator = m_token; if ( ( Operator < MULT ) || ( Operator >= UNARY_MINUS ) ) { if ( ( Operator == END ) || ( Operator == CLOSE_PAREN ) || ( Operator == COMMA ) ) { return value; // Goto Done } else { SyntaxError( interp ); } } if ( precTable[Operator] <= prec ) { return value; // (goto done) } // If we're doing an AND or OR and the first operand already // determines the result, don't execute anything in the // second operand: just parse. Same style for ?: pairs. if ( ( Operator == AND ) || ( Operator == OR ) || ( Operator == QUESTY ) ) { if ( value.type == ExprValue.DOUBLE ) { value.intValue = ( value.doubleValue != 0 ) ? 1 : 0; value.type = ExprValue.INT; } else if ( value.type == ExprValue.STRING ) { try { bool b = Util.getBoolean( null, value.stringValue ); value = new ExprValue( b ? 1 : 0 ); } catch ( TclException e ) { if ( interp.noEval == 0 ) { IllegalType( interp, ExprValue.STRING, Operator ); } // Must set value.intValue to avoid referencing // uninitialized memory in the "if" below; the actual // value doesn't matter, since it will be ignored. value.intValue = 0; } } if ( ( ( Operator == AND ) && ( value.intValue == 0 ) ) || ( ( Operator == OR ) && ( value.intValue != 0 ) ) ) { interp.noEval++; try { value2 = ExprGetValue( interp, precTable[Operator] ); } finally { interp.noEval--; } if ( Operator == OR ) { value.intValue = 1; } continue; } else if ( Operator == QUESTY ) { // Special note: ?: operators must associate right to // left. To make this happen, use a precedence one lower // than QUESTY when calling ExprGetValue recursively. if ( value.intValue != 0 ) { value = ExprGetValue( interp, precTable[QUESTY] - 1 ); if ( m_token != COLON ) { SyntaxError( interp ); } interp.noEval++; try { value2 = ExprGetValue( interp, precTable[QUESTY] - 1 ); } finally { interp.noEval--; } } else { interp.noEval++; try { value2 = ExprGetValue( interp, precTable[QUESTY] - 1 ); } finally { interp.noEval--; } if ( m_token != COLON ) { SyntaxError( interp ); } value = ExprGetValue( interp, precTable[QUESTY] - 1 ); } continue; } else { value2 = ExprGetValue( interp, precTable[Operator] ); } } else { value2 = ExprGetValue( interp, precTable[Operator] ); } if ( ( m_token < MULT ) && ( m_token != VALUE ) && ( m_token != END ) && ( m_token != COMMA ) && ( m_token != CLOSE_PAREN ) ) { SyntaxError( interp ); } if ( interp.noEval != 0 ) { continue; } // At this point we've got two values and an operator. Check // to make sure that the particular data types are appropriate // for the particular operator, and perform type conversion // if necessary. switch ( Operator ) { // For the operators below, no strings are allowed and // ints get converted to floats if necessary. case MULT: case DIVIDE: case PLUS: case MINUS: if ( ( value.type == ExprValue.STRING ) || ( value2.type == ExprValue.STRING ) ) { IllegalType( interp, ExprValue.STRING, Operator ); } if ( value.type == ExprValue.DOUBLE ) { if ( value2.type == ExprValue.INT ) { value2.doubleValue = value2.intValue; value2.type = ExprValue.DOUBLE; } } else if ( value2.type == ExprValue.DOUBLE ) { if ( value.type == ExprValue.INT ) { value.doubleValue = value.intValue; value.type = ExprValue.DOUBLE; } } break; // For the operators below, only integers are allowed. case MOD: case LEFT_SHIFT: case RIGHT_SHIFT: case BIT_AND: case BIT_XOR: case BIT_OR: if ( value.type != ExprValue.INT ) { IllegalType( interp, value.type, Operator ); } else if ( value2.type != ExprValue.INT ) { IllegalType( interp, value2.type, Operator ); } break; // For the operators below, any type is allowed but the // two operands must have the same type. Convert integers // to floats and either to strings, if necessary. case LESS: case GREATER: case LEQ: case GEQ: case EQUAL: case EQ: case NEQ: case NE: if ( value.type == ExprValue.STRING ) { if ( value2.type != ExprValue.STRING ) { ExprMakeString( interp, value2 ); } } else if ( value2.type == ExprValue.STRING ) { if ( value.type != ExprValue.STRING ) { ExprMakeString( interp, value ); } } else if ( value.type == ExprValue.DOUBLE ) { if ( value2.type == ExprValue.INT ) { value2.doubleValue = value2.intValue; value2.type = ExprValue.DOUBLE; } } else if ( value2.type == ExprValue.DOUBLE ) { if ( value.type == ExprValue.INT ) { value.doubleValue = value.intValue; value.type = ExprValue.DOUBLE; } } break; // For the operators below, no strings are allowed, but // no int->double conversions are performed. case AND: case OR: if ( value.type == ExprValue.STRING ) { IllegalType( interp, value.type, Operator ); } if ( value2.type == ExprValue.STRING ) { try { bool b = Util.getBoolean( null, value2.stringValue ); value2 = new ExprValue( b ? 1 : 0 ); } catch ( TclException e ) { IllegalType( interp, value2.type, Operator ); } } break; // For the operators below, type and conversions are // irrelevant: they're handled elsewhere. case QUESTY: case COLON: break; // Any other operator is an error. default: throw new TclException( interp, "unknown operator in expression" ); } // Carry out the function of the specified operator. switch ( Operator ) { case MULT: if ( value.type == ExprValue.INT ) { value.intValue = value.intValue * value2.intValue; } else { value.doubleValue *= value2.doubleValue; } break; case DIVIDE: case MOD: if ( value.type == ExprValue.INT ) { long divisor, quot, rem; bool negative; if ( value2.intValue == 0 ) { DivideByZero( interp ); } // The code below is tricky because C doesn't guarantee // much about the properties of the quotient or // remainder, but Tcl does: the remainder always has // the same sign as the divisor and a smaller absolute // value. divisor = value2.intValue; negative = false; if ( divisor < 0 ) { divisor = -divisor; value.intValue = -value.intValue; negative = true; } quot = value.intValue / divisor; rem = value.intValue % divisor; if ( rem < 0 ) { rem += divisor; quot -= 1; } if ( negative ) { rem = -rem; } value.intValue = ( Operator == DIVIDE ) ? quot : rem; } else { if ( value2.doubleValue == 0.0 ) { DivideByZero( interp ); } value.doubleValue /= value2.doubleValue; } break; case PLUS: if ( value.type == ExprValue.INT ) { value.intValue = value.intValue + value2.intValue; } else { value.doubleValue += value2.doubleValue; } break; case MINUS: if ( value.type == ExprValue.INT ) { value.intValue = value.intValue - value2.intValue; } else { value.doubleValue -= value2.doubleValue; } break; case LEFT_SHIFT: value.intValue <<= (int)value2.intValue; break; case RIGHT_SHIFT: if ( value.intValue < 0 ) { value.intValue = ~( ( ~value.intValue ) >> (int)value2.intValue ); } else { value.intValue >>= (int)value2.intValue; } break; case LESS: if ( value.type == ExprValue.INT ) { value.intValue = ( value.intValue < value2.intValue ) ? 1 : 0; } else if ( value.type == ExprValue.DOUBLE ) { value.intValue = ( value.doubleValue < value2.doubleValue ) ? 1 : 0; } else { value.intValue = ( value.stringValue.CompareTo( value2.stringValue ) < 0 ) ? 1 : 0; } value.type = ExprValue.INT; break; case GREATER: if ( value.type == ExprValue.INT ) { value.intValue = ( value.intValue > value2.intValue ) ? 1 : 0; } else if ( value.type == ExprValue.DOUBLE ) { value.intValue = ( value.doubleValue > value2.doubleValue ) ? 1 : 0; } else { value.intValue = ( value.stringValue.CompareTo( value2.stringValue ) > 0 ) ? 1 : 0; } value.type = ExprValue.INT; break; case LEQ: if ( value.type == ExprValue.INT ) { value.intValue = ( value.intValue <= value2.intValue ) ? 1 : 0; } else if ( value.type == ExprValue.DOUBLE ) { value.intValue = ( value.doubleValue <= value2.doubleValue ) ? 1 : 0; } else { value.intValue = ( value.stringValue.CompareTo( value2.stringValue ) <= 0 ) ? 1 : 0; } value.type = ExprValue.INT; break; case GEQ: if ( value.type == ExprValue.INT ) { value.intValue = ( value.intValue >= value2.intValue ) ? 1 : 0; } else if ( value.type == ExprValue.DOUBLE ) { value.intValue = ( value.doubleValue >= value2.doubleValue ) ? 1 : 0; } else { value.intValue = ( value.stringValue.CompareTo( value2.stringValue ) >= 0 ) ? 1 : 0; } value.type = ExprValue.INT; break; case EQUAL: case EQ: if ( value.type == ExprValue.INT ) { value.intValue = ( value.intValue == value2.intValue ) ? 1 : 0; } else if ( value.type == ExprValue.DOUBLE ) { value.intValue = ( value.doubleValue == value2.doubleValue ) ? 1 : 0; } else { value.intValue = ( value.stringValue.CompareTo( value2.stringValue ) == 0 ) ? 1 : 0; } value.type = ExprValue.INT; break; case NEQ: case NE: if ( value.type == ExprValue.INT ) { value.intValue = ( value.intValue != value2.intValue ) ? 1 : 0; } else if ( value.type == ExprValue.DOUBLE ) { value.intValue = ( value.doubleValue != value2.doubleValue ) ? 1 : 0; } else { value.intValue = ( value.stringValue.CompareTo( value2.stringValue ) != 0 ) ? 1 : 0; } value.type = ExprValue.INT; break; case BIT_AND: value.intValue &= value2.intValue; break; case BIT_XOR: value.intValue ^= value2.intValue; break; case BIT_OR: value.intValue |= value2.intValue; break; // For AND and OR, we know that the first value has already // been converted to an integer. Thus we need only consider // the possibility of int vs. double for the second value. case AND: if ( value2.type == ExprValue.DOUBLE ) { value2.intValue = ( value2.doubleValue != 0 ) ? 1 : 0; value2.type = ExprValue.INT; } value.intValue = ( ( value.intValue != 0 ) && ( value2.intValue != 0 ) ) ? 1 : 0; break; case OR: if ( value2.type == ExprValue.DOUBLE ) { value2.intValue = ( value2.doubleValue != 0 ) ? 1 : 0; value2.type = ExprValue.INT; } value.intValue = ( ( value.intValue != 0 ) || ( value2.intValue != 0 ) ) ? 1 : 0; break; case COLON: SyntaxError( interp ); break; } } } /// GetLexeme -> ExprLex /// /// Lexical analyzer for expression parser: parses a single value, /// operator, or other syntactic element from an expression string. /// /// Size effects: the "m_token" member variable is set to the value of /// the current token. /// /// /// the context in which to evaluate the expression. /// /// TclException for malformed expressions. /// /// the value of the expression. /// private ExprValue ExprLex( Interp interp ) { char c, c2; while ( m_ind < m_len && System.Char.IsWhiteSpace( m_expr[m_ind] ) ) { m_ind++; } if ( m_ind >= m_len ) { m_token = END; return null; } // First try to parse the token as an integer or // floating-point number. Don't want to check for a number if // the first character is "+" or "-". If we do, we might // treat a binary operator as unary by // mistake, which will eventually cause a syntax error. c = m_expr[m_ind]; if ( m_ind < m_len - 1 ) { c2 = m_expr[m_ind + 1]; } else { c2 = '\x0000'; } if ( ( c != '+' ) && ( c != '-' ) ) { bool startsWithDigit = System.Char.IsDigit( c ); if ( startsWithDigit && looksLikeInt( m_expr, m_len, m_ind ) ) { StrtoulResult res = Util.strtoul( m_expr, m_ind, 0 ); if ( res.errno == 0 ) { m_ind = res.index; m_token = VALUE; return new ExprValue( res.value ); } else { if ( res.errno == TCL.INTEGER_RANGE ) { IntegerTooLarge( interp ); } } } else if ( startsWithDigit || ( c == '.' ) || ( c == 'n' ) || ( c == 'N' ) ) { StrtodResult res = Util.strtod( m_expr, m_ind ); if ( res.errno == 0 ) { m_ind = res.index; m_token = VALUE; return new ExprValue( res.value ); } else { if ( res.errno == TCL.DOUBLE_RANGE ) { if ( res.value != 0 ) { DoubleTooLarge( interp ); } else { DoubleTooSmall( interp ); } } } } } ParseResult pres; ExprValue retval; m_ind += 1; // ind is advanced to point to the next token switch ( c ) { case '$': m_token = VALUE; pres = ParseAdaptor.parseVar( interp, m_expr, m_ind, m_len ); m_ind = pres.nextIndex; if ( interp.noEval != 0 ) { retval = new ExprValue( 0 ); } else { retval = ExprParseString( interp, pres.value.ToString() ); } pres.release(); return retval; case '[': m_token = VALUE; pres = ParseAdaptor.parseNestedCmd( interp, m_expr, m_ind, m_len ); m_ind = pres.nextIndex; if ( interp.noEval != 0 ) { retval = new ExprValue( 0 ); } else { retval = ExprParseString( interp, pres.value.ToString() ); } pres.release(); return retval; case '"': m_token = VALUE; //System.out.println("now to parse from ->" + m_expr + "<- at index " // + m_ind); pres = ParseAdaptor.parseQuotes( interp, m_expr, m_ind, m_len ); m_ind = pres.nextIndex; // System.out.println("after parse next index is " + m_ind); if ( interp.noEval != 0 ) { // System.out.println("returning noEval zero value"); retval = new ExprValue( 0 ); } else { // System.out.println("returning value string ->" + pres.value.toString() + "<-" ); retval = ExprParseString( interp, pres.value.ToString() ); } pres.release(); return retval; case '{': m_token = VALUE; pres = ParseAdaptor.parseBraces( interp, m_expr, m_ind, m_len ); m_ind = pres.nextIndex; if ( interp.noEval != 0 ) { retval = new ExprValue( 0 ); } else { retval = ExprParseString( interp, pres.value.ToString() ); } pres.release(); return retval; case '(': m_token = OPEN_PAREN; return null; case ')': m_token = CLOSE_PAREN; return null; case ',': m_token = COMMA; return null; case '*': m_token = MULT; return null; case '/': m_token = DIVIDE; return null; case '%': m_token = MOD; return null; case '+': m_token = PLUS; return null; case '-': m_token = MINUS; return null; case '?': m_token = QUESTY; return null; case ':': m_token = COLON; return null; case '<': switch ( c2 ) { case '<': m_ind += 1; m_token = LEFT_SHIFT; break; case '=': m_ind += 1; m_token = LEQ; break; default: m_token = LESS; break; } return null; case '>': switch ( c2 ) { case '>': m_ind += 1; m_token = RIGHT_SHIFT; break; case '=': m_ind += 1; m_token = GEQ; break; default: m_token = GREATER; break; } return null; case '=': if ( c2 == '=' ) { m_ind += 1; m_token = EQUAL; } else { m_token = UNKNOWN; } return null; case 'e': if ( c2 == 'q' ) { m_ind += 1; m_token = EQUAL; } else { m_token = UNKNOWN; } return null; case 'n': if ( c2 == 'e' ) { m_ind += 1; m_token = NEQ; } else { m_token = UNKNOWN; } return null; case '!': if ( c2 == '=' ) { m_ind += 1; m_token = NEQ; } else { m_token = NOT; } return null; case '&': if ( c2 == '&' ) { m_ind += 1; m_token = AND; } else { m_token = BIT_AND; } return null; case '^': m_token = BIT_XOR; return null; case '|': if ( c2 == '|' ) { m_ind += 1; m_token = OR; } else { m_token = BIT_OR; } return null; case '~': m_token = BIT_NOT; return null; default: if ( System.Char.IsLetter( c ) ) { // Oops, re-adjust m_ind so that it points to the beginning // of the function name. m_ind--; return mathFunction( interp ); } m_token = UNKNOWN; return null; } } /// Parses a math function from an expression string, carry out the /// function, and return the value computed. /// /// /// current interpreter. /// /// the value computed by the math function. /// /// TclException if any error happens. /// internal ExprValue mathFunction( Interp interp ) { int startIdx = m_ind; ExprValue value; string funcName; MathFunction mathFunc; TclObject[] argv = null; int numArgs; // Find the end of the math function's name and lookup the MathFunc // record for the function. Search until the char at m_ind is not // alphanumeric or '_' for ( ; m_ind < m_len; m_ind++ ) { if ( !( System.Char.IsLetterOrDigit( m_expr[m_ind] ) || m_expr[m_ind] == '_' ) ) { break; } } // Get the funcName BEFORE calling ExprLex, so the funcName // will not have trailing whitespace. funcName = m_expr.Substring( startIdx, ( m_ind ) - ( startIdx ) ); // Parse errors are thrown BEFORE unknown function names ExprLex( interp ); if ( m_token != OPEN_PAREN ) { SyntaxError( interp ); } // Now test for unknown funcName. Doing the above statements // out of order will cause some tests to fail. mathFunc = (MathFunction)mathFuncTable[funcName]; if ( mathFunc == null ) { throw new TclException( interp, "unknown math function \"" + funcName + "\"" ); } // Scan off the arguments for the function, if there are any. numArgs = mathFunc.argTypes.Length; if ( numArgs == 0 ) { ExprLex( interp ); if ( m_token != CLOSE_PAREN ) { SyntaxError( interp ); } } else { argv = new TclObject[numArgs]; for ( int i = 0; ; i++ ) { value = ExprGetValue( interp, -1 ); // Handle close paren with no value // % expr {srand()} if ( ( value == null ) && ( m_token == CLOSE_PAREN ) ) { if ( i == numArgs ) break; else throw new TclException( interp, "too few arguments for math function" ); } if ( value.type == ExprValue.STRING ) { throw new TclException( interp, "argument to math function didn't have numeric value" ); } // Copy the value to the argument record, converting it if // necessary. if ( value.type == ExprValue.INT ) { if ( mathFunc.argTypes[i] == MathFunction.DOUBLE ) { argv[i] = TclDouble.newInstance( (int)value.intValue ); } else { argv[i] = TclLong.newInstance( value.intValue ); } } else { if ( mathFunc.argTypes[i] == MathFunction.INT ) { argv[i] = TclInteger.newInstance( (int)value.doubleValue ); } else { argv[i] = TclDouble.newInstance( value.doubleValue ); } } // Check for a comma separator between arguments or a // close-paren to end the argument list. if ( i == ( numArgs - 1 ) ) { if ( m_token == CLOSE_PAREN ) { break; } if ( m_token == COMMA ) { throw new TclException( interp, "too many arguments for math function" ); } else { SyntaxError( interp ); } } if ( m_token != COMMA ) { if ( m_token == CLOSE_PAREN ) { throw new TclException( interp, "too few arguments for math function" ); } else { SyntaxError( interp ); } } } } m_token = VALUE; if ( interp.noEval != 0 ) { return new ExprValue( 0 ); } else { /* * Invoke the function and copy its result back into valuePtr. */ return mathFunc.apply( interp, argv ); } } /// This procedure decides whether the leading characters of a /// string look like an integer or something else (such as a /// floating-point number or string). /// /// a boolean value indicating if the string looks like an integer. /// internal static bool looksLikeInt( string s, int len, int i ) { while ( i < len && System.Char.IsWhiteSpace( s[i] ) ) { i++; } if ( i >= len ) { return false; } char c = s[i]; if ( ( c == '+' ) || ( c == '-' ) ) { i++; if ( i >= len ) { return false; } c = s[i]; } if ( !System.Char.IsDigit( c ) ) { return false; } while ( i < len && System.Char.IsDigit( s[i] ) ) { //System.out.println("'" + s.charAt(i) + "' is a digit"); i++; } if ( i >= len ) { return true; } //ported from C code c = s[i]; if ( ( c != '.' ) && ( c != 'e' ) && ( c != 'E' ) ) { return true; } //original /* if (i < len) { c = s.charAt(i); if ((c == '.') || (c == 'e') || (c == 'E')) { return false; } }*/ return false; } /// Converts a value from int or double representation to a string. /// interpreter to use for precision information. /// /// Value to be converted. /// internal static void ExprMakeString( Interp interp, ExprValue value ) { if ( value.type == ExprValue.INT ) { value.stringValue = System.Convert.ToString( value.intValue ); } else if ( value.type == ExprValue.DOUBLE ) { value.stringValue = value.doubleValue.ToString(); } value.type = ExprValue.STRING; } internal static void checkIntegerRange( Interp interp, double d ) { if ( d < 0 ) { if ( d < ( (double)TCL.INT_MIN ) ) { Expression.IntegerTooLarge( interp ); } } else { if ( d > ( (double)TCL.INT_MAX ) ) { Expression.IntegerTooLarge( interp ); } } } internal static void checkWideRange( Interp interp, double d ) { if ( d < 0 ) { if ( d < Int64.MinValue ) { Expression.WideTooLarge( interp ); } } else { if ( d > Int64.MaxValue ) { Expression.WideTooLarge( interp ); } } } internal static void checkDoubleRange( Interp interp, double d ) { if ( ( d == System.Double.NaN ) || ( d == System.Double.NegativeInfinity ) || ( d == System.Double.PositiveInfinity ) ) { Expression.DoubleTooLarge( interp ); } } } abstract class MathFunction { internal const int INT = 0; internal const int DOUBLE = 1; internal const int EITHER = 2; internal int[] argTypes; internal abstract ExprValue apply( Interp interp, TclObject[] argv ); } abstract class UnaryMathFunction : MathFunction { internal UnaryMathFunction() { argTypes = new int[1]; argTypes[0] = DOUBLE; } } abstract class BinaryMathFunction : MathFunction { internal BinaryMathFunction() { argTypes = new int[2]; argTypes[0] = DOUBLE; argTypes[1] = DOUBLE; } } abstract class NoArgMathFunction : MathFunction { internal NoArgMathFunction() { argTypes = new int[0]; } } class Atan2Function : BinaryMathFunction { internal override ExprValue apply( Interp interp, TclObject[] argv ) { return new ExprValue( System.Math.Atan2( TclDouble.get( interp, argv[0] ), TclDouble.get( interp, argv[1] ) ) ); } } class AbsFunction : MathFunction { internal AbsFunction() { argTypes = new int[1]; argTypes[0] = EITHER; } internal override ExprValue apply( Interp interp, TclObject[] argv ) { if ( argv[0].InternalRep is TclDouble ) { double d = TclDouble.get( interp, argv[0] ); if ( d > 0 ) { return new ExprValue( d ); } else { return new ExprValue( -d ); } } else { int i = TclInteger.get( interp, argv[0] ); if ( i > 0 ) { return new ExprValue( i ); } else { return new ExprValue( -i ); } } } } class DoubleFunction : MathFunction { internal DoubleFunction() { argTypes = new int[1]; argTypes[0] = EITHER; } internal override ExprValue apply( Interp interp, TclObject[] argv ) { return new ExprValue( TclDouble.get( interp, argv[0] ) ); } } class IntFunction : MathFunction { internal IntFunction() { argTypes = new int[1]; argTypes[0] = EITHER; } internal override ExprValue apply( Interp interp, TclObject[] argv ) { double d = TclDouble.get( interp, argv[0] ); Expression.checkIntegerRange( interp, d ); return new ExprValue( (int)d ); } } class WideFunction : MathFunction { internal WideFunction() { argTypes = new int[1]; argTypes[0] = EITHER; } internal override ExprValue apply( Interp interp, TclObject[] argv ) { double d = TclDouble.get( interp, argv[0] ); Expression.checkWideRange( interp, d ); return new ExprValue( (long)d ); } } class RoundFunction : MathFunction { internal RoundFunction() { argTypes = new int[1]; argTypes[0] = EITHER; } internal override ExprValue apply( Interp interp, TclObject[] argv ) { if ( argv[0].InternalRep is TclDouble ) { double d = TclDouble.get( interp, argv[0] ); if ( d < 0 ) { Expression.checkIntegerRange( interp, d - 0.5 ); return new ExprValue( (int)( d - 0.5 ) ); } else { Expression.checkIntegerRange( interp, d + 0.5 ); return new ExprValue( (int)( d + 0.5 ) ); } } else { return new ExprValue( TclInteger.get( interp, argv[0] ) ); } } } class PowFunction : BinaryMathFunction { internal override ExprValue apply( Interp interp, TclObject[] argv ) { double d = System.Math.Pow( TclDouble.get( interp, argv[0] ), TclDouble.get( interp, argv[1] ) ); Expression.checkDoubleRange( interp, d ); return new ExprValue( d ); } } /* * The following section is generated by this script. * set missing {fmod} set byhand {atan2 pow} foreach func {Acos Asin Atan Ceil Cos Exp Floor Log Sin Sqrt Tan} { puts " class $func\Function extends UnaryMathFunction { ExprValue apply(Interp interp, TclObject argv\[\]) throws TclException { return new ExprValue(Math.[string tolower $func](TclDouble.get(interp, argv\[0\]))); } } " } */ class AcosFunction : UnaryMathFunction { internal override ExprValue apply( Interp interp, TclObject[] argv ) { double d = TclDouble.get( interp, argv[0] ); if ( ( d < -1 ) || ( d > 1 ) ) { Expression.DomainError( interp ); } return new ExprValue( System.Math.Acos( d ) ); } } class AsinFunction : UnaryMathFunction { internal override ExprValue apply( Interp interp, TclObject[] argv ) { return new ExprValue( System.Math.Asin( TclDouble.get( interp, argv[0] ) ) ); } } class AtanFunction : UnaryMathFunction { internal override ExprValue apply( Interp interp, TclObject[] argv ) { return new ExprValue( System.Math.Atan( TclDouble.get( interp, argv[0] ) ) ); } } class CeilFunction : UnaryMathFunction { internal override ExprValue apply( Interp interp, TclObject[] argv ) { return new ExprValue( System.Math.Ceiling( TclDouble.get( interp, argv[0] ) ) ); } } class CosFunction : UnaryMathFunction { internal override ExprValue apply( Interp interp, TclObject[] argv ) { return new ExprValue( System.Math.Cos( TclDouble.get( interp, argv[0] ) ) ); } } class CoshFunction : UnaryMathFunction { internal override ExprValue apply( Interp interp, TclObject[] argv ) { double x = TclDouble.get( interp, argv[0] ); double d1 = System.Math.Pow( System.Math.E, x ); double d2 = System.Math.Pow( System.Math.E, -x ); Expression.checkDoubleRange( interp, d1 ); Expression.checkDoubleRange( interp, d2 ); return new ExprValue( ( d1 + d2 ) / 2 ); } } class ExpFunction : UnaryMathFunction { internal override ExprValue apply( Interp interp, TclObject[] argv ) { double d = System.Math.Exp( TclDouble.get( interp, argv[0] ) ); if ( ( d == System.Double.NaN ) || ( d == System.Double.NegativeInfinity ) || ( d == System.Double.PositiveInfinity ) ) { Expression.DoubleTooLarge( interp ); } return new ExprValue( d ); } } class FloorFunction : UnaryMathFunction { internal override ExprValue apply( Interp interp, TclObject[] argv ) { return new ExprValue( System.Math.Floor( TclDouble.get( interp, argv[0] ) ) ); } } class FmodFunction : BinaryMathFunction { internal override ExprValue apply( Interp interp, TclObject[] argv ) { return new ExprValue( System.Math.IEEERemainder( TclDouble.get( interp, argv[0] ), TclDouble.get( interp, argv[1] ) ) ); } } class HypotFunction : BinaryMathFunction { internal override ExprValue apply( Interp interp, TclObject[] argv ) { double x = TclDouble.get( interp, argv[0] ); double y = TclDouble.get( interp, argv[1] ); return new ExprValue( System.Math.Sqrt( ( ( x * x ) + ( y * y ) ) ) ); } } class LogFunction : UnaryMathFunction { internal override ExprValue apply( Interp interp, TclObject[] argv ) { return new ExprValue( System.Math.Log( TclDouble.get( interp, argv[0] ) ) ); } } class Log10Function : UnaryMathFunction { private static readonly double log10; internal override ExprValue apply( Interp interp, TclObject[] argv ) { return new ExprValue( System.Math.Log( TclDouble.get( interp, argv[0] ) ) / log10 ); } static Log10Function() { log10 = System.Math.Log( 10 ); } } class SinFunction : UnaryMathFunction { internal override ExprValue apply( Interp interp, TclObject[] argv ) { return new ExprValue( System.Math.Sin( TclDouble.get( interp, argv[0] ) ) ); } } class SinhFunction : UnaryMathFunction { internal override ExprValue apply( Interp interp, TclObject[] argv ) { double x = TclDouble.get( interp, argv[0] ); double d1 = System.Math.Pow( System.Math.E, x ); double d2 = System.Math.Pow( System.Math.E, -x ); Expression.checkDoubleRange( interp, d1 ); Expression.checkDoubleRange( interp, d2 ); return new ExprValue( ( d1 - d2 ) / 2 ); } } class SqrtFunction : UnaryMathFunction { internal override ExprValue apply( Interp interp, TclObject[] argv ) { return new ExprValue( System.Math.Sqrt( TclDouble.get( interp, argv[0] ) ) ); } } class TanFunction : UnaryMathFunction { internal override ExprValue apply( Interp interp, TclObject[] argv ) { return new ExprValue( System.Math.Tan( TclDouble.get( interp, argv[0] ) ) ); } } class TanhFunction : UnaryMathFunction { internal override ExprValue apply( Interp interp, TclObject[] argv ) { double x = TclDouble.get( interp, argv[0] ); if ( x == 0 ) { return new ExprValue( 0.0 ); } double d1 = System.Math.Pow( System.Math.E, x ); double d2 = System.Math.Pow( System.Math.E, -x ); Expression.checkDoubleRange( interp, d1 ); Expression.checkDoubleRange( interp, d2 ); return new ExprValue( ( d1 - d2 ) / ( d1 + d2 ) ); } } class RandFunction : NoArgMathFunction { // Generate the random number using the linear congruential // generator defined by the following recurrence: // seed = ( IA * seed ) mod IM // where IA is 16807 and IM is (2^31) - 1. In order to avoid // potential problems with integer overflow, the code uses // additional constants IQ and IR such that // IM = IA*IQ + IR // For details on how this algorithm works, refer to the following // papers: // // S.K. Park & K.W. Miller, "Random number generators: good ones // are hard to find," Comm ACM 31(10):1192-1201, Oct 1988 // // W.H. Press & S.A. Teukolsky, "Portable random number // generators," Computers in Physics 6(5):522-524, Sep/Oct 1992. private const int randIA = 16807; private const int randIM = 2147483647; private const int randIQ = 127773; private const int randIR = 2836; private static readonly System.DateTime date = System.DateTime.Now; /// Srand calls the main algorithm for rand after it sets the seed. /// To facilitate this call, the method is static and can be used /// w/o creating a new object. But we also need to maintain the /// inheritance hierarchy, thus the dynamic apply() calls the static /// statApply(). /// internal override ExprValue apply( Interp interp, TclObject[] argv ) { return ( statApply( interp, argv ) ); } internal static ExprValue statApply( Interp interp, TclObject[] argv ) { int tmp; if ( !( interp.randSeedInit ) ) { interp.randSeedInit = true; interp.randSeed = (int)date.Ticks; } if ( interp.randSeed == 0 ) { // Don't allow a 0 seed, since it breaks the generator. Shift // it to some other value. interp.randSeed = 123459876; } tmp = (int)( interp.randSeed / randIQ ); interp.randSeed = ( ( randIA * ( interp.randSeed - tmp * randIQ ) ) - randIR * tmp ); if ( interp.randSeed < 0 ) { interp.randSeed += randIM; } return new ExprValue( interp.randSeed * ( 1.0 / randIM ) ); } } class SrandFunction : UnaryMathFunction { internal override ExprValue apply( Interp interp, TclObject[] argv ) { // Reset the seed. interp.randSeedInit = true; interp.randSeed = (long)TclDouble.get( interp, argv[0] ); // To avoid duplicating the random number generation code we simply // call the static random number generator in the RandFunction // class. return ( RandFunction.statApply( interp, null ) ); } } }