blob: 9e4aa9daee093fce2f5eb1b4890da062c2b664d1 [file] [log] [blame]
/*
* 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);
}
}