/// <summary> Converts a value from int or double representation to a string.</summary> /// <param name="interp">interpreter to use for precision information. /// </param> /// <param name="value">Value to be converted. /// </param> 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; }
/// <summary> Parse a "value" from the remainder of the expression. /// /// </summary> /// <param name="interp">the context in which to evaluate the expression. /// </param> /// <param name="prec">treat any un-parenthesized operator with precedence /// <= this as the end of the expression. /// </param> /// <exception cref=""> TclException for malformed expressions. /// </exception> /// <returns> the value of the expression. /// </returns> 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; } } }
/// <summary> 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. /// /// </summary> /// <param name="interp">the context in which to evaluate the expression. /// </param> /// <exception cref=""> TclException for malformed expressions. /// </exception> /// <returns> the value of the expression. /// </returns> 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; } }