0001: /* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
0002: *
0003: * The contents of this file are subject to the Netscape Public
0004: * License Version 1.1 (the "License"); you may not use this file
0005: * except in compliance with the License. You may obtain a copy of
0006: * the License at http://www.mozilla.org/NPL/
0007: *
0008: * Software distributed under the License is distributed on an "AS
0009: * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
0010: * implied. See the License for the specific language governing
0011: * rights and limitations under the License.
0012: *
0013: * The Original Code is Rhino code, released
0014: * May 6, 1999.
0015: *
0016: * The Initial Developer of the Original Code is Netscape
0017: * Communications Corporation. Portions created by Netscape are
0018: * Copyright (C) 1997-1999 Netscape Communications Corporation. All
0019: * Rights Reserved.
0020: *
0021: * Contributor(s):
0022: * Roger Lawrence
0023: * Mike McCabe
0024: *
0025: * Alternatively, the contents of this file may be used under the
0026: * terms of the GNU Public License (the "GPL"), in which case the
0027: * provisions of the GPL are applicable instead of those above.
0028: * If you wish to allow use of your version of this file only
0029: * under the terms of the GPL and not to allow others to use your
0030: * version of this file under the NPL, indicate your decision by
0031: * deleting the provisions above and replace them with the notice
0032: * and other provisions required by the GPL. If you do not delete
0033: * the provisions above, a recipient may use your version of this
0034: * file under either the NPL or the GPL.
0035: */
0036: // Modified by Google
0037: package com.google.gwt.dev.js.rhino;
0038:
0039: import java.io.*;
0040:
0041: /**
0042: * This class implements the JavaScript scanner.
0043: *
0044: * It is based on the C source files jsscan.c and jsscan.h
0045: * in the jsref package.
0046: *
0047: * @see org.mozilla.javascript.Parser
0048: *
0049: * @author Mike McCabe
0050: * @author Brendan Eich
0051: */
0052:
0053: public class TokenStream {
0054:
0055: static final boolean RESERVED_KEYWORD_AS_IDENTIFIER = false;
0056:
0057: /*
0058: * JSTokenStream flags, mirroring those in jsscan.h. These are used
0059: * by the parser to change/check the state of the scanner.
0060: */
0061:
0062: final static int TSF_NEWLINES = 1 << 0, // tokenize newlines
0063: TSF_FUNCTION = 1 << 1, // scanning inside function body
0064: TSF_RETURN_EXPR = 1 << 2, // function has 'return expr;'
0065: TSF_RETURN_VOID = 1 << 3, // function has 'return;'
0066: TSF_REGEXP = 1 << 4, // looking for a regular expression
0067: TSF_DIRTYLINE = 1 << 5; // stuff other than whitespace since
0068: // start of line
0069:
0070: /*
0071: * For chars - because we need something out-of-range
0072: * to check. (And checking EOF by exception is annoying.)
0073: * Note distinction from EOF token type!
0074: */
0075: private final static int EOF_CHAR = -1;
0076:
0077: /**
0078: * Token types. These values correspond to JSTokenType values in
0079: * jsscan.c.
0080: */
0081:
0082: public final static int
0083: // start enum
0084: ERROR = -1, // well-known as the only code < EOF
0085: EOF = 0, // end of file token - (not EOF_CHAR)
0086: EOL = 1, // end of line
0087: // Beginning here are interpreter bytecodes. Their values
0088: // must not exceed 127.
0089: POPV = 2,
0090: ENTERWITH = 3,
0091: LEAVEWITH = 4,
0092: RETURN = 5,
0093: GOTO = 6,
0094: IFEQ = 7,
0095: IFNE = 8,
0096: DUP = 9,
0097: SETNAME = 10,
0098: BITOR = 11,
0099: BITXOR = 12,
0100: BITAND = 13,
0101: EQ = 14,
0102: NE = 15,
0103: LT = 16,
0104: LE = 17,
0105: GT = 18,
0106: GE = 19,
0107: LSH = 20,
0108: RSH = 21,
0109: URSH = 22,
0110: ADD = 23,
0111: SUB = 24,
0112: MUL = 25,
0113: DIV = 26,
0114: MOD = 27,
0115: BITNOT = 28,
0116: NEG = 29,
0117: NEW = 30,
0118: DELPROP = 31,
0119: TYPEOF = 32,
0120: NAMEINC = 33,
0121: PROPINC = 34,
0122: ELEMINC = 35,
0123: NAMEDEC = 36,
0124: PROPDEC = 37,
0125: ELEMDEC = 38,
0126: GETPROP = 39,
0127: SETPROP = 40,
0128: GETELEM = 41,
0129: SETELEM = 42,
0130: CALL = 43,
0131: NAME = 44,
0132: NUMBER = 45,
0133: STRING = 46,
0134: ZERO = 47,
0135: ONE = 48,
0136: NULL = 49,
0137: THIS = 50,
0138: FALSE = 51,
0139: TRUE = 52,
0140: SHEQ = 53, // shallow equality (===)
0141: SHNE = 54, // shallow inequality (!==)
0142: CLOSURE = 55,
0143: REGEXP = 56,
0144: POP = 57,
0145: POS = 58,
0146: VARINC = 59,
0147: VARDEC = 60,
0148: BINDNAME = 61,
0149: THROW = 62,
0150: IN = 63,
0151: INSTANCEOF = 64,
0152: GOSUB = 65,
0153: RETSUB = 66,
0154: CALLSPECIAL = 67,
0155: GETTHIS = 68,
0156: NEWTEMP = 69,
0157: USETEMP = 70,
0158: GETBASE = 71,
0159: GETVAR = 72,
0160: SETVAR = 73,
0161: UNDEFINED = 74,
0162: TRY = 75,
0163: ENDTRY = 76,
0164: NEWSCOPE = 77,
0165: TYPEOFNAME = 78,
0166: ENUMINIT = 79,
0167: ENUMNEXT = 80,
0168: GETPROTO = 81,
0169: GETPARENT = 82,
0170: SETPROTO = 83,
0171: SETPARENT = 84,
0172: SCOPE = 85,
0173: GETSCOPEPARENT = 86,
0174: THISFN = 87,
0175: JTHROW = 88,
0176: // End of interpreter bytecodes
0177: SEMI = 89, // semicolon
0178: LB = 90, // left and right brackets
0179: RB = 91,
0180: LC = 92, // left and right curlies (braces)
0181: RC = 93,
0182: LP = 94, // left and right parentheses
0183: GWT = 95,
0184: COMMA = 96, // comma operator
0185: ASSIGN = 97, // assignment ops (= += -= etc.)
0186: HOOK = 98, // conditional (?:)
0187: COLON = 99,
0188: OR = 100, // logical or (||)
0189: AND = 101, // logical and (&&)
0190: EQOP = 102, // equality ops (== !=)
0191: RELOP = 103, // relational ops (< <= > >=)
0192: SHOP = 104, // shift ops (<< >> >>>)
0193: UNARYOP = 105, // unary prefix operator
0194: INC = 106, // increment/decrement (++ --)
0195: DEC = 107,
0196: DOT = 108, // member operator (.)
0197: PRIMARY = 109, // true, false, null, this
0198: FUNCTION = 110, // function keyword
0199: EXPORT = 111, // export keyword
0200: IMPORT = 112, // import keyword
0201: IF = 113, // if keyword
0202: ELSE = 114, // else keyword
0203: SWITCH = 115, // switch keyword
0204: CASE = 116, // case keyword
0205: DEFAULT = 117, // default keyword
0206: WHILE = 118, // while keyword
0207: DO = 119, // do keyword
0208: FOR = 120, // for keyword
0209: BREAK = 121, // break keyword
0210: CONTINUE = 122, // continue keyword
0211: VAR = 123, // var keyword
0212: WITH = 124, // with keyword
0213: CATCH = 125, // catch keyword
0214: FINALLY = 126, // finally keyword
0215: RESERVED = 127, // reserved keywords
0216:
0217: /** Added by Mike - these are JSOPs in the jsref, but I
0218: * don't have them yet in the java implementation...
0219: * so they go here. Also whatever I needed.
0220:
0221: * Most of these go in the 'op' field when returning
0222: * more general token types, eg. 'DIV' as the op of 'ASSIGN'.
0223: */
0224: NOP = 128, // NOP
0225: NOT = 129, // etc.
0226: PRE = 130, // for INC, DEC nodes.
0227: POST = 131,
0228:
0229: /**
0230: * For JSOPs associated with keywords...
0231: * eg. op = THIS; token = PRIMARY
0232: */
0233:
0234: VOID = 132,
0235:
0236: /* types used for the parse tree - these never get returned
0237: * by the scanner.
0238: */
0239: BLOCK = 133, // statement block
0240: ARRAYLIT = 134, // array literal
0241: OBJLIT = 135, // object literal
0242: LABEL = 136, // label
0243: TARGET = 137, LOOP = 138,
0244: ENUMDONE = 139,
0245: EXPRSTMT = 140,
0246: PARENT = 141, CONVERT = 142,
0247: JSR = 143,
0248: NEWLOCAL = 144,
0249: USELOCAL = 145, DEBUGGER = 146, SCRIPT = 147, // top-level node for entire script
0250:
0251: LAST_TOKEN = 147;
0252:
0253: // end enum
0254:
0255: public static String tokenToName(int token) {
0256: if (Context.printTrees || Context.printICode) {
0257: switch (token) {
0258: case ERROR:
0259: return "error";
0260: case EOF:
0261: return "eof";
0262: case EOL:
0263: return "eol";
0264: case POPV:
0265: return "popv";
0266: case ENTERWITH:
0267: return "enterwith";
0268: case LEAVEWITH:
0269: return "leavewith";
0270: case RETURN:
0271: return "return";
0272: case GOTO:
0273: return "goto";
0274: case IFEQ:
0275: return "ifeq";
0276: case IFNE:
0277: return "ifne";
0278: case DUP:
0279: return "dup";
0280: case SETNAME:
0281: return "setname";
0282: case BITOR:
0283: return "bitor";
0284: case BITXOR:
0285: return "bitxor";
0286: case BITAND:
0287: return "bitand";
0288: case EQ:
0289: return "eq";
0290: case NE:
0291: return "ne";
0292: case LT:
0293: return "lt";
0294: case LE:
0295: return "le";
0296: case GT:
0297: return "gt";
0298: case GE:
0299: return "ge";
0300: case LSH:
0301: return "lsh";
0302: case RSH:
0303: return "rsh";
0304: case URSH:
0305: return "ursh";
0306: case ADD:
0307: return "add";
0308: case SUB:
0309: return "sub";
0310: case MUL:
0311: return "mul";
0312: case DIV:
0313: return "div";
0314: case MOD:
0315: return "mod";
0316: case BITNOT:
0317: return "bitnot";
0318: case NEG:
0319: return "neg";
0320: case NEW:
0321: return "new";
0322: case DELPROP:
0323: return "delprop";
0324: case TYPEOF:
0325: return "typeof";
0326: case NAMEINC:
0327: return "nameinc";
0328: case PROPINC:
0329: return "propinc";
0330: case ELEMINC:
0331: return "eleminc";
0332: case NAMEDEC:
0333: return "namedec";
0334: case PROPDEC:
0335: return "propdec";
0336: case ELEMDEC:
0337: return "elemdec";
0338: case GETPROP:
0339: return "getprop";
0340: case SETPROP:
0341: return "setprop";
0342: case GETELEM:
0343: return "getelem";
0344: case SETELEM:
0345: return "setelem";
0346: case CALL:
0347: return "call";
0348: case NAME:
0349: return "name";
0350: case NUMBER:
0351: return "number";
0352: case STRING:
0353: return "string";
0354: case ZERO:
0355: return "zero";
0356: case ONE:
0357: return "one";
0358: case NULL:
0359: return "null";
0360: case THIS:
0361: return "this";
0362: case FALSE:
0363: return "false";
0364: case TRUE:
0365: return "true";
0366: case SHEQ:
0367: return "sheq";
0368: case SHNE:
0369: return "shne";
0370: case CLOSURE:
0371: return "closure";
0372: case REGEXP:
0373: return "object";
0374: case POP:
0375: return "pop";
0376: case POS:
0377: return "pos";
0378: case VARINC:
0379: return "varinc";
0380: case VARDEC:
0381: return "vardec";
0382: case BINDNAME:
0383: return "bindname";
0384: case THROW:
0385: return "throw";
0386: case IN:
0387: return "in";
0388: case INSTANCEOF:
0389: return "instanceof";
0390: case GOSUB:
0391: return "gosub";
0392: case RETSUB:
0393: return "retsub";
0394: case CALLSPECIAL:
0395: return "callspecial";
0396: case GETTHIS:
0397: return "getthis";
0398: case NEWTEMP:
0399: return "newtemp";
0400: case USETEMP:
0401: return "usetemp";
0402: case GETBASE:
0403: return "getbase";
0404: case GETVAR:
0405: return "getvar";
0406: case SETVAR:
0407: return "setvar";
0408: case UNDEFINED:
0409: return "undefined";
0410: case TRY:
0411: return "try";
0412: case ENDTRY:
0413: return "endtry";
0414: case NEWSCOPE:
0415: return "newscope";
0416: case TYPEOFNAME:
0417: return "typeofname";
0418: case ENUMINIT:
0419: return "enuminit";
0420: case ENUMNEXT:
0421: return "enumnext";
0422: case GETPROTO:
0423: return "getproto";
0424: case GETPARENT:
0425: return "getparent";
0426: case SETPROTO:
0427: return "setproto";
0428: case SETPARENT:
0429: return "setparent";
0430: case SCOPE:
0431: return "scope";
0432: case GETSCOPEPARENT:
0433: return "getscopeparent";
0434: case THISFN:
0435: return "thisfn";
0436: case JTHROW:
0437: return "jthrow";
0438: case SEMI:
0439: return "semi";
0440: case LB:
0441: return "lb";
0442: case RB:
0443: return "rb";
0444: case LC:
0445: return "lc";
0446: case RC:
0447: return "rc";
0448: case LP:
0449: return "lp";
0450: case GWT:
0451: return "gwt";
0452: case COMMA:
0453: return "comma";
0454: case ASSIGN:
0455: return "assign";
0456: case HOOK:
0457: return "hook";
0458: case COLON:
0459: return "colon";
0460: case OR:
0461: return "or";
0462: case AND:
0463: return "and";
0464: case EQOP:
0465: return "eqop";
0466: case RELOP:
0467: return "relop";
0468: case SHOP:
0469: return "shop";
0470: case UNARYOP:
0471: return "unaryop";
0472: case INC:
0473: return "inc";
0474: case DEC:
0475: return "dec";
0476: case DOT:
0477: return "dot";
0478: case PRIMARY:
0479: return "primary";
0480: case FUNCTION:
0481: return "function";
0482: case EXPORT:
0483: return "export";
0484: case IMPORT:
0485: return "import";
0486: case IF:
0487: return "if";
0488: case ELSE:
0489: return "else";
0490: case SWITCH:
0491: return "switch";
0492: case CASE:
0493: return "case";
0494: case DEFAULT:
0495: return "default";
0496: case WHILE:
0497: return "while";
0498: case DO:
0499: return "do";
0500: case FOR:
0501: return "for";
0502: case BREAK:
0503: return "break";
0504: case CONTINUE:
0505: return "continue";
0506: case VAR:
0507: return "var";
0508: case WITH:
0509: return "with";
0510: case CATCH:
0511: return "catch";
0512: case FINALLY:
0513: return "finally";
0514: case RESERVED:
0515: return "reserved";
0516: case NOP:
0517: return "nop";
0518: case NOT:
0519: return "not";
0520: case PRE:
0521: return "pre";
0522: case POST:
0523: return "post";
0524: case VOID:
0525: return "void";
0526: case BLOCK:
0527: return "block";
0528: case ARRAYLIT:
0529: return "arraylit";
0530: case OBJLIT:
0531: return "objlit";
0532: case LABEL:
0533: return "label";
0534: case TARGET:
0535: return "target";
0536: case LOOP:
0537: return "loop";
0538: case ENUMDONE:
0539: return "enumdone";
0540: case EXPRSTMT:
0541: return "exprstmt";
0542: case PARENT:
0543: return "parent";
0544: case CONVERT:
0545: return "convert";
0546: case JSR:
0547: return "jsr";
0548: case NEWLOCAL:
0549: return "newlocal";
0550: case USELOCAL:
0551: return "uselocal";
0552: case SCRIPT:
0553: return "script";
0554: }
0555: return "<unknown=" + token + ">";
0556: }
0557: return "";
0558: }
0559:
0560: /* This function uses the cached op, string and number fields in
0561: * TokenStream; if getToken has been called since the passed token
0562: * was scanned, the op or string printed may be incorrect.
0563: */
0564: public String tokenToString(int token) {
0565: if (Context.printTrees) {
0566: String name = tokenToName(token);
0567:
0568: switch (token) {
0569: case UNARYOP:
0570: case ASSIGN:
0571: case PRIMARY:
0572: case EQOP:
0573: case SHOP:
0574: case RELOP:
0575: return name + " " + tokenToName(this .op);
0576:
0577: case STRING:
0578: case REGEXP:
0579: case NAME:
0580: return name + " `" + this .string + "'";
0581:
0582: case NUMBER:
0583: return "NUMBER " + this .number;
0584: }
0585:
0586: return name;
0587: }
0588: return "";
0589: }
0590:
0591: private static int getKeywordId(String name) {
0592: // #string_id_map#
0593: // The following assumes that EOF == 0
0594: final int Id_break = BREAK, Id_case = CASE, Id_continue = CONTINUE, Id_default = DEFAULT, Id_delete = DELPROP, Id_do = DO, Id_else = ELSE, Id_export = EXPORT, Id_false = PRIMARY
0595: | (FALSE << 8), Id_for = FOR, Id_function = FUNCTION, Id_if = IF, Id_in = RELOP
0596: | (IN << 8), Id_new = NEW, Id_null = PRIMARY
0597: | (NULL << 8), Id_return = RETURN, Id_switch = SWITCH, Id_this = PRIMARY
0598: | (THIS << 8), Id_true = PRIMARY | (TRUE << 8), Id_typeof = UNARYOP
0599: | (TYPEOF << 8), Id_var = VAR, Id_void = UNARYOP
0600: | (VOID << 8), Id_while = WHILE, Id_with = WITH,
0601:
0602: // the following are #ifdef RESERVE_JAVA_KEYWORDS in jsscan.c
0603: Id_abstract = RESERVED, Id_boolean = RESERVED, Id_byte = RESERVED, Id_catch = CATCH, Id_char = RESERVED, Id_class = RESERVED, Id_const = RESERVED, Id_debugger = DEBUGGER, Id_double = RESERVED, Id_enum = RESERVED, Id_extends = RESERVED, Id_final = RESERVED, Id_finally = FINALLY, Id_float = RESERVED, Id_goto = RESERVED, Id_implements = RESERVED, Id_import = IMPORT, Id_instanceof = RELOP
0604: | (INSTANCEOF << 8), Id_int = RESERVED, Id_interface = RESERVED, Id_long = RESERVED, Id_native = RESERVED, Id_package = RESERVED, Id_private = RESERVED, Id_protected = RESERVED, Id_public = RESERVED, Id_short = RESERVED, Id_static = RESERVED, Id_super = RESERVED, Id_synchronized = RESERVED, Id_throw = THROW, Id_throws = RESERVED, Id_transient = RESERVED, Id_try = TRY, Id_volatile = RESERVED;
0605:
0606: int id;
0607: String s = name;
0608: // #generated# Last update: 2001-06-01 17:45:01 CEST
0609: L0: {
0610: id = 0;
0611: String X = null;
0612: int c;
0613: L: switch (s.length()) {
0614: case 2:
0615: c = s.charAt(1);
0616: if (c == 'f') {
0617: if (s.charAt(0) == 'i') {
0618: id = Id_if;
0619: break L0;
0620: }
0621: } else if (c == 'n') {
0622: if (s.charAt(0) == 'i') {
0623: id = Id_in;
0624: break L0;
0625: }
0626: } else if (c == 'o') {
0627: if (s.charAt(0) == 'd') {
0628: id = Id_do;
0629: break L0;
0630: }
0631: }
0632: break L;
0633: case 3:
0634: switch (s.charAt(0)) {
0635: case 'f':
0636: if (s.charAt(2) == 'r' && s.charAt(1) == 'o') {
0637: id = Id_for;
0638: break L0;
0639: }
0640: break L;
0641: case 'i':
0642: if (s.charAt(2) == 't' && s.charAt(1) == 'n') {
0643: id = Id_int;
0644: break L0;
0645: }
0646: break L;
0647: case 'n':
0648: if (s.charAt(2) == 'w' && s.charAt(1) == 'e') {
0649: id = Id_new;
0650: break L0;
0651: }
0652: break L;
0653: case 't':
0654: if (s.charAt(2) == 'y' && s.charAt(1) == 'r') {
0655: id = Id_try;
0656: break L0;
0657: }
0658: break L;
0659: case 'v':
0660: if (s.charAt(2) == 'r' && s.charAt(1) == 'a') {
0661: id = Id_var;
0662: break L0;
0663: }
0664: break L;
0665: }
0666: break L;
0667: case 4:
0668: switch (s.charAt(0)) {
0669: case 'b':
0670: X = "byte";
0671: id = Id_byte;
0672: break L;
0673: case 'c':
0674: c = s.charAt(3);
0675: if (c == 'e') {
0676: if (s.charAt(2) == 's' && s.charAt(1) == 'a') {
0677: id = Id_case;
0678: break L0;
0679: }
0680: } else if (c == 'r') {
0681: if (s.charAt(2) == 'a' && s.charAt(1) == 'h') {
0682: id = Id_char;
0683: break L0;
0684: }
0685: }
0686: break L;
0687: case 'e':
0688: c = s.charAt(3);
0689: if (c == 'e') {
0690: if (s.charAt(2) == 's' && s.charAt(1) == 'l') {
0691: id = Id_else;
0692: break L0;
0693: }
0694: } else if (c == 'm') {
0695: if (s.charAt(2) == 'u' && s.charAt(1) == 'n') {
0696: id = Id_enum;
0697: break L0;
0698: }
0699: }
0700: break L;
0701: case 'g':
0702: X = "goto";
0703: id = Id_goto;
0704: break L;
0705: case 'l':
0706: X = "long";
0707: id = Id_long;
0708: break L;
0709: case 'n':
0710: X = "null";
0711: id = Id_null;
0712: break L;
0713: case 't':
0714: c = s.charAt(3);
0715: if (c == 'e') {
0716: if (s.charAt(2) == 'u' && s.charAt(1) == 'r') {
0717: id = Id_true;
0718: break L0;
0719: }
0720: } else if (c == 's') {
0721: if (s.charAt(2) == 'i' && s.charAt(1) == 'h') {
0722: id = Id_this ;
0723: break L0;
0724: }
0725: }
0726: break L;
0727: case 'v':
0728: X = "void";
0729: id = Id_void;
0730: break L;
0731: case 'w':
0732: X = "with";
0733: id = Id_with;
0734: break L;
0735: }
0736: break L;
0737: case 5:
0738: switch (s.charAt(2)) {
0739: case 'a':
0740: X = "class";
0741: id = Id_class;
0742: break L;
0743: case 'e':
0744: X = "break";
0745: id = Id_break;
0746: break L;
0747: case 'i':
0748: X = "while";
0749: id = Id_while;
0750: break L;
0751: case 'l':
0752: X = "false";
0753: id = Id_false;
0754: break L;
0755: case 'n':
0756: c = s.charAt(0);
0757: if (c == 'c') {
0758: X = "const";
0759: id = Id_const;
0760: } else if (c == 'f') {
0761: X = "final";
0762: id = Id_final;
0763: }
0764: break L;
0765: case 'o':
0766: c = s.charAt(0);
0767: if (c == 'f') {
0768: X = "float";
0769: id = Id_float;
0770: } else if (c == 's') {
0771: X = "short";
0772: id = Id_short;
0773: }
0774: break L;
0775: case 'p':
0776: X = "super";
0777: id = Id_super ;
0778: break L;
0779: case 'r':
0780: X = "throw";
0781: id = Id_throw;
0782: break L;
0783: case 't':
0784: X = "catch";
0785: id = Id_catch;
0786: break L;
0787: }
0788: break L;
0789: case 6:
0790: switch (s.charAt(1)) {
0791: case 'a':
0792: X = "native";
0793: id = Id_native;
0794: break L;
0795: case 'e':
0796: c = s.charAt(0);
0797: if (c == 'd') {
0798: X = "delete";
0799: id = Id_delete;
0800: } else if (c == 'r') {
0801: X = "return";
0802: id = Id_return;
0803: }
0804: break L;
0805: case 'h':
0806: X = "throws";
0807: id = Id_throws;
0808: break L;
0809: case 'm':
0810: X = "import";
0811: id = Id_import;
0812: break L;
0813: case 'o':
0814: X = "double";
0815: id = Id_double;
0816: break L;
0817: case 't':
0818: X = "static";
0819: id = Id_static;
0820: break L;
0821: case 'u':
0822: X = "public";
0823: id = Id_public;
0824: break L;
0825: case 'w':
0826: X = "switch";
0827: id = Id_switch;
0828: break L;
0829: case 'x':
0830: X = "export";
0831: id = Id_export;
0832: break L;
0833: case 'y':
0834: X = "typeof";
0835: id = Id_typeof;
0836: break L;
0837: }
0838: break L;
0839: case 7:
0840: switch (s.charAt(1)) {
0841: case 'a':
0842: X = "package";
0843: id = Id_package;
0844: break L;
0845: case 'e':
0846: X = "default";
0847: id = Id_default;
0848: break L;
0849: case 'i':
0850: X = "finally";
0851: id = Id_finally;
0852: break L;
0853: case 'o':
0854: X = "boolean";
0855: id = Id_boolean;
0856: break L;
0857: case 'r':
0858: X = "private";
0859: id = Id_private;
0860: break L;
0861: case 'x':
0862: X = "extends";
0863: id = Id_extends;
0864: break L;
0865: }
0866: break L;
0867: case 8:
0868: switch (s.charAt(0)) {
0869: case 'a':
0870: X = "abstract";
0871: id = Id_abstract;
0872: break L;
0873: case 'c':
0874: X = "continue";
0875: id = Id_continue;
0876: break L;
0877: case 'd':
0878: X = "debugger";
0879: id = Id_debugger;
0880: break L;
0881: case 'f':
0882: X = "function";
0883: id = Id_function;
0884: break L;
0885: case 'v':
0886: X = "volatile";
0887: id = Id_volatile;
0888: break L;
0889: }
0890: break L;
0891: case 9:
0892: c = s.charAt(0);
0893: if (c == 'i') {
0894: X = "interface";
0895: id = Id_interface;
0896: } else if (c == 'p') {
0897: X = "protected";
0898: id = Id_protected;
0899: } else if (c == 't') {
0900: X = "transient";
0901: id = Id_transient;
0902: }
0903: break L;
0904: case 10:
0905: c = s.charAt(1);
0906: if (c == 'm') {
0907: X = "implements";
0908: id = Id_implements ;
0909: } else if (c == 'n') {
0910: X = "instanceof";
0911: id = Id_instanceof ;
0912: }
0913: break L;
0914: case 12:
0915: X = "synchronized";
0916: id = Id_synchronized;
0917: break L;
0918: }
0919: if (X != null && X != s && !X.equals(s))
0920: id = 0;
0921: }
0922: // #/generated#
0923: // #/string_id_map#
0924:
0925: return id;
0926: }
0927:
0928: private int stringToKeyword(String name) {
0929: int id = getKeywordId(name);
0930: if (id == 0) {
0931: return EOF;
0932: }
0933: this .op = id >> 8;
0934: return id & 0xff;
0935: }
0936:
0937: public TokenStream(Reader in, String sourceName, int lineno) {
0938: this .in = new LineBuffer(in, lineno);
0939: this .pushbackToken = EOF;
0940: this .sourceName = sourceName;
0941: flags = 0;
0942: }
0943:
0944: /* return and pop the token from the stream if it matches...
0945: * otherwise return null
0946: */
0947: public boolean matchToken(int toMatch) throws IOException {
0948: int token = getToken();
0949: if (token == toMatch)
0950: return true;
0951:
0952: // didn't match, push back token
0953: tokenno--;
0954: this .pushbackToken = token;
0955: return false;
0956: }
0957:
0958: public void clearPushback() {
0959: this .pushbackToken = EOF;
0960: }
0961:
0962: public void ungetToken(int tt) {
0963: if (this .pushbackToken != EOF && tt != ERROR) {
0964: String message = Context.getMessage2(
0965: "msg.token.replaces.pushback", tokenToString(tt),
0966: tokenToString(this .pushbackToken));
0967: throw new RuntimeException(message);
0968: }
0969: this .pushbackToken = tt;
0970: tokenno--;
0971: }
0972:
0973: public int peekToken() throws IOException {
0974: int result = getToken();
0975:
0976: this .pushbackToken = result;
0977: tokenno--;
0978: return result;
0979: }
0980:
0981: public int peekTokenSameLine() throws IOException {
0982: int result;
0983:
0984: flags |= TSF_NEWLINES; // SCAN_NEWLINES from jsscan.h
0985: result = peekToken();
0986: flags &= ~TSF_NEWLINES; // HIDE_NEWLINES from jsscan.h
0987: if (this .pushbackToken == EOL)
0988: this .pushbackToken = EOF;
0989: return result;
0990: }
0991:
0992: public static boolean isJSKeyword(String s) {
0993: return getKeywordId(s) != 0;
0994: }
0995:
0996: public static boolean isJSIdentifier(String s) {
0997: int length = s.length();
0998:
0999: if (length == 0
1000: || !Character.isJavaIdentifierStart(s.charAt(0)))
1001: return false;
1002:
1003: for (int i = 1; i < length; i++) {
1004: char c = s.charAt(i);
1005: if (!Character.isJavaIdentifierPart(c)) {
1006: if (c == '\\') {
1007: if (!((i + 5) < length) && (s.charAt(i + 1) == 'u')
1008: && 0 <= xDigitToInt(s.charAt(i + 2))
1009: && 0 <= xDigitToInt(s.charAt(i + 3))
1010: && 0 <= xDigitToInt(s.charAt(i + 4))
1011: && 0 <= xDigitToInt(s.charAt(i + 5))) {
1012: return true;
1013: }
1014: }
1015:
1016: return false;
1017: }
1018: }
1019:
1020: return true;
1021: }
1022:
1023: private static boolean isAlpha(int c) {
1024: return ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'));
1025: }
1026:
1027: static boolean isDigit(int c) {
1028: return (c >= '0' && c <= '9');
1029: }
1030:
1031: static int xDigitToInt(int c) {
1032: if ('0' <= c && c <= '9') {
1033: return c - '0';
1034: }
1035: if ('a' <= c && c <= 'f') {
1036: return c - ('a' - 10);
1037: }
1038: if ('A' <= c && c <= 'F') {
1039: return c - ('A' - 10);
1040: }
1041: return -1;
1042: }
1043:
1044: /* As defined in ECMA. jsscan.c uses C isspace() (which allows
1045: * \v, I think.) note that code in in.read() implicitly accepts
1046: * '\r' ==
1047: as well.
1048: */
1049: public static boolean isJSSpace(int c) {
1050: return (c == '\u0020' || c == '\u0009' || c == '\u000C'
1051: || c == '\u000B' || c == '\u00A0' || Character
1052: .getType((char) c) == Character.SPACE_SEPARATOR);
1053: }
1054:
1055: public static boolean isJSLineTerminator(int c) {
1056: return (c == '\n' || c == '\r' || c == 0x2028 || c == 0x2029);
1057: }
1058:
1059: private void skipLine() throws IOException {
1060: // skip to end of line
1061: int c;
1062: while ((c = in.read()) != EOF_CHAR && c != '\n') {
1063: }
1064: in.unread();
1065: }
1066:
1067: public int getToken() throws IOException {
1068: int c;
1069: tokenno++;
1070:
1071: // Check for pushed-back token
1072: if (this .pushbackToken != EOF) {
1073: int result = this .pushbackToken;
1074: this .pushbackToken = EOF;
1075: return result;
1076: }
1077:
1078: // Eat whitespace, possibly sensitive to newlines.
1079: do {
1080: c = in.read();
1081: if (c == '\n') {
1082: flags &= ~TSF_DIRTYLINE;
1083: if ((flags & TSF_NEWLINES) != 0)
1084: break;
1085: }
1086: } while (isJSSpace(c) || c == '\n');
1087:
1088: if (c == EOF_CHAR)
1089: return EOF;
1090: if (c != '-' && c != '\n')
1091: flags |= TSF_DIRTYLINE;
1092:
1093: // identifier/keyword/instanceof?
1094: // watch out for starting with a <backslash>
1095: boolean identifierStart;
1096: boolean isUnicodeEscapeStart = false;
1097: if (c == '\\') {
1098: c = in.read();
1099: if (c == 'u') {
1100: identifierStart = true;
1101: isUnicodeEscapeStart = true;
1102: stringBufferTop = 0;
1103: } else {
1104: identifierStart = false;
1105: c = '\\';
1106: in.unread();
1107: }
1108: } else {
1109: identifierStart = Character.isJavaIdentifierStart((char) c);
1110: if (identifierStart) {
1111: stringBufferTop = 0;
1112: addToString(c);
1113: }
1114:
1115: // bruce: special handling of JSNI signatures
1116: // - it would be nice to handle Unicode escapes in the future
1117: //
1118: if (c == '@') {
1119: stringBufferTop = 0;
1120: addToString(c);
1121: return jsniMatchReference();
1122: }
1123: }
1124:
1125: if (identifierStart) {
1126: boolean containsEscape = isUnicodeEscapeStart;
1127: for (;;) {
1128: if (isUnicodeEscapeStart) {
1129: // strictly speaking we should probably push-back
1130: // all the bad characters if the <backslash>uXXXX
1131: // sequence is malformed. But since there isn't a
1132: // correct context(is there?) for a bad Unicode
1133: // escape sequence in an identifier, we can report
1134: // an error here.
1135: int escapeVal = 0;
1136: for (int i = 0; i != 4; ++i) {
1137: c = in.read();
1138: escapeVal = (escapeVal << 4) | xDigitToInt(c);
1139: // Next check takes care about c < 0 and bad escape
1140: if (escapeVal < 0) {
1141: break;
1142: }
1143: }
1144: if (escapeVal < 0) {
1145: reportSyntaxError("msg.invalid.escape", null);
1146: return ERROR;
1147: }
1148: addToString(escapeVal);
1149: isUnicodeEscapeStart = false;
1150: } else {
1151: c = in.read();
1152: if (c == '\\') {
1153: c = in.read();
1154: if (c == 'u') {
1155: isUnicodeEscapeStart = true;
1156: containsEscape = true;
1157: } else {
1158: reportSyntaxError("msg.illegal.character",
1159: null);
1160: return ERROR;
1161: }
1162: } else {
1163: if (!Character.isJavaIdentifierPart((char) c)) {
1164: break;
1165: }
1166: addToString(c);
1167: }
1168: }
1169: }
1170: in.unread();
1171:
1172: String str = getStringFromBuffer();
1173: if (!containsEscape) {
1174: // OPT we shouldn't have to make a string (object!) to
1175: // check if it's a keyword.
1176:
1177: // Return the corresponding token if it's a keyword
1178: int result = stringToKeyword(str);
1179: if (result != EOF) {
1180: if (result != RESERVED) {
1181: return result;
1182: } else if (!RESERVED_KEYWORD_AS_IDENTIFIER) {
1183: return result;
1184: } else {
1185: // If implementation permits to use future reserved
1186: // keywords in violation with the EcmaScript standard,
1187: // treat it as name but issue warning
1188: Object[] errArgs = { str };
1189: reportSyntaxWarning("msg.reserved.keyword",
1190: errArgs);
1191: }
1192: }
1193: }
1194: this .string = str;
1195: return NAME;
1196: }
1197:
1198: // is it a number?
1199: if (isDigit(c) || (c == '.' && isDigit(in.peek()))) {
1200:
1201: stringBufferTop = 0;
1202: int base = 10;
1203:
1204: if (c == '0') {
1205: c = in.read();
1206: if (c == 'x' || c == 'X') {
1207: base = 16;
1208: c = in.read();
1209: } else if (isDigit(c)) {
1210: base = 8;
1211: } else {
1212: addToString('0');
1213: }
1214: }
1215:
1216: if (base == 16) {
1217: while (0 <= xDigitToInt(c)) {
1218: addToString(c);
1219: c = in.read();
1220: }
1221: } else {
1222: while ('0' <= c && c <= '9') {
1223: /*
1224: * We permit 08 and 09 as decimal numbers, which
1225: * makes our behavior a superset of the ECMA
1226: * numeric grammar. We might not always be so
1227: * permissive, so we warn about it.
1228: */
1229: if (base == 8 && c >= '8') {
1230: Object[] errArgs = { c == '8' ? "8" : "9" };
1231: reportSyntaxWarning("msg.bad.octal.literal",
1232: errArgs);
1233: base = 10;
1234: }
1235: addToString(c);
1236: c = in.read();
1237: }
1238: }
1239:
1240: boolean isInteger = true;
1241:
1242: if (base == 10 && (c == '.' || c == 'e' || c == 'E')) {
1243: isInteger = false;
1244: if (c == '.') {
1245: do {
1246: addToString(c);
1247: c = in.read();
1248: } while (isDigit(c));
1249: }
1250: if (c == 'e' || c == 'E') {
1251: addToString(c);
1252: c = in.read();
1253: if (c == '+' || c == '-') {
1254: addToString(c);
1255: c = in.read();
1256: }
1257: if (!isDigit(c)) {
1258: reportSyntaxError("msg.missing.exponent", null);
1259: return ERROR;
1260: }
1261: do {
1262: addToString(c);
1263: c = in.read();
1264: } while (isDigit(c));
1265: }
1266: }
1267: in.unread();
1268: String numString = getStringFromBuffer();
1269:
1270: double dval;
1271: if (base == 10 && !isInteger) {
1272: try {
1273: // Use Java conversion to number from string...
1274: dval = (Double.valueOf(numString)).doubleValue();
1275: } catch (NumberFormatException ex) {
1276: Object[] errArgs = { ex.getMessage() };
1277: reportSyntaxError("msg.caught.nfe", errArgs);
1278: return ERROR;
1279: }
1280: } else {
1281: dval = ScriptRuntime.stringToNumber(numString, 0, base);
1282: }
1283:
1284: this .number = dval;
1285: return NUMBER;
1286: }
1287:
1288: // is it a string?
1289: if (c == '"' || c == '\'') {
1290: // We attempt to accumulate a string the fast way, by
1291: // building it directly out of the reader. But if there
1292: // are any escaped characters in the string, we revert to
1293: // building it out of a StringBuffer.
1294:
1295: int quoteChar = c;
1296: int val = 0;
1297: stringBufferTop = 0;
1298:
1299: c = in.read();
1300: strLoop: while (c != quoteChar) {
1301: if (c == '\n' || c == EOF_CHAR) {
1302: in.unread();
1303: reportSyntaxError("msg.unterminated.string.lit",
1304: null);
1305: return ERROR;
1306: }
1307:
1308: if (c == '\\') {
1309: // We've hit an escaped character
1310:
1311: c = in.read();
1312: switch (c) {
1313: case 'b':
1314: c = '\b';
1315: break;
1316: case 'f':
1317: c = '\f';
1318: break;
1319: case 'n':
1320: c = '\n';
1321: break;
1322: case 'r':
1323: c = '\r';
1324: break;
1325: case 't':
1326: c = '\t';
1327: break;
1328:
1329: // \v a late addition to the ECMA spec,
1330: // it is not in Java, so use 0xb
1331: case 'v':
1332: c = 0xb;
1333: break;
1334:
1335: case 'u': {
1336: /*
1337: * Get 4 hex digits; if the u escape is not
1338: * followed by 4 hex digits, use 'u' + the literal
1339: * character sequence that follows.
1340: */
1341: int escapeStart = stringBufferTop;
1342: addToString('u');
1343: int escapeVal = 0;
1344: for (int i = 0; i != 4; ++i) {
1345: c = in.read();
1346: escapeVal = (escapeVal << 4)
1347: | xDigitToInt(c);
1348: if (escapeVal < 0) {
1349: continue strLoop;
1350: }
1351: addToString(c);
1352: }
1353: // prepare for replace of stored 'u' sequence
1354: // by escape value
1355: stringBufferTop = escapeStart;
1356: c = escapeVal;
1357: }
1358: break;
1359:
1360: case 'x': {
1361: /* Get 2 hex digits, defaulting to 'x' + literal
1362: * sequence, as above.
1363: */
1364: c = in.read();
1365: int escapeVal = xDigitToInt(c);
1366: if (escapeVal < 0) {
1367: addToString('x');
1368: continue strLoop;
1369: } else {
1370: int c1 = c;
1371: c = in.read();
1372: escapeVal = (escapeVal << 4)
1373: | xDigitToInt(c);
1374: if (escapeVal < 0) {
1375: addToString('x');
1376: addToString(c1);
1377: continue strLoop;
1378: } else {
1379: // got 2 hex digits
1380: c = escapeVal;
1381: }
1382: }
1383: }
1384: break;
1385:
1386: case '\n':
1387: // Remove line terminator
1388: c = in.read();
1389: continue strLoop;
1390:
1391: default:
1392: if ('0' <= c && c < '8') {
1393: val = c - '0';
1394: c = in.read();
1395: if ('0' <= c && c < '8') {
1396: val = 8 * val + c - '0';
1397: c = in.read();
1398: if ('0' <= c && c < '8' && val <= 037) {
1399: // c is 3rd char of octal sequence only if
1400: // the resulting val <= 0377
1401: val = 8 * val + c - '0';
1402: c = in.read();
1403: }
1404: }
1405: in.unread();
1406: c = val;
1407: }
1408: }
1409: }
1410: addToString(c);
1411: c = in.read();
1412: }
1413:
1414: this .string = getStringFromBuffer();
1415: return STRING;
1416: }
1417:
1418: switch (c) {
1419: case '\n':
1420: return EOL;
1421: case ';':
1422: return SEMI;
1423: case '[':
1424: return LB;
1425: case ']':
1426: return RB;
1427: case '{':
1428: return LC;
1429: case '}':
1430: return RC;
1431: case '(':
1432: return LP;
1433: case ')':
1434: return GWT;
1435: case ',':
1436: return COMMA;
1437: case '?':
1438: return HOOK;
1439: case ':':
1440: return COLON;
1441: case '.':
1442: return DOT;
1443:
1444: case '|':
1445: if (in.match('|')) {
1446: return OR;
1447: } else if (in.match('=')) {
1448: this .op = BITOR;
1449: return ASSIGN;
1450: } else {
1451: return BITOR;
1452: }
1453:
1454: case '^':
1455: if (in.match('=')) {
1456: this .op = BITXOR;
1457: return ASSIGN;
1458: } else {
1459: return BITXOR;
1460: }
1461:
1462: case '&':
1463: if (in.match('&')) {
1464: return AND;
1465: } else if (in.match('=')) {
1466: this .op = BITAND;
1467: return ASSIGN;
1468: } else {
1469: return BITAND;
1470: }
1471:
1472: case '=':
1473: if (in.match('=')) {
1474: if (in.match('='))
1475: this .op = SHEQ;
1476: else
1477: this .op = EQ;
1478: return EQOP;
1479: } else {
1480: this .op = NOP;
1481: return ASSIGN;
1482: }
1483:
1484: case '!':
1485: if (in.match('=')) {
1486: if (in.match('='))
1487: this .op = SHNE;
1488: else
1489: this .op = NE;
1490: return EQOP;
1491: } else {
1492: this .op = NOT;
1493: return UNARYOP;
1494: }
1495:
1496: case '<':
1497: /* NB:treat HTML begin-comment as comment-till-eol */
1498: if (in.match('!')) {
1499: if (in.match('-')) {
1500: if (in.match('-')) {
1501: skipLine();
1502: return getToken(); // in place of 'goto retry'
1503: }
1504: in.unread();
1505: }
1506: in.unread();
1507: }
1508: if (in.match('<')) {
1509: if (in.match('=')) {
1510: this .op = LSH;
1511: return ASSIGN;
1512: } else {
1513: this .op = LSH;
1514: return SHOP;
1515: }
1516: } else {
1517: if (in.match('=')) {
1518: this .op = LE;
1519: return RELOP;
1520: } else {
1521: this .op = LT;
1522: return RELOP;
1523: }
1524: }
1525:
1526: case '>':
1527: if (in.match('>')) {
1528: if (in.match('>')) {
1529: if (in.match('=')) {
1530: this .op = URSH;
1531: return ASSIGN;
1532: } else {
1533: this .op = URSH;
1534: return SHOP;
1535: }
1536: } else {
1537: if (in.match('=')) {
1538: this .op = RSH;
1539: return ASSIGN;
1540: } else {
1541: this .op = RSH;
1542: return SHOP;
1543: }
1544: }
1545: } else {
1546: if (in.match('=')) {
1547: this .op = GE;
1548: return RELOP;
1549: } else {
1550: this .op = GT;
1551: return RELOP;
1552: }
1553: }
1554:
1555: case '*':
1556: if (in.match('=')) {
1557: this .op = MUL;
1558: return ASSIGN;
1559: } else {
1560: return MUL;
1561: }
1562:
1563: case '/':
1564: // is it a // comment?
1565: if (in.match('/')) {
1566: skipLine();
1567: return getToken();
1568: }
1569: if (in.match('*')) {
1570: while ((c = in.read()) != -1
1571: && !(c == '*' && in.match('/'))) {
1572: ; // empty loop body
1573: }
1574: if (c == EOF_CHAR) {
1575: reportSyntaxError("msg.unterminated.comment", null);
1576: return ERROR;
1577: }
1578: return getToken(); // `goto retry'
1579: }
1580:
1581: // is it a regexp?
1582: if ((flags & TSF_REGEXP) != 0) {
1583: stringBufferTop = 0;
1584: while ((c = in.read()) != '/') {
1585: if (c == '\n' || c == EOF_CHAR) {
1586: in.unread();
1587: reportSyntaxError("msg.unterminated.re.lit",
1588: null);
1589: return ERROR;
1590: }
1591: if (c == '\\') {
1592: addToString(c);
1593: c = in.read();
1594: }
1595:
1596: addToString(c);
1597: }
1598: int reEnd = stringBufferTop;
1599:
1600: while (true) {
1601: if (in.match('g'))
1602: addToString('g');
1603: else if (in.match('i'))
1604: addToString('i');
1605: else if (in.match('m'))
1606: addToString('m');
1607: else
1608: break;
1609: }
1610:
1611: if (isAlpha(in.peek())) {
1612: reportSyntaxError("msg.invalid.re.flag", null);
1613: return ERROR;
1614: }
1615:
1616: this .string = new String(stringBuffer, 0, reEnd);
1617: this .regExpFlags = new String(stringBuffer, reEnd,
1618: stringBufferTop - reEnd);
1619: return REGEXP;
1620: }
1621:
1622: if (in.match('=')) {
1623: this .op = DIV;
1624: return ASSIGN;
1625: } else {
1626: return DIV;
1627: }
1628:
1629: case '%':
1630: this .op = MOD;
1631: if (in.match('=')) {
1632: return ASSIGN;
1633: } else {
1634: return MOD;
1635: }
1636:
1637: case '~':
1638: this .op = BITNOT;
1639: return UNARYOP;
1640:
1641: case '+':
1642: if (in.match('=')) {
1643: this .op = ADD;
1644: return ASSIGN;
1645: } else if (in.match('+')) {
1646: return INC;
1647: } else {
1648: return ADD;
1649: }
1650:
1651: case '-':
1652: if (in.match('=')) {
1653: this .op = SUB;
1654: c = ASSIGN;
1655: } else if (in.match('-')) {
1656: if (0 == (flags & TSF_DIRTYLINE)) {
1657: // treat HTML end-comment after possible whitespace
1658: // after line start as comment-utill-eol
1659: if (in.match('>')) {
1660: skipLine();
1661: return getToken();
1662: }
1663: }
1664: c = DEC;
1665: } else {
1666: c = SUB;
1667: }
1668: flags |= TSF_DIRTYLINE;
1669: return c;
1670:
1671: default:
1672: reportSyntaxError("msg.illegal.character", null);
1673: return ERROR;
1674: }
1675: }
1676:
1677: private int jsniMatchReference() throws IOException {
1678:
1679: // First, read the type name whose member is being accessed.
1680: if (!jsniMatchQualifiedTypeName('.', ':')) {
1681: return ERROR;
1682: }
1683:
1684: // Now we must the second colon.
1685: //
1686: int c = in.read();
1687: if (c != ':') {
1688: in.unread();
1689: reportSyntaxError("msg.jsni.expected.char",
1690: new String[] { ":" });
1691: return ERROR;
1692: }
1693: addToString(c);
1694:
1695: // Finish by reading the field or method signature.
1696: //
1697: if (!jsniMatchMethodSignatureOrFieldName()) {
1698: return ERROR;
1699: }
1700:
1701: this .string = new String(stringBuffer, 0, stringBufferTop);
1702: return NAME;
1703: }
1704:
1705: private boolean jsniMatchParamListSignature() throws IOException {
1706: // Assume the opening '(' has already been read.
1707: // Read param type signatures until we see a closing ')'.
1708: //
1709: do {
1710: int c = in.read();
1711: if (c == ')') {
1712: // Finished successfully.
1713: //
1714: addToString(c);
1715: return true;
1716: }
1717:
1718: in.unread();
1719: } while (jsniMatchParamTypeSignature());
1720:
1721: // If we made it here, we can assume that there was an invalid type
1722: // signature that was already reported and that the offending char
1723: // was already unread.
1724: //
1725: return false;
1726: }
1727:
1728: private boolean jsniMatchParamTypeSignature() throws IOException {
1729: int c = in.read();
1730: switch (c) {
1731: case 'Z':
1732: case 'B':
1733: case 'C':
1734: case 'S':
1735: case 'I':
1736: case 'J':
1737: case 'F':
1738: case 'D':
1739: // Primitive type id.
1740: addToString(c);
1741: return true;
1742: case 'L':
1743: // Class/Interface type prefix.
1744: addToString(c);
1745: return jsniMatchQualifiedTypeName('/', ';');
1746: case '[':
1747: // Array type prefix.
1748: addToString(c);
1749: return jsniMatchParamArrayTypeSignature();
1750: default:
1751: in.unread();
1752: reportSyntaxError("msg.jsni.expected.param.type", null);
1753: return false;
1754: }
1755: }
1756:
1757: private boolean jsniMatchParamArrayTypeSignature()
1758: throws IOException {
1759: // Assume the leading '[' has already been read.
1760: // What follows must be another param type signature.
1761: //
1762: return jsniMatchParamTypeSignature();
1763: }
1764:
1765: private boolean jsniMatchMethodSignatureOrFieldName()
1766: throws IOException {
1767: int c = in.read();
1768:
1769: // We must see an ident start here.
1770: //
1771: if (!Character.isJavaIdentifierStart((char) c)) {
1772: in.unread();
1773: reportSyntaxError("msg.jsni.expected.identifier", null);
1774: return false;
1775: }
1776:
1777: addToString(c);
1778:
1779: for (;;) {
1780: c = in.read();
1781: if (Character.isJavaIdentifierPart((char) c)) {
1782: addToString(c);
1783: } else if (c == '(') {
1784: // This means we're starting a JSNI method signature.
1785: //
1786: addToString(c);
1787: if (jsniMatchParamListSignature()) {
1788: // Finished a method signature with success.
1789: // Assume the callee unread the last char.
1790: //
1791: return true;
1792: } else {
1793: // Assume the callee reported the error and unread the last char.
1794: //
1795: return false;
1796: }
1797: } else {
1798: // We don't know this char, so it finishes the token.
1799: //
1800: in.unread();
1801: return true;
1802: }
1803: }
1804: }
1805:
1806: /**
1807: * This method is called to match the fully-qualified type name that
1808: * should appear after the '@' in a JSNI reference.
1809: * @param sepChar the character that will separate the Java idents
1810: * (either a '.' or '/')
1811: * @param endChar the character that indicates the end of the
1812: */
1813: private boolean jsniMatchQualifiedTypeName(char sepChar,
1814: char endChar) throws IOException {
1815: int c = in.read();
1816:
1817: // Whether nested or not, we must see an ident start here.
1818: //
1819: if (!Character.isJavaIdentifierStart((char) c)) {
1820: in.unread();
1821: reportSyntaxError("msg.jsni.expected.identifier", null);
1822: return false;
1823: }
1824:
1825: // Now actually add the first ident char.
1826: //
1827: addToString(c);
1828:
1829: // And append any other ident chars.
1830: //
1831: for (;;) {
1832: c = in.read();
1833: if (Character.isJavaIdentifierPart((char) c)) {
1834: addToString(c);
1835: } else {
1836: break;
1837: }
1838: }
1839:
1840: // We have a non-ident char to classify.
1841: //
1842: if (c == sepChar) {
1843: addToString(c);
1844: if (jsniMatchQualifiedTypeName(sepChar, endChar)) {
1845: // We consumed up to the endChar, so we finished with total success.
1846: //
1847: return true;
1848: } else {
1849: // Assume that the nested call reported the syntax error and
1850: // unread the last character.
1851: //
1852: return false;
1853: }
1854: } else if (c == endChar) {
1855: // Matched everything up to the specified end char.
1856: //
1857: addToString(c);
1858: return true;
1859: } else {
1860: // This is an unknown char that finishes the token.
1861: //
1862: in.unread();
1863: return true;
1864: }
1865: }
1866:
1867: private String getStringFromBuffer() {
1868: return new String(stringBuffer, 0, stringBufferTop);
1869: }
1870:
1871: private void addToString(int c) {
1872: if (stringBufferTop == stringBuffer.length) {
1873: char[] tmp = new char[stringBuffer.length * 2];
1874: System.arraycopy(stringBuffer, 0, tmp, 0, stringBufferTop);
1875: stringBuffer = tmp;
1876: }
1877: stringBuffer[stringBufferTop++] = (char) c;
1878: }
1879:
1880: public void reportSyntaxError(String messageProperty, Object[] args) {
1881: String message = Context.getMessage(messageProperty, args);
1882:
1883: Context.reportError(message, getSourceName(), getLineno(),
1884: getLine(), getOffset());
1885: }
1886:
1887: private void reportSyntaxWarning(String messageProperty,
1888: Object[] args) {
1889: String message = Context.getMessage(messageProperty, args);
1890: Context.reportWarning(message, getSourceName(), getLineno(),
1891: getLine(), getOffset());
1892: }
1893:
1894: public String getSourceName() {
1895: return sourceName;
1896: }
1897:
1898: public int getLineno() {
1899: return in.getLineno();
1900: }
1901:
1902: public int getOp() {
1903: return op;
1904: }
1905:
1906: public String getString() {
1907: return string;
1908: }
1909:
1910: public double getNumber() {
1911: return number;
1912: }
1913:
1914: public String getLine() {
1915: return in.getLine();
1916: }
1917:
1918: public int getOffset() {
1919: return in.getOffset();
1920: }
1921:
1922: public int getTokenno() {
1923: return tokenno;
1924: }
1925:
1926: public boolean eof() {
1927: return in.eof();
1928: }
1929:
1930: // instance variables
1931: private LineBuffer in;
1932:
1933: /* for TSF_REGEXP, etc.
1934: * should this be manipulated by gettor/settor functions?
1935: * should it be passed to getToken();
1936: */
1937: int flags;
1938: String regExpFlags;
1939:
1940: private String sourceName;
1941: private String line;
1942: private int pushbackToken;
1943: private int tokenno;
1944:
1945: private int op;
1946:
1947: // Set this to an inital non-null value so that the Parser has
1948: // something to retrieve even if an error has occured and no
1949: // string is found. Fosters one class of error, but saves lots of
1950: // code.
1951: private String string = "";
1952: private double number;
1953:
1954: private char[] stringBuffer = new char[128];
1955: private int stringBufferTop;
1956: }
|