0001: /*
0002: * Expression.java
0003: *
0004: * Copyright (c) 1997 Cornell University.
0005: * Copyright (c) 1997 Sun Microsystems, Inc.
0006: *
0007: * See the file "license.terms" for information on usage and
0008: * redistribution of this file, and for a DISCLAIMER OF ALL
0009: * WARRANTIES.
0010: *
0011: * RCS: @(#) $Id: Expression.java,v 1.38 2007/01/14 01:02:33 mdejong Exp $
0012: *
0013: */
0014:
0015: package tcl.lang;
0016:
0017: import java.util.*;
0018:
0019: /**
0020: * This class handles Tcl expressions.
0021: */
0022: class Expression {
0023:
0024: // The token types are defined below. In addition, there is a
0025: // table associating a precedence with each operator. The order
0026: // of types is important. Consult the code before changing it.
0027:
0028: static final int VALUE = 0;
0029: static final int OPEN_PAREN = 1;
0030: static final int CLOSE_PAREN = 2;
0031: static final int COMMA = 3;
0032: static final int END = 4;
0033: static final int UNKNOWN = 5;
0034:
0035: // Binary operators:
0036:
0037: static final int MULT = 8;
0038: static final int DIVIDE = 9;
0039: static final int MOD = 10;
0040: static final int PLUS = 11;
0041: static final int MINUS = 12;
0042: static final int LEFT_SHIFT = 13;
0043: static final int RIGHT_SHIFT = 14;
0044: static final int LESS = 15;
0045: static final int GREATER = 16;
0046: static final int LEQ = 17;
0047: static final int GEQ = 18;
0048: static final int EQUAL = 19;
0049: static final int NEQ = 20;
0050: static final int BIT_AND = 21;
0051: static final int BIT_XOR = 22;
0052: static final int BIT_OR = 23;
0053: static final int AND = 24;
0054: static final int OR = 25;
0055: static final int QUESTY = 26;
0056: static final int COLON = 27;
0057: static final int STREQ = 28;
0058: static final int STRNEQ = 29;
0059:
0060: // Unary operators:
0061:
0062: static final int UNARY_MINUS = 30;
0063: static final int UNARY_PLUS = 31;
0064: static final int NOT = 32;
0065: static final int BIT_NOT = 33;
0066:
0067: // Precedence table. The values for non-operator token types are ignored.
0068:
0069: static int precTable[] = { 0, 0, 0, 0, 0, 0, 0, 0, 12, 12, 12, // MULT, DIVIDE, MOD
0070: 11, 11, // PLUS, MINUS
0071: 10, 10, // LEFT_SHIFT, RIGHT_SHIFT
0072: 9, 9, 9, 9, // LESS, GREATER, LEQ, GEQ
0073: 8, 8, // EQUAL, NEQ
0074: 7, // BIT_AND
0075: 6, // BIT_XOR
0076: 5, // BIT_OR
0077: 4, // AND
0078: 3, // OR
0079: 2, // QUESTY
0080: 1, // COLON
0081: 8, 8, // STREQ, STRNEQ
0082: 13, 13, 13, 13 // UNARY_MINUS, UNARY_PLUS, NOT,
0083: // BIT_NOT
0084: };
0085:
0086: // Mapping from operator numbers to strings; used for error messages.
0087:
0088: static String operatorStrings[] = { "VALUE", "(", ")", ",", "END",
0089: "UNKNOWN", "6", "7", "*", "/", "%", "+", "-", "<<", ">>",
0090: "<", ">", "<=", ">=", "==", "!=", "&", "^", "|", "&&",
0091: "||", "?", ":", "eq", "ne", "-", "+", "!", "~" };
0092:
0093: HashMap mathFuncTable;
0094:
0095: /**
0096: * The entire expression, as originally passed to eval et al.
0097: */
0098: private String m_expr;
0099:
0100: /**
0101: * Length of the expression.
0102: */
0103: private int m_len;
0104:
0105: /**
0106: * Type of the last token to be parsed from the expression.
0107: * Corresponds to the characters just before expr.
0108: */
0109: int m_token;
0110:
0111: /**
0112: * Position to the next character to be scanned from the expression
0113: * string.
0114: */
0115: private int m_ind;
0116:
0117: /**
0118: * Cache of ExprValue objects. These are cached on a per-interp
0119: * basis to speed up most expressions.
0120: */
0121:
0122: private ExprValue[] cachedExprValue;
0123: private int cachedExprIndex = 0;
0124: private static final int cachedExprLength = 50;
0125:
0126: /**
0127: * Evaluate a Tcl expression and set the interp result to the value.
0128: *
0129: * @param interp the context in which to evaluate the expression.
0130: * @param string expression to evaluate.
0131: * @return the value of the expression.
0132: * @exception TclException for malformed expressions.
0133: */
0134:
0135: void evalSetResult(Interp interp, String string)
0136: throws TclException {
0137: TclObject obj;
0138: ExprValue value = ExprTopLevel(interp, string);
0139: switch (value.getType()) {
0140: case ExprValue.INT:
0141: interp.setResult(value.getIntValue());
0142: break;
0143: case ExprValue.DOUBLE:
0144: interp.setResult(value.getDoubleValue());
0145: break;
0146: case ExprValue.STRING:
0147: interp.setResult(value.getStringValue());
0148: break;
0149: default:
0150: throw new TclRuntimeError(
0151: "internal error: expression, unknown");
0152: }
0153: releaseExprValue(value);
0154: return;
0155: }
0156:
0157: /**
0158: * Evaluate an Tcl expression.
0159: * @param interp the context in which to evaluate the expression.
0160: * @param string expression to evaluate.
0161: * @exception TclException for malformed expressions.
0162: * @return the value of the expression in boolean.
0163: */
0164: boolean evalBoolean(Interp interp, String string)
0165: throws TclException {
0166: ExprValue value = ExprTopLevel(interp, string);
0167: boolean b = value.getBooleanValue(interp);
0168: releaseExprValue(value);
0169: return b;
0170: }
0171:
0172: /**
0173: * Constructor.
0174: */
0175: Expression() {
0176: mathFuncTable = new HashMap();
0177:
0178: // rand -- needs testing
0179: // srand -- needs testing
0180: // hypot -- needs testing
0181: // fmod -- needs testing
0182: // try [expr fmod(4.67, 2.2)]
0183: // the answer should be .27, but I got .2699999999999996
0184:
0185: registerMathFunction("atan2", new Atan2Function());
0186: registerMathFunction("pow", new PowFunction());
0187: registerMathFunction("acos", new AcosFunction());
0188: registerMathFunction("asin", new AsinFunction());
0189: registerMathFunction("atan", new AtanFunction());
0190: registerMathFunction("ceil", new CeilFunction());
0191: registerMathFunction("cos", new CosFunction());
0192: registerMathFunction("cosh", new CoshFunction());
0193: registerMathFunction("exp", new ExpFunction());
0194: registerMathFunction("floor", new FloorFunction());
0195: registerMathFunction("fmod", new FmodFunction());
0196: registerMathFunction("hypot", new HypotFunction());
0197: registerMathFunction("log", new LogFunction());
0198: registerMathFunction("log10", new Log10Function());
0199: registerMathFunction("rand", new RandFunction());
0200: registerMathFunction("sin", new SinFunction());
0201: registerMathFunction("sinh", new SinhFunction());
0202: registerMathFunction("sqrt", new SqrtFunction());
0203: registerMathFunction("srand", new SrandFunction());
0204: registerMathFunction("tan", new TanFunction());
0205: registerMathFunction("tanh", new TanhFunction());
0206:
0207: registerMathFunction("abs", new AbsFunction());
0208: registerMathFunction("double", new DoubleFunction());
0209: registerMathFunction("int", new IntFunction());
0210: registerMathFunction("round", new RoundFunction());
0211:
0212: m_expr = null;
0213: m_ind = 0;
0214: m_len = 0;
0215: m_token = UNKNOWN;
0216:
0217: cachedExprValue = new ExprValue[cachedExprLength];
0218: for (int i = 0; i < cachedExprLength; i++) {
0219: cachedExprValue[i] = new ExprValue(0, null);
0220: }
0221: }
0222:
0223: /**
0224: * Provides top-level functionality shared by procedures like ExprInt,
0225: * ExprDouble, etc.
0226: * @param interp the context in which to evaluate the expression.
0227: * @param string the expression.
0228: * @exception TclException for malformed expressions.
0229: * @return the value of the expression.
0230: */
0231: private final ExprValue ExprTopLevel(Interp interp, String string)
0232: throws TclException {
0233:
0234: // Saved the state variables so that recursive calls to expr
0235: // can work:
0236: // expr {[expr 1+2] + 3}
0237:
0238: String m_expr_saved = m_expr;
0239: int m_len_saved = m_len;
0240: int m_token_saved = m_token;
0241: int m_ind_saved = m_ind;
0242:
0243: try {
0244: m_expr = string;
0245: m_ind = 0;
0246: m_len = string.length();
0247: m_token = UNKNOWN;
0248:
0249: ExprValue val = ExprGetValue(interp, -1);
0250: if (m_token != END) {
0251: SyntaxError(interp);
0252: }
0253: return val;
0254: } finally {
0255: m_expr = m_expr_saved;
0256: m_len = m_len_saved;
0257: m_token = m_token_saved;
0258: m_ind = m_ind_saved;
0259: }
0260: }
0261:
0262: static void IllegalType(Interp interp, int badType, int operator)
0263: throws TclException {
0264: throw new TclException(
0265: interp,
0266: "can't use "
0267: + ((badType == ExprValue.DOUBLE) ? "floating-point value"
0268: : "non-numeric string")
0269: + " as operand of \""
0270: + operatorStrings[operator] + "\"");
0271: }
0272:
0273: void SyntaxError(Interp interp) throws TclException {
0274: throw new TclException(interp, "syntax error in expression \""
0275: + m_expr + "\"");
0276: }
0277:
0278: static void DivideByZero(Interp interp) throws TclException {
0279: interp.setErrorCode(TclString
0280: .newInstance("ARITH DIVZERO {divide by zero}"));
0281: throw new TclException(interp, "divide by zero");
0282: }
0283:
0284: static void IntegerTooLarge(Interp interp) throws TclException {
0285: interp
0286: .setErrorCode(TclString
0287: .newInstance("ARITH IOVERFLOW {integer value too large to represent}"));
0288: throw new TclException(interp,
0289: "integer value too large to represent");
0290: }
0291:
0292: static void DoubleTooLarge(Interp interp) throws TclException {
0293: interp
0294: .setErrorCode(TclString
0295: .newInstance("ARITH OVERFLOW {floating-point value too large to represent}"));
0296: throw new TclException(interp,
0297: "floating-point value too large to represent");
0298: }
0299:
0300: static void DoubleTooSmall(Interp interp) throws TclException {
0301: interp
0302: .setErrorCode(TclString
0303: .newInstance("ARITH UNDERFLOW {floating-point value too small to represent}"));
0304: throw new TclException(interp,
0305: "floating-point value too small to represent");
0306: }
0307:
0308: static void DomainError(Interp interp) throws TclException {
0309: interp
0310: .setErrorCode(TclString
0311: .newInstance("ARITH DOMAIN {domain error: argument not in valid range}"));
0312: throw new TclException(interp,
0313: "domain error: argument not in valid range");
0314: }
0315:
0316: static void EmptyStringOperandError(Interp interp, int operator)
0317: throws TclException {
0318: throw new TclException(interp, "can't use " + "empty string"
0319: + " as operand of \"" + operatorStrings[operator]
0320: + "\"");
0321: }
0322:
0323: /**
0324: * Given a TclObject, such as the result of a command or
0325: * variable evaluation, fill in a ExprValue with the
0326: * parsed result. If the TclObject already has an
0327: * internal rep that is a numeric type, then no need to
0328: * parse from the string rep. If the string rep is parsed
0329: * into a numeric type, then update the internal rep
0330: * of the object to the parsed value.
0331: */
0332:
0333: static void ExprParseObject(final Interp interp,
0334: final TclObject obj, final ExprValue value)
0335: throws TclException {
0336: // If the TclObject already has an integer, boolean,
0337: // or floating point internal representation, use it.
0338:
0339: if (obj.isIntType()) {
0340: // A TclObject is a "pure" number if it
0341: // was created from a primitive type and
0342: // has no string rep. Pass the string rep
0343: // along in the ExprValue object if there
0344: // is one.
0345:
0346: value.setIntValue(obj.ivalue, // Inline TclInteger.get()
0347: (obj.hasNoStringRep() ? null : obj.toString()));
0348: return;
0349: } else if (obj.isDoubleType()) {
0350: // A TclObject with a double internal rep will
0351: // never have a string rep that could be parsed
0352: // as an integer.
0353:
0354: value.setDoubleValue(
0355: // Inline TclDouble.get()
0356: ((TclDouble) obj.getInternalRep()).value, (obj
0357: .hasNoStringRep() ? null : obj.toString()));
0358: return;
0359: }
0360:
0361: // Otherwise, try to parse a numeric value from the
0362: // object's string rep.
0363:
0364: ExprParseString(interp, obj, value);
0365:
0366: return;
0367: }
0368:
0369: /**
0370: * TclParseNumber -> ExprParseString
0371: *
0372: * Given a TclObject that contains a String to be parsed (from
0373: * a command or variable subst), fill in an ExprValue based on
0374: * the string's numeric value. The value may be a floating-point,
0375: * an integer, or a string. If the string value is converted to
0376: * a numeric value, then update the internal rep of the TclObject.
0377: *
0378: * @param interp the context in which to evaluate the expression.
0379: * @param obj the TclObject containing the string to parse.
0380: * @param value the ExprValue object to save the parsed value in.
0381: */
0382:
0383: static void ExprParseString(final Interp interp,
0384: final TclObject obj, final ExprValue value) {
0385: char c;
0386: int ival;
0387: double dval;
0388: final String s = obj.toString();
0389: final int len = s.length();
0390:
0391: //System.out.println("now to ExprParseString ->" + s +
0392: // "<- of length " + len);
0393:
0394: switch (len) {
0395: case 0: {
0396: // Take shortcut when string is of length 0, as the
0397: // empty string can't represent an int, double, or boolean.
0398:
0399: value.setStringValue("");
0400: return;
0401: }
0402: case 1: {
0403: // Check for really common strings of length 1
0404: // that we know will be integers.
0405:
0406: c = s.charAt(0);
0407: switch (c) {
0408: case '0':
0409: case '1':
0410: case '2':
0411: case '3':
0412: case '4':
0413: case '5':
0414: case '6':
0415: case '7':
0416: case '8':
0417: case '9':
0418: ival = (int) (c - '0');
0419: value.setIntValue(ival, s);
0420: TclInteger.exprSetInternalRep(obj, ival);
0421: return;
0422: default:
0423: // We know this string can't be parsed
0424: // as a number, so just treat it as
0425: // a string. A string of length 1 is
0426: // very common.
0427:
0428: value.setStringValue(s);
0429: return;
0430: }
0431: }
0432: case 2: {
0433: // Check for really common strings of length 2
0434: // that we know will be integers.
0435:
0436: c = s.charAt(0);
0437: if (c == '-') {
0438: c = s.charAt(1);
0439: switch (c) {
0440: case '0':
0441: case '1':
0442: case '2':
0443: case '3':
0444: case '4':
0445: case '5':
0446: case '6':
0447: case '7':
0448: case '8':
0449: case '9':
0450: ival = (int) -(c - '0');
0451: value.setIntValue(ival, s);
0452: TclInteger.exprSetInternalRep(obj, ival);
0453: return;
0454: }
0455: }
0456: break;
0457: }
0458: case 3: {
0459: // Check for really common strings of length 3
0460: // that we know will be doubles.
0461:
0462: c = s.charAt(1);
0463: if (c == '.') {
0464: if (s.equals("0.0")) {
0465: dval = 0.0;
0466: value.setDoubleValue(dval, s);
0467: TclDouble.exprSetInternalRep(obj, dval);
0468: return;
0469: } else if (s.equals("0.5")) {
0470: dval = 0.5;
0471: value.setDoubleValue(dval, s);
0472: TclDouble.exprSetInternalRep(obj, dval);
0473: return;
0474: } else if (s.equals("1.0")) {
0475: dval = 1.0;
0476: value.setDoubleValue(dval, s);
0477: TclDouble.exprSetInternalRep(obj, dval);
0478: return;
0479: } else if (s.equals("2.0")) {
0480: dval = 2.0;
0481: value.setDoubleValue(dval, s);
0482: TclDouble.exprSetInternalRep(obj, dval);
0483: return;
0484: }
0485: }
0486: break;
0487: }
0488: case 4: {
0489: if (s.equals("true")) {
0490: value.setStringValue(s);
0491: return;
0492: }
0493: break;
0494: }
0495: case 5: {
0496: if (s.equals("false")) {
0497: value.setStringValue(s);
0498: return;
0499: }
0500: break;
0501: }
0502: }
0503:
0504: if (looksLikeInt(s, len, 0, false)) {
0505: //System.out.println("string looks like an int");
0506:
0507: // Note: the Util.strtoul() method handles 32bit unsigned values
0508: // as well as leading sign characters.
0509:
0510: StrtoulResult res = interp.strtoulResult;
0511: Util.strtoul(s, 0, 0, res);
0512: //String token = s.substring(i, res.index);
0513: //System.out.println("token string from strtoul is \"" + token + "\"");
0514: //System.out.println("res.errno is " + res.errno);
0515:
0516: if (res.errno == 0) {
0517: // We treat this string as a number if all the charcters
0518: // following the parsed number are a whitespace chars.
0519: // E.g.: " 1", "1", "1 ", and " 1 " are all good numbers
0520:
0521: boolean trailing_blanks = true;
0522:
0523: for (int i = res.index; i < len; i++) {
0524: if ((c = s.charAt(i)) != ' '
0525: && !Character.isWhitespace(c)) {
0526: trailing_blanks = false;
0527: break;
0528: }
0529: }
0530:
0531: if (trailing_blanks) {
0532: ival = (int) res.value;
0533: //System.out.println("string is an Integer of value " + ival);
0534: value.setIntValue(ival, s);
0535: TclInteger.exprSetInternalRep(obj, ival);
0536: return;
0537: } else {
0538: //System.out.println("string failed trailing_blanks test, not an integer");
0539: }
0540: }
0541: } else {
0542: //System.out.println("string does not look like an int, checking for Double");
0543:
0544: StrtodResult res = interp.strtodResult;
0545: Util.strtod(s, 0, len, res);
0546:
0547: if (res.errno == 0) {
0548: // Trailing whitespaces are treated just like the Integer case
0549:
0550: boolean trailing_blanks = true;
0551:
0552: for (int i = res.index; i < len; i++) {
0553: if ((c = s.charAt(i)) != ' '
0554: && !Character.isWhitespace(c)) {
0555: trailing_blanks = false;
0556: break;
0557: }
0558: }
0559:
0560: if (trailing_blanks) {
0561: dval = res.value;
0562: //System.out.println("string is a Double of value " + dval);
0563: value.setDoubleValue(dval, s);
0564: TclDouble.exprSetInternalRep(obj, dval);
0565: return;
0566: }
0567:
0568: }
0569: }
0570:
0571: //System.out.println("string is not a valid number, returning as string \"" + s + "\"");
0572:
0573: // Not a valid number. Save a string value (but don't do anything
0574: // if it's already the value).
0575:
0576: value.setStringValue(s);
0577: return;
0578: }
0579:
0580: /**
0581: * Parse a "value" from the remainder of the expression.
0582: *
0583: * @param interp the context in which to evaluate the expression.
0584: * @param prec treat any un-parenthesized operator with precedence
0585: * <= this as the end of the expression.
0586: * @exception TclException for malformed expressions.
0587: * @return the value of the expression.
0588: */
0589: private ExprValue ExprGetValue(Interp interp, int prec)
0590: throws TclException {
0591: int operator;
0592: boolean gotOp = false; // True means already lexed the
0593: // operator (while picking up value
0594: // for unary operator). Don't lex
0595: // again.
0596: ExprValue value, value2 = null;
0597:
0598: // There are two phases to this procedure. First, pick off an
0599: // initial value. Then, parse (binary operator, value) pairs
0600: // until done.
0601:
0602: value = ExprLex(interp);
0603:
0604: if (m_token == OPEN_PAREN) {
0605:
0606: // Parenthesized sub-expression.
0607:
0608: value = ExprGetValue(interp, -1);
0609: if (m_token != CLOSE_PAREN) {
0610: SyntaxError(interp);
0611: }
0612: } else {
0613: if (m_token == MINUS) {
0614: m_token = UNARY_MINUS;
0615: }
0616: if (m_token == PLUS) {
0617: m_token = UNARY_PLUS;
0618: }
0619: if (m_token >= UNARY_MINUS) {
0620:
0621: // Process unary operators.
0622:
0623: operator = m_token;
0624: value = ExprGetValue(interp, precTable[m_token]);
0625:
0626: if (interp.noEval == 0) {
0627: evalUnaryOperator(interp, operator, value);
0628: }
0629: gotOp = true;
0630: } else if (m_token == CLOSE_PAREN) {
0631: // Caller needs to deal with close paren token.
0632: return null;
0633: } else if (m_token != VALUE) {
0634: SyntaxError(interp);
0635: }
0636: }
0637: if (value == null) {
0638: SyntaxError(interp);
0639: }
0640:
0641: // Got the first operand. Now fetch (operator, operand) pairs.
0642:
0643: if (!gotOp) {
0644: value2 = ExprLex(interp);
0645: }
0646:
0647: while (true) {
0648: operator = m_token;
0649: if ((operator < MULT) || (operator >= UNARY_MINUS)) {
0650: if ((operator == END) || (operator == CLOSE_PAREN)
0651: || (operator == COMMA)) {
0652: return value; // Goto Done
0653: } else {
0654: SyntaxError(interp);
0655: }
0656: }
0657: if (precTable[operator] <= prec) {
0658: return value; // (goto done)
0659: }
0660:
0661: // If we're doing an AND or OR and the first operand already
0662: // determines the result, don't execute anything in the
0663: // second operand: just parse. Same style for ?: pairs.
0664:
0665: if ((operator == AND) || (operator == OR)
0666: || (operator == QUESTY)) {
0667:
0668: if (value.isDoubleType()) {
0669: value.setIntValue(value.getDoubleValue() != 0.0);
0670: } else if (value.isStringType()) {
0671: try {
0672: boolean b = Util.getBoolean(interp, value
0673: .getStringValue());
0674: value.setIntValue(b);
0675: } catch (TclException e) {
0676: if (interp.noEval == 0) {
0677: throw e;
0678: }
0679:
0680: // Must set value.intValue to avoid referencing
0681: // uninitialized memory in the "if" below; the actual
0682: // value doesn't matter, since it will be ignored.
0683:
0684: value.setIntValue(0);
0685: }
0686: }
0687: if (((operator == AND) && (value.getIntValue() == 0))
0688: || ((operator == OR) && (value.getIntValue() != 0))) {
0689: interp.noEval++;
0690: try {
0691: value2 = ExprGetValue(interp,
0692: precTable[operator]);
0693: } finally {
0694: interp.noEval--;
0695: }
0696: if (operator == OR) {
0697: value.setIntValue(1);
0698: }
0699: continue;
0700: } else if (operator == QUESTY) {
0701: // Special note: ?: operators must associate right to
0702: // left. To make this happen, use a precedence one lower
0703: // than QUESTY when calling ExprGetValue recursively.
0704:
0705: if (value.getIntValue() != 0) {
0706: value = ExprGetValue(interp,
0707: precTable[QUESTY] - 1);
0708: if (m_token != COLON) {
0709: SyntaxError(interp);
0710: }
0711:
0712: interp.noEval++;
0713: try {
0714: value2 = ExprGetValue(interp,
0715: precTable[QUESTY] - 1);
0716: } finally {
0717: interp.noEval--;
0718: }
0719: } else {
0720: interp.noEval++;
0721: try {
0722: value2 = ExprGetValue(interp,
0723: precTable[QUESTY] - 1);
0724: } finally {
0725: interp.noEval--;
0726: }
0727: if (m_token != COLON) {
0728: SyntaxError(interp);
0729: }
0730: value = ExprGetValue(interp,
0731: precTable[QUESTY] - 1);
0732: }
0733: continue;
0734: } else {
0735: value2 = ExprGetValue(interp, precTable[operator]);
0736: }
0737: } else {
0738: value2 = ExprGetValue(interp, precTable[operator]);
0739: }
0740:
0741: if (value2 == null) {
0742: SyntaxError(interp);
0743: }
0744:
0745: if ((m_token < MULT) && (m_token != VALUE)
0746: && (m_token != END) && (m_token != COMMA)
0747: && (m_token != CLOSE_PAREN)) {
0748: SyntaxError(interp);
0749: }
0750:
0751: if (interp.noEval != 0) {
0752: continue;
0753: }
0754:
0755: if (operator == COLON) {
0756: SyntaxError(interp);
0757: }
0758: evalBinaryOperator(interp, operator, value, value2);
0759: releaseExprValue(value2);
0760: } // end of while(true) loop
0761: }
0762:
0763: // Evaluate the result of a unary operator ( - + ! ~)
0764: // when it is applied to a value. The passed in value
0765: // contains the result.
0766:
0767: static void evalUnaryOperator(final Interp interp,
0768: final int operator, final ExprValue value)
0769: throws TclException {
0770: switch (operator) {
0771: case UNARY_MINUS:
0772: if (value.isIntType()) {
0773: value.setIntValue(value.getIntValue() * -1);
0774: } else if (value.isDoubleType()) {
0775: value.setDoubleValue(value.getDoubleValue() * -1.0);
0776: } else {
0777: IllegalType(interp, value.getType(), operator);
0778: }
0779: break;
0780: case UNARY_PLUS:
0781: // Unary + operator raises an error for a String,
0782: // otherwise it tosses out the string rep.
0783:
0784: if (value.isIntOrDoubleType()) {
0785: value.nullStringValue();
0786: } else {
0787: IllegalType(interp, value.getType(), operator);
0788: }
0789: break;
0790: case NOT:
0791: if (value.isIntType()) {
0792: // Inlined method does not reset type to INT
0793: value.optIntUnaryNot();
0794: } else if (value.isDoubleType()) {
0795: value.setIntValue(value.getDoubleValue() == 0.0);
0796: } else if (value.isStringType()) {
0797: String s = value.getStringValue();
0798: int s_len = s.length();
0799: if (s_len == 0) {
0800: EmptyStringOperandError(interp, operator);
0801: }
0802: String tok = getBooleanToken(s);
0803: // Reject a string like "truea"
0804: if (tok != null && tok.length() == s_len) {
0805: if ("true".startsWith(tok) || "on".startsWith(tok)
0806: || "yes".startsWith(tok)) {
0807: value.setIntValue(0);
0808: } else {
0809: value.setIntValue(1);
0810: }
0811: } else {
0812: IllegalType(interp, value.getType(), operator);
0813: }
0814: } else {
0815: IllegalType(interp, value.getType(), operator);
0816: }
0817: break;
0818: case BIT_NOT:
0819: if (value.isIntType()) {
0820: value.setIntValue(~value.getIntValue());
0821: } else {
0822: IllegalType(interp, value.getType(), operator);
0823: }
0824: break;
0825: default:
0826: throw new TclException(interp,
0827: "unknown operator in expression");
0828: }
0829: }
0830:
0831: // Evaluate the result of a binary operator (* / + - % << >> ...)
0832: // when applied to a pair of values. The result is returned in
0833: // the first (left hand) value. This method will check data
0834: // types and perform conversions if needed before executing
0835: // the operation. The value2 argument (right hand) value will
0836: // not be released, the caller should release it.
0837:
0838: static void evalBinaryOperator(final Interp interp,
0839: final int operator, final ExprValue value, // value on left hand side
0840: final ExprValue value2) // value on right hand side
0841: throws TclException {
0842: final boolean USE_INLINED = true;
0843:
0844: int t1 = value.getType();
0845: int t2 = value2.getType();
0846:
0847: switch (operator) {
0848: // For the operators below, no strings are allowed and
0849: // ints get converted to floats if necessary.
0850:
0851: case MULT:
0852: case DIVIDE:
0853: case PLUS:
0854: case MINUS:
0855: if (t1 == ExprValue.STRING || t2 == ExprValue.STRING) {
0856: if ((value.getStringValue().length() == 0)
0857: || (value2.getStringValue().length() == 0)) {
0858: EmptyStringOperandError(interp, operator);
0859: }
0860: IllegalType(interp, ExprValue.STRING, operator);
0861: } else if (t1 == ExprValue.DOUBLE) {
0862: if (t2 == ExprValue.INT) {
0863: value2
0864: .setDoubleValue((double) value2
0865: .getIntValue());
0866: t2 = ExprValue.DOUBLE;
0867: }
0868: } else if (t2 == ExprValue.DOUBLE) {
0869: if (t1 == ExprValue.INT) {
0870: value.setDoubleValue((double) value.getIntValue());
0871: t1 = ExprValue.DOUBLE;
0872: }
0873: }
0874: break;
0875:
0876: // For the operators below, only integers are allowed.
0877:
0878: case MOD:
0879: case LEFT_SHIFT:
0880: case RIGHT_SHIFT:
0881: case BIT_AND:
0882: case BIT_XOR:
0883: case BIT_OR:
0884: if (t1 != ExprValue.INT) {
0885: if (value.getStringValue().length() == 0) {
0886: EmptyStringOperandError(interp, operator);
0887: }
0888: IllegalType(interp, value.getType(), operator);
0889: } else if (t2 != ExprValue.INT) {
0890: if (value2.getStringValue().length() == 0) {
0891: EmptyStringOperandError(interp, operator);
0892: }
0893: IllegalType(interp, value2.getType(), operator);
0894: }
0895:
0896: break;
0897:
0898: // For the operators below, any type is allowed but the
0899: // two operands must have the same type. Convert integers
0900: // to floats and either to strings, if necessary.
0901:
0902: case LESS:
0903: case GREATER:
0904: case LEQ:
0905: case GEQ:
0906: case EQUAL:
0907: case NEQ:
0908: if (t1 == t2) {
0909: // No-op, both operators are already the same type
0910: } else if (t1 == ExprValue.STRING) {
0911: if (t2 != ExprValue.STRING) {
0912: value2.toStringType();
0913: t2 = ExprValue.STRING;
0914: }
0915: } else if (t2 == ExprValue.STRING) {
0916: if (t1 != ExprValue.STRING) {
0917: value.toStringType();
0918: t1 = ExprValue.STRING;
0919: }
0920: } else if (t1 == ExprValue.DOUBLE) {
0921: if (t2 == ExprValue.INT) {
0922: value2
0923: .setDoubleValue((double) value2
0924: .getIntValue());
0925: t2 = ExprValue.DOUBLE;
0926: }
0927: } else if (t2 == ExprValue.DOUBLE) {
0928: if (t1 == ExprValue.INT) {
0929: value.setDoubleValue((double) value.getIntValue());
0930: t1 = ExprValue.DOUBLE;
0931: }
0932: }
0933: break;
0934:
0935: // For the 2 operators below, string comparison is always
0936: // done, so no operand validation is needed.
0937:
0938: case STREQ:
0939: value.setIntValue(value.getStringValue().equals(
0940: value2.getStringValue()));
0941: return;
0942: case STRNEQ:
0943: value.setIntValue(!value.getStringValue().equals(
0944: value2.getStringValue()));
0945: return;
0946:
0947: // For the operators below, no strings are allowed, but
0948: // no int->double conversions are performed.
0949:
0950: case AND:
0951: case OR:
0952: if (t1 == ExprValue.STRING) {
0953: IllegalType(interp, ExprValue.STRING, operator);
0954: }
0955: if (t2 == ExprValue.STRING) {
0956: boolean b = Util.getBoolean(interp, value2
0957: .getStringValue());
0958: value2.setIntValue(b);
0959: }
0960: break;
0961:
0962: // For the operators below, type and conversions are
0963: // irrelevant: they're handled elsewhere.
0964:
0965: case QUESTY:
0966: case COLON:
0967: break;
0968:
0969: // Any other operator is an error.
0970:
0971: default:
0972: throw new TclException(interp,
0973: "unknown operator in expression");
0974: }
0975:
0976: // Carry out the function of the specified operator.
0977:
0978: switch (operator) {
0979: case MULT:
0980: if (t1 == ExprValue.INT) {
0981: if (USE_INLINED) {
0982: value.optIntMult(value2);
0983: } else {
0984: value.setIntValue(value.getIntValue()
0985: * value2.getIntValue());
0986: }
0987: } else {
0988: if (USE_INLINED) {
0989: value.optDoubleMult(value2);
0990: } else {
0991: value.setDoubleValue(value.getDoubleValue()
0992: * value2.getDoubleValue());
0993: }
0994: }
0995: break;
0996: case DIVIDE:
0997: if (t1 == ExprValue.INT) {
0998: int dividend, divisor, quotient;
0999:
1000: if (value2.getIntValue() == 0) {
1001: DivideByZero(interp);
1002: }
1003:
1004: // quotient = dividend / divisor
1005: //
1006: // When performing integer division, protect
1007: // against integer overflow. Round towards zero
1008: // when the quotient is positive, otherwise
1009: // round towards -Infinity.
1010:
1011: dividend = value.getIntValue();
1012: divisor = value2.getIntValue();
1013:
1014: if (dividend == Integer.MIN_VALUE && divisor == -1) {
1015: // Avoid integer overflow on (Integer.MIN_VALUE / -1)
1016: quotient = Integer.MIN_VALUE;
1017: } else {
1018: quotient = dividend / divisor;
1019: // Round down to a smaller negative number if
1020: // there is a remainder and the quotient is
1021: // negative or zero and the signs don't match.
1022: if (((quotient < 0) || ((quotient == 0) && (((dividend < 0) && (divisor > 0)) || ((dividend > 0) && (divisor < 0)))))
1023: && ((quotient * divisor) != dividend)) {
1024: quotient -= 1;
1025: }
1026: }
1027: value.setIntValue(quotient);
1028: } else {
1029: double divisor = value2.getDoubleValue();
1030: if (divisor == 0.0) {
1031: DivideByZero(interp);
1032: }
1033: value.setDoubleValue(value.getDoubleValue() / divisor);
1034: }
1035: break;
1036: case MOD:
1037: int dividend,
1038: divisor,
1039: remainder;
1040: boolean neg_divisor = false;
1041:
1042: if (value2.getIntValue() == 0) {
1043: DivideByZero(interp);
1044: }
1045:
1046: // remainder = dividend % divisor
1047: //
1048: // In Tcl, the sign of the remainder must match
1049: // the sign of the divisor. The absolute value of
1050: // the remainder must be smaller than the divisor.
1051: //
1052: // In Java, the remainder can be negative only if
1053: // the dividend is negative. The remainder will
1054: // always be smaller than the divisor.
1055: //
1056: // See: http://mindprod.com/jgloss/modulus.html
1057:
1058: dividend = value.getIntValue();
1059: divisor = value2.getIntValue();
1060:
1061: if (dividend == Integer.MIN_VALUE && divisor == -1) {
1062: // Avoid integer overflow on (Integer.MIN_VALUE % -1)
1063: remainder = 0;
1064: } else {
1065: if (divisor < 0) {
1066: divisor = -divisor;
1067: dividend = -dividend; // Note: -Integer.MIN_VALUE == Integer.MIN_VALUE
1068: neg_divisor = true;
1069: }
1070: remainder = dividend % divisor;
1071:
1072: // remainder is (remainder + divisor) when the
1073: // remainder is negative. Watch out for the
1074: // special case of a Integer.MIN_VALUE dividend
1075: // and a negative divisor. Don't add the divisor
1076: // in that case because the remainder should
1077: // not be negative.
1078:
1079: if (remainder < 0
1080: && !(neg_divisor && (dividend == Integer.MIN_VALUE))) {
1081: remainder += divisor;
1082: }
1083: }
1084: if ((neg_divisor && (remainder > 0))
1085: || (!neg_divisor && (remainder < 0))) {
1086: remainder = -remainder;
1087: }
1088: value.setIntValue(remainder);
1089: break;
1090: case PLUS:
1091: if (t1 == ExprValue.INT) {
1092: if (USE_INLINED) {
1093: value.optIntPlus(value2);
1094: } else {
1095: value.setIntValue(value.getIntValue()
1096: + value2.getIntValue());
1097: }
1098: } else {
1099: if (USE_INLINED) {
1100: value.optDoublePlus(value2);
1101: } else {
1102: value.setDoubleValue(value.getDoubleValue()
1103: + value2.getDoubleValue());
1104: }
1105: }
1106: break;
1107: case MINUS:
1108: if (t1 == ExprValue.INT) {
1109: if (USE_INLINED) {
1110: value.optIntMinus(value2);
1111: } else {
1112: value.setIntValue(value.getIntValue()
1113: - value2.getIntValue());
1114: }
1115: } else {
1116: if (USE_INLINED) {
1117: value.optDoubleMinus(value2);
1118: } else {
1119: value.setDoubleValue(value.getDoubleValue()
1120: - value2.getDoubleValue());
1121: }
1122: }
1123: break;
1124: case LEFT_SHIFT:
1125: // In Java, a left shift operation will shift bits from 0
1126: // to 31 places to the left. For an int left operand
1127: // the right operand value is implicitly (value & 0x1f),
1128: // so a negative shift amount is in the 0 to 31 range.
1129:
1130: int left_shift_num = value.getIntValue();
1131: int left_shift_by = value2.getIntValue();
1132: if (left_shift_by >= 32) {
1133: left_shift_num = 0;
1134: } else {
1135: left_shift_num <<= left_shift_by;
1136: }
1137: value.setIntValue(left_shift_num);
1138: break;
1139: case RIGHT_SHIFT:
1140: // In Java, a right shift operation will shift bits from 0
1141: // to 31 places to the right and propagate the sign bit.
1142: // For an int left operand, the right operand is implicitly
1143: // (value & 0x1f), so a negative shift is in the 0 to 31 range.
1144:
1145: int right_shift_num = value.getIntValue();
1146: int right_shift_by = value2.getIntValue();
1147: if (right_shift_by >= 32) {
1148: if (right_shift_num < 0) {
1149: right_shift_num = -1;
1150: } else {
1151: right_shift_num = 0;
1152: }
1153: } else {
1154: right_shift_num >>= right_shift_by;
1155: }
1156: value.setIntValue(right_shift_num);
1157: break;
1158: case LESS:
1159: if (t1 == ExprValue.INT) {
1160: if (USE_INLINED) {
1161: value.optIntLess(value2);
1162: } else {
1163: value.setIntValue(value.getIntValue() < value2
1164: .getIntValue());
1165: }
1166: } else if (t1 == ExprValue.DOUBLE) {
1167: if (USE_INLINED) {
1168: value.optDoubleLess(value2);
1169: } else {
1170: value.setIntValue(value.getDoubleValue() < value2
1171: .getDoubleValue());
1172: }
1173: } else {
1174: value.setIntValue(value.getStringValue().compareTo(
1175: value2.getStringValue()) < 0);
1176: }
1177: break;
1178: case GREATER:
1179: if (t1 == ExprValue.INT) {
1180: if (USE_INLINED) {
1181: value.optIntGreater(value2);
1182: } else {
1183: value.setIntValue(value.getIntValue() > value2
1184: .getIntValue());
1185: }
1186: } else if (t1 == ExprValue.DOUBLE) {
1187: if (USE_INLINED) {
1188: value.optDoubleGreater(value2);
1189: } else {
1190: value.setIntValue(value.getDoubleValue() > value2
1191: .getDoubleValue());
1192: }
1193: } else {
1194: value.setIntValue(value.getStringValue().compareTo(
1195: value2.getStringValue()) > 0);
1196: }
1197: break;
1198: case LEQ:
1199: if (t1 == ExprValue.INT) {
1200: if (USE_INLINED) {
1201: value.optIntLessEq(value2);
1202: } else {
1203: value.setIntValue(value.getIntValue() <= value2
1204: .getIntValue());
1205: }
1206: } else if (t1 == ExprValue.DOUBLE) {
1207: if (USE_INLINED) {
1208: value.optDoubleLessEq(value2);
1209: } else {
1210: value.setIntValue(value.getDoubleValue() <= value2
1211: .getDoubleValue());
1212: }
1213: } else {
1214: value.setIntValue(value.getStringValue().compareTo(
1215: value2.getStringValue()) <= 0);
1216: }
1217: break;
1218: case GEQ:
1219: if (t1 == ExprValue.INT) {
1220: if (USE_INLINED) {
1221: value.optIntGreaterEq(value2);
1222: } else {
1223: value.setIntValue(value.getIntValue() >= value2
1224: .getIntValue());
1225: }
1226: } else if (t1 == ExprValue.DOUBLE) {
1227: if (USE_INLINED) {
1228: value.optDoubleGreaterEq(value2);
1229: } else {
1230: value.setIntValue(value.getDoubleValue() >= value2
1231: .getDoubleValue());
1232: }
1233: } else {
1234: value.setIntValue(value.getStringValue().compareTo(
1235: value2.getStringValue()) >= 0);
1236: }
1237: break;
1238: case EQUAL:
1239: if (t1 == ExprValue.INT) {
1240: if (USE_INLINED) {
1241: value.optIntEq(value2);
1242: } else {
1243: value.setIntValue(value.getIntValue() == value2
1244: .getIntValue());
1245: }
1246: } else if (t1 == ExprValue.DOUBLE) {
1247: if (USE_INLINED) {
1248: value.optDoubleEq(value2);
1249: } else {
1250: value.setIntValue(value.getDoubleValue() == value2
1251: .getDoubleValue());
1252: }
1253: } else {
1254: value.setIntValue(value.getStringValue().equals(
1255: value2.getStringValue()));
1256: }
1257: break;
1258: case NEQ:
1259: if (t1 == ExprValue.INT) {
1260: if (USE_INLINED) {
1261: value.optIntNotEq(value2);
1262: } else {
1263: value.setIntValue(value.getIntValue() != value2
1264: .getIntValue());
1265: }
1266: } else if (t1 == ExprValue.DOUBLE) {
1267: if (USE_INLINED) {
1268: value.optDoubleNotEq(value2);
1269: } else {
1270: value.setIntValue(value.getDoubleValue() != value2
1271: .getDoubleValue());
1272: }
1273: } else {
1274: value.setIntValue(!value.getStringValue().equals(
1275: value2.getStringValue()));
1276: }
1277: break;
1278: case BIT_AND:
1279: value.setIntValue(value.getIntValue()
1280: & value2.getIntValue());
1281: break;
1282: case BIT_XOR:
1283: value.setIntValue(value.getIntValue()
1284: ^ value2.getIntValue());
1285: break;
1286: case BIT_OR:
1287: value.setIntValue(value.getIntValue()
1288: | value2.getIntValue());
1289: break;
1290:
1291: // For AND and OR, we know that the first value has already
1292: // been converted to an integer. Thus we need only consider
1293: // the possibility of int vs. double for the second value.
1294:
1295: case AND:
1296: if (t2 == ExprValue.DOUBLE) {
1297: value2.setIntValue(value2.getDoubleValue() != 0.0);
1298: }
1299: value.setIntValue(((value.getIntValue() != 0) && (value2
1300: .getIntValue() != 0)));
1301: break;
1302: case OR:
1303: if (t2 == ExprValue.DOUBLE) {
1304: value2.setIntValue(value2.getDoubleValue() != 0.0);
1305: }
1306: value.setIntValue(((value.getIntValue() != 0) || (value2
1307: .getIntValue() != 0)));
1308: break;
1309:
1310: }
1311:
1312: return;
1313: }
1314:
1315: /**
1316: * GetLexeme -> ExprLex
1317: *
1318: * Lexical analyzer for expression parser: parses a single value,
1319: * operator, or other syntactic element from an expression string.
1320: *
1321: * Size effects: the "m_token" member variable is set to the value of
1322: * the current token.
1323: *
1324: * @param interp the context in which to evaluate the expression.
1325: * @exception TclException for malformed expressions.
1326: * @return the value of the expression.
1327: */
1328: private ExprValue ExprLex(Interp interp) throws TclException {
1329: char c, c2;
1330:
1331: while (m_ind < m_len
1332: && (((c = m_expr.charAt(m_ind)) == ' ') || Character
1333: .isWhitespace(c))) {
1334: m_ind++;
1335: }
1336: if (m_ind >= m_len) {
1337: m_token = END;
1338: return null;
1339: }
1340:
1341: // First try to parse the token as an integer or
1342: // floating-point number. Don't want to check for a number if
1343: // the first character is "+" or "-". If we do, we might
1344: // treat a binary operator as unary by
1345: // mistake, which will eventually cause a syntax error.
1346:
1347: c = m_expr.charAt(m_ind);
1348: if (m_ind < m_len - 1) {
1349: c2 = m_expr.charAt(m_ind + 1);
1350: } else {
1351: c2 = '\0';
1352: }
1353:
1354: if ((c != '+') && (c != '-')) {
1355: if (m_ind == m_len - 1) {
1356: // Integer shortcut when only 1 character left
1357: switch (c) {
1358: case '0':
1359: case '1':
1360: case '2':
1361: case '3':
1362: case '4':
1363: case '5':
1364: case '6':
1365: case '7':
1366: case '8':
1367: case '9':
1368: m_ind++;
1369: m_token = VALUE;
1370: ExprValue value = grabExprValue();
1371: value.setIntValue(c - '0', String.valueOf(c));
1372: return value;
1373: }
1374: }
1375: final boolean startsWithDigit = Character.isDigit(c);
1376: if (startsWithDigit
1377: && looksLikeInt(m_expr, m_len, m_ind, false)) {
1378: StrtoulResult res = interp.strtoulResult;
1379: Util.strtoul(m_expr, m_ind, 0, res);
1380:
1381: if (res.errno == 0) {
1382: String token = m_expr.substring(m_ind, res.index);
1383: m_ind = res.index;
1384: m_token = VALUE;
1385: ExprValue value = grabExprValue();
1386: value.setIntValue((int) res.value, token);
1387: return value;
1388: } else {
1389: if (res.errno == TCL.INTEGER_RANGE) {
1390: IntegerTooLarge(interp);
1391: }
1392: }
1393: } else if (startsWithDigit || (c == '.') || (c == 'n')
1394: || (c == 'N')) {
1395: StrtodResult res = interp.strtodResult;
1396: Util.strtod(m_expr, m_ind, -1, res);
1397: if (res.errno == 0) {
1398: String token = m_expr.substring(m_ind, res.index);
1399: m_ind = res.index;
1400: m_token = VALUE;
1401: ExprValue value = grabExprValue();
1402: value.setDoubleValue(res.value, token);
1403: return value;
1404: } else {
1405: if (res.errno == TCL.DOUBLE_RANGE) {
1406: if (res.value != 0) {
1407: DoubleTooLarge(interp);
1408: } else {
1409: DoubleTooSmall(interp);
1410: }
1411: }
1412: }
1413: }
1414: }
1415:
1416: ParseResult pres;
1417: ExprValue retval;
1418: m_ind += 1; // ind is advanced to point to the next token
1419:
1420: switch (c) {
1421: case '$':
1422: m_token = VALUE;
1423: pres = ParseAdaptor.parseVar(interp, m_expr, m_ind, m_len);
1424: m_ind = pres.nextIndex;
1425:
1426: if (interp.noEval != 0) {
1427: retval = grabExprValue();
1428: retval.setIntValue(0);
1429: } else {
1430: retval = grabExprValue();
1431: ExprParseObject(interp, pres.value, retval);
1432: }
1433: pres.release();
1434: return retval;
1435: case '[':
1436: m_token = VALUE;
1437: pres = ParseAdaptor.parseNestedCmd(interp, m_expr, m_ind,
1438: m_len);
1439: m_ind = pres.nextIndex;
1440:
1441: if (interp.noEval != 0) {
1442: retval = grabExprValue();
1443: retval.setIntValue(0);
1444: } else {
1445: retval = grabExprValue();
1446: ExprParseObject(interp, pres.value, retval);
1447: }
1448: pres.release();
1449: return retval;
1450: case '"':
1451: m_token = VALUE;
1452:
1453: //System.out.println("now to parse from ->" + m_expr + "<- at index "
1454: // + m_ind);
1455:
1456: pres = ParseAdaptor.parseQuotes(interp, m_expr, m_ind,
1457: m_len);
1458: m_ind = pres.nextIndex;
1459:
1460: // System.out.println("after parse next index is " + m_ind);
1461:
1462: if (interp.noEval != 0) {
1463: // System.out.println("returning noEval zero value");
1464: retval = grabExprValue();
1465: retval.setIntValue(0);
1466: } else {
1467: // System.out.println("returning value string ->" + pres.value.toString() + "<-" );
1468: retval = grabExprValue();
1469: ExprParseObject(interp, pres.value, retval);
1470: }
1471: pres.release();
1472: return retval;
1473: case '{':
1474: m_token = VALUE;
1475: pres = ParseAdaptor.parseBraces(interp, m_expr, m_ind,
1476: m_len);
1477: m_ind = pres.nextIndex;
1478: if (interp.noEval != 0) {
1479: retval = grabExprValue();
1480: retval.setIntValue(0);
1481: } else {
1482: retval = grabExprValue();
1483: ExprParseObject(interp, pres.value, retval);
1484: }
1485: pres.release();
1486: return retval;
1487: case '(':
1488: m_token = OPEN_PAREN;
1489: return null;
1490:
1491: case ')':
1492: m_token = CLOSE_PAREN;
1493: return null;
1494:
1495: case ',':
1496: m_token = COMMA;
1497: return null;
1498:
1499: case '*':
1500: m_token = MULT;
1501: return null;
1502:
1503: case '/':
1504: m_token = DIVIDE;
1505: return null;
1506:
1507: case '%':
1508: m_token = MOD;
1509: return null;
1510:
1511: case '+':
1512: m_token = PLUS;
1513: return null;
1514:
1515: case '-':
1516: m_token = MINUS;
1517: return null;
1518:
1519: case '?':
1520: m_token = QUESTY;
1521: return null;
1522:
1523: case ':':
1524: m_token = COLON;
1525: return null;
1526:
1527: case '<':
1528: switch (c2) {
1529: case '<':
1530: m_ind += 1;
1531: m_token = LEFT_SHIFT;
1532: break;
1533: case '=':
1534: m_ind += 1;
1535: m_token = LEQ;
1536: break;
1537: default:
1538: m_token = LESS;
1539: break;
1540: }
1541: return null;
1542:
1543: case '>':
1544: switch (c2) {
1545: case '>':
1546: m_ind += 1;
1547: m_token = RIGHT_SHIFT;
1548: break;
1549: case '=':
1550: m_ind += 1;
1551: m_token = GEQ;
1552: break;
1553: default:
1554: m_token = GREATER;
1555: break;
1556: }
1557: return null;
1558:
1559: case '=':
1560: if (c2 == '=') {
1561: m_ind += 1;
1562: m_token = EQUAL;
1563: } else {
1564: m_token = UNKNOWN;
1565: }
1566: return null;
1567:
1568: case '!':
1569: if (c2 == '=') {
1570: m_ind += 1;
1571: m_token = NEQ;
1572: } else {
1573: m_token = NOT;
1574: }
1575: return null;
1576:
1577: case '&':
1578: if (c2 == '&') {
1579: m_ind += 1;
1580: m_token = AND;
1581: } else {
1582: m_token = BIT_AND;
1583: }
1584: return null;
1585:
1586: case '^':
1587: m_token = BIT_XOR;
1588: return null;
1589:
1590: case '|':
1591: if (c2 == '|') {
1592: m_ind += 1;
1593: m_token = OR;
1594: } else {
1595: m_token = BIT_OR;
1596: }
1597: return null;
1598:
1599: case '~':
1600: m_token = BIT_NOT;
1601: return null;
1602:
1603: case 'e':
1604: case 'n':
1605: if (c == 'e' && c2 == 'q') {
1606: m_ind += 1;
1607: m_token = STREQ;
1608: return null;
1609: } else if (c == 'n' && c2 == 'e') {
1610: m_ind += 1;
1611: m_token = STRNEQ;
1612: return null;
1613: }
1614: // Fall through to default
1615:
1616: default:
1617: if (Character.isLetter(c)) {
1618: // Oops, re-adjust m_ind so that it points to the beginning
1619: // of the function name or literal.
1620:
1621: m_ind--;
1622:
1623: //
1624: // Check for boolean literals (true, false, yes, no, on, off)
1625: // This is kind of tricky since we don't want to pull a
1626: // partial boolean literal "f" off of the front of a function
1627: // invocation like expr {floor(1.1)}.
1628: //
1629:
1630: String substr = m_expr.substring(m_ind);
1631: boolean is_math_func = false;
1632:
1633: //System.out.println("default char '" + c + "' str is \"" +
1634: // m_expr + "\" and m_ind " + m_ind + " substring is \"" +
1635: // substr + "\"");
1636:
1637: final int max = substr.length();
1638: int i;
1639: for (i = 0; i < max; i++) {
1640: c = substr.charAt(i);
1641: if (!(Character.isLetterOrDigit(c) || c == '_')) {
1642: break;
1643: }
1644: }
1645: // Skip any whitespace characters too
1646: for (; i < max; i++) {
1647: c = substr.charAt(i);
1648: if (c == ' ' || Character.isWhitespace(c)) {
1649: continue;
1650: } else {
1651: break;
1652: }
1653: }
1654: if ((i < max) && (substr.charAt(i) == '(')) {
1655: //System.out.println("known to be a math func, char is '" +
1656: // substr.charAt(i) + "'");
1657: is_math_func = true;
1658: }
1659:
1660: if (!is_math_func) {
1661: String tok = getBooleanToken(substr);
1662: if (tok != null) {
1663: m_ind += tok.length();
1664: m_token = VALUE;
1665: ExprValue value = grabExprValue();
1666: value.setStringValue(tok);
1667: return value;
1668: }
1669: }
1670:
1671: return mathFunction(interp);
1672: }
1673: m_token = UNKNOWN;
1674: return null;
1675: }
1676: }
1677:
1678: /**
1679: * Parses a math function from an expression string, carry out the
1680: * function, and return the value computed.
1681: *
1682: * @param interp current interpreter.
1683: * @return the value computed by the math function.
1684: * @exception TclException if any error happens.
1685: */
1686: ExprValue mathFunction(Interp interp) throws TclException {
1687: int startIdx = m_ind;
1688: ExprValue value;
1689: String funcName;
1690: MathFunction mathFunc;
1691: ExprValue[] values = null;
1692: int numArgs;
1693:
1694: // Find the end of the math function's name and lookup the MathFunc
1695: // record for the function. Search until the char at m_ind is not
1696: // alphanumeric or '_'
1697:
1698: for (; m_ind < m_len; m_ind++) {
1699: if (!(Character.isLetterOrDigit(m_expr.charAt(m_ind)) || m_expr
1700: .charAt(m_ind) == '_')) {
1701: break;
1702: }
1703: }
1704:
1705: // Get the funcName BEFORE calling ExprLex, so the funcName
1706: // will not have trailing whitespace.
1707:
1708: funcName = m_expr.substring(startIdx, m_ind);
1709:
1710: // Parse errors are thrown BEFORE unknown function names
1711:
1712: ExprLex(interp);
1713: if (m_token != OPEN_PAREN) {
1714: SyntaxError(interp);
1715: }
1716:
1717: // Now test for unknown funcName. Doing the above statements
1718: // out of order will cause some tests to fail.
1719:
1720: mathFunc = (MathFunction) mathFuncTable.get(funcName);
1721: if (mathFunc == null) {
1722: throw new TclException(interp, "unknown math function \""
1723: + funcName + "\"");
1724: }
1725:
1726: // Scan off the arguments for the function, if there are any.
1727:
1728: numArgs = mathFunc.argTypes.length;
1729:
1730: if (numArgs == 0) {
1731: ExprLex(interp);
1732: if (m_token != CLOSE_PAREN) {
1733: SyntaxError(interp);
1734: }
1735: } else {
1736: values = new ExprValue[numArgs];
1737:
1738: for (int i = 0;; i++) {
1739: value = ExprGetValue(interp, -1);
1740:
1741: // Handle close paren with no value
1742: // % expr {srand()}
1743:
1744: if ((value == null) && (m_token == CLOSE_PAREN)) {
1745: if (i == numArgs)
1746: break;
1747: else
1748: throw new TclException(interp,
1749: "too few arguments for math function");
1750: }
1751:
1752: values[i] = value;
1753:
1754: // Check for a comma separator between arguments or a
1755: // close-paren to end the argument list.
1756:
1757: if (i == (numArgs - 1)) {
1758: if (m_token == CLOSE_PAREN) {
1759: break;
1760: }
1761: if (m_token == COMMA) {
1762: throw new TclException(interp,
1763: "too many arguments for math function");
1764: } else {
1765: SyntaxError(interp);
1766: }
1767: }
1768: if (m_token != COMMA) {
1769: if (m_token == CLOSE_PAREN) {
1770: throw new TclException(interp,
1771: "too few arguments for math function");
1772: } else {
1773: SyntaxError(interp);
1774: }
1775: }
1776: }
1777: }
1778:
1779: m_token = VALUE;
1780: if (interp.noEval != 0) {
1781: ExprValue rvalue = grabExprValue();
1782: rvalue.setIntValue(0);
1783: return rvalue;
1784: } else {
1785: // Invoke the function and copy its result back into rvalue.
1786: ExprValue rvalue = grabExprValue();
1787: evalMathFunction(interp, funcName, mathFunc, values, true,
1788: rvalue);
1789: return rvalue;
1790: }
1791: }
1792:
1793: /**
1794: * This procedure will lookup and invoke a math function
1795: * given the name of the function and an array of ExprValue
1796: * arguments. Each ExprValue is released before the function
1797: * exits. This method is intended to be used by other modules
1798: * that may need to invoke a math function at runtime. It is
1799: * assumed that the caller has checked the number of arguments,
1800: * the type of the arguments will be adjusted before invocation
1801: * if needed.
1802: *
1803: * The values argument can be null when there are no args to pass.
1804: *
1805: * The releaseValues argument should be true when the ExprValue
1806: * objecys in the array should be released.
1807: */
1808:
1809: void evalMathFunction(Interp interp, String funcName,
1810: ExprValue[] values, boolean releaseValues, ExprValue result)
1811: throws TclException {
1812: MathFunction mathFunc = (MathFunction) mathFuncTable
1813: .get(funcName);
1814: if (mathFunc == null) {
1815: throw new TclException(interp, "unknown math function \""
1816: + funcName + "\"");
1817: }
1818: evalMathFunction(interp, funcName, mathFunc, values,
1819: releaseValues, result);
1820: }
1821:
1822: /**
1823: * This procedure implements a math function invocation.
1824: * See the comments for the function above, note that
1825: * this method is used when the math function pointer
1826: * has already been resolved.
1827: *
1828: * The values argument can be null when there are no args to pass.
1829: *
1830: * The releaseValues argument should be true when the ExprValue
1831: * objecys in the array should be released.
1832: */
1833:
1834: void evalMathFunction(Interp interp, String funcName,
1835: MathFunction mathFunc, ExprValue[] values,
1836: boolean releaseValues, ExprValue result)
1837: throws TclException {
1838: if (mathFunc.argTypes == null) {
1839: throw new TclRuntimeError("math function \"" + funcName
1840: + "\" has null argTypes");
1841: }
1842:
1843: // Ensure that arguments match the int/double
1844: // expectations of the math function.
1845:
1846: int numArgs = mathFunc.argTypes.length;
1847: int expectedArgs = 0;
1848: if (values != null) {
1849: expectedArgs = values.length;
1850: }
1851:
1852: if (numArgs != expectedArgs) {
1853: if ((expectedArgs > 0) && (expectedArgs < numArgs)) {
1854: throw new TclException(interp,
1855: "too few arguments for math function");
1856: } else {
1857: throw new TclException(interp,
1858: "too many arguments for math function");
1859: }
1860: }
1861:
1862: if (values != null) {
1863: for (int i = 0; i < values.length; i++) {
1864: ExprValue value = values[i];
1865: if (value.isStringType()) {
1866: throw new TclException(interp,
1867: "argument to math function didn't have numeric value");
1868: } else if (value.isIntType()) {
1869: if (mathFunc.argTypes[i] == MathFunction.DOUBLE) {
1870: value.setDoubleValue((double) value
1871: .getIntValue());
1872: }
1873: } else {
1874: if (mathFunc.argTypes[i] == MathFunction.INT) {
1875: value.setIntValue((int) value.getDoubleValue());
1876: }
1877: }
1878: }
1879: }
1880:
1881: if (mathFunc instanceof NoArgMathFunction) {
1882: ((NoArgMathFunction) mathFunc).apply(interp, result);
1883: } else {
1884: mathFunc.apply(interp, values);
1885:
1886: // Copy result from first argument to passed in ref.
1887: // It is possible that the caller passed null as
1888: // the result, leave the value in values[0] in that
1889: // case. This optimization is only valid when
1890: // releaseValues is false.
1891:
1892: if (result != null) {
1893: result.setValue(values[0]);
1894: }
1895: }
1896:
1897: if (releaseValues && values != null) {
1898: // Release ExprValue elements in values array
1899: for (int i = 0; i < values.length; i++) {
1900: releaseExprValue(values[i]);
1901: }
1902: }
1903: }
1904:
1905: /**
1906: * This procedure will register a math function by
1907: * adding it to the table of available math functions.
1908: * This methods is used when regression testing the
1909: * expr command.
1910: */
1911:
1912: void registerMathFunction(String name, MathFunction mathFunc) {
1913: mathFuncTable.put(name, mathFunc);
1914: }
1915:
1916: /**
1917: * This procedure decides whether the leading characters of a
1918: * string look like an integer or something else (such as a
1919: * floating-point number or string). If the whole flag is
1920: * true then the entire string must look like an integer.
1921: * @return a boolean value indicating if the string looks like an integer.
1922: */
1923:
1924: static boolean looksLikeInt(String s, int len, int i, boolean whole) {
1925: char c = '\0';
1926: while (i < len
1927: && (((c = s.charAt(i)) == ' ') || Character
1928: .isWhitespace(c))) {
1929: i++;
1930: }
1931: if (i >= len) {
1932: return false;
1933: }
1934: if ((c == '+') || (c == '-')) {
1935: i++;
1936: if (i >= len) {
1937: return false;
1938: }
1939: c = s.charAt(i);
1940: }
1941: if ((c >= '0' && c <= '9') || Character.isDigit(c)) {
1942: // This is a digit
1943: i++;
1944: } else {
1945: return false;
1946: }
1947: for (; i < len; i++) {
1948: c = s.charAt(i);
1949: if ((c >= '0' && c <= '9') || Character.isDigit(c)) {
1950: // This is a digit, keep looking at rest of string.
1951: //System.out.println("'" + c + "' is a digit");
1952: } else {
1953: break;
1954: }
1955: }
1956: if (i >= len) {
1957: return true;
1958: }
1959:
1960: if (!whole && (c != '.') && (c != 'E') && (c != 'e')) {
1961: return true;
1962: }
1963: if (c == 'e' || c == 'E') {
1964: // Could be a double like 1e6 or 1e-1 but
1965: // it could also be 1eq2. If the next
1966: // character is not a digit or a + or -,
1967: // then this must not be a double. If the
1968: // whole string must look like an integer
1969: // then we know this is not an integer.
1970: if (whole) {
1971: return false;
1972: } else if (i + 1 >= len) {
1973: return true;
1974: }
1975: c = s.charAt(i + 1);
1976: if (c != '+' && c != '-' && !Character.isDigit(c)) {
1977: // Does not look like "1e1", "1e+1", or "1e-1"
1978: // so strtoul would parse the text leading up
1979: // to the 'e' as an integer.
1980: return true;
1981: }
1982: }
1983: if (whole) {
1984: while (i < len
1985: && (((c = s.charAt(i)) == ' ') || Character
1986: .isWhitespace(c))) {
1987: i++;
1988: }
1989: if (i >= len) {
1990: return true;
1991: }
1992: }
1993:
1994: return false;
1995: }
1996:
1997: static void checkIntegerRange(Interp interp, double d)
1998: throws TclException {
1999: if (d < 0) {
2000: if (d < ((double) TCL.INT_MIN)) {
2001: Expression.IntegerTooLarge(interp);
2002: }
2003: } else {
2004: if (d > ((double) TCL.INT_MAX)) {
2005: Expression.IntegerTooLarge(interp);
2006: }
2007: }
2008: }
2009:
2010: static void checkDoubleRange(Interp interp, double d)
2011: throws TclException {
2012: if ((d == Double.NaN) || (d == Double.NEGATIVE_INFINITY)
2013: || (d == Double.POSITIVE_INFINITY)) {
2014: Expression.DoubleTooLarge(interp);
2015: }
2016: }
2017:
2018: // If the string starts with a boolean token, then
2019: // return the portion of the string that matched
2020: // a boolean token. Otherwise, return null.
2021:
2022: static String getBooleanToken(String tok) {
2023: int length = tok.length();
2024: if (length == 0) {
2025: return null;
2026: }
2027: char c = tok.charAt(0);
2028: switch (c) {
2029: case 'f':
2030: if (tok.startsWith("false")) {
2031: return "false";
2032: }
2033: if (tok.startsWith("fals")) {
2034: return "fals";
2035: }
2036: if (tok.startsWith("fal")) {
2037: return "fal";
2038: }
2039: if (tok.startsWith("fa")) {
2040: return "fa";
2041: }
2042: if (tok.startsWith("f")) {
2043: return "f";
2044: }
2045: case 'n':
2046: if (tok.startsWith("no")) {
2047: return "no";
2048: }
2049: if (tok.startsWith("n")) {
2050: return "n";
2051: }
2052: case 'o':
2053: if (tok.startsWith("off")) {
2054: return "off";
2055: }
2056: if (tok.startsWith("of")) {
2057: return "of";
2058: }
2059: if (tok.startsWith("on")) {
2060: return "on";
2061: }
2062: case 't':
2063: if (tok.startsWith("true")) {
2064: return "true";
2065: }
2066: if (tok.startsWith("tru")) {
2067: return "tru";
2068: }
2069: if (tok.startsWith("tr")) {
2070: return "tr";
2071: }
2072: if (tok.startsWith("t")) {
2073: return "t";
2074: }
2075: case 'y':
2076: if (tok.startsWith("yes")) {
2077: return "yes";
2078: }
2079: if (tok.startsWith("ye")) {
2080: return "ye";
2081: }
2082: if (tok.startsWith("y")) {
2083: return "y";
2084: }
2085: }
2086: return null;
2087: }
2088:
2089: // Get an ExprValue object out of the cache
2090: // of ExprValues. These values will be released
2091: // later on by a call to releaseExprValue. Don't
2092: // bother with release on syntax or other errors
2093: // since the exprValueCache will refill itself.
2094:
2095: final ExprValue grabExprValue() {
2096: if (cachedExprIndex == cachedExprLength) {
2097: // Allocate new ExprValue if cache is empty
2098: return new ExprValue(0, null);
2099: } else {
2100: return cachedExprValue[cachedExprIndex++];
2101: }
2102: }
2103:
2104: final void releaseExprValue(ExprValue val) {
2105: // Debug check for duplicate value already in cachedExprValue
2106: if (false) {
2107: if (cachedExprIndex < 0) {
2108: throw new TclRuntimeError("cachedExprIndex is "
2109: + cachedExprIndex);
2110: }
2111: if (val == null) {
2112: throw new TclRuntimeError("ExprValue is null");
2113: }
2114: for (int i = cachedExprIndex; i < cachedExprLength; i++) {
2115: if (cachedExprValue[i] != null
2116: && val == cachedExprValue[i]) {
2117: throw new TclRuntimeError(
2118: "released ExprValue is already in cache slot "
2119: + i);
2120: }
2121: }
2122: }
2123:
2124: if (cachedExprIndex > 0) {
2125: // Cache is not full, return value to cache
2126: cachedExprValue[--cachedExprIndex] = val;
2127: }
2128: }
2129: }
2130:
2131: abstract class MathFunction {
2132: static final int INT = 0;
2133: static final int DOUBLE = 1;
2134: static final int EITHER = 2;
2135:
2136: int[] argTypes;
2137:
2138: abstract void apply(Interp interp, ExprValue[] values)
2139: throws TclException;
2140: }
2141:
2142: abstract class UnaryMathFunction extends MathFunction {
2143: UnaryMathFunction() {
2144: argTypes = new int[1];
2145: argTypes[0] = DOUBLE;
2146: }
2147: }
2148:
2149: abstract class BinaryMathFunction extends MathFunction {
2150: BinaryMathFunction() {
2151: argTypes = new int[2];
2152: argTypes[0] = DOUBLE;
2153: argTypes[1] = DOUBLE;
2154: }
2155: }
2156:
2157: abstract class NoArgMathFunction extends MathFunction {
2158: NoArgMathFunction() {
2159: argTypes = new int[0];
2160: }
2161:
2162: // A NoArgMathFunction is a special case for the
2163: // rand() math function. There are no arguments,
2164: // so pass just a result ExprValue.
2165:
2166: abstract void apply(Interp interp, ExprValue value)
2167: throws TclException;
2168:
2169: // Raise a runtime error if the wrong apply is invoked.
2170:
2171: void apply(Interp interp, ExprValue[] values) throws TclException {
2172: throw new TclRuntimeError(
2173: "NoArgMathFunction must be invoked via apply(interp, ExprValue)");
2174: }
2175: }
2176:
2177: class Atan2Function extends BinaryMathFunction {
2178: void apply(Interp interp, ExprValue[] values) throws TclException {
2179: double y = values[0].getDoubleValue();
2180: double x = values[1].getDoubleValue();
2181: if ((y == 0.0) && (x == 0.0)) {
2182: Expression.DomainError(interp);
2183: }
2184: values[0].setDoubleValue(Math.atan2(y, x));
2185: }
2186: }
2187:
2188: class AbsFunction extends MathFunction {
2189: AbsFunction() {
2190: argTypes = new int[1];
2191: argTypes[0] = EITHER;
2192: }
2193:
2194: void apply(Interp interp, ExprValue[] values) throws TclException {
2195: ExprValue value = values[0];
2196: if (value.isDoubleType()) {
2197: double d = value.getDoubleValue();
2198: if (d > 0) {
2199: value.setDoubleValue(d);
2200: } else {
2201: value.setDoubleValue(-d);
2202: }
2203: } else {
2204: int i = value.getIntValue();
2205: if (i > 0) {
2206: value.setIntValue(i);
2207: } else {
2208: value.setIntValue(-i);
2209: }
2210: }
2211: }
2212: }
2213:
2214: class DoubleFunction extends MathFunction {
2215: DoubleFunction() {
2216: argTypes = new int[1];
2217: argTypes[0] = EITHER;
2218: }
2219:
2220: void apply(Interp interp, ExprValue[] values) throws TclException {
2221: ExprValue value = values[0];
2222: if (value.isIntType()) {
2223: value.setDoubleValue((double) value.getIntValue());
2224: }
2225: }
2226: }
2227:
2228: class IntFunction extends MathFunction {
2229: IntFunction() {
2230: argTypes = new int[1];
2231: argTypes[0] = EITHER;
2232: }
2233:
2234: void apply(Interp interp, ExprValue[] values) throws TclException {
2235: ExprValue value = values[0];
2236: if (!value.isIntType()) {
2237: double d = value.getDoubleValue();
2238: Expression.checkIntegerRange(interp, d);
2239: value.setIntValue((int) d);
2240: }
2241: }
2242: }
2243:
2244: class RoundFunction extends MathFunction {
2245: RoundFunction() {
2246: argTypes = new int[1];
2247: argTypes[0] = EITHER;
2248: }
2249:
2250: void apply(Interp interp, ExprValue[] values) throws TclException {
2251: ExprValue value = values[0];
2252: if (value.isDoubleType()) {
2253: double d = value.getDoubleValue();
2254: double i = (d < 0.0 ? Math.ceil(d) : Math.floor(d));
2255: double f = d - i;
2256: if (d < 0.0) {
2257: if (f <= -0.5) {
2258: i += -1.0;
2259: }
2260: Expression.checkIntegerRange(interp, i);
2261: value.setIntValue((int) i);
2262: } else {
2263: if (f >= 0.5) {
2264: i += 1.0;
2265: }
2266: Expression.checkIntegerRange(interp, i);
2267: value.setIntValue((int) i);
2268: }
2269: }
2270: }
2271: }
2272:
2273: class PowFunction extends BinaryMathFunction {
2274: void apply(Interp interp, ExprValue[] values) throws TclException {
2275: double x = values[0].getDoubleValue();
2276: double y = values[1].getDoubleValue();
2277: if (x < 0.0) {
2278: // If x is negative then y must be an integer
2279:
2280: if (((double) ((int) y)) != y) {
2281: Expression.DomainError(interp);
2282: }
2283: }
2284:
2285: double d = Math.pow(x, y);
2286: Expression.checkDoubleRange(interp, d);
2287: values[0].setDoubleValue(d);
2288: }
2289: }
2290:
2291: /*
2292: * The following section is generated by this script.
2293: *
2294: set missing {fmod}
2295: set byhand {atan2 pow}
2296:
2297:
2298: foreach func {Acos Asin Atan Ceil Cos Exp Floor Log Sin
2299: Sqrt Tan} {
2300: puts "
2301: class $func\Function extends UnaryMathFunction {
2302: ExprValue apply(Interp interp, TclObject argv\[\])
2303: throws TclException {
2304: return new ExprValue(Math.[string tolower $func](TclDouble.get(interp, argv\[0\])));
2305: }
2306: }
2307: "
2308: }
2309:
2310: */
2311:
2312: class AcosFunction extends UnaryMathFunction {
2313: void apply(Interp interp, ExprValue[] values) throws TclException {
2314: ExprValue value = values[0];
2315: double d = value.getDoubleValue();
2316: if ((d < -1.0) || (d > 1.0)) {
2317: Expression.DomainError(interp);
2318: }
2319: value.setDoubleValue(Math.acos(d));
2320: }
2321: }
2322:
2323: class AsinFunction extends UnaryMathFunction {
2324: void apply(Interp interp, ExprValue[] values) throws TclException {
2325: ExprValue value = values[0];
2326: double d = value.getDoubleValue();
2327: if ((d < -1.0) || (d > 1.0)) {
2328: Expression.DomainError(interp);
2329: }
2330: value.setDoubleValue(Math.asin(d));
2331: }
2332: }
2333:
2334: class AtanFunction extends UnaryMathFunction {
2335: void apply(Interp interp, ExprValue[] values) throws TclException {
2336: ExprValue value = values[0];
2337: double d = value.getDoubleValue();
2338: if ((d < -Math.PI / 2) || (d > Math.PI / 2)) {
2339: Expression.DomainError(interp);
2340: }
2341: value.setDoubleValue(Math.atan(d));
2342: }
2343: }
2344:
2345: class CeilFunction extends UnaryMathFunction {
2346: void apply(Interp interp, ExprValue[] values) throws TclException {
2347: ExprValue value = values[0];
2348: value.setDoubleValue(Math.ceil(value.getDoubleValue()));
2349: }
2350: }
2351:
2352: class CosFunction extends UnaryMathFunction {
2353: void apply(Interp interp, ExprValue[] values) throws TclException {
2354: ExprValue value = values[0];
2355: value.setDoubleValue(Math.cos(value.getDoubleValue()));
2356: }
2357: }
2358:
2359: class CoshFunction extends UnaryMathFunction {
2360: void apply(Interp interp, ExprValue[] values) throws TclException {
2361: ExprValue value = values[0];
2362: double x = value.getDoubleValue();
2363: double d1 = Math.pow(Math.E, x);
2364: double d2 = Math.pow(Math.E, -x);
2365:
2366: Expression.checkDoubleRange(interp, d1);
2367: Expression.checkDoubleRange(interp, d2);
2368: value.setDoubleValue((d1 + d2) / 2);
2369: }
2370: }
2371:
2372: class ExpFunction extends UnaryMathFunction {
2373: void apply(Interp interp, ExprValue[] values) throws TclException {
2374: ExprValue value = values[0];
2375: double d = Math.exp(value.getDoubleValue());
2376: if ((d == Double.NaN) || (d == Double.NEGATIVE_INFINITY)
2377: || (d == Double.POSITIVE_INFINITY)) {
2378: Expression.DoubleTooLarge(interp);
2379: }
2380: value.setDoubleValue(d);
2381: }
2382: }
2383:
2384: class FloorFunction extends UnaryMathFunction {
2385: void apply(Interp interp, ExprValue[] values) throws TclException {
2386: ExprValue value = values[0];
2387: value.setDoubleValue(Math.floor(value.getDoubleValue()));
2388: }
2389: }
2390:
2391: class FmodFunction extends BinaryMathFunction {
2392: void apply(Interp interp, ExprValue[] values) throws TclException {
2393: double d1 = values[0].getDoubleValue();
2394: double d2 = values[1].getDoubleValue();
2395: if (d2 == 0.0) {
2396: Expression.DomainError(interp);
2397: }
2398: values[0].setDoubleValue(Math.IEEEremainder(d1, d2));
2399: }
2400: }
2401:
2402: class HypotFunction extends BinaryMathFunction {
2403: void apply(Interp interp, ExprValue[] values) throws TclException {
2404: double x = values[0].getDoubleValue();
2405: double y = values[1].getDoubleValue();
2406: values[0].setDoubleValue(Math.sqrt(((x * x) + (y * y))));
2407: }
2408: }
2409:
2410: class LogFunction extends UnaryMathFunction {
2411: void apply(Interp interp, ExprValue[] values) throws TclException {
2412: ExprValue value = values[0];
2413: double d = value.getDoubleValue();
2414: if (d < 0.0) {
2415: Expression.DomainError(interp);
2416: }
2417: value.setDoubleValue(Math.log(d));
2418: }
2419: }
2420:
2421: class Log10Function extends UnaryMathFunction {
2422: private static final double log10 = Math.log(10);
2423:
2424: void apply(Interp interp, ExprValue[] values) throws TclException {
2425: ExprValue value = values[0];
2426: double d = value.getDoubleValue();
2427: if (d < 0.0) {
2428: Expression.DomainError(interp);
2429: }
2430: value.setDoubleValue(Math.log(d) / log10);
2431: }
2432: }
2433:
2434: class SinFunction extends UnaryMathFunction {
2435: void apply(Interp interp, ExprValue[] values) throws TclException {
2436: ExprValue value = values[0];
2437: double d = value.getDoubleValue();
2438: value.setDoubleValue(Math.sin(d));
2439: }
2440: }
2441:
2442: class SinhFunction extends UnaryMathFunction {
2443: void apply(Interp interp, ExprValue[] values) throws TclException {
2444: ExprValue value = values[0];
2445: double x = value.getDoubleValue();
2446:
2447: double d1 = Math.pow(Math.E, x);
2448: double d2 = Math.pow(Math.E, -x);
2449:
2450: Expression.checkDoubleRange(interp, d1);
2451: Expression.checkDoubleRange(interp, d2);
2452:
2453: value.setDoubleValue((d1 - d2) / 2);
2454: }
2455: }
2456:
2457: class SqrtFunction extends UnaryMathFunction {
2458: void apply(Interp interp, ExprValue[] values) throws TclException {
2459: ExprValue value = values[0];
2460: double x = value.getDoubleValue();
2461: if (x < 0.0) {
2462: Expression.DomainError(interp);
2463: }
2464: value.setDoubleValue(Math.sqrt(x));
2465: }
2466: }
2467:
2468: class TanFunction extends UnaryMathFunction {
2469: void apply(Interp interp, ExprValue[] values) throws TclException {
2470: ExprValue value = values[0];
2471: value.setDoubleValue(Math.tan(value.getDoubleValue()));
2472: }
2473: }
2474:
2475: class TanhFunction extends UnaryMathFunction {
2476: void apply(Interp interp, ExprValue[] values) throws TclException {
2477: ExprValue value = values[0];
2478: double x = value.getDoubleValue();
2479: if (x == 0.0) {
2480: return;
2481: }
2482:
2483: double d1 = Math.pow(Math.E, x);
2484: double d2 = Math.pow(Math.E, -x);
2485:
2486: Expression.checkDoubleRange(interp, d1);
2487: Expression.checkDoubleRange(interp, d2);
2488:
2489: value.setDoubleValue((d1 - d2) / (d1 + d2));
2490: }
2491: }
2492:
2493: class RandFunction extends NoArgMathFunction {
2494:
2495: // Generate the random number using the linear congruential
2496: // generator defined by the following recurrence:
2497: // seed = ( IA * seed ) mod IM
2498: // where IA is 16807 and IM is (2^31) - 1. In order to avoid
2499: // potential problems with integer overflow, the code uses
2500: // additional constants IQ and IR such that
2501: // IM = IA*IQ + IR
2502: // For details on how this algorithm works, refer to the following
2503: // papers:
2504: //
2505: // S.K. Park & K.W. Miller, "Random number generators: good ones
2506: // are hard to find," Comm ACM 31(10):1192-1201, Oct 1988
2507: //
2508: // W.H. Press & S.A. Teukolsky, "Portable random number
2509: // generators," Computers in Physics 6(5):522-524, Sep/Oct 1992.
2510:
2511: private static final int randIA = 16807;
2512: private static final int randIM = 2147483647;
2513: private static final int randIQ = 127773;
2514: private static final int randIR = 2836;
2515: private static final Date date = new Date();
2516:
2517: /**
2518: * Srand calls the main algorithm for rand after it sets the seed.
2519: * To facilitate this call, the method is static and can be used
2520: * w/o creating a new object. But we also need to maintain the
2521: * inheritance hierarchy, thus the dynamic apply() calls the static
2522: * statApply().
2523: */
2524:
2525: void apply(Interp interp, ExprValue value) throws TclException {
2526: statApply(interp, value);
2527: }
2528:
2529: static void statApply(Interp interp, ExprValue value)
2530: throws TclException {
2531:
2532: int tmp;
2533:
2534: if (!(interp.randSeedInit)) {
2535: interp.randSeedInit = true;
2536: interp.randSeed = (int) date.getTime();
2537: }
2538:
2539: if (interp.randSeed == 0) {
2540: // Don't allow a 0 seed, since it breaks the generator. Shift
2541: // it to some other value.
2542:
2543: interp.randSeed = 123459876;
2544: }
2545:
2546: tmp = (int) (interp.randSeed / randIQ);
2547: interp.randSeed = ((randIA * (interp.randSeed - tmp * randIQ)) - randIR
2548: * tmp);
2549:
2550: if (interp.randSeed < 0) {
2551: interp.randSeed += randIM;
2552: }
2553:
2554: value.setDoubleValue(interp.randSeed * (1.0 / randIM));
2555: }
2556: }
2557:
2558: class SrandFunction extends UnaryMathFunction {
2559:
2560: void apply(Interp interp, ExprValue[] values) throws TclException {
2561: ExprValue value = values[0];
2562:
2563: // Reset the seed.
2564:
2565: interp.randSeedInit = true;
2566: interp.randSeed = (long) value.getDoubleValue();
2567:
2568: // To avoid duplicating the random number generation code we simply
2569: // call the static random number generator in the RandFunction
2570: // class.
2571:
2572: RandFunction.statApply(interp, value);
2573: }
2574: }
|