src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavacParser.java
changeset 47216 71c04702a3d5
parent 45915 1afba647cd44
child 47234 1b1618daf237
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavacParser.java	Tue Sep 12 19:03:39 2017 +0200
@@ -0,0 +1,4341 @@
+/*
+ * Copyright (c) 1999, 2017, Oracle and/or its affiliates. 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package com.sun.tools.javac.parser;
+
+import java.util.*;
+import java.util.stream.Collectors;
+
+import com.sun.source.tree.MemberReferenceTree.ReferenceMode;
+import com.sun.source.tree.ModuleTree.ModuleKind;
+
+import com.sun.tools.javac.code.*;
+import com.sun.tools.javac.parser.Tokens.*;
+import com.sun.tools.javac.parser.Tokens.Comment.CommentStyle;
+import com.sun.tools.javac.resources.CompilerProperties;
+import com.sun.tools.javac.resources.CompilerProperties.Errors;
+import com.sun.tools.javac.tree.*;
+import com.sun.tools.javac.tree.JCTree.*;
+import com.sun.tools.javac.util.*;
+import com.sun.tools.javac.util.JCDiagnostic.DiagnosticFlag;
+import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition;
+import com.sun.tools.javac.util.List;
+
+import static com.sun.tools.javac.parser.Tokens.TokenKind.*;
+import static com.sun.tools.javac.parser.Tokens.TokenKind.ASSERT;
+import static com.sun.tools.javac.parser.Tokens.TokenKind.CASE;
+import static com.sun.tools.javac.parser.Tokens.TokenKind.CATCH;
+import static com.sun.tools.javac.parser.Tokens.TokenKind.EQ;
+import static com.sun.tools.javac.parser.Tokens.TokenKind.GT;
+import static com.sun.tools.javac.parser.Tokens.TokenKind.IMPORT;
+import static com.sun.tools.javac.parser.Tokens.TokenKind.LT;
+import static com.sun.tools.javac.tree.JCTree.Tag.*;
+
+/** The parser maps a token sequence into an abstract syntax
+ *  tree. It operates by recursive descent, with code derived
+ *  systematically from an LL(1) grammar. For efficiency reasons, an
+ *  operator precedence scheme is used for parsing binary operation
+ *  expressions.
+ *
+ *  <p><b>This is NOT part of any supported API.
+ *  If you write code that depends on this, you do so at your own risk.
+ *  This code and its internal interfaces are subject to change or
+ *  deletion without notice.</b>
+ */
+public class JavacParser implements Parser {
+
+    /** The number of precedence levels of infix operators.
+     */
+    private static final int infixPrecedenceLevels = 10;
+
+    /** Is the parser instantiated to parse a module-info file ?
+     */
+    private final boolean parseModuleInfo;
+
+    /** The scanner used for lexical analysis.
+     */
+    protected Lexer S;
+
+    /** The factory to be used for abstract syntax tree construction.
+     */
+    protected TreeMaker F;
+
+    /** The log to be used for error diagnostics.
+     */
+    private Log log;
+
+    /** The Source language setting. */
+    private Source source;
+
+    /** The name table. */
+    private Names names;
+
+    /** End position mappings container */
+    protected final AbstractEndPosTable endPosTable;
+
+    // Because of javac's limited lookahead, some contexts are ambiguous in
+    // the presence of type annotations even though they are not ambiguous
+    // in the absence of type annotations.  Consider this code:
+    //   void m(String [] m) { }
+    //   void m(String ... m) { }
+    // After parsing "String", javac calls bracketsOpt which immediately
+    // returns if the next character is not '['.  Similarly, javac can see
+    // if the next token is ... and in that case parse an ellipsis.  But in
+    // the presence of type annotations:
+    //   void m(String @A [] m) { }
+    //   void m(String @A ... m) { }
+    // no finite lookahead is enough to determine whether to read array
+    // levels or an ellipsis.  Furthermore, if you call bracketsOpt, then
+    // bracketsOpt first reads all the leading annotations and only then
+    // discovers that it needs to fail.  bracketsOpt needs a way to push
+    // back the extra annotations that it read.  (But, bracketsOpt should
+    // not *always* be allowed to push back extra annotations that it finds
+    // -- in most contexts, any such extra annotation is an error.
+    //
+    // The following two variables permit type annotations that have
+    // already been read to be stored for later use.  Alternate
+    // implementations are possible but would cause much larger changes to
+    // the parser.
+
+    /** Type annotations that have already been read but have not yet been used. **/
+    private List<JCAnnotation> typeAnnotationsPushedBack = List.nil();
+
+    /**
+     * If the parser notices extra annotations, then it either immediately
+     * issues an error (if this variable is false) or places the extra
+     * annotations in variable typeAnnotationsPushedBack (if this variable
+     * is true).
+     */
+    private boolean permitTypeAnnotationsPushBack = false;
+
+    interface ErrorRecoveryAction {
+        JCTree doRecover(JavacParser parser);
+    }
+
+    enum BasicErrorRecoveryAction implements ErrorRecoveryAction {
+        BLOCK_STMT {public JCTree doRecover(JavacParser parser) { return parser.parseStatementAsBlock(); }},
+        CATCH_CLAUSE {public JCTree doRecover(JavacParser parser) { return parser.catchClause(); }}
+    }
+
+    /** Construct a parser from a given scanner, tree factory and log.
+     */
+    protected JavacParser(ParserFactory fac,
+                          Lexer S,
+                          boolean keepDocComments,
+                          boolean keepLineMap,
+                          boolean keepEndPositions) {
+        this(fac, S, keepDocComments, keepLineMap, keepEndPositions, false);
+
+    }
+    /** Construct a parser from a given scanner, tree factory and log.
+     */
+    protected JavacParser(ParserFactory fac,
+                     Lexer S,
+                     boolean keepDocComments,
+                     boolean keepLineMap,
+                     boolean keepEndPositions,
+                     boolean parseModuleInfo) {
+        this.S = S;
+        nextToken(); // prime the pump
+        this.F = fac.F;
+        this.log = fac.log;
+        this.names = fac.names;
+        this.source = fac.source;
+        this.allowTWR = source.allowTryWithResources();
+        this.allowEffectivelyFinalVariablesInTWR =
+                source.allowEffectivelyFinalVariablesInTryWithResources();
+        this.allowDiamond = source.allowDiamond();
+        this.allowMulticatch = source.allowMulticatch();
+        this.allowStringFolding = fac.options.getBoolean("allowStringFolding", true);
+        this.allowLambda = source.allowLambda();
+        this.allowMethodReferences = source.allowMethodReferences();
+        this.allowDefaultMethods = source.allowDefaultMethods();
+        this.allowStaticInterfaceMethods = source.allowStaticInterfaceMethods();
+        this.allowIntersectionTypesInCast = source.allowIntersectionTypesInCast();
+        this.allowTypeAnnotations = source.allowTypeAnnotations();
+        this.allowModules = source.allowModules();
+        this.allowAnnotationsAfterTypeParams = source.allowAnnotationsAfterTypeParams();
+        this.allowUnderscoreIdentifier = source.allowUnderscoreIdentifier();
+        this.allowPrivateInterfaceMethods = source.allowPrivateInterfaceMethods();
+        this.keepDocComments = keepDocComments;
+        this.parseModuleInfo = parseModuleInfo;
+        docComments = newDocCommentTable(keepDocComments, fac);
+        this.keepLineMap = keepLineMap;
+        this.errorTree = F.Erroneous();
+        endPosTable = newEndPosTable(keepEndPositions);
+    }
+
+    protected AbstractEndPosTable newEndPosTable(boolean keepEndPositions) {
+        return  keepEndPositions
+                ? new SimpleEndPosTable(this)
+                : new EmptyEndPosTable(this);
+    }
+
+    protected DocCommentTable newDocCommentTable(boolean keepDocComments, ParserFactory fac) {
+        return keepDocComments ? new LazyDocCommentTable(fac) : null;
+    }
+
+    /** Switch: Should diamond operator be recognized?
+     */
+    boolean allowDiamond;
+
+    /** Switch: Should multicatch clause be accepted?
+     */
+    boolean allowMulticatch;
+
+    /** Switch: should we recognize try-with-resources?
+     */
+    boolean allowTWR;
+
+    /** Switch: should we allow (effectively) final variables as resources in try-with-resources?
+     */
+    boolean allowEffectivelyFinalVariablesInTWR;
+
+    /** Switch: should we fold strings?
+     */
+    boolean allowStringFolding;
+
+    /** Switch: should we recognize lambda expressions?
+     */
+    boolean allowLambda;
+
+    /** Switch: should we allow method/constructor references?
+     */
+    boolean allowMethodReferences;
+
+    /** Switch: should we recognize modules?
+     */
+    boolean allowModules;
+
+    /** Switch: should we allow default methods in interfaces?
+     */
+    boolean allowDefaultMethods;
+
+    /** Switch: should we allow static methods in interfaces?
+     */
+    boolean allowStaticInterfaceMethods;
+
+    /** Switch: should we allow private (instance) methods in interfaces?
+     */
+    boolean allowPrivateInterfaceMethods;
+
+    /** Switch: should we allow intersection types in cast?
+     */
+    boolean allowIntersectionTypesInCast;
+
+    /** Switch: should we keep docComments?
+     */
+    boolean keepDocComments;
+
+    /** Switch: should we keep line table?
+     */
+    boolean keepLineMap;
+
+    /** Switch: should we recognize type annotations?
+     */
+    boolean allowTypeAnnotations;
+
+    /** Switch: should we allow annotations after the method type parameters?
+     */
+    boolean allowAnnotationsAfterTypeParams;
+
+    /** Switch: should we allow '_' as an identifier?
+     */
+    boolean allowUnderscoreIdentifier;
+
+    /** Switch: is "this" allowed as an identifier?
+     * This is needed to parse receiver types.
+     */
+    boolean allowThisIdent;
+
+    /** The type of the method receiver, as specified by a first "this" parameter.
+     */
+    JCVariableDecl receiverParam;
+
+
+    /** When terms are parsed, the mode determines which is expected:
+     *     mode = EXPR        : an expression
+     *     mode = TYPE        : a type
+     *     mode = NOPARAMS    : no parameters allowed for type
+     *     mode = TYPEARG     : type argument
+     */
+    protected static final int EXPR = 0x1;
+    protected static final int TYPE = 0x2;
+    protected static final int NOPARAMS = 0x4;
+    protected static final int TYPEARG = 0x8;
+    protected static final int DIAMOND = 0x10;
+
+    /** The current mode.
+     */
+    protected int mode = 0;
+
+    /** The mode of the term that was parsed last.
+     */
+    protected int lastmode = 0;
+
+    /* ---------- token management -------------- */
+
+    protected Token token;
+
+    public Token token() {
+        return token;
+    }
+
+    public void nextToken() {
+        S.nextToken();
+        token = S.token();
+    }
+
+    protected boolean peekToken(Filter<TokenKind> tk) {
+        return peekToken(0, tk);
+    }
+
+    protected boolean peekToken(int lookahead, Filter<TokenKind> tk) {
+        return tk.accepts(S.token(lookahead + 1).kind);
+    }
+
+    protected boolean peekToken(Filter<TokenKind> tk1, Filter<TokenKind> tk2) {
+        return peekToken(0, tk1, tk2);
+    }
+
+    protected boolean peekToken(int lookahead, Filter<TokenKind> tk1, Filter<TokenKind> tk2) {
+        return tk1.accepts(S.token(lookahead + 1).kind) &&
+                tk2.accepts(S.token(lookahead + 2).kind);
+    }
+
+    protected boolean peekToken(Filter<TokenKind> tk1, Filter<TokenKind> tk2, Filter<TokenKind> tk3) {
+        return peekToken(0, tk1, tk2, tk3);
+    }
+
+    protected boolean peekToken(int lookahead, Filter<TokenKind> tk1, Filter<TokenKind> tk2, Filter<TokenKind> tk3) {
+        return tk1.accepts(S.token(lookahead + 1).kind) &&
+                tk2.accepts(S.token(lookahead + 2).kind) &&
+                tk3.accepts(S.token(lookahead + 3).kind);
+    }
+
+    @SuppressWarnings("unchecked")
+    protected boolean peekToken(Filter<TokenKind>... kinds) {
+        return peekToken(0, kinds);
+    }
+
+    @SuppressWarnings("unchecked")
+    protected boolean peekToken(int lookahead, Filter<TokenKind>... kinds) {
+        for (; lookahead < kinds.length ; lookahead++) {
+            if (!kinds[lookahead].accepts(S.token(lookahead + 1).kind)) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /* ---------- error recovery -------------- */
+
+    private JCErroneous errorTree;
+
+    /** Skip forward until a suitable stop token is found.
+     */
+    protected void skip(boolean stopAtImport, boolean stopAtMemberDecl, boolean stopAtIdentifier, boolean stopAtStatement) {
+         while (true) {
+             switch (token.kind) {
+                case SEMI:
+                    nextToken();
+                    return;
+                case PUBLIC:
+                case FINAL:
+                case ABSTRACT:
+                case MONKEYS_AT:
+                case EOF:
+                case CLASS:
+                case INTERFACE:
+                case ENUM:
+                    return;
+                case IMPORT:
+                    if (stopAtImport)
+                        return;
+                    break;
+                case LBRACE:
+                case RBRACE:
+                case PRIVATE:
+                case PROTECTED:
+                case STATIC:
+                case TRANSIENT:
+                case NATIVE:
+                case VOLATILE:
+                case SYNCHRONIZED:
+                case STRICTFP:
+                case LT:
+                case BYTE:
+                case SHORT:
+                case CHAR:
+                case INT:
+                case LONG:
+                case FLOAT:
+                case DOUBLE:
+                case BOOLEAN:
+                case VOID:
+                    if (stopAtMemberDecl)
+                        return;
+                    break;
+                case UNDERSCORE:
+                case IDENTIFIER:
+                   if (stopAtIdentifier)
+                        return;
+                    break;
+                case CASE:
+                case DEFAULT:
+                case IF:
+                case FOR:
+                case WHILE:
+                case DO:
+                case TRY:
+                case SWITCH:
+                case RETURN:
+                case THROW:
+                case BREAK:
+                case CONTINUE:
+                case ELSE:
+                case FINALLY:
+                case CATCH:
+                case THIS:
+                case SUPER:
+                case NEW:
+                    if (stopAtStatement)
+                        return;
+                    break;
+                case ASSERT:
+                    if (stopAtStatement)
+                        return;
+                    break;
+            }
+            nextToken();
+        }
+    }
+
+    protected JCErroneous syntaxError(int pos, String key, TokenKind... args) {
+        return syntaxError(pos, List.nil(), key, args);
+    }
+
+    protected JCErroneous syntaxError(int pos, List<JCTree> errs, String key, TokenKind... args) {
+        setErrorEndPos(pos);
+        JCErroneous err = F.at(pos).Erroneous(errs);
+        reportSyntaxError(err, key, (Object[])args);
+        if (errs != null) {
+            JCTree last = errs.last();
+            if (last != null)
+                storeEnd(last, pos);
+        }
+        return toP(err);
+    }
+
+    private static final int RECOVERY_THRESHOLD = 50;
+    private int errorPos = Position.NOPOS;
+    private int count = 0;
+
+    /**
+     * Report a syntax using the given the position parameter and arguments,
+     * unless one was already reported at the same position.
+     */
+    protected void reportSyntaxError(int pos, String key, Object... args) {
+        JCDiagnostic.DiagnosticPosition diag = new JCDiagnostic.SimpleDiagnosticPosition(pos);
+        reportSyntaxError(diag, key, args);
+    }
+
+    /**
+     * Report a syntax error using the given DiagnosticPosition object and
+     * arguments, unless one was already reported at the same position.
+     */
+    protected void reportSyntaxError(JCDiagnostic.DiagnosticPosition diagPos, String key, Object... args) {
+        int pos = diagPos.getPreferredPosition();
+        if (pos > S.errPos() || pos == Position.NOPOS) {
+            if (token.kind == EOF) {
+                error(diagPos, "premature.eof");
+            } else {
+                error(diagPos, key, args);
+            }
+        }
+        S.errPos(pos);
+        if (token.pos == errorPos) {
+            //check for a possible infinite loop in parsing:
+            Assert.check(count++ < RECOVERY_THRESHOLD);
+        } else {
+            count = 0;
+            errorPos = token.pos;
+        }
+    }
+
+
+    /** Generate a syntax error at current position unless one was already
+     *  reported at the same position.
+     */
+    protected JCErroneous syntaxError(String key) {
+        return syntaxError(token.pos, key);
+    }
+
+    /** Generate a syntax error at current position unless one was
+     *  already reported at the same position.
+     */
+    protected JCErroneous syntaxError(String key, TokenKind arg) {
+        return syntaxError(token.pos, key, arg);
+    }
+
+    /** If next input token matches given token, skip it, otherwise report
+     *  an error.
+     */
+    public void accept(TokenKind tk) {
+        if (token.kind == tk) {
+            nextToken();
+        } else {
+            setErrorEndPos(token.pos);
+            reportSyntaxError(S.prevToken().endPos, "expected", tk);
+        }
+    }
+
+    /** Report an illegal start of expression/type error at given position.
+     */
+    JCExpression illegal(int pos) {
+        setErrorEndPos(pos);
+        if ((mode & EXPR) != 0)
+            return syntaxError(pos, "illegal.start.of.expr");
+        else
+            return syntaxError(pos, "illegal.start.of.type");
+
+    }
+
+    /** Report an illegal start of expression/type error at current position.
+     */
+    JCExpression illegal() {
+        return illegal(token.pos);
+    }
+
+    /** Diagnose a modifier flag from the set, if any. */
+    protected void checkNoMods(long mods) {
+        if (mods != 0) {
+            long lowestMod = mods & -mods;
+            error(token.pos, "mod.not.allowed.here",
+                    Flags.asFlagSet(lowestMod));
+        }
+    }
+
+/* ---------- doc comments --------- */
+
+    /** A table to store all documentation comments
+     *  indexed by the tree nodes they refer to.
+     *  defined only if option flag keepDocComment is set.
+     */
+    private final DocCommentTable docComments;
+
+    /** Make an entry into docComments hashtable,
+     *  provided flag keepDocComments is set and given doc comment is non-null.
+     *  @param tree   The tree to be used as index in the hashtable
+     *  @param dc     The doc comment to associate with the tree, or null.
+     */
+    protected void attach(JCTree tree, Comment dc) {
+        if (keepDocComments && dc != null) {
+//          System.out.println("doc comment = ");System.out.println(dc);//DEBUG
+            docComments.putComment(tree, dc);
+        }
+    }
+
+/* -------- source positions ------- */
+
+    protected void setErrorEndPos(int errPos) {
+        endPosTable.setErrorEndPos(errPos);
+    }
+
+    protected void storeEnd(JCTree tree, int endpos) {
+        endPosTable.storeEnd(tree, endpos);
+    }
+
+    protected <T extends JCTree> T to(T t) {
+        return endPosTable.to(t);
+    }
+
+    protected <T extends JCTree> T toP(T t) {
+        return endPosTable.toP(t);
+    }
+
+    /** Get the start position for a tree node.  The start position is
+     * defined to be the position of the first character of the first
+     * token of the node's source text.
+     * @param tree  The tree node
+     */
+    public int getStartPos(JCTree tree) {
+        return TreeInfo.getStartPos(tree);
+    }
+
+    /**
+     * Get the end position for a tree node.  The end position is
+     * defined to be the position of the last character of the last
+     * token of the node's source text.  Returns Position.NOPOS if end
+     * positions are not generated or the position is otherwise not
+     * found.
+     * @param tree  The tree node
+     */
+    public int getEndPos(JCTree tree) {
+        return endPosTable.getEndPos(tree);
+    }
+
+
+
+/* ---------- parsing -------------- */
+
+    /**
+     * Ident = IDENTIFIER
+     */
+    public Name ident() {
+        return ident(false);
+    }
+
+    protected Name ident(boolean advanceOnErrors) {
+        if (token.kind == IDENTIFIER) {
+            Name name = token.name();
+            nextToken();
+            return name;
+        } else if (token.kind == ASSERT) {
+            error(token.pos, "assert.as.identifier");
+            nextToken();
+            return names.error;
+        } else if (token.kind == ENUM) {
+            error(token.pos, "enum.as.identifier");
+            nextToken();
+            return names.error;
+        } else if (token.kind == THIS) {
+            if (allowThisIdent) {
+                // Make sure we're using a supported source version.
+                checkTypeAnnotations();
+                Name name = token.name();
+                nextToken();
+                return name;
+            } else {
+                error(token.pos, "this.as.identifier");
+                nextToken();
+                return names.error;
+            }
+        } else if (token.kind == UNDERSCORE) {
+            if (allowUnderscoreIdentifier) {
+                warning(token.pos, "underscore.as.identifier");
+            } else {
+                error(token.pos, "underscore.as.identifier");
+            }
+            Name name = token.name();
+            nextToken();
+            return name;
+        } else {
+            accept(IDENTIFIER);
+            if (advanceOnErrors) {
+                nextToken();
+            }
+            return names.error;
+        }
+    }
+
+    /**
+     * Qualident = Ident { DOT [Annotations] Ident }
+     */
+    public JCExpression qualident(boolean allowAnnos) {
+        JCExpression t = toP(F.at(token.pos).Ident(ident()));
+        while (token.kind == DOT) {
+            int pos = token.pos;
+            nextToken();
+            List<JCAnnotation> tyannos = null;
+            if (allowAnnos) {
+                tyannos = typeAnnotationsOpt();
+            }
+            t = toP(F.at(pos).Select(t, ident()));
+            if (tyannos != null && tyannos.nonEmpty()) {
+                t = toP(F.at(tyannos.head.pos).AnnotatedType(tyannos, t));
+            }
+        }
+        return t;
+    }
+
+    JCExpression literal(Name prefix) {
+        return literal(prefix, token.pos);
+    }
+
+    /**
+     * Literal =
+     *     INTLITERAL
+     *   | LONGLITERAL
+     *   | FLOATLITERAL
+     *   | DOUBLELITERAL
+     *   | CHARLITERAL
+     *   | STRINGLITERAL
+     *   | TRUE
+     *   | FALSE
+     *   | NULL
+     */
+    JCExpression literal(Name prefix, int pos) {
+        JCExpression t = errorTree;
+        switch (token.kind) {
+        case INTLITERAL:
+            try {
+                t = F.at(pos).Literal(
+                    TypeTag.INT,
+                    Convert.string2int(strval(prefix), token.radix()));
+            } catch (NumberFormatException ex) {
+                error(token.pos, "int.number.too.large", strval(prefix));
+            }
+            break;
+        case LONGLITERAL:
+            try {
+                t = F.at(pos).Literal(
+                    TypeTag.LONG,
+                    Long.valueOf(Convert.string2long(strval(prefix), token.radix())));
+            } catch (NumberFormatException ex) {
+                error(token.pos, "int.number.too.large", strval(prefix));
+            }
+            break;
+        case FLOATLITERAL: {
+            String proper = token.radix() == 16 ?
+                    ("0x"+ token.stringVal()) :
+                    token.stringVal();
+            Float n;
+            try {
+                n = Float.valueOf(proper);
+            } catch (NumberFormatException ex) {
+                // error already reported in scanner
+                n = Float.NaN;
+            }
+            if (n.floatValue() == 0.0f && !isZero(proper))
+                error(token.pos, "fp.number.too.small");
+            else if (n.floatValue() == Float.POSITIVE_INFINITY)
+                error(token.pos, "fp.number.too.large");
+            else
+                t = F.at(pos).Literal(TypeTag.FLOAT, n);
+            break;
+        }
+        case DOUBLELITERAL: {
+            String proper = token.radix() == 16 ?
+                    ("0x"+ token.stringVal()) :
+                    token.stringVal();
+            Double n;
+            try {
+                n = Double.valueOf(proper);
+            } catch (NumberFormatException ex) {
+                // error already reported in scanner
+                n = Double.NaN;
+            }
+            if (n.doubleValue() == 0.0d && !isZero(proper))
+                error(token.pos, "fp.number.too.small");
+            else if (n.doubleValue() == Double.POSITIVE_INFINITY)
+                error(token.pos, "fp.number.too.large");
+            else
+                t = F.at(pos).Literal(TypeTag.DOUBLE, n);
+            break;
+        }
+        case CHARLITERAL:
+            t = F.at(pos).Literal(
+                TypeTag.CHAR,
+                token.stringVal().charAt(0) + 0);
+            break;
+        case STRINGLITERAL:
+            t = F.at(pos).Literal(
+                TypeTag.CLASS,
+                token.stringVal());
+            break;
+        case TRUE: case FALSE:
+            t = F.at(pos).Literal(
+                TypeTag.BOOLEAN,
+                (token.kind == TRUE ? 1 : 0));
+            break;
+        case NULL:
+            t = F.at(pos).Literal(
+                TypeTag.BOT,
+                null);
+            break;
+        default:
+            Assert.error();
+        }
+        if (t == errorTree)
+            t = F.at(pos).Erroneous();
+        storeEnd(t, token.endPos);
+        nextToken();
+        return t;
+    }
+    //where
+        boolean isZero(String s) {
+            char[] cs = s.toCharArray();
+            int base = ((cs.length > 1 && Character.toLowerCase(cs[1]) == 'x') ? 16 : 10);
+            int i = ((base==16) ? 2 : 0);
+            while (i < cs.length && (cs[i] == '0' || cs[i] == '.')) i++;
+            return !(i < cs.length && (Character.digit(cs[i], base) > 0));
+        }
+
+        String strval(Name prefix) {
+            String s = token.stringVal();
+            return prefix.isEmpty() ? s : prefix + s;
+        }
+
+    /** terms can be either expressions or types.
+     */
+    public JCExpression parseExpression() {
+        return term(EXPR);
+    }
+
+    /**
+     * parses (optional) type annotations followed by a type. If the
+     * annotations are present before the type and are not consumed during array
+     * parsing, this method returns a {@link JCAnnotatedType} consisting of
+     * these annotations and the underlying type. Otherwise, it returns the
+     * underlying type.
+     *
+     * <p>
+     *
+     * Note that this method sets {@code mode} to {@code TYPE} first, before
+     * parsing annotations.
+     */
+    public JCExpression parseType() {
+        List<JCAnnotation> annotations = typeAnnotationsOpt();
+        return parseType(annotations);
+    }
+
+    public JCExpression parseType(List<JCAnnotation> annotations) {
+        JCExpression result = unannotatedType();
+
+        if (annotations.nonEmpty()) {
+            result = insertAnnotationsToMostInner(result, annotations, false);
+        }
+
+        return result;
+    }
+
+    public JCExpression unannotatedType() {
+        return term(TYPE);
+    }
+
+    protected JCExpression term(int newmode) {
+        int prevmode = mode;
+        mode = newmode;
+        JCExpression t = term();
+        lastmode = mode;
+        mode = prevmode;
+        return t;
+    }
+
+    /**
+     *  {@literal
+     *  Expression = Expression1 [ExpressionRest]
+     *  ExpressionRest = [AssignmentOperator Expression1]
+     *  AssignmentOperator = "=" | "+=" | "-=" | "*=" | "/=" |
+     *                       "&=" | "|=" | "^=" |
+     *                       "%=" | "<<=" | ">>=" | ">>>="
+     *  Type = Type1
+     *  TypeNoParams = TypeNoParams1
+     *  StatementExpression = Expression
+     *  ConstantExpression = Expression
+     *  }
+     */
+    JCExpression term() {
+        JCExpression t = term1();
+        if ((mode & EXPR) != 0 &&
+            token.kind == EQ || PLUSEQ.compareTo(token.kind) <= 0 && token.kind.compareTo(GTGTGTEQ) <= 0)
+            return termRest(t);
+        else
+            return t;
+    }
+
+    JCExpression termRest(JCExpression t) {
+        switch (token.kind) {
+        case EQ: {
+            int pos = token.pos;
+            nextToken();
+            mode = EXPR;
+            JCExpression t1 = term();
+            return toP(F.at(pos).Assign(t, t1));
+        }
+        case PLUSEQ:
+        case SUBEQ:
+        case STAREQ:
+        case SLASHEQ:
+        case PERCENTEQ:
+        case AMPEQ:
+        case BAREQ:
+        case CARETEQ:
+        case LTLTEQ:
+        case GTGTEQ:
+        case GTGTGTEQ:
+            int pos = token.pos;
+            TokenKind tk = token.kind;
+            nextToken();
+            mode = EXPR;
+            JCExpression t1 = term();
+            return F.at(pos).Assignop(optag(tk), t, t1);
+        default:
+            return t;
+        }
+    }
+
+    /** Expression1   = Expression2 [Expression1Rest]
+     *  Type1         = Type2
+     *  TypeNoParams1 = TypeNoParams2
+     */
+    JCExpression term1() {
+        JCExpression t = term2();
+        if ((mode & EXPR) != 0 && token.kind == QUES) {
+            mode = EXPR;
+            return term1Rest(t);
+        } else {
+            return t;
+        }
+    }
+
+    /** Expression1Rest = ["?" Expression ":" Expression1]
+     */
+    JCExpression term1Rest(JCExpression t) {
+        if (token.kind == QUES) {
+            int pos = token.pos;
+            nextToken();
+            JCExpression t1 = term();
+            accept(COLON);
+            JCExpression t2 = term1();
+            return F.at(pos).Conditional(t, t1, t2);
+        } else {
+            return t;
+        }
+    }
+
+    /** Expression2   = Expression3 [Expression2Rest]
+     *  Type2         = Type3
+     *  TypeNoParams2 = TypeNoParams3
+     */
+    JCExpression term2() {
+        JCExpression t = term3();
+        if ((mode & EXPR) != 0 && prec(token.kind) >= TreeInfo.orPrec) {
+            mode = EXPR;
+            return term2Rest(t, TreeInfo.orPrec);
+        } else {
+            return t;
+        }
+    }
+
+    /*  Expression2Rest = {infixop Expression3}
+     *                  | Expression3 instanceof Type
+     *  infixop         = "||"
+     *                  | "&&"
+     *                  | "|"
+     *                  | "^"
+     *                  | "&"
+     *                  | "==" | "!="
+     *                  | "<" | ">" | "<=" | ">="
+     *                  | "<<" | ">>" | ">>>"
+     *                  | "+" | "-"
+     *                  | "*" | "/" | "%"
+     */
+    JCExpression term2Rest(JCExpression t, int minprec) {
+        JCExpression[] odStack = newOdStack();
+        Token[] opStack = newOpStack();
+
+        // optimization, was odStack = new Tree[...]; opStack = new Tree[...];
+        int top = 0;
+        odStack[0] = t;
+        int startPos = token.pos;
+        Token topOp = Tokens.DUMMY;
+        while (prec(token.kind) >= minprec) {
+            opStack[top] = topOp;
+            top++;
+            topOp = token;
+            nextToken();
+            odStack[top] = (topOp.kind == INSTANCEOF) ? parseType() : term3();
+            while (top > 0 && prec(topOp.kind) >= prec(token.kind)) {
+                odStack[top-1] = makeOp(topOp.pos, topOp.kind, odStack[top-1],
+                                        odStack[top]);
+                top--;
+                topOp = opStack[top];
+            }
+        }
+        Assert.check(top == 0);
+        t = odStack[0];
+
+        if (t.hasTag(JCTree.Tag.PLUS)) {
+            t = foldStrings(t);
+        }
+
+        odStackSupply.add(odStack);
+        opStackSupply.add(opStack);
+        return t;
+    }
+    //where
+        /** Construct a binary or type test node.
+         */
+        private JCExpression makeOp(int pos,
+                                    TokenKind topOp,
+                                    JCExpression od1,
+                                    JCExpression od2)
+        {
+            if (topOp == INSTANCEOF) {
+                return F.at(pos).TypeTest(od1, od2);
+            } else {
+                return F.at(pos).Binary(optag(topOp), od1, od2);
+            }
+        }
+        /** If tree is a concatenation of string literals, replace it
+         *  by a single literal representing the concatenated string.
+         */
+        protected JCExpression foldStrings(JCExpression tree) {
+            if (!allowStringFolding)
+                return tree;
+            ListBuffer<JCExpression> opStack = new ListBuffer<>();
+            ListBuffer<JCLiteral> litBuf = new ListBuffer<>();
+            boolean needsFolding = false;
+            JCExpression curr = tree;
+            while (true) {
+                if (curr.hasTag(JCTree.Tag.PLUS)) {
+                    JCBinary op = (JCBinary)curr;
+                    needsFolding |= foldIfNeeded(op.rhs, litBuf, opStack, false);
+                    curr = op.lhs;
+                } else {
+                    needsFolding |= foldIfNeeded(curr, litBuf, opStack, true);
+                    break; //last one!
+                }
+            }
+            if (needsFolding) {
+                List<JCExpression> ops = opStack.toList();
+                JCExpression res = ops.head;
+                for (JCExpression op : ops.tail) {
+                    res = F.at(op.getStartPosition()).Binary(optag(TokenKind.PLUS), res, op);
+                    storeEnd(res, getEndPos(op));
+                }
+                return res;
+            } else {
+                return tree;
+            }
+        }
+
+        private boolean foldIfNeeded(JCExpression tree, ListBuffer<JCLiteral> litBuf,
+                                                ListBuffer<JCExpression> opStack, boolean last) {
+            JCLiteral str = stringLiteral(tree);
+            if (str != null) {
+                litBuf.prepend(str);
+                return last && merge(litBuf, opStack);
+            } else {
+                boolean res = merge(litBuf, opStack);
+                litBuf.clear();
+                opStack.prepend(tree);
+                return res;
+            }
+        }
+
+        boolean merge(ListBuffer<JCLiteral> litBuf, ListBuffer<JCExpression> opStack) {
+            if (litBuf.isEmpty()) {
+                return false;
+            } else if (litBuf.size() == 1) {
+                opStack.prepend(litBuf.first());
+                return false;
+            } else {
+                JCExpression t = F.at(litBuf.first().getStartPosition()).Literal(TypeTag.CLASS,
+                        litBuf.stream().map(lit -> (String)lit.getValue()).collect(Collectors.joining()));
+                storeEnd(t, litBuf.last().getEndPosition(endPosTable));
+                opStack.prepend(t);
+                return true;
+            }
+        }
+
+        private JCLiteral stringLiteral(JCTree tree) {
+            if (tree.hasTag(LITERAL)) {
+                JCLiteral lit = (JCLiteral)tree;
+                if (lit.typetag == TypeTag.CLASS) {
+                    return lit;
+                }
+            }
+            return null;
+        }
+
+
+        /** optimization: To save allocating a new operand/operator stack
+         *  for every binary operation, we use supplys.
+         */
+        ArrayList<JCExpression[]> odStackSupply = new ArrayList<>();
+        ArrayList<Token[]> opStackSupply = new ArrayList<>();
+
+        private JCExpression[] newOdStack() {
+            if (odStackSupply.isEmpty())
+                return new JCExpression[infixPrecedenceLevels + 1];
+            return odStackSupply.remove(odStackSupply.size() - 1);
+        }
+
+        private Token[] newOpStack() {
+            if (opStackSupply.isEmpty())
+                return new Token[infixPrecedenceLevels + 1];
+            return opStackSupply.remove(opStackSupply.size() - 1);
+        }
+
+    /**
+     *  Expression3    = PrefixOp Expression3
+     *                 | "(" Expr | TypeNoParams ")" Expression3
+     *                 | Primary {Selector} {PostfixOp}
+     *
+     *  {@literal
+     *  Primary        = "(" Expression ")"
+     *                 | Literal
+     *                 | [TypeArguments] THIS [Arguments]
+     *                 | [TypeArguments] SUPER SuperSuffix
+     *                 | NEW [TypeArguments] Creator
+     *                 | "(" Arguments ")" "->" ( Expression | Block )
+     *                 | Ident "->" ( Expression | Block )
+     *                 | [Annotations] Ident { "." [Annotations] Ident }
+     *                 | Expression3 MemberReferenceSuffix
+     *                   [ [Annotations] "[" ( "]" BracketsOpt "." CLASS | Expression "]" )
+     *                   | Arguments
+     *                   | "." ( CLASS | THIS | [TypeArguments] SUPER Arguments | NEW [TypeArguments] InnerCreator )
+     *                   ]
+     *                 | BasicType BracketsOpt "." CLASS
+     *  }
+     *
+     *  PrefixOp       = "++" | "--" | "!" | "~" | "+" | "-"
+     *  PostfixOp      = "++" | "--"
+     *  Type3          = Ident { "." Ident } [TypeArguments] {TypeSelector} BracketsOpt
+     *                 | BasicType
+     *  TypeNoParams3  = Ident { "." Ident } BracketsOpt
+     *  Selector       = "." [TypeArguments] Ident [Arguments]
+     *                 | "." THIS
+     *                 | "." [TypeArguments] SUPER SuperSuffix
+     *                 | "." NEW [TypeArguments] InnerCreator
+     *                 | "[" Expression "]"
+     *  TypeSelector   = "." Ident [TypeArguments]
+     *  SuperSuffix    = Arguments | "." Ident [Arguments]
+     */
+    protected JCExpression term3() {
+        int pos = token.pos;
+        JCExpression t;
+        List<JCExpression> typeArgs = typeArgumentsOpt(EXPR);
+        switch (token.kind) {
+        case QUES:
+            if ((mode & TYPE) != 0 && (mode & (TYPEARG|NOPARAMS)) == TYPEARG) {
+                mode = TYPE;
+                return typeArgument();
+            } else
+                return illegal();
+        case PLUSPLUS: case SUBSUB: case BANG: case TILDE: case PLUS: case SUB:
+            if (typeArgs == null && (mode & EXPR) != 0) {
+                TokenKind tk = token.kind;
+                nextToken();
+                mode = EXPR;
+                if (tk == SUB &&
+                    (token.kind == INTLITERAL || token.kind == LONGLITERAL) &&
+                    token.radix() == 10) {
+                    mode = EXPR;
+                    t = literal(names.hyphen, pos);
+                } else {
+                    t = term3();
+                    return F.at(pos).Unary(unoptag(tk), t);
+                }
+            } else return illegal();
+            break;
+        case LPAREN:
+            if (typeArgs == null && (mode & EXPR) != 0) {
+                ParensResult pres = analyzeParens();
+                switch (pres) {
+                    case CAST:
+                       accept(LPAREN);
+                       mode = TYPE;
+                       int pos1 = pos;
+                       List<JCExpression> targets = List.of(t = term3());
+                       while (token.kind == AMP) {
+                           checkIntersectionTypesInCast();
+                           accept(AMP);
+                           targets = targets.prepend(term3());
+                       }
+                       if (targets.length() > 1) {
+                           t = toP(F.at(pos1).TypeIntersection(targets.reverse()));
+                       }
+                       accept(RPAREN);
+                       mode = EXPR;
+                       JCExpression t1 = term3();
+                       return F.at(pos).TypeCast(t, t1);
+                    case IMPLICIT_LAMBDA:
+                    case EXPLICIT_LAMBDA:
+                        t = lambdaExpressionOrStatement(true, pres == ParensResult.EXPLICIT_LAMBDA, pos);
+                        break;
+                    default: //PARENS
+                        accept(LPAREN);
+                        mode = EXPR;
+                        t = termRest(term1Rest(term2Rest(term3(), TreeInfo.orPrec)));
+                        accept(RPAREN);
+                        t = toP(F.at(pos).Parens(t));
+                        break;
+                }
+            } else {
+                return illegal();
+            }
+            break;
+        case THIS:
+            if ((mode & EXPR) != 0) {
+                mode = EXPR;
+                t = to(F.at(pos).Ident(names._this));
+                nextToken();
+                if (typeArgs == null)
+                    t = argumentsOpt(null, t);
+                else
+                    t = arguments(typeArgs, t);
+                typeArgs = null;
+            } else return illegal();
+            break;
+        case SUPER:
+            if ((mode & EXPR) != 0) {
+                mode = EXPR;
+                t = to(F.at(pos).Ident(names._super));
+                t = superSuffix(typeArgs, t);
+                typeArgs = null;
+            } else return illegal();
+            break;
+        case INTLITERAL: case LONGLITERAL: case FLOATLITERAL: case DOUBLELITERAL:
+        case CHARLITERAL: case STRINGLITERAL:
+        case TRUE: case FALSE: case NULL:
+            if (typeArgs == null && (mode & EXPR) != 0) {
+                mode = EXPR;
+                t = literal(names.empty);
+            } else return illegal();
+            break;
+        case NEW:
+            if (typeArgs != null) return illegal();
+            if ((mode & EXPR) != 0) {
+                mode = EXPR;
+                nextToken();
+                if (token.kind == LT) typeArgs = typeArguments(false);
+                t = creator(pos, typeArgs);
+                typeArgs = null;
+            } else return illegal();
+            break;
+        case MONKEYS_AT:
+            // Only annotated cast types and method references are valid
+            List<JCAnnotation> typeAnnos = typeAnnotationsOpt();
+            if (typeAnnos.isEmpty()) {
+                // else there would be no '@'
+                throw new AssertionError("Expected type annotations, but found none!");
+            }
+
+            JCExpression expr = term3();
+
+            if ((mode & TYPE) == 0) {
+                // Type annotations on class literals no longer legal
+                switch (expr.getTag()) {
+                case REFERENCE: {
+                    JCMemberReference mref = (JCMemberReference) expr;
+                    mref.expr = toP(F.at(pos).AnnotatedType(typeAnnos, mref.expr));
+                    t = mref;
+                    break;
+                }
+                case SELECT: {
+                    JCFieldAccess sel = (JCFieldAccess) expr;
+
+                    if (sel.name != names._class) {
+                        return illegal();
+                    } else {
+                        log.error(token.pos, Errors.NoAnnotationsOnDotClass);
+                        return expr;
+                    }
+                }
+                default:
+                    return illegal(typeAnnos.head.pos);
+                }
+
+            } else {
+                // Type annotations targeting a cast
+                t = insertAnnotationsToMostInner(expr, typeAnnos, false);
+            }
+            break;
+        case UNDERSCORE: case IDENTIFIER: case ASSERT: case ENUM:
+            if (typeArgs != null) return illegal();
+            if ((mode & EXPR) != 0 && peekToken(ARROW)) {
+                t = lambdaExpressionOrStatement(false, false, pos);
+            } else {
+                t = toP(F.at(token.pos).Ident(ident()));
+                loop: while (true) {
+                    pos = token.pos;
+                    final List<JCAnnotation> annos = typeAnnotationsOpt();
+
+                    // need to report an error later if LBRACKET is for array
+                    // index access rather than array creation level
+                    if (!annos.isEmpty() && token.kind != LBRACKET && token.kind != ELLIPSIS)
+                        return illegal(annos.head.pos);
+
+                    switch (token.kind) {
+                    case LBRACKET:
+                        nextToken();
+                        if (token.kind == RBRACKET) {
+                            nextToken();
+                            t = bracketsOpt(t);
+                            t = toP(F.at(pos).TypeArray(t));
+                            if (annos.nonEmpty()) {
+                                t = toP(F.at(pos).AnnotatedType(annos, t));
+                            }
+                            t = bracketsSuffix(t);
+                        } else {
+                            if ((mode & EXPR) != 0) {
+                                mode = EXPR;
+                                JCExpression t1 = term();
+                                if (!annos.isEmpty()) t = illegal(annos.head.pos);
+                                t = to(F.at(pos).Indexed(t, t1));
+                            }
+                            accept(RBRACKET);
+                        }
+                        break loop;
+                    case LPAREN:
+                        if ((mode & EXPR) != 0) {
+                            mode = EXPR;
+                            t = arguments(typeArgs, t);
+                            if (!annos.isEmpty()) t = illegal(annos.head.pos);
+                            typeArgs = null;
+                        }
+                        break loop;
+                    case DOT:
+                        nextToken();
+                        int oldmode = mode;
+                        mode &= ~NOPARAMS;
+                        typeArgs = typeArgumentsOpt(EXPR);
+                        mode = oldmode;
+                        if ((mode & EXPR) != 0) {
+                            switch (token.kind) {
+                            case CLASS:
+                                if (typeArgs != null) return illegal();
+                                mode = EXPR;
+                                t = to(F.at(pos).Select(t, names._class));
+                                nextToken();
+                                break loop;
+                            case THIS:
+                                if (typeArgs != null) return illegal();
+                                mode = EXPR;
+                                t = to(F.at(pos).Select(t, names._this));
+                                nextToken();
+                                break loop;
+                            case SUPER:
+                                mode = EXPR;
+                                t = to(F.at(pos).Select(t, names._super));
+                                t = superSuffix(typeArgs, t);
+                                typeArgs = null;
+                                break loop;
+                            case NEW:
+                                if (typeArgs != null) return illegal();
+                                mode = EXPR;
+                                int pos1 = token.pos;
+                                nextToken();
+                                if (token.kind == LT) typeArgs = typeArguments(false);
+                                t = innerCreator(pos1, typeArgs, t);
+                                typeArgs = null;
+                                break loop;
+                            }
+                        }
+
+                        List<JCAnnotation> tyannos = null;
+                        if ((mode & TYPE) != 0 && token.kind == MONKEYS_AT) {
+                            tyannos = typeAnnotationsOpt();
+                        }
+                        // typeArgs saved for next loop iteration.
+                        t = toP(F.at(pos).Select(t, ident()));
+                        if (tyannos != null && tyannos.nonEmpty()) {
+                            t = toP(F.at(tyannos.head.pos).AnnotatedType(tyannos, t));
+                        }
+                        break;
+                    case ELLIPSIS:
+                        if (this.permitTypeAnnotationsPushBack) {
+                            this.typeAnnotationsPushedBack = annos;
+                        } else if (annos.nonEmpty()) {
+                            // Don't return here -- error recovery attempt
+                            illegal(annos.head.pos);
+                        }
+                        break loop;
+                    case LT:
+                        if ((mode & TYPE) == 0 && isUnboundMemberRef()) {
+                            //this is an unbound method reference whose qualifier
+                            //is a generic type i.e. A<S>::m
+                            int pos1 = token.pos;
+                            accept(LT);
+                            ListBuffer<JCExpression> args = new ListBuffer<>();
+                            args.append(typeArgument());
+                            while (token.kind == COMMA) {
+                                nextToken();
+                                args.append(typeArgument());
+                            }
+                            accept(GT);
+                            t = toP(F.at(pos1).TypeApply(t, args.toList()));
+                            while (token.kind == DOT) {
+                                nextToken();
+                                mode = TYPE;
+                                t = toP(F.at(token.pos).Select(t, ident()));
+                                t = typeArgumentsOpt(t);
+                            }
+                            t = bracketsOpt(t);
+                            if (token.kind != COLCOL) {
+                                //method reference expected here
+                                t = illegal();
+                            }
+                            mode = EXPR;
+                            return term3Rest(t, typeArgs);
+                        }
+                        break loop;
+                    default:
+                        break loop;
+                    }
+                }
+            }
+            if (typeArgs != null) illegal();
+            t = typeArgumentsOpt(t);
+            break;
+        case BYTE: case SHORT: case CHAR: case INT: case LONG: case FLOAT:
+        case DOUBLE: case BOOLEAN:
+            if (typeArgs != null) illegal();
+            t = bracketsSuffix(bracketsOpt(basicType()));
+            break;
+        case VOID:
+            if (typeArgs != null) illegal();
+            if ((mode & EXPR) != 0) {
+                nextToken();
+                if (token.kind == DOT) {
+                    JCPrimitiveTypeTree ti = toP(F.at(pos).TypeIdent(TypeTag.VOID));
+                    t = bracketsSuffix(ti);
+                } else {
+                    return illegal(pos);
+                }
+            } else {
+                // Support the corner case of myMethodHandle.<void>invoke() by passing
+                // a void type (like other primitive types) to the next phase.
+                // The error will be reported in Attr.attribTypes or Attr.visitApply.
+                JCPrimitiveTypeTree ti = to(F.at(pos).TypeIdent(TypeTag.VOID));
+                nextToken();
+                return ti;
+                //return illegal();
+            }
+            break;
+        default:
+            return illegal();
+        }
+        return term3Rest(t, typeArgs);
+    }
+
+    JCExpression term3Rest(JCExpression t, List<JCExpression> typeArgs) {
+        if (typeArgs != null) illegal();
+        while (true) {
+            int pos1 = token.pos;
+            final List<JCAnnotation> annos = typeAnnotationsOpt();
+
+            if (token.kind == LBRACKET) {
+                nextToken();
+                if ((mode & TYPE) != 0) {
+                    int oldmode = mode;
+                    mode = TYPE;
+                    if (token.kind == RBRACKET) {
+                        nextToken();
+                        t = bracketsOpt(t);
+                        t = toP(F.at(pos1).TypeArray(t));
+                        if (token.kind == COLCOL) {
+                            mode = EXPR;
+                            continue;
+                        }
+                        if (annos.nonEmpty()) {
+                            t = toP(F.at(pos1).AnnotatedType(annos, t));
+                        }
+                        return t;
+                    }
+                    mode = oldmode;
+                }
+                if ((mode & EXPR) != 0) {
+                    mode = EXPR;
+                    JCExpression t1 = term();
+                    t = to(F.at(pos1).Indexed(t, t1));
+                }
+                accept(RBRACKET);
+            } else if (token.kind == DOT) {
+                nextToken();
+                typeArgs = typeArgumentsOpt(EXPR);
+                if (token.kind == SUPER && (mode & EXPR) != 0) {
+                    mode = EXPR;
+                    t = to(F.at(pos1).Select(t, names._super));
+                    nextToken();
+                    t = arguments(typeArgs, t);
+                    typeArgs = null;
+                } else if (token.kind == NEW && (mode & EXPR) != 0) {
+                    if (typeArgs != null) return illegal();
+                    mode = EXPR;
+                    int pos2 = token.pos;
+                    nextToken();
+                    if (token.kind == LT) typeArgs = typeArguments(false);
+                    t = innerCreator(pos2, typeArgs, t);
+                    typeArgs = null;
+                } else {
+                    List<JCAnnotation> tyannos = null;
+                    if ((mode & TYPE) != 0 && token.kind == MONKEYS_AT) {
+                        // is the mode check needed?
+                        tyannos = typeAnnotationsOpt();
+                    }
+                    t = toP(F.at(pos1).Select(t, ident(true)));
+                    if (tyannos != null && tyannos.nonEmpty()) {
+                        t = toP(F.at(tyannos.head.pos).AnnotatedType(tyannos, t));
+                    }
+                    t = argumentsOpt(typeArgs, typeArgumentsOpt(t));
+                    typeArgs = null;
+                }
+            } else if ((mode & EXPR) != 0 && token.kind == COLCOL) {
+                mode = EXPR;
+                if (typeArgs != null) return illegal();
+                accept(COLCOL);
+                t = memberReferenceSuffix(pos1, t);
+            } else {
+                if (!annos.isEmpty()) {
+                    if (permitTypeAnnotationsPushBack)
+                        typeAnnotationsPushedBack = annos;
+                    else
+                        return illegal(annos.head.pos);
+                }
+                break;
+            }
+        }
+        while ((token.kind == PLUSPLUS || token.kind == SUBSUB) && (mode & EXPR) != 0) {
+            mode = EXPR;
+            t = to(F.at(token.pos).Unary(
+                  token.kind == PLUSPLUS ? POSTINC : POSTDEC, t));
+            nextToken();
+        }
+        return toP(t);
+    }
+
+    /**
+     * If we see an identifier followed by a '&lt;' it could be an unbound
+     * method reference or a binary expression. To disambiguate, look for a
+     * matching '&gt;' and see if the subsequent terminal is either '.' or '::'.
+     */
+    @SuppressWarnings("fallthrough")
+    boolean isUnboundMemberRef() {
+        int pos = 0, depth = 0;
+        outer: for (Token t = S.token(pos) ; ; t = S.token(++pos)) {
+            switch (t.kind) {
+                case IDENTIFIER: case UNDERSCORE: case QUES: case EXTENDS: case SUPER:
+                case DOT: case RBRACKET: case LBRACKET: case COMMA:
+                case BYTE: case SHORT: case INT: case LONG: case FLOAT:
+                case DOUBLE: case BOOLEAN: case CHAR:
+                case MONKEYS_AT:
+                    break;
+
+                case LPAREN:
+                    // skip annotation values
+                    int nesting = 0;
+                    for (; ; pos++) {
+                        TokenKind tk2 = S.token(pos).kind;
+                        switch (tk2) {
+                            case EOF:
+                                return false;
+                            case LPAREN:
+                                nesting++;
+                                break;
+                            case RPAREN:
+                                nesting--;
+                                if (nesting == 0) {
+                                    continue outer;
+                                }
+                                break;
+                        }
+                    }
+
+                case LT:
+                    depth++; break;
+                case GTGTGT:
+                    depth--;
+                case GTGT:
+                    depth--;
+                case GT:
+                    depth--;
+                    if (depth == 0) {
+                        TokenKind nextKind = S.token(pos + 1).kind;
+                        return
+                            nextKind == TokenKind.DOT ||
+                            nextKind == TokenKind.LBRACKET ||
+                            nextKind == TokenKind.COLCOL;
+                    }
+                    break;
+                default:
+                    return false;
+            }
+        }
+    }
+
+    /**
+     * If we see an identifier followed by a '&lt;' it could be an unbound
+     * method reference or a binary expression. To disambiguate, look for a
+     * matching '&gt;' and see if the subsequent terminal is either '.' or '::'.
+     */
+    @SuppressWarnings("fallthrough")
+    ParensResult analyzeParens() {
+        int depth = 0;
+        boolean type = false;
+        outer: for (int lookahead = 0 ; ; lookahead++) {
+            TokenKind tk = S.token(lookahead).kind;
+            switch (tk) {
+                case COMMA:
+                    type = true;
+                case EXTENDS: case SUPER: case DOT: case AMP:
+                    //skip
+                    break;
+                case QUES:
+                    if (peekToken(lookahead, EXTENDS) ||
+                            peekToken(lookahead, SUPER)) {
+                        //wildcards
+                        type = true;
+                    }
+                    break;
+                case BYTE: case SHORT: case INT: case LONG: case FLOAT:
+                case DOUBLE: case BOOLEAN: case CHAR: case VOID:
+                    if (peekToken(lookahead, RPAREN)) {
+                        //Type, ')' -> cast
+                        return ParensResult.CAST;
+                    } else if (peekToken(lookahead, LAX_IDENTIFIER)) {
+                        //Type, Identifier/'_'/'assert'/'enum' -> explicit lambda
+                        return ParensResult.EXPLICIT_LAMBDA;
+                    }
+                    break;
+                case LPAREN:
+                    if (lookahead != 0) {
+                        // '(' in a non-starting position -> parens
+                        return ParensResult.PARENS;
+                    } else if (peekToken(lookahead, RPAREN)) {
+                        // '(', ')' -> explicit lambda
+                        return ParensResult.EXPLICIT_LAMBDA;
+                    }
+                    break;
+                case RPAREN:
+                    // if we have seen something that looks like a type,
+                    // then it's a cast expression
+                    if (type) return ParensResult.CAST;
+                    // otherwise, disambiguate cast vs. parenthesized expression
+                    // based on subsequent token.
+                    switch (S.token(lookahead + 1).kind) {
+                        /*case PLUSPLUS: case SUBSUB: */
+                        case BANG: case TILDE:
+                        case LPAREN: case THIS: case SUPER:
+                        case INTLITERAL: case LONGLITERAL: case FLOATLITERAL:
+                        case DOUBLELITERAL: case CHARLITERAL: case STRINGLITERAL:
+                        case TRUE: case FALSE: case NULL:
+                        case NEW: case IDENTIFIER: case ASSERT: case ENUM: case UNDERSCORE:
+                        case BYTE: case SHORT: case CHAR: case INT:
+                        case LONG: case FLOAT: case DOUBLE: case BOOLEAN: case VOID:
+                            return ParensResult.CAST;
+                        default:
+                            return ParensResult.PARENS;
+                    }
+                case UNDERSCORE:
+                case ASSERT:
+                case ENUM:
+                case IDENTIFIER:
+                    if (peekToken(lookahead, LAX_IDENTIFIER)) {
+                        // Identifier, Identifier/'_'/'assert'/'enum' -> explicit lambda
+                        return ParensResult.EXPLICIT_LAMBDA;
+                    } else if (peekToken(lookahead, RPAREN, ARROW)) {
+                        // Identifier, ')' '->' -> implicit lambda
+                        return ParensResult.IMPLICIT_LAMBDA;
+                    }
+                    type = false;
+                    break;
+                case FINAL:
+                case ELLIPSIS:
+                    //those can only appear in explicit lambdas
+                    return ParensResult.EXPLICIT_LAMBDA;
+                case MONKEYS_AT:
+                    type = true;
+                    lookahead += 1; //skip '@'
+                    while (peekToken(lookahead, DOT)) {
+                        lookahead += 2;
+                    }
+                    if (peekToken(lookahead, LPAREN)) {
+                        lookahead++;
+                        //skip annotation values
+                        int nesting = 0;
+                        for (; ; lookahead++) {
+                            TokenKind tk2 = S.token(lookahead).kind;
+                            switch (tk2) {
+                                case EOF:
+                                    return ParensResult.PARENS;
+                                case LPAREN:
+                                    nesting++;
+                                    break;
+                                case RPAREN:
+                                    nesting--;
+                                    if (nesting == 0) {
+                                        continue outer;
+                                    }
+                                break;
+                            }
+                        }
+                    }
+                    break;
+                case LBRACKET:
+                    if (peekToken(lookahead, RBRACKET, LAX_IDENTIFIER)) {
+                        // '[', ']', Identifier/'_'/'assert'/'enum' -> explicit lambda
+                        return ParensResult.EXPLICIT_LAMBDA;
+                    } else if (peekToken(lookahead, RBRACKET, RPAREN) ||
+                            peekToken(lookahead, RBRACKET, AMP)) {
+                        // '[', ']', ')' -> cast
+                        // '[', ']', '&' -> cast (intersection type)
+                        return ParensResult.CAST;
+                    } else if (peekToken(lookahead, RBRACKET)) {
+                        //consume the ']' and skip
+                        type = true;
+                        lookahead++;
+                        break;
+                    } else {
+                        return ParensResult.PARENS;
+                    }
+                case LT:
+                    depth++; break;
+                case GTGTGT:
+                    depth--;
+                case GTGT:
+                    depth--;
+                case GT:
+                    depth--;
+                    if (depth == 0) {
+                        if (peekToken(lookahead, RPAREN) ||
+                                peekToken(lookahead, AMP)) {
+                            // '>', ')' -> cast
+                            // '>', '&' -> cast
+                            return ParensResult.CAST;
+                        } else if (peekToken(lookahead, LAX_IDENTIFIER, COMMA) ||
+                                peekToken(lookahead, LAX_IDENTIFIER, RPAREN, ARROW) ||
+                                peekToken(lookahead, ELLIPSIS)) {
+                            // '>', Identifier/'_'/'assert'/'enum', ',' -> explicit lambda
+                            // '>', Identifier/'_'/'assert'/'enum', ')', '->' -> explicit lambda
+                            // '>', '...' -> explicit lambda
+                            return ParensResult.EXPLICIT_LAMBDA;
+                        }
+                        //it looks a type, but could still be (i) a cast to generic type,
+                        //(ii) an unbound method reference or (iii) an explicit lambda
+                        type = true;
+                        break;
+                    } else if (depth < 0) {
+                        //unbalanced '<', '>' - not a generic type
+                        return ParensResult.PARENS;
+                    }
+                    break;
+                default:
+                    //this includes EOF
+                    return ParensResult.PARENS;
+            }
+        }
+    }
+
+    /** Accepts all identifier-like tokens */
+    protected Filter<TokenKind> LAX_IDENTIFIER = t -> t == IDENTIFIER || t == UNDERSCORE || t == ASSERT || t == ENUM;
+
+    enum ParensResult {
+        CAST,
+        EXPLICIT_LAMBDA,
+        IMPLICIT_LAMBDA,
+        PARENS
+    }
+
+    JCExpression lambdaExpressionOrStatement(boolean hasParens, boolean explicitParams, int pos) {
+        List<JCVariableDecl> params = explicitParams ?
+                formalParameters(true) :
+                implicitParameters(hasParens);
+
+        return lambdaExpressionOrStatementRest(params, pos);
+    }
+
+    JCExpression lambdaExpressionOrStatementRest(List<JCVariableDecl> args, int pos) {
+        checkLambda();
+        accept(ARROW);
+
+        return token.kind == LBRACE ?
+            lambdaStatement(args, pos, token.pos) :
+            lambdaExpression(args, pos);
+    }
+
+    JCExpression lambdaStatement(List<JCVariableDecl> args, int pos, int pos2) {
+        JCBlock block = block(pos2, 0);
+        return toP(F.at(pos).Lambda(args, block));
+    }
+
+    JCExpression lambdaExpression(List<JCVariableDecl> args, int pos) {
+        JCTree expr = parseExpression();
+        return toP(F.at(pos).Lambda(args, expr));
+    }
+
+    /** SuperSuffix = Arguments | "." [TypeArguments] Ident [Arguments]
+     */
+    JCExpression superSuffix(List<JCExpression> typeArgs, JCExpression t) {
+        nextToken();
+        if (token.kind == LPAREN || typeArgs != null) {
+            t = arguments(typeArgs, t);
+        } else if (token.kind == COLCOL) {
+            if (typeArgs != null) return illegal();
+            t = memberReferenceSuffix(t);
+        } else {
+            int pos = token.pos;
+            accept(DOT);
+            typeArgs = (token.kind == LT) ? typeArguments(false) : null;
+            t = toP(F.at(pos).Select(t, ident()));
+            t = argumentsOpt(typeArgs, t);
+        }
+        return t;
+    }
+
+    /** BasicType = BYTE | SHORT | CHAR | INT | LONG | FLOAT | DOUBLE | BOOLEAN
+     */
+    JCPrimitiveTypeTree basicType() {
+        JCPrimitiveTypeTree t = to(F.at(token.pos).TypeIdent(typetag(token.kind)));
+        nextToken();
+        return t;
+    }
+
+    /** ArgumentsOpt = [ Arguments ]
+     */
+    JCExpression argumentsOpt(List<JCExpression> typeArgs, JCExpression t) {
+        if ((mode & EXPR) != 0 && token.kind == LPAREN || typeArgs != null) {
+            mode = EXPR;
+            return arguments(typeArgs, t);
+        } else {
+            return t;
+        }
+    }
+
+    /** Arguments = "(" [Expression { COMMA Expression }] ")"
+     */
+    List<JCExpression> arguments() {
+        ListBuffer<JCExpression> args = new ListBuffer<>();
+        if (token.kind == LPAREN) {
+            nextToken();
+            if (token.kind != RPAREN) {
+                args.append(parseExpression());
+                while (token.kind == COMMA) {
+                    nextToken();
+                    args.append(parseExpression());
+                }
+            }
+            accept(RPAREN);
+        } else {
+            syntaxError(token.pos, "expected", LPAREN);
+        }
+        return args.toList();
+    }
+
+    JCMethodInvocation arguments(List<JCExpression> typeArgs, JCExpression t) {
+        int pos = token.pos;
+        List<JCExpression> args = arguments();
+        return toP(F.at(pos).Apply(typeArgs, t, args));
+    }
+
+    /**  TypeArgumentsOpt = [ TypeArguments ]
+     */
+    JCExpression typeArgumentsOpt(JCExpression t) {
+        if (token.kind == LT &&
+            (mode & TYPE) != 0 &&
+            (mode & NOPARAMS) == 0) {
+            mode = TYPE;
+            return typeArguments(t, false);
+        } else {
+            return t;
+        }
+    }
+    List<JCExpression> typeArgumentsOpt() {
+        return typeArgumentsOpt(TYPE);
+    }
+
+    List<JCExpression> typeArgumentsOpt(int useMode) {
+        if (token.kind == LT) {
+            if ((mode & useMode) == 0 ||
+                (mode & NOPARAMS) != 0) {
+                illegal();
+            }
+            mode = useMode;
+            return typeArguments(false);
+        }
+        return null;
+    }
+
+    /**
+     *  {@literal
+     *  TypeArguments  = "<" TypeArgument {"," TypeArgument} ">"
+     *  }
+     */
+    List<JCExpression> typeArguments(boolean diamondAllowed) {
+        if (token.kind == LT) {
+            nextToken();
+            if (token.kind == GT && diamondAllowed) {
+                checkDiamond();
+                mode |= DIAMOND;
+                nextToken();
+                return List.nil();
+            } else {
+                ListBuffer<JCExpression> args = new ListBuffer<>();
+                args.append(((mode & EXPR) == 0) ? typeArgument() : parseType());
+                while (token.kind == COMMA) {
+                    nextToken();
+                    args.append(((mode & EXPR) == 0) ? typeArgument() : parseType());
+                }
+                switch (token.kind) {
+
+                case GTGTGTEQ: case GTGTEQ: case GTEQ:
+                case GTGTGT: case GTGT:
+                    token = S.split();
+                    break;
+                case GT:
+                    nextToken();
+                    break;
+                default:
+                    args.append(syntaxError(token.pos, "expected", GT));
+                    break;
+                }
+                return args.toList();
+            }
+        } else {
+            return List.of(syntaxError(token.pos, "expected", LT));
+        }
+    }
+
+    /**
+     *  {@literal
+     *  TypeArgument = Type
+     *               | [Annotations] "?"
+     *               | [Annotations] "?" EXTENDS Type {"&" Type}
+     *               | [Annotations] "?" SUPER Type
+     *  }
+     */
+    JCExpression typeArgument() {
+        List<JCAnnotation> annotations = typeAnnotationsOpt();
+        if (token.kind != QUES) return parseType(annotations);
+        int pos = token.pos;
+        nextToken();
+        JCExpression result;
+        if (token.kind == EXTENDS) {
+            TypeBoundKind t = to(F.at(pos).TypeBoundKind(BoundKind.EXTENDS));
+            nextToken();
+            JCExpression bound = parseType();
+            result = F.at(pos).Wildcard(t, bound);
+        } else if (token.kind == SUPER) {
+            TypeBoundKind t = to(F.at(pos).TypeBoundKind(BoundKind.SUPER));
+            nextToken();
+            JCExpression bound = parseType();
+            result = F.at(pos).Wildcard(t, bound);
+        } else if (LAX_IDENTIFIER.accepts(token.kind)) {
+            //error recovery
+            TypeBoundKind t = F.at(Position.NOPOS).TypeBoundKind(BoundKind.UNBOUND);
+            JCExpression wc = toP(F.at(pos).Wildcard(t, null));
+            JCIdent id = toP(F.at(token.pos).Ident(ident()));
+            JCErroneous err = F.at(pos).Erroneous(List.<JCTree>of(wc, id));
+            reportSyntaxError(err, "expected3", GT, EXTENDS, SUPER);
+            result = err;
+        } else {
+            TypeBoundKind t = toP(F.at(pos).TypeBoundKind(BoundKind.UNBOUND));
+            result = toP(F.at(pos).Wildcard(t, null));
+        }
+        if (!annotations.isEmpty()) {
+            result = toP(F.at(annotations.head.pos).AnnotatedType(annotations,result));
+        }
+        return result;
+    }
+
+    JCTypeApply typeArguments(JCExpression t, boolean diamondAllowed) {
+        int pos = token.pos;
+        List<JCExpression> args = typeArguments(diamondAllowed);
+        return toP(F.at(pos).TypeApply(t, args));
+    }
+
+    /**
+     * BracketsOpt = { [Annotations] "[" "]" }*
+     *
+     * <p>
+     *
+     * <code>annotations</code> is the list of annotations targeting
+     * the expression <code>t</code>.
+     */
+    private JCExpression bracketsOpt(JCExpression t,
+            List<JCAnnotation> annotations) {
+        List<JCAnnotation> nextLevelAnnotations = typeAnnotationsOpt();
+
+        if (token.kind == LBRACKET) {
+            int pos = token.pos;
+            nextToken();
+            t = bracketsOptCont(t, pos, nextLevelAnnotations);
+        } else if (!nextLevelAnnotations.isEmpty()) {
+            if (permitTypeAnnotationsPushBack) {
+                this.typeAnnotationsPushedBack = nextLevelAnnotations;
+            } else {
+                return illegal(nextLevelAnnotations.head.pos);
+            }
+        }
+
+        if (!annotations.isEmpty()) {
+            t = toP(F.at(token.pos).AnnotatedType(annotations, t));
+        }
+        return t;
+    }
+
+    /** BracketsOpt = [ "[" "]" { [Annotations] "[" "]"} ]
+     */
+    private JCExpression bracketsOpt(JCExpression t) {
+        return bracketsOpt(t, List.nil());
+    }
+
+    private JCExpression bracketsOptCont(JCExpression t, int pos,
+            List<JCAnnotation> annotations) {
+        accept(RBRACKET);
+        t = bracketsOpt(t);
+        t = toP(F.at(pos).TypeArray(t));
+        if (annotations.nonEmpty()) {
+            t = toP(F.at(pos).AnnotatedType(annotations, t));
+        }
+        return t;
+    }
+
+    /** BracketsSuffixExpr = "." CLASS
+     *  BracketsSuffixType =
+     */
+    JCExpression bracketsSuffix(JCExpression t) {
+        if ((mode & EXPR) != 0 && token.kind == DOT) {
+            mode = EXPR;
+            int pos = token.pos;
+            nextToken();
+            accept(CLASS);
+            if (token.pos == endPosTable.errorEndPos) {
+                // error recovery
+                Name name;
+                if (LAX_IDENTIFIER.accepts(token.kind)) {
+                    name = token.name();
+                    nextToken();
+                } else {
+                    name = names.error;
+                }
+                t = F.at(pos).Erroneous(List.<JCTree>of(toP(F.at(pos).Select(t, name))));
+            } else {
+                Tag tag = t.getTag();
+                // Type annotations are illegal on class literals. Annotated non array class literals
+                // are complained about directly in term3(), Here check for type annotations on dimensions
+                // taking care to handle some interior dimension(s) being annotated.
+                if ((tag == TYPEARRAY && TreeInfo.containsTypeAnnotation(t)) || tag == ANNOTATED_TYPE)
+                    syntaxError("no.annotations.on.dot.class");
+                t = toP(F.at(pos).Select(t, names._class));
+            }
+        } else if ((mode & TYPE) != 0) {
+            if (token.kind != COLCOL) {
+                mode = TYPE;
+            }
+        } else if (token.kind != COLCOL) {
+            syntaxError(token.pos, "dot.class.expected");
+        }
+        return t;
+    }
+
+    /**
+     * MemberReferenceSuffix = "::" [TypeArguments] Ident
+     *                       | "::" [TypeArguments] "new"
+     */
+    JCExpression memberReferenceSuffix(JCExpression t) {
+        int pos1 = token.pos;
+        accept(COLCOL);
+        return memberReferenceSuffix(pos1, t);
+    }
+
+    JCExpression memberReferenceSuffix(int pos1, JCExpression t) {
+        checkMethodReferences();
+        mode = EXPR;
+        List<JCExpression> typeArgs = null;
+        if (token.kind == LT) {
+            typeArgs = typeArguments(false);
+        }
+        Name refName;
+        ReferenceMode refMode;
+        if (token.kind == NEW) {
+            refMode = ReferenceMode.NEW;
+            refName = names.init;
+            nextToken();
+        } else {
+            refMode = ReferenceMode.INVOKE;
+            refName = ident();
+        }
+        return toP(F.at(t.getStartPosition()).Reference(refMode, refName, t, typeArgs));
+    }
+
+    /** Creator = [Annotations] Qualident [TypeArguments] ( ArrayCreatorRest | ClassCreatorRest )
+     */
+    JCExpression creator(int newpos, List<JCExpression> typeArgs) {
+        List<JCAnnotation> newAnnotations = typeAnnotationsOpt();
+
+        switch (token.kind) {
+        case BYTE: case SHORT: case CHAR: case INT: case LONG: case FLOAT:
+        case DOUBLE: case BOOLEAN:
+            if (typeArgs == null) {
+                if (newAnnotations.isEmpty()) {
+                    return arrayCreatorRest(newpos, basicType());
+                } else {
+                    return arrayCreatorRest(newpos, toP(F.at(newAnnotations.head.pos).AnnotatedType(newAnnotations, basicType())));
+                }
+            }
+            break;
+        default:
+        }
+        JCExpression t = qualident(true);
+
+        int oldmode = mode;
+        mode = TYPE;
+        boolean diamondFound = false;
+        int lastTypeargsPos = -1;
+        if (token.kind == LT) {
+            lastTypeargsPos = token.pos;
+            t = typeArguments(t, true);
+            diamondFound = (mode & DIAMOND) != 0;
+        }
+        while (token.kind == DOT) {
+            if (diamondFound) {
+                //cannot select after a diamond
+                illegal();
+            }
+            int pos = token.pos;
+            nextToken();
+            List<JCAnnotation> tyannos = typeAnnotationsOpt();
+            t = toP(F.at(pos).Select(t, ident()));
+
+            if (tyannos != null && tyannos.nonEmpty()) {
+                t = toP(F.at(tyannos.head.pos).AnnotatedType(tyannos, t));
+            }
+
+            if (token.kind == LT) {
+                lastTypeargsPos = token.pos;
+                t = typeArguments(t, true);
+                diamondFound = (mode & DIAMOND) != 0;
+            }
+        }
+        mode = oldmode;
+        if (token.kind == LBRACKET || token.kind == MONKEYS_AT) {
+            // handle type annotations for non primitive arrays
+            if (newAnnotations.nonEmpty()) {
+                t = insertAnnotationsToMostInner(t, newAnnotations, false);
+            }
+
+            JCExpression e = arrayCreatorRest(newpos, t);
+            if (diamondFound) {
+                reportSyntaxError(lastTypeargsPos, "cannot.create.array.with.diamond");
+                return toP(F.at(newpos).Erroneous(List.of(e)));
+            }
+            else if (typeArgs != null) {
+                int pos = newpos;
+                if (!typeArgs.isEmpty() && typeArgs.head.pos != Position.NOPOS) {
+                    // note: this should always happen but we should
+                    // not rely on this as the parser is continuously
+                    // modified to improve error recovery.
+                    pos = typeArgs.head.pos;
+                }
+                setErrorEndPos(S.prevToken().endPos);
+                JCErroneous err = F.at(pos).Erroneous(typeArgs.prepend(e));
+                reportSyntaxError(err, "cannot.create.array.with.type.arguments");
+                return toP(err);
+            }
+            return e;
+        } else if (token.kind == LPAREN) {
+            JCNewClass newClass = classCreatorRest(newpos, null, typeArgs, t);
+            if (newClass.def != null) {
+                assert newClass.def.mods.annotations.isEmpty();
+                if (newAnnotations.nonEmpty()) {
+                    // Add type and declaration annotations to the new class;
+                    // com.sun.tools.javac.code.TypeAnnotations.TypeAnnotationPositions.visitNewClass(JCNewClass)
+                    // will later remove all type annotations and only leave the
+                    // declaration annotations.
+                    newClass.def.mods.pos = earlier(newClass.def.mods.pos, newAnnotations.head.pos);
+                    newClass.def.mods.annotations = newAnnotations;
+                }
+            } else {
+                // handle type annotations for instantiations
+                if (newAnnotations.nonEmpty()) {
+                    t = insertAnnotationsToMostInner(t, newAnnotations, false);
+                    newClass.clazz = t;
+                }
+            }
+            return newClass;
+        } else {
+            setErrorEndPos(token.pos);
+            reportSyntaxError(token.pos, "expected2", LPAREN, LBRACKET);
+            t = toP(F.at(newpos).NewClass(null, typeArgs, t, List.nil(), null));
+            return toP(F.at(newpos).Erroneous(List.<JCTree>of(t)));
+        }
+    }
+
+    /** InnerCreator = [Annotations] Ident [TypeArguments] ClassCreatorRest
+     */
+    JCExpression innerCreator(int newpos, List<JCExpression> typeArgs, JCExpression encl) {
+        List<JCAnnotation> newAnnotations = typeAnnotationsOpt();
+
+        JCExpression t = toP(F.at(token.pos).Ident(ident()));
+
+        if (newAnnotations.nonEmpty()) {
+            t = toP(F.at(newAnnotations.head.pos).AnnotatedType(newAnnotations, t));
+        }
+
+        if (token.kind == LT) {
+            int oldmode = mode;
+            t = typeArguments(t, true);
+            mode = oldmode;
+        }
+        return classCreatorRest(newpos, encl, typeArgs, t);
+    }
+
+    /** ArrayCreatorRest = [Annotations] "[" ( "]" BracketsOpt ArrayInitializer
+     *                         | Expression "]" {[Annotations]  "[" Expression "]"} BracketsOpt )
+     */
+    JCExpression arrayCreatorRest(int newpos, JCExpression elemtype) {
+        List<JCAnnotation> annos = typeAnnotationsOpt();
+
+        accept(LBRACKET);
+        if (token.kind == RBRACKET) {
+            accept(RBRACKET);
+            elemtype = bracketsOpt(elemtype, annos);
+            if (token.kind == LBRACE) {
+                JCNewArray na = (JCNewArray)arrayInitializer(newpos, elemtype);
+                if (annos.nonEmpty()) {
+                    // when an array initializer is present then
+                    // the parsed annotations should target the
+                    // new array tree
+                    // bracketsOpt inserts the annotation in
+                    // elemtype, and it needs to be corrected
+                    //
+                    JCAnnotatedType annotated = (JCAnnotatedType)elemtype;
+                    assert annotated.annotations == annos;
+                    na.annotations = annotated.annotations;
+                    na.elemtype = annotated.underlyingType;
+                }
+                return na;
+            } else {
+                JCExpression t = toP(F.at(newpos).NewArray(elemtype, List.nil(), null));
+                return syntaxError(token.pos, List.of(t), "array.dimension.missing");
+            }
+        } else {
+            ListBuffer<JCExpression> dims = new ListBuffer<>();
+
+            // maintain array dimension type annotations
+            ListBuffer<List<JCAnnotation>> dimAnnotations = new ListBuffer<>();
+            dimAnnotations.append(annos);
+
+            dims.append(parseExpression());
+            accept(RBRACKET);
+            while (token.kind == LBRACKET
+                    || token.kind == MONKEYS_AT) {
+                List<JCAnnotation> maybeDimAnnos = typeAnnotationsOpt();
+                int pos = token.pos;
+                nextToken();
+                if (token.kind == RBRACKET) {
+                    elemtype = bracketsOptCont(elemtype, pos, maybeDimAnnos);
+                } else {
+                    if (token.kind == RBRACKET) { // no dimension
+                        elemtype = bracketsOptCont(elemtype, pos, maybeDimAnnos);
+                    } else {
+                        dimAnnotations.append(maybeDimAnnos);
+                        dims.append(parseExpression());
+                        accept(RBRACKET);
+                    }
+                }
+            }
+
+            List<JCExpression> elems = null;
+            int errpos = token.pos;
+
+            if (token.kind == LBRACE) {
+                elems = arrayInitializerElements(newpos, elemtype);
+            }
+
+            JCNewArray na = toP(F.at(newpos).NewArray(elemtype, dims.toList(), elems));
+            na.dimAnnotations = dimAnnotations.toList();
+
+            if (elems != null) {
+                return syntaxError(errpos, List.of(na), "illegal.array.creation.both.dimension.and.initialization");
+            }
+
+            return na;
+        }
+    }
+
+    /** ClassCreatorRest = Arguments [ClassBody]
+     */
+    JCNewClass classCreatorRest(int newpos,
+                                  JCExpression encl,
+                                  List<JCExpression> typeArgs,
+                                  JCExpression t)
+    {
+        List<JCExpression> args = arguments();
+        JCClassDecl body = null;
+        if (token.kind == LBRACE) {
+            int pos = token.pos;
+            List<JCTree> defs = classOrInterfaceBody(names.empty, false);
+            JCModifiers mods = F.at(Position.NOPOS).Modifiers(0);
+            body = toP(F.at(pos).AnonymousClassDef(mods, defs));
+        }
+        return toP(F.at(newpos).NewClass(encl, typeArgs, t, args, body));
+    }
+
+    /** ArrayInitializer = "{" [VariableInitializer {"," VariableInitializer}] [","] "}"
+     */
+    JCExpression arrayInitializer(int newpos, JCExpression t) {
+        List<JCExpression> elems = arrayInitializerElements(newpos, t);
+        return toP(F.at(newpos).NewArray(t, List.nil(), elems));
+    }
+
+    List<JCExpression> arrayInitializerElements(int newpos, JCExpression t) {
+        accept(LBRACE);
+        ListBuffer<JCExpression> elems = new ListBuffer<>();
+        if (token.kind == COMMA) {
+            nextToken();
+        } else if (token.kind != RBRACE) {
+            elems.append(variableInitializer());
+            while (token.kind == COMMA) {
+                nextToken();
+                if (token.kind == RBRACE) break;
+                elems.append(variableInitializer());
+            }
+        }
+        accept(RBRACE);
+        return elems.toList();
+    }
+
+    /** VariableInitializer = ArrayInitializer | Expression
+     */
+    public JCExpression variableInitializer() {
+        return token.kind == LBRACE ? arrayInitializer(token.pos, null) : parseExpression();
+    }
+
+    /** ParExpression = "(" Expression ")"
+     */
+    JCExpression parExpression() {
+        int pos = token.pos;
+        accept(LPAREN);
+        JCExpression t = parseExpression();
+        accept(RPAREN);
+        return toP(F.at(pos).Parens(t));
+    }
+
+    /** Block = "{" BlockStatements "}"
+     */
+    JCBlock block(int pos, long flags) {
+        accept(LBRACE);
+        List<JCStatement> stats = blockStatements();
+        JCBlock t = F.at(pos).Block(flags, stats);
+        while (token.kind == CASE || token.kind == DEFAULT) {
+            syntaxError("orphaned", token.kind);
+            switchBlockStatementGroups();
+        }
+        // the Block node has a field "endpos" for first char of last token, which is
+        // usually but not necessarily the last char of the last token.
+        t.endpos = token.pos;
+        accept(RBRACE);
+        return toP(t);
+    }
+
+    public JCBlock block() {
+        return block(token.pos, 0);
+    }
+
+    /** BlockStatements = { BlockStatement }
+     *  BlockStatement  = LocalVariableDeclarationStatement
+     *                  | ClassOrInterfaceOrEnumDeclaration
+     *                  | [Ident ":"] Statement
+     *  LocalVariableDeclarationStatement
+     *                  = { FINAL | '@' Annotation } Type VariableDeclarators ";"
+     */
+    @SuppressWarnings("fallthrough")
+    List<JCStatement> blockStatements() {
+        //todo: skip to anchor on error(?)
+        int lastErrPos = -1;
+        ListBuffer<JCStatement> stats = new ListBuffer<>();
+        while (true) {
+            List<JCStatement> stat = blockStatement();
+            if (stat.isEmpty()) {
+                return stats.toList();
+            } else {
+                // error recovery
+                if (token.pos == lastErrPos)
+                    return stats.toList();
+                if (token.pos <= endPosTable.errorEndPos) {
+                    skip(false, true, true, true);
+                    lastErrPos = token.pos;
+                }
+                stats.addAll(stat);
+            }
+        }
+    }
+
+    /*
+     * Parse a Statement (JLS 14.5). As an enhancement to improve error recovery,
+     * this method will also recognize variable and class declarations (which are
+     * not legal for a Statement) by delegating the parsing to BlockStatement (JLS 14.2).
+     * If any illegal declarations are found, they will be wrapped in an erroneous tree,
+     * and an error will be produced by this method.
+     */
+    JCStatement parseStatementAsBlock() {
+        int pos = token.pos;
+        List<JCStatement> stats = blockStatement();
+        if (stats.isEmpty()) {
+            JCErroneous e = F.at(pos).Erroneous();
+            error(e, "illegal.start.of.stmt");
+            return F.at(pos).Exec(e);
+        } else {
+            JCStatement first = stats.head;
+            String error = null;
+            switch (first.getTag()) {
+            case CLASSDEF:
+                error = "class.not.allowed";
+                break;
+            case VARDEF:
+                error = "variable.not.allowed";
+                break;
+            }
+            if (error != null) {
+                error(first, error);
+                List<JCBlock> blist = List.of(F.at(first.pos).Block(0, stats));
+                return toP(F.at(pos).Exec(F.at(first.pos).Erroneous(blist)));
+            }
+            return first;
+        }
+    }
+
+    /**This method parses a statement appearing inside a block.
+     */
+    List<JCStatement> blockStatement() {
+        //todo: skip to anchor on error(?)
+        int pos = token.pos;
+        switch (token.kind) {
+        case RBRACE: case CASE: case DEFAULT: case EOF:
+            return List.nil();
+        case LBRACE: case IF: case FOR: case WHILE: case DO: case TRY:
+        case SWITCH: case SYNCHRONIZED: case RETURN: case THROW: case BREAK:
+        case CONTINUE: case SEMI: case ELSE: case FINALLY: case CATCH:
+        case ASSERT:
+            return List.of(parseSimpleStatement());
+        case MONKEYS_AT:
+        case FINAL: {
+            Comment dc = token.comment(CommentStyle.JAVADOC);
+            JCModifiers mods = modifiersOpt();
+            if (token.kind == INTERFACE ||
+                token.kind == CLASS ||
+                token.kind == ENUM) {
+                return List.of(classOrInterfaceOrEnumDeclaration(mods, dc));
+            } else {
+                JCExpression t = parseType();
+                ListBuffer<JCStatement> stats =
+                        variableDeclarators(mods, t, new ListBuffer<JCStatement>());
+                // A "LocalVariableDeclarationStatement" subsumes the terminating semicolon
+                accept(SEMI);
+                storeEnd(stats.last(), S.prevToken().endPos);
+                return stats.toList();
+            }
+        }
+        case ABSTRACT: case STRICTFP: {
+            Comment dc = token.comment(CommentStyle.JAVADOC);
+            JCModifiers mods = modifiersOpt();
+            return List.of(classOrInterfaceOrEnumDeclaration(mods, dc));
+        }
+        case INTERFACE:
+        case CLASS:
+            Comment dc = token.comment(CommentStyle.JAVADOC);
+            return List.of(classOrInterfaceOrEnumDeclaration(modifiersOpt(), dc));
+        case ENUM:
+            error(token.pos, "local.enum");
+            dc = token.comment(CommentStyle.JAVADOC);
+            return List.of(classOrInterfaceOrEnumDeclaration(modifiersOpt(), dc));
+        default:
+            Token prevToken = token;
+            JCExpression t = term(EXPR | TYPE);
+            if (token.kind == COLON && t.hasTag(IDENT)) {
+                nextToken();
+                JCStatement stat = parseStatementAsBlock();
+                return List.of(F.at(pos).Labelled(prevToken.name(), stat));
+            } else if ((lastmode & TYPE) != 0 && LAX_IDENTIFIER.accepts(token.kind)) {
+                pos = token.pos;
+                JCModifiers mods = F.at(Position.NOPOS).Modifiers(0);
+                F.at(pos);
+                ListBuffer<JCStatement> stats =
+                        variableDeclarators(mods, t, new ListBuffer<JCStatement>());
+                // A "LocalVariableDeclarationStatement" subsumes the terminating semicolon
+                accept(SEMI);
+                storeEnd(stats.last(), S.prevToken().endPos);
+                return stats.toList();
+            } else {
+                // This Exec is an "ExpressionStatement"; it subsumes the terminating semicolon
+                t = checkExprStat(t);
+                accept(SEMI);
+                JCExpressionStatement expr = toP(F.at(pos).Exec(t));
+                return List.of(expr);
+            }
+        }
+    }
+
+    /** Statement =
+     *       Block
+     *     | IF ParExpression Statement [ELSE Statement]
+     *     | FOR "(" ForInitOpt ";" [Expression] ";" ForUpdateOpt ")" Statement
+     *     | FOR "(" FormalParameter : Expression ")" Statement
+     *     | WHILE ParExpression Statement
+     *     | DO Statement WHILE ParExpression ";"
+     *     | TRY Block ( Catches | [Catches] FinallyPart )
+     *     | TRY "(" ResourceSpecification ";"opt ")" Block [Catches] [FinallyPart]
+     *     | SWITCH ParExpression "{" SwitchBlockStatementGroups "}"
+     *     | SYNCHRONIZED ParExpression Block
+     *     | RETURN [Expression] ";"
+     *     | THROW Expression ";"
+     *     | BREAK [Ident] ";"
+     *     | CONTINUE [Ident] ";"
+     *     | ASSERT Expression [ ":" Expression ] ";"
+     *     | ";"
+     */
+    public JCStatement parseSimpleStatement() {
+        int pos = token.pos;
+        switch (token.kind) {
+        case LBRACE:
+            return block();
+        case IF: {
+            nextToken();
+            JCExpression cond = parExpression();
+            JCStatement thenpart = parseStatementAsBlock();
+            JCStatement elsepart = null;
+            if (token.kind == ELSE) {
+                nextToken();
+                elsepart = parseStatementAsBlock();
+            }
+            return F.at(pos).If(cond, thenpart, elsepart);
+        }
+        case FOR: {
+            nextToken();
+            accept(LPAREN);
+            List<JCStatement> inits = token.kind == SEMI ? List.nil() : forInit();
+            if (inits.length() == 1 &&
+                inits.head.hasTag(VARDEF) &&
+                ((JCVariableDecl) inits.head).init == null &&
+                token.kind == COLON) {
+                JCVariableDecl var = (JCVariableDecl)inits.head;
+                accept(COLON);
+                JCExpression expr = parseExpression();
+                accept(RPAREN);
+                JCStatement body = parseStatementAsBlock();
+                return F.at(pos).ForeachLoop(var, expr, body);
+            } else {
+                accept(SEMI);
+                JCExpression cond = token.kind == SEMI ? null : parseExpression();
+                accept(SEMI);
+                List<JCExpressionStatement> steps = token.kind == RPAREN ? List.nil() : forUpdate();
+                accept(RPAREN);
+                JCStatement body = parseStatementAsBlock();
+                return F.at(pos).ForLoop(inits, cond, steps, body);
+            }
+        }
+        case WHILE: {
+            nextToken();
+            JCExpression cond = parExpression();
+            JCStatement body = parseStatementAsBlock();
+            return F.at(pos).WhileLoop(cond, body);
+        }
+        case DO: {
+            nextToken();
+            JCStatement body = parseStatementAsBlock();
+            accept(WHILE);
+            JCExpression cond = parExpression();
+            accept(SEMI);
+            JCDoWhileLoop t = toP(F.at(pos).DoLoop(body, cond));
+            return t;
+        }
+        case TRY: {
+            nextToken();
+            List<JCTree> resources = List.nil();
+            if (token.kind == LPAREN) {
+                checkTryWithResources();
+                nextToken();
+                resources = resources();
+                accept(RPAREN);
+            }
+            JCBlock body = block();
+            ListBuffer<JCCatch> catchers = new ListBuffer<>();
+            JCBlock finalizer = null;
+            if (token.kind == CATCH || token.kind == FINALLY) {
+                while (token.kind == CATCH) catchers.append(catchClause());
+                if (token.kind == FINALLY) {
+                    nextToken();
+                    finalizer = block();
+                }
+            } else {
+                if (resources.isEmpty()) {
+                    if (allowTWR) {
+                        error(pos, "try.without.catch.finally.or.resource.decls");
+                    } else {
+                        error(pos, "try.without.catch.or.finally");
+                    }
+                }
+            }
+            return F.at(pos).Try(resources, body, catchers.toList(), finalizer);
+        }
+        case SWITCH: {
+            nextToken();
+            JCExpression selector = parExpression();
+            accept(LBRACE);
+            List<JCCase> cases = switchBlockStatementGroups();
+            JCSwitch t = to(F.at(pos).Switch(selector, cases));
+            accept(RBRACE);
+            return t;
+        }
+        case SYNCHRONIZED: {
+            nextToken();
+            JCExpression lock = parExpression();
+            JCBlock body = block();
+            return F.at(pos).Synchronized(lock, body);
+        }
+        case RETURN: {
+            nextToken();
+            JCExpression result = token.kind == SEMI ? null : parseExpression();
+            accept(SEMI);
+            JCReturn t = toP(F.at(pos).Return(result));
+            return t;
+        }
+        case THROW: {
+            nextToken();
+            JCExpression exc = parseExpression();
+            accept(SEMI);
+            JCThrow t = toP(F.at(pos).Throw(exc));
+            return t;
+        }
+        case BREAK: {
+            nextToken();
+            Name label = LAX_IDENTIFIER.accepts(token.kind) ? ident() : null;
+            accept(SEMI);
+            JCBreak t = toP(F.at(pos).Break(label));
+            return t;
+        }
+        case CONTINUE: {
+            nextToken();
+            Name label = LAX_IDENTIFIER.accepts(token.kind) ? ident() : null;
+            accept(SEMI);
+            JCContinue t =  toP(F.at(pos).Continue(label));
+            return t;
+        }
+        case SEMI:
+            nextToken();
+            return toP(F.at(pos).Skip());
+        case ELSE:
+            int elsePos = token.pos;
+            nextToken();
+            return doRecover(elsePos, BasicErrorRecoveryAction.BLOCK_STMT, "else.without.if");
+        case FINALLY:
+            int finallyPos = token.pos;
+            nextToken();
+            return doRecover(finallyPos, BasicErrorRecoveryAction.BLOCK_STMT, "finally.without.try");
+        case CATCH:
+            return doRecover(token.pos, BasicErrorRecoveryAction.CATCH_CLAUSE, "catch.without.try");
+        case ASSERT: {
+            nextToken();
+            JCExpression assertion = parseExpression();
+            JCExpression message = null;
+            if (token.kind == COLON) {
+                nextToken();
+                message = parseExpression();
+            }
+            accept(SEMI);
+            JCAssert t = toP(F.at(pos).Assert(assertion, message));
+            return t;
+        }
+        default:
+            Assert.error();
+            return null;
+        }
+    }
+
+    @Override
+    public JCStatement parseStatement() {
+        return parseStatementAsBlock();
+    }
+
+    private JCStatement doRecover(int startPos, ErrorRecoveryAction action, String key) {
+        int errPos = S.errPos();
+        JCTree stm = action.doRecover(this);
+        S.errPos(errPos);
+        return toP(F.Exec(syntaxError(startPos, List.of(stm), key)));
+    }
+
+    /** CatchClause     = CATCH "(" FormalParameter ")" Block
+     * TODO: the "FormalParameter" is not correct, it uses the special "catchTypes" rule below.
+     */
+    protected JCCatch catchClause() {
+        int pos = token.pos;
+        accept(CATCH);
+        accept(LPAREN);
+        JCModifiers mods = optFinal(Flags.PARAMETER);
+        List<JCExpression> catchTypes = catchTypes();
+        JCExpression paramType = catchTypes.size() > 1 ?
+                toP(F.at(catchTypes.head.getStartPosition()).TypeUnion(catchTypes)) :
+                catchTypes.head;
+        JCVariableDecl formal = variableDeclaratorId(mods, paramType);
+        accept(RPAREN);
+        JCBlock body = block();
+        return F.at(pos).Catch(formal, body);
+    }
+
+    List<JCExpression> catchTypes() {
+        ListBuffer<JCExpression> catchTypes = new ListBuffer<>();
+        catchTypes.add(parseType());
+        while (token.kind == BAR) {
+            checkMulticatch();
+            nextToken();
+            // Instead of qualident this is now parseType.
+            // But would that allow too much, e.g. arrays or generics?
+            catchTypes.add(parseType());
+        }
+        return catchTypes.toList();
+    }
+
+    /** SwitchBlockStatementGroups = { SwitchBlockStatementGroup }
+     *  SwitchBlockStatementGroup = SwitchLabel BlockStatements
+     *  SwitchLabel = CASE ConstantExpression ":" | DEFAULT ":"
+     */
+    List<JCCase> switchBlockStatementGroups() {
+        ListBuffer<JCCase> cases = new ListBuffer<>();
+        while (true) {
+            int pos = token.pos;
+            switch (token.kind) {
+            case CASE:
+            case DEFAULT:
+                cases.append(switchBlockStatementGroup());
+                break;
+            case RBRACE: case EOF:
+                return cases.toList();
+            default:
+                nextToken(); // to ensure progress
+                syntaxError(pos, "expected3",
+                    CASE, DEFAULT, RBRACE);
+            }
+        }
+    }
+
+    protected JCCase switchBlockStatementGroup() {
+        int pos = token.pos;
+        List<JCStatement> stats;
+        JCCase c;
+        switch (token.kind) {
+        case CASE:
+            nextToken();
+            JCExpression pat = parseExpression();
+            accept(COLON);
+            stats = blockStatements();
+            c = F.at(pos).Case(pat, stats);
+            if (stats.isEmpty())
+                storeEnd(c, S.prevToken().endPos);
+            return c;
+        case DEFAULT:
+            nextToken();
+            accept(COLON);
+            stats = blockStatements();
+            c = F.at(pos).Case(null, stats);
+            if (stats.isEmpty())
+                storeEnd(c, S.prevToken().endPos);
+            return c;
+        }
+        throw new AssertionError("should not reach here");
+    }
+
+    /** MoreStatementExpressions = { COMMA StatementExpression }
+     */
+    <T extends ListBuffer<? super JCExpressionStatement>> T moreStatementExpressions(int pos,
+                                                                    JCExpression first,
+                                                                    T stats) {
+        // This Exec is a "StatementExpression"; it subsumes no terminating token
+        stats.append(toP(F.at(pos).Exec(checkExprStat(first))));
+        while (token.kind == COMMA) {
+            nextToken();
+            pos = token.pos;
+            JCExpression t = parseExpression();
+            // This Exec is a "StatementExpression"; it subsumes no terminating token
+            stats.append(toP(F.at(pos).Exec(checkExprStat(t))));
+        }
+        return stats;
+    }
+
+    /** ForInit = StatementExpression MoreStatementExpressions
+     *           |  { FINAL | '@' Annotation } Type VariableDeclarators
+     */
+    List<JCStatement> forInit() {
+        ListBuffer<JCStatement> stats = new ListBuffer<>();
+        int pos = token.pos;
+        if (token.kind == FINAL || token.kind == MONKEYS_AT) {
+            return variableDeclarators(optFinal(0), parseType(), stats).toList();
+        } else {
+            JCExpression t = term(EXPR | TYPE);
+            if ((lastmode & TYPE) != 0 && LAX_IDENTIFIER.accepts(token.kind)) {
+                return variableDeclarators(modifiersOpt(), t, stats).toList();
+            } else if ((lastmode & TYPE) != 0 && token.kind == COLON) {
+                error(pos, "bad.initializer", "for-loop");
+                return List.of((JCStatement)F.at(pos).VarDef(null, null, t, null));
+            } else {
+                return moreStatementExpressions(pos, t, stats).toList();
+            }
+        }
+    }
+
+    /** ForUpdate = StatementExpression MoreStatementExpressions
+     */
+    List<JCExpressionStatement> forUpdate() {
+        return moreStatementExpressions(token.pos,
+                                        parseExpression(),
+                                        new ListBuffer<JCExpressionStatement>()).toList();
+    }
+
+    /** AnnotationsOpt = { '@' Annotation }
+     *
+     * @param kind Whether to parse an ANNOTATION or TYPE_ANNOTATION
+     */
+    protected List<JCAnnotation> annotationsOpt(Tag kind) {
+        if (token.kind != MONKEYS_AT) return List.nil(); // optimization
+        ListBuffer<JCAnnotation> buf = new ListBuffer<>();
+        int prevmode = mode;
+        while (token.kind == MONKEYS_AT) {
+            int pos = token.pos;
+            nextToken();
+            buf.append(annotation(pos, kind));
+        }
+        lastmode = mode;
+        mode = prevmode;
+        List<JCAnnotation> annotations = buf.toList();
+
+        return annotations;
+    }
+
+    List<JCAnnotation> typeAnnotationsOpt() {
+        List<JCAnnotation> annotations = annotationsOpt(Tag.TYPE_ANNOTATION);
+        return annotations;
+    }
+
+    /** ModifiersOpt = { Modifier }
+     *  Modifier = PUBLIC | PROTECTED | PRIVATE | STATIC | ABSTRACT | FINAL
+     *           | NATIVE | SYNCHRONIZED | TRANSIENT | VOLATILE | "@"
+     *           | "@" Annotation
+     */
+    protected JCModifiers modifiersOpt() {
+        return modifiersOpt(null);
+    }
+    protected JCModifiers modifiersOpt(JCModifiers partial) {
+        long flags;
+        ListBuffer<JCAnnotation> annotations = new ListBuffer<>();
+        int pos;
+        if (partial == null) {
+            flags = 0;
+            pos = token.pos;
+        } else {
+            flags = partial.flags;
+            annotations.appendList(partial.annotations);
+            pos = partial.pos;
+        }
+        if (token.deprecatedFlag()) {
+            flags |= Flags.DEPRECATED;
+        }
+        int lastPos;
+    loop:
+        while (true) {
+            long flag;
+            switch (token.kind) {
+            case PRIVATE     : flag = Flags.PRIVATE; break;
+            case PROTECTED   : flag = Flags.PROTECTED; break;
+            case PUBLIC      : flag = Flags.PUBLIC; break;
+            case STATIC      : flag = Flags.STATIC; break;
+            case TRANSIENT   : flag = Flags.TRANSIENT; break;
+            case FINAL       : flag = Flags.FINAL; break;
+            case ABSTRACT    : flag = Flags.ABSTRACT; break;
+            case NATIVE      : flag = Flags.NATIVE; break;
+            case VOLATILE    : flag = Flags.VOLATILE; break;
+            case SYNCHRONIZED: flag = Flags.SYNCHRONIZED; break;
+            case STRICTFP    : flag = Flags.STRICTFP; break;
+            case MONKEYS_AT  : flag = Flags.ANNOTATION; break;
+            case DEFAULT     : checkDefaultMethods(); flag = Flags.DEFAULT; break;
+            case ERROR       : flag = 0; nextToken(); break;
+            default: break loop;
+            }
+            if ((flags & flag) != 0) error(token.pos, "repeated.modifier");
+            lastPos = token.pos;
+            nextToken();
+            if (flag == Flags.ANNOTATION) {
+                if (token.kind != INTERFACE) {
+                    JCAnnotation ann = annotation(lastPos, Tag.ANNOTATION);
+                    // if first modifier is an annotation, set pos to annotation's.
+                    if (flags == 0 && annotations.isEmpty())
+                        pos = ann.pos;
+                    annotations.append(ann);
+                    flag = 0;
+                }
+            }
+            flags |= flag;
+        }
+        switch (token.kind) {
+        case ENUM: flags |= Flags.ENUM; break;
+        case INTERFACE: flags |= Flags.INTERFACE; break;
+        default: break;
+        }
+
+        /* A modifiers tree with no modifier tokens or annotations
+         * has no text position. */
+        if ((flags & (Flags.ModifierFlags | Flags.ANNOTATION)) == 0 && annotations.isEmpty())
+            pos = Position.NOPOS;
+
+        JCModifiers mods = F.at(pos).Modifiers(flags, annotations.toList());
+        if (pos != Position.NOPOS)
+            storeEnd(mods, S.prevToken().endPos);
+        return mods;
+    }
+
+    /** Annotation              = "@" Qualident [ "(" AnnotationFieldValues ")" ]
+     *
+     * @param pos position of "@" token
+     * @param kind Whether to parse an ANNOTATION or TYPE_ANNOTATION
+     */
+    JCAnnotation annotation(int pos, Tag kind) {
+        // accept(AT); // AT consumed by caller
+        if (kind == Tag.TYPE_ANNOTATION) {
+            checkTypeAnnotations();
+        }
+        JCTree ident = qualident(false);
+        List<JCExpression> fieldValues = annotationFieldValuesOpt();
+        JCAnnotation ann;
+        if (kind == Tag.ANNOTATION) {
+            ann = F.at(pos).Annotation(ident, fieldValues);
+        } else if (kind == Tag.TYPE_ANNOTATION) {
+            ann = F.at(pos).TypeAnnotation(ident, fieldValues);
+        } else {
+            throw new AssertionError("Unhandled annotation kind: " + kind);
+        }
+
+        storeEnd(ann, S.prevToken().endPos);
+        return ann;
+    }
+
+    List<JCExpression> annotationFieldValuesOpt() {
+        return (token.kind == LPAREN) ? annotationFieldValues() : List.nil();
+    }
+
+    /** AnnotationFieldValues   = "(" [ AnnotationFieldValue { "," AnnotationFieldValue } ] ")" */
+    List<JCExpression> annotationFieldValues() {
+        accept(LPAREN);
+        ListBuffer<JCExpression> buf = new ListBuffer<>();
+        if (token.kind != RPAREN) {
+            buf.append(annotationFieldValue());
+            while (token.kind == COMMA) {
+                nextToken();
+                buf.append(annotationFieldValue());
+            }
+        }
+        accept(RPAREN);
+        return buf.toList();
+    }
+
+    /** AnnotationFieldValue    = AnnotationValue
+     *                          | Identifier "=" AnnotationValue
+     */
+    JCExpression annotationFieldValue() {
+        if (LAX_IDENTIFIER.accepts(token.kind)) {
+            mode = EXPR;
+            JCExpression t1 = term1();
+            if (t1.hasTag(IDENT) && token.kind == EQ) {
+                int pos = token.pos;
+                accept(EQ);
+                JCExpression v = annotationValue();
+                return toP(F.at(pos).Assign(t1, v));
+            } else {
+                return t1;
+            }
+        }
+        return annotationValue();
+    }
+
+    /* AnnotationValue          = ConditionalExpression
+     *                          | Annotation
+     *                          | "{" [ AnnotationValue { "," AnnotationValue } ] [","] "}"
+     */
+    JCExpression annotationValue() {
+        int pos;
+        switch (token.kind) {
+        case MONKEYS_AT:
+            pos = token.pos;
+            nextToken();
+            return annotation(pos, Tag.ANNOTATION);
+        case LBRACE:
+            pos = token.pos;
+            accept(LBRACE);
+            ListBuffer<JCExpression> buf = new ListBuffer<>();
+            if (token.kind == COMMA) {
+                nextToken();
+            } else if (token.kind != RBRACE) {
+                buf.append(annotationValue());
+                while (token.kind == COMMA) {
+                    nextToken();
+                    if (token.kind == RBRACE) break;
+                    buf.append(annotationValue());
+                }
+            }
+            accept(RBRACE);
+            return toP(F.at(pos).NewArray(null, List.nil(), buf.toList()));
+        default:
+            mode = EXPR;
+            return term1();
+        }
+    }
+
+    /** VariableDeclarators = VariableDeclarator { "," VariableDeclarator }
+     */
+    public <T extends ListBuffer<? super JCVariableDecl>> T variableDeclarators(JCModifiers mods,
+                                                                         JCExpression type,
+                                                                         T vdefs)
+    {
+        return variableDeclaratorsRest(token.pos, mods, type, ident(), false, null, vdefs);
+    }
+
+    /** VariableDeclaratorsRest = VariableDeclaratorRest { "," VariableDeclarator }
+     *  ConstantDeclaratorsRest = ConstantDeclaratorRest { "," ConstantDeclarator }
+     *
+     *  @param reqInit  Is an initializer always required?
+     *  @param dc       The documentation comment for the variable declarations, or null.
+     */
+    protected <T extends ListBuffer<? super JCVariableDecl>> T variableDeclaratorsRest(int pos,
+                                                                     JCModifiers mods,
+                                                                     JCExpression type,
+                                                                     Name name,
+                                                                     boolean reqInit,
+                                                                     Comment dc,
+                                                                     T vdefs)
+    {
+        vdefs.append(variableDeclaratorRest(pos, mods, type, name, reqInit, dc));
+        while (token.kind == COMMA) {
+            // All but last of multiple declarators subsume a comma
+            storeEnd((JCTree)vdefs.last(), token.endPos);
+            nextToken();
+            vdefs.append(variableDeclarator(mods, type, reqInit, dc));
+        }
+        return vdefs;
+    }
+
+    /** VariableDeclarator = Ident VariableDeclaratorRest
+     *  ConstantDeclarator = Ident ConstantDeclaratorRest
+     */
+    JCVariableDecl variableDeclarator(JCModifiers mods, JCExpression type, boolean reqInit, Comment dc) {
+        return variableDeclaratorRest(token.pos, mods, type, ident(), reqInit, dc);
+    }
+
+    /** VariableDeclaratorRest = BracketsOpt ["=" VariableInitializer]
+     *  ConstantDeclaratorRest = BracketsOpt "=" VariableInitializer
+     *
+     *  @param reqInit  Is an initializer always required?
+     *  @param dc       The documentation comment for the variable declarations, or null.
+     */
+    JCVariableDecl variableDeclaratorRest(int pos, JCModifiers mods, JCExpression type, Name name,
+                                  boolean reqInit, Comment dc) {
+        type = bracketsOpt(type);
+        JCExpression init = null;
+        if (token.kind == EQ) {
+            nextToken();
+            init = variableInitializer();
+        }
+        else if (reqInit) syntaxError(token.pos, "expected", EQ);
+        JCVariableDecl result =
+            toP(F.at(pos).VarDef(mods, name, type, init));
+        attach(result, dc);
+        return result;
+    }
+
+    /** VariableDeclaratorId = Ident BracketsOpt
+     */
+    JCVariableDecl variableDeclaratorId(JCModifiers mods, JCExpression type) {
+        return variableDeclaratorId(mods, type, false);
+    }
+    //where
+    JCVariableDecl variableDeclaratorId(JCModifiers mods, JCExpression type, boolean lambdaParameter) {
+        int pos = token.pos;
+        Name name;
+        if (lambdaParameter && token.kind == UNDERSCORE) {
+            log.error(pos, Errors.UnderscoreAsIdentifierInLambda);
+            name = token.name();
+            nextToken();
+        } else {
+            if (allowThisIdent && !lambdaParameter) {
+                JCExpression pn = qualident(false);
+                if (pn.hasTag(Tag.IDENT) && ((JCIdent)pn).name != names._this) {
+                    name = ((JCIdent)pn).name;
+                } else {
+                    if ((mods.flags & Flags.VARARGS) != 0) {
+                        log.error(token.pos, Errors.VarargsAndReceiver);
+                    }
+                    if (token.kind == LBRACKET) {
+                        log.error(token.pos, Errors.ArrayAndReceiver);
+                    }
+                    return toP(F.at(pos).ReceiverVarDef(mods, pn, type));
+                }
+            } else {
+                name = ident();
+            }
+        }
+        if ((mods.flags & Flags.VARARGS) != 0 &&
+                token.kind == LBRACKET) {
+            log.error(token.pos, Errors.VarargsAndOldArraySyntax);
+        }
+        type = bracketsOpt(type);
+        return toP(F.at(pos).VarDef(mods, name, type, null));
+    }
+
+    /** Resources = Resource { ";" Resources }
+     */
+    List<JCTree> resources() {
+        ListBuffer<JCTree> defs = new ListBuffer<>();
+        defs.append(resource());
+        while (token.kind == SEMI) {
+            // All but last of multiple declarators must subsume a semicolon
+            storeEnd(defs.last(), token.endPos);
+            int semiColonPos = token.pos;
+            nextToken();
+            if (token.kind == RPAREN) { // Optional trailing semicolon
+                                       // after last resource
+                break;
+            }
+            defs.append(resource());
+        }
+        return defs.toList();
+    }
+
+    /** Resource = VariableModifiersOpt Type VariableDeclaratorId "=" Expression
+     *           | Expression
+     */
+    protected JCTree resource() {
+        int startPos = token.pos;
+        if (token.kind == FINAL || token.kind == MONKEYS_AT) {
+            JCModifiers mods = optFinal(Flags.FINAL);
+            JCExpression t = parseType();
+            return variableDeclaratorRest(token.pos, mods, t, ident(), true, null);
+        }
+        JCExpression t = term(EXPR | TYPE);
+        if ((lastmode & TYPE) != 0 && LAX_IDENTIFIER.accepts(token.kind)) {
+            JCModifiers mods = toP(F.at(startPos).Modifiers(Flags.FINAL));
+            return variableDeclaratorRest(token.pos, mods, t, ident(), true, null);
+        } else {
+            checkVariableInTryWithResources(startPos);
+            if (!t.hasTag(IDENT) && !t.hasTag(SELECT)) {
+                log.error(t.pos(), Errors.TryWithResourcesExprNeedsVar);
+            }
+
+            return t;
+        }
+    }
+
+    /** CompilationUnit = [ { "@" Annotation } PACKAGE Qualident ";"] {ImportDeclaration} {TypeDeclaration}
+     */
+    public JCTree.JCCompilationUnit parseCompilationUnit() {
+        Token firstToken = token;
+        JCModifiers mods = null;
+        boolean consumedToplevelDoc = false;
+        boolean seenImport = false;
+        boolean seenPackage = false;
+        ListBuffer<JCTree> defs = new ListBuffer<>();
+        if (token.kind == MONKEYS_AT)
+            mods = modifiersOpt();
+
+        if (token.kind == PACKAGE) {
+            int packagePos = token.pos;
+            List<JCAnnotation> annotations = List.nil();
+            seenPackage = true;
+            if (mods != null) {
+                checkNoMods(mods.flags);
+                annotations = mods.annotations;
+                mods = null;
+            }
+            nextToken();
+            JCExpression pid = qualident(false);
+            accept(SEMI);
+            JCPackageDecl pd = F.at(packagePos).PackageDecl(annotations, pid);
+            attach(pd, firstToken.comment(CommentStyle.JAVADOC));
+            consumedToplevelDoc = true;
+            storeEnd(pd, token.pos);
+            defs.append(pd);
+        }
+
+        boolean checkForImports = true;
+        boolean firstTypeDecl = true;
+        while (token.kind != EOF) {
+            if (token.pos <= endPosTable.errorEndPos) {
+                // error recovery
+                skip(checkForImports, false, false, false);
+                if (token.kind == EOF)
+                    break;
+            }
+            if (checkForImports && mods == null && token.kind == IMPORT) {
+                seenImport = true;
+                defs.append(importDeclaration());
+            } else {
+                Comment docComment = token.comment(CommentStyle.JAVADOC);
+                if (firstTypeDecl && !seenImport && !seenPackage) {
+                    docComment = firstToken.comment(CommentStyle.JAVADOC);
+                    consumedToplevelDoc = true;
+                }
+                if (mods != null || token.kind != SEMI)
+                    mods = modifiersOpt(mods);
+                if (firstTypeDecl && token.kind == IDENTIFIER) {
+                    ModuleKind kind = ModuleKind.STRONG;
+                    if (token.name() == names.open) {
+                        kind = ModuleKind.OPEN;
+                        nextToken();
+                    }
+                    if (token.kind == IDENTIFIER && token.name() == names.module) {
+                        if (mods != null) {
+                            checkNoMods(mods.flags & ~Flags.DEPRECATED);
+                        }
+                        defs.append(moduleDecl(mods, kind, docComment));
+                        consumedToplevelDoc = true;
+                        break;
+                    } else if (kind != ModuleKind.STRONG) {
+                        reportSyntaxError(token.pos, "expected.module");
+                    }
+                }
+                JCTree def = typeDeclaration(mods, docComment);
+                if (def instanceof JCExpressionStatement)
+                    def = ((JCExpressionStatement)def).expr;
+                defs.append(def);
+                if (def instanceof JCClassDecl)
+                    checkForImports = false;
+                mods = null;
+                firstTypeDecl = false;
+            }
+        }
+        JCTree.JCCompilationUnit toplevel = F.at(firstToken.pos).TopLevel(defs.toList());
+        if (!consumedToplevelDoc)
+            attach(toplevel, firstToken.comment(CommentStyle.JAVADOC));
+        if (defs.isEmpty())
+            storeEnd(toplevel, S.prevToken().endPos);
+        if (keepDocComments)
+            toplevel.docComments = docComments;
+        if (keepLineMap)
+            toplevel.lineMap = S.getLineMap();
+        this.endPosTable.setParser(null); // remove reference to parser
+        toplevel.endPositions = this.endPosTable;
+        return toplevel;
+    }
+
+    JCModuleDecl moduleDecl(JCModifiers mods, ModuleKind kind, Comment dc) {
+        int pos = token.pos;
+        if (!allowModules) {
+            log.error(pos, Errors.ModulesNotSupportedInSource(source.name));
+            allowModules = true;
+        }
+
+        nextToken();
+        JCExpression name = qualident(false);
+        List<JCDirective> directives = null;
+
+        accept(LBRACE);
+        directives = moduleDirectiveList();
+        accept(RBRACE);
+        accept(EOF);
+
+        JCModuleDecl result = toP(F.at(pos).ModuleDef(mods, kind, name, directives));
+        attach(result, dc);
+        return result;
+    }
+
+    List<JCDirective> moduleDirectiveList() {
+        ListBuffer<JCDirective> defs = new ListBuffer<>();
+        while (token.kind == IDENTIFIER) {
+            int pos = token.pos;
+            if (token.name() == names.requires) {
+                nextToken();
+                boolean isTransitive = false;
+                boolean isStaticPhase = false;
+            loop:
+                while (true) {
+                    switch (token.kind) {
+                        case IDENTIFIER:
+                            if (token.name() == names.transitive && !isTransitive) {
+                                Token t1 = S.token(1);
+                                if (t1.kind == SEMI || t1.kind == DOT) {
+                                    break loop;
+                                }
+                                isTransitive = true;
+                                break;
+                            } else {
+                                break loop;
+                            }
+                        case STATIC:
+                            if (isStaticPhase) {
+                                error(token.pos, "repeated.modifier");
+                            }
+                            isStaticPhase = true;
+                            break;
+                        default:
+                            break loop;
+                    }
+                    nextToken();
+                }
+                JCExpression moduleName = qualident(false);
+                accept(SEMI);
+                defs.append(toP(F.at(pos).Requires(isTransitive, isStaticPhase, moduleName)));
+            } else if (token.name() == names.exports || token.name() == names.opens) {
+                boolean exports = token.name() == names.exports;
+                nextToken();
+                JCExpression pkgName = qualident(false);
+                List<JCExpression> moduleNames = null;
+                if (token.kind == IDENTIFIER && token.name() == names.to) {
+                    nextToken();
+                    moduleNames = qualidentList(false);
+                }
+                accept(SEMI);
+                JCDirective d;
+                if (exports) {
+                    d = F.at(pos).Exports(pkgName, moduleNames);
+                } else {
+                    d = F.at(pos).Opens(pkgName, moduleNames);
+                }
+                defs.append(toP(d));
+            } else if (token.name() == names.provides) {
+                nextToken();
+                JCExpression serviceName = qualident(false);
+                if (token.kind == IDENTIFIER && token.name() == names.with) {
+                    nextToken();
+                    List<JCExpression> implNames = qualidentList(false);
+                    accept(SEMI);
+                    defs.append(toP(F.at(pos).Provides(serviceName, implNames)));
+                } else {
+                    error(token.pos, "expected", "'" + names.with + "'");
+                    skip(false, false, false, false);
+                }
+            } else if (token.name() == names.uses) {
+                nextToken();
+                JCExpression service = qualident(false);
+                accept(SEMI);
+                defs.append(toP(F.at(pos).Uses(service)));
+            } else {
+                setErrorEndPos(pos);
+                reportSyntaxError(pos, "invalid.module.directive");
+                break;
+            }
+        }
+        return defs.toList();
+    }
+
+    /** ImportDeclaration = IMPORT [ STATIC ] Ident { "." Ident } [ "." "*" ] ";"
+     */
+    protected JCTree importDeclaration() {
+        int pos = token.pos;
+        nextToken();
+        boolean importStatic = false;
+        if (token.kind == STATIC) {
+            importStatic = true;
+            nextToken();
+        }
+        JCExpression pid = toP(F.at(token.pos).Ident(ident()));
+        do {
+            int pos1 = token.pos;
+            accept(DOT);
+            if (token.kind == STAR) {
+                pid = to(F.at(pos1).Select(pid, names.asterisk));
+                nextToken();
+                break;
+            } else {
+                pid = toP(F.at(pos1).Select(pid, ident()));
+            }
+        } while (token.kind == DOT);
+        accept(SEMI);
+        return toP(F.at(pos).Import(pid, importStatic));
+    }
+
+    /** TypeDeclaration = ClassOrInterfaceOrEnumDeclaration
+     *                  | ";"
+     */
+    JCTree typeDeclaration(JCModifiers mods, Comment docComment) {
+        int pos = token.pos;
+        if (mods == null && token.kind == SEMI) {
+            nextToken();
+            return toP(F.at(pos).Skip());
+        } else {
+            return classOrInterfaceOrEnumDeclaration(modifiersOpt(mods), docComment);
+        }
+    }
+
+    /** ClassOrInterfaceOrEnumDeclaration = ModifiersOpt
+     *           (ClassDeclaration | InterfaceDeclaration | EnumDeclaration)
+     *  @param mods     Any modifiers starting the class or interface declaration
+     *  @param dc       The documentation comment for the class, or null.
+     */
+    protected JCStatement classOrInterfaceOrEnumDeclaration(JCModifiers mods, Comment dc) {
+        if (token.kind == CLASS) {
+            return classDeclaration(mods, dc);
+        } else if (token.kind == INTERFACE) {
+            return interfaceDeclaration(mods, dc);
+        } else if (token.kind == ENUM) {
+            return enumDeclaration(mods, dc);
+        } else {
+            int pos = token.pos;
+            List<JCTree> errs;
+            if (LAX_IDENTIFIER.accepts(token.kind)) {
+                errs = List.of(mods, toP(F.at(pos).Ident(ident())));
+                setErrorEndPos(token.pos);
+            } else {
+                errs = List.of(mods);
+            }
+            final JCErroneous erroneousTree;
+            if (parseModuleInfo) {
+                erroneousTree = syntaxError(pos, errs, "expected.module.or.open");
+            } else {
+                erroneousTree = syntaxError(pos, errs, "expected3", CLASS, INTERFACE, ENUM);
+            }
+            return toP(F.Exec(erroneousTree));
+        }
+    }
+
+    /** ClassDeclaration = CLASS Ident TypeParametersOpt [EXTENDS Type]
+     *                     [IMPLEMENTS TypeList] ClassBody
+     *  @param mods    The modifiers starting the class declaration
+     *  @param dc       The documentation comment for the class, or null.
+     */
+    protected JCClassDecl classDeclaration(JCModifiers mods, Comment dc) {
+        int pos = token.pos;
+        accept(CLASS);
+        Name name = ident();
+
+        List<JCTypeParameter> typarams = typeParametersOpt();
+
+        JCExpression extending = null;
+        if (token.kind == EXTENDS) {
+            nextToken();
+            extending = parseType();
+        }
+        List<JCExpression> implementing = List.nil();
+        if (token.kind == IMPLEMENTS) {
+            nextToken();
+            implementing = typeList();
+        }
+        List<JCTree> defs = classOrInterfaceBody(name, false);
+        JCClassDecl result = toP(F.at(pos).ClassDef(
+            mods, name, typarams, extending, implementing, defs));
+        attach(result, dc);
+        return result;
+    }
+
+    /** InterfaceDeclaration = INTERFACE Ident TypeParametersOpt
+     *                         [EXTENDS TypeList] InterfaceBody
+     *  @param mods    The modifiers starting the interface declaration
+     *  @param dc       The documentation comment for the interface, or null.
+     */
+    protected JCClassDecl interfaceDeclaration(JCModifiers mods, Comment dc) {
+        int pos = token.pos;
+        accept(INTERFACE);
+        Name name = ident();
+
+        List<JCTypeParameter> typarams = typeParametersOpt();
+
+        List<JCExpression> extending = List.nil();
+        if (token.kind == EXTENDS) {
+            nextToken();
+            extending = typeList();
+        }
+        List<JCTree> defs = classOrInterfaceBody(name, true);
+        JCClassDecl result = toP(F.at(pos).ClassDef(
+            mods, name, typarams, null, extending, defs));
+        attach(result, dc);
+        return result;
+    }
+
+    /** EnumDeclaration = ENUM Ident [IMPLEMENTS TypeList] EnumBody
+     *  @param mods    The modifiers starting the enum declaration
+     *  @param dc       The documentation comment for the enum, or null.
+     */
+    protected JCClassDecl enumDeclaration(JCModifiers mods, Comment dc) {
+        int pos = token.pos;
+        accept(ENUM);
+        Name name = ident();
+
+        List<JCExpression> implementing = List.nil();
+        if (token.kind == IMPLEMENTS) {
+            nextToken();
+            implementing = typeList();
+        }
+
+        List<JCTree> defs = enumBody(name);
+        mods.flags |= Flags.ENUM;
+        JCClassDecl result = toP(F.at(pos).
+            ClassDef(mods, name, List.nil(),
+                     null, implementing, defs));
+        attach(result, dc);
+        return result;
+    }
+
+    /** EnumBody = "{" { EnumeratorDeclarationList } [","]
+     *                  [ ";" {ClassBodyDeclaration} ] "}"
+     */
+    List<JCTree> enumBody(Name enumName) {
+        accept(LBRACE);
+        ListBuffer<JCTree> defs = new ListBuffer<>();
+        if (token.kind == COMMA) {
+            nextToken();
+        } else if (token.kind != RBRACE && token.kind != SEMI) {
+            defs.append(enumeratorDeclaration(enumName));
+            while (token.kind == COMMA) {
+                nextToken();
+                if (token.kind == RBRACE || token.kind == SEMI) break;
+                defs.append(enumeratorDeclaration(enumName));
+            }
+            if (token.kind != SEMI && token.kind != RBRACE) {
+                defs.append(syntaxError(token.pos, "expected3",
+                                COMMA, RBRACE, SEMI));
+                nextToken();
+            }
+        }
+        if (token.kind == SEMI) {
+            nextToken();
+            while (token.kind != RBRACE && token.kind != EOF) {
+                defs.appendList(classOrInterfaceBodyDeclaration(enumName,
+                                                                false));
+                if (token.pos <= endPosTable.errorEndPos) {
+                    // error recovery
+                   skip(false, true, true, false);
+                }
+            }
+        }
+        accept(RBRACE);
+        return defs.toList();
+    }
+
+    /** EnumeratorDeclaration = AnnotationsOpt [TypeArguments] IDENTIFIER [ Arguments ] [ "{" ClassBody "}" ]
+     */
+    JCTree enumeratorDeclaration(Name enumName) {
+        Comment dc = token.comment(CommentStyle.JAVADOC);
+        int flags = Flags.PUBLIC|Flags.STATIC|Flags.FINAL|Flags.ENUM;
+        if (token.deprecatedFlag()) {
+            flags |= Flags.DEPRECATED;
+        }
+        int pos = token.pos;
+        List<JCAnnotation> annotations = annotationsOpt(Tag.ANNOTATION);
+        JCModifiers mods = F.at(annotations.isEmpty() ? Position.NOPOS : pos).Modifiers(flags, annotations);
+        List<JCExpression> typeArgs = typeArgumentsOpt();
+        int identPos = token.pos;
+        Name name = ident();
+        int createPos = token.pos;
+        List<JCExpression> args = (token.kind == LPAREN)
+            ? arguments() : List.nil();
+        JCClassDecl body = null;
+        if (token.kind == LBRACE) {
+            JCModifiers mods1 = F.at(Position.NOPOS).Modifiers(Flags.ENUM);
+            List<JCTree> defs = classOrInterfaceBody(names.empty, false);
+            body = toP(F.at(identPos).AnonymousClassDef(mods1, defs));
+        }
+        if (args.isEmpty() && body == null)
+            createPos = identPos;
+        JCIdent ident = F.at(identPos).Ident(enumName);
+        JCNewClass create = F.at(createPos).NewClass(null, typeArgs, ident, args, body);
+        if (createPos != identPos)
+            storeEnd(create, S.prevToken().endPos);
+        ident = F.at(identPos).Ident(enumName);
+        JCTree result = toP(F.at(pos).VarDef(mods, name, ident, create));
+        attach(result, dc);
+        return result;
+    }
+
+    /** TypeList = Type {"," Type}
+     */
+    List<JCExpression> typeList() {
+        ListBuffer<JCExpression> ts = new ListBuffer<>();
+        ts.append(parseType());
+        while (token.kind == COMMA) {
+            nextToken();
+            ts.append(parseType());
+        }
+        return ts.toList();
+    }
+
+    /** ClassBody     = "{" {ClassBodyDeclaration} "}"
+     *  InterfaceBody = "{" {InterfaceBodyDeclaration} "}"
+     */
+    List<JCTree> classOrInterfaceBody(Name className, boolean isInterface) {
+        accept(LBRACE);
+        if (token.pos <= endPosTable.errorEndPos) {
+            // error recovery
+            skip(false, true, false, false);
+            if (token.kind == LBRACE)
+                nextToken();
+        }
+        ListBuffer<JCTree> defs = new ListBuffer<>();
+        while (token.kind != RBRACE && token.kind != EOF) {
+            defs.appendList(classOrInterfaceBodyDeclaration(className, isInterface));
+            if (token.pos <= endPosTable.errorEndPos) {
+               // error recovery
+               skip(false, true, true, false);
+           }
+        }
+        accept(RBRACE);
+        return defs.toList();
+    }
+
+    /** ClassBodyDeclaration =
+     *      ";"
+     *    | [STATIC] Block
+     *    | ModifiersOpt
+     *      ( Type Ident
+     *        ( VariableDeclaratorsRest ";" | MethodDeclaratorRest )
+     *      | VOID Ident VoidMethodDeclaratorRest
+     *      | TypeParameters [Annotations]
+     *        ( Type Ident MethodDeclaratorRest
+     *        | VOID Ident VoidMethodDeclaratorRest
+     *        )
+     *      | Ident ConstructorDeclaratorRest
+     *      | TypeParameters Ident ConstructorDeclaratorRest
+     *      | ClassOrInterfaceOrEnumDeclaration
+     *      )
+     *  InterfaceBodyDeclaration =
+     *      ";"
+     *    | ModifiersOpt
+     *      ( Type Ident
+     *        ( ConstantDeclaratorsRest ";" | MethodDeclaratorRest )
+     *      | VOID Ident MethodDeclaratorRest
+     *      | TypeParameters [Annotations]
+     *        ( Type Ident MethodDeclaratorRest
+     *        | VOID Ident VoidMethodDeclaratorRest
+     *        )
+     *      | ClassOrInterfaceOrEnumDeclaration
+     *      )
+     *
+     */
+    protected List<JCTree> classOrInterfaceBodyDeclaration(Name className, boolean isInterface) {
+        if (token.kind == SEMI) {
+            nextToken();
+            return List.nil();
+        } else {
+            Comment dc = token.comment(CommentStyle.JAVADOC);
+            int pos = token.pos;
+            JCModifiers mods = modifiersOpt();
+            if (token.kind == CLASS ||
+                token.kind == INTERFACE ||
+                token.kind == ENUM) {
+                return List.of(classOrInterfaceOrEnumDeclaration(mods, dc));
+            } else if (token.kind == LBRACE &&
+                       (mods.flags & Flags.StandardFlags & ~Flags.STATIC) == 0 &&
+                       mods.annotations.isEmpty()) {
+                if (isInterface) {
+                    error(token.pos, "initializer.not.allowed");
+                }
+                return List.of(block(pos, mods.flags));
+            } else {
+                pos = token.pos;
+                List<JCTypeParameter> typarams = typeParametersOpt();
+                // if there are type parameters but no modifiers, save the start
+                // position of the method in the modifiers.
+                if (typarams.nonEmpty() && mods.pos == Position.NOPOS) {
+                    mods.pos = pos;
+                    storeEnd(mods, pos);
+                }
+                List<JCAnnotation> annosAfterParams = annotationsOpt(Tag.ANNOTATION);
+
+                if (annosAfterParams.nonEmpty()) {
+                    checkAnnotationsAfterTypeParams(annosAfterParams.head.pos);
+                    mods.annotations = mods.annotations.appendList(annosAfterParams);
+                    if (mods.pos == Position.NOPOS)
+                        mods.pos = mods.annotations.head.pos;
+                }
+
+                Token tk = token;
+                pos = token.pos;
+                JCExpression type;
+                boolean isVoid = token.kind == VOID;
+                if (isVoid) {
+                    type = to(F.at(pos).TypeIdent(TypeTag.VOID));
+                    nextToken();
+                } else {
+                    // method returns types are un-annotated types
+                    type = unannotatedType();
+                }
+                if (token.kind == LPAREN && !isInterface && type.hasTag(IDENT)) {
+                    if (isInterface || tk.name() != className)
+                        error(pos, "invalid.meth.decl.ret.type.req");
+                    else if (annosAfterParams.nonEmpty())
+                        illegal(annosAfterParams.head.pos);
+                    return List.of(methodDeclaratorRest(
+                        pos, mods, null, names.init, typarams,
+                        isInterface, true, dc));
+                } else {
+                    pos = token.pos;
+                    Name name = ident();
+                    if (token.kind == LPAREN) {
+                        return List.of(methodDeclaratorRest(
+                            pos, mods, type, name, typarams,
+                            isInterface, isVoid, dc));
+                    } else if (!isVoid && typarams.isEmpty()) {
+                        List<JCTree> defs =
+                            variableDeclaratorsRest(pos, mods, type, name, isInterface, dc,
+                                                    new ListBuffer<JCTree>()).toList();
+                        accept(SEMI);
+                        storeEnd(defs.last(), S.prevToken().endPos);
+                        return defs;
+                    } else {
+                        pos = token.pos;
+                        List<JCTree> err = isVoid
+                            ? List.of(toP(F.at(pos).MethodDef(mods, name, type, typarams,
+                                List.nil(), List.nil(), null, null)))
+                            : null;
+                        return List.of(syntaxError(token.pos, err, "expected", LPAREN));
+                    }
+                }
+            }
+        }
+    }
+
+    /** MethodDeclaratorRest =
+     *      FormalParameters BracketsOpt [THROWS TypeList] ( MethodBody | [DEFAULT AnnotationValue] ";")
+     *  VoidMethodDeclaratorRest =
+     *      FormalParameters [THROWS TypeList] ( MethodBody | ";")
+     *  ConstructorDeclaratorRest =
+     *      "(" FormalParameterListOpt ")" [THROWS TypeList] MethodBody
+     */
+    protected JCTree methodDeclaratorRest(int pos,
+                              JCModifiers mods,
+                              JCExpression type,
+                              Name name,
+                              List<JCTypeParameter> typarams,
+                              boolean isInterface, boolean isVoid,
+                              Comment dc) {
+        if (isInterface) {
+            if ((mods.flags & Flags.STATIC) != 0) {
+                checkStaticInterfaceMethods();
+            }
+            if ((mods.flags & Flags.PRIVATE) != 0) {
+                checkPrivateInterfaceMethods();
+            }
+        }
+        JCVariableDecl prevReceiverParam = this.receiverParam;
+        try {
+            this.receiverParam = null;
+            // Parsing formalParameters sets the receiverParam, if present
+            List<JCVariableDecl> params = formalParameters();
+            if (!isVoid) type = bracketsOpt(type);
+            List<JCExpression> thrown = List.nil();
+            if (token.kind == THROWS) {
+                nextToken();
+                thrown = qualidentList(true);
+            }
+            JCBlock body = null;
+            JCExpression defaultValue;
+            if (token.kind == LBRACE) {
+                body = block();
+                defaultValue = null;
+            } else {
+                if (token.kind == DEFAULT) {
+                    accept(DEFAULT);
+                    defaultValue = annotationValue();
+                } else {
+                    defaultValue = null;
+                }
+                accept(SEMI);
+                if (token.pos <= endPosTable.errorEndPos) {
+                    // error recovery
+                    skip(false, true, false, false);
+                    if (token.kind == LBRACE) {
+                        body = block();
+                    }
+                }
+            }
+
+            JCMethodDecl result =
+                    toP(F.at(pos).MethodDef(mods, name, type, typarams,
+                                            receiverParam, params, thrown,
+                                            body, defaultValue));
+            attach(result, dc);
+            return result;
+        } finally {
+            this.receiverParam = prevReceiverParam;
+        }
+    }
+
+    /** QualidentList = [Annotations] Qualident {"," [Annotations] Qualident}
+     */
+    List<JCExpression> qualidentList(boolean allowAnnos) {
+        ListBuffer<JCExpression> ts = new ListBuffer<>();
+
+        List<JCAnnotation> typeAnnos = allowAnnos ? typeAnnotationsOpt() : List.nil();
+        JCExpression qi = qualident(allowAnnos);
+        if (!typeAnnos.isEmpty()) {
+            JCExpression at = insertAnnotationsToMostInner(qi, typeAnnos, false);
+            ts.append(at);
+        } else {
+            ts.append(qi);
+        }
+        while (token.kind == COMMA) {
+            nextToken();
+
+            typeAnnos = allowAnnos ? typeAnnotationsOpt() : List.nil();
+            qi = qualident(allowAnnos);
+            if (!typeAnnos.isEmpty()) {
+                JCExpression at = insertAnnotationsToMostInner(qi, typeAnnos, false);
+                ts.append(at);
+            } else {
+                ts.append(qi);
+            }
+        }
+        return ts.toList();
+    }
+
+    /**
+     *  {@literal
+     *  TypeParametersOpt = ["<" TypeParameter {"," TypeParameter} ">"]
+     *  }
+     */
+    protected List<JCTypeParameter> typeParametersOpt() {
+        if (token.kind == LT) {
+            ListBuffer<JCTypeParameter> typarams = new ListBuffer<>();
+            nextToken();
+            typarams.append(typeParameter());
+            while (token.kind == COMMA) {
+                nextToken();
+                typarams.append(typeParameter());
+            }
+            accept(GT);
+            return typarams.toList();
+        } else {
+            return List.nil();
+        }
+    }
+
+    /**
+     *  {@literal
+     *  TypeParameter = [Annotations] TypeVariable [TypeParameterBound]
+     *  TypeParameterBound = EXTENDS Type {"&" Type}
+     *  TypeVariable = Ident
+     *  }
+     */
+    JCTypeParameter typeParameter() {
+        int pos = token.pos;
+        List<JCAnnotation> annos = typeAnnotationsOpt();
+        Name name = ident();
+        ListBuffer<JCExpression> bounds = new ListBuffer<>();
+        if (token.kind == EXTENDS) {
+            nextToken();
+            bounds.append(parseType());
+            while (token.kind == AMP) {
+                nextToken();
+                bounds.append(parseType());
+            }
+        }
+        return toP(F.at(pos).TypeParameter(name, bounds.toList(), annos));
+    }
+
+    /** FormalParameters = "(" [ FormalParameterList ] ")"
+     *  FormalParameterList = [ FormalParameterListNovarargs , ] LastFormalParameter
+     *  FormalParameterListNovarargs = [ FormalParameterListNovarargs , ] FormalParameter
+     */
+    List<JCVariableDecl> formalParameters() {
+        return formalParameters(false);
+    }
+    List<JCVariableDecl> formalParameters(boolean lambdaParameters) {
+        ListBuffer<JCVariableDecl> params = new ListBuffer<>();
+        JCVariableDecl lastParam;
+        accept(LPAREN);
+        if (token.kind != RPAREN) {
+            this.allowThisIdent = true;
+            lastParam = formalParameter(lambdaParameters);
+            if (lastParam.nameexpr != null) {
+                this.receiverParam = lastParam;
+            } else {
+                params.append(lastParam);
+            }
+            this.allowThisIdent = false;
+            while (token.kind == COMMA) {
+                if ((lastParam.mods.flags & Flags.VARARGS) != 0) {
+                    error(lastParam, "varargs.must.be.last");
+                }
+                nextToken();
+                params.append(lastParam = formalParameter(lambdaParameters));
+            }
+        }
+        if (token.kind == RPAREN) {
+            nextToken();
+        } else {
+            setErrorEndPos(token.pos);
+            reportSyntaxError(S.prevToken().endPos, "expected3", COMMA, RPAREN, LBRACKET);
+        }
+        return params.toList();
+    }
+
+    List<JCVariableDecl> implicitParameters(boolean hasParens) {
+        if (hasParens) {
+            accept(LPAREN);
+        }
+        ListBuffer<JCVariableDecl> params = new ListBuffer<>();
+        if (token.kind != RPAREN && token.kind != ARROW) {
+            params.append(implicitParameter());
+            while (token.kind == COMMA) {
+                nextToken();
+                params.append(implicitParameter());
+            }
+        }
+        if (hasParens) {
+            accept(RPAREN);
+        }
+        return params.toList();
+    }
+
+    JCModifiers optFinal(long flags) {
+        JCModifiers mods = modifiersOpt();
+        checkNoMods(mods.flags & ~(Flags.FINAL | Flags.DEPRECATED));
+        mods.flags |= flags;
+        return mods;
+    }
+
+    /**
+     * Inserts the annotations (and possibly a new array level)
+     * to the left-most type in an array or nested type.
+     *
+     * When parsing a type like {@code @B Outer.Inner @A []}, the
+     * {@code @A} annotation should target the array itself, while
+     * {@code @B} targets the nested type {@code Outer}.
+     *
+     * Currently the parser parses the annotation first, then
+     * the array, and then inserts the annotation to the left-most
+     * nested type.
+     *
+     * When {@code createNewLevel} is true, then a new array
+     * level is inserted as the most inner type, and have the
+     * annotations target it.  This is useful in the case of
+     * varargs, e.g. {@code String @A [] @B ...}, as the parser
+     * first parses the type {@code String @A []} then inserts
+     * a new array level with {@code @B} annotation.
+     */
+    private JCExpression insertAnnotationsToMostInner(
+            JCExpression type, List<JCAnnotation> annos,
+            boolean createNewLevel) {
+        int origEndPos = getEndPos(type);
+        JCExpression mostInnerType = type;
+        JCArrayTypeTree mostInnerArrayType = null;
+        while (TreeInfo.typeIn(mostInnerType).hasTag(TYPEARRAY)) {
+            mostInnerArrayType = (JCArrayTypeTree) TreeInfo.typeIn(mostInnerType);
+            mostInnerType = mostInnerArrayType.elemtype;
+        }
+
+        if (createNewLevel) {
+            mostInnerType = to(F.at(token.pos).TypeArray(mostInnerType));
+        }
+
+        JCExpression mostInnerTypeToReturn = mostInnerType;
+        if (annos.nonEmpty()) {
+            JCExpression lastToModify = mostInnerType;
+
+            while (TreeInfo.typeIn(mostInnerType).hasTag(SELECT) ||
+                    TreeInfo.typeIn(mostInnerType).hasTag(TYPEAPPLY)) {
+                while (TreeInfo.typeIn(mostInnerType).hasTag(SELECT)) {
+                    lastToModify = mostInnerType;
+                    mostInnerType = ((JCFieldAccess) TreeInfo.typeIn(mostInnerType)).getExpression();
+                }
+                while (TreeInfo.typeIn(mostInnerType).hasTag(TYPEAPPLY)) {
+                    lastToModify = mostInnerType;
+                    mostInnerType = ((JCTypeApply) TreeInfo.typeIn(mostInnerType)).clazz;
+                }
+            }
+
+            mostInnerType = F.at(annos.head.pos).AnnotatedType(annos, mostInnerType);
+
+            if (TreeInfo.typeIn(lastToModify).hasTag(TYPEAPPLY)) {
+                ((JCTypeApply) TreeInfo.typeIn(lastToModify)).clazz = mostInnerType;
+            } else if (TreeInfo.typeIn(lastToModify).hasTag(SELECT)) {
+                ((JCFieldAccess) TreeInfo.typeIn(lastToModify)).selected = mostInnerType;
+            } else {
+                // We never saw a SELECT or TYPEAPPLY, return the annotated type.
+                mostInnerTypeToReturn = mostInnerType;
+            }
+        }
+
+        if (mostInnerArrayType == null) {
+            return mostInnerTypeToReturn;
+        } else {
+            mostInnerArrayType.elemtype = mostInnerTypeToReturn;
+            storeEnd(type, origEndPos);
+            return type;
+        }
+    }
+
+    /** FormalParameter = { FINAL | '@' Annotation } Type VariableDeclaratorId
+     *  LastFormalParameter = { FINAL | '@' Annotation } Type '...' Ident | FormalParameter
+     */
+    protected JCVariableDecl formalParameter() {
+        return formalParameter(false);
+    }
+    protected JCVariableDecl formalParameter(boolean lambdaParameter) {
+        JCModifiers mods = optFinal(Flags.PARAMETER);
+        // need to distinguish between vararg annos and array annos
+        // look at typeAnnotationsPushedBack comment
+        this.permitTypeAnnotationsPushBack = true;
+        JCExpression type = parseType();
+        this.permitTypeAnnotationsPushBack = false;
+
+        if (token.kind == ELLIPSIS) {
+            List<JCAnnotation> varargsAnnos = typeAnnotationsPushedBack;
+            typeAnnotationsPushedBack = List.nil();
+            mods.flags |= Flags.VARARGS;
+            // insert var arg type annotations
+            type = insertAnnotationsToMostInner(type, varargsAnnos, true);
+            nextToken();
+        } else {
+            // if not a var arg, then typeAnnotationsPushedBack should be null
+            if (typeAnnotationsPushedBack.nonEmpty()) {
+                reportSyntaxError(typeAnnotationsPushedBack.head.pos,
+                        "illegal.start.of.type");
+            }
+            typeAnnotationsPushedBack = List.nil();
+        }
+        return variableDeclaratorId(mods, type, lambdaParameter);
+    }
+
+    protected JCVariableDecl implicitParameter() {
+        JCModifiers mods = F.at(token.pos).Modifiers(Flags.PARAMETER);
+        return variableDeclaratorId(mods, null, true);
+    }
+
+/* ---------- auxiliary methods -------------- */
+
+    void error(int pos, String key, Object ... args) {
+        log.error(DiagnosticFlag.SYNTAX, pos, key, args);
+    }
+
+    void error(DiagnosticPosition pos, String key, Object ... args) {
+        log.error(DiagnosticFlag.SYNTAX, pos, key, args);
+    }
+
+    void warning(int pos, String key, Object ... args) {
+        log.warning(pos, key, args);
+    }
+
+    /** Check that given tree is a legal expression statement.
+     */
+    protected JCExpression checkExprStat(JCExpression t) {
+        if (!TreeInfo.isExpressionStatement(t)) {
+            JCExpression ret = F.at(t.pos).Erroneous(List.<JCTree>of(t));
+            error(ret, "not.stmt");
+            return ret;
+        } else {
+            return t;
+        }
+    }
+
+    /** Return precedence of operator represented by token,
+     *  -1 if token is not a binary operator. @see TreeInfo.opPrec
+     */
+    static int prec(TokenKind token) {
+        JCTree.Tag oc = optag(token);
+        return (oc != NO_TAG) ? TreeInfo.opPrec(oc) : -1;
+    }
+
+    /**
+     * Return the lesser of two positions, making allowance for either one
+     * being unset.
+     */
+    static int earlier(int pos1, int pos2) {
+        if (pos1 == Position.NOPOS)
+            return pos2;
+        if (pos2 == Position.NOPOS)
+            return pos1;
+        return (pos1 < pos2 ? pos1 : pos2);
+    }
+
+    /** Return operation tag of binary operator represented by token,
+     *  No_TAG if token is not a binary operator.
+     */
+    static JCTree.Tag optag(TokenKind token) {
+        switch (token) {
+        case BARBAR:
+            return OR;
+        case AMPAMP:
+            return AND;
+        case BAR:
+            return BITOR;
+        case BAREQ:
+            return BITOR_ASG;
+        case CARET:
+            return BITXOR;
+        case CARETEQ:
+            return BITXOR_ASG;
+        case AMP:
+            return BITAND;
+        case AMPEQ:
+            return BITAND_ASG;
+        case EQEQ:
+            return JCTree.Tag.EQ;
+        case BANGEQ:
+            return NE;
+        case LT:
+            return JCTree.Tag.LT;
+        case GT:
+            return JCTree.Tag.GT;
+        case LTEQ:
+            return LE;
+        case GTEQ:
+            return GE;
+        case LTLT:
+            return SL;
+        case LTLTEQ:
+            return SL_ASG;
+        case GTGT:
+            return SR;
+        case GTGTEQ:
+            return SR_ASG;
+        case GTGTGT:
+            return USR;
+        case GTGTGTEQ:
+            return USR_ASG;
+        case PLUS:
+            return JCTree.Tag.PLUS;
+        case PLUSEQ:
+            return PLUS_ASG;
+        case SUB:
+            return MINUS;
+        case SUBEQ:
+            return MINUS_ASG;
+        case STAR:
+            return MUL;
+        case STAREQ:
+            return MUL_ASG;
+        case SLASH:
+            return DIV;
+        case SLASHEQ:
+            return DIV_ASG;
+        case PERCENT:
+            return MOD;
+        case PERCENTEQ:
+            return MOD_ASG;
+        case INSTANCEOF:
+            return TYPETEST;
+        default:
+            return NO_TAG;
+        }
+    }
+
+    /** Return operation tag of unary operator represented by token,
+     *  No_TAG if token is not a binary operator.
+     */
+    static JCTree.Tag unoptag(TokenKind token) {
+        switch (token) {
+        case PLUS:
+            return POS;
+        case SUB:
+            return NEG;
+        case BANG:
+            return NOT;
+        case TILDE:
+            return COMPL;
+        case PLUSPLUS:
+            return PREINC;
+        case SUBSUB:
+            return PREDEC;
+        default:
+            return NO_TAG;
+        }
+    }
+
+    /** Return type tag of basic type represented by token,
+     *  NONE if token is not a basic type identifier.
+     */
+    static TypeTag typetag(TokenKind token) {
+        switch (token) {
+        case BYTE:
+            return TypeTag.BYTE;
+        case CHAR:
+            return TypeTag.CHAR;
+        case SHORT:
+            return TypeTag.SHORT;
+        case INT:
+            return TypeTag.INT;
+        case LONG:
+            return TypeTag.LONG;
+        case FLOAT:
+            return TypeTag.FLOAT;
+        case DOUBLE:
+            return TypeTag.DOUBLE;
+        case BOOLEAN:
+            return TypeTag.BOOLEAN;
+        default:
+            return TypeTag.NONE;
+        }
+    }
+
+    void checkDiamond() {
+        if (!allowDiamond) {
+            log.error(DiagnosticFlag.SOURCE_LEVEL, token.pos, Errors.DiamondNotSupportedInSource(source.name));
+        }
+    }
+    void checkMulticatch() {
+        if (!allowMulticatch) {
+            log.error(DiagnosticFlag.SOURCE_LEVEL, token.pos, Errors.MulticatchNotSupportedInSource(source.name));
+        }
+    }
+    void checkTryWithResources() {
+        if (!allowTWR) {
+            log.error(DiagnosticFlag.SOURCE_LEVEL, token.pos, Errors.TryWithResourcesNotSupportedInSource(source.name));
+        }
+    }
+    void checkVariableInTryWithResources(int startPos) {
+        if (!allowEffectivelyFinalVariablesInTWR) {
+            log.error(DiagnosticFlag.SOURCE_LEVEL, startPos, Errors.VarInTryWithResourcesNotSupportedInSource(source.name));
+        }
+    }
+    void checkLambda() {
+        if (!allowLambda) {
+            log.error(DiagnosticFlag.SOURCE_LEVEL, token.pos, Errors.LambdaNotSupportedInSource(source.name));
+        }
+    }
+    void checkMethodReferences() {
+        if (!allowMethodReferences) {
+            log.error(DiagnosticFlag.SOURCE_LEVEL, token.pos, Errors.MethodReferencesNotSupportedInSource(source.name));
+        }
+    }
+    void checkDefaultMethods() {
+        if (!allowDefaultMethods) {
+            log.error(DiagnosticFlag.SOURCE_LEVEL, token.pos, Errors.DefaultMethodsNotSupportedInSource(source.name));
+        }
+    }
+    void checkIntersectionTypesInCast() {
+        if (!allowIntersectionTypesInCast) {
+            log.error(DiagnosticFlag.SOURCE_LEVEL, token.pos, Errors.IntersectionTypesInCastNotSupportedInSource(source.name));
+        }
+    }
+    void checkStaticInterfaceMethods() {
+        if (!allowStaticInterfaceMethods) {
+            log.error(DiagnosticFlag.SOURCE_LEVEL, token.pos, Errors.StaticIntfMethodsNotSupportedInSource(source.name));
+        }
+    }
+    void checkTypeAnnotations() {
+        if (!allowTypeAnnotations) {
+            log.error(DiagnosticFlag.SOURCE_LEVEL, token.pos, Errors.TypeAnnotationsNotSupportedInSource(source.name));
+        }
+    }
+    void checkPrivateInterfaceMethods() {
+        if (!allowPrivateInterfaceMethods) {
+            log.error(DiagnosticFlag.SOURCE_LEVEL, token.pos, CompilerProperties.Errors.PrivateIntfMethodsNotSupportedInSource(source.name));
+        }
+    }
+    protected void checkAnnotationsAfterTypeParams(int pos) {
+        if (!allowAnnotationsAfterTypeParams) {
+            log.error(DiagnosticFlag.SOURCE_LEVEL, pos, Errors.AnnotationsAfterTypeParamsNotSupportedInSource(source.name));
+        }
+    }
+
+    /*
+     * a functional source tree and end position mappings
+     */
+    protected static class SimpleEndPosTable extends AbstractEndPosTable {
+
+        private final IntHashTable endPosMap;
+
+        SimpleEndPosTable(JavacParser parser) {
+            super(parser);
+            endPosMap = new IntHashTable();
+        }
+
+        public void storeEnd(JCTree tree, int endpos) {
+            endPosMap.putAtIndex(tree, errorEndPos > endpos ? errorEndPos : endpos,
+                                 endPosMap.lookup(tree));
+        }
+
+        protected <T extends JCTree> T to(T t) {
+            storeEnd(t, parser.token.endPos);
+            return t;
+        }
+
+        protected <T extends JCTree> T toP(T t) {
+            storeEnd(t, parser.S.prevToken().endPos);
+            return t;
+        }
+
+        public int getEndPos(JCTree tree) {
+            int value = endPosMap.getFromIndex(endPosMap.lookup(tree));
+            // As long as Position.NOPOS==-1, this just returns value.
+            return (value == -1) ? Position.NOPOS : value;
+        }
+
+        public int replaceTree(JCTree oldTree, JCTree newTree) {
+            int pos = endPosMap.remove(oldTree);
+            if (pos != -1) {
+                storeEnd(newTree, pos);
+                return pos;
+            }
+            return Position.NOPOS;
+        }
+    }
+
+    /*
+     * a default skeletal implementation without any mapping overhead.
+     */
+    protected static class EmptyEndPosTable extends AbstractEndPosTable {
+
+        EmptyEndPosTable(JavacParser parser) {
+            super(parser);
+        }
+
+        public void storeEnd(JCTree tree, int endpos) { /* empty */ }
+
+        protected <T extends JCTree> T to(T t) {
+            return t;
+        }
+
+        protected <T extends JCTree> T toP(T t) {
+            return t;
+        }
+
+        public int getEndPos(JCTree tree) {
+            return Position.NOPOS;
+        }
+
+        public int replaceTree(JCTree oldTree, JCTree newTree) {
+            return Position.NOPOS;
+        }
+
+    }
+
+    protected static abstract class AbstractEndPosTable implements EndPosTable {
+        /**
+         * The current parser.
+         */
+        protected JavacParser parser;
+
+        /**
+         * Store the last error position.
+         */
+        public int errorEndPos = Position.NOPOS;
+
+        public AbstractEndPosTable(JavacParser parser) {
+            this.parser = parser;
+        }
+
+        /**
+         * Store current token's ending position for a tree, the value of which
+         * will be the greater of last error position and the ending position of
+         * the current token.
+         * @param t The tree.
+         */
+        protected abstract <T extends JCTree> T to(T t);
+
+        /**
+         * Store current token's ending position for a tree, the value of which
+         * will be the greater of last error position and the ending position of
+         * the previous token.
+         * @param t The tree.
+         */
+        protected abstract <T extends JCTree> T toP(T t);
+
+        /**
+         * Set the error position during the parsing phases, the value of which
+         * will be set only if it is greater than the last stored error position.
+         * @param errPos The error position
+         */
+        public void setErrorEndPos(int errPos) {
+            if (errPos > errorEndPos) {
+                errorEndPos = errPos;
+            }
+        }
+
+        public void setParser(JavacParser parser) {
+            this.parser = parser;
+        }
+    }
+}