/*
 * 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.

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.

Error recovery is implemented by catching SyntaxError exceptions and discarding input tokens until an input token is reached that is possibly a legal continuation.

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.

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.

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
/** * 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
/** * 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.
/** * 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.
/** * Create a parser, given a scanner and the semantic callback. */
public Parser(Scanner scanner, ParserActions actions) throws IOException { this(scanner); this.actions = actions; }
Usually this.actions == (ParserActions)this. However, a delegate scanner can produce tokens for this parser, in which case (Scanner)this is unused, except for this.token and this.pos instance variables which are filled from the real scanner by this.scan() and the constructor.
/** * 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:
/** * 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 */
@Deprecated protected void packageDeclaration(long off, Identifier nm) { throw new RuntimeException("beginClass method is abstract"); }
import class
Deprecated:
/** * 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 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:
/** * 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 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:
/** * 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 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().
/** * 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:
/** * 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 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:
/** * 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 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.
/** * 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.
/** * 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.
/** * 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.
/** * 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.
/** * 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.
/** * 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.
/** * 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.
/** * 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.
/** * 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.
/** * 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.
/** * 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.
/** * 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; }
See Also:
Deprecated:
/** * @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.
/** * 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.
/** * 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.
/** * 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.
/** * 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.
/** * 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.
/** * 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.
/** * 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".
@argctx Syntactic context of the class, one of {PACKAGE CLASS STAT EXPR}.
/** * 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.
/** * 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.
/** * 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.
/** * 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 this.scanner == (Scanner)this. However, a delegate scanner can produce tokens for this parser, in which case (Scanner)this is unused, except for this.token and this.pos instance variables which are filled from the real scanner by this.scan() and the constructor.
/** * 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); } }