--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/sun/tools/java/Parser.java Sat Dec 01 00:00:00 2007 +0000
@@ -0,0 +1,2130 @@
+/*
+ * Copyright 1994-2004 Sun Microsystems, Inc. All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.tools.java;
+
+import sun.tools.tree.*;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Enumeration;
+import java.util.Vector;
+
+/**
+ * This class is used to parse Java statements and expressions.
+ * The result is a parse tree.<p>
+ *
+ * This class implements an operator precedence parser. Errors are
+ * reported to the Environment object, if the error can't be
+ * resolved immediately, a SyntaxError exception is thrown.<p>
+ *
+ * Error recovery is implemented by catching SyntaxError exceptions
+ * and discarding input tokens until an input token is reached that
+ * is possibly a legal continuation.<p>
+ *
+ * The parse tree that is constructed represents the input
+ * exactly (no rewrites to simpler forms). This is important
+ * if the resulting tree is to be used for code formatting in
+ * a programming environment. Currently only documentation comments
+ * are retained.<p>
+ *
+ * The parsing algorithm does NOT use any type information. Changes
+ * in the type system do not affect the structure of the parse tree.
+ * This restriction does introduce an ambiguity an expression of the
+ * form: (e1) e2 is assumed to be a cast if e2 does not start with
+ * an operator. That means that (a) - b is interpreted as subtract
+ * b from a and not cast negative b to type a. However, if a is a
+ * simple type (byte, int, ...) then it is assumed to be a cast.<p>
+ *
+ * WARNING: The contents of this source file are not part of any
+ * supported API. Code that depends on them does so at its own risk:
+ * they are subject to change or removal without notice.
+ *
+ * @author Arthur van Hoff
+ */
+
+public
+class Parser extends Scanner implements ParserActions, Constants {
+ /**
+ * Create a parser
+ */
+ protected Parser(Environment env, InputStream in) throws IOException {
+ super(env, in);
+ this.scanner = this;
+ this.actions = this;
+ }
+
+ /**
+ * Create a parser, given a scanner.
+ */
+ protected Parser(Scanner scanner) throws IOException {
+ super(scanner.env);
+ this.scanner = scanner;
+ ((Scanner)this).env = scanner.env;
+ ((Scanner)this).token = scanner.token;
+ ((Scanner)this).pos = scanner.pos;
+ this.actions = this;
+ }
+
+ /**
+ * Create a parser, given a scanner and the semantic callback.
+ */
+ public Parser(Scanner scanner, ParserActions actions) throws IOException {
+ this(scanner);
+ this.actions = actions;
+ }
+
+ /**
+ * Usually <code>this.actions == (ParserActions)this</code>.
+ * However, a delegate scanner can produce tokens for this parser,
+ * in which case <code>(Scanner)this</code> is unused,
+ * except for <code>this.token</code> and <code>this.pos</code>
+ * instance variables which are filled from the real scanner
+ * by <code>this.scan()</code> and the constructor.
+ */
+ ParserActions actions;
+
+ // Note: The duplication of methods allows pre-1.1 classes to
+ // be binary compatible with the new version of the parser,
+ // which now passes IdentifierTokens to the semantics phase,
+ // rather than just Identifiers. This change is necessary,
+ // since the parser is no longer responsible for managing the
+ // resolution of type names. (That caused the "Vector" bug.)
+ //
+ // In a future release, the old "plain-Identifier" methods will
+ // go away, and the corresponding "IdentifierToken" methods
+ // may become abstract.
+
+ /**
+ * package declaration
+ * @deprecated
+ */
+ @Deprecated
+ public void packageDeclaration(long off, IdentifierToken nm) {
+ // By default, call the deprecated version.
+ // Any application must override one of the packageDeclaration methods.
+ packageDeclaration(off, nm.id);
+ }
+ /**
+ * @deprecated
+ */
+ @Deprecated
+ protected void packageDeclaration(long off, Identifier nm) {
+ throw new RuntimeException("beginClass method is abstract");
+ }
+
+ /**
+ * import class
+ * @deprecated
+ */
+ @Deprecated
+ public void importClass(long off, IdentifierToken nm) {
+ // By default, call the deprecated version.
+ // Any application must override one of the packageDeclaration methods.
+ importClass(off, nm.id);
+ }
+ /**
+ * @deprecated Use the version with the IdentifierToken arguments.
+ */
+ @Deprecated
+ protected void importClass(long off, Identifier nm) {
+ throw new RuntimeException("importClass method is abstract");
+ }
+
+ /**
+ * import package
+ * @deprecated
+ */
+ @Deprecated
+ public void importPackage(long off, IdentifierToken nm) {
+ // By default, call the deprecated version.
+ // Any application must override one of the importPackage methods.
+ importPackage(off, nm.id);
+ }
+ /**
+ * @deprecated Use the version with the IdentifierToken arguments.
+ */
+ @Deprecated
+ protected void importPackage(long off, Identifier nm) {
+ throw new RuntimeException("importPackage method is abstract");
+ }
+
+ /**
+ * Define class
+ * @deprecated
+ */
+ @Deprecated
+ public ClassDefinition beginClass(long off, String doc,
+ int mod, IdentifierToken nm,
+ IdentifierToken sup,
+ IdentifierToken impl[]) {
+ // By default, call the deprecated version.
+ // Any application must override one of the beginClass methods.
+ Identifier supId = (sup == null) ? null : sup.id;
+ Identifier implIds[] = null;
+ if (impl != null) {
+ implIds = new Identifier[impl.length];
+ for (int i = 0; i < impl.length; i++) {
+ implIds[i] = impl[i].id;
+ }
+ }
+ beginClass(off, doc, mod, nm.id, supId, implIds);
+ return getCurrentClass();
+ }
+ /**
+ * @deprecated Use the version with the IdentifierToken arguments.
+ */
+ @Deprecated
+ protected void beginClass(long off, String doc, int mod, Identifier nm,
+ Identifier sup, Identifier impl[]) {
+ throw new RuntimeException("beginClass method is abstract");
+ }
+
+ /**
+ * Report the current class under construction.
+ * By default, it's a no-op which returns null.
+ * It may only be called before the corresponding endClass().
+ */
+ protected ClassDefinition getCurrentClass() {
+ return null;
+ }
+
+ /**
+ * End class
+ * @deprecated
+ */
+ @Deprecated
+ public void endClass(long off, ClassDefinition c) {
+ // By default, call the deprecated version.
+ // Any application must override one of the beginClass methods.
+ endClass(off, c.getName().getFlatName().getName());
+ }
+ /**
+ * @deprecated Use the version with the IdentifierToken arguments.
+ */
+ @Deprecated
+ protected void endClass(long off, Identifier nm) {
+ throw new RuntimeException("endClass method is abstract");
+ }
+
+ /**
+ * Define a field
+ * @deprecated
+ */
+ @Deprecated
+ public void defineField(long where, ClassDefinition c,
+ String doc, int mod, Type t,
+ IdentifierToken nm, IdentifierToken args[],
+ IdentifierToken exp[], Node val) {
+ // By default, call the deprecated version.
+ // Any application must override one of the defineField methods.
+ Identifier argIds[] = null;
+ Identifier expIds[] = null;
+ if (args != null) {
+ argIds = new Identifier[args.length];
+ for (int i = 0; i < args.length; i++) {
+ argIds[i] = args[i].id;
+ }
+ }
+ if (exp != null) {
+ expIds = new Identifier[exp.length];
+ for (int i = 0; i < exp.length; i++) {
+ expIds[i] = exp[i].id;
+ }
+ }
+ defineField(where, doc, mod, t, nm.id, argIds, expIds, val);
+ }
+
+ /**
+ * @deprecated Use the version with the IdentifierToken arguments.
+ */
+ @Deprecated
+ protected void defineField(long where, String doc, int mod, Type t,
+ Identifier nm, Identifier args[],
+ Identifier exp[], Node val) {
+ throw new RuntimeException("defineField method is abstract");
+ }
+
+ /*
+ * A growable array of nodes. It is used as a growable
+ * buffer to hold argument lists and expression lists.
+ * I'm not using Vector to make it more efficient.
+ */
+ private Node args[] = new Node[32];
+ protected int argIndex = 0;
+
+ protected final void addArgument(Node n) {
+ if (argIndex == args.length) {
+ Node newArgs[] = new Node[args.length * 2];
+ System.arraycopy(args, 0, newArgs, 0, args.length);
+ args = newArgs;
+ }
+ args[argIndex++] = n;
+ }
+ protected final Expression exprArgs(int index)[] {
+ Expression e[] = new Expression[argIndex - index];
+ System.arraycopy(args, index, e, 0, argIndex - index);
+ argIndex = index;
+ return e;
+ }
+ protected final Statement statArgs(int index)[] {
+ Statement s[] = new Statement[argIndex - index];
+ System.arraycopy(args, index, s, 0, argIndex - index);
+ argIndex = index;
+ return s;
+ }
+
+ /**
+ * Expect a token, return its value, scan the next token or
+ * throw an exception.
+ */
+ protected void expect(int t) throws SyntaxError, IOException {
+ if (token != t) {
+ switch (t) {
+ case IDENT:
+ env.error(scanner.prevPos, "identifier.expected");
+ break;
+ default:
+ env.error(scanner.prevPos, "token.expected", opNames[t]);
+ break;
+ }
+ throw new SyntaxError();
+ }
+ scan();
+ }
+
+ /**
+ * Parse a type expression. Does not parse the []'s.
+ */
+ protected Expression parseTypeExpression() throws SyntaxError, IOException {
+ switch (token) {
+ case VOID:
+ return new TypeExpression(scan(), Type.tVoid);
+ case BOOLEAN:
+ return new TypeExpression(scan(), Type.tBoolean);
+ case BYTE:
+ return new TypeExpression(scan(), Type.tByte);
+ case CHAR:
+ return new TypeExpression(scan(), Type.tChar);
+ case SHORT:
+ return new TypeExpression(scan(), Type.tShort);
+ case INT:
+ return new TypeExpression(scan(), Type.tInt);
+ case LONG:
+ return new TypeExpression(scan(), Type.tLong);
+ case FLOAT:
+ return new TypeExpression(scan(), Type.tFloat);
+ case DOUBLE:
+ return new TypeExpression(scan(), Type.tDouble);
+ case IDENT:
+ Expression e = new IdentifierExpression(pos, scanner.idValue);
+ scan();
+ while (token == FIELD) {
+ e = new FieldExpression(scan(), e, scanner.idValue);
+ expect(IDENT);
+ }
+ return e;
+ }
+
+ env.error(pos, "type.expected");
+ throw new SyntaxError();
+ }
+
+ /**
+ * Parse a method invocation. Should be called when the current
+ * then is the '(' of the argument list.
+ */
+ protected Expression parseMethodExpression(Expression e, Identifier id) throws SyntaxError, IOException {
+ long p = scan();
+ int i = argIndex;
+ if (token != RPAREN) {
+ addArgument(parseExpression());
+ while (token == COMMA) {
+ scan();
+ addArgument(parseExpression());
+ }
+ }
+ expect(RPAREN);
+ return new MethodExpression(p, e, id, exprArgs(i));
+ }
+
+ /**
+ * Parse a new instance expression. Should be called when the current
+ * token is the '(' of the argument list.
+ */
+ protected Expression parseNewInstanceExpression(long p, Expression outerArg, Expression type) throws SyntaxError, IOException {
+ int i = argIndex;
+ expect(LPAREN);
+ if (token != RPAREN) {
+ addArgument(parseExpression());
+ while (token == COMMA) {
+ scan();
+ addArgument(parseExpression());
+ }
+ }
+ expect(RPAREN);
+ ClassDefinition body = null;
+ if (token == LBRACE && !(type instanceof TypeExpression)) {
+ long tp = pos;
+ // x = new Type(arg) { subclass body ... }
+ Identifier superName = FieldExpression.toIdentifier(type);
+ if (superName == null) {
+ env.error(type.getWhere(), "type.expected");
+ }
+ Vector ext = new Vector(1);
+ Vector impl = new Vector(0);
+ ext.addElement(new IdentifierToken(idNull));
+ if (token == IMPLEMENTS || token == EXTENDS) {
+ env.error(pos, "anonymous.extends");
+ parseInheritance(ext, impl); // error recovery
+ }
+ body = parseClassBody(new IdentifierToken(tp, idNull),
+ M_ANONYMOUS | M_LOCAL, EXPR, null,
+ ext, impl, type.getWhere());
+ }
+ if (outerArg == null && body == null) {
+ return new NewInstanceExpression(p, type, exprArgs(i));
+ }
+ return new NewInstanceExpression(p, type, exprArgs(i), outerArg, body);
+ }
+
+ /**
+ * Parse a primary expression.
+ */
+ protected Expression parseTerm() throws SyntaxError, IOException {
+ switch (token) {
+ case CHARVAL: {
+ char v = scanner.charValue;
+ return new CharExpression(scan(), v);
+ }
+ case INTVAL: {
+ int v = scanner.intValue;
+ long q = scan();
+ if (v < 0 && radix == 10) env.error(q, "overflow.int.dec");
+ return new IntExpression(q, v);
+ }
+ case LONGVAL: {
+ long v = scanner.longValue;
+ long q = scan();
+ if (v < 0 && radix == 10) env.error(q, "overflow.long.dec");
+ return new LongExpression(q, v);
+ }
+ case FLOATVAL: {
+ float v = scanner.floatValue;
+ return new FloatExpression(scan(), v);
+ }
+ case DOUBLEVAL: {
+ double v = scanner.doubleValue;
+ return new DoubleExpression(scan(), v);
+ }
+ case STRINGVAL: {
+ String v = scanner.stringValue;
+ return new StringExpression(scan(), v);
+ }
+ case IDENT: {
+ Identifier v = scanner.idValue;
+ long p = scan();
+ return (token == LPAREN) ?
+ parseMethodExpression(null, v) : new IdentifierExpression(p, v);
+ }
+
+ case TRUE:
+ return new BooleanExpression(scan(), true);
+ case FALSE:
+ return new BooleanExpression(scan(), false);
+ case NULL:
+ return new NullExpression(scan());
+
+ case THIS: {
+ Expression e = new ThisExpression(scan());
+ return (token == LPAREN) ? parseMethodExpression(e, idInit) : e;
+ }
+ case SUPER: {
+ Expression e = new SuperExpression(scan());
+ return (token == LPAREN) ? parseMethodExpression(e, idInit) : e;
+ }
+
+ case VOID:
+ case BOOLEAN:
+ case BYTE:
+ case CHAR:
+ case SHORT:
+ case INT:
+ case LONG:
+ case FLOAT:
+ case DOUBLE:
+ return parseTypeExpression();
+
+ case ADD: {
+ long p = scan();
+ switch (token) {
+ case INTVAL: {
+ int v = scanner.intValue;
+ long q = scan();
+ if (v < 0 && radix == 10) env.error(q, "overflow.int.dec");
+ return new IntExpression(q, v);
+ }
+ case LONGVAL: {
+ long v = scanner.longValue;
+ long q = scan();
+ if (v < 0 && radix == 10) env.error(q, "overflow.long.dec");
+ return new LongExpression(q, v);
+ }
+ case FLOATVAL: {
+ float v = scanner.floatValue;
+ return new FloatExpression(scan(), v);
+ }
+ case DOUBLEVAL: {
+ double v = scanner.doubleValue;
+ return new DoubleExpression(scan(), v);
+ }
+ }
+ return new PositiveExpression(p, parseTerm());
+ }
+ case SUB: {
+ long p = scan();
+ switch (token) {
+ case INTVAL: {
+ int v = -scanner.intValue;
+ return new IntExpression(scan(), v);
+ }
+ case LONGVAL: {
+ long v = -scanner.longValue;
+ return new LongExpression(scan(), v);
+ }
+ case FLOATVAL: {
+ float v = -scanner.floatValue;
+ return new FloatExpression(scan(), v);
+ }
+ case DOUBLEVAL: {
+ double v = -scanner.doubleValue;
+ return new DoubleExpression(scan(), v);
+ }
+ }
+ return new NegativeExpression(p, parseTerm());
+ }
+ case NOT:
+ return new NotExpression(scan(), parseTerm());
+ case BITNOT:
+ return new BitNotExpression(scan(), parseTerm());
+ case INC:
+ return new PreIncExpression(scan(), parseTerm());
+ case DEC:
+ return new PreDecExpression(scan(), parseTerm());
+
+ case LPAREN: {
+ // bracketed-expr: (expr)
+ long p = scan();
+ Expression e = parseExpression();
+ expect(RPAREN);
+
+ if (e.getOp() == TYPE) {
+ // cast-expr: (simple-type) expr
+ return new CastExpression(p, e, parseTerm());
+ }
+
+ switch (token) {
+
+ // We handle INC and DEC specially.
+ // See the discussion in JLS section 15.14.1.
+ // (Part of fix for 4044502.)
+
+ case INC:
+ // We know this must be a postfix increment.
+ return new PostIncExpression(scan(), e);
+
+ case DEC:
+ // We know this must be a postfix decrement.
+ return new PostDecExpression(scan(), e);
+
+ case LPAREN:
+ case CHARVAL:
+ case INTVAL:
+ case LONGVAL:
+ case FLOATVAL:
+ case DOUBLEVAL:
+ case STRINGVAL:
+ case IDENT:
+ case TRUE:
+ case FALSE:
+ case NOT:
+ case BITNOT:
+ case THIS:
+ case SUPER:
+ case NULL:
+ case NEW:
+ // cast-expr: (expr) expr
+ return new CastExpression(p, e, parseTerm());
+ }
+ return new ExprExpression(p, e);
+ }
+
+ case LBRACE: {
+ // array initializer: {expr1, expr2, ... exprn}
+ long p = scan();
+ int i = argIndex;
+ if (token != RBRACE) {
+ addArgument(parseExpression());
+ while (token == COMMA) {
+ scan();
+ if (token == RBRACE) {
+ break;
+ }
+ addArgument(parseExpression());
+ }
+ }
+ expect(RBRACE);
+ return new ArrayExpression(p, exprArgs(i));
+ }
+
+ case NEW: {
+ long p = scan();
+ int i = argIndex;
+
+ if (token == LPAREN) {
+ scan();
+ Expression e = parseExpression();
+ expect(RPAREN);
+ env.error(p, "not.supported", "new(...)");
+ return new NullExpression(p);
+ }
+
+ Expression e = parseTypeExpression();
+
+ if (token == LSQBRACKET) {
+ while (token == LSQBRACKET) {
+ scan();
+ addArgument((token != RSQBRACKET) ? parseExpression() : null);
+ expect(RSQBRACKET);
+ }
+ Expression[] dims = exprArgs(i);
+ if (token == LBRACE) {
+ return new NewArrayExpression(p, e, dims, parseTerm());
+ }
+ return new NewArrayExpression(p, e, dims);
+ } else {
+ return parseNewInstanceExpression(p, null, e);
+ }
+ }
+ }
+
+ // System.err.println("NEAR: " + opNames[token]);
+ env.error(scanner.prevPos, "missing.term");
+ return new IntExpression(pos, 0);
+ }
+
+ /**
+ * Parse an expression.
+ */
+ protected Expression parseExpression() throws SyntaxError, IOException {
+ for (Expression e = parseTerm() ; e != null ; e = e.order()) {
+ Expression more = parseBinaryExpression(e);
+ if (more == null)
+ return e;
+ e = more;
+ }
+ // this return is bogus
+ return null;
+ }
+
+ /**
+ * Given a left-hand term, parse an operator and right-hand term.
+ */
+ protected Expression parseBinaryExpression(Expression e) throws SyntaxError, IOException {
+ if (e != null) {
+ switch (token) {
+ case LSQBRACKET: {
+ // index: expr1[expr2]
+ long p = scan();
+ Expression index = (token != RSQBRACKET) ? parseExpression() : null;
+ expect(RSQBRACKET);
+ e = new ArrayAccessExpression(p, e, index);
+ break;
+ }
+
+ case INC:
+ e = new PostIncExpression(scan(), e);
+ break;
+ case DEC:
+ e = new PostDecExpression(scan(), e);
+ break;
+ case FIELD: {
+ long p = scan();
+ if (token == THIS) {
+ // class C { class N { ... C.this ... } }
+ // class C { class N { N(C c){ ... c.this() ... } } }
+ long q = scan();
+ if (token == LPAREN) {
+ e = new ThisExpression(q, e);
+ e = parseMethodExpression(e, idInit);
+ } else {
+ e = new FieldExpression(p, e, idThis);
+ }
+ break;
+ }
+ if (token == SUPER) {
+ // class D extends C.N { D(C.N n) { n.super(); } }
+ // Also, 'C.super', as in:
+ // class C extends CS { class N { ... C.super.foo ... } }
+ // class C extends CS { class N { ... C.super.foo() ... } }
+ long q = scan();
+ if (token == LPAREN) {
+ e = new SuperExpression(q, e);
+ e = parseMethodExpression(e, idInit);
+ } else {
+ // We must check elsewhere that this expression
+ // does not stand alone, but qualifies a member name.
+ e = new FieldExpression(p, e, idSuper);
+ }
+ break;
+ }
+ if (token == NEW) {
+ // new C().new N()
+ scan();
+ if (token != IDENT)
+ expect(IDENT);
+ e = parseNewInstanceExpression(p, e, parseTypeExpression());
+ break;
+ }
+ if (token == CLASS) {
+ // just class literals, really
+ // Class c = C.class;
+ scan();
+ e = new FieldExpression(p, e, idClass);
+ break;
+ }
+ Identifier id = scanner.idValue;
+ expect(IDENT);
+ if (token == LPAREN) {
+ e = parseMethodExpression(e, id);
+ } else {
+ e = new FieldExpression(p, e, id);
+ }
+ break;
+ }
+ case INSTANCEOF:
+ e = new InstanceOfExpression(scan(), e, parseTerm());
+ break;
+ case ADD:
+ e = new AddExpression(scan(), e, parseTerm());
+ break;
+ case SUB:
+ e = new SubtractExpression(scan(), e, parseTerm());
+ break;
+ case MUL:
+ e = new MultiplyExpression(scan(), e, parseTerm());
+ break;
+ case DIV:
+ e = new DivideExpression(scan(), e, parseTerm());
+ break;
+ case REM:
+ e = new RemainderExpression(scan(), e, parseTerm());
+ break;
+ case LSHIFT:
+ e = new ShiftLeftExpression(scan(), e, parseTerm());
+ break;
+ case RSHIFT:
+ e = new ShiftRightExpression(scan(), e, parseTerm());
+ break;
+ case URSHIFT:
+ e = new UnsignedShiftRightExpression(scan(), e, parseTerm());
+ break;
+ case LT:
+ e = new LessExpression(scan(), e, parseTerm());
+ break;
+ case LE:
+ e = new LessOrEqualExpression(scan(), e, parseTerm());
+ break;
+ case GT:
+ e = new GreaterExpression(scan(), e, parseTerm());
+ break;
+ case GE:
+ e = new GreaterOrEqualExpression(scan(), e, parseTerm());
+ break;
+ case EQ:
+ e = new EqualExpression(scan(), e, parseTerm());
+ break;
+ case NE:
+ e = new NotEqualExpression(scan(), e, parseTerm());
+ break;
+ case BITAND:
+ e = new BitAndExpression(scan(), e, parseTerm());
+ break;
+ case BITXOR:
+ e = new BitXorExpression(scan(), e, parseTerm());
+ break;
+ case BITOR:
+ e = new BitOrExpression(scan(), e, parseTerm());
+ break;
+ case AND:
+ e = new AndExpression(scan(), e, parseTerm());
+ break;
+ case OR:
+ e = new OrExpression(scan(), e, parseTerm());
+ break;
+ case ASSIGN:
+ e = new AssignExpression(scan(), e, parseTerm());
+ break;
+ case ASGMUL:
+ e = new AssignMultiplyExpression(scan(), e, parseTerm());
+ break;
+ case ASGDIV:
+ e = new AssignDivideExpression(scan(), e, parseTerm());
+ break;
+ case ASGREM:
+ e = new AssignRemainderExpression(scan(), e, parseTerm());
+ break;
+ case ASGADD:
+ e = new AssignAddExpression(scan(), e, parseTerm());
+ break;
+ case ASGSUB:
+ e = new AssignSubtractExpression(scan(), e, parseTerm());
+ break;
+ case ASGLSHIFT:
+ e = new AssignShiftLeftExpression(scan(), e, parseTerm());
+ break;
+ case ASGRSHIFT:
+ e = new AssignShiftRightExpression(scan(), e, parseTerm());
+ break;
+ case ASGURSHIFT:
+ e = new AssignUnsignedShiftRightExpression(scan(), e, parseTerm());
+ break;
+ case ASGBITAND:
+ e = new AssignBitAndExpression(scan(), e, parseTerm());
+ break;
+ case ASGBITOR:
+ e = new AssignBitOrExpression(scan(), e, parseTerm());
+ break;
+ case ASGBITXOR:
+ e = new AssignBitXorExpression(scan(), e, parseTerm());
+ break;
+ case QUESTIONMARK: {
+ long p = scan();
+ Expression second = parseExpression();
+ expect(COLON);
+ Expression third = parseExpression();
+
+ // The grammar in the JLS does not allow assignment
+ // expressions as the third part of a ?: expression.
+ // Even though javac has no trouble parsing this,
+ // check for this case and signal an error.
+ // (fix for bug 4092958)
+ if (third instanceof AssignExpression
+ || third instanceof AssignOpExpression) {
+ env.error(third.getWhere(), "assign.in.conditionalexpr");
+ }
+
+ e = new ConditionalExpression(p, e, second, third);
+ break;
+ }
+
+ default:
+ return null; // mark end of binary expressions
+ }
+ }
+ return e; // return more binary expression stuff
+ }
+
+ /**
+ * Recover after a syntax error in a statement. This involves
+ * discarding tokens until EOF or a possible continuation is
+ * encountered.
+ */
+ protected boolean recoverStatement() throws SyntaxError, IOException {
+ while (true) {
+ switch (token) {
+ case EOF:
+ case RBRACE:
+ case LBRACE:
+ case IF:
+ case FOR:
+ case WHILE:
+ case DO:
+ case TRY:
+ case CATCH:
+ case FINALLY:
+ case BREAK:
+ case CONTINUE:
+ case RETURN:
+ // begin of a statement, return
+ return true;
+
+ case VOID:
+ case STATIC:
+ case PUBLIC:
+ case PRIVATE:
+ case SYNCHRONIZED:
+ case INTERFACE:
+ case CLASS:
+ case TRANSIENT:
+ // begin of something outside a statement, panic some more
+ expect(RBRACE);
+ return false;
+
+ case LPAREN:
+ match(LPAREN, RPAREN);
+ scan();
+ break;
+
+ case LSQBRACKET:
+ match(LSQBRACKET, RSQBRACKET);
+ scan();
+ break;
+
+ default:
+ // don't know what to do, skip
+ scan();
+ break;
+ }
+ }
+ }
+
+ /**
+ * Parse declaration, called after the type expression
+ * has been parsed and the current token is IDENT.
+ */
+ protected Statement parseDeclaration(long p, int mod, Expression type) throws SyntaxError, IOException {
+ int i = argIndex;
+ if (token == IDENT) {
+ addArgument(new VarDeclarationStatement(pos, parseExpression()));
+ while (token == COMMA) {
+ scan();
+ addArgument(new VarDeclarationStatement(pos, parseExpression()));
+ }
+ }
+ return new DeclarationStatement(p, mod, type, statArgs(i));
+ }
+
+ /**
+ * Check if an expression is a legal toplevel expression.
+ * Only method, inc, dec, and new expression are allowed.
+ */
+ protected void topLevelExpression(Expression e) {
+ switch (e.getOp()) {
+ case ASSIGN:
+ case ASGMUL:
+ case ASGDIV:
+ case ASGREM:
+ case ASGADD:
+ case ASGSUB:
+ case ASGLSHIFT:
+ case ASGRSHIFT:
+ case ASGURSHIFT:
+ case ASGBITAND:
+ case ASGBITOR:
+ case ASGBITXOR:
+ case PREINC:
+ case PREDEC:
+ case POSTINC:
+ case POSTDEC:
+ case METHOD:
+ case NEWINSTANCE:
+ return;
+ }
+ env.error(e.getWhere(), "invalid.expr");
+ }
+
+ /**
+ * Parse a statement.
+ */
+ protected Statement parseStatement() throws SyntaxError, IOException {
+ switch (token) {
+ case SEMICOLON:
+ return new CompoundStatement(scan(), new Statement[0]);
+
+ case LBRACE:
+ return parseBlockStatement();
+
+ case IF: {
+ // if-statement: if (expr) stat
+ // if-statement: if (expr) stat else stat
+ long p = scan();
+
+ expect(LPAREN);
+ Expression c = parseExpression();
+ expect(RPAREN);
+ Statement t = parseStatement();
+ if (token == ELSE) {
+ scan();
+ return new IfStatement(p, c, t, parseStatement());
+ } else {
+ return new IfStatement(p, c, t, null);
+ }
+ }
+
+ case ELSE: {
+ // else-statement: else stat
+ env.error(scan(), "else.without.if");
+ return parseStatement();
+ }
+
+ case FOR: {
+ // for-statement: for (decl-expr? ; expr? ; expr?) stat
+ long p = scan();
+ Statement init = null;
+ Expression cond = null, inc = null;
+
+ expect(LPAREN);
+ if (token != SEMICOLON) {
+ long p2 = pos;
+ int mod = parseModifiers(M_FINAL);
+ Expression e = parseExpression();
+
+ if (token == IDENT) {
+ init = parseDeclaration(p2, mod, e);
+ } else {
+ if (mod != 0) {
+ expect(IDENT); // should have been a declaration
+ }
+ topLevelExpression(e);
+ while (token == COMMA) {
+ long p3 = scan();
+ Expression e2 = parseExpression();
+ topLevelExpression(e2);
+ e = new CommaExpression(p3, e, e2);
+ }
+ init = new ExpressionStatement(p2, e);
+ }
+ }
+ expect(SEMICOLON);
+ if (token != SEMICOLON) {
+ cond = parseExpression();
+ }
+ expect(SEMICOLON);
+ if (token != RPAREN) {
+ inc = parseExpression();
+ topLevelExpression(inc);
+ while (token == COMMA) {
+ long p2 = scan();
+ Expression e2 = parseExpression();
+ topLevelExpression(e2);
+ inc = new CommaExpression(p2, inc, e2);
+ }
+ }
+ expect(RPAREN);
+ return new ForStatement(p, init, cond, inc, parseStatement());
+ }
+
+ case WHILE: {
+ // while-statement: while (expr) stat
+ long p = scan();
+
+ expect(LPAREN);
+ Expression cond = parseExpression();
+ expect(RPAREN);
+ return new WhileStatement(p, cond, parseStatement());
+ }
+
+ case DO: {
+ // do-statement: do stat while (expr)
+ long p = scan();
+
+ Statement body = parseStatement();
+ expect(WHILE);
+ expect(LPAREN);
+ Expression cond = parseExpression();
+ expect(RPAREN);
+ expect(SEMICOLON);
+ return new DoStatement(p, body, cond);
+ }
+
+ case BREAK: {
+ // break-statement: break ;
+ long p = scan();
+ Identifier label = null;
+
+ if (token == IDENT) {
+ label = scanner.idValue;
+ scan();
+ }
+ expect(SEMICOLON);
+ return new BreakStatement(p, label);
+ }
+
+ case CONTINUE: {
+ // continue-statement: continue ;
+ long p = scan();
+ Identifier label = null;
+
+ if (token == IDENT) {
+ label = scanner.idValue;
+ scan();
+ }
+ expect(SEMICOLON);
+ return new ContinueStatement(p, label);
+ }
+
+ case RETURN: {
+ // return-statement: return ;
+ // return-statement: return expr ;
+ long p = scan();
+ Expression e = null;
+
+ if (token != SEMICOLON) {
+ e = parseExpression();
+ }
+ expect(SEMICOLON);
+ return new ReturnStatement(p, e);
+ }
+
+ case SWITCH: {
+ // switch statement: switch ( expr ) stat
+ long p = scan();
+ int i = argIndex;
+
+ expect(LPAREN);
+ Expression e = parseExpression();
+ expect(RPAREN);
+ expect(LBRACE);
+
+ while ((token != EOF) && (token != RBRACE)) {
+ int j = argIndex;
+ try {
+ switch (token) {
+ case CASE:
+ // case-statement: case expr:
+ addArgument(new CaseStatement(scan(), parseExpression()));
+ expect(COLON);
+ break;
+
+ case DEFAULT:
+ // default-statement: default:
+ addArgument(new CaseStatement(scan(), null));
+ expect(COLON);
+ break;
+
+ default:
+ addArgument(parseStatement());
+ break;
+ }
+ } catch (SyntaxError ee) {
+ argIndex = j;
+ if (!recoverStatement()) {
+ throw ee;
+ }
+ }
+ }
+ expect(RBRACE);
+ return new SwitchStatement(p, e, statArgs(i));
+ }
+
+ case CASE: {
+ // case-statement: case expr : stat
+ env.error(pos, "case.without.switch");
+ while (token == CASE) {
+ scan();
+ parseExpression();
+ expect(COLON);
+ }
+ return parseStatement();
+ }
+
+ case DEFAULT: {
+ // default-statement: default : stat
+ env.error(pos, "default.without.switch");
+ scan();
+ expect(COLON);
+ return parseStatement();
+ }
+
+ case TRY: {
+ // try-statement: try stat catch (type-expr ident) stat finally stat
+ long p = scan();
+ Statement init = null; // try-object specification
+ int i = argIndex;
+ boolean catches = false;
+
+ if (false && token == LPAREN) {
+ expect(LPAREN);
+ long p2 = pos;
+ int mod = parseModifiers(M_FINAL);
+ Expression e = parseExpression();
+
+ if (token == IDENT) {
+ init = parseDeclaration(p2, mod, e);
+ // leave check for try (T x, y) for semantic phase
+ } else {
+ if (mod != 0) {
+ expect(IDENT); // should have been a declaration
+ }
+ init = new ExpressionStatement(p2, e);
+ }
+ expect(RPAREN);
+ }
+
+ Statement s = parseBlockStatement();
+
+ if (init != null) {
+ // s = new FinallyStatement(p, init, s, 0);
+ }
+
+ while (token == CATCH) {
+ long pp = pos;
+ expect(CATCH);
+ expect(LPAREN);
+ int mod = parseModifiers(M_FINAL);
+ Expression t = parseExpression();
+ IdentifierToken id = scanner.getIdToken();
+ expect(IDENT);
+ id.modifiers = mod;
+ // We only catch Throwable's, so this is no longer required
+ // while (token == LSQBRACKET) {
+ // t = new ArrayAccessExpression(scan(), t, null);
+ // expect(RSQBRACKET);
+ // }
+ expect(RPAREN);
+ addArgument(new CatchStatement(pp, t, id, parseBlockStatement()));
+ catches = true;
+ }
+
+ if (catches)
+ s = new TryStatement(p, s, statArgs(i));
+
+ if (token == FINALLY) {
+ scan();
+ return new FinallyStatement(p, s, parseBlockStatement());
+ } else if (catches || init != null) {
+ return s;
+ } else {
+ env.error(pos, "try.without.catch.finally");
+ return new TryStatement(p, s, null);
+ }
+ }
+
+ case CATCH: {
+ // catch-statement: catch (expr ident) stat finally stat
+ env.error(pos, "catch.without.try");
+
+ Statement s;
+ do {
+ scan();
+ expect(LPAREN);
+ parseModifiers(M_FINAL);
+ parseExpression();
+ expect(IDENT);
+ expect(RPAREN);
+ s = parseBlockStatement();
+ } while (token == CATCH);
+
+ if (token == FINALLY) {
+ scan();
+ s = parseBlockStatement();
+ }
+ return s;
+ }
+
+ case FINALLY: {
+ // finally-statement: finally stat
+ env.error(pos, "finally.without.try");
+ scan();
+ return parseBlockStatement();
+ }
+
+ case THROW: {
+ // throw-statement: throw expr;
+ long p = scan();
+ Expression e = parseExpression();
+ expect(SEMICOLON);
+ return new ThrowStatement(p, e);
+ }
+
+ case GOTO: {
+ long p = scan();
+ expect(IDENT);
+ expect(SEMICOLON);
+ env.error(p, "not.supported", "goto");
+ return new CompoundStatement(p, new Statement[0]);
+ }
+
+ case SYNCHRONIZED: {
+ // synchronized-statement: synchronized (expr) stat
+ long p = scan();
+ expect(LPAREN);
+ Expression e = parseExpression();
+ expect(RPAREN);
+ return new SynchronizedStatement(p, e, parseBlockStatement());
+ }
+
+ case INTERFACE:
+ case CLASS:
+ // Inner class.
+ return parseLocalClass(0);
+
+ case CONST:
+ case ABSTRACT:
+ case FINAL:
+ case STRICTFP: {
+ // a declaration of some sort
+ long p = pos;
+
+ // A class which is local to a block is not a member, and so
+ // cannot be public, private, protected, or static. It is in
+ // effect private to the block, since it cannot be used outside
+ // its scope.
+ //
+ // However, any class (if it has a name) can be declared final,
+ // abstract, or strictfp.
+ int mod = parseModifiers(M_FINAL | M_ABSTRACT
+ | M_STRICTFP );
+
+ switch (token) {
+ case INTERFACE:
+ case CLASS:
+ return parseLocalClass(mod);
+
+ case BOOLEAN:
+ case BYTE:
+ case CHAR:
+ case SHORT:
+ case INT:
+ case LONG:
+ case FLOAT:
+ case DOUBLE:
+ case IDENT: {
+ if ((mod & (M_ABSTRACT | M_STRICTFP )) != 0) {
+ mod &= ~ (M_ABSTRACT | M_STRICTFP );
+ expect(CLASS);
+ }
+ Expression e = parseExpression();
+ if (token != IDENT) {
+ expect(IDENT);
+ }
+ // declaration: final expr expr
+ Statement s = parseDeclaration(p, mod, e);
+ expect(SEMICOLON);
+ return s;
+ }
+
+ default:
+ env.error(pos, "type.expected");
+ throw new SyntaxError();
+ }
+ }
+
+ case VOID:
+ case STATIC:
+ case PUBLIC:
+ case PRIVATE:
+ case TRANSIENT:
+ // This is the start of something outside a statement
+ env.error(pos, "statement.expected");
+ throw new SyntaxError();
+ }
+
+ long p = pos;
+ Expression e = parseExpression();
+
+ if (token == IDENT) {
+ // declaration: expr expr
+ Statement s = parseDeclaration(p, 0, e);
+ expect(SEMICOLON);
+ return s;
+ }
+ if (token == COLON) {
+ // label: id: stat
+ scan();
+ Statement s = parseStatement();
+ s.setLabel(env, e);
+ return s;
+ }
+
+ // it was just an expression...
+ topLevelExpression(e);
+ expect(SEMICOLON);
+ return new ExpressionStatement(p, e);
+ }
+
+ protected Statement parseBlockStatement() throws SyntaxError, IOException {
+ // compound statement: { stat1 stat2 ... statn }
+ if (token != LBRACE) {
+ // We're expecting a block statement. But we'll probably do the
+ // least damage if we try to parse a normal statement instead.
+ env.error(scanner.prevPos, "token.expected", opNames[LBRACE]);
+ return parseStatement();
+ }
+ long p = scan();
+ int i = argIndex;
+ while ((token != EOF) && (token != RBRACE)) {
+ int j = argIndex;
+ try {
+ addArgument(parseStatement());
+ } catch (SyntaxError e) {
+ argIndex = j;
+ if (!recoverStatement()) {
+ throw e;
+ }
+ }
+ }
+
+ expect(RBRACE);
+ return new CompoundStatement(p, statArgs(i));
+ }
+
+
+ /**
+ * Parse an identifier. ie: a.b.c returns "a.b.c"
+ * If star is true then "a.b.*" is allowed.
+ * The return value encodes both the identifier and its location.
+ */
+ protected IdentifierToken parseName(boolean star) throws SyntaxError, IOException {
+ IdentifierToken res = scanner.getIdToken();
+ expect(IDENT);
+
+ if (token != FIELD) {
+ return res;
+ }
+
+ StringBuffer buf = new StringBuffer(res.id.toString());
+
+ while (token == FIELD) {
+ scan();
+ if ((token == MUL) && star) {
+ scan();
+ buf.append(".*");
+ break;
+ }
+
+ buf.append('.');
+ if (token == IDENT) {
+ buf.append(scanner.idValue);
+ }
+ expect(IDENT);
+ }
+
+ res.id = Identifier.lookup(buf.toString());
+ return res;
+ }
+ /**
+ * @deprecated
+ * @see #parseName
+ */
+ @Deprecated
+ protected Identifier parseIdentifier(boolean star) throws SyntaxError, IOException {
+ return parseName(star).id;
+ }
+
+ /**
+ * Parse a type expression, this results in a Type.
+ * The parse includes trailing array brackets.
+ */
+ protected Type parseType() throws SyntaxError, IOException {
+ Type t;
+
+ switch (token) {
+ case IDENT:
+ t = Type.tClass(parseName(false).id);
+ break;
+ case VOID:
+ scan();
+ t = Type.tVoid;
+ break;
+ case BOOLEAN:
+ scan();
+ t = Type.tBoolean;
+ break;
+ case BYTE:
+ scan();
+ t = Type.tByte;
+ break;
+ case CHAR:
+ scan();
+ t = Type.tChar;
+ break;
+ case SHORT:
+ scan();
+ t = Type.tShort;
+ break;
+ case INT:
+ scan();
+ t = Type.tInt;
+ break;
+ case FLOAT:
+ scan();
+ t = Type.tFloat;
+ break;
+ case LONG:
+ scan();
+ t = Type.tLong;
+ break;
+ case DOUBLE:
+ scan();
+ t = Type.tDouble;
+ break;
+ default:
+ env.error(pos, "type.expected");
+ throw new SyntaxError();
+ }
+ return parseArrayBrackets(t);
+ }
+
+ /**
+ * Parse the tail of a type expression, which might be array brackets.
+ * Return the given type, as possibly modified by the suffix.
+ */
+ protected Type parseArrayBrackets(Type t) throws SyntaxError, IOException {
+
+ // Parse []'s
+ while (token == LSQBRACKET) {
+ scan();
+ if (token != RSQBRACKET) {
+ env.error(pos, "array.dim.in.decl");
+ parseExpression();
+ }
+ expect(RSQBRACKET);
+ t = Type.tArray(t);
+ }
+ return t;
+ }
+
+ /*
+ * Dealing with argument lists, I'm not using
+ * Vector for efficiency.
+ */
+
+ private int aCount = 0;
+ private Type aTypes[] = new Type[8];
+ private IdentifierToken aNames[] = new IdentifierToken[aTypes.length];
+
+ private void addArgument(int mod, Type t, IdentifierToken nm) {
+ nm.modifiers = mod;
+ if (aCount >= aTypes.length) {
+ Type newATypes[] = new Type[aCount * 2];
+ System.arraycopy(aTypes, 0, newATypes, 0, aCount);
+ aTypes = newATypes;
+ IdentifierToken newANames[] = new IdentifierToken[aCount * 2];
+ System.arraycopy(aNames, 0, newANames, 0, aCount);
+ aNames = newANames;
+ }
+ aTypes[aCount] = t;
+ aNames[aCount++] = nm;
+ }
+
+ /**
+ * Parse a possibly-empty sequence of modifier keywords.
+ * Return the resulting bitmask.
+ * Diagnose repeated modifiers, but make no other checks.
+ * Only modifiers mentioned in the given bitmask are scanned;
+ * an unmatched modifier must be handled by the caller.
+ */
+ protected int parseModifiers(int mask) throws IOException {
+ int mod = 0;
+ while (true) {
+ if (token==CONST) {
+ // const isn't in java, but handle a common C++ usage gently
+ env.error(pos, "not.supported", "const");
+ scan();
+ }
+ int nextmod = 0;
+ switch (token) {
+ case PRIVATE: nextmod = M_PRIVATE; break;
+ case PUBLIC: nextmod = M_PUBLIC; break;
+ case PROTECTED: nextmod = M_PROTECTED; break;
+ case STATIC: nextmod = M_STATIC; break;
+ case TRANSIENT: nextmod = M_TRANSIENT; break;
+ case FINAL: nextmod = M_FINAL; break;
+ case ABSTRACT: nextmod = M_ABSTRACT; break;
+ case NATIVE: nextmod = M_NATIVE; break;
+ case VOLATILE: nextmod = M_VOLATILE; break;
+ case SYNCHRONIZED: nextmod = M_SYNCHRONIZED; break;
+ case STRICTFP: nextmod = M_STRICTFP; break;
+ }
+ if ((nextmod & mask) == 0) {
+ break;
+ }
+ if ((nextmod & mod) != 0) {
+ env.error(pos, "repeated.modifier");
+ }
+ mod |= nextmod;
+ scan();
+ }
+ return mod;
+ }
+
+ private ClassDefinition curClass;
+
+ /**
+ * Parse a field.
+ */
+ protected void parseField() throws SyntaxError, IOException {
+
+ // Empty fields are not allowed by the JLS but are accepted by
+ // the compiler, and much code has come to rely on this. It has
+ // been decided that the language will be extended to legitimize them.
+ if (token == SEMICOLON) {
+ // empty field
+ scan();
+ return;
+ }
+
+ // Optional doc comment
+ String doc = scanner.docComment;
+
+ // The start of the field
+ long p = pos;
+
+ // Parse the modifiers
+ int mod = parseModifiers(MM_FIELD | MM_METHOD);
+
+ // Check for static initializer
+ // ie: static { ... }
+ // or an instance initializer (w/o the static).
+ if ((mod == (mod & M_STATIC)) && (token == LBRACE)) {
+ // static initializer
+ actions.defineField(p, curClass, doc, mod,
+ Type.tMethod(Type.tVoid),
+ new IdentifierToken(idClassInit), null, null,
+ parseStatement());
+ return;
+ }
+
+ // Check for inner class
+ if (token == CLASS || token == INTERFACE) {
+ parseNamedClass(mod, CLASS, doc);
+ return;
+ }
+
+ // Parse the type
+ p = pos;
+ Type t = parseType();
+ IdentifierToken id = null;
+
+ // Check that the type is followed by an Identifier
+ // (the name of the method or the first variable),
+ // otherwise it is a constructor.
+ switch (token) {
+ case IDENT:
+ id = scanner.getIdToken();
+ p = scan();
+ break;
+
+ case LPAREN:
+ // It is a constructor
+ id = new IdentifierToken(idInit);
+ if ((mod & M_STRICTFP) != 0)
+ env.error(pos, "bad.constructor.modifier");
+ break;
+
+ default:
+ expect(IDENT);
+ }
+
+ // If the next token is a left-bracket then we
+ // are dealing with a method or constructor, otherwise it is
+ // a list of variables
+ if (token == LPAREN) {
+ // It is a method or constructor declaration
+ scan();
+ aCount = 0;
+
+ if (token != RPAREN) {
+ // Parse argument type and identifier
+ // (arguments (like locals) are allowed to be final)
+ int am = parseModifiers(M_FINAL);
+ Type at = parseType();
+ IdentifierToken an = scanner.getIdToken();
+ expect(IDENT);
+
+ // Parse optional array specifier, ie: a[][]
+ at = parseArrayBrackets(at);
+ addArgument(am, at, an);
+
+ // If the next token is a comma then there are
+ // more arguments
+ while (token == COMMA) {
+ // Parse argument type and identifier
+ scan();
+ am = parseModifiers(M_FINAL);
+ at = parseType();
+ an = scanner.getIdToken();
+ expect(IDENT);
+
+ // Parse optional array specifier, ie: a[][]
+ at = parseArrayBrackets(at);
+ addArgument(am, at, an);
+ }
+ }
+ expect(RPAREN);
+
+ // Parse optional array sepecifier, ie: foo()[][]
+ t = parseArrayBrackets(t);
+
+ // copy arguments
+ Type atypes[] = new Type[aCount];
+ System.arraycopy(aTypes, 0, atypes, 0, aCount);
+
+ IdentifierToken anames[] = new IdentifierToken[aCount];
+ System.arraycopy(aNames, 0, anames, 0, aCount);
+
+ // Construct the type signature
+ t = Type.tMethod(t, atypes);
+
+ // Parse and ignore throws clause
+ IdentifierToken exp[] = null;
+ if (token == THROWS) {
+ Vector v = new Vector();
+ scan();
+ v.addElement(parseName(false));
+ while (token == COMMA) {
+ scan();
+ v.addElement(parseName(false));
+ }
+
+ exp = new IdentifierToken[v.size()];
+ v.copyInto(exp);
+ }
+
+ // Check if it is a method definition or a method declaration
+ // ie: foo() {...} or foo();
+ switch (token) {
+ case LBRACE: // It's a method definition
+
+ // Set the state of FP strictness for the body of the method
+ int oldFPstate = FPstate;
+ if ((mod & M_STRICTFP)!=0) {
+ FPstate = M_STRICTFP;
+ } else {
+ mod |= FPstate & M_STRICTFP;
+ }
+
+ actions.defineField(p, curClass, doc, mod, t, id,
+ anames, exp, parseStatement());
+
+ FPstate = oldFPstate;
+
+ break;
+
+ case SEMICOLON:
+ scan();
+ actions.defineField(p, curClass, doc, mod, t, id,
+ anames, exp, null);
+ break;
+
+ default:
+ // really expected a statement body here
+ if ((mod & (M_NATIVE | M_ABSTRACT)) == 0) {
+ expect(LBRACE);
+ } else {
+ expect(SEMICOLON);
+ }
+ }
+ return;
+ }
+
+ // It is a list of instance variables
+ while (true) {
+ p = pos; // get the current position
+ // parse the array brackets (if any)
+ // ie: var[][][]
+ Type vt = parseArrayBrackets(t);
+
+ // Parse the optional initializer
+ Node init = null;
+ if (token == ASSIGN) {
+ scan();
+ init = parseExpression();
+ }
+
+ // Define the variable
+ actions.defineField(p, curClass, doc, mod, vt, id,
+ null, null, init);
+
+ // If the next token is a comma, then there is more
+ if (token != COMMA) {
+ expect(SEMICOLON);
+ return;
+ }
+ scan();
+
+ // The next token must be an identifier
+ id = scanner.getIdToken();
+ expect(IDENT);
+ }
+ }
+
+ /**
+ * Recover after a syntax error in a field. This involves
+ * discarding tokens until an EOF or a possible legal
+ * continuation is encountered.
+ */
+ protected void recoverField(ClassDefinition newClass) throws SyntaxError, IOException {
+ while (true) {
+ switch (token) {
+ case EOF:
+ case STATIC:
+ case FINAL:
+ case PUBLIC:
+ case PRIVATE:
+ case SYNCHRONIZED:
+ case TRANSIENT:
+
+ case VOID:
+ case BOOLEAN:
+ case BYTE:
+ case CHAR:
+ case SHORT:
+ case INT:
+ case FLOAT:
+ case LONG:
+ case DOUBLE:
+ // possible begin of a field, continue
+ return;
+
+ case LBRACE:
+ match(LBRACE, RBRACE);
+ scan();
+ break;
+
+ case LPAREN:
+ match(LPAREN, RPAREN);
+ scan();
+ break;
+
+ case LSQBRACKET:
+ match(LSQBRACKET, RSQBRACKET);
+ scan();
+ break;
+
+ case RBRACE:
+ case INTERFACE:
+ case CLASS:
+ case IMPORT:
+ case PACKAGE:
+ // begin of something outside a class, panic more
+ actions.endClass(pos, newClass);
+ throw new SyntaxError();
+
+ default:
+ // don't know what to do, skip
+ scan();
+ break;
+ }
+ }
+ }
+
+ /**
+ * Parse a top-level class or interface declaration.
+ */
+ protected void parseClass() throws SyntaxError, IOException {
+ String doc = scanner.docComment;
+
+ // Parse the modifiers.
+ int mod = parseModifiers(MM_CLASS | MM_MEMBER);
+
+ parseNamedClass(mod, PACKAGE, doc);
+ }
+
+ // Current strict/default state of floating point. This is
+ // set and reset with a stack discipline around methods and named
+ // classes. Only M_STRICTFP may be set in this word. try...
+ // finally is not needed to protect setting and resetting because
+ // there are no error messages based on FPstate.
+ private int FPstate = 0;
+
+ /**
+ * Parse a block-local class or interface declaration.
+ */
+ protected Statement parseLocalClass(int mod) throws SyntaxError, IOException {
+ long p = pos;
+ ClassDefinition body = parseNamedClass(M_LOCAL | mod, STAT, null);
+ Statement ds[] = {
+ new VarDeclarationStatement(p, new LocalMember(body), null)
+ };
+ Expression type = new TypeExpression(p, body.getType());
+ return new DeclarationStatement(p, 0, type, ds);
+ }
+
+ /**
+ * Parse a named class or interface declaration,
+ * starting at "class" or "interface".
+ * @arg ctx Syntactic context of the class, one of {PACKAGE CLASS STAT EXPR}.
+ */
+ protected ClassDefinition parseNamedClass(int mod, int ctx, String doc) throws SyntaxError, IOException {
+ // Parse class/interface
+ switch (token) {
+ case INTERFACE:
+ scan();
+ mod |= M_INTERFACE;
+ break;
+
+ case CLASS:
+ scan();
+ break;
+
+ default:
+ env.error(pos, "class.expected");
+ break;
+ }
+
+ int oldFPstate = FPstate;
+ if ((mod & M_STRICTFP)!=0) {
+ FPstate = M_STRICTFP;
+ } else {
+ // The & (...) isn't really necessary here because we do maintain
+ // the invariant that FPstate has no extra bits set.
+ mod |= FPstate & M_STRICTFP;
+ }
+
+ // Parse the class name
+ IdentifierToken nm = scanner.getIdToken();
+ long p = pos;
+ expect(IDENT);
+
+ Vector ext = new Vector();
+ Vector impl = new Vector();
+ parseInheritance(ext, impl);
+
+ ClassDefinition tmp = parseClassBody(nm, mod, ctx, doc, ext, impl, p);
+
+ FPstate = oldFPstate;
+
+ return tmp;
+ }
+
+ protected void parseInheritance(Vector ext, Vector impl) throws SyntaxError, IOException {
+ // Parse extends clause
+ if (token == EXTENDS) {
+ scan();
+ ext.addElement(parseName(false));
+ while (token == COMMA) {
+ scan();
+ ext.addElement(parseName(false));
+ }
+ }
+
+ // Parse implements clause
+ if (token == IMPLEMENTS) {
+ scan();
+ impl.addElement(parseName(false));
+ while (token == COMMA) {
+ scan();
+ impl.addElement(parseName(false));
+ }
+ }
+ }
+
+ /**
+ * Parse the body of a class or interface declaration,
+ * starting at the left brace.
+ */
+ protected ClassDefinition parseClassBody(IdentifierToken nm, int mod,
+ int ctx, String doc,
+ Vector ext, Vector impl, long p
+ ) throws SyntaxError, IOException {
+ // Decide which is the super class
+ IdentifierToken sup = null;
+ if ((mod & M_INTERFACE) != 0) {
+ if (impl.size() > 0) {
+ env.error(((IdentifierToken)impl.elementAt(0)).getWhere(),
+ "intf.impl.intf");
+ }
+ impl = ext;
+ } else {
+ if (ext.size() > 0) {
+ if (ext.size() > 1) {
+ env.error(((IdentifierToken)ext.elementAt(1)).getWhere(),
+ "multiple.inherit");
+ }
+ sup = (IdentifierToken)ext.elementAt(0);
+ }
+ }
+
+ ClassDefinition oldClass = curClass;
+
+ // Begin a new class
+ IdentifierToken implids[] = new IdentifierToken[impl.size()];
+ impl.copyInto(implids);
+ ClassDefinition newClass =
+ actions.beginClass(p, doc, mod, nm, sup, implids);
+
+ // Parse fields
+ expect(LBRACE);
+ while ((token != EOF) && (token != RBRACE)) {
+ try {
+ curClass = newClass;
+ parseField();
+ } catch (SyntaxError e) {
+ recoverField(newClass);
+ } finally {
+ curClass = oldClass;
+ }
+ }
+ expect(RBRACE);
+
+ // End the class
+ actions.endClass(scanner.prevPos, newClass);
+ return newClass;
+ }
+
+ /**
+ * Recover after a syntax error in the file.
+ * This involves discarding tokens until an EOF
+ * or a possible legal continuation is encountered.
+ */
+ protected void recoverFile() throws IOException {
+ while (true) {
+ switch (token) {
+ case CLASS:
+ case INTERFACE:
+ // Start of a new source file statement, continue
+ return;
+
+ case LBRACE:
+ match(LBRACE, RBRACE);
+ scan();
+ break;
+
+ case LPAREN:
+ match(LPAREN, RPAREN);
+ scan();
+ break;
+
+ case LSQBRACKET:
+ match(LSQBRACKET, RSQBRACKET);
+ scan();
+ break;
+
+ case EOF:
+ return;
+
+ default:
+ // Don't know what to do, skip
+ scan();
+ break;
+ }
+ }
+ }
+
+ /**
+ * Parse an Java file.
+ */
+ public void parseFile() {
+ try {
+ try {
+ if (token == PACKAGE) {
+ // Package statement
+ long p = scan();
+ IdentifierToken id = parseName(false);
+ expect(SEMICOLON);
+ actions.packageDeclaration(p, id);
+ }
+ } catch (SyntaxError e) {
+ recoverFile();
+ }
+ while (token == IMPORT) {
+ try{
+ // Import statement
+ long p = scan();
+ IdentifierToken id = parseName(true);
+ expect(SEMICOLON);
+ if (id.id.getName().equals(idStar)) {
+ id.id = id.id.getQualifier();
+ actions.importPackage(p, id);
+ } else {
+ actions.importClass(p, id);
+ }
+ } catch (SyntaxError e) {
+ recoverFile();
+ }
+ }
+
+ while (token != EOF) {
+ try {
+ switch (token) {
+ case FINAL:
+ case PUBLIC:
+ case PRIVATE:
+ case ABSTRACT:
+ case CLASS:
+ case INTERFACE:
+ case STRICTFP:
+ // Start of a class
+ parseClass();
+ break;
+
+ case SEMICOLON:
+ // Bogus semicolon.
+ // According to the JLS (7.6,19.6), a TypeDeclaration
+ // may consist of a single semicolon, however, this
+ // usage is discouraged (JLS 7.6). In contrast,
+ // a FieldDeclaration may not be empty, and is flagged
+ // as an error. See parseField above.
+ scan();
+ break;
+
+ case EOF:
+ // The end
+ return;
+
+ default:
+ // Oops
+ env.error(pos, "toplevel.expected");
+ throw new SyntaxError();
+ }
+ } catch (SyntaxError e) {
+ recoverFile();
+ }
+ }
+ } catch (IOException e) {
+ env.error(pos, "io.exception", env.getSource());
+ return;
+ }
+ }
+
+ /**
+ * Usually <code>this.scanner == (Scanner)this</code>.
+ * However, a delegate scanner can produce tokens for this parser,
+ * in which case <code>(Scanner)this</code> is unused,
+ * except for <code>this.token</code> and <code>this.pos</code>
+ * instance variables which are filled from the real scanner
+ * by <code>this.scan()</code> and the constructor.
+ */
+ protected Scanner scanner;
+
+ // Design Note: We ought to disinherit Parser from Scanner.
+ // We also should split out the interface ParserActions from
+ // Parser, and make BatchParser implement ParserActions,
+ // not extend Parser. This would split scanning, parsing,
+ // and class building into distinct responsibility areas.
+ // (Perhaps tree building could be virtualized too.)
+
+ public long scan() throws IOException {
+ if (scanner != this && scanner != null) {
+ long result = scanner.scan();
+ ((Scanner)this).token = scanner.token;
+ ((Scanner)this).pos = scanner.pos;
+ return result;
+ }
+ return super.scan();
+ }
+
+ public void match(int open, int close) throws IOException {
+ if (scanner != this) {
+ scanner.match(open, close);
+ ((Scanner)this).token = scanner.token;
+ ((Scanner)this).pos = scanner.pos;
+ return;
+ }
+ super.match(open, close);
+ }
+}