/*
* Janino - An embedded Java[TM] compiler
*
* Copyright (c) 2001-2010, Arno Unkrig
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the
* following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the
* following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the
* following disclaimer in the documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
* THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
package org.codehaus.janino;
import java.io.*;
import java.util.*;
import org.codehaus.commons.compiler.CompileException;
import org.codehaus.commons.compiler.Location;
import org.codehaus.janino.Java.CompilationUnit.ImportDeclaration;
import org.codehaus.janino.util.enumerator.Enumerator;
/**
* A parser for the Java™ programming language.
*/
public class Parser {
private final Scanner scanner;
public Parser(Scanner scanner) {
this.scanner = scanner;
}
public Scanner getScanner() {
return this.scanner;
}
/**
* <pre>
* CompilationUnit := [ PackageDeclaration ]
* { ImportDeclaration }
* { TypeDeclaration }
* </pre>
*/
public Java.CompilationUnit parseCompilationUnit() throws CompileException, IOException {
Java.CompilationUnit compilationUnit = new Java.CompilationUnit(this.location().getFileName());
if (this.peekKeyword("package")) {
compilationUnit.setPackageDeclaration(this.parsePackageDeclaration());
}
while (this.peekKeyword("import")) {
compilationUnit.addImportDeclaration(this.parseImportDeclaration());
}
while (!this.scanner.peek().isEOF()) {
if (this.peekOperator(";")) {
this.eatToken();
} else
{
compilationUnit.addPackageMemberTypeDeclaration(this.parsePackageMemberTypeDeclaration());
}
}
return compilationUnit;
}
/**
* <pre>
* PackageDeclaration := 'package' QualifiedIdentifier ';'
* </pre>
*/
public Java.PackageDeclaration parsePackageDeclaration() throws CompileException, IOException {
this.readKeyword("package");
Location loc = this.location();
String packageName = Parser.join(this.parseQualifiedIdentifier(), ".");
this.readOperator(";");
this.verifyStringIsConventionalPackageName(packageName, loc);
return new Java.PackageDeclaration(loc, packageName);
}
/**
* <pre>
* ImportDeclaration := 'import' ImportDeclarationBody ';'
* </pre>
*/
public Java.CompilationUnit.ImportDeclaration parseImportDeclaration() throws CompileException, IOException {
this.readKeyword("import");
Java.CompilationUnit.ImportDeclaration importDeclaration = this.parseImportDeclarationBody();
this.readOperator(";");
return importDeclaration;
}
/**
* <pre>
* ImportDeclarationBody := [ 'static' ] Identifier { '.' Identifier } [ '.' '*' ]
* </pre>
*/
public Java.CompilationUnit.ImportDeclaration parseImportDeclarationBody() throws CompileException, IOException {
Location loc = this.location();
boolean isStatic;
if (this.peekKeyword("static")) {
isStatic = true;
this.eatToken();
} else
{
isStatic = false;
}
List l = new ArrayList();
l.add(this.readIdentifier());
for (;;) {
if (!this.peekOperator(".")) {
String[] identifiers = (String[]) l.toArray(new String[l.size()]);
return (
isStatic
? (ImportDeclaration) new Java.CompilationUnit.SingleStaticImportDeclaration(loc, identifiers)
: (ImportDeclaration) new Java.CompilationUnit.SingleTypeImportDeclaration(loc, identifiers)
);
}
this.readOperator(".");
if (this.peekOperator("*")) {
this.eatToken();
String[] identifiers = (String[]) l.toArray(new String[l.size()]);
return (
isStatic
? (ImportDeclaration) new Java.CompilationUnit.StaticImportOnDemandDeclaration(loc, identifiers)
: (ImportDeclaration) new Java.CompilationUnit.TypeImportOnDemandDeclaration(loc, identifiers)
);
}
l.add(this.readIdentifier());
}
}
/**
* QualifiedIdentifier := Identifier { '.' Identifier }
*/
public String[] parseQualifiedIdentifier() throws CompileException, IOException {
if (!this.scanner.peek().isIdentifier()) this.throwCompileException("Identifier expected");
List l = new ArrayList();
l.add(this.readIdentifier());
while (this.peekOperator(".") && this.scanner.peekNextButOne().isIdentifier()) {
this.eatToken();
l.add(this.readIdentifier());
}
return (String[]) l.toArray(new String[l.size()]);
}
/**
* <pre>
* PackageMemberTypeDeclaration :=
* ModifiersOpt 'class' ClassDeclarationRest |
* ModifiersOpt 'interface' InterfaceDeclarationRest
* </pre>
*/
public Java.PackageMemberTypeDeclaration parsePackageMemberTypeDeclaration() throws CompileException, IOException {
String optionalDocComment = this.scanner.doc();
short modifiers = this.parseModifiersOpt();
Java.PackageMemberTypeDeclaration res;
if (this.peekKeyword("class")) {
if (optionalDocComment == null) this.warning("CDCM", "Class doc comment missing", this.location());
this.eatToken();
res = (Java.PackageMemberClassDeclaration) this.parseClassDeclarationRest(
optionalDocComment, // optionalDocComment
modifiers, // modifiers
ClassDeclarationContext.COMPILATION_UNIT // context
);
} else
if (this.peekKeyword("interface")) {
if (optionalDocComment == null) this.warning("IDCM", "Interface doc comment missing", this.location());
this.eatToken();
res = (Java.PackageMemberInterfaceDeclaration) this.parseInterfaceDeclarationRest(
optionalDocComment, // optionalDocComment
modifiers, // modifiers
InterfaceDeclarationContext.COMPILATION_UNIT // context
);
} else
{
this.throwCompileException(
"Unexpected token \""
+ this.scanner.peek()
+ "\" in class or interface declaration"
);
/* NEVER REACHED */ return null;
}
return res;
}
/**
* <pre>
* ModifiersOpt := { 'public' | 'protected' | 'private' | 'static' |
* 'abstract' | 'final' | 'native' | 'synchronized' |
* 'transient' | 'volatile' | 'strictfp'
* </pre>
*/
public short parseModifiersOpt() throws CompileException, IOException {
short mod = 0;
while (this.peekKeyword()) {
String kw = this.scanner.peek().getKeyword();
short x = (short) (
kw == "public" ? Mod.PUBLIC :
kw == "protected" ? Mod.PROTECTED :
kw == "private" ? Mod.PRIVATE :
kw == "static" ? Mod.STATIC :
kw == "abstract" ? Mod.ABSTRACT :
kw == "final" ? Mod.FINAL :
kw == "native" ? Mod.NATIVE :
kw == "synchronized" ? Mod.SYNCHRONIZED :
kw == "transient" ? Mod.TRANSIENT :
kw == "volatile" ? Mod.VOLATILE :
kw == "strictfp" ? Mod.STRICTFP :
-1
);
if (x == -1) break;
this.eatToken();
if ((mod & x) != 0) this.throwCompileException("Duplicate modifier \"" + kw + "\"");
for (int i = 0; i < Parser.MUTUALS.length; ++i) {
short m = Parser.MUTUALS[i];
if ((x & m) != 0 && (mod & m) != 0) {
this.throwCompileException("Only one of \"" + Mod.shortToString(m) + "\" allowed");
}
}
mod |= x;
}
return mod;
}
private static final short[] MUTUALS = {
Mod.PUBLIC | Mod.PROTECTED | Mod.PRIVATE,
};
/**
* <pre>
* ClassDeclarationRest :=
* Identifier
* [ 'extends' ReferenceType ]
* [ 'implements' ReferenceTypeList ]
* ClassBody
* </pre>
*/
public Java.NamedClassDeclaration parseClassDeclarationRest(
String optionalDocComment,
short modifiers,
ClassDeclarationContext context
) throws CompileException, IOException {
Location location = this.location();
String className = this.readIdentifier();
this.verifyIdentifierIsConventionalClassOrInterfaceName(className, location);
Java.ReferenceType optionalExtendedType = null;
if (this.peekKeyword("extends")) {
this.eatToken();
optionalExtendedType = this.parseReferenceType();
}
Java.ReferenceType[] implementedTypes = new Java.ReferenceType[0];
if (this.peekKeyword("implements")) {
this.eatToken();
implementedTypes = this.parseReferenceTypeList();
}
Java.NamedClassDeclaration namedClassDeclaration;
if (context == ClassDeclarationContext.COMPILATION_UNIT) {
namedClassDeclaration = new Java.PackageMemberClassDeclaration(
location, // location
optionalDocComment, // optionalDocComment
modifiers, // modifiers
className, // name
optionalExtendedType, // optinalExtendedType
implementedTypes // implementedTypes
);
} else
if (context == ClassDeclarationContext.TYPE_DECLARATION) {
namedClassDeclaration = new Java.MemberClassDeclaration(
location, // location
optionalDocComment, // optionalDocComment
modifiers, // modifiers
className, // name
optionalExtendedType, // optionalExtendedType
implementedTypes // implementedTypes
);
} else
if (context == ClassDeclarationContext.BLOCK) {
namedClassDeclaration = new Java.LocalClassDeclaration(
location, // location
optionalDocComment, // optionalDocComment
modifiers, // modifiers
className, // name
optionalExtendedType, // optionalExtendedType
implementedTypes // implementedTypes
);
} else
{
throw new JaninoRuntimeException("SNO: Class declaration in unexpected context " + context);
}
this.parseClassBody(namedClassDeclaration);
return namedClassDeclaration;
}
public static final class ClassDeclarationContext extends Enumerator {
public static final ClassDeclarationContext BLOCK = new ClassDeclarationContext("block");
public static final ClassDeclarationContext TYPE_DECLARATION = new ClassDeclarationContext("type_declaration");
public static final ClassDeclarationContext COMPILATION_UNIT = new ClassDeclarationContext("compilation_unit");
private ClassDeclarationContext(String name) { super(name); }
}
/**
* <pre>
* ClassBody := '{' { ClassBodyDeclaration } '}'
* </pre>
*/
public void parseClassBody(
Java.ClassDeclaration classDeclaration
) throws CompileException, IOException {
if (!this.peekOperator("{")) this.throwCompileException("\"{\" expected at start of class body");
this.eatToken();
for (;;) {
if (this.peekOperator("}")) {
this.eatToken();
return;
}
this.parseClassBodyDeclaration(classDeclaration);
}
}
/**
* <pre>
* ClassBodyDeclaration :=
* ';' |
* ModifiersOpt (
* Block | // Instance (JLS2 8.6) or static initializer (JLS2 8.7)
* 'void' Identifier MethodDeclarationRest |
* 'class' ClassDeclarationRest |
* 'interface' InterfaceDeclarationRest |
* ConstructorDeclarator |
* Type Identifier (
* MethodDeclarationRest |
* FieldDeclarationRest ';'
* )
* )
*
* </pre>
*/
public void parseClassBodyDeclaration(
Java.ClassDeclaration classDeclaration
) throws CompileException, IOException {
if (this.peekOperator(";")) {
this.eatToken();
return;
}
String optionalDocComment = this.scanner.doc();
short modifiers = this.parseModifiersOpt();
// Initializer?
if (this.peekOperator("{")) {
if ((modifiers & ~Mod.STATIC) != 0) {
this.throwCompileException("Only modifier \"static\" allowed on initializer");
}
Java.Initializer initializer = new Java.Initializer(
this.location(), // location
(modifiers & Mod.STATIC) != 0, // statiC
this.parseBlock() // block
);
classDeclaration.addVariableDeclaratorOrInitializer(initializer);
return;
}
// "void" method declaration.
if (this.peekKeyword("void")) {
Location location = this.location();
this.eatToken();
if (optionalDocComment == null) this.warning("MDCM", "Method doc comment missing", location);
String name = this.readIdentifier();
classDeclaration.addDeclaredMethod(this.parseMethodDeclarationRest(
optionalDocComment, // declaringType
modifiers, // optionalDocComment
new Java.BasicType(location, Java.BasicType.VOID), // modifiers
name // name
));
return;
}
// Member class.
if (this.peekKeyword("class")) {
if (optionalDocComment == null) this.warning("MCDCM", "Member class doc comment missing", this.location());
this.eatToken();
classDeclaration.addMemberTypeDeclaration((Java.MemberTypeDeclaration) this.parseClassDeclarationRest(
optionalDocComment, // optionalDocComment
modifiers, // modifiers
ClassDeclarationContext.TYPE_DECLARATION // context
));
return;
}
// Member interface.
if (this.peekKeyword("interface")) {
if (optionalDocComment == null) {
this.warning("MIDCM", "Member interface doc comment missing", this.location());
}
this.eatToken();
classDeclaration.addMemberTypeDeclaration((Java.MemberTypeDeclaration) this.parseInterfaceDeclarationRest(
optionalDocComment, // optionalDocComment
(short) (modifiers | Mod.STATIC), // modifiers
InterfaceDeclarationContext.NAMED_TYPE_DECLARATION // context
));
return;
}
// Constructor.
if (
classDeclaration instanceof Java.NamedClassDeclaration &&
this.scanner.peek().isIdentifier(((Java.NamedClassDeclaration) classDeclaration).getName()) &&
this.scanner.peekNextButOne().isOperator("(")
) {
if (optionalDocComment == null) this.warning("CDCM", "Constructor doc comment missing", this.location());
classDeclaration.addConstructor(this.parseConstructorDeclarator(
optionalDocComment, // declaringClass
modifiers // modifiers
));
return;
}
// Member method or field.
Java.Type memberType = this.parseType();
Location location = this.location();
String memberName = this.readIdentifier();
// Method declarator.
if (this.peekOperator("(")) {
if (optionalDocComment == null) this.warning("MDCM", "Method doc comment missing", this.location());
classDeclaration.addDeclaredMethod(this.parseMethodDeclarationRest(
optionalDocComment, // declaringType
modifiers, // optionalDocComment
memberType, // modifiers
memberName // name
));
return;
}
// Field declarator.
if (optionalDocComment == null) this.warning("FDCM", "Field doc comment missing", this.location());
Java.FieldDeclaration fd = new Java.FieldDeclaration(
location, // location
optionalDocComment, // optionalDocComment
modifiers, // modifiers
memberType, // type
this.parseFieldDeclarationRest(memberName) // variableDeclarators
);
this.readOperator(";");
classDeclaration.addVariableDeclaratorOrInitializer(fd);
}
/**
* <pre>
* InterfaceDeclarationRest :=
* Identifier
* [ 'extends' ReferenceTypeList ]
* InterfaceBody
* </pre>
*/
public Java.InterfaceDeclaration parseInterfaceDeclarationRest(
String optionalDocComment,
short modifiers,
InterfaceDeclarationContext context
) throws CompileException, IOException {
Location location = this.location();
String interfaceName = this.readIdentifier();
this.verifyIdentifierIsConventionalClassOrInterfaceName(interfaceName, location);
Java.ReferenceType[] extendedTypes = new Java.ReferenceType[0];
if (this.peekKeyword("extends")) {
this.eatToken();
extendedTypes = this.parseReferenceTypeList();
}
Java.InterfaceDeclaration interfaceDeclaration;
if (context == InterfaceDeclarationContext.COMPILATION_UNIT) {
interfaceDeclaration = new Java.PackageMemberInterfaceDeclaration(
location, // location
optionalDocComment, // optionalDocComment
modifiers, // modifiers
interfaceName, // name
extendedTypes // extendedTypes
);
} else
if (context == InterfaceDeclarationContext.NAMED_TYPE_DECLARATION) {
interfaceDeclaration = new Java.MemberInterfaceDeclaration(
location, // location
optionalDocComment, // optionalDocComment
modifiers, // modifiers
interfaceName, // name
extendedTypes // extendedTypes
);
} else
{
throw new JaninoRuntimeException("SNO: Interface declaration in unexpected context " + context);
}
this.parseInterfaceBody(interfaceDeclaration);
return interfaceDeclaration;
}
public static final class InterfaceDeclarationContext extends Enumerator {
// CHECKSTYLE(LineLengthCheck):OFF
public static final InterfaceDeclarationContext NAMED_TYPE_DECLARATION = new InterfaceDeclarationContext("named_type_declaration");
public static final InterfaceDeclarationContext COMPILATION_UNIT = new InterfaceDeclarationContext("compilation_unit");
// CHECKSTYLE(LineLengthCheck):ON
InterfaceDeclarationContext(String name) { super(name); }
}
/**
* <pre>
* InterfaceBody := '{' {
* ';' |
* ModifiersOpt (
* 'void' Identifier MethodDeclarationRest |
* 'class' ClassDeclarationRest |
* 'interface' InterfaceDeclarationRest |
* Type Identifier (
* MethodDeclarationRest |
* FieldDeclarationRest
* )
* )
* } '}'
* </pre>
*/
public void parseInterfaceBody(
Java.InterfaceDeclaration interfaceDeclaration
) throws CompileException, IOException {
this.readOperator("{");
for (;;) {
if (this.peekOperator("}")) {
this.eatToken();
break;
}
if (this.peekOperator(";")) {
this.eatToken();
continue;
}
String optionalDocComment = this.scanner.doc();
short modifiers = this.parseModifiersOpt();
// "void" method declaration.
if (this.peekKeyword("void")) {
if (optionalDocComment == null) this.warning("MDCM", "Method doc comment missing", this.location());
Location location = this.location();
this.eatToken();
String name = this.readIdentifier();
interfaceDeclaration.addDeclaredMethod(this.parseMethodDeclarationRest(
optionalDocComment, // declaringType
(short) (modifiers | Mod.ABSTRACT | Mod.PUBLIC), // optionalDocComment
new Java.BasicType(location, Java.BasicType.VOID), // modifiers
name // name
));
} else
// Member class.
if (this.peekKeyword("class")) {
if (optionalDocComment == null) {
this.warning("MCDCM", "Member class doc comment missing", this.location());
}
this.eatToken();
interfaceDeclaration.addMemberTypeDeclaration(
(Java.MemberTypeDeclaration) this.parseClassDeclarationRest(
optionalDocComment, // optionalDocComment
(short) (modifiers | Mod.STATIC | Mod.PUBLIC), // modifiers
ClassDeclarationContext.TYPE_DECLARATION // context
)
);
} else
// Member interface.
if (this.peekKeyword("interface")) {
if (optionalDocComment == null) {
this.warning("MIDCM", "Member interface doc comment missing", this.location());
}
this.eatToken();
interfaceDeclaration.addMemberTypeDeclaration(
(Java.MemberTypeDeclaration) this.parseInterfaceDeclarationRest(
optionalDocComment, // optionalDocComment
(short) (modifiers | Mod.STATIC | Mod.PUBLIC), // modifiers
InterfaceDeclarationContext.NAMED_TYPE_DECLARATION // context
)
);
} else
// Member method or field.
{
Java.Type memberType = this.parseType();
if (!this.scanner.peek().isIdentifier()) {
this.throwCompileException("Identifier expected in member declaration");
}
Location location = this.location();
String memberName = this.readIdentifier();
// Method declarator.
if (this.peekOperator("(")) {
if (optionalDocComment == null) this.warning("MDCM", "Method doc comment missing", this.location());
interfaceDeclaration.addDeclaredMethod(this.parseMethodDeclarationRest(
optionalDocComment, // declaringType
(short) (modifiers | Mod.ABSTRACT | Mod.PUBLIC), // optionalDocComment
memberType, // modifiers
memberName // name
));
} else
// Field declarator.
{
if (optionalDocComment == null) this.warning("FDCM", "Field doc comment missing", this.location());
Java.FieldDeclaration fd = new Java.FieldDeclaration(
location, // location
optionalDocComment, // optionalDocComment
(short) ( // modifiers
modifiers |
Mod.PUBLIC | Mod.STATIC | Mod.FINAL
),
memberType, // type
this.parseFieldDeclarationRest(memberName) // variableDeclarators
);
interfaceDeclaration.addConstantDeclaration(fd);
}
}
}
}
/**
* <pre>
* ConstructorDeclarator :=
* Identifier
* FormalParameters
* [ 'throws' ReferenceTypeList ]
* '{'
* [ 'this' Arguments ';' | 'super' Arguments ';' | Primary '.' 'super' Arguments ';' ]
* BlockStatements
* '}'
* </pre>
*/
public Java.ConstructorDeclarator parseConstructorDeclarator(
String optionalDocComment,
short modifiers
) throws CompileException, IOException {
Location location = this.location();
this.readIdentifier(); // Class name
// Parse formal parameters.
Java.FunctionDeclarator.FormalParameter[] formalParameters = this.parseFormalParameters(
);
// Parse "throws" clause.
Java.ReferenceType[] thrownExceptions;
if (this.peekKeyword("throws")) {
this.eatToken();
thrownExceptions = this.parseReferenceTypeList();
} else {
thrownExceptions = new Java.ReferenceType[0];
}
// Parse constructor body.
location = this.location();
this.readOperator("{");
// Special treatment for the first statement of the constructor body: If this is surely an
// expression statement, and if it could be a "ConstructorInvocation", then parse the
// expression and check if it IS a ConstructorInvocation.
Java.ConstructorInvocation optionalConstructorInvocation = null;
List/*<BlockStatement>*/ statements = new ArrayList();
if (
this.peekKeyword(new String[] {
"this", "super", "new", "void",
"byte", "char", "short", "int", "long", "float", "double", "boolean",
})
|| this.scanner.peek().isLiteral()
|| this.scanner.peek().isIdentifier()
) {
Java.Atom a = this.parseExpression();
if (a instanceof Java.ConstructorInvocation) {
this.readOperator(";");
optionalConstructorInvocation = (Java.ConstructorInvocation) a;
} else {
Java.Statement s;
if (this.scanner.peek().isIdentifier()) {
Java.Type variableType = a.toTypeOrPE();
s = new Java.LocalVariableDeclarationStatement(
a.getLocation(), // location
(short) 0, // modifiers
variableType, // type
this.parseLocalVariableDeclarators() // variableDeclarators
);
this.readOperator(";");
} else {
s = new Java.ExpressionStatement(a.toRvalueOrPE());
this.readOperator(";");
}
statements.add(s);
}
}
statements.addAll(this.parseBlockStatements());
this.readOperator("}");
return new Java.ConstructorDeclarator(
location, // location
optionalDocComment, // optionalDocComment
modifiers, // modifiers
formalParameters, // formalParameters
thrownExceptions, // thrownExceptions
optionalConstructorInvocation, // optionalConstructorInvocationStatement
statements // statements
);
}
/**
* <pre>
* MethodDeclarationRest :=
* FormalParameters
* { '[' ']' }
* [ 'throws' ReferenceTypeList ]
* ( ';' | MethodBody )
* </pre>
*/
public Java.MethodDeclarator parseMethodDeclarationRest(
String optionalDocComment,
short modifiers,
Java.Type type,
String name
) throws CompileException, IOException {
Location location = this.location();
this.verifyIdentifierIsConventionalMethodName(name, location);
Java.FunctionDeclarator.FormalParameter[] formalParameters = this.parseFormalParameters();
for (int i = this.parseBracketsOpt(); i > 0; --i) type = new Java.ArrayType(type);
Java.ReferenceType[] thrownExceptions;
if (this.peekKeyword("throws")) {
this.eatToken();
thrownExceptions = this.parseReferenceTypeList();
} else {
thrownExceptions = new Java.ReferenceType[0];
}
List/*<BlockStatement>*/ optionalStatements;
if (this.peekOperator(";")) {
if ((modifiers & (Mod.ABSTRACT | Mod.NATIVE)) == 0) {
this.throwCompileException("Non-abstract, non-native method must have a body");
}
this.eatToken();
optionalStatements = null;
} else {
if ((modifiers & (Mod.ABSTRACT | Mod.NATIVE)) != 0) {
this.throwCompileException("Abstract or native method must not have a body");
}
this.readOperator("{");
optionalStatements = this.parseBlockStatements();
this.readOperator("}");
}
return new Java.MethodDeclarator(
location, // location
optionalDocComment, // optionalDocComment
modifiers, // modifiers
type, // type
name, // name
formalParameters, // formalParameters
thrownExceptions, // thrownExceptions
optionalStatements // optionalStatements
);
}
/**
* <pre>
* VariableInitializer :=
* ArrayInitializer |
* Expression
* </pre>
*/
public Java.ArrayInitializerOrRvalue parseVariableInitializer() throws CompileException, IOException {
if (this.peekOperator("{")) {
return this.parseArrayInitializer();
} else
{
return this.parseExpression().toRvalueOrPE();
}
}
/**
* <pre>
* ArrayInitializer :=
* '{' [ VariableInitializer { ',' VariableInitializer } [ ',' ] '}'
* </pre>
*/
public Java.ArrayInitializer parseArrayInitializer() throws CompileException, IOException {
Location location = this.location();
this.readOperator("{");
List l = new ArrayList(); // ArrayInitializerOrRvalue
while (!this.peekOperator("}")) {
l.add(this.parseVariableInitializer());
if (this.peekOperator("}")) break;
if (!this.peekOperator(",")) this.throwCompileException("\",\" or \"}\" expected");
this.eatToken();
}
this.eatToken();
return new Java.ArrayInitializer(
location,
(Java.ArrayInitializerOrRvalue[]) l.toArray(new Java.ArrayInitializerOrRvalue[l.size()])
);
}
/**
* <pre>
* FormalParameters := '(' [ FormalParameter { ',' FormalParameter } ] ')'
* </pre>
*/
public Java.FunctionDeclarator.FormalParameter[] parseFormalParameters(
) throws CompileException, IOException {
this.readOperator("(");
if (this.peekOperator(")")) {
this.eatToken();
return new Java.FunctionDeclarator.FormalParameter[0];
}
List l = new ArrayList(); // Java.FormalParameter
for (;;) {
l.add(this.parseFormalParameter());
if (!this.peekOperator(",")) break;
this.eatToken();
}
this.readOperator(")");
return (Java.FunctionDeclarator.FormalParameter[]) l.toArray(
new Java.FunctionDeclarator.FormalParameter[l.size()]
);
}
/**
* <pre>
* FormalParameter := [ 'final' ] Type Identifier BracketsOpt
* </pre>
*/
public Java.FunctionDeclarator.FormalParameter parseFormalParameter() throws CompileException, IOException {
boolean finaL = this.peekKeyword("final");
if (finaL) this.eatToken();
Java.Type type = this.parseType();
Location location = this.location();
String name = this.readIdentifier();
this.verifyIdentifierIsConventionalLocalVariableOrParameterName(name, location);
for (int i = this.parseBracketsOpt(); i > 0; --i) type = new Java.ArrayType(type);
return new Java.FunctionDeclarator.FormalParameter(location, finaL, type, name);
}
/**
* <pre>
* BracketsOpt := { '[' ']' }
* </pre>
*/
int parseBracketsOpt() throws CompileException, IOException {
int res = 0;
while (
this.scanner.peek().isOperator("[")
&& this.scanner.peekNextButOne().isOperator("]")
) {
this.eatToken();
this.eatToken();
++res;
}
return res;
}
/**
* <pre>
* MethodBody := Block
* </pre>
*/
public Java.Block parseMethodBody() throws CompileException, IOException {
return this.parseBlock();
}
/**
* <pre>
* '{' BlockStatements '}'
* </pre>
*/
public Java.Block parseBlock() throws CompileException, IOException {
Java.Block block = new Java.Block(this.location());
this.readOperator("{");
block.addStatements(this.parseBlockStatements());
this.readOperator("}");
return block;
}
/**
* <pre>
* BlockStatements := { BlockStatement }
* </pre>
*/
public List parseBlockStatements() throws CompileException, IOException {
List l = new ArrayList();
while (
!this.peekOperator("}") &&
!this.peekKeyword("case") &&
!this.peekKeyword("default")
) l.add(this.parseBlockStatement());
return l;
}
/**
* <pre>
* BlockStatement := { Identifier ':' } (
* ( Modifiers Type | ModifiersOpt BasicType ) LocalVariableDeclarators ';' |
* 'class' ... |
* Statement |
* 'final' Type LocalVariableDeclarators ';' |
* Expression ';' |
* Expression LocalVariableDeclarators ';' (1)
* )
* </pre>
*
* (1) "Expression" must pose a type, and has optional trailing brackets.
*/
public Java.BlockStatement parseBlockStatement() throws CompileException, IOException {
// Statement?
if (
(
this.scanner.peek().isIdentifier() &&
this.scanner.peekNextButOne().isOperator(":")
) ||
this.peekKeyword(new String[] {
"if", "for", "while", "do", "try", "switch", "synchronized",
"return", "throw", "break", "continue"
}) ||
this.peekOperator(new String[] { "{", ";" })
) return this.parseStatement();
// Local class declaration?
if (this.peekKeyword("class")) {
// JAVADOC[TM] ignores doc comments for local classes, but we
// don't...
String optionalDocComment = this.scanner.doc();
if (optionalDocComment == null) this.warning("LCDCM", "Local class doc comment missing", this.location());
this.eatToken();
final Java.LocalClassDeclaration lcd = (Java.LocalClassDeclaration) this.parseClassDeclarationRest(
optionalDocComment, // optionalDocComment
Mod.NONE, // modifiers
ClassDeclarationContext.BLOCK // context
);
return new Java.LocalClassDeclarationStatement(lcd);
}
// 'final' Type LocalVariableDeclarators ';'
if (this.peekKeyword("final")) {
Location location = this.location();
this.eatToken();
Java.Type variableType = this.parseType();
Java.LocalVariableDeclarationStatement lvds = new Java.LocalVariableDeclarationStatement(
location, // location
Mod.FINAL, // modifiers
variableType, // type
this.parseLocalVariableDeclarators() // variableDeclarators
);
this.readOperator(";");
return lvds;
}
// It's either a non-final local variable declaration or an expression statement. We can
// only tell after parsing an expression.
Java.Atom a = this.parseExpression();
// Expression ';'
if (this.peekOperator(";")) {
this.eatToken();
return new Java.ExpressionStatement(a.toRvalueOrPE());
}
// Expression LocalVariableDeclarators ';'
Java.Type variableType = a.toTypeOrPE();
Java.LocalVariableDeclarationStatement lvds = new Java.LocalVariableDeclarationStatement(
a.getLocation(), // location
Mod.NONE, // modifiers
variableType, // type
this.parseLocalVariableDeclarators() // variableDeclarators
);
this.readOperator(";");
return lvds;
}
/**
* <pre>
* LocalVariableDeclarators := VariableDeclarator { ',' VariableDeclarator }
* </pre>
*/
public Java.VariableDeclarator[] parseLocalVariableDeclarators() throws CompileException, IOException {
List l = new ArrayList();
for (;;) {
Java.VariableDeclarator vd = this.parseVariableDeclarator();
this.verifyIdentifierIsConventionalLocalVariableOrParameterName(vd.name, vd.getLocation());
l.add(vd);
if (!this.peekOperator(",")) break;
this.eatToken();
}
return (Java.VariableDeclarator[]) l.toArray(new Java.VariableDeclarator[l.size()]);
}
/**
* <pre>
* FieldDeclarationRest :=
* VariableDeclaratorRest
* { ',' VariableDeclarator }
* </pre>
*/
public Java.VariableDeclarator[] parseFieldDeclarationRest(String name) throws CompileException, IOException {
List l = new ArrayList();
Java.VariableDeclarator vd = this.parseVariableDeclaratorRest(name);
this.verifyIdentifierIsConventionalFieldName(vd.name, vd.getLocation());
l.add(vd);
while (this.peekOperator(",")) {
this.eatToken();
vd = this.parseVariableDeclarator();
this.verifyIdentifierIsConventionalFieldName(vd.name, vd.getLocation());
l.add(vd);
}
return (Java.VariableDeclarator[]) l.toArray(new Java.VariableDeclarator[l.size()]);
}
/**
* <pre>
* VariableDeclarator := Identifier VariableDeclaratorRest
* </pre>
*/
public Java.VariableDeclarator parseVariableDeclarator() throws CompileException, IOException {
return this.parseVariableDeclaratorRest(this.readIdentifier());
}
/**
* <pre>
* VariableDeclaratorRest := { '[' ']' } [ '=' VariableInitializer ]
* </pre>
* Used by field declarations and local variable declarations.
*/
public Java.VariableDeclarator parseVariableDeclaratorRest(String name) throws CompileException, IOException {
Location loc = this.location();
int brackets = this.parseBracketsOpt();
Java.ArrayInitializerOrRvalue initializer = null;
if (this.peekOperator("=")) {
this.eatToken();
initializer = this.parseVariableInitializer();
}
return new Java.VariableDeclarator(loc, name, brackets, initializer);
}
/**
* <pre>
* Statement :=
* LabeledStatement |
* Block |
* IfStatement |
* ForStatement |
* WhileStatement |
* DoStatement |
* TryStatement |
* 'switch' ... |
* 'synchronized' ... |
* ReturnStatement |
* ThrowStatement |
* BreakStatement |
* ContinueStatement |
* EmptyStatement |
* ExpressionStatement
* </pre>
*/
public Java.Statement parseStatement() throws CompileException, IOException {
if (
this.scanner.peek().isIdentifier() &&
this.scanner.peekNextButOne().isOperator(":")
) {
return this.parseLabeledStatement();
}
Scanner.Token t = this.scanner.peek();
Java.Statement stmt = (
t.isOperator("{") ? this.parseBlock() :
t.isKeyword("if") ? this.parseIfStatement() :
t.isKeyword("for") ? this.parseForStatement() :
t.isKeyword("while") ? this.parseWhileStatement() :
t.isKeyword("do") ? this.parseDoStatement() :
t.isKeyword("try") ? this.parseTryStatement() :
t.isKeyword("switch") ? this.parseSwitchStatement() :
t.isKeyword("synchronized") ? this.parseSynchronizedStatement() :
t.isKeyword("return") ? this.parseReturnStatement() :
t.isKeyword("throw") ? this.parseThrowStatement() :
t.isKeyword("break") ? this.parseBreakStatement() :
t.isKeyword("continue") ? this.parseContinueStatement() :
t.isOperator(";") ? this.parseEmptyStatement() :
this.parseExpressionStatement()
);
if (stmt == null) this.throwCompileException("\"" + t.getKeyword() + "\" NYI");
return stmt;
}
/**
* <pre>
* LabeledStatement := Identifier ':' Statement
* </pre>
*/
public Java.Statement parseLabeledStatement() throws CompileException, IOException {
String label = this.readIdentifier();
this.readOperator(":");
return new Java.LabeledStatement(
this.location(), // location
label, // label
this.parseStatement() // body
);
}
/**
* <pre>
* IfStatement := 'if' '(' Expression ')' Statement [ 'else' Statement ]
* </pre>
*/
public Java.Statement parseIfStatement() throws CompileException, IOException {
Location location = this.location();
this.readKeyword("if");
this.readOperator("(");
final Java.Rvalue condition = this.parseExpression().toRvalueOrPE();
this.readOperator(")");
Java.Statement thenStatement = this.parseStatement();
Java.Statement optionalElseStatement = null;
if (this.peekKeyword("else")) {
this.eatToken();
optionalElseStatement = this.parseStatement();
}
return new Java.IfStatement(
location, // location
condition, // condition
thenStatement, // thenStatement
optionalElseStatement // optionalElseStatement
);
}
/**
* <pre>
* ForStatement :=
* 'for' '('
* [ ForInit ] ';'
* [ Expression ] ';'
* [ ExpressionList ]
* ')' Statement
* </pre>
*/
public Java.Statement parseForStatement() throws CompileException, IOException {
Location location = this.location();
this.readKeyword("for");
this.readOperator("(");
Java.BlockStatement optionalInit = null;
if (!this.peekOperator(";")) optionalInit = this.parseForInit();
this.readOperator(";");
Java.Rvalue optionalCondition = null;
if (!this.peekOperator(";")) optionalCondition = this.parseExpression().toRvalueOrPE();
this.readOperator(";");
Java.Rvalue[] optionalUpdate = null;
if (!this.peekOperator(")")) optionalUpdate = this.parseExpressionList();
this.readOperator(")");
// KLUDGE: Insert an implicit Block here.
// Java.Block implicitBlock = new Java.Block(location);
return /*Java.ForStatement forStatement =*/ new Java.ForStatement(
location, // location
optionalInit, // optionalInit
optionalCondition, // optionalCondition
optionalUpdate, // optionalUpdate
this.parseStatement() // body
);
// implicitBlock.addStatement(forStatement);
// return implicitBlock;
}
/**
* <pre>
* ForInit :=
* Modifiers Type LocalVariableDeclarators |
* ModifiersOpt BasicType LocalVariableDeclarators |
* Expression (
* LocalVariableDeclarators | (1)
* { ',' Expression }
* )
* </pre>
*
* (1) "Expression" must pose a type.
*/
private Java.BlockStatement parseForInit() throws CompileException, IOException {
// Modifiers Type LocalVariableDeclarators
// ModifiersOpt BasicType LocalVariableDeclarators
if (this.peekKeyword(new String[] {
"final", "byte", "short", "char", "int", "long", "float", "double", "boolean"
})) {
short modifiers = this.parseModifiersOpt();
Java.Type variableType = this.parseType();
return new Java.LocalVariableDeclarationStatement(
this.location(), // location
modifiers, // modifiers
variableType, // type
this.parseLocalVariableDeclarators() // variableDeclarators
);
}
Java.Atom a = this.parseExpression();
// Expression LocalVariableDeclarators
if (this.scanner.peek().isIdentifier()) {
Java.Type variableType = a.toTypeOrPE();
return new Java.LocalVariableDeclarationStatement(
a.getLocation(), // location
Mod.NONE, // modifiers
variableType, // type
this.parseLocalVariableDeclarators() // variableDeclarators
);
}
// Expression { ',' Expression }
if (!this.peekOperator(",")) {
return new Java.ExpressionStatement(a.toRvalueOrPE());
}
this.eatToken();
List l = new ArrayList();
l.add(new Java.ExpressionStatement(a.toRvalueOrPE()));
for (;;) {
l.add(new Java.ExpressionStatement(this.parseExpression().toRvalueOrPE()));
if (!this.peekOperator(",")) break;
this.eatToken();
}
Java.Block b = new Java.Block(a.getLocation());
b.addStatements(l);
return b;
}
/**
* <pre>
* WhileStatement := 'while' '(' Expression ')' Statement
* </pre>
*/
public Java.Statement parseWhileStatement() throws CompileException, IOException {
Location location = this.location();
this.readKeyword("while");
this.readOperator("(");
Java.Rvalue condition = this.parseExpression().toRvalueOrPE();
this.readOperator(")");
return new Java.WhileStatement(
location, // location
condition, // condition
this.parseStatement() // body
);
}
/**
* <pre>
* DoStatement := 'do' Statement 'while' '(' Expression ')' ';'
* </pre>
*/
public Java.Statement parseDoStatement() throws CompileException, IOException {
Location location = this.location();
this.readKeyword("do");
Java.Statement body = this.parseStatement();
this.readKeyword("while");
this.readOperator("(");
Java.Rvalue condition = this.parseExpression().toRvalueOrPE();
this.readOperator(")");
this.readOperator(";");
return new Java.DoStatement(
location, // location
body, // body
condition // condition
);
}
/**
* <pre>
* TryStatement :=
* 'try' Block Catches [ Finally ] |
* 'try' Block Finally
*
* Catches := CatchClause { CatchClause }
*
* CatchClause := 'catch' '(' FormalParameter ')' Block
*
* Finally := 'finally' Block
* </pre>
*/
public Java.Statement parseTryStatement() throws CompileException, IOException {
Location location = this.location();
this.readKeyword("try");
Java.Block body = this.parseBlock();
// { CatchClause }
List ccs = new ArrayList();
while (this.peekKeyword("catch")) {
Location loc = this.location();
this.eatToken();
this.readOperator("(");
Java.FunctionDeclarator.FormalParameter caughtException = this.parseFormalParameter();
this.readOperator(")");
ccs.add(new Java.CatchClause(
loc, // location
caughtException, // caughtException
this.parseBlock() // body
));
}
Java.Block optionalFinally = null;
if (this.peekKeyword("finally")) {
this.eatToken();
optionalFinally = this.parseBlock();
}
if (ccs.size() == 0 && optionalFinally == null) {
this.throwCompileException(
"\"try\" statement must have at least one \"catch\" clause or a \"finally\" clause"
);
}
return new Java.TryStatement(
location, // location
body, // body
ccs, // catchClauses
optionalFinally // optionalFinally
);
}
/**
* <pre>
* SwitchStatement :=
* 'switch' '(' Expression ')' '{' { SwitchLabels BlockStatements } '}'
*
* SwitchLabels := SwitchLabels { SwitchLabels }
*
* SwitchLabel := 'case' Expression ':' | 'default' ':'
* </pre>
*/
public Java.Statement parseSwitchStatement() throws CompileException, IOException {
Location location = this.location();
this.readKeyword("switch");
this.readOperator("(");
Java.Rvalue condition = this.parseExpression().toRvalueOrPE();
this.readOperator(")");
this.readOperator("{");
List sbsgs = new ArrayList();
while (!this.peekOperator("}")) {
Location location2 = this.location();
boolean hasDefaultLabel = false;
List caseLabels = new ArrayList();
do {
if (this.peekKeyword("case")) {
this.eatToken();
caseLabels.add(this.parseExpression().toRvalueOrPE());
} else
if (this.peekKeyword("default")) {
this.eatToken();
if (hasDefaultLabel) this.throwCompileException("Duplicate \"default\" label");
hasDefaultLabel = true;
} else {
this.throwCompileException("\"case\" or \"default\" expected");
}
this.readOperator(":");
} while (this.peekKeyword(new String[] { "case", "default" }));
Java.SwitchStatement.SwitchBlockStatementGroup sbsg = new Java.SwitchStatement.SwitchBlockStatementGroup(
location2, // location
caseLabels, // caseLabels
hasDefaultLabel, // hasDefaultLabel
this.parseBlockStatements() // blockStatements
);
sbsgs.add(sbsg);
}
this.eatToken();
return new Java.SwitchStatement(
location, // location
condition, // condition
sbsgs // sbsgs
);
}
/**
* <pre>
* SynchronizedStatement :=
* 'synchronized' '(' expression ')' Block
* </pre>
*/
public Java.Statement parseSynchronizedStatement() throws CompileException, IOException {
Location location = this.location();
this.readKeyword("synchronized");
this.readOperator("(");
Java.Rvalue expression = this.parseExpression().toRvalueOrPE();
this.readOperator(")");
return new Java.SynchronizedStatement(
location, // location
expression, // expression
this.parseBlock() // body
);
}
/**
* <pre>
* ReturnStatement := 'return' [ Expression ] ';'
* </pre>
*/
public Java.Statement parseReturnStatement() throws CompileException, IOException {
Location location = this.location();
this.readKeyword("return");
Java.Rvalue returnValue = this.peekOperator(";") ? null : this.parseExpression().toRvalueOrPE();
this.readOperator(";");
return new Java.ReturnStatement(location, returnValue);
}
/**
* <pre>
* ThrowStatement := 'throw' Expression ';'
* </pre>
*/
public Java.Statement parseThrowStatement() throws CompileException, IOException {
Location location = this.location();
this.readKeyword("throw");
final Java.Rvalue expression = this.parseExpression().toRvalueOrPE();
this.readOperator(";");
return new Java.ThrowStatement(location, expression);
}
/**
* <pre>
* BreakStatement := 'break' [ Identifier ] ';'
* </pre>
*/
public Java.Statement parseBreakStatement() throws CompileException, IOException {
Location location = this.location();
this.readKeyword("break");
String optionalLabel = null;
if (this.scanner.peek().isIdentifier()) optionalLabel = this.readIdentifier();
this.readOperator(";");
return new Java.BreakStatement(location, optionalLabel);
}
/**
* <pre>
* ContinueStatement := 'continue' [ Identifier ] ';'
* </pre>
*/
public Java.Statement parseContinueStatement() throws CompileException, IOException {
Location location = this.location();
this.readKeyword("continue");
String optionalLabel = null;
if (this.scanner.peek().isIdentifier()) optionalLabel = this.readIdentifier();
this.readOperator(";");
return new Java.ContinueStatement(location, optionalLabel);
}
/**
* <pre>
* EmptyStatement := ';'
* </pre>
*/
public Java.Statement parseEmptyStatement() throws CompileException, IOException {
Location location = this.location();
this.readOperator(";");
return new Java.EmptyStatement(location);
}
/**
* <pre>
* ExpressionList := Expression { ',' Expression }
* </pre>
*/
public Java.Rvalue[] parseExpressionList() throws CompileException, IOException {
List l = new ArrayList();
for (;;) {
l.add(this.parseExpression().toRvalueOrPE());
if (!this.peekOperator(",")) break;
this.eatToken();
}
return (Java.Rvalue[]) l.toArray(new Java.Rvalue[l.size()]);
}
/**
* <pre>
* Type := (
* 'byte' | 'short' | 'char' | 'int' | 'long' |
* 'float' | 'double' | 'boolean' |
* ReferenceType
* ) { '[' ']' }
* </pre>
*/
public Java.Type parseType() throws CompileException, IOException {
Scanner.Token t = this.scanner.peek();
int bt = -1;
if (t.isKeyword("byte")) { bt = Java.BasicType.BYTE; } else
if (t.isKeyword("short")) { bt = Java.BasicType.SHORT; } else
if (t.isKeyword("char")) { bt = Java.BasicType.CHAR; } else
if (t.isKeyword("int")) { bt = Java.BasicType.INT; } else
if (t.isKeyword("long")) { bt = Java.BasicType.LONG; } else
if (t.isKeyword("float")) { bt = Java.BasicType.FLOAT; } else
if (t.isKeyword("double")) { bt = Java.BasicType.DOUBLE; } else
if (t.isKeyword("boolean")) { bt = Java.BasicType.BOOLEAN; }
Java.Type res;
if (bt != -1) {
res = new Java.BasicType(t.getLocation(), bt);
this.eatToken();
} else {
res = this.parseReferenceType();
}
for (int i = this.parseBracketsOpt(); i > 0; --i) res = new Java.ArrayType(res);
return res;
}
/**
* <pre>
* ReferenceType := QualifiedIdentifier
* </pre>
*/
public Java.ReferenceType parseReferenceType() throws CompileException, IOException {
return new Java.ReferenceType(
this.location(), // location
this.parseQualifiedIdentifier() // identifiers
);
}
/**
* <pre>
* ReferenceTypeList := ReferenceType { ',' ReferenceType }
* </pre>
*/
public Java.ReferenceType[] parseReferenceTypeList() throws CompileException, IOException {
List l = new ArrayList();
l.add(this.parseReferenceType());
while (this.peekOperator(",")) {
this.eatToken();
l.add(this.parseReferenceType());
}
return (Java.ReferenceType[]) l.toArray(new Java.ReferenceType[l.size()]);
}
/**
* <pre>
* Expression := AssignmentExpression
* </pre>
*/
public Java.Atom parseExpression() throws CompileException, IOException {
return this.parseAssignmentExpression();
}
/**
* <pre>
* AssignmentExpression :=
* ConditionalExpression [ AssignmentOperator AssignmentExpression ]
*
* AssignmentOperator :=
* '=' | '*=' | '/=' | '%=' | '+=' | '-=' | '<<=' |
* '>>=' | '>>>=' | '&=' | '^=' | '|='
* </pre>
*/
public Java.Atom parseAssignmentExpression() throws CompileException, IOException {
Java.Atom a = this.parseConditionalExpression();
if (this.peekOperator(
new String[] { "=", "+=", "-=", "*=", "/=", "&=", "|=", "^=", "%=", "<<=", ">>=", ">>>=" }
)) {
Location location = this.location();
String operator = this.readOperator();
final Java.Lvalue lhs = a.toLvalueOrPE();
final Java.Rvalue rhs = this.parseAssignmentExpression().toRvalueOrPE();
return new Java.Assignment(location, lhs, operator, rhs);
}
return a;
}
/**
* <pre>
* ConditionalExpression :=
* ConditionalOrExpression [ '?' Expression ':' ConditionalExpression ]
* </pre>
*/
public Java.Atom parseConditionalExpression() throws CompileException, IOException {
Java.Atom a = this.parseConditionalOrExpression();
if (!this.peekOperator("?")) return a;
Location location = this.location();
this.eatToken();
Java.Rvalue lhs = a.toRvalueOrPE();
Java.Rvalue mhs = this.parseExpression().toRvalueOrPE();
this.readOperator(":");
Java.Rvalue rhs = this.parseConditionalExpression().toRvalueOrPE();
return new Java.ConditionalExpression(location, lhs, mhs, rhs);
}
/**
* <pre>
* ConditionalOrExpression :=
* ConditionalAndExpression { '||' ConditionalAndExpression ]
* </pre>
*/
public Java.Atom parseConditionalOrExpression() throws CompileException, IOException {
Java.Atom a = this.parseConditionalAndExpression();
while (this.peekOperator("||")) {
Location location = this.location();
this.eatToken();
a = new Java.BinaryOperation(
location,
a.toRvalueOrPE(),
"||",
this.parseConditionalAndExpression().toRvalueOrPE()
);
}
return a;
}
/**
* <pre>
* ConditionalAndExpression :=
* InclusiveOrExpression { '&&' InclusiveOrExpression }
* </pre>
*/
public Java.Atom parseConditionalAndExpression() throws CompileException, IOException {
Java.Atom a = this.parseInclusiveOrExpression();
while (this.peekOperator("&&")) {
Location location = this.location();
this.eatToken();
a = new Java.BinaryOperation(
location,
a.toRvalueOrPE(),
"&&",
this.parseInclusiveOrExpression().toRvalueOrPE()
);
}
return a;
}
/**
* <pre>
* InclusiveOrExpression :=
* ExclusiveOrExpression { '|' ExclusiveOrExpression }
* </pre>
*/
public Java.Atom parseInclusiveOrExpression() throws CompileException, IOException {
Java.Atom a = this.parseExclusiveOrExpression();
while (this.peekOperator("|")) {
Location location = this.location();
this.eatToken();
a = new Java.BinaryOperation(
location,
a.toRvalueOrPE(),
"|",
this.parseExclusiveOrExpression().toRvalueOrPE()
);
}
return a;
}
/**
* <pre>
* ExclusiveOrExpression :=
* AndExpression { '^' AndExpression }
* </pre>
*/
public Java.Atom parseExclusiveOrExpression() throws CompileException, IOException {
Java.Atom a = this.parseAndExpression();
while (this.peekOperator("^")) {
Location location = this.location();
this.eatToken();
a = new Java.BinaryOperation(
location,
a.toRvalueOrPE(),
"^",
this.parseAndExpression().toRvalueOrPE()
);
}
return a;
}
/**
* <pre>
* AndExpression :=
* EqualityExpression { '&' EqualityExpression }
* </pre>
*/
public Java.Atom parseAndExpression() throws CompileException, IOException {
Java.Atom a = this.parseEqualityExpression();
while (this.peekOperator("&")) {
Location location = this.location();
this.eatToken();
a = new Java.BinaryOperation(
location,
a.toRvalueOrPE(),
"&",
this.parseEqualityExpression().toRvalueOrPE()
);
}
return a;
}
/**
* <pre>
* EqualityExpression :=
* RelationalExpression { ( '==' | '!=' ) RelationalExpression }
* </pre>
*/
public Java.Atom parseEqualityExpression() throws CompileException, IOException {
Java.Atom a = this.parseRelationalExpression();
while (this.peekOperator(new String[] { "==", "!=" })) {
a = new Java.BinaryOperation(
this.location(), // location
a.toRvalueOrPE(), // lhs
this.readOperator(), // op
this.parseRelationalExpression().toRvalueOrPE() // rhs
);
}
return a;
}
/**
* <pre>
* RelationalExpression :=
* ShiftExpression {
* ( ( '<' | '>' | '<=' | '>=' ) ShiftExpression ) |
* ( 'instanceof' ReferenceType )
* }
* </pre>
*/
public Java.Atom parseRelationalExpression() throws CompileException, IOException {
Java.Atom a = this.parseShiftExpression();
for (;;) {
if (this.peekKeyword("instanceof")) {
Location location = this.location();
this.eatToken();
a = new Java.Instanceof(
location,
a.toRvalueOrPE(),
this.parseType()
);
} else
if (this.peekOperator(new String[] { "<", ">", "<=", ">=" })) {
a = new Java.BinaryOperation(
this.location(), // location
a.toRvalueOrPE(), // lhs
this.readOperator(), // op
this.parseShiftExpression().toRvalueOrPE() // rhs
);
} else {
return a;
}
}
}
/**
* <pre>
* ShiftExpression :=
* AdditiveExpression { ( '<<' | '>>' | '>>>' ) AdditiveExpression }
* </pre>
*/
public Java.Atom parseShiftExpression() throws CompileException, IOException {
Java.Atom a = this.parseAdditiveExpression();
while (this.peekOperator(new String[] { "<<", ">>", ">>>" })) {
a = new Java.BinaryOperation(
this.location(), // location
a.toRvalueOrPE(), // lhs
this.readOperator(), // op
this.parseAdditiveExpression().toRvalueOrPE() // rhs
);
}
return a;
}
/**
* <pre>
* AdditiveExpression :=
* MultiplicativeExpression { ( '+' | '-' ) MultiplicativeExpression }
* </pre>
*/
public Java.Atom parseAdditiveExpression() throws CompileException, IOException {
Java.Atom a = this.parseMultiplicativeExpression();
while (this.peekOperator(new String[] { "+", "-" })) {
a = new Java.BinaryOperation(
this.location(), // location
a.toRvalueOrPE(), // lhs
this.readOperator(), // op
this.parseMultiplicativeExpression().toRvalueOrPE() // rhs
);
}
return a;
}
/**
* <pre>
* MultiplicativeExpression :=
* UnaryExpression { ( '*' | '/' | '%' ) UnaryExpression }
* </pre>
*/
public Java.Atom parseMultiplicativeExpression() throws CompileException, IOException {
Java.Atom a = this.parseUnaryExpression();
while (this.peekOperator(new String[] { "*", "/", "%" })) {
a = new Java.BinaryOperation(
this.location(), // location
a.toRvalueOrPE(), // lhs
this.readOperator(), // op
this.parseUnaryExpression().toRvalueOrPE() // rhs
);
}
return a;
}
/**
* <pre>
* UnaryExpression :=
* { PrefixOperator } Primary { Selector } { PostfixOperator }
*
* PrefixOperator := '++' | '--' | '+' | '-' | '~' | '!'
*
* PostfixOperator := '++' | '--'
* </pre>
*/
public Java.Atom parseUnaryExpression() throws CompileException, IOException {
if (this.peekOperator(new String[] { "++", "--" })) {
return new Java.Crement(
this.location(), // location
this.readOperator(), // operator
this.parseUnaryExpression().toLvalueOrPE() // operand
);
}
if (this.peekOperator(new String[] { "+", "-", "~", "!" })) {
return new Java.UnaryOperation(
this.location(), // location
this.readOperator(), // operator
this.parseUnaryExpression().toRvalueOrPE() // operand
);
}
Java.Atom a = this.parsePrimary();
while (this.peekOperator(new String[] { ".", "[" })) {
a = this.parseSelector(a);
}
while (this.peekOperator(new String[] { "++", "--" })) {
a = new Java.Crement(
this.location(), // location
a.toLvalueOrPE(), // operand
this.readOperator() // operator
);
}
return a;
}
/**
* <pre>
* Primary :=
* CastExpression | // CastExpression 15.16
* '(' Expression ')' | // ParenthesizedExpression 15.8.5
* Literal | // Literal 15.8.1
* Name | // AmbiguousName
* Name Arguments | // MethodInvocation
* Name '[]' { '[]' } | // ArrayType 10.1
* Name '[]' { '[]' } '.' 'class' | // ClassLiteral 15.8.2
* 'this' | // This 15.8.3
* 'this' Arguments | // Alternate constructor invocation 8.8.5.1
* 'super' Arguments | // Unqualified superclass constructor invocation 8.8.5.1
* 'super' '.' Identifier | // SuperclassFieldAccess 15.11.2
* 'super' '.' Identifier Arguments | // SuperclassMethodInvocation 15.12.4.9
* NewClassInstance |
* NewAnonymousClassInstance | // ClassInstanceCreationExpression 15.9
* NewArray | // ArrayCreationExpression 15.10
* NewInitializedArray | // ArrayInitializer 10.6
* BasicType { '[]' } | // Type
* BasicType { '[]' } '.' 'class' | // ClassLiteral 15.8.2
* 'void' '.' 'class' // ClassLiteral 15.8.2
*
* CastExpression :=
* '(' PrimitiveType { '[]' } ')' UnaryExpression |
* '(' Expression ')' UnaryExpression
*
* NewClassInstance := 'new' ReferenceType Arguments
*
* NewAnonymousClassInstance := 'new' ReferenceType Arguments [ ClassBody ]
*
* NewArray := 'new' Type DimExprs { '[]' }
*
* NewInitializedArray := 'new' ArrayType ArrayInitializer
* </pre>
*/
public Java.Atom parsePrimary() throws CompileException, IOException {
if (this.peekOperator("(")) {
this.eatToken();
if (
this.peekKeyword(new String[] { "boolean", "char", "byte", "short", "int", "long", "float", "double", })
) {
// '(' PrimitiveType { '[]' } ')' UnaryExpression
Java.Type type = this.parseType();
int brackets = this.parseBracketsOpt();
this.readOperator(")");
for (int i = 0; i < brackets; ++i) type = new Java.ArrayType(type);
return new Java.Cast(
this.location(), // location
type, // targetType
this.parseUnaryExpression().toRvalueOrPE() // value
);
}
Java.Atom a = this.parseExpression();
this.readOperator(")");
if (
this.scanner.peek().isLiteral() ||
this.scanner.peek().isIdentifier() ||
this.peekOperator(new String[] { "(", "~", "!", }) ||
this.peekKeyword(new String[] { "this", "super", "new", })
) {
// '(' Expression ')' UnaryExpression
return new Java.Cast(
this.location(), // location
a.toTypeOrPE(), // targetType
this.parseUnaryExpression().toRvalueOrPE() // value
);
}
// '(' Expression ')'
return new Java.ParenthesizedExpression(a.getLocation(), a.toRvalueOrPE());
}
if (this.scanner.peek().isLiteral()) {
// Literal
return this.parseLiteral();
}
if (this.scanner.peek().isIdentifier()) {
Location location = this.location();
String[] qi = this.parseQualifiedIdentifier();
if (this.peekOperator("(")) {
// Name Arguments
return new Java.MethodInvocation(
this.location(), // location
qi.length == 1 ? null : new Java.AmbiguousName( // optionalTarget
location, // location
qi, // identifiers
qi.length - 1 // n
),
qi[qi.length - 1], // methodName
this.parseArguments() // arguments
);
}
if (
this.peekOperator("[")
&& this.scanner.peekNextButOne().isOperator("]")
) {
// Name '[]' { '[]' }
// Name '[]' { '[]' } '.' 'class'
Java.Type res = new Java.ReferenceType(
location, // location
qi // identifiers
);
int brackets = this.parseBracketsOpt();
for (int i = 0; i < brackets; ++i) res = new Java.ArrayType(res);
if (
this.peekOperator(".")
&& this.scanner.peekNextButOne().isKeyword("class")
) {
this.eatToken();
Location location2 = this.location();
this.eatToken();
return new Java.ClassLiteral(location2, res);
} else {
return res;
}
}
// Name
return new Java.AmbiguousName(
this.location(), // location
qi // identifiers
);
}
if (this.peekKeyword("this")) {
Location location = this.location();
this.eatToken();
if (this.peekOperator("(")) {
// 'this' Arguments
// Alternate constructor invocation (JLS 8.8.5.1)
return new Java.AlternateConstructorInvocation(
location, // location
this.parseArguments() // arguments
);
} else
{
// 'this'
return new Java.ThisReference(location);
}
}
if (this.peekKeyword("super")) {
this.eatToken();
if (this.peekOperator("(")) {
// 'super' Arguments
// Unqualified superclass constructor invocation (JLS 8.8.5.1)
return new Java.SuperConstructorInvocation(
this.location(), // location
(Java.Rvalue) null, // optionalQualification
this.parseArguments() // arguments
);
}
this.readOperator(".");
String name = this.readIdentifier();
if (this.peekOperator("(")) {
// 'super' '.' Identifier Arguments
return new Java.SuperclassMethodInvocation(
this.location(), // location
name, // methodName
this.parseArguments() // arguments
);
} else {
// 'super' '.' Identifier
return new Java.SuperclassFieldAccessExpression(
this.location(), // location
(Java.Type) null, // optionalQualification
name // fieldName
);
}
}
// 'new'
if (this.peekKeyword("new")) {
Location location = this.location();
this.eatToken();
Java.Type type = this.parseType();
if (type instanceof Java.ArrayType) {
// 'new' ArrayType ArrayInitializer
return new Java.NewInitializedArray(location, (Java.ArrayType) type, this.parseArrayInitializer());
}
if (
type instanceof Java.ReferenceType
&& this.peekOperator("(")
) {
// 'new' ReferenceType Arguments [ ClassBody ]
Java.Rvalue[] arguments = this.parseArguments();
if (this.peekOperator("{")) {
// 'new' ReferenceType Arguments ClassBody
final Java.AnonymousClassDeclaration anonymousClassDeclaration = new Java.AnonymousClassDeclaration(
this.location(), // location
type // baseType
);
this.parseClassBody(anonymousClassDeclaration);
return new Java.NewAnonymousClassInstance(
location, // location
(Java.Rvalue) null, // optionalQualification
anonymousClassDeclaration, // anonymousClassDeclaration
arguments // arguments
);
} else {
// 'new' ReferenceType Arguments
return new Java.NewClassInstance(
location, // location
(Java.Rvalue) null, // optionalQualification
type, // type
arguments // arguments
);
}
}
// 'new' Type DimExprs { '[]' }
return new Java.NewArray(
location, // location
type, // type
this.parseDimExprs(), // dimExprs
this.parseBracketsOpt() // dims
);
}
// BasicType
if (this.peekKeyword(new String[] { "boolean", "char", "byte", "short", "int", "long", "float", "double", })) {
Java.Type res = this.parseType();
int brackets = this.parseBracketsOpt();
for (int i = 0; i < brackets; ++i) res = new Java.ArrayType(res);
if (
this.peekOperator(".")
&& this.scanner.peekNextButOne().isKeyword("class")
) {
// BasicType { '[]' } '.' 'class'
this.eatToken();
Location location = this.location();
this.eatToken();
return new Java.ClassLiteral(location, res);
}
// BasicType { '[]' }
return res;
}
// 'void'
if (this.peekKeyword("void")) {
this.eatToken();
if (
this.peekOperator(".")
&& this.scanner.peekNextButOne().isKeyword("class")
) {
// 'void' '.' 'class'
this.eatToken();
Location location = this.location();
this.eatToken();
return new Java.ClassLiteral(location, new Java.BasicType(location, Java.BasicType.VOID));
}
this.throwCompileException("\"void\" encountered in wrong context");
}
this.throwCompileException("Unexpected token \"" + this.scanner.peek() + "\" in primary");
/* NEVER REACHED */ return null;
}
/**
* <pre>
* Selector :=
* '.' Identifier | // FieldAccess 15.11.1
* '.' Identifier Arguments | // MethodInvocation
* '.' 'this' // QualifiedThis 15.8.4
* '.' 'super' Arguments // Qualified superclass constructor invocation (JLS 8.8.5.1)
* '.' 'super' '.' Identifier | // SuperclassFieldReference (JLS 15.11.2)
* '.' 'super' '.' Identifier Arguments | // SuperclassMethodInvocation (JLS 15.12.4.9)
* '.' 'new' Identifier Arguments [ ClassBody ] | // QualifiedClassInstanceCreationExpression 15.9
* '.' 'class'
* '[' Expression ']' // ArrayAccessExpression 15.13
* </pre>
*/
public Java.Atom parseSelector(Java.Atom atom) throws CompileException, IOException {
if (this.peekOperator(".")) {
this.eatToken();
if (this.scanner.peek().isIdentifier()) {
String identifier = this.readIdentifier();
if (this.peekOperator("(")) {
// '.' Identifier Arguments
return new Java.MethodInvocation(
this.location(), // location
atom.toRvalueOrPE(), // optionalTarget
identifier, // methodName
this.parseArguments() // arguments
);
}
// '.' Identifier
return new Java.FieldAccessExpression(
this.location(), // location
atom.toRvalueOrPE(), // lhs
identifier // fieldName
);
}
if (this.peekKeyword("this")) {
// '.' 'this'
Location location = this.location();
this.eatToken();
return new Java.QualifiedThisReference(
location, // location
atom.toTypeOrPE() // qualification
);
}
if (this.peekKeyword("super")) {
Location location = this.location();
this.eatToken();
if (this.peekOperator("(")) {
// '.' 'super' Arguments
// Qualified superclass constructor invocation (JLS 8.8.5.1) (LHS is an Rvalue)
return new Java.SuperConstructorInvocation(
location, // location
atom.toRvalueOrPE(), // optionalQualification
this.parseArguments() // arguments
);
}
this.readOperator(".");
String identifier = this.readIdentifier();
if (this.peekOperator("(")) {
// '.' 'super' '.' Identifier Arguments
// Qualified superclass method invocation (JLS 15.12) (LHS is a ClassName)
// TODO: Qualified superclass method invocation
this.throwCompileException("Qualified superclass method invocation NYI");
} else {
// '.' 'super' '.' Identifier
// Qualified superclass field access (JLS 15.11.2) (LHS is an Rvalue)
return new Java.SuperclassFieldAccessExpression(
location, // location
atom.toTypeOrPE(), // optionalQualification
identifier // fieldName
);
}
}
if (this.peekKeyword("new")) {
// '.' 'new' Identifier Arguments [ ClassBody ]
Java.Rvalue lhs = atom.toRvalue();
Location location = this.location();
this.eatToken();
String identifier = this.readIdentifier();
Java.Type type = new Java.RvalueMemberType(
location, // location
lhs, // rValue
identifier // identifier
);
Java.Rvalue[] arguments = this.parseArguments();
if (this.peekOperator("{")) {
// '.' 'new' Identifier Arguments ClassBody (LHS is an Rvalue)
final Java.AnonymousClassDeclaration anonymousClassDeclaration = new Java.AnonymousClassDeclaration(
this.location(), // location
type // baseType
);
this.parseClassBody(anonymousClassDeclaration);
return new Java.NewAnonymousClassInstance(
location, // location
lhs, // optionalQualification
anonymousClassDeclaration, // anonymousClassDeclaration
arguments // arguments
);
} else {
// '.' 'new' Identifier Arguments (LHS is an Rvalue)
return new Java.NewClassInstance(
location, // location
lhs, // optionalQualification
type, // referenceType
arguments // arguments
);
}
}
if (this.peekKeyword("class")) {
// '.' 'class'
Location location = this.location();
this.eatToken();
return new Java.ClassLiteral(location, atom.toTypeOrPE());
}
this.throwCompileException("Unexpected selector \"" + this.scanner.peek() + "\" after \".\"");
}
if (this.peekOperator("[")) {
// '[' Expression ']'
Location location = this.location();
this.eatToken();
Java.Rvalue index = this.parseExpression().toRvalueOrPE();
this.readOperator("]");
return new Java.ArrayAccessExpression(
location, // location
atom.toRvalueOrPE(), // lhs
index // index
);
}
this.throwCompileException("Unexpected token \"" + this.scanner.peek() + "\" in selector");
/* NEVER REACHED */ return null;
}
/**
* <pre>
* DimExprs := DimExpr { DimExpr }
* </pre>
*/
public Java.Rvalue[] parseDimExprs() throws CompileException, IOException {
List l = new ArrayList();
l.add(this.parseDimExpr());
while (
this.peekOperator("[")
&& !this.scanner.peekNextButOne().isOperator("]")
) l.add(this.parseDimExpr());
return (Java.Rvalue[]) l.toArray(new Java.Rvalue[l.size()]);
}
/**
* <pre>
* DimExpr := '[' Expression ']'
* </pre>
*/
public Java.Rvalue parseDimExpr() throws CompileException, IOException {
this.readOperator("[");
Java.Rvalue res = this.parseExpression().toRvalueOrPE();
this.readOperator("]");
return res;
}
/**
* <pre>
* Arguments := '(' [ ArgumentList ] ')'
* </pre>
*/
public Java.Rvalue[] parseArguments() throws CompileException, IOException {
this.readOperator("(");
if (this.peekOperator(")")) {
this.eatToken();
return new Java.Rvalue[0];
}
Java.Rvalue[] arguments = this.parseArgumentList();
this.readOperator(")");
return arguments;
}
/**
* <pre>
* ArgumentList := Expression { ',' Expression }
* </pre>
*/
public Java.Rvalue[] parseArgumentList() throws CompileException, IOException {
List l = new ArrayList();
for (;;) {
l.add(this.parseExpression().toRvalueOrPE());
if (!this.peekOperator(",")) break;
this.eatToken();
}
return (Java.Rvalue[]) l.toArray(new Java.Rvalue[l.size()]);
}
public Java.Atom parseLiteral() throws CompileException, IOException {
Scanner.Token t = this.scanner.read();
if (!t.isLiteral()) this.throwCompileException("Literal expected");
return new Java.Literal(t.getLocation(), t.getLiteralValue());
}
// Simplified access to the scanner.
public Location location() { return this.scanner.location(); }
public void eatToken() throws CompileException, IOException { this.scanner.read(); }
// Keyword-related.
public boolean peekKeyword() { return this.scanner.peek().isKeyword(); }
public boolean peekKeyword(String keyword) { return this.scanner.peek().isKeyword(keyword); }
public boolean peekKeyword(String[] keywords) { return this.scanner.peek().isKeyword(keywords); }
public void readKeyword(String keyword) throws CompileException, IOException {
if (!this.scanner.read().isKeyword(keyword)) this.throwCompileException("\"" + keyword + "\" expected");
}
// Operator-related.
public boolean peekOperator(String operator) { return this.scanner.peek().isOperator(operator); }
public boolean peekOperator(String[] operators) { return this.scanner.peek().isOperator(operators); }
public String readOperator() throws CompileException, IOException {
Scanner.Token t = this.scanner.read();
if (!t.isOperator()) this.throwCompileException("Operator expected");
return t.getOperator();
}
public void readOperator(String operator) throws CompileException, IOException {
if (!this.scanner.read().isOperator(operator)) {
this.throwCompileException("Operator \"" + operator + "\" expected");
}
}
// Identifier-related.
public boolean peekIdentifier() { return this.scanner.peek().isIdentifier(); }
public String readIdentifier() throws CompileException, IOException {
Scanner.Token t = this.scanner.read();
if (!t.isIdentifier()) this.throwCompileException("Identifier expected");
return t.getIdentifier();
}
/**
* <pre>
* ExpressionStatement := Expression ';'
* </pre>
*/
public Java.Statement parseExpressionStatement() throws CompileException, IOException {
Java.Rvalue rv = this.parseExpression().toRvalueOrPE();
this.readOperator(";");
return new Java.ExpressionStatement(rv);
}
/**
* Issue a warning if the given string does not comply with the package naming conventions
* (JLS2 6.8.1).
*/
private void verifyStringIsConventionalPackageName(String s, Location loc) {
if (!Character.isLowerCase(s.charAt(0))) {
this.warning(
"UPN",
"Package name \"" + s + "\" does not begin with a lower-case letter (see JLS2 6.8.1)",
loc
);
return;
}
for (int i = 0; i < s.length(); ++i) {
char c = s.charAt(i);
if (!Character.isLowerCase(c) && c != '_' && c != '.') {
this.warning("PPN", "Poorly chosen package name \"" + s + "\" contains bad character '" + c + "'", loc);
return;
}
}
}
/**
* Issue a warning if the given identifier does not comply with the class and interface type
* naming conventions (JLS2 6.8.2).
*/
private void verifyIdentifierIsConventionalClassOrInterfaceName(String id, Location loc) {
if (!Character.isUpperCase(id.charAt(0))) {
this.warning(
"UCOIN1",
"Class or interface name \"" + id + "\" does not begin with an upper-case letter (see JLS2 6.8.2)",
loc
);
return;
}
for (int i = 0; i < id.length(); ++i) {
char c = id.charAt(i);
if (!Character.isLetter(c) && !Character.isDigit(c)) {
this.warning("UCOIN", (
"Class or interface name \""
+ id
+ "\" contains unconventional character \""
+ c
+ "\" (see JLS2 6.8.2)"
), loc);
return;
}
}
}
/**
* Issue a warning if the given identifier does not comply with the method naming conventions
* (JLS2 6.8.3).
*/
private void verifyIdentifierIsConventionalMethodName(String id, Location loc) {
if (!Character.isLowerCase(id.charAt(0))) {
this.warning(
"UMN1",
"Method name \"" + id + "\" does not begin with a lower-case letter (see JLS2 6.8.3)",
loc
);
return;
}
for (int i = 0; i < id.length(); ++i) {
char c = id.charAt(i);
if (!Character.isLetter(c) && !Character.isDigit(c)) {
this.warning(
"UMN",
"Method name \"" + id + "\" contains unconventional character \"" + c + "\" (see JLS2 6.8.3)",
loc
);
return;
}
}
}
/**
* Issue a warning if the given identifier does not comply with the field naming conventions
* (JLS2 6.8.4) and constant naming conventions (JLS2 6.8.5).
*/
private void verifyIdentifierIsConventionalFieldName(String id, Location loc) {
// In practice, a field is not always a constant iff it is static-final. So let's
// always tolerate both field and constant names.
if (Character.isUpperCase(id.charAt(0))) {
for (int i = 0; i < id.length(); ++i) {
char c = id.charAt(i);
if (!Character.isUpperCase(c) && !Character.isDigit(c) && c != '_') {
this.warning(
"UCN",
"Constant name \"" + id + "\" contains unconventional character \"" + c + "\" (see JLS2 6.8.5)",
loc
);
return;
}
}
} else
if (Character.isLowerCase(id.charAt(0))) {
for (int i = 0; i < id.length(); ++i) {
char c = id.charAt(i);
if (!Character.isLetter(c) && !Character.isDigit(c)) {
this.warning(
"UFN",
"Field name \"" + id + "\" contains unconventional character \"" + c + "\" (see JLS2 6.8.4)",
loc
);
return;
}
}
} else {
this.warning("UFN1", (
"\""
+ id
+ "\" is neither a conventional field name (JLS2 6.8.4) nor a conventional constant name (JLS2 6.8.5)"
), loc);
}
}
/**
* Issue a warning if the given identifier does not comply with the local variable and
* parameter naming conventions (JLS2 6.8.6).
*/
private void verifyIdentifierIsConventionalLocalVariableOrParameterName(String id, Location loc) {
if (!Character.isLowerCase(id.charAt(0))) {
this.warning(
"ULVN1",
"Local variable name \"" + id + "\" does not begin with a lower-case letter (see JLS2 6.8.6)",
loc
);
return;
}
for (int i = 0; i < id.length(); ++i) {
char c = id.charAt(i);
if (!Character.isLetter(c) && !Character.isDigit(c)) {
this.warning("ULVN", (
"Local variable name \""
+ id
+ "\" contains unconventional character \""
+ c
+ "\" (see JLS2 6.8.6)"
), loc);
return;
}
}
}
/**
* By default, warnings are discarded, but an application my install a {@link WarningHandler}.
* <p>
* Notice that there is no <code>Parser.setErrorHandler()</code> method, but parse errors always throw a {@link
* CompileException}. The reason being is that there is no reasonable way to recover from parse errors and continue
* parsing, so there is no need to install a custom parse error handler.
*
* @param optionalWarningHandler <code>null</code> to indicate that no warnings be issued
*/
public void setWarningHandler(WarningHandler optionalWarningHandler) {
this.optionalWarningHandler = optionalWarningHandler;
}
// Used for elaborate warning handling.
private WarningHandler optionalWarningHandler = null;
/**
* Issues a warning with the given message and location and returns. This is done through
* a {@link WarningHandler} that was installed through
* {@link #setWarningHandler(WarningHandler)}.
* <p>
* The <code>handle</code> argument qulifies the warning and is typically used by
* the {@link WarningHandler} to suppress individual warnings.
*/
private void warning(String handle, String message, Location optionalLocation) {
if (this.optionalWarningHandler != null) {
this.optionalWarningHandler.handleWarning(handle, message, optionalLocation);
}
}
/**
* Convenience method for throwing a CompileException.
*/
protected final void throwCompileException(String message) throws CompileException {
throw new CompileException(message, this.location());
}
private static String join(String[] sa, String separator) {
if (sa == null) return ("(null)");
if (sa.length == 0) return ("(zero length array)");
StringBuffer sb = new StringBuffer(sa[0]);
for (int i = 1; i < sa.length; ++i) {
sb.append(separator).append(sa[i]);
}
return sb.toString();
}
}