diff -r 000000000000 -r 9a66ca7c79fa src/share/classes/com/sun/tools/javac/parser/Parser.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/share/classes/com/sun/tools/javac/parser/Parser.java Sat Dec 01 00:00:00 2007 +0000 @@ -0,0 +1,2863 @@ +/* + * Copyright 1999-2006 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +package com.sun.tools.javac.parser; + +import java.util.*; + +import com.sun.tools.javac.tree.*; +import com.sun.tools.javac.code.*; +import com.sun.tools.javac.util.*; +import com.sun.tools.javac.util.List; +import static com.sun.tools.javac.util.ListBuffer.lb; + +import com.sun.tools.javac.tree.JCTree.*; + +import static com.sun.tools.javac.parser.Token.*; + +/** 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. + * + *

This is NOT part of any API supported by Sun Microsystems. 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. + */ +public class Parser { + + /** A factory for creating parsers. */ + public static class Factory { + /** The context key for the parser factory. */ + protected static final Context.Key parserFactoryKey = + new Context.Key(); + + /** Get the Factory instance for this context. */ + public static Factory instance(Context context) { + Factory instance = context.get(parserFactoryKey); + if (instance == null) + instance = new Factory(context); + return instance; + } + + final TreeMaker F; + final Log log; + final Keywords keywords; + final Source source; + final Name.Table names; + final Options options; + + /** Create a new parser factory. */ + protected Factory(Context context) { + context.put(parserFactoryKey, this); + this.F = TreeMaker.instance(context); + this.log = Log.instance(context); + this.names = Name.Table.instance(context); + this.keywords = Keywords.instance(context); + this.source = Source.instance(context); + this.options = Options.instance(context); + } + + /** + * Create a new Parser. + * @param S Lexer for getting tokens while parsing + * @param keepDocComments true if javadoc comments should be kept + * @param genEndPos true if end positions should be generated + */ + public Parser newParser(Lexer S, boolean keepDocComments, boolean genEndPos) { + if (!genEndPos) + return new Parser(this, S, keepDocComments); + else + return new EndPosParser(this, S, keepDocComments); + } + } + + /** The number of precedence levels of infix operators. + */ + private static final int infixPrecedenceLevels = 10; + + /** The scanner used for lexical analysis. + */ + private 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 keyword table. */ + private Keywords keywords; + + /** The Source language setting. */ + private Source source; + + /** The name table. */ + private Name.Table names; + + /** Construct a parser from a given scanner, tree factory and log. + */ + protected Parser(Factory fac, + Lexer S, + boolean keepDocComments) { + this.S = S; + S.nextToken(); // prime the pump + this.F = fac.F; + this.log = fac.log; + this.names = fac.names; + this.keywords = fac.keywords; + this.source = fac.source; + Options options = fac.options; + this.allowGenerics = source.allowGenerics(); + this.allowVarargs = source.allowVarargs(); + this.allowAsserts = source.allowAsserts(); + this.allowEnums = source.allowEnums(); + this.allowForeach = source.allowForeach(); + this.allowStaticImport = source.allowStaticImport(); + this.allowAnnotations = source.allowAnnotations(); + this.keepDocComments = keepDocComments; + if (keepDocComments) docComments = new HashMap(); + this.errorTree = F.Erroneous(); + } + + /** Switch: Should generics be recognized? + */ + boolean allowGenerics; + + /** Switch: Should varargs be recognized? + */ + boolean allowVarargs; + + /** Switch: should we recognize assert statements, or just give a warning? + */ + boolean allowAsserts; + + /** Switch: should we recognize enums, or just give a warning? + */ + boolean allowEnums; + + /** Switch: should we recognize foreach? + */ + boolean allowForeach; + + /** Switch: should we recognize foreach? + */ + boolean allowStaticImport; + + /** Switch: should we recognize annotations? + */ + boolean allowAnnotations; + + /** Switch: should we keep docComments? + */ + boolean keepDocComments; + + /** 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 + */ + static final int EXPR = 1; + static final int TYPE = 2; + static final int NOPARAMS = 4; + static final int TYPEARG = 8; + + /** The current mode. + */ + private int mode = 0; + + /** The mode of the term that was parsed last. + */ + private int lastmode = 0; + +/* ---------- error recovery -------------- */ + + private JCErroneous errorTree; + + /** Skip forward until a suitable stop token is found. + */ + private void skip(boolean stopAtImport, boolean stopAtMemberDecl, boolean stopAtIdentifier, boolean stopAtStatement) { + while (true) { + switch (S.token()) { + case SEMI: + S.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 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: + if (stopAtStatement) + return; + break; + } + S.nextToken(); + } + } + + private JCErroneous syntaxError(int pos, String key, Object... arg) { + return syntaxError(pos, null, key, arg); + } + + private JCErroneous syntaxError(int pos, List errs, String key, Object... arg) { + setErrorEndPos(pos); + reportSyntaxError(pos, key, arg); + return toP(F.at(pos).Erroneous(errs)); + } + + private int errorPos = Position.NOPOS; + /** + * Report a syntax error at given position using the given + * argument unless one was already reported at the same position. + */ + private void reportSyntaxError(int pos, String key, Object... arg) { + if (pos > S.errPos() || pos == Position.NOPOS) { + if (S.token() == EOF) + log.error(pos, "premature.eof"); + else + log.error(pos, key, arg); + } + S.errPos(pos); + if (S.pos() == errorPos) + S.nextToken(); // guarantee progress + errorPos = S.pos(); + } + + + /** Generate a syntax error at current position unless one was already + * reported at the same position. + */ + private JCErroneous syntaxError(String key) { + return syntaxError(S.pos(), key); + } + + /** Generate a syntax error at current position unless one was + * already reported at the same position. + */ + private JCErroneous syntaxError(String key, String arg) { + return syntaxError(S.pos(), key, arg); + } + + /** If next input token matches given token, skip it, otherwise report + * an error. + */ + public void accept(Token token) { + if (S.token() == token) { + S.nextToken(); + } else { + setErrorEndPos(S.pos()); + reportSyntaxError(S.prevEndPos(), "expected", keywords.token2string(token)); + } + } + + /** Report an illegal start of expression/type error at given position. + */ + JCExpression illegal(int pos) { + setErrorEndPos(S.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(S.pos()); + } + + /** Diagnose a modifier flag from the set, if any. */ + void checkNoMods(long mods) { + if (mods != 0) { + long lowestMod = mods & -mods; + log.error(S.pos(), "mod.not.allowed.here", + Flags.toString(lowestMod).trim()); + } + } + +/* ---------- doc comments --------- */ + + /** A hashtable to store all documentation comments + * indexed by the tree nodes they refer to. + * defined only if option flag keepDocComment is set. + */ + Map 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. + */ + void attach(JCTree tree, String dc) { + if (keepDocComments && dc != null) { +// System.out.println("doc comment = ");System.out.println(dc);//DEBUG + docComments.put(tree, dc); + } + } + +/* -------- source positions ------- */ + + private int errorEndPos = -1; + + private void setErrorEndPos(int errPos) { + if (errPos > errorEndPos) + errorEndPos = errPos; + } + + protected int getErrorEndPos() { + return errorEndPos; + } + + /** + * Store ending position for a tree. + * @param tree The tree. + * @param endpos The ending position to associate with the tree. + */ + protected void storeEnd(JCTree tree, int endpos) {} + + /** + * Store ending position for a tree. The ending position should + * be the ending position of the current token. + * @param t The tree. + */ + protected T to(T t) { return t; } + + /** + * Store ending position for a tree. The ending position should + * be greater of the ending position of the previous token and errorEndPos. + * @param t The tree. + */ + protected T toP(T t) { return 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 Position.NOPOS; + } + + + +/* ---------- parsing -------------- */ + + /** + * Ident = IDENTIFIER + */ + Name ident() { + if (S.token() == IDENTIFIER) { + Name name = S.name(); + S.nextToken(); + return name; + } else if (S.token() == ASSERT) { + if (allowAsserts) { + log.error(S.pos(), "assert.as.identifier"); + S.nextToken(); + return names.error; + } else { + log.warning(S.pos(), "assert.as.identifier"); + Name name = S.name(); + S.nextToken(); + return name; + } + } else if (S.token() == ENUM) { + if (allowEnums) { + log.error(S.pos(), "enum.as.identifier"); + S.nextToken(); + return names.error; + } else { + log.warning(S.pos(), "enum.as.identifier"); + Name name = S.name(); + S.nextToken(); + return name; + } + } else { + accept(IDENTIFIER); + return names.error; + } +} + + /** + * Qualident = Ident { DOT Ident } + */ + public JCExpression qualident() { + JCExpression t = toP(F.at(S.pos()).Ident(ident())); + while (S.token() == DOT) { + int pos = S.pos(); + S.nextToken(); + t = toP(F.at(pos).Select(t, ident())); + } + return t; + } + + /** + * Literal = + * INTLITERAL + * | LONGLITERAL + * | FLOATLITERAL + * | DOUBLELITERAL + * | CHARLITERAL + * | STRINGLITERAL + * | TRUE + * | FALSE + * | NULL + */ + JCExpression literal(Name prefix) { + int pos = S.pos(); + JCExpression t = errorTree; + switch (S.token()) { + case INTLITERAL: + try { + t = F.at(pos).Literal( + TypeTags.INT, + Convert.string2int(strval(prefix), S.radix())); + } catch (NumberFormatException ex) { + log.error(S.pos(), "int.number.too.large", strval(prefix)); + } + break; + case LONGLITERAL: + try { + t = F.at(pos).Literal( + TypeTags.LONG, + new Long(Convert.string2long(strval(prefix), S.radix()))); + } catch (NumberFormatException ex) { + log.error(S.pos(), "int.number.too.large", strval(prefix)); + } + break; + case FLOATLITERAL: { + String proper = (S.radix() == 16 ? ("0x"+ S.stringVal()) : S.stringVal()); + Float n; + try { + n = Float.valueOf(proper); + } catch (NumberFormatException ex) { + // error already repoted in scanner + n = Float.NaN; + } + if (n.floatValue() == 0.0f && !isZero(proper)) + log.error(S.pos(), "fp.number.too.small"); + else if (n.floatValue() == Float.POSITIVE_INFINITY) + log.error(S.pos(), "fp.number.too.large"); + else + t = F.at(pos).Literal(TypeTags.FLOAT, n); + break; + } + case DOUBLELITERAL: { + String proper = (S.radix() == 16 ? ("0x"+ S.stringVal()) : S.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)) + log.error(S.pos(), "fp.number.too.small"); + else if (n.doubleValue() == Double.POSITIVE_INFINITY) + log.error(S.pos(), "fp.number.too.large"); + else + t = F.at(pos).Literal(TypeTags.DOUBLE, n); + break; + } + case CHARLITERAL: + t = F.at(pos).Literal( + TypeTags.CHAR, + S.stringVal().charAt(0) + 0); + break; + case STRINGLITERAL: + t = F.at(pos).Literal( + TypeTags.CLASS, + S.stringVal()); + break; + case TRUE: case FALSE: + t = F.at(pos).Literal( + TypeTags.BOOLEAN, + (S.token() == TRUE ? 1 : 0)); + break; + case NULL: + t = F.at(pos).Literal( + TypeTags.BOT, + null); + break; + default: + assert false; + } + if (t == errorTree) + t = F.at(pos).Erroneous(); + storeEnd(t, S.endPos()); + S.nextToken(); + return t; + } +//where + boolean isZero(String s) { + char[] cs = s.toCharArray(); + int base = ((Character.toLowerCase(s.charAt(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 = S.stringVal(); + return (prefix.len == 0) ? s : prefix + s; + } + + /** terms can be either expressions or types. + */ + public JCExpression expression() { + return term(EXPR); + } + + public JCExpression type() { + return term(TYPE); + } + + JCExpression term(int newmode) { + int prevmode = mode; + mode = newmode; + JCExpression t = term(); + lastmode = mode; + mode = prevmode; + return t; + } + + /** + * Expression = Expression1 [ExpressionRest] + * ExpressionRest = [AssignmentOperator Expression1] + * AssignmentOperator = "=" | "+=" | "-=" | "*=" | "/=" | + * "&=" | "|=" | "^=" | + * "%=" | "<<=" | ">>=" | ">>>=" + * Type = Type1 + * TypeNoParams = TypeNoParams1 + * StatementExpression = Expression + * ConstantExpression = Expression + */ + JCExpression term() { + JCExpression t = term1(); + if ((mode & EXPR) != 0 && + S.token() == EQ || PLUSEQ.compareTo(S.token()) <= 0 && S.token().compareTo(GTGTGTEQ) <= 0) + return termRest(t); + else + return t; + } + + JCExpression termRest(JCExpression t) { + switch (S.token()) { + case EQ: { + int pos = S.pos(); + S.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 = S.pos(); + Token token = S.token(); + S.nextToken(); + mode = EXPR; + JCExpression t1 = term(); + return F.at(pos).Assignop(optag(token), t, t1); + default: + return t; + } + } + + /** Expression1 = Expression2 [Expression1Rest] + * Type1 = Type2 + * TypeNoParams1 = TypeNoParams2 + */ + JCExpression term1() { + JCExpression t = term2(); + if ((mode & EXPR) != 0 && S.token() == QUES) { + mode = EXPR; + return term1Rest(t); + } else { + return t; + } + } + + /** Expression1Rest = ["?" Expression ":" Expression1] + */ + JCExpression term1Rest(JCExpression t) { + if (S.token() == QUES) { + int pos = S.pos(); + S.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(S.token()) >= 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) { + List savedOd = odStackSupply.elems; + JCExpression[] odStack = newOdStack(); + List savedOp = opStackSupply.elems; + Token[] opStack = newOpStack(); + // optimization, was odStack = new Tree[...]; opStack = new Tree[...]; + int top = 0; + odStack[0] = t; + int startPos = S.pos(); + Token topOp = ERROR; + while (prec(S.token()) >= minprec) { + opStack[top] = topOp; + top++; + topOp = S.token(); + int pos = S.pos(); + S.nextToken(); + odStack[top] = topOp == INSTANCEOF ? type() : term3(); + while (top > 0 && prec(topOp) >= prec(S.token())) { + odStack[top-1] = makeOp(pos, topOp, odStack[top-1], + odStack[top]); + top--; + topOp = opStack[top]; + } + } + assert top == 0; + t = odStack[0]; + + if (t.getTag() == JCTree.PLUS) { + StringBuffer buf = foldStrings(t); + if (buf != null) { + t = toP(F.at(startPos).Literal(TypeTags.CLASS, buf.toString())); + } + } + + odStackSupply.elems = savedOd; // optimization + opStackSupply.elems = savedOp; // optimization + return t; + } +//where + /** Construct a binary or type test node. + */ + private JCExpression makeOp(int pos, + Token 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 StringBuffer foldStrings(JCTree tree) { + List buf = List.nil(); + while (true) { + if (tree.getTag() == JCTree.LITERAL) { + JCLiteral lit = (JCLiteral) tree; + if (lit.typetag == TypeTags.CLASS) { + StringBuffer sbuf = + new StringBuffer((String)lit.value); + while (buf.nonEmpty()) { + sbuf.append(buf.head); + buf = buf.tail; + } + return sbuf; + } + } else if (tree.getTag() == JCTree.PLUS) { + JCBinary op = (JCBinary)tree; + if (op.rhs.getTag() == JCTree.LITERAL) { + JCLiteral lit = (JCLiteral) op.rhs; + if (lit.typetag == TypeTags.CLASS) { + buf = buf.prepend((String) lit.value); + tree = op.lhs; + continue; + } + } + } + return null; + } + } + + /** optimization: To save allocating a new operand/operator stack + * for every binary operation, we use supplys. + */ + ListBuffer odStackSupply = new ListBuffer(); + ListBuffer opStackSupply = new ListBuffer(); + + private JCExpression[] newOdStack() { + if (odStackSupply.elems == odStackSupply.last) + odStackSupply.append(new JCExpression[infixPrecedenceLevels + 1]); + JCExpression[] odStack = odStackSupply.elems.head; + odStackSupply.elems = odStackSupply.elems.tail; + return odStack; + } + + private Token[] newOpStack() { + if (opStackSupply.elems == opStackSupply.last) + opStackSupply.append(new Token[infixPrecedenceLevels + 1]); + Token[] opStack = opStackSupply.elems.head; + opStackSupply.elems = opStackSupply.elems.tail; + return opStack; + } + + /** Expression3 = PrefixOp Expression3 + * | "(" Expr | TypeNoParams ")" Expression3 + * | Primary {Selector} {PostfixOp} + * Primary = "(" Expression ")" + * | Literal + * | [TypeArguments] THIS [Arguments] + * | [TypeArguments] SUPER SuperSuffix + * | NEW [TypeArguments] Creator + * | Ident { "." Ident } + * [ "[" ( "]" 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 = S.pos(); + JCExpression t; + List typeArgs = typeArgumentsOpt(EXPR); + switch (S.token()) { + 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) { + Token token = S.token(); + S.nextToken(); + mode = EXPR; + if (token == SUB && + (S.token() == INTLITERAL || S.token() == LONGLITERAL) && + S.radix() == 10) { + mode = EXPR; + t = literal(names.hyphen); + } else { + t = term3(); + return F.at(pos).Unary(unoptag(token), t); + } + } else return illegal(); + break; + case LPAREN: + if (typeArgs == null && (mode & EXPR) != 0) { + S.nextToken(); + mode = EXPR | TYPE | NOPARAMS; + t = term3(); + if ((mode & TYPE) != 0 && S.token() == LT) { + // Could be a cast to a parameterized type + int op = JCTree.LT; + int pos1 = S.pos(); + S.nextToken(); + mode &= (EXPR | TYPE); + mode |= TYPEARG; + JCExpression t1 = term3(); + if ((mode & TYPE) != 0 && + (S.token() == COMMA || S.token() == GT)) { + mode = TYPE; + ListBuffer args = new ListBuffer(); + args.append(t1); + while (S.token() == COMMA) { + S.nextToken(); + args.append(typeArgument()); + } + accept(GT); + t = F.at(pos1).TypeApply(t, args.toList()); + checkGenerics(); + t = bracketsOpt(toP(t)); + } else if ((mode & EXPR) != 0) { + mode = EXPR; + t = F.at(pos1).Binary(op, t, term2Rest(t1, TreeInfo.shiftPrec)); + t = termRest(term1Rest(term2Rest(t, TreeInfo.orPrec))); + } else { + accept(GT); + } + } else { + t = termRest(term1Rest(term2Rest(t, TreeInfo.orPrec))); + } + accept(RPAREN); + lastmode = mode; + mode = EXPR; + if ((lastmode & EXPR) == 0) { + JCExpression t1 = term3(); + return F.at(pos).TypeCast(t, t1); + } else if ((lastmode & TYPE) != 0) { + switch (S.token()) { + /*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 BYTE: case SHORT: case CHAR: case INT: + case LONG: case FLOAT: case DOUBLE: case BOOLEAN: case VOID: + JCExpression t1 = term3(); + return F.at(pos).TypeCast(t, t1); + } + } + } else return illegal(); + t = toP(F.at(pos).Parens(t)); + break; + case THIS: + if ((mode & EXPR) != 0) { + mode = EXPR; + t = to(F.at(pos).Ident(names._this)); + S.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(superSuffix(typeArgs, F.at(pos).Ident(names._super))); + 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; + S.nextToken(); + if (S.token() == LT) typeArgs = typeArguments(); + t = creator(pos, typeArgs); + typeArgs = null; + } else return illegal(); + break; + case IDENTIFIER: case ASSERT: case ENUM: + if (typeArgs != null) return illegal(); + t = toP(F.at(S.pos()).Ident(ident())); + loop: while (true) { + pos = S.pos(); + switch (S.token()) { + case LBRACKET: + S.nextToken(); + if (S.token() == RBRACKET) { + S.nextToken(); + t = bracketsOpt(t); + t = toP(F.at(pos).TypeArray(t)); + t = bracketsSuffix(t); + } else { + if ((mode & EXPR) != 0) { + mode = EXPR; + JCExpression t1 = term(); + t = to(F.at(pos).Indexed(t, t1)); + } + accept(RBRACKET); + } + break loop; + case LPAREN: + if ((mode & EXPR) != 0) { + mode = EXPR; + t = arguments(typeArgs, t); + typeArgs = null; + } + break loop; + case DOT: + S.nextToken(); + typeArgs = typeArgumentsOpt(EXPR); + if ((mode & EXPR) != 0) { + switch (S.token()) { + case CLASS: + if (typeArgs != null) return illegal(); + mode = EXPR; + t = to(F.at(pos).Select(t, names._class)); + S.nextToken(); + break loop; + case THIS: + if (typeArgs != null) return illegal(); + mode = EXPR; + t = to(F.at(pos).Select(t, names._this)); + S.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 = S.pos(); + S.nextToken(); + if (S.token() == LT) typeArgs = typeArguments(); + t = innerCreator(pos1, typeArgs, t); + typeArgs = null; + break loop; + } + } + // typeArgs saved for next loop iteration. + t = toP(F.at(pos).Select(t, ident())); + break; + 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) { + S.nextToken(); + if (S.token() == DOT) { + JCPrimitiveTypeTree ti = toP(F.at(pos).TypeIdent(TypeTags.VOID)); + t = bracketsSuffix(ti); + } else { + return illegal(pos); + } + } else { + return illegal(); + } + break; + default: + return illegal(); + } + if (typeArgs != null) illegal(); + while (true) { + int pos1 = S.pos(); + if (S.token() == LBRACKET) { + S.nextToken(); + if ((mode & TYPE) != 0) { + int oldmode = mode; + mode = TYPE; + if (S.token() == RBRACKET) { + S.nextToken(); + t = bracketsOpt(t); + t = toP(F.at(pos1).TypeArray(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 (S.token() == DOT) { + S.nextToken(); + typeArgs = typeArgumentsOpt(EXPR); + if (S.token() == SUPER && (mode & EXPR) != 0) { + mode = EXPR; + t = to(F.at(pos1).Select(t, names._super)); + S.nextToken(); + t = arguments(typeArgs, t); + typeArgs = null; + } else if (S.token() == NEW && (mode & EXPR) != 0) { + if (typeArgs != null) return illegal(); + mode = EXPR; + int pos2 = S.pos(); + S.nextToken(); + if (S.token() == LT) typeArgs = typeArguments(); + t = innerCreator(pos2, typeArgs, t); + typeArgs = null; + } else { + t = toP(F.at(pos1).Select(t, ident())); + t = argumentsOpt(typeArgs, typeArgumentsOpt(t)); + typeArgs = null; + } + } else { + break; + } + } + while ((S.token() == PLUSPLUS || S.token() == SUBSUB) && (mode & EXPR) != 0) { + mode = EXPR; + t = to(F.at(S.pos()).Unary( + S.token() == PLUSPLUS ? JCTree.POSTINC : JCTree.POSTDEC, t)); + S.nextToken(); + } + return toP(t); + } + + /** SuperSuffix = Arguments | "." [TypeArguments] Ident [Arguments] + */ + JCExpression superSuffix(List typeArgs, JCExpression t) { + S.nextToken(); + if (S.token() == LPAREN || typeArgs != null) { + t = arguments(typeArgs, t); + } else { + int pos = S.pos(); + accept(DOT); + typeArgs = (S.token() == LT) ? typeArguments() : 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(S.pos()).TypeIdent(typetag(S.token()))); + S.nextToken(); + return t; + } + + /** ArgumentsOpt = [ Arguments ] + */ + JCExpression argumentsOpt(List typeArgs, JCExpression t) { + if ((mode & EXPR) != 0 && S.token() == LPAREN || typeArgs != null) { + mode = EXPR; + return arguments(typeArgs, t); + } else { + return t; + } + } + + /** Arguments = "(" [Expression { COMMA Expression }] ")" + */ + List arguments() { + ListBuffer args = lb(); + if (S.token() == LPAREN) { + S.nextToken(); + if (S.token() != RPAREN) { + args.append(expression()); + while (S.token() == COMMA) { + S.nextToken(); + args.append(expression()); + } + } + accept(RPAREN); + } else { + syntaxError(S.pos(), "expected", keywords.token2string(LPAREN)); + } + return args.toList(); + } + + JCMethodInvocation arguments(List typeArgs, JCExpression t) { + int pos = S.pos(); + List args = arguments(); + return toP(F.at(pos).Apply(typeArgs, t, args)); + } + + /** TypeArgumentsOpt = [ TypeArguments ] + */ + JCExpression typeArgumentsOpt(JCExpression t) { + if (S.token() == LT && + (mode & TYPE) != 0 && + (mode & NOPARAMS) == 0) { + mode = TYPE; + checkGenerics(); + return typeArguments(t); + } else { + return t; + } + } + List typeArgumentsOpt() { + return typeArgumentsOpt(TYPE); + } + + List typeArgumentsOpt(int useMode) { + if (S.token() == LT) { + checkGenerics(); + if ((mode & useMode) == 0 || + (mode & NOPARAMS) != 0) { + illegal(); + } + mode = useMode; + return typeArguments(); + } + return null; + } + + /** TypeArguments = "<" TypeArgument {"," TypeArgument} ">" + */ + List typeArguments() { + ListBuffer args = lb(); + if (S.token() == LT) { + S.nextToken(); + args.append(((mode & EXPR) == 0) ? typeArgument() : type()); + while (S.token() == COMMA) { + S.nextToken(); + args.append(((mode & EXPR) == 0) ? typeArgument() : type()); + } + switch (S.token()) { + case GTGTGTEQ: + S.token(GTGTEQ); + break; + case GTGTEQ: + S.token(GTEQ); + break; + case GTEQ: + S.token(EQ); + break; + case GTGTGT: + S.token(GTGT); + break; + case GTGT: + S.token(GT); + break; + default: + accept(GT); + break; + } + } else { + syntaxError(S.pos(), "expected", keywords.token2string(LT)); + } + return args.toList(); + } + + /** TypeArgument = Type + * | "?" + * | "?" EXTENDS Type {"&" Type} + * | "?" SUPER Type + */ + JCExpression typeArgument() { + if (S.token() != QUES) return type(); + int pos = S.pos(); + S.nextToken(); + if (S.token() == EXTENDS) { + TypeBoundKind t = to(F.at(S.pos()).TypeBoundKind(BoundKind.EXTENDS)); + S.nextToken(); + return F.at(pos).Wildcard(t, type()); + } else if (S.token() == SUPER) { + TypeBoundKind t = to(F.at(S.pos()).TypeBoundKind(BoundKind.SUPER)); + S.nextToken(); + return F.at(pos).Wildcard(t, type()); + } else if (S.token() == IDENTIFIER) { + //error recovery + reportSyntaxError(S.prevEndPos(), "expected3", + keywords.token2string(GT), + keywords.token2string(EXTENDS), + keywords.token2string(SUPER)); + TypeBoundKind t = F.at(Position.NOPOS).TypeBoundKind(BoundKind.UNBOUND); + JCExpression wc = toP(F.at(pos).Wildcard(t, null)); + JCIdent id = toP(F.at(S.pos()).Ident(ident())); + return F.at(pos).Erroneous(List.of(wc, id)); + } else { + TypeBoundKind t = F.at(Position.NOPOS).TypeBoundKind(BoundKind.UNBOUND); + return toP(F.at(pos).Wildcard(t, null)); + } + } + + JCTypeApply typeArguments(JCExpression t) { + int pos = S.pos(); + List args = typeArguments(); + return toP(F.at(pos).TypeApply(t, args)); + } + + /** BracketsOpt = {"[" "]"} + */ + private JCExpression bracketsOpt(JCExpression t) { + if (S.token() == LBRACKET) { + int pos = S.pos(); + S.nextToken(); + t = bracketsOptCont(t, pos); + F.at(pos); + } + return t; + } + + private JCArrayTypeTree bracketsOptCont(JCExpression t, int pos) { + accept(RBRACKET); + t = bracketsOpt(t); + return toP(F.at(pos).TypeArray(t)); + } + + /** BracketsSuffixExpr = "." CLASS + * BracketsSuffixType = + */ + JCExpression bracketsSuffix(JCExpression t) { + if ((mode & EXPR) != 0 && S.token() == DOT) { + mode = EXPR; + int pos = S.pos(); + S.nextToken(); + accept(CLASS); + if (S.pos() == errorEndPos) { + // error recovery + Name name = null; + if (S.token() == IDENTIFIER) { + name = S.name(); + S.nextToken(); + } else { + name = names.error; + } + t = F.at(pos).Erroneous(List.of(toP(F.at(pos).Select(t, name)))); + } else { + t = toP(F.at(pos).Select(t, names._class)); + } + } else if ((mode & TYPE) != 0) { + mode = TYPE; + } else { + syntaxError(S.pos(), "dot.class.expected"); + } + return t; + } + + /** Creator = Qualident [TypeArguments] ( ArrayCreatorRest | ClassCreatorRest ) + */ + JCExpression creator(int newpos, List typeArgs) { + switch (S.token()) { + case BYTE: case SHORT: case CHAR: case INT: case LONG: case FLOAT: + case DOUBLE: case BOOLEAN: + if (typeArgs == null) + return arrayCreatorRest(newpos, basicType()); + break; + default: + } + JCExpression t = qualident(); + int oldmode = mode; + mode = TYPE; + if (S.token() == LT) { + checkGenerics(); + t = typeArguments(t); + } + while (S.token() == DOT) { + int pos = S.pos(); + S.nextToken(); + t = toP(F.at(pos).Select(t, ident())); + if (S.token() == LT) { + checkGenerics(); + t = typeArguments(t); + } + } + mode = oldmode; + if (S.token() == LBRACKET) { + JCExpression e = arrayCreatorRest(newpos, t); + 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.prevEndPos()); + reportSyntaxError(pos, "cannot.create.array.with.type.arguments"); + return toP(F.at(newpos).Erroneous(typeArgs.prepend(e))); + } + return e; + } else if (S.token() == LPAREN) { + return classCreatorRest(newpos, null, typeArgs, t); + } else { + reportSyntaxError(S.pos(), "expected2", + keywords.token2string(LPAREN), + keywords.token2string(LBRACKET)); + t = toP(F.at(newpos).NewClass(null, typeArgs, t, List.nil(), null)); + return toP(F.at(newpos).Erroneous(List.of(t))); + } + } + + /** InnerCreator = Ident [TypeArguments] ClassCreatorRest + */ + JCExpression innerCreator(int newpos, List typeArgs, JCExpression encl) { + JCExpression t = toP(F.at(S.pos()).Ident(ident())); + if (S.token() == LT) { + checkGenerics(); + t = typeArguments(t); + } + return classCreatorRest(newpos, encl, typeArgs, t); + } + + /** ArrayCreatorRest = "[" ( "]" BracketsOpt ArrayInitializer + * | Expression "]" {"[" Expression "]"} BracketsOpt ) + */ + JCExpression arrayCreatorRest(int newpos, JCExpression elemtype) { + accept(LBRACKET); + if (S.token() == RBRACKET) { + accept(RBRACKET); + elemtype = bracketsOpt(elemtype); + if (S.token() == LBRACE) { + return arrayInitializer(newpos, elemtype); + } else { + return syntaxError(S.pos(), "array.dimension.missing"); + } + } else { + ListBuffer dims = new ListBuffer(); + dims.append(expression()); + accept(RBRACKET); + while (S.token() == LBRACKET) { + int pos = S.pos(); + S.nextToken(); + if (S.token() == RBRACKET) { + elemtype = bracketsOptCont(elemtype, pos); + } else { + dims.append(expression()); + accept(RBRACKET); + } + } + return toP(F.at(newpos).NewArray(elemtype, dims.toList(), null)); + } + } + + /** ClassCreatorRest = Arguments [ClassBody] + */ + JCExpression classCreatorRest(int newpos, + JCExpression encl, + List typeArgs, + JCExpression t) + { + List args = arguments(); + JCClassDecl body = null; + if (S.token() == LBRACE) { + int pos = S.pos(); + List 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) { + accept(LBRACE); + ListBuffer elems = new ListBuffer(); + if (S.token() == COMMA) { + S.nextToken(); + } else if (S.token() != RBRACE) { + elems.append(variableInitializer()); + while (S.token() == COMMA) { + S.nextToken(); + if (S.token() == RBRACE) break; + elems.append(variableInitializer()); + } + } + accept(RBRACE); + return toP(F.at(newpos).NewArray(t, List.nil(), elems.toList())); + } + + /** VariableInitializer = ArrayInitializer | Expression + */ + public JCExpression variableInitializer() { + return S.token() == LBRACE ? arrayInitializer(S.pos(), null) : expression(); + } + + /** ParExpression = "(" Expression ")" + */ + JCExpression parExpression() { + accept(LPAREN); + JCExpression t = expression(); + accept(RPAREN); + return t; + } + + /** Block = "{" BlockStatements "}" + */ + JCBlock block(int pos, long flags) { + accept(LBRACE); + List stats = blockStatements(); + JCBlock t = F.at(pos).Block(flags, stats); + while (S.token() == CASE || S.token() == DEFAULT) { + syntaxError("orphaned", keywords.token2string(S.token())); + 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 = S.pos(); + accept(RBRACE); + return toP(t); + } + + public JCBlock block() { + return block(S.pos(), 0); + } + + /** BlockStatements = { BlockStatement } + * BlockStatement = LocalVariableDeclarationStatement + * | ClassOrInterfaceOrEnumDeclaration + * | [Ident ":"] Statement + * LocalVariableDeclarationStatement + * = { FINAL | '@' Annotation } Type VariableDeclarators ";" + */ + @SuppressWarnings("fallthrough") + List blockStatements() { +//todo: skip to anchor on error(?) + int lastErrPos = -1; + ListBuffer stats = new ListBuffer(); + while (true) { + int pos = S.pos(); + switch (S.token()) { + case RBRACE: case CASE: case DEFAULT: case EOF: + return stats.toList(); + 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: + stats.append(statement()); + break; + case MONKEYS_AT: + case FINAL: { + String dc = S.docComment(); + JCModifiers mods = modifiersOpt(); + if (S.token() == INTERFACE || + S.token() == CLASS || + allowEnums && S.token() == ENUM) { + stats.append(classOrInterfaceOrEnumDeclaration(mods, dc)); + } else { + JCExpression t = type(); + stats.appendList(variableDeclarators(mods, t, + new ListBuffer())); + // A "LocalVariableDeclarationStatement" subsumes the terminating semicolon + storeEnd(stats.elems.last(), S.endPos()); + accept(SEMI); + } + break; + } + case ABSTRACT: case STRICTFP: { + String dc = S.docComment(); + JCModifiers mods = modifiersOpt(); + stats.append(classOrInterfaceOrEnumDeclaration(mods, dc)); + break; + } + case INTERFACE: + case CLASS: + stats.append(classOrInterfaceOrEnumDeclaration(modifiersOpt(), + S.docComment())); + break; + case ENUM: + case ASSERT: + if (allowEnums && S.token() == ENUM) { + log.error(S.pos(), "local.enum"); + stats. + append(classOrInterfaceOrEnumDeclaration(modifiersOpt(), + S.docComment())); + break; + } else if (allowAsserts && S.token() == ASSERT) { + stats.append(statement()); + break; + } + /* fall through to default */ + default: + Name name = S.name(); + JCExpression t = term(EXPR | TYPE); + if (S.token() == COLON && t.getTag() == JCTree.IDENT) { + S.nextToken(); + JCStatement stat = statement(); + stats.append(F.at(pos).Labelled(name, stat)); + } else if ((lastmode & TYPE) != 0 && + (S.token() == IDENTIFIER || + S.token() == ASSERT || + S.token() == ENUM)) { + pos = S.pos(); + JCModifiers mods = F.at(Position.NOPOS).Modifiers(0); + F.at(pos); + stats.appendList(variableDeclarators(mods, t, + new ListBuffer())); + // A "LocalVariableDeclarationStatement" subsumes the terminating semicolon + storeEnd(stats.elems.last(), S.endPos()); + accept(SEMI); + } else { + // This Exec is an "ExpressionStatement"; it subsumes the terminating semicolon + stats.append(to(F.at(pos).Exec(checkExprStat(t)))); + accept(SEMI); + } + } + + // error recovery + if (S.pos() == lastErrPos) + return stats.toList(); + if (S.pos() <= errorEndPos) { + skip(false, true, true, true); + lastErrPos = S.pos(); + } + + // ensure no dangling /** @deprecated */ active + S.resetDeprecatedFlag(); + } + } + + /** 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 ) + * | SWITCH ParExpression "{" SwitchBlockStatementGroups "}" + * | SYNCHRONIZED ParExpression Block + * | RETURN [Expression] ";" + * | THROW Expression ";" + * | BREAK [Ident] ";" + * | CONTINUE [Ident] ";" + * | ASSERT Expression [ ":" Expression ] ";" + * | ";" + * | ExpressionStatement + * | Ident ":" Statement + */ + @SuppressWarnings("fallthrough") + public JCStatement statement() { + int pos = S.pos(); + switch (S.token()) { + case LBRACE: + return block(); + case IF: { + S.nextToken(); + JCExpression cond = parExpression(); + JCStatement thenpart = statement(); + JCStatement elsepart = null; + if (S.token() == ELSE) { + S.nextToken(); + elsepart = statement(); + } + return F.at(pos).If(cond, thenpart, elsepart); + } + case FOR: { + S.nextToken(); + accept(LPAREN); + List inits = S.token() == SEMI ? List.nil() : forInit(); + if (inits.length() == 1 && + inits.head.getTag() == JCTree.VARDEF && + ((JCVariableDecl) inits.head).init == null && + S.token() == COLON) { + checkForeach(); + JCVariableDecl var = (JCVariableDecl)inits.head; + accept(COLON); + JCExpression expr = expression(); + accept(RPAREN); + JCStatement body = statement(); + return F.at(pos).ForeachLoop(var, expr, body); + } else { + accept(SEMI); + JCExpression cond = S.token() == SEMI ? null : expression(); + accept(SEMI); + List steps = S.token() == RPAREN ? List.nil() : forUpdate(); + accept(RPAREN); + JCStatement body = statement(); + return F.at(pos).ForLoop(inits, cond, steps, body); + } + } + case WHILE: { + S.nextToken(); + JCExpression cond = parExpression(); + JCStatement body = statement(); + return F.at(pos).WhileLoop(cond, body); + } + case DO: { + S.nextToken(); + JCStatement body = statement(); + accept(WHILE); + JCExpression cond = parExpression(); + JCDoWhileLoop t = to(F.at(pos).DoLoop(body, cond)); + accept(SEMI); + return t; + } + case TRY: { + S.nextToken(); + JCBlock body = block(); + ListBuffer catchers = new ListBuffer(); + JCBlock finalizer = null; + if (S.token() == CATCH || S.token() == FINALLY) { + while (S.token() == CATCH) catchers.append(catchClause()); + if (S.token() == FINALLY) { + S.nextToken(); + finalizer = block(); + } + } else { + log.error(pos, "try.without.catch.or.finally"); + } + return F.at(pos).Try(body, catchers.toList(), finalizer); + } + case SWITCH: { + S.nextToken(); + JCExpression selector = parExpression(); + accept(LBRACE); + List cases = switchBlockStatementGroups(); + JCSwitch t = to(F.at(pos).Switch(selector, cases)); + accept(RBRACE); + return t; + } + case SYNCHRONIZED: { + S.nextToken(); + JCExpression lock = parExpression(); + JCBlock body = block(); + return F.at(pos).Synchronized(lock, body); + } + case RETURN: { + S.nextToken(); + JCExpression result = S.token() == SEMI ? null : expression(); + JCReturn t = to(F.at(pos).Return(result)); + accept(SEMI); + return t; + } + case THROW: { + S.nextToken(); + JCExpression exc = expression(); + JCThrow t = to(F.at(pos).Throw(exc)); + accept(SEMI); + return t; + } + case BREAK: { + S.nextToken(); + Name label = (S.token() == IDENTIFIER || S.token() == ASSERT || S.token() == ENUM) ? ident() : null; + JCBreak t = to(F.at(pos).Break(label)); + accept(SEMI); + return t; + } + case CONTINUE: { + S.nextToken(); + Name label = (S.token() == IDENTIFIER || S.token() == ASSERT || S.token() == ENUM) ? ident() : null; + JCContinue t = to(F.at(pos).Continue(label)); + accept(SEMI); + return t; + } + case SEMI: + S.nextToken(); + return toP(F.at(pos).Skip()); + case ELSE: + return toP(F.Exec(syntaxError("else.without.if"))); + case FINALLY: + return toP(F.Exec(syntaxError("finally.without.try"))); + case CATCH: + return toP(F.Exec(syntaxError("catch.without.try"))); + case ASSERT: { + if (allowAsserts && S.token() == ASSERT) { + S.nextToken(); + JCExpression assertion = expression(); + JCExpression message = null; + if (S.token() == COLON) { + S.nextToken(); + message = expression(); + } + JCAssert t = to(F.at(pos).Assert(assertion, message)); + accept(SEMI); + return t; + } + /* else fall through to default case */ + } + case ENUM: + default: + Name name = S.name(); + JCExpression expr = expression(); + if (S.token() == COLON && expr.getTag() == JCTree.IDENT) { + S.nextToken(); + JCStatement stat = statement(); + return F.at(pos).Labelled(name, stat); + } else { + // This Exec is an "ExpressionStatement"; it subsumes the terminating semicolon + JCExpressionStatement stat = to(F.at(pos).Exec(checkExprStat(expr))); + accept(SEMI); + return stat; + } + } + } + + /** CatchClause = CATCH "(" FormalParameter ")" Block + */ + JCCatch catchClause() { + int pos = S.pos(); + accept(CATCH); + accept(LPAREN); + JCVariableDecl formal = + variableDeclaratorId(optFinal(Flags.PARAMETER), + qualident()); + accept(RPAREN); + JCBlock body = block(); + return F.at(pos).Catch(formal, body); + } + + /** SwitchBlockStatementGroups = { SwitchBlockStatementGroup } + * SwitchBlockStatementGroup = SwitchLabel BlockStatements + * SwitchLabel = CASE ConstantExpression ":" | DEFAULT ":" + */ + List switchBlockStatementGroups() { + ListBuffer cases = new ListBuffer(); + while (true) { + int pos = S.pos(); + switch (S.token()) { + case CASE: { + S.nextToken(); + JCExpression pat = expression(); + accept(COLON); + List stats = blockStatements(); + JCCase c = F.at(pos).Case(pat, stats); + if (stats.isEmpty()) + storeEnd(c, S.prevEndPos()); + cases.append(c); + break; + } + case DEFAULT: { + S.nextToken(); + accept(COLON); + List stats = blockStatements(); + JCCase c = F.at(pos).Case(null, stats); + if (stats.isEmpty()) + storeEnd(c, S.prevEndPos()); + cases.append(c); + break; + } + case RBRACE: case EOF: + return cases.toList(); + default: + S.nextToken(); // to ensure progress + syntaxError(pos, "expected3", + keywords.token2string(CASE), + keywords.token2string(DEFAULT), + keywords.token2string(RBRACE)); + } + } + } + + /** MoreStatementExpressions = { COMMA StatementExpression } + */ + > 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 (S.token() == COMMA) { + S.nextToken(); + pos = S.pos(); + JCExpression t = expression(); + // 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 forInit() { + ListBuffer stats = lb(); + int pos = S.pos(); + if (S.token() == FINAL || S.token() == MONKEYS_AT) { + return variableDeclarators(optFinal(0), type(), stats).toList(); + } else { + JCExpression t = term(EXPR | TYPE); + if ((lastmode & TYPE) != 0 && + (S.token() == IDENTIFIER || S.token() == ASSERT || S.token() == ENUM)) + return variableDeclarators(modifiersOpt(), t, stats).toList(); + else + return moreStatementExpressions(pos, t, stats).toList(); + } + } + + /** ForUpdate = StatementExpression MoreStatementExpressions + */ + List forUpdate() { + return moreStatementExpressions(S.pos(), + expression(), + new ListBuffer()).toList(); + } + + /** AnnotationsOpt = { '@' Annotation } + */ + List annotationsOpt() { + if (S.token() != MONKEYS_AT) return List.nil(); // optimization + ListBuffer buf = new ListBuffer(); + while (S.token() == MONKEYS_AT) { + int pos = S.pos(); + S.nextToken(); + buf.append(annotation(pos)); + } + return buf.toList(); + } + + /** ModifiersOpt = { Modifier } + * Modifier = PUBLIC | PROTECTED | PRIVATE | STATIC | ABSTRACT | FINAL + * | NATIVE | SYNCHRONIZED | TRANSIENT | VOLATILE | "@" + * | "@" Annotation + */ + JCModifiers modifiersOpt() { + return modifiersOpt(null); + } + JCModifiers modifiersOpt(JCModifiers partial) { + long flags = (partial == null) ? 0 : partial.flags; + if (S.deprecatedFlag()) { + flags |= Flags.DEPRECATED; + S.resetDeprecatedFlag(); + } + ListBuffer annotations = new ListBuffer(); + if (partial != null) annotations.appendList(partial.annotations); + int pos = S.pos(); + int lastPos = Position.NOPOS; + loop: + while (true) { + long flag; + switch (S.token()) { + 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; + default: break loop; + } + if ((flags & flag) != 0) log.error(S.pos(), "repeated.modifier"); + lastPos = S.pos(); + S.nextToken(); + if (flag == Flags.ANNOTATION) { + checkAnnotations(); + if (S.token() != INTERFACE) { + JCAnnotation ann = annotation(lastPos); + // if first modifier is an annotation, set pos to annotation's. + if (flags == 0 && annotations.isEmpty()) + pos = ann.pos; + annotations.append(ann); + lastPos = ann.pos; + flag = 0; + } + } + flags |= flag; + } + switch (S.token()) { + 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 == 0 && annotations.isEmpty()) + pos = Position.NOPOS; + + JCModifiers mods = F.at(pos).Modifiers(flags, annotations.toList()); + if (pos != Position.NOPOS) + storeEnd(mods, S.prevEndPos()); + return mods; + } + + /** Annotation = "@" Qualident [ "(" AnnotationFieldValues ")" ] + * @param pos position of "@" token + */ + JCAnnotation annotation(int pos) { + // accept(AT); // AT consumed by caller + checkAnnotations(); + JCTree ident = qualident(); + List fieldValues = annotationFieldValuesOpt(); + JCAnnotation ann = F.at(pos).Annotation(ident, fieldValues); + storeEnd(ann, S.prevEndPos()); + return ann; + } + + List annotationFieldValuesOpt() { + return (S.token() == LPAREN) ? annotationFieldValues() : List.nil(); + } + + /** AnnotationFieldValues = "(" [ AnnotationFieldValue { "," AnnotationFieldValue } ] ")" */ + List annotationFieldValues() { + accept(LPAREN); + ListBuffer buf = new ListBuffer(); + if (S.token() != RPAREN) { + buf.append(annotationFieldValue()); + while (S.token() == COMMA) { + S.nextToken(); + buf.append(annotationFieldValue()); + } + } + accept(RPAREN); + return buf.toList(); + } + + /** AnnotationFieldValue = AnnotationValue + * | Identifier "=" AnnotationValue + */ + JCExpression annotationFieldValue() { + if (S.token() == IDENTIFIER) { + mode = EXPR; + JCExpression t1 = term1(); + if (t1.getTag() == JCTree.IDENT && S.token() == EQ) { + int pos = S.pos(); + accept(EQ); + return toP(F.at(pos).Assign(t1, annotationValue())); + } else { + return t1; + } + } + return annotationValue(); + } + + /* AnnotationValue = ConditionalExpression + * | Annotation + * | "{" [ AnnotationValue { "," AnnotationValue } ] "}" + */ + JCExpression annotationValue() { + int pos; + switch (S.token()) { + case MONKEYS_AT: + pos = S.pos(); + S.nextToken(); + return annotation(pos); + case LBRACE: + pos = S.pos(); + accept(LBRACE); + ListBuffer buf = new ListBuffer(); + if (S.token() != RBRACE) { + buf.append(annotationValue()); + while (S.token() == COMMA) { + S.nextToken(); + if (S.token() == RPAREN) 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 variableDeclarators(JCModifiers mods, + JCExpression type, + T vdefs) + { + return variableDeclaratorsRest(S.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. + */ + > T variableDeclaratorsRest(int pos, + JCModifiers mods, + JCExpression type, + Name name, + boolean reqInit, + String dc, + T vdefs) + { + vdefs.append(variableDeclaratorRest(pos, mods, type, name, reqInit, dc)); + while (S.token() == COMMA) { + // All but last of multiple declarators subsume a comma + storeEnd((JCTree)vdefs.elems.last(), S.endPos()); + S.nextToken(); + vdefs.append(variableDeclarator(mods, type, reqInit, dc)); + } + return vdefs; + } + + /** VariableDeclarator = Ident VariableDeclaratorRest + * ConstantDeclarator = Ident ConstantDeclaratorRest + */ + JCVariableDecl variableDeclarator(JCModifiers mods, JCExpression type, boolean reqInit, String dc) { + return variableDeclaratorRest(S.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, String dc) { + type = bracketsOpt(type); + JCExpression init = null; + if (S.token() == EQ) { + S.nextToken(); + init = variableInitializer(); + } + else if (reqInit) syntaxError(S.pos(), "expected", keywords.token2string(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) { + int pos = S.pos(); + Name name = ident(); + if ((mods.flags & Flags.VARARGS) == 0) + type = bracketsOpt(type); + return toP(F.at(pos).VarDef(mods, name, type, null)); + } + + /** CompilationUnit = [ { "@" Annotation } PACKAGE Qualident ";"] {ImportDeclaration} {TypeDeclaration} + */ + public JCTree.JCCompilationUnit compilationUnit() { + int pos = S.pos(); + JCExpression pid = null; + String dc = S.docComment(); + JCModifiers mods = null; + List packageAnnotations = List.nil(); + if (S.token() == MONKEYS_AT) + mods = modifiersOpt(); + + if (S.token() == PACKAGE) { + if (mods != null) { + checkNoMods(mods.flags); + packageAnnotations = mods.annotations; + mods = null; + } + S.nextToken(); + pid = qualident(); + accept(SEMI); + } + ListBuffer defs = new ListBuffer(); + boolean checkForImports = true; + while (S.token() != EOF) { + if (S.pos() <= errorEndPos) { + // error recovery + skip(checkForImports, false, false, false); + if (S.token() == EOF) + break; + } + if (checkForImports && mods == null && S.token() == IMPORT) { + defs.append(importDeclaration()); + } else { + JCTree def = typeDeclaration(mods); + if (def instanceof JCExpressionStatement) + def = ((JCExpressionStatement)def).expr; + defs.append(def); + if (def instanceof JCClassDecl) + checkForImports = false; + mods = null; + } + } + JCTree.JCCompilationUnit toplevel = F.at(pos).TopLevel(packageAnnotations, pid, defs.toList()); + attach(toplevel, dc); + if (defs.elems.isEmpty()) + storeEnd(toplevel, S.prevEndPos()); + if (keepDocComments) toplevel.docComments = docComments; + return toplevel; + } + + /** ImportDeclaration = IMPORT [ STATIC ] Ident { "." Ident } [ "." "*" ] ";" + */ + JCTree importDeclaration() { + int pos = S.pos(); + S.nextToken(); + boolean importStatic = false; + if (S.token() == STATIC) { + checkStaticImports(); + importStatic = true; + S.nextToken(); + } + JCExpression pid = toP(F.at(S.pos()).Ident(ident())); + do { + int pos1 = S.pos(); + accept(DOT); + if (S.token() == STAR) { + pid = to(F.at(pos1).Select(pid, names.asterisk)); + S.nextToken(); + break; + } else { + pid = toP(F.at(pos1).Select(pid, ident())); + } + } while (S.token() == DOT); + accept(SEMI); + return toP(F.at(pos).Import(pid, importStatic)); + } + + /** TypeDeclaration = ClassOrInterfaceOrEnumDeclaration + * | ";" + */ + JCTree typeDeclaration(JCModifiers mods) { + int pos = S.pos(); + if (mods == null && S.token() == SEMI) { + S.nextToken(); + return toP(F.at(pos).Skip()); + } else { + String dc = S.docComment(); + return classOrInterfaceOrEnumDeclaration(modifiersOpt(mods), dc); + } + } + + /** 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. + */ + JCStatement classOrInterfaceOrEnumDeclaration(JCModifiers mods, String dc) { + if (S.token() == CLASS) { + return classDeclaration(mods, dc); + } else if (S.token() == INTERFACE) { + return interfaceDeclaration(mods, dc); + } else if (allowEnums) { + if (S.token() == ENUM) { + return enumDeclaration(mods, dc); + } else { + int pos = S.pos(); + List errs; + if (S.token() == IDENTIFIER) { + errs = List.of(mods, toP(F.at(pos).Ident(ident()))); + setErrorEndPos(S.pos()); + } else { + errs = List.of(mods); + } + return toP(F.Exec(syntaxError(pos, errs, "expected3", + keywords.token2string(CLASS), + keywords.token2string(INTERFACE), + keywords.token2string(ENUM)))); + } + } else { + if (S.token() == ENUM) { + log.error(S.pos(), "enums.not.supported.in.source", source.name); + allowEnums = true; + return enumDeclaration(mods, dc); + } + int pos = S.pos(); + List errs; + if (S.token() == IDENTIFIER) { + errs = List.of(mods, toP(F.at(pos).Ident(ident()))); + setErrorEndPos(S.pos()); + } else { + errs = List.of(mods); + } + return toP(F.Exec(syntaxError(pos, errs, "expected2", + keywords.token2string(CLASS), + keywords.token2string(INTERFACE)))); + } + } + + /** 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. + */ + JCClassDecl classDeclaration(JCModifiers mods, String dc) { + int pos = S.pos(); + accept(CLASS); + Name name = ident(); + + List typarams = typeParametersOpt(); + + JCTree extending = null; + if (S.token() == EXTENDS) { + S.nextToken(); + extending = type(); + } + List implementing = List.nil(); + if (S.token() == IMPLEMENTS) { + S.nextToken(); + implementing = typeList(); + } + List 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. + */ + JCClassDecl interfaceDeclaration(JCModifiers mods, String dc) { + int pos = S.pos(); + accept(INTERFACE); + Name name = ident(); + + List typarams = typeParametersOpt(); + + List extending = List.nil(); + if (S.token() == EXTENDS) { + S.nextToken(); + extending = typeList(); + } + List 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. + */ + JCClassDecl enumDeclaration(JCModifiers mods, String dc) { + int pos = S.pos(); + accept(ENUM); + Name name = ident(); + + List implementing = List.nil(); + if (S.token() == IMPLEMENTS) { + S.nextToken(); + implementing = typeList(); + } + + List defs = enumBody(name); + JCModifiers newMods = + F.at(mods.pos).Modifiers(mods.flags|Flags.ENUM, mods.annotations); + JCClassDecl result = toP(F.at(pos). + ClassDef(newMods, name, List.nil(), + null, implementing, defs)); + attach(result, dc); + return result; + } + + /** EnumBody = "{" { EnumeratorDeclarationList } [","] + * [ ";" {ClassBodyDeclaration} ] "}" + */ + List enumBody(Name enumName) { + accept(LBRACE); + ListBuffer defs = new ListBuffer(); + if (S.token() == COMMA) { + S.nextToken(); + } else if (S.token() != RBRACE && S.token() != SEMI) { + defs.append(enumeratorDeclaration(enumName)); + while (S.token() == COMMA) { + S.nextToken(); + if (S.token() == RBRACE || S.token() == SEMI) break; + defs.append(enumeratorDeclaration(enumName)); + } + if (S.token() != SEMI && S.token() != RBRACE) { + defs.append(syntaxError(S.pos(), "expected3", + keywords.token2string(COMMA), + keywords.token2string(RBRACE), + keywords.token2string(SEMI))); + S.nextToken(); + } + } + if (S.token() == SEMI) { + S.nextToken(); + while (S.token() != RBRACE && S.token() != EOF) { + defs.appendList(classOrInterfaceBodyDeclaration(enumName, + false)); + if (S.pos() <= errorEndPos) { + // error recovery + skip(false, true, true, false); + } + } + } + accept(RBRACE); + return defs.toList(); + } + + /** EnumeratorDeclaration = AnnotationsOpt [TypeArguments] IDENTIFIER [ Arguments ] [ "{" ClassBody "}" ] + */ + JCTree enumeratorDeclaration(Name enumName) { + String dc = S.docComment(); + int flags = Flags.PUBLIC|Flags.STATIC|Flags.FINAL|Flags.ENUM; + if (S.deprecatedFlag()) { + flags |= Flags.DEPRECATED; + S.resetDeprecatedFlag(); + } + int pos = S.pos(); + List annotations = annotationsOpt(); + JCModifiers mods = F.at(annotations.isEmpty() ? Position.NOPOS : pos).Modifiers(flags, annotations); + List typeArgs = typeArgumentsOpt(); + int identPos = S.pos(); + Name name = ident(); + int createPos = S.pos(); + List args = (S.token() == LPAREN) + ? arguments() : List.nil(); + JCClassDecl body = null; + if (S.token() == LBRACE) { + JCModifiers mods1 = F.at(Position.NOPOS).Modifiers(Flags.ENUM | Flags.STATIC); + List defs = classOrInterfaceBody(names.empty, false); + body = toP(F.at(identPos).AnonymousClassDef(mods1, defs)); + } + if (args.isEmpty() && body == null) + createPos = Position.NOPOS; + JCIdent ident = F.at(Position.NOPOS).Ident(enumName); + JCNewClass create = F.at(createPos).NewClass(null, typeArgs, ident, args, body); + if (createPos != Position.NOPOS) + storeEnd(create, S.prevEndPos()); + ident = F.at(Position.NOPOS).Ident(enumName); + JCTree result = toP(F.at(pos).VarDef(mods, name, ident, create)); + attach(result, dc); + return result; + } + + /** TypeList = Type {"," Type} + */ + List typeList() { + ListBuffer ts = new ListBuffer(); + ts.append(type()); + while (S.token() == COMMA) { + S.nextToken(); + ts.append(type()); + } + return ts.toList(); + } + + /** ClassBody = "{" {ClassBodyDeclaration} "}" + * InterfaceBody = "{" {InterfaceBodyDeclaration} "}" + */ + List classOrInterfaceBody(Name className, boolean isInterface) { + accept(LBRACE); + if (S.pos() <= errorEndPos) { + // error recovery + skip(false, true, false, false); + if (S.token() == LBRACE) + S.nextToken(); + } + ListBuffer defs = new ListBuffer(); + while (S.token() != RBRACE && S.token() != EOF) { + defs.appendList(classOrInterfaceBodyDeclaration(className, isInterface)); + if (S.pos() <= errorEndPos) { + // error recovery + skip(false, true, true, false); + } + } + accept(RBRACE); + return defs.toList(); + } + + /** ClassBodyDeclaration = + * ";" + * | [STATIC] Block + * | ModifiersOpt + * ( Type Ident + * ( VariableDeclaratorsRest ";" | MethodDeclaratorRest ) + * | VOID Ident MethodDeclaratorRest + * | TypeParameters (Type | VOID) Ident MethodDeclaratorRest + * | Ident ConstructorDeclaratorRest + * | TypeParameters Ident ConstructorDeclaratorRest + * | ClassOrInterfaceOrEnumDeclaration + * ) + * InterfaceBodyDeclaration = + * ";" + * | ModifiersOpt Type Ident + * ( ConstantDeclaratorsRest | InterfaceMethodDeclaratorRest ";" ) + */ + List classOrInterfaceBodyDeclaration(Name className, boolean isInterface) { + if (S.token() == SEMI) { + S.nextToken(); + return List.of(F.at(Position.NOPOS).Block(0, List.nil())); + } else { + String dc = S.docComment(); + int pos = S.pos(); + JCModifiers mods = modifiersOpt(); + if (S.token() == CLASS || + S.token() == INTERFACE || + allowEnums && S.token() == ENUM) { + return List.of(classOrInterfaceOrEnumDeclaration(mods, dc)); + } else if (S.token() == LBRACE && !isInterface && + (mods.flags & Flags.StandardFlags & ~Flags.STATIC) == 0 && + mods.annotations.isEmpty()) { + return List.of(block(pos, mods.flags)); + } else { + pos = S.pos(); + List typarams = typeParametersOpt(); + // Hack alert: if there are type arguments but no Modifiers, the start + // position will be lost unless we set the Modifiers position. There + // should be an AST node for type parameters (BugId 5005090). + if (typarams.length() > 0 && mods.pos == Position.NOPOS) { + mods.pos = pos; + } + Token token = S.token(); + Name name = S.name(); + pos = S.pos(); + JCExpression type; + boolean isVoid = S.token() == VOID; + if (isVoid) { + type = to(F.at(pos).TypeIdent(TypeTags.VOID)); + S.nextToken(); + } else { + type = type(); + } + if (S.token() == LPAREN && !isInterface && type.getTag() == JCTree.IDENT) { + if (isInterface || name != className) + log.error(pos, "invalid.meth.decl.ret.type.req"); + return List.of(methodDeclaratorRest( + pos, mods, null, names.init, typarams, + isInterface, true, dc)); + } else { + pos = S.pos(); + name = ident(); + if (S.token() == LPAREN) { + return List.of(methodDeclaratorRest( + pos, mods, type, name, typarams, + isInterface, isVoid, dc)); + } else if (!isVoid && typarams.isEmpty()) { + List defs = + variableDeclaratorsRest(pos, mods, type, name, isInterface, dc, + new ListBuffer()).toList(); + storeEnd(defs.last(), S.endPos()); + accept(SEMI); + return defs; + } else { + pos = S.pos(); + List err = isVoid + ? List.of(toP(F.at(pos).MethodDef(mods, name, type, typarams, + List.nil(), List.nil(), null, null))) + : null; + return List.of(syntaxError(S.pos(), err, "expected", keywords.token2string(LPAREN))); + } + } + } + } + } + + /** MethodDeclaratorRest = + * FormalParameters BracketsOpt [Throws TypeList] ( MethodBody | [DEFAULT AnnotationValue] ";") + * VoidMethodDeclaratorRest = + * FormalParameters [Throws TypeList] ( MethodBody | ";") + * InterfaceMethodDeclaratorRest = + * FormalParameters BracketsOpt [THROWS TypeList] ";" + * VoidInterfaceMethodDeclaratorRest = + * FormalParameters [THROWS TypeList] ";" + * ConstructorDeclaratorRest = + * "(" FormalParameterListOpt ")" [THROWS TypeList] MethodBody + */ + JCTree methodDeclaratorRest(int pos, + JCModifiers mods, + JCExpression type, + Name name, + List typarams, + boolean isInterface, boolean isVoid, + String dc) { + List params = formalParameters(); + if (!isVoid) type = bracketsOpt(type); + List thrown = List.nil(); + if (S.token() == THROWS) { + S.nextToken(); + thrown = qualidentList(); + } + JCBlock body = null; + JCExpression defaultValue; + if (S.token() == LBRACE) { + body = block(); + defaultValue = null; + } else { + if (S.token() == DEFAULT) { + accept(DEFAULT); + defaultValue = annotationValue(); + } else { + defaultValue = null; + } + accept(SEMI); + if (S.pos() <= errorEndPos) { + // error recovery + skip(false, true, false, false); + if (S.token() == LBRACE) { + body = block(); + } + } + } + JCMethodDecl result = + toP(F.at(pos).MethodDef(mods, name, type, typarams, + params, thrown, + body, defaultValue)); + attach(result, dc); + return result; + } + + /** QualidentList = Qualident {"," Qualident} + */ + List qualidentList() { + ListBuffer ts = new ListBuffer(); + ts.append(qualident()); + while (S.token() == COMMA) { + S.nextToken(); + ts.append(qualident()); + } + return ts.toList(); + } + + /** TypeParametersOpt = ["<" TypeParameter {"," TypeParameter} ">"] + */ + List typeParametersOpt() { + if (S.token() == LT) { + checkGenerics(); + ListBuffer typarams = new ListBuffer(); + S.nextToken(); + typarams.append(typeParameter()); + while (S.token() == COMMA) { + S.nextToken(); + typarams.append(typeParameter()); + } + accept(GT); + return typarams.toList(); + } else { + return List.nil(); + } + } + + /** TypeParameter = TypeVariable [TypeParameterBound] + * TypeParameterBound = EXTENDS Type {"&" Type} + * TypeVariable = Ident + */ + JCTypeParameter typeParameter() { + int pos = S.pos(); + Name name = ident(); + ListBuffer bounds = new ListBuffer(); + if (S.token() == EXTENDS) { + S.nextToken(); + bounds.append(type()); + while (S.token() == AMP) { + S.nextToken(); + bounds.append(type()); + } + } + return toP(F.at(pos).TypeParameter(name, bounds.toList())); + } + + /** FormalParameters = "(" [ FormalParameterList ] ")" + * FormalParameterList = [ FormalParameterListNovarargs , ] LastFormalParameter + * FormalParameterListNovarargs = [ FormalParameterListNovarargs , ] FormalParameter + */ + List formalParameters() { + ListBuffer params = new ListBuffer(); + JCVariableDecl lastParam = null; + accept(LPAREN); + if (S.token() != RPAREN) { + params.append(lastParam = formalParameter()); + while ((lastParam.mods.flags & Flags.VARARGS) == 0 && S.token() == COMMA) { + S.nextToken(); + params.append(lastParam = formalParameter()); + } + } + accept(RPAREN); + return params.toList(); + } + + JCModifiers optFinal(long flags) { + JCModifiers mods = modifiersOpt(); + checkNoMods(mods.flags & ~(Flags.FINAL | Flags.DEPRECATED)); + mods.flags |= flags; + return mods; + } + + /** FormalParameter = { FINAL | '@' Annotation } Type VariableDeclaratorId + * LastFormalParameter = { FINAL | '@' Annotation } Type '...' Ident | FormalParameter + */ + JCVariableDecl formalParameter() { + JCModifiers mods = optFinal(Flags.PARAMETER); + JCExpression type = type(); + if (S.token() == ELLIPSIS) { + checkVarargs(); + mods.flags |= Flags.VARARGS; + type = to(F.at(S.pos()).TypeArray(type)); + S.nextToken(); + } + return variableDeclaratorId(mods, type); + } + +/* ---------- auxiliary methods -------------- */ + + /** Check that given tree is a legal expression statement. + */ + protected JCExpression checkExprStat(JCExpression t) { + switch(t.getTag()) { + case JCTree.PREINC: case JCTree.PREDEC: + case JCTree.POSTINC: case JCTree.POSTDEC: + case JCTree.ASSIGN: + case JCTree.BITOR_ASG: case JCTree.BITXOR_ASG: case JCTree.BITAND_ASG: + case JCTree.SL_ASG: case JCTree.SR_ASG: case JCTree.USR_ASG: + case JCTree.PLUS_ASG: case JCTree.MINUS_ASG: + case JCTree.MUL_ASG: case JCTree.DIV_ASG: case JCTree.MOD_ASG: + case JCTree.APPLY: case JCTree.NEWCLASS: + case JCTree.ERRONEOUS: + return t; + default: + log.error(t.pos, "not.stmt"); + return F.at(t.pos).Erroneous(List.of(t)); + } + } + + /** Return precedence of operator represented by token, + * -1 if token is not a binary operator. @see TreeInfo.opPrec + */ + static int prec(Token token) { + int oc = optag(token); + return (oc >= 0) ? TreeInfo.opPrec(oc) : -1; + } + + /** Return operation tag of binary operator represented by token, + * -1 if token is not a binary operator. + */ + static int optag(Token token) { + switch (token) { + case BARBAR: + return JCTree.OR; + case AMPAMP: + return JCTree.AND; + case BAR: + return JCTree.BITOR; + case BAREQ: + return JCTree.BITOR_ASG; + case CARET: + return JCTree.BITXOR; + case CARETEQ: + return JCTree.BITXOR_ASG; + case AMP: + return JCTree.BITAND; + case AMPEQ: + return JCTree.BITAND_ASG; + case EQEQ: + return JCTree.EQ; + case BANGEQ: + return JCTree.NE; + case LT: + return JCTree.LT; + case GT: + return JCTree.GT; + case LTEQ: + return JCTree.LE; + case GTEQ: + return JCTree.GE; + case LTLT: + return JCTree.SL; + case LTLTEQ: + return JCTree.SL_ASG; + case GTGT: + return JCTree.SR; + case GTGTEQ: + return JCTree.SR_ASG; + case GTGTGT: + return JCTree.USR; + case GTGTGTEQ: + return JCTree.USR_ASG; + case PLUS: + return JCTree.PLUS; + case PLUSEQ: + return JCTree.PLUS_ASG; + case SUB: + return JCTree.MINUS; + case SUBEQ: + return JCTree.MINUS_ASG; + case STAR: + return JCTree.MUL; + case STAREQ: + return JCTree.MUL_ASG; + case SLASH: + return JCTree.DIV; + case SLASHEQ: + return JCTree.DIV_ASG; + case PERCENT: + return JCTree.MOD; + case PERCENTEQ: + return JCTree.MOD_ASG; + case INSTANCEOF: + return JCTree.TYPETEST; + default: + return -1; + } + } + + /** Return operation tag of unary operator represented by token, + * -1 if token is not a binary operator. + */ + static int unoptag(Token token) { + switch (token) { + case PLUS: + return JCTree.POS; + case SUB: + return JCTree.NEG; + case BANG: + return JCTree.NOT; + case TILDE: + return JCTree.COMPL; + case PLUSPLUS: + return JCTree.PREINC; + case SUBSUB: + return JCTree.PREDEC; + default: + return -1; + } + } + + /** Return type tag of basic type represented by token, + * -1 if token is not a basic type identifier. + */ + static int typetag(Token token) { + switch (token) { + case BYTE: + return TypeTags.BYTE; + case CHAR: + return TypeTags.CHAR; + case SHORT: + return TypeTags.SHORT; + case INT: + return TypeTags.INT; + case LONG: + return TypeTags.LONG; + case FLOAT: + return TypeTags.FLOAT; + case DOUBLE: + return TypeTags.DOUBLE; + case BOOLEAN: + return TypeTags.BOOLEAN; + default: + return -1; + } + } + + void checkGenerics() { + if (!allowGenerics) { + log.error(S.pos(), "generics.not.supported.in.source", source.name); + allowGenerics = true; + } + } + void checkVarargs() { + if (!allowVarargs) { + log.error(S.pos(), "varargs.not.supported.in.source", source.name); + allowVarargs = true; + } + } + void checkForeach() { + if (!allowForeach) { + log.error(S.pos(), "foreach.not.supported.in.source", source.name); + allowForeach = true; + } + } + void checkStaticImports() { + if (!allowStaticImport) { + log.error(S.pos(), "static.import.not.supported.in.source", source.name); + allowStaticImport = true; + } + } + void checkAnnotations() { + if (!allowAnnotations) { + log.error(S.pos(), "annotations.not.supported.in.source", source.name); + allowAnnotations = true; + } + } +}