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