All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.hazelcast.org.codehaus.janino.Parser Maven / Gradle / Ivy

There is a newer version: 5.5.0
Show newest version

/*
 * Janino - An embedded Java[TM] compiler
 *
 * Copyright (c) 2001-2010 Arno Unkrig. All rights reserved.
 * Copyright (c) 2015-2016 TIBCO Software Inc. 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. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote
 *       products derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT HOLDER OR CONTRIBUTORS 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 com.hazelcast.org.codehaus.janino;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import com.hazelcast.org.codehaus.commons.compiler.CompileException;
import com.hazelcast.org.codehaus.commons.compiler.Location;
import com.hazelcast.org.codehaus.commons.compiler.WarningHandler;
import com.hazelcast.org.codehaus.commons.nullanalysis.Nullable;
import com.hazelcast.org.codehaus.janino.Java.AbstractClassDeclaration;
import com.hazelcast.org.codehaus.janino.Java.AlternateConstructorInvocation;
import com.hazelcast.org.codehaus.janino.Java.AmbiguousName;
import com.hazelcast.org.codehaus.janino.Java.Annotation;
import com.hazelcast.org.codehaus.janino.Java.AnnotationTypeDeclaration;
import com.hazelcast.org.codehaus.janino.Java.AnonymousClassDeclaration;
import com.hazelcast.org.codehaus.janino.Java.ArrayAccessExpression;
import com.hazelcast.org.codehaus.janino.Java.ArrayInitializer;
import com.hazelcast.org.codehaus.janino.Java.ArrayInitializerOrRvalue;
import com.hazelcast.org.codehaus.janino.Java.ArrayType;
import com.hazelcast.org.codehaus.janino.Java.AssertStatement;
import com.hazelcast.org.codehaus.janino.Java.Assignment;
import com.hazelcast.org.codehaus.janino.Java.Atom;
import com.hazelcast.org.codehaus.janino.Java.BinaryOperation;
import com.hazelcast.org.codehaus.janino.Java.Block;
import com.hazelcast.org.codehaus.janino.Java.BlockStatement;
import com.hazelcast.org.codehaus.janino.Java.BooleanLiteral;
import com.hazelcast.org.codehaus.janino.Java.BreakStatement;
import com.hazelcast.org.codehaus.janino.Java.Cast;
import com.hazelcast.org.codehaus.janino.Java.CatchClause;
import com.hazelcast.org.codehaus.janino.Java.CharacterLiteral;
import com.hazelcast.org.codehaus.janino.Java.ClassLiteral;
import com.hazelcast.org.codehaus.janino.Java.CompilationUnit;
import com.hazelcast.org.codehaus.janino.Java.CompilationUnit.ImportDeclaration;
import com.hazelcast.org.codehaus.janino.Java.ConditionalExpression;
import com.hazelcast.org.codehaus.janino.Java.ConstructorDeclarator;
import com.hazelcast.org.codehaus.janino.Java.ConstructorInvocation;
import com.hazelcast.org.codehaus.janino.Java.ContinueStatement;
import com.hazelcast.org.codehaus.janino.Java.Crement;
import com.hazelcast.org.codehaus.janino.Java.DoStatement;
import com.hazelcast.org.codehaus.janino.Java.ElementValue;
import com.hazelcast.org.codehaus.janino.Java.ElementValuePair;
import com.hazelcast.org.codehaus.janino.Java.EmptyStatement;
import com.hazelcast.org.codehaus.janino.Java.ExpressionStatement;
import com.hazelcast.org.codehaus.janino.Java.FieldAccess;
import com.hazelcast.org.codehaus.janino.Java.FieldAccessExpression;
import com.hazelcast.org.codehaus.janino.Java.FieldDeclaration;
import com.hazelcast.org.codehaus.janino.Java.FloatingPointLiteral;
import com.hazelcast.org.codehaus.janino.Java.ForEachStatement;
import com.hazelcast.org.codehaus.janino.Java.ForStatement;
import com.hazelcast.org.codehaus.janino.Java.FunctionDeclarator.FormalParameter;
import com.hazelcast.org.codehaus.janino.Java.FunctionDeclarator.FormalParameters;
import com.hazelcast.org.codehaus.janino.Java.IfStatement;
import com.hazelcast.org.codehaus.janino.Java.Initializer;
import com.hazelcast.org.codehaus.janino.Java.Instanceof;
import com.hazelcast.org.codehaus.janino.Java.IntegerLiteral;
import com.hazelcast.org.codehaus.janino.Java.InterfaceDeclaration;
import com.hazelcast.org.codehaus.janino.Java.LabeledStatement;
import com.hazelcast.org.codehaus.janino.Java.LocalClassDeclaration;
import com.hazelcast.org.codehaus.janino.Java.LocalClassDeclarationStatement;
import com.hazelcast.org.codehaus.janino.Java.LocalVariableDeclarationStatement;
import com.hazelcast.org.codehaus.janino.Java.Lvalue;
import com.hazelcast.org.codehaus.janino.Java.MemberAnnotationTypeDeclaration;
import com.hazelcast.org.codehaus.janino.Java.MemberClassDeclaration;
import com.hazelcast.org.codehaus.janino.Java.MemberInterfaceDeclaration;
import com.hazelcast.org.codehaus.janino.Java.MemberTypeDeclaration;
import com.hazelcast.org.codehaus.janino.Java.MethodDeclarator;
import com.hazelcast.org.codehaus.janino.Java.MethodInvocation;
import com.hazelcast.org.codehaus.janino.Java.Modifiers;
import com.hazelcast.org.codehaus.janino.Java.NamedClassDeclaration;
import com.hazelcast.org.codehaus.janino.Java.NewAnonymousClassInstance;
import com.hazelcast.org.codehaus.janino.Java.NewArray;
import com.hazelcast.org.codehaus.janino.Java.NewClassInstance;
import com.hazelcast.org.codehaus.janino.Java.NewInitializedArray;
import com.hazelcast.org.codehaus.janino.Java.NullLiteral;
import com.hazelcast.org.codehaus.janino.Java.PackageDeclaration;
import com.hazelcast.org.codehaus.janino.Java.PackageMemberAnnotationTypeDeclaration;
import com.hazelcast.org.codehaus.janino.Java.PackageMemberClassDeclaration;
import com.hazelcast.org.codehaus.janino.Java.PackageMemberEnumDeclaration;
import com.hazelcast.org.codehaus.janino.Java.PackageMemberInterfaceDeclaration;
import com.hazelcast.org.codehaus.janino.Java.PackageMemberTypeDeclaration;
import com.hazelcast.org.codehaus.janino.Java.ParenthesizedExpression;
import com.hazelcast.org.codehaus.janino.Java.Primitive;
import com.hazelcast.org.codehaus.janino.Java.PrimitiveType;
import com.hazelcast.org.codehaus.janino.Java.QualifiedThisReference;
import com.hazelcast.org.codehaus.janino.Java.ReferenceType;
import com.hazelcast.org.codehaus.janino.Java.ReturnStatement;
import com.hazelcast.org.codehaus.janino.Java.Rvalue;
import com.hazelcast.org.codehaus.janino.Java.RvalueMemberType;
import com.hazelcast.org.codehaus.janino.Java.Statement;
import com.hazelcast.org.codehaus.janino.Java.StringLiteral;
import com.hazelcast.org.codehaus.janino.Java.SuperConstructorInvocation;
import com.hazelcast.org.codehaus.janino.Java.SuperclassFieldAccessExpression;
import com.hazelcast.org.codehaus.janino.Java.SuperclassMethodInvocation;
import com.hazelcast.org.codehaus.janino.Java.SwitchStatement;
import com.hazelcast.org.codehaus.janino.Java.SynchronizedStatement;
import com.hazelcast.org.codehaus.janino.Java.ThisReference;
import com.hazelcast.org.codehaus.janino.Java.ThrowStatement;
import com.hazelcast.org.codehaus.janino.Java.TryStatement;
import com.hazelcast.org.codehaus.janino.Java.Type;
import com.hazelcast.org.codehaus.janino.Java.TypeArgument;
import com.hazelcast.org.codehaus.janino.Java.TypeParameter;
import com.hazelcast.org.codehaus.janino.Java.UnaryOperation;
import com.hazelcast.org.codehaus.janino.Java.VariableDeclarator;
import com.hazelcast.org.codehaus.janino.Java.WhileStatement;
import com.hazelcast.org.codehaus.janino.Java.Wildcard;

/**
 * A parser for the Java programming language.
 * 

* 'JLS7' refers to the Java Language Specification, Java SE 7 * Edition. *

*/ public class Parser { private final Scanner scanner; private final TokenStream tokenStream; public Parser(Scanner scanner) { this(scanner, new TokenStreamImpl(scanner)); } public Parser(Scanner scanner, TokenStream tokenStream) { (this.scanner = scanner).setIgnoreWhiteSpace(true); this.tokenStream = tokenStream; } /** * The optional JAVADOC comment preceding the {@link #nextToken}. */ @Nullable private String optionalDocComment; /** * Gets the text of the doc comment (a.k.a. "JAVADOC comment") preceding the next token. * * @return {@code null} if the next token is not preceded by a doc comment */ @Nullable public String doc() { String s = this.optionalDocComment; this.optionalDocComment = null; return s; } /** * @return The scanner that produces the tokens for this parser. */ public Scanner getScanner() { return this.scanner; } /** *
     *   CompilationUnit := [ PackageDeclaration ]
     *                      { ImportDeclaration }
     *                      { TypeDeclaration }
     * 
*/ public CompilationUnit parseCompilationUnit() throws CompileException, IOException { CompilationUnit compilationUnit = new CompilationUnit(this.location().getFileName()); String docComment = this.doc(); Modifiers modifiers = this.parseModifiers(); if (this.peek("package")) { if (modifiers.isDefault) { this.warning( "package.defaultModifier", "No \"default\" modifier allowed on package declaration", this.location() ); } if (modifiers.accessFlags != 0) { this.warning("package.accessFlags", "No access flags allowed on package declaration", this.location()); } // Ignore doc comment and annotations. compilationUnit.setPackageDeclaration(this.parsePackageDeclaration()); docComment = this.doc(); modifiers = this.parseModifiers(); } while (this.peek("import")) { if (modifiers.isDefault) { this.warning( "import.defaultModifier", "No \"default\" modifier allowed on import declaration", this.location() ); } if (modifiers.accessFlags != 0) { this.warning("import.accessFlags", "No access flags allowed on import declarations", this.location()); } if (modifiers.annotations.length > 0) { this.warning("import.annotations", "No annotations allowed on import declarations", this.location()); } if (docComment != null) { this.warning("import.doc_comment", "Doc comment on import declaration", this.location()); } compilationUnit.addImportDeclaration(this.parseImportDeclaration()); docComment = this.doc(); modifiers = this.parseModifiers(); } if (this.peek(TokenType.END_OF_INPUT)) return compilationUnit; // Parse the first package-member type declaration. compilationUnit.addPackageMemberTypeDeclaration( this.parsePackageMemberTypeDeclarationRest(docComment, modifiers) ); // Parse the second, third, ... package-member type declaration. while (!this.peek(TokenType.END_OF_INPUT)) { if (this.peekRead(";")) continue; compilationUnit.addPackageMemberTypeDeclaration(this.parsePackageMemberTypeDeclaration()); } return compilationUnit; } /** *
     *   PackageDeclaration := 'package' QualifiedIdentifier ';'
     * 
*/ public PackageDeclaration parsePackageDeclaration() throws CompileException, IOException { this.read("package"); Location loc = this.location(); String packageName = Parser.join(this.parseQualifiedIdentifier(), "."); this.read(";"); this.verifyStringIsConventionalPackageName(packageName, loc); return new PackageDeclaration(loc, packageName); } /** *
     *   ImportDeclaration := 'import' ImportDeclarationBody ';'
     * 
*/ public CompilationUnit.ImportDeclaration parseImportDeclaration() throws CompileException, IOException { this.read("import"); CompilationUnit.ImportDeclaration importDeclaration = this.parseImportDeclarationBody(); this.read(";"); return importDeclaration; } /** *
     *   ImportDeclarationBody := [ 'static' ] Identifier { '.' Identifier } [ '.' '*' ]
     * 
*/ public CompilationUnit.ImportDeclaration parseImportDeclarationBody() throws CompileException, IOException { final Location loc = this.location(); boolean isStatic = this.peekRead("static"); List l = new ArrayList(); l.add(this.read(TokenType.IDENTIFIER)); for (;;) { if (!this.peek(".")) { String[] identifiers = (String[]) l.toArray(new String[l.size()]); return ( isStatic ? (ImportDeclaration) new CompilationUnit.SingleStaticImportDeclaration(loc, identifiers) : (ImportDeclaration) new CompilationUnit.SingleTypeImportDeclaration(loc, identifiers) ); } this.read("."); if (this.peekRead("*")) { String[] identifiers = (String[]) l.toArray(new String[l.size()]); return ( isStatic ? (ImportDeclaration) new CompilationUnit.StaticImportOnDemandDeclaration(loc, identifiers) : (ImportDeclaration) new CompilationUnit.TypeImportOnDemandDeclaration(loc, identifiers) ); } l.add(this.read(TokenType.IDENTIFIER)); } } /** *
     *   QualifiedIdentifier := Identifier { '.' Identifier }
     * 
*/ public String[] parseQualifiedIdentifier() throws CompileException, IOException { List l = new ArrayList(); l.add(this.read(TokenType.IDENTIFIER)); while (this.peek(".") && this.peekNextButOne().type == TokenType.IDENTIFIER) { this.read(); l.add(this.read(TokenType.IDENTIFIER)); } return (String[]) l.toArray(new String[l.size()]); } /** *
     *   PackageMemberTypeDeclaration := ModifiersOpt PackageMemberTypeDeclarationRest
     * 
*/ public PackageMemberTypeDeclaration parsePackageMemberTypeDeclaration() throws CompileException, IOException { return this.parsePackageMemberTypeDeclarationRest(this.doc(), this.parseModifiers()); } /** *
     *   PackageMemberTypeDeclarationRest :=
     *             'class' ClassDeclarationRest |
     *             'enum' EnumDeclarationRest |
     *             'interface' InterfaceDeclarationRest
     *             '@' 'interface' AnnotationTypeDeclarationRest
     * 
*/ private PackageMemberTypeDeclaration parsePackageMemberTypeDeclarationRest(@Nullable String optionalDocComment, Modifiers modifiers) throws CompileException, IOException { if (modifiers.isDefault) { this.warning( "classOrInterfaceDeclaration.defaultModifier", "No \"default\" modifier allowed for class or interface declaration", this.location() ); } switch (this.read("class", "enum", "interface", "@")) { case 0: // "class" if (optionalDocComment == null) this.warning("CDCM", "Class doc comment missing", this.location()); return (PackageMemberClassDeclaration) this.parseClassDeclarationRest( optionalDocComment, // optionalDocComment modifiers, // modifiers ClassDeclarationContext.COMPILATION_UNIT // context ); case 1: // "enum" if (optionalDocComment == null) this.warning("EDCM", "Enum doc comment missing", this.location()); return (PackageMemberEnumDeclaration) this.parseEnumDeclarationRest( optionalDocComment, // optionalDocComment modifiers, // modifiers ClassDeclarationContext.COMPILATION_UNIT // context ); case 2: // "interface" if (optionalDocComment == null) this.warning("IDCM", "Interface doc comment missing", this.location()); return (PackageMemberInterfaceDeclaration) this.parseInterfaceDeclarationRest( optionalDocComment, // optionalDocComment modifiers, // modifiers InterfaceDeclarationContext.COMPILATION_UNIT // context ); case 3: // "@" this.read("interface"); if (optionalDocComment == null) { this.warning("ATDCM", "Annotation type doc comment missing", this.location()); } return (PackageMemberAnnotationTypeDeclaration) this.parseAnnotationTypeDeclarationRest( optionalDocComment, // optionalDocComment modifiers, // modifiers InterfaceDeclarationContext.COMPILATION_UNIT // context ); default: throw new IllegalStateException(); } } /** *
     *   Modifiers := {
     *       'public' | 'protected' | 'private' | 'static' | 'abstract' | 'final' | 'native'
     *       | 'synchronized' | 'transient' | 'volatile' | 'strictfp'
     *       | Annotation
     *       | 'default'
     *   }
     * 
*

* Includes the case "no modifiers". *

*/ public Java.Modifiers parseModifiers() throws CompileException, IOException { short afs = 0; List as = new ArrayList(); boolean isDefault = false; for (;;) { if (this.peek("@")) { // Annotation type declaration ahead? if (this.peekNextButOne().value.equals("interface")) break; as.add(this.parseAnnotation()); continue; } if (this.peekRead("default")) { if (isDefault) throw this.compileException("At most one \"default\" modifier allowed\""); isDefault = true; continue; } int idx = this.peekRead(Parser.ACCESS_FLAG_NAMES); if (idx == -1) break; String afn = Parser.ACCESS_FLAG_NAMES[idx]; short af = Parser.ACCESS_FLAG_CODES[idx]; if ((afs & af) != 0) throw this.compileException("Duplicate access flag \"" + afn + "\""); for (short meafs : Parser.MUTUALLY_EXCLUSIVE_ACCESS_FLAGS) { if ((af & meafs) != 0 && (afs & meafs) != 0) { throw this.compileException("Only one of \"" + Mod.shortToString(meafs) + "\" allowed"); } } afs |= af; } if (as.isEmpty() && afs == 0 && !isDefault) return Parser.NO_MODIFIERS; return new Modifiers(afs, (Annotation[]) as.toArray(new Java.Annotation[as.size()]), isDefault); } private static final String[] ACCESS_FLAG_NAMES = { "public", "protected", "private", "static", "abstract", "final", "native", "synchronized", "transient", "volatile", "strictfp" }; private static final short[] ACCESS_FLAG_CODES = { Mod.PUBLIC, Mod.PROTECTED, Mod.PRIVATE, Mod.STATIC, Mod.ABSTRACT, Mod.FINAL, Mod.NATIVE, Mod.SYNCHRONIZED, Mod.TRANSIENT, Mod.VOLATILE, Mod.STRICTFP }; private static final short[] MUTUALLY_EXCLUSIVE_ACCESS_FLAGS = { Mod.PUBLIC | Mod.PROTECTED | Mod.PRIVATE, Mod.ABSTRACT | Mod.FINAL, }; private static final Modifiers NO_MODIFIERS = new Modifiers(); /** *
     *   Annotation :=
     *           MarkerAnnotation             // JLS7 9.7.2
     *           | SingleElementAnnotation    // JLS7 9.7.3
     *           | NormalAnnotation           // JLS7 9.7.1
     *
     *   MarkerAnnotation        := '@' Identifier
     *
     *   SingleElementAnnotation := '@' Identifier '(' ElementValue ')'
     *
     *   NormalAnnotation        := '@' TypeName '(' ElementValuePairsOpt ')'
     *
     *   ElementValuePairsOpt    := [ ElementValuePair { ',' ElementValuePair } ]
     * 
*/ private Java.Annotation parseAnnotation() throws CompileException, IOException { this.read("@"); ReferenceType type = new ReferenceType(this.location(), this.parseQualifiedIdentifier(), null); // Marker annotation? if (!this.peekRead("(")) return new Java.MarkerAnnotation(type); // Single-element annotation? if (!this.peek(TokenType.IDENTIFIER) || !this.peekNextButOne("=")) { Java.ElementValue elementValue = this.parseElementValue(); this.read(")"); return new Java.SingleElementAnnotation(type, elementValue); } // Normal annotation. ElementValuePair[] elementValuePairs; if (this.peekRead(")")) { elementValuePairs = new ElementValuePair[0]; } else { List evps = new ArrayList(); do { evps.add(this.parseElementValuePair()); } while (this.read(",", ")") == 0); elementValuePairs = (ElementValuePair[]) evps.toArray(new Java.ElementValuePair[evps.size()]); } return new Java.NormalAnnotation( type, elementValuePairs ); } /** *
     *   ElementValuePair := Identifier '=' ElementValue
     * 
*/ private Java.ElementValuePair parseElementValuePair() throws CompileException, IOException { String identifier = this.read(TokenType.IDENTIFIER); this.read("="); return new Java.ElementValuePair(identifier, this.parseElementValue()); } /** *
     *   ElementValue :=
     *           ConditionalExpression
     *           | Annotation
     *           | ElementValueArrayInitializer
     * 
*/ private Java.ElementValue parseElementValue() throws CompileException, IOException { if (this.peek("@")) return this.parseAnnotation(); if (this.peek("{")) return this.parseElementValueArrayInitializer(); return this.parseConditionalAndExpression().toRvalueOrCompileException(); } /** *
     *   ElementValueArrayInitializer := '{' { ElementValue | ',' } '}'
     * 
*/ private ElementValue parseElementValueArrayInitializer() throws CompileException, IOException { this.read("{"); Location loc = this.location(); List evs = new ArrayList(); while (!this.peekRead("}")) { if (this.peekRead(",")) continue; evs.add(this.parseElementValue()); } return new Java.ElementValueArrayInitializer( (ElementValue[]) evs.toArray(new Java.ElementValue[evs.size()]), loc ); } /** *
     *   ClassDeclarationRest :=
     *        Identifier [ typeParameters ]
     *        [ 'extends' ReferenceType ]
     *        [ 'implements' ReferenceTypeList ]
     *        ClassBody
     * 
*/ public NamedClassDeclaration parseClassDeclarationRest( @Nullable String optionalDocComment, Modifiers modifiers, ClassDeclarationContext context ) throws CompileException, IOException { Location location = this.location(); String className = this.read(TokenType.IDENTIFIER); this.verifyIdentifierIsConventionalClassOrInterfaceName(className, location); TypeParameter[] optionalTypeParameters = this.parseTypeParametersOpt(); ReferenceType optionalExtendedType = null; if (this.peekRead("extends")) { optionalExtendedType = this.parseReferenceType(); } ReferenceType[] implementedTypes = new ReferenceType[0]; if (this.peekRead("implements")) { implementedTypes = this.parseReferenceTypeList(); } NamedClassDeclaration namedClassDeclaration; if (context == ClassDeclarationContext.COMPILATION_UNIT) { namedClassDeclaration = new PackageMemberClassDeclaration( location, // location optionalDocComment, // optionalDocComment modifiers, // modifiers className, // name optionalTypeParameters, // optionalTypeParameters optionalExtendedType, // optinalExtendedType implementedTypes // implementedTypes ); } else if (context == ClassDeclarationContext.TYPE_DECLARATION) { namedClassDeclaration = new MemberClassDeclaration( location, // location optionalDocComment, // optionalDocComment modifiers, // modifiers className, // name optionalTypeParameters, // optionalTypeParameters optionalExtendedType, // optionalExtendedType implementedTypes // implementedTypes ); } else if (context == ClassDeclarationContext.BLOCK) { namedClassDeclaration = new LocalClassDeclaration( location, // location optionalDocComment, // optionalDocComment modifiers, // modifiers className, // name optionalTypeParameters, // optionalTypeParameters optionalExtendedType, // optionalExtendedType implementedTypes // implementedTypes ); } else { throw new InternalCompilerException("SNO: Class declaration in unexpected context " + context); } this.parseClassBody(namedClassDeclaration); return namedClassDeclaration; } /** *
     *   EnumDeclarationRest := Identifier [ 'implements' ReferenceTypeList ] EnumBody
     * 
*/ public Java.EnumDeclaration parseEnumDeclarationRest( @Nullable String optionalDocComment, Modifiers modifiers, ClassDeclarationContext context ) throws CompileException, IOException { Location location = this.location(); String enumName = this.read(TokenType.IDENTIFIER); this.verifyIdentifierIsConventionalClassOrInterfaceName(enumName, location); if (this.peekRead("<")) { throw new CompileException("Enum declaration must not have type parameters", this.location()); } if (this.peekRead("extends")) { throw new CompileException("Enum declaration must not have an EXTENDS clause", this.location()); } ReferenceType[] implementedTypes = new ReferenceType[0]; if (this.peekRead("implements")) { implementedTypes = this.parseReferenceTypeList(); } Java.EnumDeclaration enumDeclaration; if (context == ClassDeclarationContext.COMPILATION_UNIT) { enumDeclaration = new Java.PackageMemberEnumDeclaration( location, // location optionalDocComment, // optionalDocComment modifiers, // modifiers enumName, // name implementedTypes // implementedTypes ); } else if (context == ClassDeclarationContext.TYPE_DECLARATION) { enumDeclaration = new Java.MemberEnumDeclaration( location, // location optionalDocComment, // optionalDocComment modifiers, // modifiers enumName, // name implementedTypes // implementedTypes ); } else { throw new InternalCompilerException("SNO: Enum declaration in unexpected context " + context); } this.parseEnumBody(enumDeclaration); return enumDeclaration; } /** * The kinds of context where a class declaration can occur. */ public enum ClassDeclarationContext { /** * The class declaration appears inside a 'block'. */ BLOCK, /** * The class declaration appears (directly) inside a type declaration. */ TYPE_DECLARATION, /** * The class declaration appears on the top level. */ COMPILATION_UNIT, } /** *
     *   ClassBody := '{' { ClassBodyDeclaration } '}'
     * 
*/ public void parseClassBody(AbstractClassDeclaration classDeclaration) throws CompileException, IOException { this.read("{"); for (;;) { if (this.peekRead("}")) return; this.parseClassBodyDeclaration(classDeclaration); } } /** *
     *   EnumBody := '{' [ EnumConstant { ',' EnumConstant } [ ',' ] [ ';' ] { ClassBodyDeclaration } '}'
     * 
*/ public void parseEnumBody(Java.EnumDeclaration enumDeclaration) throws CompileException, IOException { this.read("{"); while (this.peek(";", "}") == -1) { enumDeclaration.addConstant(this.parseEnumConstant()); if (!this.peekRead(",")) break; } while (!this.peekRead("}")) { this.parseClassBodyDeclaration((AbstractClassDeclaration) enumDeclaration); } } /** *
     *   EnumConstant := [ Annotations ] Identifier [ Arguments ] [ ClassBody ]
     * 
*/ public Java.EnumConstant parseEnumConstant() throws CompileException, IOException { List annotations = new ArrayList(); while (this.peek("@")) { annotations.add(this.parseAnnotation()); } String name = this.read(TokenType.IDENTIFIER); Rvalue[] arguments = this.peek("(") ? this.parseArguments() : null; Java.EnumConstant result = new Java.EnumConstant( this.location(), // location this.doc(), // optionalDocComment annotations, // annotations name, // name arguments // optionalArguments ); if (this.peek("{")) { this.parseClassBody(result); } return result; } /** *
     *   ClassBodyDeclaration :=
     *     ';' |
     *     ModifiersOpt (
     *       Block |                                    // Instance (JLS7 8.6) or static initializer (JLS7 8.7)
     *       'void' Identifier MethodDeclarationRest |
     *       'class' ClassDeclarationRest |
     *       'interface' InterfaceDeclarationRest |
     *       ConstructorDeclarator |
     *       [ TypeArguments ] Type Identifier MethodDeclarationRest |
     *       Type Identifier FieldDeclarationRest ';'
     *     )
     * 
*/ public void parseClassBodyDeclaration(AbstractClassDeclaration classDeclaration) throws CompileException, IOException { if (this.peekRead(";")) return; final String optionalDocComment = this.doc(); Modifiers modifiers = this.parseModifiers(); if (modifiers.isDefault) { throw this.compileException("Modifier \"default\" not allowed on class member"); } // Initializer? if (this.peek("{")) { if ((modifiers.accessFlags & ~Mod.STATIC) != 0) { throw this.compileException("Only access flag \"static\" allowed on initializer"); } Initializer initializer = new Initializer( this.location(), // location Mod.isStatic(modifiers.accessFlags), // statiC this.parseBlock() // block ); classDeclaration.addInitializer(initializer); return; } // "void" method declaration (without type arguments). if (this.peekRead("void")) { Location location = this.location(); if (optionalDocComment == null) this.warning("MDCM", "Method doc comment missing", location); String name = this.read(TokenType.IDENTIFIER); classDeclaration.addDeclaredMethod(this.parseMethodDeclarationRest( optionalDocComment, // optionalDocComment modifiers, // modifiers null, // optionalTypeParameters new PrimitiveType(location, Primitive.VOID), // type name, // name false // allowDefaultClause )); return; } // Member class. if (this.peekRead("class")) { if (optionalDocComment == null) this.warning("MCDCM", "Member class doc comment missing", this.location()); classDeclaration.addMemberTypeDeclaration((MemberTypeDeclaration) this.parseClassDeclarationRest( optionalDocComment, // optionalDocComment modifiers, // modifiers ClassDeclarationContext.TYPE_DECLARATION // context )); return; } // Member enum. if (this.peekRead("enum")) { if (optionalDocComment == null) this.warning("MEDCM", "Member enum doc comment missing", this.location()); classDeclaration.addMemberTypeDeclaration((MemberTypeDeclaration) this.parseEnumDeclarationRest( optionalDocComment, // optionalDocComment modifiers.add(Mod.ENUM), // modifiers ClassDeclarationContext.TYPE_DECLARATION // context )); return; } // Member interface. if (this.peekRead("interface")) { if (optionalDocComment == null) { this.warning("MIDCM", "Member interface doc comment missing", this.location()); } classDeclaration.addMemberTypeDeclaration((MemberTypeDeclaration) this.parseInterfaceDeclarationRest( optionalDocComment, // optionalDocComment modifiers.add(Mod.STATIC), // modifiers InterfaceDeclarationContext.NAMED_TYPE_DECLARATION // context )); return; } // Member annotation type. if (this.peek("@") && this.peekNextButOne("interface")) { this.read(); this.read(); if (optionalDocComment == null) { this.warning("MATDCM", "Member annotation type doc comment missing", this.location()); } classDeclaration.addMemberTypeDeclaration((MemberTypeDeclaration) this.parseInterfaceDeclarationRest( optionalDocComment, // optionalDocComment modifiers.add(Mod.STATIC | Mod.ANNOTATION), // modifiers InterfaceDeclarationContext.NAMED_TYPE_DECLARATION // context )); return; } // Constructor. if ( classDeclaration instanceof NamedClassDeclaration && this.peek().value.equals(((NamedClassDeclaration) classDeclaration).getName()) && this.peekNextButOne("(") ) { 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. TypeParameter[] optionalTypeParameters = this.parseTypeParametersOpt(); // VOID method declaration? if (this.peekRead("void")) { if (optionalDocComment == null) this.warning("MDCM", "Method doc comment missing", this.location()); String name = this.read(TokenType.IDENTIFIER); classDeclaration.addDeclaredMethod(this.parseMethodDeclarationRest( optionalDocComment, // optionalDocComment modifiers, // modifiers optionalTypeParameters, // optionalTypeParameters new PrimitiveType(this.location(), Primitive.VOID), // type name, // name false // allowDefaultClause )); return; } Type memberType = this.parseType(); Location location = this.location(); String memberName = this.read(TokenType.IDENTIFIER); // Method declarator. if (this.peek("(")) { if (optionalDocComment == null) this.warning("MDCM", "Method doc comment missing", this.location()); classDeclaration.addDeclaredMethod(this.parseMethodDeclarationRest( optionalDocComment, // optionalDocComment modifiers, // modifiers optionalTypeParameters, // optionalTypeParameters memberType, // type memberName, // name false // allowDefaultClause )); return; } // Field declarator. if (optionalTypeParameters != null) { throw new CompileException("Type parameters not allowed on field declaration", this.location()); } if (optionalDocComment == null) this.warning("FDCM", "Field doc comment missing", this.location()); FieldDeclaration fd = new FieldDeclaration( location, // location optionalDocComment, // optionalDocComment modifiers, // modifiers memberType, // type this.parseFieldDeclarationRest(memberName) // variableDeclarators ); this.read(";"); classDeclaration.addFieldDeclaration(fd); } /** *
     *   InterfaceDeclarationRest :=
     *     Identifier [ typeParameters ]
     *     [ 'extends' ReferenceTypeList ]
     *     InterfaceBody
     * 
*/ public InterfaceDeclaration parseInterfaceDeclarationRest( @Nullable String optionalDocComment, Modifiers modifiers, InterfaceDeclarationContext context ) throws CompileException, IOException { Location location = this.location(); String interfaceName = this.read(TokenType.IDENTIFIER); this.verifyIdentifierIsConventionalClassOrInterfaceName(interfaceName, location); TypeParameter[] optionalTypeParameters = this.parseTypeParametersOpt(); ReferenceType[] extendedTypes = new ReferenceType[0]; if (this.peekRead("extends")) { extendedTypes = this.parseReferenceTypeList(); } InterfaceDeclaration id; if (context == InterfaceDeclarationContext.COMPILATION_UNIT) { id = new PackageMemberInterfaceDeclaration( location, // location optionalDocComment, // optionalDocComment modifiers, // modifiers interfaceName, // name optionalTypeParameters, // optionalTypeParameters extendedTypes // extendedTypes ); } else if (context == InterfaceDeclarationContext.NAMED_TYPE_DECLARATION) { id = new MemberInterfaceDeclaration( location, // location optionalDocComment, // optionalDocComment modifiers, // modifiers interfaceName, // name optionalTypeParameters, // optionalTypeParameters extendedTypes // extendedTypes ); } else { throw new InternalCompilerException("SNO: Interface declaration in unexpected context " + context); } this.parseInterfaceBody(id); return id; } /** *
     *   AnnotationTypeDeclarationRest := Identifier AnnotationTypeBody
     * 
*/ public AnnotationTypeDeclaration parseAnnotationTypeDeclarationRest( @Nullable String optionalDocComment, Modifiers modifiers, InterfaceDeclarationContext context ) throws CompileException, IOException { Location location = this.location(); String annotationTypeName = this.read(TokenType.IDENTIFIER); this.verifyIdentifierIsConventionalClassOrInterfaceName(annotationTypeName, location); AnnotationTypeDeclaration atd; if (context == InterfaceDeclarationContext.COMPILATION_UNIT) { atd = new PackageMemberAnnotationTypeDeclaration( location, // location optionalDocComment, // optionalDocComment modifiers, // modifiers annotationTypeName // name ); } else if (context == InterfaceDeclarationContext.NAMED_TYPE_DECLARATION) { atd = new MemberAnnotationTypeDeclaration( location, // location optionalDocComment, // optionalDocComment modifiers, // modifiers annotationTypeName // name ); } else { throw new InternalCompilerException("SNO: Interface declaration in unexpected context " + context); } this.parseInterfaceBody((InterfaceDeclaration) atd); return atd; } /** * The kinds of context where an interface declaration can occur. */ public enum InterfaceDeclarationContext { /** * The interface declaration appears (directly) inside a 'named type declaration'. */ NAMED_TYPE_DECLARATION, /** * The interface declaration appears at the top level. */ COMPILATION_UNIT, } /** *
     *   InterfaceBody := '{' {
     *     ';' |
     *     ModifiersOpt (
     *       'void' Identifier MethodDeclarationRest |
     *       'class' ClassDeclarationRest |
     *       'interface' InterfaceDeclarationRest |
     *       Type Identifier (
     *         MethodDeclarationRest |
     *         FieldDeclarationRest
     *       )
     *     )
     *   } '}'
     * 
*/ public void parseInterfaceBody(InterfaceDeclaration interfaceDeclaration) throws CompileException, IOException { this.read("{"); while (!this.peekRead("}")) { if (this.peekRead(";")) continue; String optionalDocComment = this.doc(); Modifiers modifiers = this.parseModifiers(); // "void" method declaration (without type parameters). if (this.peekRead("void")) { if (optionalDocComment == null) this.warning("MDCM", "Method doc comment missing", this.location()); if (modifiers.isDefault) throw this.compileException("Default interface methods not implemented"); interfaceDeclaration.addDeclaredMethod(this.parseMethodDeclarationRest( optionalDocComment, // optionalDocComment modifiers.add(Mod.ABSTRACT | Mod.PUBLIC), // modifiers null, // optionalTypeParameters new PrimitiveType(this.location(), Primitive.VOID), // type this.read(TokenType.IDENTIFIER), // name false // allowDefaultClause )); continue; } // Member class. if (this.peekRead("class")) { if (optionalDocComment == null) { this.warning("MCDCM", "Member class doc comment missing", this.location()); } if (modifiers.isDefault) { throw this.compileException("Modifier \"default\" not allowed on member class declaration"); } interfaceDeclaration.addMemberTypeDeclaration( (MemberTypeDeclaration) this.parseClassDeclarationRest( optionalDocComment, // optionalDocComment modifiers.add(Mod.STATIC | Mod.PUBLIC), // modifiers ClassDeclarationContext.TYPE_DECLARATION // context ) ); continue; } // Member enum. if (this.peekRead("enum")) { if (optionalDocComment == null) { this.warning("MEDCM", "Member enum doc comment missing", this.location()); } if (modifiers.isDefault) { throw this.compileException("Modifier \"default\" not allowed on member enum declaration"); } interfaceDeclaration.addMemberTypeDeclaration( (MemberTypeDeclaration) this.parseClassDeclarationRest( optionalDocComment, // optionalDocComment modifiers.add(Mod.STATIC | Mod.PUBLIC | Mod.ENUM), // modifiers ClassDeclarationContext.TYPE_DECLARATION // context ) ); continue; } // Member interface. if (this.peekRead("interface")) { if (optionalDocComment == null) { this.warning("MIDCM", "Member interface doc comment missing", this.location()); } if (modifiers.isDefault) { throw this.compileException("Modifier \"default\" not allowed on member interface declaration"); } interfaceDeclaration.addMemberTypeDeclaration( (MemberTypeDeclaration) this.parseInterfaceDeclarationRest( optionalDocComment, // optionalDocComment modifiers.add(Mod.STATIC | Mod.PUBLIC), // modifiers InterfaceDeclarationContext.NAMED_TYPE_DECLARATION // context ) ); continue; } // Member annotation type. if (this.peek("@") && this.peekNextButOne("interface")) { this.read(); this.read(); if (optionalDocComment == null) { this.warning("MATDCM", "Member annotation type doc comment missing", this.location()); } if (modifiers.isDefault) { throw this.compileException( "Modifier \"default\" not allowed on member annotation type declaration" ); } interfaceDeclaration.addMemberTypeDeclaration( (MemberTypeDeclaration) this.parseInterfaceDeclarationRest( optionalDocComment, // optionalDocComment modifiers.add(Mod.STATIC | Mod.PUBLIC | Mod.ANNOTATION), // modifiers InterfaceDeclarationContext.NAMED_TYPE_DECLARATION // context ) ); continue; } // Member method or field. TypeParameter[] optionalTypeParameters = this.parseTypeParametersOpt(); // "void" method declaration? if (this.peekRead("void")) { if (optionalDocComment == null) this.warning("MDCM", "Method doc comment missing", this.location()); if (modifiers.isDefault) throw this.compileException("Default interface methods not implemented"); Location location = this.location(); String name = this.read(TokenType.IDENTIFIER); interfaceDeclaration.addDeclaredMethod(this.parseMethodDeclarationRest( optionalDocComment, // optionalDocComment modifiers.add(Mod.ABSTRACT | Mod.PUBLIC), // modifiers optionalTypeParameters, // optionalTypeParameters new PrimitiveType(location, Primitive.VOID), // type name, // name false // allowDefaultClause )); continue; } Type memberType = this.parseType(); String memberName = this.read(TokenType.IDENTIFIER); Location location = this.location(); // Method declarator? if (this.peek("(")) { if (optionalDocComment == null) this.warning("MDCM", "Method doc comment missing", this.location()); if (modifiers.isDefault) throw this.compileException("Default interface methods not implemented"); interfaceDeclaration.addDeclaredMethod(this.parseMethodDeclarationRest( optionalDocComment, // optionalDocComment modifiers.add(Mod.PUBLIC), // modifiers optionalTypeParameters, // optionalTypeParameters memberType, // type memberName, // name true // allowDefaultClause )); continue; } // Must be a field declarator. if (optionalTypeParameters != null) { throw new CompileException("Type parameters not allowed with field declaration", this.location()); } if (optionalDocComment == null) this.warning("FDCM", "Field doc comment missing", this.location()); if (modifiers.isDefault) throw this.compileException("Modifier \"default\" not allowed for fields"); FieldDeclaration fd = new FieldDeclaration( location, // location optionalDocComment, // optionalDocComment modifiers.add(Mod.PUBLIC | Mod.STATIC | Mod.FINAL), // modifiers memberType, // type this.parseFieldDeclarationRest(memberName) // variableDeclarators ); interfaceDeclaration.addConstantDeclaration(fd); } } /** *
     *   ConstructorDeclarator :=
     *     Identifier
     *     FormalParameters
     *     [ 'throws' ReferenceTypeList ]
     *     '{'
     *       [ 'this' Arguments ';' | 'super' Arguments ';' | Primary '.' 'super' Arguments ';' ]
     *       BlockStatements
     *     '}'
     * 
*/ public ConstructorDeclarator parseConstructorDeclarator(@Nullable String optionalDocComment, Modifiers modifiers) throws CompileException, IOException { this.read(TokenType.IDENTIFIER); // Class name // Parse formal parameters. final FormalParameters formalParameters = this.parseFormalParameters(); // Parse "throws" clause. ReferenceType[] thrownExceptions; if (this.peekRead("throws")) { thrownExceptions = this.parseReferenceTypeList(); } else { thrownExceptions = new ReferenceType[0]; } // Parse constructor body. final Location location = this.location(); this.read("{"); // 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. ConstructorInvocation optionalConstructorInvocation = null; List statements = new ArrayList(); if ( this.peek( "this", "super", "new", "void", // SUPPRESS CHECKSTYLE Wrap:1 "byte", "char", "short", "int", "long", "float", "double", "boolean" ) != -1 || this.peekLiteral() || this.peek(TokenType.IDENTIFIER) ) { Atom a = this.parseExpression(); if (a instanceof ConstructorInvocation) { this.read(";"); optionalConstructorInvocation = (ConstructorInvocation) a; } else { Statement s; if (this.peek(TokenType.IDENTIFIER)) { Type variableType = a.toTypeOrCompileException(); s = new LocalVariableDeclarationStatement( a.getLocation(), // location new Java.Modifiers(Mod.NONE), // modifiers variableType, // type this.parseVariableDeclarators() // variableDeclarators ); this.read(";"); } else { s = new ExpressionStatement(a.toRvalueOrCompileException()); this.read(";"); } statements.add(s); } } statements.addAll(this.parseBlockStatements()); this.read("}"); return new ConstructorDeclarator( location, // location optionalDocComment, // optionalDocComment modifiers, // modifiers formalParameters, // formalParameters thrownExceptions, // thrownExceptions optionalConstructorInvocation, // optionalConstructorInvocationStatement statements // statements ); } /** *
     *   MethodDeclaration :=
     *     [ DocComment ] Modifiers [ TypeParameters ] VoidOrType Identifier MethodDeclarationRest
     * 
* * @param allowDefaultClause Whether a "default clause" for an "annotation type element" (JLS8 9.6.2) should be * parsed */ public MethodDeclarator parseMethodDeclaration(boolean allowDefaultClause) throws CompileException, IOException { return this.parseMethodDeclarationRest( this.doc(), // optionalDocComment this.parseModifiers(), // modifiers this.parseTypeParametersOpt(), // optionalTypeParameters this.parseVoidOrType(), // type this.read(TokenType.IDENTIFIER), // name allowDefaultClause // allowDefaultClause ); } /** *
     *   VoidOrType := 'void' | Type
     * 
*/ public Type parseVoidOrType() throws CompileException, IOException { return this.peekRead("void") ? new PrimitiveType(this.location(), Primitive.VOID) : this.parseType(); } /** *
     *   MethodDeclarationRest :=
     *     FormalParameters
     *     { '[' ']' }
     *     [ 'throws' ReferenceTypeList ]
     *     [ 'default' expression ]
     *     ( ';' | MethodBody )
     * 
* * @param allowDefaultClause Whether a "default clause" for an "annotation type element" (JLS8 9.6.2) should be * parsed */ public MethodDeclarator parseMethodDeclarationRest( @Nullable String optionalDocComment, Modifiers modifiers, @Nullable TypeParameter[] optionalTypeParameters, Type type, String name, boolean allowDefaultClause ) throws CompileException, IOException { Location location = this.location(); this.verifyIdentifierIsConventionalMethodName(name, location); final FormalParameters formalParameters = this.parseFormalParameters(); for (int i = this.parseBracketsOpt(); i > 0; --i) type = new ArrayType(type); ReferenceType[] thrownExceptions; if (this.peekRead("throws")) { thrownExceptions = this.parseReferenceTypeList(); } else { thrownExceptions = new ReferenceType[0]; } ElementValue defaultValue = ( allowDefaultClause && this.peekRead("default") ? this.parseElementValue() : null ); List optionalStatements; if (this.peekRead(";")) { optionalStatements = null; } else { if (Mod.isAbstract(modifiers.accessFlags) || Mod.isNative(modifiers.accessFlags)) { throw this.compileException("Abstract or native method must not have a body"); } this.read("{"); optionalStatements = this.parseBlockStatements(); this.read("}"); } return new MethodDeclarator( location, // location optionalDocComment, // optionalDocComment modifiers, // modifiers optionalTypeParameters, // optionalTypeParameters type, // type name, // name formalParameters, // formalParameters thrownExceptions, // thrownExceptions defaultValue, // defaultValue optionalStatements // optionalStatements ); } /** *
     *   VariableInitializer :=
     *     ArrayInitializer |
     *     Expression
     * 
*/ public ArrayInitializerOrRvalue parseVariableInitializer() throws CompileException, IOException { if (this.peek("{")) { return this.parseArrayInitializer(); } else { return this.parseExpression().toRvalueOrCompileException(); } } /** *
     *   ArrayInitializer :=
     *     '{' [ VariableInitializer { ',' VariableInitializer } [ ',' ] '}'
     * 
*/ public ArrayInitializer parseArrayInitializer() throws CompileException, IOException { final Location location = this.location(); this.read("{"); List l = new ArrayList(); while (!this.peekRead("}")) { l.add(this.parseVariableInitializer()); if (this.peekRead("}")) break; this.read(","); } return new ArrayInitializer( location, (ArrayInitializerOrRvalue[]) l.toArray(new ArrayInitializerOrRvalue[l.size()]) ); } /** *
     *   FormalParameters := '(' [ FormalParameter { ',' FormalParameter } ] ')'
     * 
*/ public FormalParameters parseFormalParameters() throws CompileException, IOException { this.read("("); if (this.peekRead(")")) return new FormalParameters(this.location()); List l = new ArrayList(); final boolean[] hasEllipsis = new boolean[1]; do { if (hasEllipsis[0]) throw this.compileException("Only the last parameter may have an ellipsis"); l.add(this.parseFormalParameter(hasEllipsis)); } while (this.read(",", ")") == 0); return new FormalParameters( this.location(), // location (FormalParameter[]) l.toArray(new FormalParameter[l.size()]), // parameters hasEllipsis[0] // variableArity ); } /** *
     *   FormalParameter := [ 'final' ] Type [ '.' '.' '.' ] Identifier BracketsOpt
     * 
*/ public FormalParameter parseFormalParameter(boolean[] hasEllipsis) throws CompileException, IOException { Modifiers modifiers = this.parseModifiers(); if (modifiers.isDefault) throw this.compileException("Modifier \"default\" not allowed on formal parameters"); if ((modifiers.accessFlags & ~Mod.FINAL) != 0) { this.warning( "OFAAAOPD", "Only \"final\" and annotations allowed on parameter declaration", this.location() ); } final boolean finaL = (modifiers.accessFlags & Mod.FINAL) != 0; // Ignore annotations. Type type = this.parseType(); // SUPPRESS CHECKSTYLE UsageDistance if (this.peekRead(".")) { this.read("."); this.read("."); hasEllipsis[0] = true; } Location location = this.location(); String name = this.read(TokenType.IDENTIFIER); this.verifyIdentifierIsConventionalLocalVariableOrParameterName(name, location); for (int i = this.parseBracketsOpt(); i > 0; --i) type = new ArrayType(type); return new FormalParameter(location, finaL, type, name); } /** *
     *   BracketsOpt := { '[' ']' }
     * 
*/ int parseBracketsOpt() throws CompileException, IOException { int res = 0; while (this.peek("[") && this.peekNextButOne("]")) { this.read(); this.read(); ++res; } return res; } /** *
     *   MethodBody := Block
     * 
*/ public Block parseMethodBody() throws CompileException, IOException { return this.parseBlock(); } /** *
     *   Block := '{' BlockStatements '}'
     * 
*/ public Block parseBlock() throws CompileException, IOException { Block block = new Block(this.location()); this.read("{"); block.addStatements(this.parseBlockStatements()); this.read("}"); return block; } /** *
     *   BlockStatements := { BlockStatement }
     * 
*/ public List parseBlockStatements() throws CompileException, IOException { List l = new ArrayList(); while (!this.peek("}") && !this.peek("case") && !this.peek("default")) l.add(this.parseBlockStatement()); return l; } /** *
     *   BlockStatement :=
     *     Statement |                                     (1)
     *     'class' ... |                                   (2)
     *     Modifiers Type VariableDeclarators ';' |
     *     Expression ';' |
     *     Expression BracketsOpt VariableDeclarators ';'  (3)
     * 
* *

* (1) Includes the "labeled statement". *

*

* (2) Local class declaration. *

*

* (3) Local variable declaration statement; "Expression" must pose a type, and has optional trailing brackets. *

*/ public BlockStatement parseBlockStatement() throws CompileException, IOException { // Statement? if ( (this.peek(TokenType.IDENTIFIER) && this.peekNextButOne(":")) || this.peek( "if", "for", "while", "do", "try", "switch", "synchronized", // SUPPRESS CHECKSTYLE Wrap|LineLength:1 "return", "throw", "break", "continue", "assert" ) != -1 || this.peek("{", ";") != -1 ) return this.parseStatement(); // Local class declaration? if (this.peekRead("class")) { // JAVADOC[TM] ignores doc comments for local classes, but we // don't... String optionalDocComment = this.doc(); if (optionalDocComment == null) this.warning("LCDCM", "Local class doc comment missing", this.location()); final LocalClassDeclaration lcd = (LocalClassDeclaration) this.parseClassDeclarationRest( optionalDocComment, // optionalDocComment new Modifiers(), // modifiers ClassDeclarationContext.BLOCK // context ); return new LocalClassDeclarationStatement(lcd); } // Modifiers Type VariableDeclarators ';' if (this.peek("final", "@") != -1) { LocalVariableDeclarationStatement lvds = new LocalVariableDeclarationStatement( this.location(), // location this.parseModifiers(), // modifiers this.parseType(), // type this.parseVariableDeclarators() // variableDeclarators ); this.read(";"); return lvds; } // It's either a non-final local variable declaration or an expression statement. We can // only tell after parsing an expression. Atom a = this.parseExpression(); // Expression ';' if (this.peekRead(";")) { return new ExpressionStatement(a.toRvalueOrCompileException()); } // Expression BracketsOpt VariableDeclarators ';' Type variableType = a.toTypeOrCompileException(); for (int i = this.parseBracketsOpt(); i > 0; --i) variableType = new ArrayType(variableType); LocalVariableDeclarationStatement lvds = new LocalVariableDeclarationStatement( a.getLocation(), // location new Java.Modifiers(Mod.NONE), // modifiers variableType, // type this.parseVariableDeclarators() // variableDeclarators ); this.read(";"); return lvds; } /** *
     *   VariableDeclarators := VariableDeclarator { ',' VariableDeclarator }
     * 
*/ public VariableDeclarator[] parseVariableDeclarators() throws CompileException, IOException { List l = new ArrayList(); do { VariableDeclarator vd = this.parseVariableDeclarator(); this.verifyIdentifierIsConventionalLocalVariableOrParameterName(vd.name, vd.getLocation()); l.add(vd); } while (this.peekRead(",")); return (VariableDeclarator[]) l.toArray(new VariableDeclarator[l.size()]); } /** *
     *   FieldDeclarationRest :=
     *     VariableDeclaratorRest
     *     { ',' VariableDeclarator }
     * 
*/ public VariableDeclarator[] parseFieldDeclarationRest(String name) throws CompileException, IOException { List l = new ArrayList(); VariableDeclarator vd = this.parseVariableDeclaratorRest(name); this.verifyIdentifierIsConventionalFieldName(vd.name, vd.getLocation()); l.add(vd); while (this.peekRead(",")) { vd = this.parseVariableDeclarator(); this.verifyIdentifierIsConventionalFieldName(vd.name, vd.getLocation()); l.add(vd); } return (VariableDeclarator[]) l.toArray(new VariableDeclarator[l.size()]); } /** *
     *   VariableDeclarator := Identifier VariableDeclaratorRest
     * 
*/ public VariableDeclarator parseVariableDeclarator() throws CompileException, IOException { return this.parseVariableDeclaratorRest(this.read(TokenType.IDENTIFIER)); } /** *
     *   VariableDeclaratorRest := { '[' ']' } [ '=' VariableInitializer ]
     * 
*

* Used by field declarations and local variable declarations. *

*/ public VariableDeclarator parseVariableDeclaratorRest(String name) throws CompileException, IOException { Location loc = this.location(); int brackets = this.parseBracketsOpt(); ArrayInitializerOrRvalue initializer = null; if (this.peekRead("=")) initializer = this.parseVariableInitializer(); return new VariableDeclarator(loc, name, brackets, initializer); } /** *
     *   Statement :=
     *     LabeledStatement |
     *     Block |
     *     IfStatement |
     *     ForStatement |
     *     WhileStatement |
     *     DoStatement |
     *     TryStatement |
     *     'switch' ... |
     *     'synchronized' ... |
     *     ReturnStatement |
     *     ThrowStatement |
     *     BreakStatement |
     *     ContinueStatement |
     *     EmptyStatement |
     *     ExpressionStatement
     * 
*/ public Statement parseStatement() throws CompileException, IOException { if (this.peek(TokenType.IDENTIFIER) && this.peekNextButOne(":")) { return this.parseLabeledStatement(); } Statement stmt = ( this.peek("{") ? this.parseBlock() : this.peek("if") ? this.parseIfStatement() : this.peek("for") ? this.parseForStatement() : this.peek("while") ? this.parseWhileStatement() : this.peek("do") ? this.parseDoStatement() : this.peek("try") ? this.parseTryStatement() : this.peek("switch") ? this.parseSwitchStatement() : this.peek("synchronized") ? this.parseSynchronizedStatement() : this.peek("return") ? this.parseReturnStatement() : this.peek("throw") ? this.parseThrowStatement() : this.peek("break") ? this.parseBreakStatement() : this.peek("continue") ? this.parseContinueStatement() : this.peek("assert") ? this.parseAssertStatement() : this.peek(";") ? this.parseEmptyStatement() : this.parseExpressionStatement() ); return stmt; } /** *
     *   LabeledStatement := Identifier ':' Statement
     * 
*/ public Statement parseLabeledStatement() throws CompileException, IOException { String label = this.read(TokenType.IDENTIFIER); this.read(":"); return new LabeledStatement( this.location(), // location label, // label this.parseStatement() // body ); } /** *
     *   IfStatement := 'if' '(' Expression ')' Statement [ 'else' Statement ]
     * 
*/ public Statement parseIfStatement() throws CompileException, IOException { final Location location = this.location(); this.read("if"); this.read("("); final Rvalue condition = this.parseExpression().toRvalueOrCompileException(); this.read(")"); Statement thenStatement = this.parseStatement(); Statement elseStatement = this.peekRead("else") ? this.parseStatement() : null; return new IfStatement( location, // location condition, // condition thenStatement, // thenStatement elseStatement // optionalElseStatement ); } /** *
     *   ForStatement :=
     *     'for' '(' [ ForInit ] ';' [ Expression ] ';' [ ExpressionList ] ')' Statement
     *     | 'for' '(' FormalParameter ':' Expression ')' Statement
     *
     *   ForInit :=
     *     Modifiers Type VariableDeclarators
     *     | ModifiersOpt PrimitiveType VariableDeclarators
     *     | Expression VariableDeclarators              (1)
     *     | Expression { ',' Expression }
     * 
*

* (1) "Expression" must pose a type. *

*/ public Statement parseForStatement() throws CompileException, IOException { this.read("for"); Location forLocation = this.location(); this.read("("); BlockStatement optionalInit = null; INIT: if (!this.peek(";")) { // 'for' '(' Modifiers Type VariableDeclarators // 'for' '(' [ Modifiers ] PrimitiveType VariableDeclarators if (this.peek( "final", "@", "byte", "short", "char", "int", "long", "float", "double", "boolean" // SUPPRESS CHECKSTYLE LineLength|Wrap ) != -1) { Modifiers modifiers = this.parseModifiers(); Type type = this.parseType(); if (this.peek(TokenType.IDENTIFIER) && this.peekNextButOne(":")) { // 'for' '(' [ Modifiers ] Type identifier ':' Expression ')' Statement final String name = this.read(TokenType.IDENTIFIER); final Location nameLocation = this.location(); this.read(":"); Rvalue expression = this.parseExpression().toRvalueOrCompileException(); this.read(")"); return new ForEachStatement( forLocation, // location new FormalParameter( // currentElement nameLocation, Mod.isFinal(modifiers.accessFlags), type, name ), expression, // expression this.parseStatement() // body ); } // 'for' '(' [ Modifiers ] Type VariableDeclarators optionalInit = new LocalVariableDeclarationStatement( this.location(), // location modifiers, // modifiers type, // type this.parseVariableDeclarators() // variableDeclarators ); break INIT; } Atom a = this.parseExpression(); if (this.peek(TokenType.IDENTIFIER)) { if (this.peekNextButOne(":")) { // 'for' '(' Expression identifier ':' Expression ')' Statement final String name = this.read(TokenType.IDENTIFIER); final Location nameLocation = this.location(); this.read(":"); Rvalue expression = this.parseExpression().toRvalueOrCompileException(); this.read(")"); return new ForEachStatement( forLocation, // location new FormalParameter( // currentElement nameLocation, false, a.toTypeOrCompileException(), name ), expression, // expression this.parseStatement() // body ); } // 'for' '(' Expression VariableDeclarators optionalInit = new LocalVariableDeclarationStatement( this.location(), // location new Java.Modifiers(Mod.NONE), // modifiers a.toTypeOrCompileException(), // type this.parseVariableDeclarators() // variableDeclarators ); break INIT; } if (!this.peekRead(",")) { // 'for' '(' Expression optionalInit = new ExpressionStatement(a.toRvalueOrCompileException()); break INIT; } // 'for' '(' Expression { ',' Expression } { List l = new ArrayList(); l.add(new ExpressionStatement(a.toRvalueOrCompileException())); do { l.add(new ExpressionStatement(this.parseExpression().toRvalueOrCompileException())); } while (this.peekRead(",")); Block b = new Block(a.getLocation()); b.addStatements(l); optionalInit = b; } } this.read(";"); Rvalue optionalCondition = null; if (!this.peek(";")) optionalCondition = this.parseExpression().toRvalueOrCompileException(); this.read(";"); Rvalue[] optionalUpdate = null; if (!this.peek(")")) optionalUpdate = this.parseExpressionList(); this.read(")"); return new ForStatement( forLocation, // location optionalInit, // optionalInit optionalCondition, // optionalCondition optionalUpdate, // optionalUpdate this.parseStatement() // body ); } /** *
     *   WhileStatement := 'while' '(' Expression ')' Statement
     * 
*/ public Statement parseWhileStatement() throws CompileException, IOException { final Location location = this.location(); this.read("while"); this.read("("); Rvalue condition = this.parseExpression().toRvalueOrCompileException(); this.read(")"); return new WhileStatement( location, // location condition, // condition this.parseStatement() // body ); } /** *
     *   DoStatement := 'do' Statement 'while' '(' Expression ')' ';'
     * 
*/ public Statement parseDoStatement() throws CompileException, IOException { final Location location = this.location(); this.read("do"); final Statement body = this.parseStatement(); this.read("while"); this.read("("); final Rvalue condition = this.parseExpression().toRvalueOrCompileException(); this.read(")"); this.read(";"); return new DoStatement( location, // location body, // body condition // condition ); } /** *
     *   TryStatement :=
     *     'try' Block Catches [ Finally ] |
     *     'try' Block Finally
     *
     *   Catches := CatchClause { CatchClause }
     *
     *   CatchClause := 'catch' '(' FormalParameter ')' Block
     *
     *   Finally := 'finally' Block
     * 
*/ public Statement parseTryStatement() throws CompileException, IOException { final Location location = this.location(); this.read("try"); // '(' Resource { ';' Resource } [ ';' ] ')' List resources = new ArrayList(); if (this.peekRead("(")) { resources.add(this.parseResource()); RESOURCES: for (;;) { switch (this.read(";", ")")) { case 0: if (this.peekRead(")")) break RESOURCES; resources.add(this.parseResource()); break; case 1: break RESOURCES; default: throw new AssertionError(); } } } final Block body = this.parseBlock(); // { CatchClause } List ccs = new ArrayList(); while (this.peekRead("catch")) { final Location loc = this.location(); this.read("("); boolean[] hasEllipsis = new boolean[1]; final FormalParameter caughtException = this.parseFormalParameter(hasEllipsis); if (hasEllipsis[0]) throw this.compileException("Catch clause parameter must not have an ellipsis"); this.read(")"); ccs.add(new CatchClause( loc, // location caughtException, // caughtException this.parseBlock() // body )); } // [ 'finally' block ] Block finallY = this.peekRead("finally") ? this.parseBlock() : null; if (resources.isEmpty() && ccs.isEmpty() && finallY == null) { throw this.compileException( "\"try\" statement must have at least one resource, \"catch\" clause or \"finally\" clause" ); } return new TryStatement( location, // location resources, // resources body, // body ccs, // catchClauses finallY // finallY ); } /** *
     * Resource :=
     *     Modifiers Type VariableDeclarator
     *     | VariableAccess
     * 
*/ private TryStatement.Resource parseResource() throws CompileException, IOException { Location loc = this.location(); Modifiers modifiers = this.parseModifiers(); Atom a = this.parseExpression(); if (modifiers != Parser.NO_MODIFIERS || this.peek(TokenType.IDENTIFIER)) { if (modifiers.isDefault) throw this.compileException("Modifier \"default\" not allowed on resource"); if ((modifiers.accessFlags & ~Mod.FINAL) != 0) { throw this.compileException("Only access flag FINAL allowed in this place"); } // Modifiers Type VariableDeclarator return new TryStatement.LocalVariableDeclaratorResource( loc, // location modifiers, // modifiers a.toTypeOrCompileException(), // type this.parseVariableDeclarator() // variableDeclarator ); } // VariableAccess Rvalue rv = a.toRvalueOrCompileException(); if (!(rv instanceof FieldAccess)) { this.compileException("Rvalue " + rv.getClass().getSimpleName() + " disallowed as a resource"); } return new TryStatement.VariableAccessResource(loc, rv); } /** *
     *   SwitchStatement :=
     *     'switch' '(' Expression ')' '{' { SwitchLabels BlockStatements } '}'
     *
     *   SwitchLabels := SwitchLabels { SwitchLabels }
     *
     *   SwitchLabel := 'case' Expression ':' | 'default' ':'
     * 
*/ public Statement parseSwitchStatement() throws CompileException, IOException { final Location location = this.location(); this.read("switch"); this.read("("); final Rvalue condition = this.parseExpression().toRvalueOrCompileException(); this.read(")"); this.read("{"); List sbsgs = new ArrayList(); while (!this.peekRead("}")) { Location location2 = this.location(); boolean hasDefaultLabel = false; List caseLabels = new ArrayList(); do { if (this.peekRead("case")) { caseLabels.add(this.parseExpression().toRvalueOrCompileException()); } else if (this.peekRead("default")) { if (hasDefaultLabel) throw this.compileException("Duplicate \"default\" label"); hasDefaultLabel = true; } else { throw this.compileException("\"case\" or \"default\" expected"); } this.read(":"); } while (this.peek("case", "default") != -1); SwitchStatement.SwitchBlockStatementGroup sbsg = new SwitchStatement.SwitchBlockStatementGroup( location2, // location caseLabels, // caseLabels hasDefaultLabel, // hasDefaultLabel this.parseBlockStatements() // blockStatements ); sbsgs.add(sbsg); } return new SwitchStatement( location, // location condition, // condition sbsgs // sbsgs ); } /** *
     *   SynchronizedStatement :=
     *     'synchronized' '(' expression ')' Block
     * 
*/ public Statement parseSynchronizedStatement() throws CompileException, IOException { final Location location = this.location(); this.read("synchronized"); this.read("("); Rvalue expression = this.parseExpression().toRvalueOrCompileException(); this.read(")"); return new SynchronizedStatement( location, // location expression, // expression this.parseBlock() // body ); } /** *
     *   ReturnStatement := 'return' [ Expression ] ';'
     * 
*/ public Statement parseReturnStatement() throws CompileException, IOException { final Location location = this.location(); this.read("return"); Rvalue returnValue = this.peek(";") ? null : this.parseExpression().toRvalueOrCompileException(); this.read(";"); return new ReturnStatement(location, returnValue); } /** *
     *   ThrowStatement := 'throw' Expression ';'
     * 
*/ public Statement parseThrowStatement() throws CompileException, IOException { final Location location = this.location(); this.read("throw"); final Rvalue expression = this.parseExpression().toRvalueOrCompileException(); this.read(";"); return new ThrowStatement(location, expression); } /** *
     *   BreakStatement := 'break' [ Identifier ] ';'
     * 
*/ public Statement parseBreakStatement() throws CompileException, IOException { final Location location = this.location(); this.read("break"); String optionalLabel = null; if (this.peek(TokenType.IDENTIFIER)) optionalLabel = this.read(TokenType.IDENTIFIER); this.read(";"); return new BreakStatement(location, optionalLabel); } /** *
     *   ContinueStatement := 'continue' [ Identifier ] ';'
     * 
*/ public Statement parseContinueStatement() throws CompileException, IOException { final Location location = this.location(); this.read("continue"); String optionalLabel = null; if (this.peek(TokenType.IDENTIFIER)) optionalLabel = this.read(TokenType.IDENTIFIER); this.read(";"); return new ContinueStatement(location, optionalLabel); } /** *
     *   AssertStatement := 'assert' Expression [ ':' Expression ] ';'
     * 
*/ public Statement parseAssertStatement() throws CompileException, IOException { this.read("assert"); Location loc = this.location(); Rvalue expression1 = this.parseExpression().toRvalueOrCompileException(); Rvalue optionalExpression2 = this.peekRead(":") ? this.parseExpression().toRvalueOrCompileException() : null; this.read(";"); return new AssertStatement(loc, expression1, optionalExpression2); } /** *
     *   EmptyStatement := ';'
     * 
*/ public Statement parseEmptyStatement() throws CompileException, IOException { Location location = this.location(); this.read(";"); return new EmptyStatement(location); } /** *
     *   ExpressionList := Expression { ',' Expression }
     * 
*/ public Rvalue[] parseExpressionList() throws CompileException, IOException { List l = new ArrayList(); do { l.add(this.parseExpression().toRvalueOrCompileException()); } while (this.peekRead(",")); return (Rvalue[]) l.toArray(new Rvalue[l.size()]); } /** *
     *   Type := (
     *     'byte' | 'short' | 'char' | 'int' | 'long' |
     *     'float' | 'double' | 'boolean' |
     *     ReferenceType
     *   ) { '[' ']' }
     * 
*/ public Type parseType() throws CompileException, IOException { Type res; switch (this.peekRead( "byte", "short", "char", "int", "long", "float", "double", "boolean" // SUPPRESS CHECKSTYLE Wrap|LineLength )) { case 0: res = new PrimitiveType(this.location(), Primitive.BYTE); break; case 1: res = new PrimitiveType(this.location(), Primitive.SHORT); break; case 2: res = new PrimitiveType(this.location(), Primitive.CHAR); break; case 3: res = new PrimitiveType(this.location(), Primitive.INT); break; case 4: res = new PrimitiveType(this.location(), Primitive.LONG); break; case 5: res = new PrimitiveType(this.location(), Primitive.FLOAT); break; case 6: res = new PrimitiveType(this.location(), Primitive.DOUBLE); break; case 7: res = new PrimitiveType(this.location(), Primitive.BOOLEAN); break; case -1: res = this.parseReferenceType(); break; default: throw new AssertionError(); } for (int i = this.parseBracketsOpt(); i > 0; --i) res = new ArrayType(res); return res; } /** *
     *   ReferenceType := QualifiedIdentifier [ TypeArguments ]
     * 
*/ public ReferenceType parseReferenceType() throws CompileException, IOException { return new ReferenceType(this.location(), this.parseQualifiedIdentifier(), this.parseTypeArgumentsOpt()); } /** *
     *   TypeParameters := '<' TypeParameter { ',' TypeParameter } '>'
     * 
*/ @Nullable private TypeParameter[] parseTypeParametersOpt() throws CompileException, IOException { if (!this.peekRead("<")) return null; List l = new ArrayList(); l.add(this.parseTypeParameter()); while (this.read(",", ">") == 0) { l.add(this.parseTypeParameter()); } return (TypeParameter[]) l.toArray(new TypeParameter[l.size()]); } /** *
     *   TypeParameter := identifier [ 'extends' ( identifier | ReferenceType { '&' ReferenceType }
     * 
*/ private TypeParameter parseTypeParameter() throws CompileException, IOException { String name = this.read(TokenType.IDENTIFIER); if (this.peekRead("extends")) { List bound = new ArrayList(); bound.add(this.parseReferenceType()); while (this.peekRead("&")) this.parseReferenceType(); return new TypeParameter(name, (ReferenceType[]) bound.toArray(new ReferenceType[bound.size()])); } return new TypeParameter(name, null); } /** *
     *   TypeArguments := '<' TypeArgument { ',' TypeArgument } '>'
     * 
* * @return {@code null} iff there are no type arguments */ @Nullable private TypeArgument[] parseTypeArgumentsOpt() throws CompileException, IOException { if (!this.peekRead("<")) return null; List typeArguments = new ArrayList(); typeArguments.add(this.parseTypeArgument()); while (this.read(">", ",") == 1) { typeArguments.add(this.parseTypeArgument()); } return (TypeArgument[]) typeArguments.toArray(new TypeArgument[typeArguments.size()]); } /** *
     *   TypeArgument :=
     *     ReferenceType { '[' ']' }    <= The optional brackets are missing in JLS7, section 18!?
     *     | PrimitiveType '[' ']' { '[' ']' }
     *     | '?' extends ReferenceType
     *     | '?' super ReferenceType
     * 
*/ private TypeArgument parseTypeArgument() throws CompileException, IOException { if (this.peekRead("?")) { return ( this.peekRead("extends") ? new Wildcard(Wildcard.BOUNDS_EXTENDS, this.parseReferenceType()) : this.peekRead("super") ? new Wildcard(Wildcard.BOUNDS_SUPER, this.parseReferenceType()) : new Wildcard() ); } Type t = this.parseType(); int i = this.parseBracketsOpt(); for (; i > 0; i--) t = new ArrayType(t); if (!(t instanceof TypeArgument)) throw this.compileException("'" + t + "' is not a valid type argument"); return (TypeArgument) t; } /** *
     *   ReferenceTypeList := ReferenceType { ',' ReferenceType }
     * 
*/ public ReferenceType[] parseReferenceTypeList() throws CompileException, IOException { List l = new ArrayList(); l.add(this.parseReferenceType()); while (this.peekRead(",")) { l.add(this.parseReferenceType()); } return (ReferenceType[]) l.toArray(new ReferenceType[l.size()]); } /** *
     *   Expression := AssignmentExpression
     * 
*/ public Atom parseExpression() throws CompileException, IOException { return this.parseAssignmentExpression(); } /** *
     *   AssignmentExpression :=
     *     ConditionalExpression [ AssignmentOperator AssignmentExpression ]
     *
     *   AssignmentOperator :=
     *     '=' | '*=' | '/=' | '%=' | '+=' | '-=' | '<<=' |
     *     '>>=' | '>>>=' | '&=' | '^=' | '|='
     * 
*/ public Atom parseAssignmentExpression() throws CompileException, IOException { Atom a = this.parseConditionalExpression(); if (this.peek( "=", "+=", "-=", "*=", "/=", "&=", "|=", "^=", "%=", "<<=", ">>=", ">>>=" // SUPPRESS CHECKSTYLE Wrap|LineLength ) != -1) { final Lvalue lhs = a.toLvalueOrCompileException(); Location location = this.location(); String operator = this.read(TokenType.OPERATOR); // An interned string! final Rvalue rhs = this.parseAssignmentExpression().toRvalueOrCompileException(); return new Assignment(location, lhs, operator, rhs); } return a; } /** *
     *   ConditionalExpression :=
     *     ConditionalOrExpression [ '?' Expression ':' ConditionalExpression ]
     * 
*/ public Atom parseConditionalExpression() throws CompileException, IOException { Atom a = this.parseConditionalOrExpression(); if (!this.peekRead("?")) return a; Location location = this.location(); Rvalue lhs = a.toRvalueOrCompileException(); Rvalue mhs = this.parseExpression().toRvalueOrCompileException(); this.read(":"); Rvalue rhs = this.parseConditionalExpression().toRvalueOrCompileException(); return new ConditionalExpression(location, lhs, mhs, rhs); } /** *
     *   ConditionalOrExpression :=
     *     ConditionalAndExpression { '||' ConditionalAndExpression ]
     * 
*/ public Atom parseConditionalOrExpression() throws CompileException, IOException { Atom a = this.parseConditionalAndExpression(); while (this.peekRead("||")) { Location location = this.location(); a = new BinaryOperation( location, a.toRvalueOrCompileException(), "||", this.parseConditionalAndExpression().toRvalueOrCompileException() ); } return a; } /** *
     *   ConditionalAndExpression :=
     *     InclusiveOrExpression { '&&' InclusiveOrExpression }
     * 
*/ public Atom parseConditionalAndExpression() throws CompileException, IOException { Atom a = this.parseInclusiveOrExpression(); while (this.peekRead("&&")) { Location location = this.location(); a = new BinaryOperation( location, a.toRvalueOrCompileException(), "&&", this.parseInclusiveOrExpression().toRvalueOrCompileException() ); } return a; } /** *
     *   InclusiveOrExpression :=
     *     ExclusiveOrExpression { '|' ExclusiveOrExpression }
     * 
*/ public Atom parseInclusiveOrExpression() throws CompileException, IOException { Atom a = this.parseExclusiveOrExpression(); while (this.peekRead("|")) { Location location = this.location(); a = new BinaryOperation( location, a.toRvalueOrCompileException(), "|", this.parseExclusiveOrExpression().toRvalueOrCompileException() ); } return a; } /** *
     *   ExclusiveOrExpression :=
     *     AndExpression { '^' AndExpression }
     * 
*/ public Atom parseExclusiveOrExpression() throws CompileException, IOException { Atom a = this.parseAndExpression(); while (this.peekRead("^")) { Location location = this.location(); a = new BinaryOperation( location, a.toRvalueOrCompileException(), "^", this.parseAndExpression().toRvalueOrCompileException() ); } return a; } /** *
     *   AndExpression :=
     *     EqualityExpression { '&' EqualityExpression }
     * 
*/ public Atom parseAndExpression() throws CompileException, IOException { Atom a = this.parseEqualityExpression(); while (this.peekRead("&")) { Location location = this.location(); a = new BinaryOperation( location, a.toRvalueOrCompileException(), "&", this.parseEqualityExpression().toRvalueOrCompileException() ); } return a; } /** *
     *   EqualityExpression :=
     *     RelationalExpression { ( '==' | '!=' ) RelationalExpression }
     * 
*/ public Atom parseEqualityExpression() throws CompileException, IOException { Atom a = this.parseRelationalExpression(); while (this.peek("==", "!=") != -1) { a = new BinaryOperation( this.location(), // location a.toRvalueOrCompileException(), // lhs this.read().value, // operator this.parseRelationalExpression().toRvalueOrCompileException() // rhs ); } return a; } /** *
     *   RelationalExpression :=
     *     ShiftExpression {
     *       'instanceof' ReferenceType
     *       | '<' ShiftExpression [ { ',' TypeArgument } '>' ]
     *       | '<' TypeArgument [ { ',' TypeArgument } '>' ]
     *       | ( '>' | '<=' | '>=' ) ShiftExpression
     *     }
     * 
*/ public Atom parseRelationalExpression() throws CompileException, IOException { Atom a = this.parseShiftExpression(); for (;;) { if (this.peekRead("instanceof")) { Location location = this.location(); a = new Instanceof( location, a.toRvalueOrCompileException(), this.parseType() ); } else if (this.peek("<", ">", "<=", ">=") != -1) { // ambiguous-name '<' '?' ... if (a instanceof Java.AmbiguousName && this.peek("<") && this.peekNextButOne("?")) { return new ReferenceType( this.location(), ((Java.AmbiguousName) a).identifiers, this.parseTypeArgumentsOpt() ); } String operator = this.read().value; Atom rhs = this.parseShiftExpression(); if ("<".equals(operator) && a instanceof Java.AmbiguousName) { if (this.peek("<", ">", ",") != -1) { final String[] identifiers = ((Java.AmbiguousName) a).identifiers; // '<' ShiftExpression [ TypeArguments ] ( '<' | '>' | ',' ) this.parseTypeArgumentsOpt(); Type t = rhs.toTypeOrCompileException(); TypeArgument ta; if (t instanceof ArrayType) { ta = (ArrayType) t; } else if (t instanceof ReferenceType) { ta = (ReferenceType) t; } else { throw this.compileException("'" + t + "' is not a valid type argument"); } List typeArguments = new ArrayList(); typeArguments.add(ta); while (this.read(">", ",") == 1) { typeArguments.add(this.parseTypeArgument()); } return new ReferenceType( this.location(), identifiers, (TypeArgument[]) typeArguments.toArray(new TypeArgument[typeArguments.size()]) ); } } a = new BinaryOperation( this.location(), // location a.toRvalueOrCompileException(), // lhs operator, // operator rhs.toRvalueOrCompileException() // rhs ); } else { return a; } } } /** *
     *   ShiftExpression :=
     *     AdditiveExpression { ( '<<' | '>>' | '>>>' ) AdditiveExpression }
     * 
*/ public Atom parseShiftExpression() throws CompileException, IOException { Atom a = this.parseAdditiveExpression(); while (this.peek("<<", ">>", ">>>") != -1) { a = new BinaryOperation( this.location(), // location a.toRvalueOrCompileException(), // lhs this.read().value, // operator this.parseAdditiveExpression().toRvalueOrCompileException() // rhs ); } return a; } /** *
     *   AdditiveExpression :=
     *     MultiplicativeExpression { ( '+' | '-' ) MultiplicativeExpression }
     * 
*/ public Atom parseAdditiveExpression() throws CompileException, IOException { Atom a = this.parseMultiplicativeExpression(); while (this.peek("+", "-") != -1) { a = new BinaryOperation( this.location(), // location a.toRvalueOrCompileException(), // lhs this.read().value, // operator this.parseMultiplicativeExpression().toRvalueOrCompileException() // rhs ); } return a; } /** *
     *   MultiplicativeExpression :=
     *     UnaryExpression { ( '*' | '/' | '%' ) UnaryExpression }
     * 
*/ public Atom parseMultiplicativeExpression() throws CompileException, IOException { Atom a = this.parseUnaryExpression(); while (this.peek("*", "/", "%") != -1) { a = new BinaryOperation( this.location(), // location a.toRvalueOrCompileException(), // lhs this.read().value, // operator this.parseUnaryExpression().toRvalueOrCompileException() // rhs ); } return a; } /** *
     *   UnaryExpression :=
     *     { PrefixOperator } Primary { Selector } { PostfixOperator }
     *
     *   PrefixOperator := '++' | '--' | '+' | '-' | '~' | '!'
     *
     *   PostfixOperator := '++' | '--'
     * 
*/ public Atom parseUnaryExpression() throws CompileException, IOException { if (this.peek("++", "--") != -1) { return new Crement( this.location(), // location this.read().value, // operator this.parseUnaryExpression().toLvalueOrCompileException() // operand ); } if (this.peek("+", "-", "~", "!") != -1) { return new UnaryOperation( this.location(), // location this.read().value, // operator this.parseUnaryExpression().toRvalueOrCompileException() // operand ); } Atom a = this.parsePrimary(); while (this.peek(".", "[") != -1) { a = this.parseSelector(a); } while (this.peek("++", "--") != -1) { a = new Crement( this.location(), // location a.toLvalueOrCompileException(), // operand this.read().value // operator ); } return a; } /** *
     *   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
     *     PrimitiveType { '[]' } |                // Type
     *     PrimitiveType { '[]' } '.' '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
     * 
*/ public Atom parsePrimary() throws CompileException, IOException { if (this.peekRead("(")) { if ( this.peek("boolean", "char", "byte", "short", "int", "long", "float", "double") != -1 ) { // '(' PrimitiveType { '[]' } ')' UnaryExpression Type type = this.parseType(); int brackets = this.parseBracketsOpt(); this.read(")"); for (int i = 0; i < brackets; ++i) type = new ArrayType(type); return new Cast( this.location(), // location type, // targetType this.parseUnaryExpression().toRvalueOrCompileException() // value ); } Atom a = this.parseExpression(); this.read(")"); if ( this.peekLiteral() || this.peek(TokenType.IDENTIFIER) || this.peek("(", "~", "!") != -1 || this.peek("this", "super", "new") != -1 ) { // '(' Expression ')' UnaryExpression return new Cast( this.location(), // location a.toTypeOrCompileException(), // targetType this.parseUnaryExpression().toRvalueOrCompileException() // value ); } // '(' Expression ')' return new ParenthesizedExpression(a.getLocation(), a.toRvalueOrCompileException()); } if (this.peekLiteral()) { // Literal return this.parseLiteral(); } if (this.peek(TokenType.IDENTIFIER)) { Location location = this.location(); String[] qi = this.parseQualifiedIdentifier(); if (this.peek("(")) { // Name Arguments return new MethodInvocation( this.location(), // location qi.length == 1 ? null : new AmbiguousName( // optionalTarget location, // location qi, // identifiers qi.length - 1 // n ), qi[qi.length - 1], // methodName this.parseArguments() // arguments ); } if (this.peek("[") && this.peekNextButOne("]")) { // Name '[]' { '[]' } // Name '[]' { '[]' } '.' 'class' Type res = new ReferenceType( location, // location qi, // identifiers null // optionalTypeArguments ); int brackets = this.parseBracketsOpt(); for (int i = 0; i < brackets; ++i) res = new ArrayType(res); if (this.peek(".") && this.peekNextButOne("class")) { this.read(); Location location2 = this.location(); this.read(); return new ClassLiteral(location2, res); } else { return res; } } // Name return new AmbiguousName( location, // location qi // identifiers ); } if (this.peekRead("this")) { Location location = this.location(); if (this.peek("(")) { // 'this' Arguments // Alternate constructor invocation (JLS7 8.8.7.1). return new AlternateConstructorInvocation( location, // location this.parseArguments() // arguments ); } else { // 'this' return new ThisReference(location); } } if (this.peekRead("super")) { if (this.peek("(")) { // 'super' Arguments // Unqualified superclass constructor invocation (JLS7 8.8.7.1). return new SuperConstructorInvocation( this.location(), // location (Rvalue) null, // optionalQualification this.parseArguments() // arguments ); } this.read("."); String name = this.read(TokenType.IDENTIFIER); if (this.peek("(")) { // 'super' '.' Identifier Arguments return new SuperclassMethodInvocation( this.location(), // location name, // methodName this.parseArguments() // arguments ); } else { // 'super' '.' Identifier return new SuperclassFieldAccessExpression( this.location(), // location (Type) null, // optionalQualification name // fieldName ); } } // 'new' if (this.peekRead("new")) { Location location = this.location(); Type type = this.parseType(); if (type instanceof ArrayType) { // 'new' ArrayType ArrayInitializer return new NewInitializedArray(location, (ArrayType) type, this.parseArrayInitializer()); } if (type instanceof ReferenceType && this.peek("(")) { // 'new' ReferenceType Arguments [ ClassBody ] Rvalue[] arguments = this.parseArguments(); if (this.peek("{")) { // 'new' ReferenceType Arguments ClassBody final AnonymousClassDeclaration anonymousClassDeclaration = new AnonymousClassDeclaration( this.location(), // location type // baseType ); this.parseClassBody(anonymousClassDeclaration); return new NewAnonymousClassInstance( location, // location (Rvalue) null, // optionalQualification anonymousClassDeclaration, // anonymousClassDeclaration arguments // arguments ); } else { // 'new' ReferenceType Arguments return new NewClassInstance( location, // location (Rvalue) null, // optionalQualification type, // type arguments // arguments ); } } // 'new' Type DimExprs { '[]' } return new NewArray( location, // location type, // type this.parseDimExprs(), // dimExprs this.parseBracketsOpt() // dims ); } // PrimitiveType if (this.peek("boolean", "char", "byte", "short", "int", "long", "float", "double") != -1) { Type res = this.parseType(); int brackets = this.parseBracketsOpt(); for (int i = 0; i < brackets; ++i) res = new ArrayType(res); if (this.peek(".") && this.peekNextButOne("class")) { // PrimitiveType { '[]' } '.' 'class' this.read(); Location location = this.location(); this.read(); return new ClassLiteral(location, res); } // PrimitiveType { '[]' } return res; } // 'void' if (this.peekRead("void")) { if (this.peek(".") && this.peekNextButOne("class")) { // 'void' '.' 'class' this.read(); Location location = this.location(); this.read(); return new ClassLiteral(location, new PrimitiveType(location, Primitive.VOID)); } throw this.compileException("\"void\" encountered in wrong context"); } throw this.compileException("Unexpected token \"" + this.peek().value + "\" in primary"); } /** *
     *   Selector :=
     *     '.' Identifier |                                // FieldAccess 15.11.1
     *     '.' Identifier Arguments |                      // MethodInvocation
     *     '.' '<' TypeList '>' 'super' Arguments                  // Superconstructor invocation (?)
     *     '.' '<' TypeList '>' 'super' '.' . Identifier           // ???
     *     '.' '<' TypeList '>' 'super' '.' . Identifier Arguments // Supermethod invocation
     *     '.' '<' TypeList '>' Identifier Arguments // ExplicitGenericInvocation
     *     '.' 'this'                                      // QualifiedThis 15.8.4
     *     '.' 'super' Arguments                           // Qualified superclass constructor invocation (JLS7 8.8.7.1)
     *     '.' 'super' '.' Identifier |                    // SuperclassFieldReference (JLS7 15.11.2)
     *     '.' 'super' '.' Identifier Arguments |          // SuperclassMethodInvocation (JLS7 15.12.3)
     *     '.' 'new' Identifier Arguments [ ClassBody ] |  // QualifiedClassInstanceCreationExpression  15.9
     *     '.' 'class'
     *     '[' Expression ']'                              // ArrayAccessExpression 15.13
     *
     *   ExplicitGenericInvocationSuffix :=
     *     'super' SuperSuffix
     *     | Identifier Arguments
     * 
*/ public Atom parseSelector(Atom atom) throws CompileException, IOException { if (this.peekRead(".")) { // Parse and ignore type arguments. this.parseTypeArgumentsOpt(); if (this.peek().type == TokenType.IDENTIFIER) { String identifier = this.read(TokenType.IDENTIFIER); if (this.peek("(")) { // '.' Identifier Arguments return new MethodInvocation( this.location(), // location atom.toRvalueOrCompileException(), // optionalTarget identifier, // methodName this.parseArguments() // arguments ); } // '.' Identifier return new FieldAccessExpression( this.location(), // location atom.toRvalueOrCompileException(), // lhs identifier // fieldName ); } if (this.peekRead("this")) { // '.' 'this' Location location = this.location(); return new QualifiedThisReference( location, // location atom.toTypeOrCompileException() // qualification ); } if (this.peekRead("super")) { Location location = this.location(); if (this.peek("(")) { // '.' 'super' Arguments // Qualified superclass constructor invocation (JLS7 8.7.1.2.2) (LHS is an Rvalue) return new SuperConstructorInvocation( location, // location atom.toRvalueOrCompileException(), // optionalQualification this.parseArguments() // arguments ); } this.read("."); String identifier = this.read(TokenType.IDENTIFIER); if (this.peek("(")) { // '.' 'super' '.' Identifier Arguments // Qualified superclass method invocation (JLS7 15.12.1.1.4) (LHS is a ClassName). // TODO: Qualified superclass method invocation throw this.compileException("Qualified superclass method invocation NYI"); } else { // '.' 'super' '.' Identifier // Qualified superclass field access (JLS7 15.11.2) (LHS is an Rvalue). return new SuperclassFieldAccessExpression( location, // location atom.toTypeOrCompileException(), // optionalQualification identifier // fieldName ); } } if (this.peekRead("new")) { // '.' 'new' Identifier Arguments [ ClassBody ] Rvalue lhs = atom.toRvalueOrCompileException(); Location location = this.location(); String identifier = this.read(TokenType.IDENTIFIER); Type type = new RvalueMemberType( location, // location lhs, // rValue identifier // identifier ); Rvalue[] arguments = this.parseArguments(); if (this.peek("{")) { // '.' 'new' Identifier Arguments ClassBody (LHS is an Rvalue) final AnonymousClassDeclaration anonymousClassDeclaration = new AnonymousClassDeclaration( this.location(), // location type // baseType ); this.parseClassBody(anonymousClassDeclaration); return new NewAnonymousClassInstance( location, // location lhs, // optionalQualification anonymousClassDeclaration, // anonymousClassDeclaration arguments // arguments ); } else { // '.' 'new' Identifier Arguments (LHS is an Rvalue) return new NewClassInstance( location, // location lhs, // optionalQualification type, // referenceType arguments // arguments ); } } if (this.peekRead("class")) { // '.' 'class' Location location = this.location(); return new ClassLiteral(location, atom.toTypeOrCompileException()); } throw this.compileException("Unexpected selector '" + this.peek().value + "' after \".\""); } if (this.peekRead("[")) { // '[' Expression ']' Location location = this.location(); Rvalue index = this.parseExpression().toRvalueOrCompileException(); this.read("]"); return new ArrayAccessExpression( location, // location atom.toRvalueOrCompileException(), // lhs index // index ); } throw this.compileException("Unexpected token '" + this.peek().value + "' in selector"); } /** *
     *   DimExprs := DimExpr { DimExpr }
     * 
*/ public Rvalue[] parseDimExprs() throws CompileException, IOException { List l = new ArrayList(); l.add(this.parseDimExpr()); while (this.peek("[") && !this.peekNextButOne("]")) { l.add(this.parseDimExpr()); } return (Rvalue[]) l.toArray(new Rvalue[l.size()]); } /** *
     *   DimExpr := '[' Expression ']'
     * 
*/ public Rvalue parseDimExpr() throws CompileException, IOException { this.read("["); Rvalue res = this.parseExpression().toRvalueOrCompileException(); this.read("]"); return res; } /** *
     *   Arguments := '(' [ ArgumentList ] ')'
     * 
*/ public Rvalue[] parseArguments() throws CompileException, IOException { this.read("("); if (this.peekRead(")")) return new Rvalue[0]; Rvalue[] arguments = this.parseArgumentList(); this.read(")"); return arguments; } /** *
     *   ArgumentList := Expression { ',' Expression }
     * 
*/ public Rvalue[] parseArgumentList() throws CompileException, IOException { List l = new ArrayList(); do { l.add(this.parseExpression().toRvalueOrCompileException()); } while (this.peekRead(",")); return (Rvalue[]) l.toArray(new Rvalue[l.size()]); } /** *
     *   Literal :=
     *     IntegerLiteral
     *     | FloatingPointLiteral
     *     | BooleanLiteral
     *     | CharacterLiteral
     *     | StringLiteral
     *     | NullLiteral
     * 
*/ public Rvalue parseLiteral() throws CompileException, IOException { Token t = this.read(); switch (t.type) { case INTEGER_LITERAL: return new IntegerLiteral(t.getLocation(), t.value); case FLOATING_POINT_LITERAL: return new FloatingPointLiteral(t.getLocation(), t.value); case BOOLEAN_LITERAL: return new BooleanLiteral(t.getLocation(), t.value); case CHARACTER_LITERAL: return new CharacterLiteral(t.getLocation(), t.value); case STRING_LITERAL: return new StringLiteral(t.getLocation(), t.value); case NULL_LITERAL: return new NullLiteral(t.getLocation()); default: throw this.compileException("Literal expected"); } } /** *
     *   ExpressionStatement := Expression ';'
     * 
*/ public Statement parseExpressionStatement() throws CompileException, IOException { Rvalue rv = this.parseExpression().toRvalueOrCompileException(); this.read(";"); return new ExpressionStatement(rv); } /** * @return The location of the first character of the previously {@link #peek()}ed or {@link #read()} token */ public Location location() { return this.scanner.location(); } // Token-level methods. // Shorthand for the various "TokenStream" methods. SUPPRESS CHECKSTYLE LineLength|JavadocMethod:16 public Token peek() throws CompileException, IOException { return this.tokenStream.peek(); } public Token read() throws CompileException, IOException { return this.tokenStream.read(); } public boolean peek(String suspected) throws CompileException, IOException { return this.tokenStream.peek(suspected); } public int peek(String... suspected) throws CompileException, IOException { return this.tokenStream.peek(suspected); } public boolean peek(TokenType suspected) throws CompileException, IOException { return this.tokenStream.peek(suspected); } public int peek(TokenType... suspected) throws CompileException, IOException { return this.tokenStream.peek(suspected); } public Token peekNextButOne() throws CompileException, IOException { return this.tokenStream.peekNextButOne(); } public boolean peekNextButOne(String suspected) throws CompileException, IOException { return this.tokenStream.peekNextButOne(suspected); } public void read(String expected) throws CompileException, IOException { this.tokenStream.read(expected); } public int read(String... expected) throws CompileException, IOException { return this.tokenStream.read(expected); } public String read(TokenType expected) throws CompileException, IOException { return this.tokenStream.read(expected); } public boolean peekRead(String suspected) throws CompileException, IOException { return this.tokenStream.peekRead(suspected); } public int peekRead(String... suspected) throws CompileException, IOException { return this.tokenStream.peekRead(suspected); } public boolean peekLiteral() throws CompileException, IOException { return this.peek( TokenType.INTEGER_LITERAL, TokenType.FLOATING_POINT_LITERAL, TokenType.BOOLEAN_LITERAL, // SUPPRESS CHECKSTYLE LineLength|Wrap:1 TokenType.CHARACTER_LITERAL, TokenType.STRING_LITERAL, TokenType.NULL_LITERAL ) != -1; } /** * Issues a warning if the given string does not comply with the package naming conventions. */ private void verifyStringIsConventionalPackageName(String s, Location loc) throws CompileException { if (!Character.isLowerCase(s.charAt(0))) { this.warning( "UPN", "Package name \"" + s + "\" does not begin with a lower-case letter (see JLS7 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; } } } /** * Issues a warning if the given identifier does not comply with the class and interface type naming conventions * (JLS7 6.8.2). */ private void verifyIdentifierIsConventionalClassOrInterfaceName(String id, Location loc) throws CompileException { if (!Character.isUpperCase(id.charAt(0))) { this.warning( "UCOIN1", "Class or interface name \"" + id + "\" does not begin with an upper-case letter (see JLS7 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 JLS7 6.8.2)" ), loc); return; } } } /** * Issues a warning if the given identifier does not comply with the method naming conventions (JLS7 6.8.3). */ private void verifyIdentifierIsConventionalMethodName(String id, Location loc) throws CompileException { if (!Character.isLowerCase(id.charAt(0))) { this.warning( "UMN1", "Method name \"" + id + "\" does not begin with a lower-case letter (see JLS7 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 JLS7 6.8.3)", loc ); return; } } } /** * Issues a warning if the given identifier does not comply with the field naming conventions (JLS7 6.8.4) and * constant naming conventions (JLS7 6.8.5). */ private void verifyIdentifierIsConventionalFieldName(String id, Location loc) throws CompileException { // 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 JLS7 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 JLS7 6.8.4)", loc ); return; } } } else { this.warning("UFN1", ( "\"" + id + "\" is neither a conventional field name (JLS7 6.8.4) nor a conventional constant name (JLS7 6.8.5)" ), loc); } } /** * Issues a warning if the given identifier does not comply with the local variable and parameter naming * conventions (JLS7 6.8.6). */ private void verifyIdentifierIsConventionalLocalVariableOrParameterName(String id, Location loc) throws CompileException { if (!Character.isLowerCase(id.charAt(0))) { this.warning( "ULVN1", "Local variable name \"" + id + "\" does not begin with a lower-case letter (see JLS7 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 JLS7 6.8.6)" ), loc); return; } } } /** * By default, warnings are discarded, but an application my install a {@link WarningHandler}. *

* Notice that there is no {@code Parser.setErrorHandler()} 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} to indicate that no warnings be issued */ public void setWarningHandler(@Nullable WarningHandler optionalWarningHandler) { this.optionalWarningHandler = optionalWarningHandler; this.tokenStream.setWarningHandler(optionalWarningHandler); } // Used for elaborate warning handling. @Nullable private WarningHandler optionalWarningHandler; /** * 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)}. *

* The {@code handle} argument qualifies the warning and is typically used by * the {@link WarningHandler} to suppress individual warnings. * * @throws CompileException The optionally installed {@link WarningHandler} decided to throw a {@link * CompileException} */ private void warning(String handle, String message, @Nullable Location optionalLocation) throws CompileException { if (this.optionalWarningHandler != null) { this.optionalWarningHandler.handleWarning(handle, message, optionalLocation); } } /** * Convenience method for throwing a {@link CompileException}. */ protected final CompileException compileException(String message) { return new CompileException(message, this.location()); } private static String join(@Nullable String[] sa, String separator) { if (sa == null) return ("(null)"); if (sa.length == 0) return ("(zero length array)"); StringBuilder sb = new StringBuilder(sa[0]); for (int i = 1; i < sa.length; ++i) { sb.append(separator).append(sa[i]); } return sb.toString(); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy