0001: /*
0002: * Parser.java --
0003: *
0004: * This class contains methods that parse Tcl scripts. They
0005: * do so in a general-purpose fashion that can be used for many
0006: * different purposes, including compilation, direct execution,
0007: * code analysis, etc. This class also includes a few additional
0008: * procedures such as evalObjv, eval, and eval2, which allow
0009: * scripts to be evaluated directly, without compiling.
0010: *
0011: * Copyright (c) 1998 by Sun Microsystems, Inc.
0012: *
0013: * See the file "license.terms" for information on usage and redistribution
0014: * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
0015: *
0016: * RCS: @(#) $Id: Parser.java,v 1.30 2006/08/03 22:33:12 mdejong Exp $
0017: */
0018:
0019: package tcl.lang;
0020:
0021: import java.util.Arrays;
0022:
0023: class Parser {
0024:
0025: /*
0026: *----------------------------------------------------------------------
0027: *
0028: * parseCommand --
0029: *
0030: * Given a script, this procedure parses the first Tcl command
0031: * in the string and returns information about the structure of
0032: * the command.
0033: *
0034: * Results:
0035: * The return value is TclParse object that contains information
0036: * about the parsed command. If the command was parsed
0037: * successfully, the TclParse object's result variable is set to
0038: * TCL_OK and TCL_ERROR otherwise. If an error occurs and interp
0039: * isn't null then an error message is left in its result.
0040: *
0041: * Side effects:
0042: * None.
0043: *
0044: *----------------------------------------------------------------------
0045: */
0046:
0047: static TclParse parseCommand(Interp interp, // Interpreter to use for error reporting;
0048: // if null, then no error message is
0049: // provided.
0050:
0051: char[] script_array, // References the script and contains an
0052: int script_index, // index to the next character to parse.
0053:
0054: int numChars, // Total number of characters in string. If < 0,
0055: // the script consists of all characters up to the
0056: // first null character.
0057: String fileName, // Name of file from which script was
0058: // loaded. Null means there is no
0059: // known file for the script. Used for
0060: // error messages.
0061: int lineNum, // Line number of first byte in string.
0062: // Used for error messages.
0063: boolean nested) // True means that this is a nested command:
0064: // close bracket should be considered
0065: // a command terminator. If false, then close
0066: // bracket has no special meaning.
0067: {
0068:
0069: char cur; //the char we are currently parsing
0070: int type; // Result returned by charType(src.charAt()).
0071: TclToken token; // Pointer to token being filled in.
0072: int wordIndex; // Index of word token for current word.
0073: int level; // Nesting level of curly braces: gives
0074: // number of right braces we must find to
0075: // end word.
0076: TclParse parse; // Return value to fill in with information
0077: // about the parsed command.
0078: int terminators; // charType() bits that indicate the end
0079: // of a command.
0080: BackSlashResult bs; // Result of a call to backslash(...).
0081: int endIndex; // Index that points to the character after
0082: // the last character to be parsed.
0083: char savedChar; // To terminate the parsing correctly, the
0084: // character at endIndex is set to \0. This
0085: // stores the value to return when finished.
0086:
0087: final boolean debug = false;
0088:
0089: if (debug) {
0090: System.out.println();
0091: System.out.println("Entered Parser.parseCommand()");
0092: System.out.print("now to parse the string \"");
0093: for (int k = script_index; k < script_array.length; k++) {
0094: System.out.print(script_array[k]);
0095: }
0096: System.out.println("\"");
0097: }
0098:
0099: int saved_script_index = script_index; //save the original index
0100:
0101: int script_length = script_array.length - 1;
0102:
0103: int scanned;
0104: ParseWhitespaceResult pwsr;
0105:
0106: if (numChars < 0) {
0107: numChars = script_length - script_index;
0108: }
0109: endIndex = script_index + numChars;
0110: if (endIndex > script_length) {
0111: endIndex = script_length;
0112: }
0113:
0114: savedChar = script_array[endIndex];
0115: script_array[endIndex] = '\0';
0116:
0117: parse = new TclParse(interp, script_array, endIndex, fileName,
0118: lineNum);
0119:
0120: if (nested) {
0121: terminators = TYPE_COMMAND_END | TYPE_CLOSE_BRACK;
0122: } else {
0123: terminators = TYPE_COMMAND_END;
0124: }
0125:
0126: // Parse any leading space and comments before the first word of the
0127: // command.
0128:
0129: // FIXME: Use TclParseWhiteSpace to scan whitespace here!
0130:
0131: try {
0132:
0133: while (true) {
0134:
0135: cur = script_array[script_index];
0136:
0137: while (((cur <= TYPE_MAX) && (typeTable[cur] == TYPE_SPACE))
0138: || (cur == '\n')) {
0139: cur = script_array[++script_index];
0140: }
0141:
0142: if ((cur == '\\')
0143: && (script_array[script_index + 1] == '\n')) {
0144:
0145: // Skip backslash-newline sequence: it should be treated
0146: // just like white space.
0147:
0148: if ((script_index + 2) == parse.endIndex) {
0149: parse.incomplete = true;
0150: }
0151:
0152: //this will add 2 to the offset and return to the top
0153: //of the while(true) loop which will get the next cur
0154:
0155: script_index += 2;
0156: continue;
0157: }
0158:
0159: // If we have found the start of a command goto the word parsing loop
0160: if (cur != '#') {
0161: break;
0162: }
0163:
0164: // Record the index where the comment starts
0165: if (parse.commentStart < 0) {
0166: parse.commentStart = script_index;
0167: }
0168:
0169: while (true) {
0170: cur = script_array[script_index];
0171: if (script_index == parse.endIndex) {
0172: if (nested) {
0173: parse.incomplete = true;
0174: }
0175: parse.commentSize = script_index
0176: - parse.commentStart;
0177: break;
0178: } else if (cur == '\\') {
0179: if ((script_array[script_index + 1] == '\n')
0180: && ((script_index + 2) == parse.endIndex)) {
0181: parse.incomplete = true;
0182: }
0183: bs = backslash(script_array, script_index);
0184: script_index = bs.nextIndex;
0185: } else if (cur == '\n') {
0186: script_index++;
0187: parse.commentSize = script_index
0188: - parse.commentStart;
0189: break;
0190: } else {
0191: script_index++;
0192: }
0193: }
0194: }
0195:
0196: // The following loop parses the words of the command, one word
0197: // in each iteration through the loop.
0198:
0199: parse.commandStart = script_index;
0200:
0201: while (true) {
0202:
0203: // Create the token for the word.
0204: wordIndex = parse.numTokens;
0205:
0206: token = parse.getToken(wordIndex);
0207: token.type = TCL_TOKEN_WORD;
0208:
0209: // Skip white space before the word. Also skip a backslash-newline
0210: // sequence: it should be treated just like white space.
0211:
0212: while (true) {
0213: cur = script_array[script_index];
0214: type = ((cur > TYPE_MAX) ? TYPE_NORMAL
0215: : typeTable[cur]);
0216:
0217: if (type == TYPE_SPACE) {
0218: script_index++;
0219: continue;
0220: } else if ((cur == '\\')
0221: && (script_array[script_index + 1] == '\n')) {
0222: if ((script_index + 2) == parse.endIndex) {
0223: parse.incomplete = true;
0224: }
0225: bs = backslash(script_array, script_index);
0226: script_index = bs.nextIndex;
0227: continue;
0228: }
0229: break;
0230: }
0231: if ((type & terminators) != 0) {
0232: parse.termIndex = script_index;
0233: script_index++;
0234: break;
0235: }
0236:
0237: if (script_index == parse.endIndex) {
0238: if (nested && savedChar != ']') {
0239: //parse.termIndex = token.script_index;
0240: parse.incomplete = true;
0241: parse.errorType = Parser.TCL_PARSE_MISSING_BRACKET;
0242: throw new TclException(interp,
0243: "missing close-bracket");
0244: }
0245: break;
0246: }
0247:
0248: token.script_array = script_array;
0249: token.script_index = script_index;
0250:
0251: parse.numTokens++;
0252: parse.numWords++;
0253:
0254: // At this point the word can have one of three forms: something
0255: // enclosed in quotes, something enclosed in braces, or an
0256: // unquoted word (anything else).
0257:
0258: cur = script_array[script_index];
0259:
0260: if (cur == '"') {
0261: script_index++;
0262: parse = parseTokens(script_array, script_index,
0263: TYPE_QUOTE, parse);
0264: if (parse.result != TCL.OK) {
0265: throw new TclException(parse.result);
0266: }
0267: if (parse.string[parse.termIndex] != '"') {
0268: parse.termIndex = script_index - 1;
0269: parse.incomplete = true;
0270: parse.errorType = Parser.TCL_PARSE_MISSING_QUOTE;
0271: throw new TclException(parse.interp,
0272: "missing \"");
0273: }
0274: script_index = parse.termIndex + 1;
0275: } else if (cur == '{') {
0276: // Find the matching right brace that terminates the word,
0277: // then generate a single token for everything between the
0278: // braces.
0279:
0280: script_index++;
0281: token = parse.getToken(parse.numTokens);
0282: token.type = TCL_TOKEN_TEXT;
0283: token.script_array = script_array;
0284: token.script_index = script_index;
0285: token.numComponents = 0;
0286: level = 1;
0287: while (true) {
0288: cur = script_array[script_index];
0289:
0290: // get the current char in the array and lookup its type
0291: while (((cur > TYPE_MAX) ? TYPE_NORMAL
0292: : typeTable[cur]) == TYPE_NORMAL) {
0293: cur = script_array[++script_index];
0294: }
0295: if (script_array[script_index] == '}') {
0296: level--;
0297: if (level == 0) {
0298: break;
0299: }
0300: script_index++;
0301: } else if (script_array[script_index] == '{') {
0302: level++;
0303: script_index++;
0304: } else if (script_array[script_index] == '\\') {
0305: bs = backslash(script_array, script_index);
0306: if (script_array[script_index + 1] == '\n') {
0307: // A backslash-newline sequence requires special
0308: // treatment: it must be collapsed, even inside
0309: // braces, so we have to split the word into
0310: // multiple tokens so that the backslash-newline
0311: // can be represented explicitly.
0312:
0313: if ((script_index + 2) == parse.endIndex) {
0314: parse.incomplete = true;
0315: }
0316: token.size = script_index
0317: - token.script_index;
0318: if (token.size != 0) {
0319: parse.numTokens++;
0320: }
0321: token = parse.getToken(parse.numTokens);
0322: token.type = TCL_TOKEN_BS;
0323: token.script_array = script_array;
0324: token.script_index = script_index;
0325: token.size = bs.nextIndex
0326: - script_index;
0327: token.numComponents = 0;
0328: parse.numTokens++;
0329: script_index = bs.nextIndex;
0330: token = parse.getToken(parse.numTokens);
0331: token.type = TCL_TOKEN_TEXT;
0332: token.script_array = script_array;
0333: token.script_index = script_index;
0334: token.numComponents = 0;
0335: } else {
0336: script_index = bs.nextIndex;
0337: }
0338: } else if (script_index == parse.endIndex) {
0339: parse.termIndex = parse.getToken(wordIndex).script_index;
0340: parse.incomplete = true;
0341: parse.errorType = Parser.TCL_PARSE_MISSING_BRACE;
0342: throw new TclException(interp,
0343: "missing close-brace");
0344: } else {
0345: script_index++;
0346: }
0347: }
0348: if ((script_index != token.script_index)
0349: || (parse.numTokens == (wordIndex + 1))) {
0350: token.size = script_index - token.script_index;
0351: parse.numTokens++;
0352: }
0353: script_index++;
0354: } else {
0355: // This is an unquoted word. Call parseTokens and let it do
0356: // all of the work.
0357:
0358: parse = parseTokens(script_array, script_index,
0359: TYPE_SPACE | terminators, parse);
0360: if (parse.result != TCL.OK) {
0361: throw new TclException(parse.result);
0362: }
0363: script_index = parse.termIndex;
0364: }
0365:
0366: // Finish filling in the token for the word and check for the
0367: // special case of a word consisting of a single range of
0368: // literal text.
0369:
0370: token = parse.getToken(wordIndex);
0371: token.size = script_index - token.script_index;
0372: token.numComponents = parse.numTokens - (wordIndex + 1);
0373: if ((token.numComponents == 1)
0374: && (parse.getToken(wordIndex + 1).type == TCL_TOKEN_TEXT)) {
0375: token.type = TCL_TOKEN_SIMPLE_WORD;
0376: }
0377:
0378: // Do two additional checks: (a) make sure we're really at the
0379: // end of a word (there might have been garbage left after a
0380: // quoted or braced word), and (b) check for the end of the
0381: // command.
0382:
0383: numChars = endIndex - script_index;
0384: pwsr = ParseWhiteSpace(script_array, script_index,
0385: numChars, parse);
0386: scanned = pwsr.numScanned;
0387: type = pwsr.type;
0388:
0389: if (scanned > 0) {
0390: script_index += scanned;
0391: numChars -= scanned;
0392: continue;
0393: }
0394:
0395: // if (numChars == 0) {
0396: // parse.termIndex = script_index;
0397: // break;
0398: // }
0399:
0400: if ((type & terminators) != 0) {
0401: parse.termIndex = script_index;
0402: script_index++;
0403: break;
0404: }
0405:
0406: if (script_index == parse.endIndex) {
0407: if (nested && savedChar != ']') {
0408: //parse.termIndex = token.script_index;
0409: parse.incomplete = true;
0410: parse.errorType = Parser.TCL_PARSE_MISSING_BRACKET;
0411: throw new TclException(interp,
0412: "missing close-bracket");
0413: }
0414: break;
0415: }
0416:
0417: parse.termIndex = script_index;
0418: if (script_array[script_index - 1] == '"') {
0419: parse.errorType = Parser.TCL_PARSE_QUOTE_EXTRA;
0420: throw new TclException(interp,
0421: "extra characters after close-quote");
0422: } else {
0423: parse.errorType = Parser.TCL_PARSE_BRACE_EXTRA;
0424: throw new TclException(interp,
0425: "extra characters after close-brace");
0426: }
0427: }
0428: } catch (TclException e) {
0429: script_array[endIndex] = savedChar;
0430: if (parse.commandStart < 0) {
0431: parse.commandStart = saved_script_index;
0432: }
0433: parse.commandSize = parse.termIndex - parse.commandStart;
0434: parse.result = TCL.ERROR;
0435: return parse;
0436: }
0437:
0438: script_array[endIndex] = savedChar;
0439: parse.commandSize = script_index - parse.commandStart;
0440: parse.result = TCL.OK;
0441: return parse;
0442: }
0443:
0444: /*
0445: *----------------------------------------------------------------------
0446: *
0447: * parseTokens --
0448: *
0449: * This procedure forms the heart of the Tcl parser. It parses one
0450: * or more tokens from a string, up to a termination point
0451: * specified by the caller. This procedure is used to parse
0452: * unquoted command words (those not in quotes or braces), words in
0453: * quotes, and array indices for variables.
0454: *
0455: * Results:
0456: * Tokens are added to parse and parse.termIndex is filled in
0457: * with the index of the character that terminated the parse (the
0458: * first one that has a type matching the mask or the character at
0459: * parse.endIndex). The return value is TclParse object that
0460: * contains information about the parsed command. If the
0461: * command was parsed successfully, the TclParse object's result
0462: * variable is set to TCL_OK and TCL_ERROR otherwise. If an error
0463: * occurs and interp isn't null then an error message is left in
0464: * its result.
0465: *
0466: * Side effects:
0467: * None.
0468: *
0469: *----------------------------------------------------------------------
0470: */
0471:
0472: static TclParse parseTokens(char[] script_array, // The text to parse
0473: int script_index, // Index of first character to parse.
0474:
0475: int mask, // Specifies when to stop parsing. The
0476: // parse stops at the first unquoted
0477: // character whose charType() contains
0478: // any of the bits in mask.
0479: TclParse parse) // Information about parse in progress.
0480: // Updated with additional tokens and
0481: // termination information.
0482: {
0483: char cur;
0484: int type, originalTokens, varToken;
0485: TclToken token;
0486: TclParse nested;
0487: BackSlashResult bs;
0488:
0489: final boolean debug = false;
0490:
0491: if (debug) {
0492: System.out.println();
0493: System.out.println("Entered Parser.parseTokens()");
0494: System.out.print("now to parse the string \"");
0495: for (int k = script_index; k < script_array.length; k++) {
0496: System.out.print(script_array[k]);
0497: }
0498: System.out.println("\"");
0499: }
0500:
0501: // Each iteration through the following loop adds one token of
0502: // type TCL_TOKEN_TEXT, TCL_TOKEN_BS, TCL_TOKEN_COMMAND, or
0503: // TCL_TOKEN_VARIABLE to parsePtr. For TCL_TOKEN_VARIABLE additional,
0504: // tokens tokens are added for the parsed variable name.
0505:
0506: originalTokens = parse.numTokens;
0507: while (true) {
0508: token = parse.getToken(parse.numTokens);
0509: token.script_array = script_array;
0510: token.script_index = script_index;
0511: token.numComponents = 0;
0512:
0513: if (debug) {
0514: System.out.println();
0515: System.out.println("Now on index " + script_index);
0516: char tmp_c = script_array[script_index];
0517: System.out.println("Char is '" + tmp_c + "'");
0518: System.out.println("Unicode id is " + ((int) tmp_c));
0519: int tmp_i = ((int) ((tmp_c > TYPE_MAX) ? TYPE_NORMAL
0520: : typeTable[tmp_c]));
0521: System.out.println("Type is " + tmp_i);
0522: System.out.println("Mask is " + mask);
0523: System.out.println("(type & mask) is "
0524: + ((int) (tmp_i & mask)));
0525: System.out.println("orig token.size is " + token.size);
0526: }
0527:
0528: cur = script_array[script_index];
0529: type = ((cur > TYPE_MAX) ? TYPE_NORMAL : typeTable[cur]);
0530:
0531: if ((type & mask) != 0) {
0532: if (debug) {
0533: System.out.println("mask break");
0534: }
0535: break;
0536: }
0537:
0538: if ((type & TYPE_SUBS) == 0) {
0539: // This is a simple range of characters. Scan to find the end
0540: // of the range.
0541:
0542: if (debug) {
0543: System.out.println("simple range");
0544: }
0545:
0546: while (true) {
0547: cur = script_array[++script_index];
0548: type = ((cur > TYPE_MAX) ? TYPE_NORMAL
0549: : typeTable[cur]);
0550:
0551: if (debug) {
0552: System.out.println("skipping '" + cur + "'");
0553: }
0554:
0555: if ((type & (mask | TYPE_SUBS)) != 0) {
0556: break;
0557: }
0558: }
0559: token.type = TCL_TOKEN_TEXT;
0560: token.size = script_index - token.script_index;
0561: parse.numTokens++;
0562:
0563: if (debug) {
0564: System.out.println("end simple range");
0565: System.out.println("token.size is " + token.size);
0566: System.out.println("parse.numTokens is "
0567: + parse.numTokens);
0568: System.out.println();
0569: }
0570:
0571: } else if (cur == '$') {
0572: // This is a variable reference. Call parseVarName to do
0573: // all the dirty work of parsing the name.
0574:
0575: if (debug) {
0576: System.out.println("dollar sign");
0577: }
0578:
0579: varToken = parse.numTokens;
0580: parse = parseVarName(parse.interp, script_array,
0581: script_index, parse.endIndex - script_index,
0582: parse, true);
0583: if (parse.result != TCL.OK) {
0584: return parse;
0585: }
0586: script_index += parse.getToken(varToken).size;
0587: } else if (cur == '[') {
0588: // Command substitution. Call parseCommand recursively
0589: // (and repeatedly) to parse the nested command(s), then
0590: // throw away the parse information.
0591:
0592: if (debug) {
0593: System.out.println("command");
0594: }
0595:
0596: script_index++;
0597: while (true) {
0598: nested = parseCommand(parse.interp, script_array,
0599: script_index,
0600: parse.endIndex - script_index,
0601: parse.fileName, parse.lineNum, true);
0602: if (nested.result != TCL.OK) {
0603: parse.termIndex = nested.termIndex;
0604: parse.incomplete = nested.incomplete;
0605: parse.errorType = nested.errorType;
0606: parse.result = nested.result;
0607: return parse;
0608: }
0609: script_index = nested.commandStart
0610: + nested.commandSize;
0611: if ((script_array[script_index - 1] == ']')
0612: && !nested.incomplete) {
0613: break;
0614: }
0615: if (script_index == parse.endIndex) {
0616: if (parse.interp != null) {
0617: parse.interp
0618: .setResult("missing close-bracket");
0619: }
0620: parse.termIndex = token.script_index;
0621: parse.incomplete = true;
0622: parse.errorType = Parser.TCL_PARSE_MISSING_BRACKET;
0623: parse.result = TCL.ERROR;
0624: return parse;
0625: }
0626: }
0627: token.type = TCL_TOKEN_COMMAND;
0628: token.size = script_index - token.script_index;
0629: parse.numTokens++;
0630: } else if (cur == '\\') {
0631: // Backslash substitution.
0632:
0633: if (debug) {
0634: System.out.println("backslash");
0635: }
0636:
0637: if (script_array[script_index + 1] == '\n') {
0638: if ((script_index + 2) == parse.endIndex) {
0639: parse.incomplete = true;
0640: }
0641:
0642: // Note: backslash-newline is special in that it is
0643: // treated the same as a space character would be. This
0644: // means that it could terminate the token.
0645:
0646: if ((mask & TYPE_SPACE) != 0) {
0647: break;
0648: }
0649: }
0650: token.type = TCL_TOKEN_BS;
0651: bs = backslash(script_array, script_index);
0652: //token.size = bs.nextIndex - script_index;
0653: token.size = bs.count;
0654: if (token.size == 1) {
0655: // Just a backslash, due to end of string
0656: token.type = TCL_TOKEN_TEXT;
0657: parse.numTokens++;
0658: script_index++;
0659: //numBytes--;
0660: continue;
0661: }
0662: // FIXME: Add code for backslash newline processing
0663: parse.numTokens++;
0664: script_index += token.size;
0665: } else if (cur == '\0') {
0666: // We encountered a null character. If it is the null
0667: // character at the end of the string, then return.
0668: // Otherwise generate a text token for the single
0669: // character.
0670:
0671: if (debug) {
0672: System.out.println("null char");
0673: System.out.println("script_index is "
0674: + script_index);
0675: System.out.println("parse.endIndex is "
0676: + parse.endIndex);
0677: }
0678:
0679: if (script_index == parse.endIndex) {
0680: break;
0681: }
0682:
0683: token.type = TCL_TOKEN_TEXT;
0684: token.size = 1;
0685: parse.numTokens++;
0686: script_index++;
0687: } else {
0688: throw new TclRuntimeError(
0689: "parseTokens encountered unknown character");
0690: }
0691: } // end while (true)
0692:
0693: if (parse.numTokens == originalTokens) {
0694: // There was nothing in this range of text. Add an empty token
0695: // for the empty range, so that there is always at least one
0696: // token added.
0697:
0698: if (debug) {
0699: System.out.println("empty token");
0700: }
0701:
0702: token.type = TCL_TOKEN_TEXT;
0703: token.size = 0;
0704: parse.numTokens++;
0705: } else {
0706: if (debug) {
0707: System.out.println("non empty token case");
0708: }
0709: }
0710:
0711: parse.termIndex = script_index;
0712: parse.result = TCL.OK;
0713:
0714: if (debug) {
0715: System.out.println();
0716: System.out.println("Leaving Parser.parseTokens()");
0717:
0718: System.out.println("after parse, parse.numTokens is "
0719: + parse.numTokens);
0720: System.out.println("after parse, token.size is "
0721: + token.size);
0722: System.out.println("after parse, token.hashCode() is "
0723: + token.hashCode());
0724:
0725: //System.out.println( parse.toString() );
0726:
0727: System.out.print("printing "
0728: + (parse.numTokens - originalTokens) + " token(s)");
0729:
0730: for (int k = originalTokens; k < parse.numTokens; k++) {
0731: token = parse.getToken(k);
0732: System.out.println(token);
0733: }
0734:
0735: System.out.println("done printing tokens");
0736:
0737: }
0738:
0739: return parse;
0740: }
0741:
0742: /*
0743: *----------------------------------------------------------------------
0744: *
0745: * TclEvalObjvInternal -> evalObjv
0746: *
0747: * This procedure evaluates a Tcl command that has already been
0748: * parsed into words, with one TclObject holding each word.
0749: *
0750: * Results:
0751: * A result or error message is left in interp's result. If an
0752: * error occurs, this procedure does NOT add any information to
0753: * the errorInfo variable.
0754: *
0755: * Side effects:
0756: * Depends on the command.
0757: *
0758: *----------------------------------------------------------------------
0759: */
0760:
0761: static void evalObjv(Interp interp, // Interpreter in which to evaluate the
0762: // command. Also used for error
0763: // reporting.
0764: TclObject[] objv, // An array of pointers to objects that are
0765: // the words that make up the command.
0766: int length, // Number of characters in command; if -1, all
0767: // characters up to the first null character are
0768: // used. Currently unused.
0769: int flags) // Collection of OR-ed bits that control
0770: // the evaluation of the script. Only
0771: // TCL.EVAL_GLOBAL is currently
0772: // supported.
0773: throws TclException {
0774: Command cmd;
0775: TclObject[] newObjv = null;
0776: int i;
0777: CallFrame savedVarFrame; // Saves old copy of interp.varFrame
0778: // in case TCL.EVAL_GLOBAL was set.
0779:
0780: if (objv.length == 0) {
0781: interp.resetResult();
0782: return;
0783: }
0784:
0785: // Reset result, check for deleted interp, and check nest level
0786:
0787: interp.ready();
0788:
0789: interp.nestLevel++;
0790: savedVarFrame = interp.varFrame;
0791:
0792: try {
0793: // Find the procedure to execute this command. If there isn't one,
0794: // then see if there is a command "unknown". If so, create a new
0795: // word array with "unknown" as the first word and the original
0796: // command words as arguments. Then call ourselves recursively
0797: // to execute it.
0798:
0799: cmd = interp.getCommand(objv[0].toString());
0800: if (cmd == null) {
0801: newObjv = Parser.grabObjv(interp, objv.length + 1);
0802: for (i = (objv.length - 1); i >= 0; i--) {
0803: newObjv[i + 1] = objv[i];
0804: }
0805: newObjv[0] = TclString.newInstance("unknown");
0806: newObjv[0].preserve();
0807: cmd = interp.getCommand("unknown");
0808: if (cmd == null) {
0809: throw new TclException(interp,
0810: "invalid command name \""
0811: + objv[0].toString() + "\"");
0812: } else {
0813: evalObjv(interp, newObjv, length, 0);
0814: }
0815: newObjv[0].release();
0816: Parser.releaseObjv(interp, newObjv, newObjv.length);
0817: return;
0818: }
0819:
0820: // Finally, invoke the Command's cmdProc.
0821:
0822: interp.cmdCount++;
0823:
0824: if ((flags & TCL.EVAL_GLOBAL) != 0) {
0825: interp.varFrame = null;
0826: }
0827:
0828: cmd.cmdProc(interp, objv);
0829:
0830: // (TODO)
0831: //
0832: //if (AsyncReady()) {
0833: // code = AsyncInvoke(interp, code);
0834: //}
0835: } finally {
0836: interp.varFrame = savedVarFrame;
0837: interp.nestLevel--;
0838: }
0839: }
0840:
0841: /*
0842: *----------------------------------------------------------------------
0843: *
0844: * Tcl_LogCommandInfo -> logCommandInfo
0845: *
0846: * This procedure is invoked after an error occurs in an interpreter.
0847: * It adds information to the "errorInfo" variable to describe the
0848: * command that was being executed when the error occurred.
0849: *
0850: * Results:
0851: * None.
0852: *
0853: * Side effects:
0854: * Information about the command is added to errorInfo and the
0855: * line number stored internally in the interpreter is set. If this
0856: * is the first call to this procedure or interp.addErrorInfo since
0857: * an error occurred, then old information in errorInfo is
0858: * deleted.
0859: *
0860: *----------------------------------------------------------------------
0861: */
0862:
0863: static void logCommandInfo(Interp interp, // Interpreter in which to log information.
0864: char[] script_array, // The script to be logged
0865: int script_index, // First character in script containing
0866: // command (must be <= cmdIndex).
0867:
0868: int cmdIndex, // First character in command that
0869: // generated the error.
0870: int length, // Number of bytes in command (-1 means
0871: // use all bytes up to first null byte).
0872: TclException e) // The exception caused by the script
0873: // evaluation.
0874: {
0875: String ellipsis;
0876: String msg;
0877: int offset;
0878: int pIndex;
0879:
0880: if (interp.errAlreadyLogged) {
0881: // Someone else has already logged error information for this
0882: // command; we shouldn't add anything more.
0883:
0884: return;
0885: }
0886:
0887: // Compute the line number where the error occurred.
0888: // Note: The script array must be accessed directly
0889: // because we want to count from the beginning of
0890: // the script, not the current index.
0891:
0892: interp.errorLine = 1;
0893:
0894: for (pIndex = 0; pIndex < cmdIndex; pIndex++) {
0895: if (script_array[pIndex] == '\n') {
0896: interp.errorLine++;
0897: }
0898: }
0899:
0900: // Create an error message to add to errorInfo, including up to a
0901: // maximum number of characters of the command.
0902:
0903: if (length < 0) {
0904: //take into account the trailing '\0'
0905: int script_length = script_array.length - 1;
0906:
0907: length = script_length - cmdIndex;
0908: }
0909: if (length > 150) {
0910: offset = 150;
0911: ellipsis = "...";
0912: } else {
0913: offset = length;
0914: ellipsis = "";
0915: }
0916:
0917: msg = new String(script_array, cmdIndex, offset);
0918: if (!(interp.errInProgress)) {
0919: interp.addErrorInfo("\n while executing\n\"" + msg
0920: + ellipsis + "\"");
0921: } else {
0922: interp.addErrorInfo("\n invoked from within\n\"" + msg
0923: + ellipsis + "\"");
0924: }
0925: interp.errAlreadyLogged = false;
0926: e.errIndex = cmdIndex + offset;
0927: }
0928:
0929: /*
0930: *----------------------------------------------------------------------
0931: *
0932: * Tcl_EvalTokensStandard -> evalTokens
0933: *
0934: * Given an array of tokens parsed from a Tcl command (e.g., the
0935: * tokens that make up a word or the index for an array variable)
0936: * this procedure evaluates the tokens and concatenates their
0937: * values to form a single result value.
0938: *
0939: * Results:
0940: * The return value is a pointer to a newly allocated TclObject
0941: * containing the value of the array of tokens. The reference
0942: * count of the returned object has been incremented. If an error
0943: * occurs in evaluating the tokens then a TclException is
0944: * generated.
0945: *
0946: * Side effects:
0947: * A new object is allocated to hold the result.
0948: *
0949: *----------------------------------------------------------------------
0950: */
0951:
0952: static TclObject evalTokens(Interp interp, // Interpreter in which to lookup
0953: // variables, execute nested commands,
0954: // and report errors.
0955: TclToken[] tokenList, // Token list from a TclParse object.
0956: int tIndex, // Index to first token to evaluate
0957: // and concatenate.
0958: int count) // Number of tokens to consider at tIndex.
0959: // Must be at least 1.
0960: throws TclException {
0961: TclObject result, index, value;
0962: TclToken token;
0963: String p = null;
0964: String varName;
0965: BackSlashResult bs;
0966:
0967: // The only tricky thing about this procedure is that it attempts to
0968: // avoid object creation and string copying whenever possible. For
0969: // example, if the value is just a nested command, then use the
0970: // command's result object directly.
0971:
0972: result = null;
0973: for (; count > 0; count--) {
0974: token = tokenList[tIndex];
0975:
0976: // The switch statement below computes the next value to be
0977: // concat to the result, as either a range of text or an
0978: // object.
0979:
0980: value = null;
0981: switch (token.type) {
0982: case TCL_TOKEN_TEXT:
0983: p = token.getTokenString();
0984: break;
0985:
0986: case TCL_TOKEN_BS:
0987: bs = backslash(token.script_array, token.script_index);
0988: if (bs.isWordSep) {
0989: p = "\\" + bs.c;
0990: } else {
0991: Character ch = new Character(bs.c);
0992: p = ch.toString();
0993: }
0994: break;
0995:
0996: case TCL_TOKEN_COMMAND:
0997: interp.evalFlags |= Parser.TCL_BRACKET_TERM;
0998: token.script_index++;
0999:
1000: //should the nest level be changed???
1001: //interp.nestLevel++;
1002:
1003: eval2(interp, token.script_array, token.script_index,
1004: token.size - 2, 0);
1005:
1006: token.script_index--;
1007: //interp.nestLevel--;
1008: value = interp.getResult();
1009: break;
1010:
1011: case TCL_TOKEN_VARIABLE:
1012: if (token.numComponents == 1) {
1013: index = null;
1014: } else {
1015: index = evalTokens(interp, tokenList, tIndex + 2,
1016: token.numComponents - 1);
1017: if (index == null) {
1018: return null;
1019: }
1020: }
1021: varName = tokenList[tIndex + 1].getTokenString();
1022:
1023: // In order to get the existing expr parser to work with the
1024: // new Parser, we test the interp.noEval flag which is set
1025: // by the expr parser. If it is != 0, then we do not evaluate
1026: // the variable. This should be removed when the new expr
1027: // parser is implemented.
1028:
1029: if (interp.noEval == 0) {
1030: if (index != null) {
1031: try {
1032: value = interp.getVar(varName, index
1033: .toString(), 0);
1034: } finally {
1035: index.release();
1036: }
1037: } else {
1038: value = interp.getVar(varName, null, 0);
1039: }
1040: } else {
1041: value = TclString.newInstance("");
1042: value.preserve();
1043: }
1044: count -= token.numComponents;
1045: tIndex += token.numComponents;
1046: break;
1047:
1048: default:
1049: throw new TclRuntimeError(
1050: "unexpected token type in evalTokens");
1051: }
1052:
1053: // If value isn't null, the next piece of text comes from that
1054: // object; otherwise, take value of p.
1055:
1056: if (result == null) {
1057: if (value != null) {
1058: result = value;
1059: } else {
1060: result = TclString.newInstance(p);
1061: }
1062: result.preserve();
1063: } else {
1064: if (result.isShared()) {
1065: result.release();
1066: result = result.duplicate();
1067: result.preserve();
1068: }
1069: if (value != null) {
1070: p = value.toString();
1071: }
1072: TclString.append(result, p);
1073: }
1074: tIndex++;
1075: }
1076: return result;
1077: }
1078:
1079: /*
1080: *----------------------------------------------------------------------
1081: *
1082: * eval2 --
1083: *
1084: * This procedure evaluates a Tcl script without using the compiler
1085: * or byte-code interpreter. It just parses the script, creates
1086: * values for each word of each command, then calls evalObjv
1087: * to execute each command.
1088: *
1089: * Results:
1090: * A result or error message is left in interp's result.
1091: *
1092: * Side effects:
1093: * Depends on the script.
1094: *
1095: *----------------------------------------------------------------------
1096: */
1097:
1098: static void eval2(Interp interp, // Interpreter in which to evaluate the
1099: // script. Also used for error reporting.
1100: char[] script_array, // the array of charcters
1101: int script_index, // the starting index into this array
1102:
1103: int numChars, // Number of characters in script. If < 0, the
1104: // script consists of all characters up to the
1105: // first end of script.
1106: int flags) // Collection of OR-ed bits that control
1107: // the evaluation of the script. Only
1108: // TCL.EVAL_GLOBAL is currently
1109: // supported.
1110: throws TclException {
1111: int i;
1112: int objUsed = 0;
1113: int nextIndex, tokenIndex;
1114: int commandLength, charsLeft;
1115: boolean nested;
1116: TclObject[] objv;
1117: TclObject obj;
1118: TclParse parse = null;
1119: TclToken token;
1120:
1121: // Saves old copy of interp.varFrame in case TCL.EVAL_GLOBAL was set
1122: CallFrame savedVarFrame;
1123:
1124: // Take into account the trailing '\0'
1125: int script_length = script_array.length - 1;
1126:
1127: // These are modified instead of script_array and script_index
1128: char[] src_array = script_array;
1129: int src_index = script_index;
1130:
1131: //System.out.println("call to eval2");
1132:
1133: final boolean debug = false;
1134:
1135: if (debug) {
1136: System.out.println();
1137: System.out.println("Entered eval2()");
1138: System.out.print("now to eval2 the string \"");
1139: for (int k = script_index; k < script_array.length; k++) {
1140: System.out.print(script_array[k]);
1141: }
1142: System.out.println("\"");
1143: }
1144:
1145: if (numChars < 0) {
1146: numChars = script_length - script_index;
1147: }
1148: interp.resetResult();
1149: savedVarFrame = interp.varFrame;
1150: if ((flags & TCL.EVAL_GLOBAL) != 0) {
1151: interp.varFrame = null;
1152: }
1153:
1154: // Each iteration through the following loop parses the next
1155: // command from the script and then executes it.
1156:
1157: charsLeft = numChars;
1158:
1159: // Init objv with the most commonly used array size
1160: objv = grabObjv(interp, 3);
1161:
1162: if ((interp.evalFlags & TCL_BRACKET_TERM) != 0) {
1163: nested = true;
1164: } else {
1165: nested = false;
1166: }
1167: interp.evalFlags &= ~TCL_BRACKET_TERM;
1168:
1169: try {
1170:
1171: do {
1172: parse = parseCommand(interp, src_array, src_index,
1173: charsLeft, null, 0, nested);
1174:
1175: if (parse.result != TCL.OK) {
1176: throw new TclException(parse.result);
1177: }
1178:
1179: // The test on noEval is temporary. As soon as the new expr
1180: // parser is implemented it should be removed.
1181:
1182: if (parse.numWords > 0 && interp.noEval == 0) {
1183: // Generate an array of objects for the words of the command.
1184:
1185: try {
1186: tokenIndex = 0;
1187: token = parse.getToken(tokenIndex);
1188:
1189: // Test to see if new space needs to be allocated. If objv
1190: // is the EXACT size of parse.numWords, then no allocation
1191: // needs to be performed.
1192:
1193: if (objv.length != parse.numWords) {
1194: //System.out.println("need new size " + objv.length);
1195: releaseObjv(interp, objv, objv.length); //let go of resource
1196: objv = grabObjv(interp, parse.numWords); //get new resource
1197: } else {
1198: //System.out.println("reusing size " + objv.length);
1199: }
1200:
1201: for (objUsed = 0; objUsed < parse.numWords; objUsed++) {
1202: obj = evalTokens(interp, parse.tokenList,
1203: tokenIndex + 1, token.numComponents);
1204: if (obj == null) {
1205: throw new TclException(TCL.ERROR);
1206: } else {
1207: objv[objUsed] = obj;
1208: }
1209: tokenIndex += (token.numComponents + 1);
1210: token = parse.getToken(tokenIndex);
1211: }
1212:
1213: // Execute the command and free the objects for its words.
1214: try {
1215: evalObjv(interp, objv, /*src,*/charsLeft,
1216: 0);
1217: } catch (StackOverflowError e) {
1218: Parser.infiniteLoopException(interp);
1219: }
1220: } catch (TclException e) {
1221: // Generate various pieces of error information, such
1222: // as the line number where the error occurred and
1223: // information to add to the errorInfo variable. Then
1224: // free resources that had been allocated
1225: // to the command.
1226:
1227: if (e.getCompletionCode() == TCL.ERROR
1228: && !(interp.errAlreadyLogged)) {
1229: commandLength = parse.commandSize;
1230:
1231: char term = script_array[parse.commandStart
1232: + commandLength - 1];
1233: int type = charType(term);
1234: int terminators;
1235: if (nested) {
1236: terminators = TYPE_COMMAND_END
1237: | TYPE_CLOSE_BRACK;
1238: } else {
1239: terminators = TYPE_COMMAND_END;
1240: }
1241: if ((type & terminators) != 0) {
1242: // The command where the error occurred didn't end
1243: // at the end of the script (i.e. it ended at a
1244: // terminator character such as ";". Reduce the
1245: // length by one so that the error message
1246: // doesn't include the terminator character.
1247:
1248: commandLength -= 1;
1249: }
1250: interp.varFrame = savedVarFrame;
1251: logCommandInfo(interp, script_array,
1252: script_index, parse.commandStart,
1253: commandLength, e);
1254: }
1255: throw e;
1256: } finally {
1257: for (i = 0; i < objUsed; i++) {
1258: objv[i].release();
1259: objv[i] = null;
1260: }
1261: objUsed = 0;
1262:
1263: parse.release(); // Cleanup parser resources
1264: }
1265: }
1266:
1267: // Advance to the next command in the script.
1268:
1269: nextIndex = parse.commandStart + parse.commandSize;
1270: charsLeft -= (nextIndex - src_index);
1271: src_index = nextIndex;
1272: if (nested && (src_index > 1)
1273: && (src_array[src_index - 1] == ']')) {
1274:
1275: // We get here in the special case where the TCL_BRACKET_TERM
1276: // flag was set in the interpreter and we reached a close
1277: // bracket in the script. Return immediately.
1278:
1279: interp.termOffset = (src_index - 1) - script_index;
1280: interp.varFrame = savedVarFrame;
1281: return;
1282: }
1283: } while (charsLeft > 0);
1284:
1285: } finally {
1286: if (parse != null) {
1287: parse.release(); // Let go of parser resources
1288: }
1289: releaseObjv(interp, objv, objv.length); // Let go of objv buffer
1290: }
1291:
1292: interp.termOffset = src_index - script_index;
1293: interp.varFrame = savedVarFrame;
1294: return;
1295: }
1296:
1297: /*
1298: *----------------------------------------------------------------------
1299: *
1300: * parseVarName --
1301: *
1302: * Given a string starting with a $ sign, parse off a variable
1303: * name and return information about the parse.
1304: *
1305: * Results:
1306: * The return value is TclParse object that contains information
1307: * about the parsed command. If the command was parsed
1308: * successfully, the TclParse object's result variable is set to
1309: * TCL_OK and TCL_ERROR otherwise. If an error occurs and interp
1310: * isn't null then an error message is left in its result.
1311: * On a successful return, tokenList and numTokens fields of
1312: * TclParse are filled in with information about the variable name
1313: * that was parsed. The "size" field of the first new token gives
1314: * the total number of bytes in the variable name. Other fields in
1315: * TclParse are undefined.
1316: *
1317: * Side effects:
1318: * None.
1319: *
1320: *----------------------------------------------------------------------
1321: */
1322:
1323: static TclParse parseVarName(Interp interp, // Interpreter to use for error reporting;
1324: // if NULL, then no error message is
1325: // provided.
1326:
1327: char[] script_array, // String containing variable name. First
1328: int script_index, // character must be "$".
1329:
1330: int numBytes, // Total number of bytes in string. If < 0,
1331: // the string consists of all bytes up to the
1332: // first null character.
1333: TclParse parse, // Structure to fill in with information
1334: // about the variable name.
1335: boolean append) // Non-zero means append tokens to existing
1336: // information in parsePtr; zero means ignore
1337: // existing tokens in parsePtr and reinitialize
1338: // it.
1339: {
1340: char cur;
1341: TclToken token, startToken;
1342: int endIndex, varIndex;
1343:
1344: final boolean debug = false;
1345:
1346: if (debug) {
1347: System.out.println();
1348: System.out.println("Entered parseVarName()");
1349: System.out.print("now to parse var off the string \"");
1350: for (int k = script_index; k < script_array.length; k++) {
1351: System.out.print(script_array[k]);
1352: }
1353: System.out.println("\"");
1354: }
1355:
1356: if (numBytes >= 0) {
1357: endIndex = script_index + numBytes;
1358: } else {
1359: endIndex = script_array.length - 1;
1360: }
1361: if (!append) {
1362: parse = new TclParse(interp, script_array, endIndex, null,
1363: -1);
1364: }
1365:
1366: // Generate one token for the variable, an additional token for the
1367: // name, plus any number of additional tokens for the index, if
1368: // there is one.
1369:
1370: token = parse.getToken(parse.numTokens);
1371: token.type = TCL_TOKEN_VARIABLE;
1372: token.script_array = script_array;
1373: token.script_index = script_index;
1374: varIndex = parse.numTokens;
1375: parse.numTokens++;
1376: script_index++;
1377: if (script_index >= endIndex) {
1378: // The dollar sign isn't followed by a variable name.
1379: // replace the TCL_TOKEN_VARIABLE token with a
1380: // TCL_TOKEN_TEXT token for the dollar sign.
1381:
1382: token.type = TCL_TOKEN_TEXT;
1383: token.size = 1;
1384: token.numComponents = 0;
1385: parse.result = TCL.OK;
1386: return parse;
1387: }
1388: startToken = token;
1389: token = parse.getToken(parse.numTokens);
1390:
1391: // The name of the variable can have three forms:
1392: // 1. The $ sign is followed by an open curly brace. Then
1393: // the variable name is everything up to the next close
1394: // curly brace, and the variable is a scalar variable.
1395: // 2. The $ sign is not followed by an open curly brace. Then
1396: // the variable name is everything up to the next
1397: // character that isn't a letter, digit, or underscore.
1398: // :: sequences are also considered part of the variable
1399: // name, in order to support namespaces. If the following
1400: // character is an open parenthesis, then the information
1401: // between parentheses is the array element name.
1402: // 3. The $ sign is followed by something that isn't a letter,
1403: // digit, or underscore: in this case, there is no variable
1404: // name and the token is just "$".
1405:
1406: if (script_array[script_index] == '{') {
1407: if (debug) {
1408: System.out.println("parsing curley var name");
1409: }
1410:
1411: script_index++;
1412: token.type = TCL_TOKEN_TEXT;
1413: token.script_array = script_array;
1414: token.script_index = script_index;
1415: token.numComponents = 0;
1416:
1417: while (true) {
1418: if (script_index == endIndex) {
1419: if (interp != null) {
1420: interp
1421: .setResult("missing close-brace for variable name");
1422: }
1423: parse.termIndex = token.script_index - 1;
1424: parse.incomplete = true;
1425: parse.errorType = Parser.TCL_PARSE_MISSING_VAR_BRACE;
1426: parse.result = TCL.ERROR;
1427: return parse;
1428: }
1429: if (script_array[script_index] == '}') {
1430: break;
1431: }
1432: script_index++;
1433: }
1434: token.size = script_index - token.script_index;
1435: startToken.size = script_index - startToken.script_index;
1436: parse.numTokens++;
1437: script_index++;
1438: } else {
1439: if (debug) {
1440: System.out.println("parsing non curley var name");
1441: }
1442:
1443: token.type = TCL_TOKEN_TEXT;
1444: token.script_array = script_array;
1445: token.script_index = script_index;
1446: token.numComponents = 0;
1447: while (script_index != endIndex) {
1448: cur = script_array[script_index];
1449: if ((Character.isLetterOrDigit(cur)) || (cur == '_')) {
1450: script_index++;
1451: continue;
1452: }
1453: if ((cur == ':')
1454: && (((script_index + 1) != endIndex) && (script_array[script_index + 1] == ':'))) {
1455: script_index += 2;
1456: while ((script_index != endIndex)
1457: && (script_array[script_index] == ':')) {
1458: script_index++;
1459: }
1460: continue;
1461: }
1462: break;
1463: }
1464: token.size = script_index - token.script_index;
1465: if (token.size == 0) {
1466: // The dollar sign isn't followed by a variable name.
1467: // replace the TCL_TOKEN_VARIABLE token with a
1468: // TCL_TOKEN_TEXT token for the dollar sign.
1469:
1470: if (debug) {
1471: System.out
1472: .println("single $ with no var name found");
1473: }
1474:
1475: startToken.type = TCL_TOKEN_TEXT;
1476: startToken.size = 1;
1477: startToken.numComponents = 0;
1478: parse.result = TCL.OK;
1479: return parse;
1480: }
1481: parse.numTokens++;
1482: if ((script_index != endIndex)
1483: && (script_array[script_index] == '(')) {
1484: // This is a reference to an array element. Call
1485: // parseTokens recursively to parse the element name,
1486: // since it could contain any number of substitutions.
1487:
1488: if (debug) {
1489: System.out.println("parsing array element");
1490: }
1491:
1492: script_index++;
1493: parse = parseTokens(script_array, script_index,
1494: TYPE_CLOSE_PAREN, parse);
1495: if (parse.result != TCL.OK) {
1496: return parse;
1497: }
1498: if ((parse.termIndex == endIndex)
1499: || (parse.string[parse.termIndex] != ')')) {
1500: if (interp != null) {
1501: interp.setResult("missing )");
1502: }
1503: parse.termIndex = script_index - 1;
1504: parse.incomplete = true;
1505: parse.errorType = Parser.TCL_PARSE_MISSING_PAREN;
1506: parse.result = TCL.ERROR;
1507: return parse;
1508: }
1509: script_index = parse.termIndex + 1;
1510: }
1511: }
1512:
1513: if (debug) {
1514: System.out.println("default end parse case");
1515: System.out.print("var token is \"");
1516: for (int k = startToken.script_index; k < script_index; k++) {
1517: System.out.print(script_array[k]);
1518: }
1519: System.out.println("\"");
1520: }
1521:
1522: startToken.size = script_index - startToken.script_index;
1523: startToken.numComponents = parse.numTokens - (varIndex + 1);
1524: parse.result = TCL.OK;
1525: return parse;
1526: }
1527:
1528: /*
1529: *----------------------------------------------------------------------
1530: *
1531: * parseVar --
1532: *
1533: * Given a string starting with a $ sign, parse off a variable
1534: * name and return its value.
1535: *
1536: * Results:
1537: * The return value is a ParseResult object that contains the
1538: * contents of the variable given by the leading characters of
1539: * string and a index to the character just after the last one
1540: * in the variable specifier. If the variable doesn't exist,
1541: * then a TclException is generated.
1542: *
1543: * Side effects:
1544: * None.
1545: *
1546: *----------------------------------------------------------------------
1547: */
1548:
1549: static ParseResult parseVar(Interp interp, // Context for looking up variable.
1550: String string) // String containing variable name.
1551: // First character must be "$".
1552: throws TclException {
1553: TclParse parse;
1554: TclObject obj;
1555:
1556: final boolean debug = false;
1557:
1558: if (debug) {
1559: System.out.println();
1560: System.out.println("Entered parseVar()");
1561: System.out.print("now to parse var off the string \""
1562: + string + "\"");
1563: }
1564:
1565: CharPointer src = new CharPointer(string);
1566: parse = parseVarName(interp, src.array, src.index, -1, null,
1567: false);
1568: if (parse.result != TCL.OK) {
1569: throw new TclException(interp, interp.getResult()
1570: .toString());
1571: }
1572:
1573: try {
1574: if (debug) {
1575: System.out.println();
1576: System.out.print("parsed " + parse.numTokens
1577: + " tokens");
1578: }
1579:
1580: if (parse.numTokens == 1) {
1581: // There isn't a variable name after all: the $ is just a $.
1582: return new ParseResult("$", 1);
1583: }
1584:
1585: obj = evalTokens(interp, parse.tokenList, 0,
1586: parse.numTokens);
1587: if (!obj.isShared()) {
1588: throw new TclRuntimeError(
1589: "parseVar got temporary object from evalTokens");
1590: }
1591: return new ParseResult(obj, parse.tokenList[0].size);
1592: } finally {
1593: parse.release(); // Release parser resources
1594: }
1595: }
1596:
1597: /*
1598: *----------------------------------------------------------------------
1599: *
1600: * commandComplete --
1601: *
1602: * This procedure is shared by interp.commandComplete and
1603: * objCommandComplete; it does all the real work of seeing
1604: * whether a script is complete.
1605: *
1606: * Results:
1607: * True is returned if the script is complete, false if there are
1608: * open delimiters such as " or (. True is also returned if there
1609: * is a parse error in the script other than unmatched delimiters.
1610: *
1611: * Side effects:
1612: * None.
1613: *
1614: *----------------------------------------------------------------------
1615: */
1616:
1617: static boolean commandComplete(String string, // Script to check.
1618: int charLength) // Number of characters in script.
1619: {
1620: TclParse parse;
1621:
1622: CharPointer src = new CharPointer(string);
1623:
1624: do {
1625: parse = parseCommand(null, src.array, src.index,
1626: charLength, null, 0, false);
1627:
1628: src.index = parse.commandStart + parse.commandSize;
1629:
1630: parse.release(); // Release parser resources
1631:
1632: if (src.index >= charLength) {
1633: break;
1634: }
1635: } while (parse.result == TCL.OK);
1636:
1637: if (parse.incomplete) {
1638: return false;
1639: }
1640: return true;
1641: }
1642:
1643: /*
1644: *----------------------------------------------------------------------
1645: *
1646: * objCommandComplete --
1647: *
1648: * Given a partial or complete Tcl command in a Tcl object, this
1649: * procedure determines whether the command is complete in the
1650: * sense of having matched braces and quotes and brackets.
1651: *
1652: * Results:
1653: * True is returned if the command is complete, false otherwise.
1654: *
1655: * Side effects:
1656: * None.
1657: *
1658: *----------------------------------------------------------------------
1659: */
1660:
1661: static boolean objCommandComplete(TclObject obj) // Points to object holding script
1662: // to check.
1663: {
1664: String string = obj.toString();
1665: return commandComplete(string, string.length());
1666: }
1667:
1668: /*
1669: *----------------------------------------------------------------------
1670: *
1671: * TclParseBackslash -> backslash
1672: *
1673: * Figure out how to handle a backslash sequence.
1674: * The script_index value must be the index of the first \.
1675: *
1676: * Results:
1677: * The return value is an instance of BackSlashResult that
1678: * contains the character that should be substituted in place
1679: * of the backslash sequence that starts at script_index, and
1680: * an index to the next character after the backslash sequence.
1681: *
1682: * Side effects:
1683: * None.
1684: *
1685: *----------------------------------------------------------------------
1686: */
1687:
1688: static BackSlashResult backslash(char[] script_array, // script to parse
1689: int script_index) // index of first backslash
1690: {
1691: int result;
1692: char c;
1693: int count;
1694: int numChars; // Max number of characters to scan
1695:
1696: // FIXME: max number of chars to parse is needed
1697: // for embedded null support. This needs to be
1698: // calculated in the caller.
1699: numChars = script_array.length - script_index;
1700:
1701: script_index++;
1702: int endIndex = script_array.length - 1;
1703:
1704: if (script_index == endIndex) {
1705: count = 1;
1706: return new BackSlashResult('\\', script_index, count);
1707: }
1708:
1709: count = 2;
1710: c = script_array[script_index];
1711: switch (c) {
1712: case 'a': {
1713: return new BackSlashResult((char) 0x7, script_index + 1,
1714: count);
1715: }
1716: case 'b': {
1717: return new BackSlashResult((char) 0x8, script_index + 1,
1718: count);
1719: }
1720: case 'f': {
1721: return new BackSlashResult((char) 0xc, script_index + 1,
1722: count);
1723: }
1724: case 'n': {
1725: return new BackSlashResult('\n', script_index + 1, count);
1726: }
1727: case 'r': {
1728: return new BackSlashResult('\r', script_index + 1, count);
1729: }
1730: case 't': {
1731: return new BackSlashResult('\t', script_index + 1, count);
1732: }
1733: case 'v': {
1734: return new BackSlashResult((char) 0xb, script_index + 1,
1735: count);
1736: }
1737: case 'x': {
1738: script_index++;
1739: ParseHexResult pr = ParseHex(script_array, script_index,
1740: numChars - 1);
1741: count += pr.numScanned;
1742:
1743: if (count == 2) {
1744: // No hexadigits -> This is just "x".
1745: c = 'x';
1746: } else {
1747: // Keep only the last byte (2 hex digits)
1748: c = (char) (pr.result & 0xff);
1749: }
1750: return new BackSlashResult(c, script_index + pr.numScanned,
1751: count);
1752: }
1753: case 'u': {
1754: script_index++;
1755: ParseHexResult pr = ParseHex(script_array, script_index,
1756: (numChars > 5) ? 4 : numChars - 1);
1757: count += pr.numScanned;
1758:
1759: if (count == 2) {
1760: // No hexadigits -> This is just "u".
1761: c = 'u';
1762: } else {
1763: c = (char) pr.result;
1764: }
1765: return new BackSlashResult(c, script_index + pr.numScanned,
1766: count);
1767: }
1768: case '\r':
1769: case '\n': {
1770: // FIXME: This CR switch branch should not be needed in the Jacl parser.
1771: if (c == '\r') {
1772: if ((script_index + 1) < endIndex) {
1773: if (script_array[script_index + 1] == '\n') {
1774: script_index++;
1775: count++;
1776: }
1777: }
1778: }
1779: count--;
1780: do {
1781: script_index++;
1782: count++;
1783: c = script_array[script_index];
1784: } while ((count < numChars)
1785: && ((c == ' ') || (c == '\t') || Character
1786: .isWhitespace(c)));
1787: return new BackSlashResult(' ', script_index, count);
1788: }
1789: case 0: {
1790: return new BackSlashResult('\\', script_index + 1, count);
1791: }
1792: default: {
1793: // FIXME: This octal impl needs to be updated so that it does
1794: // not allow 09 to match the Tcl 8.4 impl.
1795: if ((c >= '0') && (c <= '9')) {
1796: // Convert it to an octal number. This implementation is
1797: // compatible with tcl 7.6 - characters 8 and 9 are allowed.
1798:
1799: result = c - '0';
1800: script_index++;
1801:
1802: getoctal: {
1803: if (script_index == endIndex) {
1804: break getoctal;
1805: }
1806: c = script_array[script_index];
1807: if (!((c >= '0') && (c <= '9'))) {
1808: break getoctal;
1809: }
1810: count++;
1811: result = (result * 8) + (c - '0');
1812: script_index++;
1813:
1814: if (script_index == endIndex) {
1815: break getoctal;
1816: }
1817: c = script_array[script_index];
1818: if (!((c >= '0') && (c <= '9'))) {
1819: break getoctal;
1820: }
1821: count++;
1822: result = (result * 8) + (c - '0');
1823: script_index++;
1824: }
1825:
1826: // We force result to be a 8-bit (ASCII) character so
1827: // that it compatible with Tcl 7.6.
1828:
1829: return new BackSlashResult((char) (result & 0xff),
1830: script_index, count);
1831: } else {
1832: return new BackSlashResult(c, script_index + 1, count);
1833: }
1834: }
1835: }
1836: }
1837:
1838: /*
1839: *----------------------------------------------------------------------
1840: *
1841: * Tcl_ParseBraces --
1842: *
1843: * Given a string in braces such as a Tcl command argument or a string
1844: * value in a Tcl expression, this procedure parses the string and
1845: * returns information about the parse. No more than numChars
1846: * characters will be scanned.
1847: *
1848: * Results:
1849: * The return value is a reference to a TclParse. The index of the
1850: * braced string terminating close-brace is returned in the
1851: * TclParse structure. If the parse was unsuccessful, then
1852: * a TclException will be raised.
1853: *
1854: * Side effects:
1855: * If there is insufficient space in parse to hold all the
1856: * information about the command, then additional space is
1857: * allocated.
1858: *
1859: *----------------------------------------------------------------------
1860: */
1861:
1862: static TclParse ParseBraces(Interp interp, // Interpreter to use for error reporting;
1863: // if null, then no error message is
1864: // provided.
1865: char[] script_array, // Script containing the string in braces.
1866: // The first character must be '{'.
1867: int script_index, // Script containing the string in braces.
1868: // The first character must be '{'.
1869: int numChars, // Total number of characters in string. If < 0,
1870: // the string consists of all characters up to
1871: // the first null character.
1872: TclParse parse,
1873: // Structure to fill in with information
1874: // about the string.
1875: boolean append) // true means append tokens to existing
1876: // information in parse; false means
1877: // ignore existing tokens in parse and
1878: // reinitialize it.
1879: throws TclException {
1880: TclToken token;
1881: int src;
1882: int startIndex, level, length;
1883: char cur;
1884: int type;
1885: BackSlashResult bs;
1886:
1887: if ((numChars == 0) || (script_array == null)) {
1888: throw new TclException(interp, "empty script");
1889: }
1890:
1891: int script_length = script_array.length - 1;
1892:
1893: if (numChars < 0) {
1894: numChars = script_length - script_index;
1895: }
1896: int endIndex = script_index + numChars;
1897: if (endIndex > script_length) {
1898: endIndex = script_length;
1899: }
1900:
1901: if (script_array[script_index] != '{') {
1902: throw new TclRuntimeError(
1903: "expected open brace character at script_index");
1904: }
1905:
1906: if (!append) {
1907: parse = new TclParse(interp, script_array, endIndex, null,
1908: -1);
1909: }
1910:
1911: src = script_index;
1912: startIndex = parse.numTokens;
1913:
1914: if (parse.numTokens == parse.tokensAvailable) {
1915: parse.expandTokenArray(parse.numTokens + 1);
1916: }
1917: token = parse.getToken(startIndex);
1918: token.type = TCL_TOKEN_TEXT;
1919: token.script_array = script_array;
1920: token.script_index = src + 1;
1921: token.numComponents = 0;
1922: level = 1;
1923: while (true) {
1924: for (src++, numChars--; numChars > 0; src++, numChars--) {
1925: cur = script_array[src];
1926: type = ((cur > TYPE_MAX) ? TYPE_NORMAL : typeTable[cur]);
1927: if (type != TYPE_NORMAL) {
1928: break;
1929: }
1930: }
1931: if (numChars == 0) {
1932: boolean openBrace = false;
1933:
1934: parse.errorType = TCL_PARSE_MISSING_BRACE;
1935: parse.termIndex = script_index;
1936: parse.incomplete = true;
1937:
1938: String msg = "missing close-brace";
1939:
1940: error: {
1941: if (interp == null) {
1942: break error; // Skip to exception code
1943: }
1944:
1945: // Guess if the problem is due to comments by searching
1946: // the source string for a possible open brace within the
1947: // context of a comment. Since we aren't performing a
1948: // full Tcl parse, just look for an open brace preceded
1949: // by a '<whitespace>#' on the same line.
1950:
1951: for (; src > script_index; src--) {
1952: switch (script_array[src]) {
1953: case '{':
1954: openBrace = true;
1955: break;
1956: case '\n':
1957: openBrace = false;
1958: break;
1959: case '#':
1960: cur = script_array[src - 1];
1961: if (openBrace
1962: && Character.isWhitespace(cur)) {
1963: msg = msg
1964: + ": possible unbalanced brace in comment";
1965: break error;
1966: }
1967: break;
1968: }
1969: }
1970: } // end error block
1971:
1972: parse.release();
1973: throw new TclException(interp, msg);
1974: }
1975: cur = script_array[src];
1976: switch (cur) {
1977: case '{':
1978: level++;
1979: break;
1980: case '}':
1981: if (--level == 0) {
1982:
1983: // Decide if we need to finish emitting a
1984: // partially-finished token. There are 3 cases:
1985: // {abc \newline xyz} or {xyz}
1986: // - finish emitting "xyz" token
1987: // {abc \newline}
1988: // - don't emit token after \newline
1989: // {} - finish emitting zero-sized token
1990: //
1991: // The last case ensures that there is a token
1992: // (even if empty) that describes the braced string.
1993:
1994: if ((src != token.script_index)
1995: || (parse.numTokens == startIndex)) {
1996: token.size = (src - token.script_index);
1997: parse.numTokens++;
1998: }
1999: parse.extra = src + 1;
2000: return parse;
2001: }
2002: break;
2003: case '\\':
2004: bs = backslash(script_array, src);
2005: length = bs.count;
2006: if ((length > 1) && (script_array[src + 1] == '\n')) {
2007: // A backslash-newline sequence must be collapsed, even
2008: // inside braces, so we have to split the word into
2009: // multiple tokens so that the backslash-newline can be
2010: // represented explicitly.
2011:
2012: if (numChars == 2) {
2013: parse.incomplete = true;
2014: }
2015: token.size = (src - token.script_index);
2016: if (token.size != 0) {
2017: parse.numTokens++;
2018: }
2019: if ((parse.numTokens + 1) >= parse.tokensAvailable) {
2020: parse.expandTokenArray(parse.numTokens + 1);
2021: }
2022: token = parse.getToken(parse.numTokens);
2023: token.type = TCL_TOKEN_BS;
2024: token.script_array = script_array;
2025: token.script_index = src;
2026: token.size = length;
2027: token.numComponents = 0;
2028: parse.numTokens++;
2029:
2030: src += length - 1;
2031: numChars -= length - 1;
2032: token = parse.getToken(parse.numTokens);
2033: token.type = TCL_TOKEN_TEXT;
2034: token.script_array = script_array;
2035: token.script_index = src + 1;
2036: token.numComponents = 0;
2037: } else {
2038: src += length - 1;
2039: numChars -= length - 1;
2040: }
2041: break;
2042: }
2043: }
2044: }
2045:
2046: /*
2047: *----------------------------------------------------------------------
2048: *
2049: * Tcl_ParseQuotedString -> ParseQuotedString
2050: *
2051: * Given a double-quoted string such as a quoted Tcl command argument
2052: * or a quoted value in a Tcl expression, this procedure parses the
2053: * string and returns information about the parse. No more than
2054: * numBytes bytes will be scanned.
2055: *
2056: * Results:
2057: * The return value is a reference to a TclParse. The index of the
2058: * quoted string's terminating close-quote is returned in the
2059: * TclParse structure. If the parse was unsuccessful, then
2060: * a TclException will be raised.
2061: *
2062: * Side effects:
2063: * If there is insufficient space in parse to hold all the
2064: * information about the command, then additional space is
2065: * allocated.
2066: *----------------------------------------------------------------------
2067: */
2068:
2069: // Note: This method is ported from Tcl 8.4, it is not used by the parser
2070: // yet. Currently, this methods is only used in the expr parser.
2071: static TclParse ParseQuotedString(Interp interp, // Interpreter to use for error reporting;
2072: // if null, then no error message is
2073: // provided.
2074: char[] script_array, // String containing the quoted string.
2075: // The first character must be '"'.
2076: int script_index, // Index of first character in script_array.
2077: int numBytes, // Total number of bytes in string. If < 0,
2078: // the string consists of all bytes up to
2079: // the first null character.
2080: TclParse parse,
2081: // Structure to fill in with information
2082: // about the string.
2083: boolean append) // true means append tokens to existing
2084: // information in parsePtr; false means
2085: // ignore existing tokens in parsePtr and
2086: // reinitialize it.
2087: throws TclException {
2088: if ((numBytes == 0) || (script_array == null)) {
2089: throw new TclException(interp, "empty script");
2090: }
2091:
2092: int script_length = script_array.length - 1;
2093:
2094: if (numBytes < 0) {
2095: numBytes = script_length - script_index;
2096: }
2097: int endIndex = script_index + numBytes;
2098: if (endIndex > script_length) {
2099: endIndex = script_length;
2100: }
2101:
2102: if (script_array[script_index] != '"') {
2103: throw new TclRuntimeError(
2104: "expected quote character at script_index");
2105: }
2106:
2107: if (!append) {
2108: parse = new TclParse(interp, script_array, endIndex, null,
2109: -1);
2110: }
2111:
2112: // FIXME: numBytes-1 not passed since field not supported by parseTokens().
2113: parse = Parser.parseTokens(script_array, script_index + 1,
2114: TYPE_QUOTE, parse);
2115: if (parse.result != TCL.OK) {
2116: // FIXME: look for other locations where parse.release() is not invoked!
2117: parse.release(); // Tcl_FreeParse()
2118: throw new TclException(parse.result);
2119: }
2120: if (script_array[parse.termIndex] != '"') {
2121: parse.release(); // Tcl_FreeParse()
2122: parse.errorType = Parser.TCL_PARSE_MISSING_QUOTE;
2123: parse.termIndex = script_index;
2124: parse.incomplete = true;
2125: throw new TclException(interp, "missing \"");
2126: }
2127: parse.extra = parse.termIndex + 1;
2128: return parse;
2129: }
2130:
2131: /*
2132: *----------------------------------------------------------------------
2133: *
2134: * TclParseWhiteSpace -> ParseWhiteSpace
2135: *
2136: * Scans up to numChars characters starting at script_index,
2137: * consuming white space as defined by Tcl's parsing rules.
2138: *
2139: * Results:
2140: * Returns a ParseWhitespaceResult that indicates the number of
2141: * characters of type whitespace along with the type of
2142: * non-whitespace character that terminated the scan.
2143: * Will set the incomplete member of the parse argument if
2144: * the scanning indicates an incomplete command.
2145: *
2146: * Side effects:
2147: * None.
2148: *
2149: *----------------------------------------------------------------------
2150: */
2151:
2152: // FIXME: Create a more general parse result object that is
2153: // returned by any of the parse operations, also make this
2154: // result an interp member so that an allocation is not needed
2155: // for each parse operation.
2156: static class ParseWhitespaceResult {
2157: int numScanned;
2158: int type;
2159: }
2160:
2161: static ParseWhitespaceResult ParseWhiteSpace(char[] script_array, // Array of characters to parse.
2162: int script_index, // First index in character array.
2163: int numChars, // Max number of characters to scan
2164: TclParse parse) // Updated if parsing indicates
2165: // an incomplete command.
2166: {
2167: int type = TYPE_NORMAL;
2168: int p = script_index;
2169: char c;
2170:
2171: while (true) {
2172: while (numChars > 0) {
2173: c = script_array[p];
2174: type = ((c > TYPE_MAX) ? TYPE_NORMAL : typeTable[c]);
2175: if ((type & TYPE_SPACE) == 0) {
2176: break;
2177: }
2178: numChars--;
2179: p++;
2180: }
2181: if ((numChars > 0) && ((type & TYPE_SUBS) != 0)) {
2182: c = script_array[p];
2183: if (c != '\\') {
2184: break;
2185: }
2186: if (--numChars == 0) {
2187: break;
2188: }
2189: c = script_array[p + 1];
2190: if (c != '\n') {
2191: break;
2192: }
2193: p += 2;
2194: if (--numChars == 0) {
2195: parse.incomplete = true;
2196: break;
2197: }
2198: continue;
2199: }
2200: break;
2201: }
2202: ParseWhitespaceResult pwsr = new ParseWhitespaceResult();
2203: pwsr.type = type;
2204: pwsr.numScanned = (p - script_index);
2205: return pwsr;
2206: }
2207:
2208: /*
2209: *----------------------------------------------------------------------
2210: *
2211: * TclParseHex -> ParseHex
2212: *
2213: * Scans a hexadecimal number as a character value.
2214: * (e.g., for parsing hex and unicode escape sequences).
2215: * At most numChars chars are scanned.
2216: *
2217: * Results:
2218: * A ParseHexResult containing the character value
2219: * and the number of characters consumed is returned.
2220: *
2221: * Notes:
2222: * Relies on the following properties of the ASCII
2223: * character set, with which UTF-8 is compatible:
2224: *
2225: * The digits '0' .. '9' and the letters 'A' .. 'Z' and 'a' .. 'z'
2226: * occupy consecutive code points, and '0' < 'A' < 'a'.
2227: *
2228: *----------------------------------------------------------------------
2229: */
2230:
2231: static class ParseHexResult {
2232: int result;
2233: int numScanned;
2234: }
2235:
2236: static ParseHexResult ParseHex(char[] script_array, // Array of characters to parse.
2237: int script_index, // First index in character array.
2238: int numChars) // Max number of characters to scan
2239: {
2240: int result = 0;
2241: char digit;
2242: int p = script_index;
2243:
2244: for (; numChars > 0; numChars--) {
2245: digit = script_array[p];
2246:
2247: if (((digit >= '0') && (digit <= '9'))
2248: || ((digit >= 'A') && (digit <= 'F'))
2249: || ((digit >= 'a') && (digit <= 'f'))) {
2250: // This is a hex character
2251: } else {
2252: break;
2253: }
2254:
2255: p++;
2256: result <<= 4;
2257:
2258: if (digit >= 'a') {
2259: result |= (10 + digit - 'a');
2260: } else if (digit >= 'A') {
2261: result |= (10 + digit - 'A');
2262: } else {
2263: result |= (digit - '0');
2264: }
2265: }
2266:
2267: ParseHexResult pr = new ParseHexResult();
2268: pr.result = result;
2269: pr.numScanned = p - script_index;
2270: return pr;
2271: }
2272:
2273: /*
2274: *----------------------------------------------------------------------
2275: *
2276: * charType --
2277: *
2278: * Looks into the typeTable to determine the character type.
2279: * Possible types are TYPE_NORMAL, TYPE_SPACE, TYPE_COMMAND_END,
2280: * TYPE_SUBS, etc. See below for more detail.
2281: *
2282: * Results:
2283: * A char that specifies the character type
2284: *
2285: * Side effects:
2286: * None.
2287: *
2288: *----------------------------------------------------------------------
2289: */
2290:
2291: static char charType(char c) {
2292: return ((c > TYPE_MAX) ? TYPE_NORMAL : typeTable[c]);
2293: }
2294:
2295: // The following table provides parsing information about each possible
2296: // character.
2297: //
2298: // The method charType is used to index into the table and return
2299: // information about its character argument. The following return
2300: // values are defined.
2301: //
2302: // TYPE_NORMAL - All characters that don't have special significance
2303: // to the Tcl parser.
2304: // TYPE_SPACE - The character is a whitespace character other
2305: // than newline.
2306: // TYPE_COMMAND_END - Character is newline or semicolon.
2307: // TYPE_SUBS - Character begins a substitution or has other
2308: // special meaning in parseTokens: backslash, dollar
2309: // sign, open bracket, or null.
2310: // TYPE_QUOTE - Character is a double quote.
2311: // TYPE_CLOSE_PAREN - Character is a right parenthesis.
2312: // TYPE_CLOSE_BRACK - Character is a right square bracket.
2313: // TYPE_BRACE - Character is a curly brace (either left or right).
2314:
2315: static final char TYPE_NORMAL = 0;
2316: static final char TYPE_SPACE = 0x1;
2317: static final char TYPE_COMMAND_END = 0x2;
2318: static final char TYPE_SUBS = 0x4;
2319: static final char TYPE_QUOTE = 0x8;
2320: static final char TYPE_CLOSE_PAREN = 0x10;
2321: static final char TYPE_CLOSE_BRACK = 0x20;
2322: static final char TYPE_BRACE = 0x40;
2323:
2324: // This is the largest value in the type table. If a
2325: // char value is larger then the char type is TYPE_NORMAL.
2326: // Lookup -> ((c > TYPE_MAX) ? TYPE_NORMAL : typeTable[c])
2327:
2328: static final char TYPE_MAX = 127;
2329:
2330: static char[] typeTable = {
2331: // Character values, from 0-127:
2332:
2333: TYPE_SUBS, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL,
2334: TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL,
2335: TYPE_NORMAL, TYPE_SPACE, TYPE_COMMAND_END, TYPE_SPACE,
2336: TYPE_SPACE, TYPE_SPACE, TYPE_NORMAL, TYPE_NORMAL,
2337: TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL,
2338: TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL,
2339: TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL,
2340: TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL,
2341: TYPE_SPACE, TYPE_NORMAL, TYPE_QUOTE, TYPE_NORMAL,
2342: TYPE_SUBS, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL,
2343: TYPE_NORMAL, TYPE_CLOSE_PAREN, TYPE_NORMAL, TYPE_NORMAL,
2344: TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL,
2345: TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL,
2346: TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL,
2347: TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_COMMAND_END,
2348: TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL,
2349: TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL,
2350: TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL,
2351: TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL,
2352: TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL,
2353: TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL,
2354: TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL,
2355: TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_SUBS,
2356: TYPE_SUBS, TYPE_CLOSE_BRACK, TYPE_NORMAL, TYPE_NORMAL,
2357: TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL,
2358: TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL,
2359: TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL,
2360: TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL,
2361: TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL,
2362: TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL,
2363: TYPE_NORMAL, TYPE_NORMAL, TYPE_NORMAL, TYPE_BRACE,
2364: TYPE_NORMAL, TYPE_BRACE, TYPE_NORMAL, TYPE_NORMAL, };
2365:
2366: // Type values defined for TclToken structures. These values are
2367: // defined as mask bits so that it's easy to check for collections of
2368: // types.
2369: //
2370: // TCL_TOKEN_WORD - The token describes one word of a command,
2371: // from the first non-blank character of
2372: // the word (which may be " or {) up to but
2373: // not including the space, semicolon, or
2374: // bracket that terminates the word.
2375: // NumComponents counts the total number of
2376: // sub-tokens that make up the word. This
2377: // includes, for example, sub-tokens of
2378: // TCL_TOKEN_VARIABLE tokens.
2379: // TCL_TOKEN_SIMPLE_WORD - This token is just like TCL_TOKEN_WORD
2380: // except that the word is guaranteed to
2381: // consist of a single TCL_TOKEN_TEXT
2382: // sub-token.
2383: // TCL_TOKEN_TEXT - The token describes a range of literal
2384: // text that is part of a word.
2385: // NumComponents is always 0.
2386: // TCL_TOKEN_BS - The token describes a backslash sequence
2387: // that must be collapsed. NumComponents
2388: // is always 0.
2389: // TCL_TOKEN_COMMAND - The token describes a command whose result
2390: // must be substituted into the word. The
2391: // token includes the enclosing brackets.
2392: // NumComponents is always 0.
2393: // TCL_TOKEN_VARIABLE - The token describes a variable
2394: // substitution, including the dollar sign,
2395: // variable name, and array index (if there
2396: // is one) up through the right
2397: // parentheses. NumComponents tells how
2398: // many additional tokens follow to
2399: // represent the variable name. The first
2400: // token will be a TCL_TOKEN_TEXT token
2401: // that describes the variable name. If
2402: // the variable is an array reference then
2403: // there will be one or more additional
2404: // tokens, of type TCL_TOKEN_TEXT,
2405: // TCL_TOKEN_BS, TCL_TOKEN_COMMAND, and
2406: // TCL_TOKEN_VARIABLE, that describe the
2407: // array index; numComponents counts the
2408: // total number of nested tokens that make
2409: // up the variable reference, including
2410: // sub-tokens of TCL_TOKEN_VARIABLE tokens.
2411: // TCL_TOKEN_SUB_EXPR - The token describes one subexpression of a
2412: // expression, from the first non-blank
2413: // character of the subexpression up to but not
2414: // including the space, brace, or bracket
2415: // that terminates the subexpression.
2416: // NumComponents counts the total number of
2417: // following subtokens that make up the
2418: // subexpression; this includes all subtokens
2419: // for any nested TCL_TOKEN_SUB_EXPR tokens.
2420: // For example, a numeric value used as a
2421: // primitive operand is described by a
2422: // TCL_TOKEN_SUB_EXPR token followed by a
2423: // TCL_TOKEN_TEXT token. A binary subexpression
2424: // is described by a TCL_TOKEN_SUB_EXPR token
2425: // followed by the TCL_TOKEN_OPERATOR token
2426: // for the operator, then TCL_TOKEN_SUB_EXPR
2427: // tokens for the left then the right operands.
2428: // TCL_TOKEN_OPERATOR - The token describes one expression operator.
2429: // An operator might be the name of a math
2430: // function such as "abs". A TCL_TOKEN_OPERATOR
2431: // token is always preceeded by one
2432: // TCL_TOKEN_SUB_EXPR token for the operator's
2433: // subexpression, and is followed by zero or
2434: // more TCL_TOKEN_SUB_EXPR tokens for the
2435: // operator's operands. NumComponents is
2436: // always 0.
2437:
2438: static final int TCL_TOKEN_WORD = 1;
2439: static final int TCL_TOKEN_SIMPLE_WORD = 2;
2440: static final int TCL_TOKEN_TEXT = 4;
2441: static final int TCL_TOKEN_BS = 8;
2442: static final int TCL_TOKEN_COMMAND = 16;
2443: static final int TCL_TOKEN_VARIABLE = 32;
2444: static final int TCL_TOKEN_SUB_EXPR = 64;
2445: static final int TCL_TOKEN_OPERATOR = 128;
2446:
2447: // Parsing error types. On any parsing error, one of these values
2448: // will be stored in the error field of the TclParse class.
2449:
2450: static final int TCL_PARSE_SUCCESS = 0;
2451: static final int TCL_PARSE_QUOTE_EXTRA = 1;
2452: static final int TCL_PARSE_BRACE_EXTRA = 2;
2453: static final int TCL_PARSE_MISSING_BRACE = 3;
2454: static final int TCL_PARSE_MISSING_BRACKET = 4;
2455: static final int TCL_PARSE_MISSING_PAREN = 5;
2456: static final int TCL_PARSE_MISSING_QUOTE = 6;
2457: static final int TCL_PARSE_MISSING_VAR_BRACE = 7;
2458: static final int TCL_PARSE_SYNTAX = 8;
2459: static final int TCL_PARSE_BAD_NUMBER = 9;
2460:
2461: // Note: Most of the variables below will not be used until the
2462: // Compilier is implemented, but are left for consistency.
2463:
2464: // A structure of the following type is filled in by parseCommand.
2465: // It describes a single command parsed from an input string.
2466:
2467: // evalFlag bits for Interp structures:
2468: //
2469: // TCL_BRACKET_TERM 1 means that the current script is terminated by
2470: // a close bracket rather than the end of the string.
2471: // TCL_ALLOW_EXCEPTIONS 1 means it's OK for the script to terminate with
2472: // a code other than TCL_OK or TCL_ERROR; 0 means
2473: // codes other than these should be turned into errors.
2474:
2475: static final int TCL_BRACKET_TERM = 1;
2476: static final int TCL_ALLOW_EXCEPTIONS = 4;
2477:
2478: // Flag bits for Interp structures:
2479: //
2480: // DELETED: Non-zero means the interpreter has been deleted:
2481: // don't process any more commands for it, and destroy
2482: // the structure as soon as all nested invocations of
2483: // Tcl_Eval are done.
2484: // ERR_IN_PROGRESS: Non-zero means an error unwind is already in
2485: // progress. Zero means a command proc has been
2486: // invoked since last error occured.
2487: // ERR_ALREADY_LOGGED: Non-zero means information has already been logged
2488: // in $errorInfo for the current Tcl_Eval instance,
2489: // so Tcl_Eval needn't log it (used to implement the
2490: // "error message log" command).
2491: // ERROR_CODE_SET: Non-zero means that Tcl_SetErrorCode has been
2492: // called to record information for the current
2493: // error. Zero means Tcl_Eval must clear the
2494: // errorCode variable if an error is returned.
2495: // EXPR_INITIALIZED: Non-zero means initialization specific to
2496: // expressions has been carried out.
2497: // DONT_COMPILE_CMDS_INLINE: Non-zero means that the bytecode compiler
2498: // should not compile any commands into an inline
2499: // sequence of instructions. This is set 1, for
2500: // example, when command traces are requested.
2501: // RAND_SEED_INITIALIZED: Non-zero means that the randSeed value of the
2502: // interp has not be initialized. This is set 1
2503: // when we first use the rand() or srand() functions.
2504: // SAFE_INTERP: Non zero means that the current interp is a
2505: // safe interp (ie it has only the safe commands
2506: // installed, less priviledge than a regular interp).
2507: // USE_EVAL_DIRECT: Non-zero means don't use the compiler or byte-code
2508: // interpreter; instead, have Tcl_EvalObj call
2509: // Tcl_EvalDirect. Used primarily for testing the
2510: // new parser.
2511:
2512: static final int DELETED = 1;
2513: static final int ERR_IN_PROGRESS = 2;
2514: static final int ERR_ALREADY_LOGGED = 4;
2515: static final int ERROR_CODE_SET = 8;
2516: static final int EXPR_INITIALIZED = 0x10;
2517: static final int DONT_COMPILE_CMDS_INLINE = 0x20;
2518: static final int RAND_SEED_INITIALIZED = 0x40;
2519: static final int SAFE_INTERP = 0x80;
2520: static final int USE_EVAL_DIRECT = 0x100;
2521:
2522: // These are private read only values that are used by the parser
2523: // class to implement a TclObject[] cache
2524:
2525: // Max size of array to cache (1..N-1)
2526: private static final int OBJV_CACHE_MAX = 11;
2527:
2528: // The number of array to cache for each size
2529: // for example if the number of 3 elements is set to 5
2530: // an array of 5 TclObject[] objects
2531: // which will each be 3 elements long
2532:
2533: private static final int[] OBJV_CACHE_SIZES = { 0, 12, 12, 10, 6,
2534: 4, 4, 4, 2, 1, 1 };
2535:
2536: //private static final int[] OBJV_CACHE_HITS = {0,0,0,0,0,0,0,0,0,0,0};
2537: //private static final int[] OBJV_CACHE_MISSES = {0,0,0,0,0,0,0,0,0,0,0};
2538:
2539: static void init(Interp interp) {
2540: //System.out.println("called Parser.init()");
2541:
2542: TclObject[][][] OBJV = new TclObject[OBJV_CACHE_MAX][][];
2543: int[] USED = new int[OBJV_CACHE_MAX];
2544:
2545: int i, j, size;
2546:
2547: for (i = 0; i < OBJV_CACHE_MAX; i++) {
2548: size = OBJV_CACHE_SIZES[i];
2549: //System.out.println("size " + i + " has " + size + " cache blocks");
2550: OBJV[i] = new TclObject[size][];
2551: USED[i] = 0;
2552: for (j = 0; j < size; j++) {
2553: // Java arrays are allocated with all null values
2554: OBJV[i][j] = new TclObject[i];
2555: }
2556: }
2557:
2558: interp.parserObjv = OBJV;
2559: interp.parserObjvUsed = USED;
2560: }
2561:
2562: // Get a TclObject[] array of a given size. The array
2563: // is always filled with null values.
2564:
2565: static TclObject[] grabObjv(final Interp interp, final int size) {
2566: // Get number of used markers for this size
2567: int OPEN;
2568:
2569: if ((size < OBJV_CACHE_MAX)
2570: && ((OPEN = interp.parserObjvUsed[size]) < OBJV_CACHE_SIZES[size])) {
2571: // Found an open cache slot
2572: if (false) {
2573: //System.out.println("cache hit for objv of size " + size);
2574: //OBJV_CACHE_HITS[i] = OBJV_CACHE_HITS[i] + 1;
2575: }
2576: interp.parserObjvUsed[size] += 1;
2577: return interp.parserObjv[size][OPEN];
2578: } else {
2579: // Did not find a free cache array of this size
2580: if (false) {
2581: if (size >= OBJV_CACHE_MAX) {
2582: //System.out.println("cache allocate for big objv of size " + size);
2583: } else {
2584: //System.out.println("cache miss for objv of size " + size);
2585: //OBJV_CACHE_MISS[i] = OBJV_CACHE_MISS[i] + 1;
2586: }
2587: }
2588: return new TclObject[size];
2589: }
2590:
2591: }
2592:
2593: // Return a TclObject[] array of a given size to the cache.
2594: // The size argument is the length of the array, it is never zero.
2595:
2596: static void releaseObjv(final Interp interp,
2597: final TclObject[] objv, final int size) {
2598: if (size < OBJV_CACHE_MAX) {
2599: int OPEN = interp.parserObjvUsed[size];
2600:
2601: if (OPEN > 0) {
2602: OPEN--;
2603: interp.parserObjvUsed[size] = OPEN;
2604: // Optimize nulling out of array, the
2605: // most common cases are handled here.
2606: switch (size) {
2607: case 1:
2608: objv[0] = null;
2609: break;
2610: case 2:
2611: objv[0] = null;
2612: objv[1] = null;
2613: break;
2614: case 3:
2615: objv[0] = null;
2616: objv[1] = null;
2617: objv[2] = null;
2618: break;
2619: case 4:
2620: objv[0] = null;
2621: objv[1] = null;
2622: objv[2] = null;
2623: objv[3] = null;
2624: break;
2625: case 5:
2626: objv[0] = null;
2627: objv[1] = null;
2628: objv[2] = null;
2629: objv[3] = null;
2630: objv[4] = null;
2631: break;
2632: default:
2633: Arrays.fill(objv, null);
2634: break;
2635: }
2636: interp.parserObjv[size][OPEN] = objv;
2637: }
2638: }
2639: }
2640:
2641: // Raise an infinite loop TclException
2642:
2643: static void infiniteLoopException(Interp interp)
2644: throws TclException {
2645: throw new TclException(interp,
2646: "too many nested calls to eval (infinite loop?)");
2647: }
2648:
2649: } // end class Parser
|