jdk/src/share/classes/sun/tools/java/Parser.java
changeset 2 90ce3da70b43
child 5506 202f599c92aa
--- /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);
+    }
+}