Source Code Cross Referenced for Expression.java in  » Scripting » jacl » tcl » lang » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Java Source Code / Java Documentation
1. 6.0 JDK Core
2. 6.0 JDK Modules
3. 6.0 JDK Modules com.sun
4. 6.0 JDK Modules com.sun.java
5. 6.0 JDK Modules sun
6. 6.0 JDK Platform
7. Ajax
8. Apache Harmony Java SE
9. Aspect oriented
10. Authentication Authorization
11. Blogger System
12. Build
13. Byte Code
14. Cache
15. Chart
16. Chat
17. Code Analyzer
18. Collaboration
19. Content Management System
20. Database Client
21. Database DBMS
22. Database JDBC Connection Pool
23. Database ORM
24. Development
25. EJB Server geronimo
26. EJB Server GlassFish
27. EJB Server JBoss 4.2.1
28. EJB Server resin 3.1.5
29. ERP CRM Financial
30. ESB
31. Forum
32. GIS
33. Graphic Library
34. Groupware
35. HTML Parser
36. IDE
37. IDE Eclipse
38. IDE Netbeans
39. Installer
40. Internationalization Localization
41. Inversion of Control
42. Issue Tracking
43. J2EE
44. JBoss
45. JMS
46. JMX
47. Library
48. Mail Clients
49. Net
50. Parser
51. PDF
52. Portal
53. Profiler
54. Project Management
55. Report
56. RSS RDF
57. Rule Engine
58. Science
59. Scripting
60. Search Engine
61. Security
62. Sevlet Container
63. Source Control
64. Swing Library
65. Template Engine
66. Test Coverage
67. Testing
68. UML
69. Web Crawler
70. Web Framework
71. Web Mail
72. Web Server
73. Web Services
74. Web Services apache cxf 2.0.1
75. Web Services AXIS2
76. Wiki Engine
77. Workflow Engines
78. XML
79. XML UI
Java
Java Tutorial
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
Photoshop Tutorials
Maya Tutorials
Flash Tutorials
3ds-Max Tutorials
Illustrator Tutorials
GIMP Tutorials
C# / C Sharp
C# / CSharp Tutorial
C# / CSharp Open Source
ASP.Net
ASP.NET Tutorial
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
Ruby
PHP
Python
Python Tutorial
Python Open Source
SQL Server / T-SQL
SQL Server / T-SQL Tutorial
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
XML
XML Tutorial
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
Java Source Code / Java Documentation » Scripting » jacl » tcl.lang 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


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:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.