0001: /*
0002: * Janino - An embedded Java[TM] compiler
0003: *
0004: * Copyright (c) 2001-2007, Arno Unkrig
0005: * All rights reserved.
0006: *
0007: * Redistribution and use in source and binary forms, with or without
0008: * modification, are permitted provided that the following conditions
0009: * are met:
0010: *
0011: * 1. Redistributions of source code must retain the above copyright
0012: * notice, this list of conditions and the following disclaimer.
0013: * 2. Redistributions in binary form must reproduce the above
0014: * copyright notice, this list of conditions and the following
0015: * disclaimer in the documentation and/or other materials
0016: * provided with the distribution.
0017: * 3. The name of the author may not be used to endorse or promote
0018: * products derived from this software without specific prior
0019: * written permission.
0020: *
0021: * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
0022: * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
0023: * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
0024: * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
0025: * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
0026: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
0027: * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
0028: * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
0029: * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
0030: * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
0031: * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
0032: */
0033:
0034: package org.codehaus.janino;
0035:
0036: import java.io.*;
0037: import java.util.*;
0038:
0039: import org.codehaus.janino.Java.CompilationUnit.ImportDeclaration;
0040: import org.codehaus.janino.util.LocatedException;
0041: import org.codehaus.janino.util.enumerator.Enumerator;
0042:
0043: /**
0044: * A parser for the Java<sup>TM</sup> programming language.
0045: */
0046: public class Parser {
0047: private final Scanner scanner;
0048:
0049: public Parser(Scanner scanner) {
0050: this .scanner = scanner;
0051: }
0052:
0053: public Scanner getScanner() {
0054: return this .scanner;
0055: }
0056:
0057: /**
0058: * <pre>
0059: * CompilationUnit := [ PackageDeclaration ]
0060: * { ImportDeclaration }
0061: * { TypeDeclaration }
0062: * </pre>
0063: */
0064: public Java.CompilationUnit parseCompilationUnit()
0065: throws ParseException, Scanner.ScanException, IOException {
0066: Java.CompilationUnit compilationUnit = new Java.CompilationUnit(
0067: this .location().getFileName());
0068:
0069: if (this .peekKeyword("package")) {
0070: compilationUnit.setPackageDeclaration(this
0071: .parsePackageDeclaration());
0072: }
0073:
0074: while (this .peekKeyword("import")) {
0075: compilationUnit.addImportDeclaration(this
0076: .parseImportDeclaration());
0077: }
0078:
0079: while (!this .scanner.peek().isEOF()) {
0080: if (this .peekOperator(";")) {
0081: this .eatToken();
0082: } else {
0083: compilationUnit.addPackageMemberTypeDeclaration(this
0084: .parsePackageMemberTypeDeclaration());
0085: }
0086: }
0087:
0088: return compilationUnit;
0089: }
0090:
0091: /**
0092: * <pre>
0093: * PackageDeclaration := 'package' QualifiedIdentifier ';'
0094: * </pre>
0095: */
0096: public Java.PackageDeclaration parsePackageDeclaration()
0097: throws Parser.ParseException, Scanner.ScanException,
0098: IOException {
0099: this .readKeyword("package");
0100: Location loc = this .location();
0101: String packageName = Parser.join(this
0102: .parseQualifiedIdentifier(), ".");
0103: this .readOperator(";");
0104: this .verifyStringIsConventionalPackageName(packageName, loc);
0105: return new Java.PackageDeclaration(loc, packageName);
0106: }
0107:
0108: /**
0109: * <pre>
0110: * ImportDeclaration := 'import' ImportDeclarationBody ';'
0111: * </pre>
0112: */
0113: public Java.CompilationUnit.ImportDeclaration parseImportDeclaration()
0114: throws ParseException, Scanner.ScanException, IOException {
0115: this .readKeyword("import");
0116: Java.CompilationUnit.ImportDeclaration importDeclaration = this
0117: .parseImportDeclarationBody();
0118: this .readOperator(";");
0119: return importDeclaration;
0120: }
0121:
0122: /**
0123: * <pre>
0124: * ImportDeclarationBody := [ 'static' ] Identifier { '.' Identifier } [ '.' '*' ]
0125: * </pre>
0126: */
0127: public Java.CompilationUnit.ImportDeclaration parseImportDeclarationBody()
0128: throws ParseException, Scanner.ScanException, IOException {
0129: Location loc = this .location();
0130: boolean isStatic;
0131: if (this .peekKeyword("static")) {
0132: isStatic = true;
0133: this .eatToken();
0134: } else {
0135: isStatic = false;
0136: }
0137: List l = new ArrayList();
0138: l.add(this .readIdentifier());
0139: for (;;) {
0140: if (!this .peekOperator(".")) {
0141: String[] identifiers = (String[]) l
0142: .toArray(new String[l.size()]);
0143: return (isStatic ? (ImportDeclaration) new Java.CompilationUnit.SingleStaticImportDeclaration(
0144: loc, identifiers)
0145: : (ImportDeclaration) new Java.CompilationUnit.SingleTypeImportDeclaration(
0146: loc, identifiers));
0147: }
0148: this .readOperator(".");
0149: if (this .peekOperator("*")) {
0150: this .eatToken();
0151: String[] identifiers = (String[]) l
0152: .toArray(new String[l.size()]);
0153: return (isStatic ? (ImportDeclaration) new Java.CompilationUnit.StaticImportOnDemandDeclaration(
0154: loc, identifiers)
0155: : (ImportDeclaration) new Java.CompilationUnit.TypeImportOnDemandDeclaration(
0156: loc, identifiers));
0157: }
0158: l.add(this .readIdentifier());
0159: }
0160: }
0161:
0162: /**
0163: * QualifiedIdentifier := Identifier { '.' Identifier }
0164: */
0165: public String[] parseQualifiedIdentifier() throws ParseException,
0166: Scanner.ScanException, IOException {
0167: if (!this .scanner.peek().isIdentifier())
0168: this .throwParseException("Identifier expected");
0169: List l = new ArrayList();
0170: l.add(this .readIdentifier());
0171: while (this .peekOperator(".")
0172: && this .scanner.peekNextButOne().isIdentifier()) {
0173: this .eatToken();
0174: l.add(this .readIdentifier());
0175: }
0176: return (String[]) l.toArray(new String[l.size()]);
0177: }
0178:
0179: /**
0180: * <pre>
0181: * PackageMemberTypeDeclaration :=
0182: * ModifiersOpt 'class' ClassDeclarationRest |
0183: * ModifiersOpt 'interface' InterfaceDeclarationRest
0184: * </pre>
0185: */
0186: public Java.PackageMemberTypeDeclaration parsePackageMemberTypeDeclaration()
0187: throws ParseException, Scanner.ScanException, IOException {
0188: String optionalDocComment = this .scanner.doc();
0189:
0190: short modifiers = this .parseModifiersOpt();
0191:
0192: Java.PackageMemberTypeDeclaration res;
0193: if (this .peekKeyword("class")) {
0194: if (optionalDocComment == null)
0195: this .warning("CDCM", "Class doc comment missing", this
0196: .location());
0197: this .eatToken();
0198: res = (Java.PackageMemberClassDeclaration) this
0199: .parseClassDeclarationRest(optionalDocComment, // optionalDocComment
0200: modifiers, // modifiers
0201: ClassDeclarationContext.COMPILATION_UNIT // context
0202: );
0203: } else if (this .peekKeyword("interface")) {
0204: if (optionalDocComment == null)
0205: this .warning("IDCM", "Interface doc comment missing",
0206: this .location());
0207: this .eatToken();
0208: res = (Java.PackageMemberInterfaceDeclaration) this
0209: .parseInterfaceDeclarationRest(
0210: optionalDocComment, // optionalDocComment
0211: modifiers, // modifiers
0212: InterfaceDeclarationContext.COMPILATION_UNIT // context
0213: );
0214: } else {
0215: this .throwParseException("Unexpected token \""
0216: + this .scanner.peek()
0217: + "\" in class or interface declaration");
0218: /* NEVER REACHED */return null;
0219: }
0220: return res;
0221: }
0222:
0223: /**
0224: * <pre>
0225: * ModifiersOpt := { 'public' | 'protected' | 'private' | 'static' |
0226: * 'abstract' | 'final' | 'native' | 'synchronized' |
0227: * 'transient' | 'volatile' | 'strictfp'
0228: * </pre>
0229: */
0230: public short parseModifiersOpt() throws ParseException,
0231: Scanner.ScanException, IOException {
0232: short mod = 0;
0233: while (this .peekKeyword()) {
0234: String kw = this .scanner.peek().getKeyword();
0235: short x = (short) (kw == "public" ? Mod.PUBLIC
0236: : kw == "protected" ? Mod.PROTECTED
0237: : kw == "private" ? Mod.PRIVATE
0238: : kw == "static" ? Mod.STATIC
0239: : kw == "abstract" ? Mod.ABSTRACT
0240: : kw == "final" ? Mod.FINAL
0241: : kw == "native" ? Mod.NATIVE
0242: : kw == "synchronized" ? Mod.SYNCHRONIZED
0243: : kw == "transient" ? Mod.TRANSIENT
0244: : kw == "volatile" ? Mod.VOLATILE
0245: : kw == "strictfp" ? Mod.STRICTFP
0246: : -1);
0247: if (x == -1)
0248: break;
0249:
0250: this .eatToken();
0251: if ((mod & x) != 0)
0252: this .throwParseException("Duplicate modifier \"" + kw
0253: + "\"");
0254: for (int i = 0; i < Parser.MUTUALS.length; ++i) {
0255: short m = Parser.MUTUALS[i];
0256: if ((x & m) != 0 && (mod & m) != 0) {
0257: this .throwParseException("Only one of \""
0258: + Mod.shortToString(m) + "\" allowed");
0259: }
0260: }
0261: mod |= x;
0262: }
0263: return mod;
0264: }
0265:
0266: private static final short[] MUTUALS = { Mod.PUBLIC | Mod.PROTECTED
0267: | Mod.PRIVATE, };
0268:
0269: /**
0270: * <pre>
0271: * ClassDeclarationRest :=
0272: * Identifier
0273: * [ 'extends' ReferenceType ]
0274: * [ 'implements' ReferenceTypeList ]
0275: * ClassBody
0276: * </pre>
0277: */
0278: public Java.NamedClassDeclaration parseClassDeclarationRest(
0279: String optionalDocComment, short modifiers,
0280: ClassDeclarationContext context) throws ParseException,
0281: Scanner.ScanException, IOException {
0282: Location location = this .location();
0283: String className = this .readIdentifier();
0284: this .verifyIdentifierIsConventionalClassOrInterfaceName(
0285: className, location);
0286:
0287: Java.ReferenceType optionalExtendedType = null;
0288: if (this .peekKeyword("extends")) {
0289: this .eatToken();
0290: optionalExtendedType = this .parseReferenceType();
0291: }
0292:
0293: Java.ReferenceType[] implementedTypes = new Java.ReferenceType[0];
0294: if (this .peekKeyword("implements")) {
0295: this .eatToken();
0296: implementedTypes = this .parseReferenceTypeList();
0297: }
0298:
0299: Java.NamedClassDeclaration namedClassDeclaration;
0300: if (context == ClassDeclarationContext.COMPILATION_UNIT) {
0301: namedClassDeclaration = new Java.PackageMemberClassDeclaration(
0302: location, // location
0303: optionalDocComment, // optionalDocComment
0304: modifiers, // modifiers
0305: className, // name
0306: optionalExtendedType, // optinalExtendedType
0307: implementedTypes // implementedTypes
0308: );
0309: } else if (context == ClassDeclarationContext.TYPE_DECLARATION) {
0310: namedClassDeclaration = new Java.MemberClassDeclaration(
0311: location, // location
0312: optionalDocComment, // optionalDocComment
0313: modifiers, // modifiers
0314: className, // name
0315: optionalExtendedType, // optionalExtendedType
0316: implementedTypes // implementedTypes
0317: );
0318: } else if (context == ClassDeclarationContext.BLOCK) {
0319: namedClassDeclaration = new Java.LocalClassDeclaration(
0320: location, // location
0321: optionalDocComment, // optionalDocComment
0322: modifiers, // modifiers
0323: className, // name
0324: optionalExtendedType, // optionalExtendedType
0325: implementedTypes // implementedTypes
0326: );
0327: } else {
0328: throw new RuntimeException(
0329: "SNO: Class declaration in unexpected context "
0330: + context);
0331: }
0332:
0333: this .parseClassBody(namedClassDeclaration);
0334: return namedClassDeclaration;
0335: }
0336:
0337: public static class ClassDeclarationContext extends Enumerator {
0338: public static final ClassDeclarationContext BLOCK = new ClassDeclarationContext(
0339: "block");
0340: public static final ClassDeclarationContext TYPE_DECLARATION = new ClassDeclarationContext(
0341: "type_declaration");
0342: public static final ClassDeclarationContext COMPILATION_UNIT = new ClassDeclarationContext(
0343: "compilation_unit");
0344:
0345: private ClassDeclarationContext(String name) {
0346: super (name);
0347: }
0348: }
0349:
0350: /**
0351: * <pre>
0352: * ClassBody := '{' { ClassBodyDeclaration } '}'
0353: * </pre>
0354: */
0355: public void parseClassBody(Java.ClassDeclaration classDeclaration)
0356: throws ParseException, Scanner.ScanException, IOException {
0357: if (!this .peekOperator("{"))
0358: this
0359: .throwParseException("\"{\" expected at start of class body");
0360: this .eatToken();
0361:
0362: for (;;) {
0363: if (this .peekOperator("}")) {
0364: this .eatToken();
0365: return;
0366: }
0367:
0368: this .parseClassBodyDeclaration(classDeclaration);
0369: }
0370: }
0371:
0372: /**
0373: * <pre>
0374: * ClassBodyDeclaration :=
0375: * ';' |
0376: * ModifiersOpt (
0377: * Block | // Instance (JLS2 8.6) or static initializer (JLS2 8.7)
0378: * 'void' Identifier MethodDeclarationRest |
0379: * 'class' ClassDeclarationRest |
0380: * 'interface' InterfaceDeclarationRest |
0381: * ConstructorDeclarator |
0382: * Type Identifier (
0383: * MethodDeclarationRest |
0384: * FieldDeclarationRest ';'
0385: * )
0386: * )
0387: *
0388: * </pre>
0389: */
0390: public void parseClassBodyDeclaration(
0391: Java.ClassDeclaration classDeclaration)
0392: throws ParseException, Scanner.ScanException, IOException {
0393: if (this .peekOperator(";")) {
0394: this .eatToken();
0395: return;
0396: }
0397:
0398: String optionalDocComment = this .scanner.doc();
0399: short modifiers = this .parseModifiersOpt();
0400:
0401: // Initializer?
0402: if (this .peekOperator("{")) {
0403: if ((modifiers & ~Mod.STATIC) != 0)
0404: this
0405: .throwParseException("Only modifier \"static\" allowed on initializer");
0406:
0407: Java.Initializer initializer = new Java.Initializer(this
0408: .location(), // location
0409: (modifiers & Mod.STATIC) != 0, // statiC
0410: this .parseBlock() // block
0411: );
0412:
0413: classDeclaration
0414: .addVariableDeclaratorOrInitializer(initializer);
0415: return;
0416: }
0417:
0418: // "void" method declaration.
0419: if (this .peekKeyword("void")) {
0420: Location location = this .location();
0421: this .eatToken();
0422: if (optionalDocComment == null)
0423: this .warning("MDCM", "Method doc comment missing",
0424: location);
0425: String name = this .readIdentifier();
0426: classDeclaration.addDeclaredMethod(this
0427: .parseMethodDeclarationRest(optionalDocComment, // declaringType
0428: modifiers, // optionalDocComment
0429: new Java.BasicType(location,
0430: Java.BasicType.VOID), // modifiers
0431: name // name
0432: ));
0433: return;
0434: }
0435:
0436: // Member class.
0437: if (this .peekKeyword("class")) {
0438: if (optionalDocComment == null)
0439: this .warning("MCDCM",
0440: "Member class doc comment missing", this
0441: .location());
0442: this .eatToken();
0443: classDeclaration
0444: .addMemberTypeDeclaration((Java.MemberTypeDeclaration) this
0445: .parseClassDeclarationRest(
0446: optionalDocComment, // optionalDocComment
0447: modifiers, // modifiers
0448: ClassDeclarationContext.TYPE_DECLARATION // context
0449: ));
0450: return;
0451: }
0452:
0453: // Member interface.
0454: if (this .peekKeyword("interface")) {
0455: if (optionalDocComment == null)
0456: this .warning("MIDCM",
0457: "Member interface doc comment missing", this
0458: .location());
0459: this .eatToken();
0460: classDeclaration
0461: .addMemberTypeDeclaration((Java.MemberTypeDeclaration) this
0462: .parseInterfaceDeclarationRest(
0463: optionalDocComment, // optionalDocComment
0464: (short) (modifiers | Mod.STATIC), // modifiers
0465: InterfaceDeclarationContext.NAMED_TYPE_DECLARATION // context
0466: ));
0467: return;
0468: }
0469:
0470: // Constructor.
0471: if (classDeclaration instanceof Java.NamedClassDeclaration
0472: && this .scanner.peek().isIdentifier(
0473: ((Java.NamedClassDeclaration) classDeclaration)
0474: .getName())
0475: && this .scanner.peekNextButOne().isOperator("(")) {
0476: if (optionalDocComment == null)
0477: this .warning("CDCM", "Constructor doc comment missing",
0478: this .location());
0479: classDeclaration.addConstructor(this
0480: .parseConstructorDeclarator(optionalDocComment, // declaringClass
0481: modifiers // modifiers
0482: ));
0483: return;
0484: }
0485:
0486: // Member method or field.
0487: Java.Type memberType = this .parseType();
0488: Location location = this .location();
0489: String memberName = this .readIdentifier();
0490:
0491: // Method declarator.
0492: if (this .peekOperator("(")) {
0493: if (optionalDocComment == null)
0494: this .warning("MDCM", "Method doc comment missing", this
0495: .location());
0496: classDeclaration.addDeclaredMethod(this
0497: .parseMethodDeclarationRest(optionalDocComment, // declaringType
0498: modifiers, // optionalDocComment
0499: memberType, // modifiers
0500: memberName // name
0501: ));
0502: return;
0503: }
0504:
0505: // Field declarator.
0506: if (optionalDocComment == null)
0507: this .warning("FDCM", "Field doc comment missing", this
0508: .location());
0509: Java.FieldDeclaration fd = new Java.FieldDeclaration(location, // location
0510: optionalDocComment, // optionalDocComment
0511: modifiers, // modifiers
0512: memberType, // type
0513: this .parseFieldDeclarationRest(memberName) // variableDeclarators
0514: );
0515: this .readOperator(";");
0516: classDeclaration.addVariableDeclaratorOrInitializer(fd);
0517: }
0518:
0519: /**
0520: * <pre>
0521: * InterfaceDeclarationRest :=
0522: * Identifier
0523: * [ 'extends' ReferenceTypeList ]
0524: * InterfaceBody
0525: * </pre>
0526: */
0527: public Java.InterfaceDeclaration parseInterfaceDeclarationRest(
0528: String optionalDocComment, short modifiers,
0529: InterfaceDeclarationContext context) throws ParseException,
0530: Scanner.ScanException, IOException {
0531: Location location = this .location();
0532: String interfaceName = this .readIdentifier();
0533: this .verifyIdentifierIsConventionalClassOrInterfaceName(
0534: interfaceName, location);
0535:
0536: Java.ReferenceType[] extendedTypes = new Java.ReferenceType[0];
0537: if (this .peekKeyword("extends")) {
0538: this .eatToken();
0539: extendedTypes = this .parseReferenceTypeList();
0540: }
0541:
0542: Java.InterfaceDeclaration interfaceDeclaration;
0543: if (context == InterfaceDeclarationContext.COMPILATION_UNIT) {
0544: interfaceDeclaration = new Java.PackageMemberInterfaceDeclaration(
0545: location, // location
0546: optionalDocComment, // optionalDocComment
0547: modifiers, // modifiers
0548: interfaceName, // name
0549: extendedTypes // extendedTypes
0550: );
0551: } else if (context == InterfaceDeclarationContext.NAMED_TYPE_DECLARATION) {
0552: interfaceDeclaration = new Java.MemberInterfaceDeclaration(
0553: location, // location
0554: optionalDocComment, // optionalDocComment
0555: modifiers, // modifiers
0556: interfaceName, // name
0557: extendedTypes // extendedTypes
0558: );
0559: } else {
0560: throw new RuntimeException(
0561: "SNO: Interface declaration in unexpected context "
0562: + context);
0563: }
0564:
0565: this .parseInterfaceBody(interfaceDeclaration);
0566: return interfaceDeclaration;
0567: }
0568:
0569: public static class InterfaceDeclarationContext extends Enumerator {
0570: public static final InterfaceDeclarationContext NAMED_TYPE_DECLARATION = new InterfaceDeclarationContext(
0571: "named_type_declaration");
0572: public static final InterfaceDeclarationContext COMPILATION_UNIT = new InterfaceDeclarationContext(
0573: "compilation_unit");
0574:
0575: private InterfaceDeclarationContext(String name) {
0576: super (name);
0577: }
0578: }
0579:
0580: /**
0581: * <pre>
0582: * InterfaceBody := '{' {
0583: * ';' |
0584: * ModifiersOpt (
0585: * 'void' Identifier MethodDeclarationRest |
0586: * 'class' ClassDeclarationRest |
0587: * 'interface' InterfaceDeclarationRest |
0588: * Type Identifier (
0589: * MethodDeclarationRest |
0590: * FieldDeclarationRest
0591: * )
0592: * )
0593: * } '}'
0594: * </pre>
0595: */
0596: public void parseInterfaceBody(
0597: Java.InterfaceDeclaration interfaceDeclaration)
0598: throws ParseException, Scanner.ScanException, IOException {
0599: this .readOperator("{");
0600:
0601: for (;;) {
0602: if (this .peekOperator("}")) {
0603: this .eatToken();
0604: break;
0605: }
0606:
0607: if (this .peekOperator(";")) {
0608: this .eatToken();
0609: continue;
0610: }
0611:
0612: String optionalDocComment = this .scanner.doc();
0613: short modifiers = this .parseModifiersOpt();
0614:
0615: // "void" method declaration.
0616: if (this .peekKeyword("void")) {
0617: if (optionalDocComment == null)
0618: this .warning("MDCM", "Method doc comment missing",
0619: this .location());
0620: Location location = this .location();
0621: this .eatToken();
0622: String name = this .readIdentifier();
0623: interfaceDeclaration
0624: .addDeclaredMethod(this
0625: .parseMethodDeclarationRest(
0626: optionalDocComment, // declaringType
0627: (short) (modifiers
0628: | Mod.ABSTRACT | Mod.PUBLIC), // optionalDocComment
0629: new Java.BasicType(location,
0630: Java.BasicType.VOID), // modifiers
0631: name // name
0632: ));
0633: } else
0634:
0635: // Member class.
0636: if (this .peekKeyword("class")) {
0637: if (optionalDocComment == null)
0638: this .warning("MCDCM",
0639: "Member class doc comment missing", this
0640: .location());
0641: this .eatToken();
0642: interfaceDeclaration
0643: .addMemberTypeDeclaration((Java.MemberTypeDeclaration) this
0644: .parseClassDeclarationRest(
0645: optionalDocComment, // optionalDocComment
0646: (short) (modifiers | Mod.STATIC | Mod.PUBLIC), // modifiers
0647: ClassDeclarationContext.TYPE_DECLARATION // context
0648: ));
0649: } else
0650:
0651: // Member interface.
0652: if (this .peekKeyword("interface")) {
0653: if (optionalDocComment == null)
0654: this .warning("MIDCM",
0655: "Member interface doc comment missing",
0656: this .location());
0657: this .eatToken();
0658: interfaceDeclaration
0659: .addMemberTypeDeclaration((Java.MemberTypeDeclaration) this
0660: .parseInterfaceDeclarationRest(
0661: optionalDocComment, // optionalDocComment
0662: (short) (modifiers | Mod.STATIC | Mod.PUBLIC), // modifiers
0663: InterfaceDeclarationContext.NAMED_TYPE_DECLARATION // context
0664: ));
0665: } else
0666:
0667: // Member method or field.
0668: {
0669: Java.Type memberType = this .parseType();
0670: if (!this .scanner.peek().isIdentifier())
0671: this
0672: .throwParseException("Identifier expected in member declaration");
0673: Location location = this .location();
0674: String memberName = this .readIdentifier();
0675:
0676: // Method declarator.
0677: if (this .peekOperator("(")) {
0678: if (optionalDocComment == null)
0679: this .warning("MDCM",
0680: "Method doc comment missing", this
0681: .location());
0682: interfaceDeclaration
0683: .addDeclaredMethod(this
0684: .parseMethodDeclarationRest(
0685: optionalDocComment, // declaringType
0686: (short) (modifiers
0687: | Mod.ABSTRACT | Mod.PUBLIC), // optionalDocComment
0688: memberType, // modifiers
0689: memberName // name
0690: ));
0691: } else
0692:
0693: // Field declarator.
0694: {
0695: if (optionalDocComment == null)
0696: this .warning("FDCM",
0697: "Field doc comment missing", this
0698: .location());
0699: Java.FieldDeclaration fd = new Java.FieldDeclaration(
0700: location, // location
0701: optionalDocComment, // optionalDocComment
0702: (short) ( // modifiers
0703: modifiers | Mod.PUBLIC | Mod.STATIC | Mod.FINAL),
0704: memberType, // type
0705: this .parseFieldDeclarationRest(memberName) // variableDeclarators
0706: );
0707: interfaceDeclaration.addConstantDeclaration(fd);
0708: }
0709: }
0710: }
0711: }
0712:
0713: /**
0714: * <pre>
0715: * ConstructorDeclarator :=
0716: * Identifier
0717: * FormalParameters
0718: * [ 'throws' ReferenceTypeList ]
0719: * '{'
0720: * [ 'this' Arguments ';' | 'super' Arguments ';' | Primary '.' 'super' Arguments ';' ]
0721: * BlockStatements
0722: * '}'
0723: * </pre>
0724: */
0725: public Java.ConstructorDeclarator parseConstructorDeclarator(
0726: String optionalDocComment, short modifiers)
0727: throws ParseException, Scanner.ScanException, IOException {
0728: Location location = this .location();
0729: this .readIdentifier(); // Class name
0730:
0731: // Parse formal parameters.
0732: Java.FunctionDeclarator.FormalParameter[] formalParameters = this
0733: .parseFormalParameters();
0734:
0735: // Parse "throws" clause.
0736: Java.ReferenceType[] thrownExceptions;
0737: if (this .peekKeyword("throws")) {
0738: this .eatToken();
0739: thrownExceptions = this .parseReferenceTypeList();
0740: } else {
0741: thrownExceptions = new Java.ReferenceType[0];
0742: }
0743:
0744: // Parse constructor body.
0745: location = this .location();
0746: this .readOperator("{");
0747:
0748: // Special treatment for the first statement of the constructor body: If this is surely an
0749: // expression statement, and if it could be a "ConstructorInvocation", then parse the
0750: // expression and check if it IS a ConstructorInvocation.
0751: Java.ConstructorInvocation optionalConstructorInvocation = null;
0752: Java.Block body = new Java.Block(location);
0753: if (this .peekKeyword(new String[] { "this", "super", "new",
0754: "void", "byte", "char", "short", "int", "long",
0755: "float", "double", "boolean", })
0756: || this .scanner.peek().isLiteral()
0757: || this .scanner.peek().isIdentifier()) {
0758: Java.Atom a = this .parseExpression();
0759: if (a instanceof Java.ConstructorInvocation) {
0760: this .readOperator(";");
0761: optionalConstructorInvocation = (Java.ConstructorInvocation) a;
0762: } else {
0763: Java.Statement s;
0764: if (this .scanner.peek().isIdentifier()) {
0765: Java.Type variableType = a.toTypeOrPE();
0766: s = new Java.LocalVariableDeclarationStatement(a
0767: .getLocation(), // location
0768: (short) 0, // modifiers
0769: variableType, // type
0770: this .parseLocalVariableDeclarators() // variableDeclarators
0771: );
0772: this .readOperator(";");
0773: } else {
0774: s = new Java.ExpressionStatement(a.toRvalueOrPE());
0775: this .readOperator(";");
0776: }
0777: body.addStatement(s);
0778: }
0779: }
0780: body.addStatements(this .parseBlockStatements());
0781:
0782: this .readOperator("}");
0783:
0784: return new Java.ConstructorDeclarator(location, // location
0785: optionalDocComment, // optionalDocComment
0786: modifiers, // modifiers
0787: formalParameters, // formalParameters
0788: thrownExceptions, // thrownExceptions
0789: optionalConstructorInvocation, // optionalConstructorInvocationStatement
0790: body // body
0791: );
0792: }
0793:
0794: /**
0795: * <pre>
0796: * MethodDeclarationRest :=
0797: * FormalParameters
0798: * { '[' ']' }
0799: * [ 'throws' ReferenceTypeList ]
0800: * ( ';' | MethodBody )
0801: * </pre>
0802: */
0803: public Java.MethodDeclarator parseMethodDeclarationRest(
0804: String optionalDocComment, short modifiers, Java.Type type,
0805: String name) throws ParseException, Scanner.ScanException,
0806: IOException {
0807: Location location = this .location();
0808:
0809: this .verifyIdentifierIsConventionalMethodName(name, location);
0810:
0811: Java.FunctionDeclarator.FormalParameter[] formalParameters = this
0812: .parseFormalParameters();
0813:
0814: for (int i = this .parseBracketsOpt(); i > 0; --i)
0815: type = new Java.ArrayType(type);
0816:
0817: Java.ReferenceType[] thrownExceptions;
0818: if (this .peekKeyword("throws")) {
0819: this .eatToken();
0820: thrownExceptions = this .parseReferenceTypeList();
0821: } else {
0822: thrownExceptions = new Java.ReferenceType[0];
0823: }
0824:
0825: Java.Block optionalBody;
0826: if (this .peekOperator(";")) {
0827: if ((modifiers & (Mod.ABSTRACT | Mod.NATIVE)) == 0)
0828: this
0829: .throwParseException("Non-abstract, non-native method must have a body");
0830: this .eatToken();
0831: optionalBody = null;
0832: } else {
0833: if ((modifiers & (Mod.ABSTRACT | Mod.NATIVE)) != 0)
0834: this
0835: .throwParseException("Abstract or native method must not have a body");
0836: optionalBody = this .parseMethodBody();
0837: }
0838: return new Java.MethodDeclarator(location, // location
0839: optionalDocComment, // optionalDocComment
0840: modifiers, // modifiers
0841: type, // type
0842: name, // name
0843: formalParameters, // formalParameters
0844: thrownExceptions, // thrownExceptions
0845: optionalBody // optionalBody
0846: );
0847: }
0848:
0849: /**
0850: * <pre>
0851: * VariableInitializer :=
0852: * ArrayInitializer |
0853: * Expression
0854: * </pre>
0855: */
0856: public Java.ArrayInitializerOrRvalue parseVariableInitializer()
0857: throws ParseException, Scanner.ScanException, IOException {
0858: if (this .peekOperator("{")) {
0859: return this .parseArrayInitializer();
0860: } else {
0861: return this .parseExpression().toRvalueOrPE();
0862: }
0863: }
0864:
0865: /**
0866: * <pre>
0867: * ArrayInitializer :=
0868: * '{' [ VariableInitializer { ',' VariableInitializer } [ ',' ] '}'
0869: * </pre>
0870: */
0871: public Java.ArrayInitializer parseArrayInitializer()
0872: throws ParseException, Scanner.ScanException, IOException {
0873: Location location = this .location();
0874: this .readOperator("{");
0875: List l = new ArrayList(); // ArrayInitializerOrRvalue
0876: while (!this .peekOperator("}")) {
0877: l.add(this .parseVariableInitializer());
0878: if (this .peekOperator("}"))
0879: break;
0880: if (!this .peekOperator(","))
0881: this .throwParseException("\",\" or \"}\" expected");
0882: this .eatToken();
0883: }
0884: this .eatToken();
0885: return new Java.ArrayInitializer(location,
0886: (Java.ArrayInitializerOrRvalue[]) l
0887: .toArray(new Java.ArrayInitializerOrRvalue[l
0888: .size()]));
0889: }
0890:
0891: /**
0892: * <pre>
0893: * FormalParameters := '(' [ FormalParameter { ',' FormalParameter } ] ')'
0894: * </pre>
0895: */
0896: public Java.FunctionDeclarator.FormalParameter[] parseFormalParameters()
0897: throws ParseException, Scanner.ScanException, IOException {
0898: this .readOperator("(");
0899: if (this .peekOperator(")")) {
0900: this .eatToken();
0901: return new Java.FunctionDeclarator.FormalParameter[0];
0902: }
0903:
0904: List l = new ArrayList(); // Java.FormalParameter
0905: for (;;) {
0906: l.add(this .parseFormalParameter());
0907: if (!this .peekOperator(","))
0908: break;
0909: this .eatToken();
0910: }
0911: this .readOperator(")");
0912: return (Java.FunctionDeclarator.FormalParameter[]) l
0913: .toArray(new Java.FunctionDeclarator.FormalParameter[l
0914: .size()]);
0915: }
0916:
0917: /**
0918: * <pre>
0919: * FormalParameter := [ 'final' ] Type Identifier BracketsOpt
0920: * </pre>
0921: */
0922: public Java.FunctionDeclarator.FormalParameter parseFormalParameter()
0923: throws ParseException, Scanner.ScanException, IOException {
0924: boolean finaL = this .peekKeyword("final");
0925: if (finaL)
0926: this .eatToken();
0927:
0928: Java.Type type = this .parseType();
0929:
0930: Location location = this .location();
0931: String name = this .readIdentifier();
0932: this
0933: .verifyIdentifierIsConventionalLocalVariableOrParameterName(
0934: name, location);
0935:
0936: for (int i = this .parseBracketsOpt(); i > 0; --i)
0937: type = new Java.ArrayType(type);
0938: return new Java.FunctionDeclarator.FormalParameter(location,
0939: finaL, type, name);
0940: }
0941:
0942: /**
0943: * <pre>
0944: * BracketsOpt := { '[' ']' }
0945: * </pre>
0946: */
0947: int parseBracketsOpt() throws Scanner.ScanException, IOException {
0948: int res = 0;
0949: while (this .scanner.peek().isOperator("[")
0950: && this .scanner.peekNextButOne().isOperator("]")) {
0951: this .eatToken();
0952: this .eatToken();
0953: ++res;
0954: }
0955: return res;
0956: }
0957:
0958: /**
0959: * <pre>
0960: * MethodBody := Block
0961: * </pre>
0962: */
0963: public Java.Block parseMethodBody() throws ParseException,
0964: Scanner.ScanException, IOException {
0965: return this .parseBlock();
0966: }
0967:
0968: /**
0969: * <pre>
0970: * '{' BlockStatements '}'
0971: * </pre>
0972: */
0973: public Java.Block parseBlock() throws ParseException,
0974: Scanner.ScanException, IOException {
0975: Java.Block block = new Java.Block(this .location());
0976: this .readOperator("{");
0977: block.addStatements(this .parseBlockStatements());
0978: this .readOperator("}");
0979: return block;
0980: }
0981:
0982: /**
0983: * <pre>
0984: * BlockStatements := { BlockStatement }
0985: * </pre>
0986: */
0987: public List parseBlockStatements() throws ParseException,
0988: Scanner.ScanException, IOException {
0989: List l = new ArrayList();
0990: while (!this .peekOperator("}") && !this .peekKeyword("case")
0991: && !this .peekKeyword("default"))
0992: l.add(this .parseBlockStatement());
0993: return l;
0994: }
0995:
0996: /**
0997: * <pre>
0998: * BlockStatement := { Identifier ':' } (
0999: * ( Modifiers Type | ModifiersOpt BasicType ) LocalVariableDeclarators ';' |
1000: * 'class' ... |
1001: * Statement |
1002: * 'final' Type LocalVariableDeclarators ';' |
1003: * Expression ';' |
1004: * Expression LocalVariableDeclarators ';' (1)
1005: * )
1006: * </pre>
1007: *
1008: * (1) "Expression" must pose a type, and has optional trailing brackets.
1009: */
1010: public Java.BlockStatement parseBlockStatement()
1011: throws ParseException, Scanner.ScanException, IOException {
1012:
1013: // Statement?
1014: if ((this .scanner.peek().isIdentifier() && this .scanner
1015: .peekNextButOne().isOperator(":"))
1016: || this .peekKeyword(new String[] { "if", "for",
1017: "while", "do", "try", "switch", "synchronized",
1018: "return", "throw", "break", "continue" })
1019: || this .peekOperator(new String[] { "{", ";" }))
1020: return this .parseStatement();
1021:
1022: // Local class declaration?
1023: if (this .peekKeyword("class")) {
1024: // JAVADOC[TM] ignores doc comments for local classes, but we
1025: // don't...
1026: String optionalDocComment = this .scanner.doc();
1027: if (optionalDocComment == null)
1028: this .warning("LCDCM",
1029: "Local class doc comment missing", this
1030: .location());
1031:
1032: this .eatToken();
1033: final Java.LocalClassDeclaration lcd = (Java.LocalClassDeclaration) this
1034: .parseClassDeclarationRest(optionalDocComment, // optionalDocComment
1035: (short) (Mod.FINAL | Mod.PRIVATE), // modifiers
1036: ClassDeclarationContext.BLOCK // context
1037: );
1038: return new Java.LocalClassDeclarationStatement(lcd);
1039: }
1040:
1041: // 'final' Type LocalVariableDeclarators ';'
1042: if (this .peekKeyword("final")) {
1043: Location location = this .location();
1044: this .eatToken();
1045: Java.Type variableType = this .parseType();
1046: Java.LocalVariableDeclarationStatement lvds = new Java.LocalVariableDeclarationStatement(
1047: location, // location
1048: Mod.FINAL, // modifiers
1049: variableType, // type
1050: this .parseLocalVariableDeclarators() // variableDeclarators
1051: );
1052: this .readOperator(";");
1053: return lvds;
1054: }
1055:
1056: // It's either a non-final local variable declaration or an expression statement. We can
1057: // only tell after parsing an expression.
1058: Java.Atom a = this .parseExpression();
1059:
1060: // Expression ';'
1061: if (this .peekOperator(";")) {
1062: this .eatToken();
1063: return new Java.ExpressionStatement(a.toRvalueOrPE());
1064: }
1065:
1066: // Expression LocalVariableDeclarators ';'
1067: Java.Type variableType = a.toTypeOrPE();
1068: Java.LocalVariableDeclarationStatement lvds = new Java.LocalVariableDeclarationStatement(
1069: a.getLocation(), // location
1070: Mod.NONE, // modifiers
1071: variableType, // type
1072: this .parseLocalVariableDeclarators() // variableDeclarators
1073: );
1074: this .readOperator(";");
1075: return lvds;
1076: }
1077:
1078: /**
1079: * <pre>
1080: * LocalVariableDeclarators := VariableDeclarator { ',' VariableDeclarator }
1081: * </pre>
1082: */
1083: public Java.VariableDeclarator[] parseLocalVariableDeclarators()
1084: throws ParseException, Scanner.ScanException, IOException {
1085: List l = new ArrayList();
1086: for (;;) {
1087: Java.VariableDeclarator vd = this .parseVariableDeclarator();
1088: this
1089: .verifyIdentifierIsConventionalLocalVariableOrParameterName(
1090: vd.name, vd.getLocation());
1091: l.add(vd);
1092: if (!this .peekOperator(","))
1093: break;
1094: this .eatToken();
1095: }
1096: return (Java.VariableDeclarator[]) l
1097: .toArray(new Java.VariableDeclarator[l.size()]);
1098: }
1099:
1100: /**
1101: * <pre>
1102: * FieldDeclarationRest :=
1103: * VariableDeclaratorRest
1104: * { ',' VariableDeclarator }
1105: * </pre>
1106: */
1107: public Java.VariableDeclarator[] parseFieldDeclarationRest(
1108: String name) throws ParseException, Scanner.ScanException,
1109: IOException {
1110: List l = new ArrayList();
1111:
1112: Java.VariableDeclarator vd = this
1113: .parseVariableDeclaratorRest(name);
1114: this .verifyIdentifierIsConventionalFieldName(vd.name, vd
1115: .getLocation());
1116: l.add(vd);
1117:
1118: while (this .peekOperator(",")) {
1119: this .eatToken();
1120:
1121: vd = this .parseVariableDeclarator();
1122: this .verifyIdentifierIsConventionalFieldName(vd.name, vd
1123: .getLocation());
1124: l.add(vd);
1125: }
1126: return (Java.VariableDeclarator[]) l
1127: .toArray(new Java.VariableDeclarator[l.size()]);
1128: }
1129:
1130: /**
1131: * <pre>
1132: * VariableDeclarator := Identifier VariableDeclaratorRest
1133: * </pre>
1134: */
1135: public Java.VariableDeclarator parseVariableDeclarator()
1136: throws ParseException, Scanner.ScanException, IOException {
1137: return this .parseVariableDeclaratorRest(this .readIdentifier());
1138: }
1139:
1140: /**
1141: * <pre>
1142: * VariableDeclaratorRest := { '[' ']' } [ '=' VariableInitializer ]
1143: * </pre>
1144: * Used by field declarations and local variable declarations.
1145: */
1146: public Java.VariableDeclarator parseVariableDeclaratorRest(
1147: String name) throws ParseException, Scanner.ScanException,
1148: IOException {
1149: Location loc = this .location();
1150: int brackets = this .parseBracketsOpt();
1151: Java.ArrayInitializerOrRvalue initializer = null;
1152: if (this .peekOperator("=")) {
1153: this .eatToken();
1154: initializer = this .parseVariableInitializer();
1155: }
1156: return new Java.VariableDeclarator(loc, name, brackets,
1157: initializer);
1158: }
1159:
1160: /**
1161: * <pre>
1162: * Statement :=
1163: * LabeledStatement |
1164: * Block |
1165: * IfStatement |
1166: * ForStatement |
1167: * WhileStatement |
1168: * DoStatement |
1169: * TryStatement |
1170: * 'switch' ... |
1171: * 'synchronized' ... |
1172: * ReturnStatement |
1173: * ThrowStatement |
1174: * BreakStatement |
1175: * ContinueStatement |
1176: * EmptyStatement |
1177: * ExpressionStatement
1178: * </pre>
1179: */
1180: public Java.Statement parseStatement() throws ParseException,
1181: Scanner.ScanException, IOException {
1182: if (this .scanner.peek().isIdentifier()
1183: && this .scanner.peekNextButOne().isOperator(":")) {
1184: return this .parseLabeledStatement();
1185: }
1186:
1187: Scanner.Token t = this .scanner.peek();
1188: Java.Statement stmt = (t.isOperator("{") ? this .parseBlock()
1189: : t.isKeyword("if") ? this .parseIfStatement() : t
1190: .isKeyword("for") ? this .parseForStatement()
1191: : t.isKeyword("while") ? this
1192: .parseWhileStatement() : t
1193: .isKeyword("do") ? this
1194: .parseDoStatement() : t
1195: .isKeyword("try") ? this
1196: .parseTryStatement() : t
1197: .isKeyword("switch") ? this
1198: .parseSwitchStatement() : t
1199: .isKeyword("synchronized") ? this
1200: .parseSynchronizedStatement() : t
1201: .isKeyword("return") ? this
1202: .parseReturnStatement() : t
1203: .isKeyword("throw") ? this
1204: .parseThrowStatement() : t
1205: .isKeyword("break") ? this
1206: .parseBreakStatement() : t
1207: .isKeyword("continue") ? this
1208: .parseContinueStatement() : t
1209: .isOperator(";") ? this
1210: .parseEmptyStatement() : this
1211: .parseExpressionStatement());
1212: if (stmt == null)
1213: this .throwParseException("\"" + t.getKeyword() + "\" NYI");
1214:
1215: return stmt;
1216: }
1217:
1218: /**
1219: * <pre>
1220: * LabeledStatement := Identifier ':' Statement
1221: * </pre>
1222: */
1223: public Java.Statement parseLabeledStatement()
1224: throws ParseException, Scanner.ScanException, IOException {
1225: String label = this .readIdentifier();
1226: this .readOperator(":");
1227: return new Java.LabeledStatement(this .location(), // location
1228: label, // label
1229: this .parseStatement() // body
1230: );
1231: }
1232:
1233: /**
1234: * <pre>
1235: * IfStatement := 'if' '(' Expression ')' Statement [ 'else' Statement ]
1236: * </pre>
1237: */
1238: public Java.Statement parseIfStatement() throws ParseException,
1239: Scanner.ScanException, IOException {
1240: Location location = this .location();
1241: this .readKeyword("if");
1242: this .readOperator("(");
1243: final Java.Rvalue condition = this .parseExpression()
1244: .toRvalueOrPE();
1245: this .readOperator(")");
1246:
1247: Java.Statement thenStatement = this .parseStatement();
1248:
1249: Java.Statement optionalElseStatement = null;
1250: if (this .peekKeyword("else")) {
1251: this .eatToken();
1252: optionalElseStatement = this .parseStatement();
1253: }
1254:
1255: return new Java.IfStatement(location, // location
1256: condition, // condition
1257: thenStatement, // thenStatement
1258: optionalElseStatement // optionalElseStatement
1259: );
1260: }
1261:
1262: /**
1263: * <pre>
1264: * ForStatement :=
1265: * 'for' '('
1266: * [ ForInit ] ';'
1267: * [ Expression ] ';'
1268: * [ ExpressionList ]
1269: * ')' Statement
1270: * </pre>
1271: */
1272: public Java.Statement parseForStatement() throws ParseException,
1273: Scanner.ScanException, IOException {
1274: Location location = this .location();
1275: this .readKeyword("for");
1276:
1277: this .readOperator("(");
1278:
1279: Java.BlockStatement optionalInit = null;
1280: if (!this .peekOperator(";"))
1281: optionalInit = this .parseForInit();
1282:
1283: this .readOperator(";");
1284:
1285: Java.Rvalue optionalCondition = null;
1286: if (!this .peekOperator(";"))
1287: optionalCondition = this .parseExpression().toRvalueOrPE();
1288:
1289: this .readOperator(";");
1290:
1291: Java.Rvalue[] optionalUpdate = null;
1292: if (!this .peekOperator(")"))
1293: optionalUpdate = this .parseExpressionList();
1294:
1295: this .readOperator(")");
1296:
1297: // KLUDGE: Insert an implicit Block here.
1298: // Java.Block implicitBlock = new Java.Block(location);
1299: return /*Java.ForStatement forStatement =*/new Java.ForStatement(
1300: location, // location
1301: optionalInit, // optionalInit
1302: optionalCondition, // optionalCondition
1303: optionalUpdate, // optionalUpdate
1304: this .parseStatement() // body
1305: );
1306: // implicitBlock.addStatement(forStatement);
1307:
1308: // return implicitBlock;
1309: }
1310:
1311: /**
1312: * <pre>
1313: * ForInit :=
1314: * Modifiers Type LocalVariableDeclarators |
1315: * ModifiersOpt BasicType LocalVariableDeclarators |
1316: * Expression (
1317: * LocalVariableDeclarators | (1)
1318: * { ',' Expression }
1319: * )
1320: * </pre>
1321: *
1322: * (1) "Expression" must pose a type.
1323: */
1324: private Java.BlockStatement parseForInit() throws ParseException,
1325: Scanner.ScanException, IOException {
1326:
1327: // Modifiers Type LocalVariableDeclarators
1328: // ModifiersOpt BasicType LocalVariableDeclarators
1329: if (this .peekKeyword(new String[] { "final", "byte", "short",
1330: "char", "int", "long", "float", "double", "boolean" })) {
1331: short modifiers = this .parseModifiersOpt();
1332: Java.Type variableType = this .parseType();
1333: return new Java.LocalVariableDeclarationStatement(this
1334: .location(), // location
1335: modifiers, // modifiers
1336: variableType, // type
1337: this .parseLocalVariableDeclarators() // variableDeclarators
1338: );
1339: }
1340:
1341: Java.Atom a = this .parseExpression();
1342:
1343: // Expression LocalVariableDeclarators
1344: if (this .scanner.peek().isIdentifier()) {
1345: Java.Type variableType = a.toTypeOrPE();
1346: return new Java.LocalVariableDeclarationStatement(a
1347: .getLocation(), // location
1348: Mod.NONE, // modifiers
1349: variableType, // type
1350: this .parseLocalVariableDeclarators() // variableDeclarators
1351: );
1352: }
1353:
1354: // Expression { ',' Expression }
1355: if (!this .peekOperator(",")) {
1356: return new Java.ExpressionStatement(a.toRvalueOrPE());
1357: }
1358: this .eatToken();
1359: List l = new ArrayList();
1360: l.add(new Java.ExpressionStatement(a.toRvalueOrPE()));
1361: for (;;) {
1362: l.add(new Java.ExpressionStatement(this .parseExpression()
1363: .toRvalueOrPE()));
1364: if (!this .peekOperator(","))
1365: break;
1366: this .eatToken();
1367: }
1368:
1369: Java.Block b = new Java.Block(a.getLocation());
1370: b.addStatements(l);
1371: return b;
1372: }
1373:
1374: /**
1375: * <pre>
1376: * WhileStatement := 'while' '(' Expression ')' Statement
1377: * </pre>
1378: */
1379: public Java.Statement parseWhileStatement() throws ParseException,
1380: Scanner.ScanException, IOException {
1381: Location location = this .location();
1382: this .readKeyword("while");
1383:
1384: this .readOperator("(");
1385: Java.Rvalue condition = this .parseExpression().toRvalueOrPE();
1386: this .readOperator(")");
1387:
1388: return new Java.WhileStatement(location, // location
1389: condition, // condition
1390: this .parseStatement() // body
1391: );
1392: }
1393:
1394: /**
1395: * <pre>
1396: * DoStatement := 'do' Statement 'while' '(' Expression ')' ';'
1397: * </pre>
1398: */
1399: public Java.Statement parseDoStatement() throws ParseException,
1400: Scanner.ScanException, IOException {
1401: Location location = this .location();
1402: this .readKeyword("do");
1403:
1404: Java.Statement body = this .parseStatement();
1405:
1406: this .readKeyword("while");
1407: this .readOperator("(");
1408: Java.Rvalue condition = this .parseExpression().toRvalueOrPE();
1409: this .readOperator(")");
1410: this .readOperator(";");
1411:
1412: return new Java.DoStatement(location, // location
1413: body, // body
1414: condition // condition
1415: );
1416: }
1417:
1418: /**
1419: * <pre>
1420: * TryStatement :=
1421: * 'try' Block Catches [ Finally ] |
1422: * 'try' Block Finally
1423: *
1424: * Catches := CatchClause { CatchClause }
1425: *
1426: * CatchClause := 'catch' '(' FormalParameter ')' Block
1427: *
1428: * Finally := 'finally' Block
1429: * </pre>
1430: */
1431: public Java.Statement parseTryStatement() throws ParseException,
1432: Scanner.ScanException, IOException {
1433: Location location = this .location();
1434: this .readKeyword("try");
1435:
1436: Java.Block body = this .parseBlock();
1437:
1438: // { CatchClause }
1439: List ccs = new ArrayList();
1440: while (this .peekKeyword("catch")) {
1441: Location loc = this .location();
1442: this .eatToken();
1443: this .readOperator("(");
1444: Java.FunctionDeclarator.FormalParameter caughtException = this
1445: .parseFormalParameter();
1446: this .readOperator(")");
1447: ccs.add(new Java.CatchClause(loc, // location
1448: caughtException, // caughtException
1449: this .parseBlock() // body
1450: ));
1451: }
1452: Java.Block optionalFinally = null;
1453: if (this .peekKeyword("finally")) {
1454: this .eatToken();
1455: optionalFinally = this .parseBlock();
1456: }
1457: if (ccs.size() == 0 && optionalFinally == null)
1458: this
1459: .throwParseException("\"try\" statement must have at least one \"catch\" clause or a \"finally\" clause");
1460:
1461: return new Java.TryStatement(location, // location
1462: body, // body
1463: ccs, // catchClauses
1464: optionalFinally // optionalFinally
1465: );
1466: }
1467:
1468: /**
1469: * <pre>
1470: * SwitchStatement :=
1471: * 'switch' '(' Expression ')' '{' { SwitchLabels BlockStatements } '}'
1472: *
1473: * SwitchLabels := SwitchLabels { SwitchLabels }
1474: *
1475: * SwitchLabel := 'case' Expression ':' | 'default' ':'
1476: * </pre>
1477: */
1478: public Java.Statement parseSwitchStatement() throws ParseException,
1479: Scanner.ScanException, IOException {
1480: Location location = this .location();
1481: this .readKeyword("switch");
1482:
1483: this .readOperator("(");
1484: Java.Rvalue condition = this .parseExpression().toRvalueOrPE();
1485: this .readOperator(")");
1486:
1487: this .readOperator("{");
1488: List sbsgs = new ArrayList();
1489: while (!this .peekOperator("}")) {
1490: Location location2 = this .location();
1491: boolean hasDefaultLabel = false;
1492: List caseLabels = new ArrayList();
1493: do {
1494: if (this .peekKeyword("case")) {
1495: this .eatToken();
1496: caseLabels.add(this .parseExpression()
1497: .toRvalueOrPE());
1498: } else if (this .peekKeyword("default")) {
1499: this .eatToken();
1500: if (hasDefaultLabel)
1501: this
1502: .throwParseException("Duplicate \"default\" label");
1503: hasDefaultLabel = true;
1504: } else {
1505: this
1506: .throwParseException("\"case\" or \"default\" expected");
1507: }
1508: this .readOperator(":");
1509: } while (this
1510: .peekKeyword(new String[] { "case", "default" }));
1511:
1512: Java.SwitchStatement.SwitchBlockStatementGroup sbsg = new Java.SwitchStatement.SwitchBlockStatementGroup(
1513: location2, // location
1514: caseLabels, // caseLabels
1515: hasDefaultLabel, // hasDefaultLabel
1516: this .parseBlockStatements() // blockStatements
1517: );
1518: sbsgs.add(sbsg);
1519: }
1520: this .eatToken();
1521: return new Java.SwitchStatement(location, // location
1522: condition, // condition
1523: sbsgs // sbsgs
1524: );
1525: }
1526:
1527: /**
1528: * <pre>
1529: * SynchronizedStatement :=
1530: * 'synchronized' '(' expression ')' Block
1531: * </pre>
1532: */
1533: public Java.Statement parseSynchronizedStatement()
1534: throws ParseException, Scanner.ScanException, IOException {
1535: Location location = this .location();
1536: this .readKeyword("synchronized");
1537: this .readOperator("(");
1538: Java.Rvalue expression = this .parseExpression().toRvalueOrPE();
1539: this .readOperator(")");
1540: return new Java.SynchronizedStatement(location, // location
1541: expression, // expression
1542: this .parseBlock() // body
1543: );
1544: }
1545:
1546: /**
1547: * <pre>
1548: * ReturnStatement := 'return' [ Expression ] ';'
1549: * </pre>
1550: */
1551: public Java.Statement parseReturnStatement() throws ParseException,
1552: Scanner.ScanException, IOException {
1553: Location location = this .location();
1554: this .readKeyword("return");
1555: Java.Rvalue returnValue = this .peekOperator(";") ? null : this
1556: .parseExpression().toRvalueOrPE();
1557: this .readOperator(";");
1558: return new Java.ReturnStatement(location, returnValue);
1559: }
1560:
1561: /**
1562: * <pre>
1563: * ThrowStatement := 'throw' Expression ';'
1564: * </pre>
1565: */
1566: public Java.Statement parseThrowStatement() throws ParseException,
1567: Scanner.ScanException, IOException {
1568: Location location = this .location();
1569: this .readKeyword("throw");
1570: final Java.Rvalue expression = this .parseExpression()
1571: .toRvalueOrPE();
1572: this .readOperator(";");
1573:
1574: return new Java.ThrowStatement(location, expression);
1575: }
1576:
1577: /**
1578: * <pre>
1579: * BreakStatement := 'break' [ Identifier ] ';'
1580: * </pre>
1581: */
1582: public Java.Statement parseBreakStatement() throws ParseException,
1583: Scanner.ScanException, IOException {
1584: Location location = this .location();
1585: this .readKeyword("break");
1586: String optionalLabel = null;
1587: if (this .scanner.peek().isIdentifier())
1588: optionalLabel = this .readIdentifier();
1589: this .readOperator(";");
1590: return new Java.BreakStatement(location, optionalLabel);
1591: }
1592:
1593: /**
1594: * <pre>
1595: * ContinueStatement := 'continue' [ Identifier ] ';'
1596: * </pre>
1597: */
1598: public Java.Statement parseContinueStatement()
1599: throws ParseException, Scanner.ScanException, IOException {
1600: Location location = this .location();
1601: this .readKeyword("continue");
1602: String optionalLabel = null;
1603: if (this .scanner.peek().isIdentifier())
1604: optionalLabel = this .readIdentifier();
1605: this .readOperator(";");
1606: return new Java.ContinueStatement(location, optionalLabel);
1607: }
1608:
1609: /**
1610: * <pre>
1611: * EmptyStatement := ';'
1612: * </pre>
1613: */
1614: public Java.Statement parseEmptyStatement() throws ParseException,
1615: Scanner.ScanException, IOException {
1616: Location location = this .location();
1617: this .readOperator(";");
1618: return new Java.EmptyStatement(location);
1619: }
1620:
1621: /**
1622: * <pre>
1623: * ExpressionList := Expression { ',' Expression }
1624: * </pre>
1625: */
1626: public Java.Rvalue[] parseExpressionList() throws ParseException,
1627: Scanner.ScanException, IOException {
1628: List l = new ArrayList();
1629: for (;;) {
1630: l.add(this .parseExpression().toRvalueOrPE());
1631: if (!this .peekOperator(","))
1632: break;
1633: this .eatToken();
1634: }
1635: return (Java.Rvalue[]) l.toArray(new Java.Rvalue[l.size()]);
1636: }
1637:
1638: /**
1639: * <pre>
1640: * Type := (
1641: * 'byte' | 'short' | 'char' | 'int' | 'long' |
1642: * 'float' | 'double' | 'boolean' |
1643: * ReferenceType
1644: * ) { '[' ']' }
1645: * </pre>
1646: */
1647: public Java.Type parseType() throws ParseException,
1648: Scanner.ScanException, IOException {
1649: Scanner.Token t = this .scanner.peek();
1650: int bt = -1;
1651: if (t.isKeyword("byte")) {
1652: bt = Java.BasicType.BYTE;
1653: } else if (t.isKeyword("short")) {
1654: bt = Java.BasicType.SHORT;
1655: } else if (t.isKeyword("char")) {
1656: bt = Java.BasicType.CHAR;
1657: } else if (t.isKeyword("int")) {
1658: bt = Java.BasicType.INT;
1659: } else if (t.isKeyword("long")) {
1660: bt = Java.BasicType.LONG;
1661: } else if (t.isKeyword("float")) {
1662: bt = Java.BasicType.FLOAT;
1663: } else if (t.isKeyword("double")) {
1664: bt = Java.BasicType.DOUBLE;
1665: } else if (t.isKeyword("boolean")) {
1666: bt = Java.BasicType.BOOLEAN;
1667: }
1668: Java.Type res;
1669: if (bt != -1) {
1670: res = new Java.BasicType(t.getLocation(), bt);
1671: this .eatToken();
1672: } else {
1673: res = this .parseReferenceType();
1674: }
1675: for (int i = this .parseBracketsOpt(); i > 0; --i)
1676: res = new Java.ArrayType(res);
1677: return res;
1678: }
1679:
1680: /**
1681: * <pre>
1682: * ReferenceType := QualifiedIdentifier
1683: * </pre>
1684: */
1685: public Java.ReferenceType parseReferenceType()
1686: throws ParseException, Scanner.ScanException, IOException {
1687: return new Java.ReferenceType(this .location(), // location
1688: this .parseQualifiedIdentifier() // identifiers
1689: );
1690: }
1691:
1692: /**
1693: * <pre>
1694: * ReferenceTypeList := ReferenceType { ',' ReferenceType }
1695: * </pre>
1696: */
1697: public Java.ReferenceType[] parseReferenceTypeList()
1698: throws ParseException, Scanner.ScanException, IOException {
1699: List l = new ArrayList();
1700: l.add(this .parseReferenceType());
1701: while (this .peekOperator(",")) {
1702: this .eatToken();
1703: l.add(this .parseReferenceType());
1704: }
1705: return (Java.ReferenceType[]) l
1706: .toArray(new Java.ReferenceType[l.size()]);
1707: }
1708:
1709: /**
1710: * <pre>
1711: * Expression := AssignmentExpression
1712: * </pre>
1713: */
1714: public Java.Atom parseExpression() throws ParseException,
1715: Scanner.ScanException, IOException {
1716: return this .parseAssignmentExpression();
1717: }
1718:
1719: /**
1720: * <pre>
1721: * AssignmentExpression :=
1722: * ConditionalExpression [ AssignmentOperator AssignmentExpression ]
1723: *
1724: * AssignmentOperator :=
1725: * '=' | '*=' | '/=' | '%=' | '+=' | '-=' | '<<=' |
1726: * '>>=' | '>>>=' | '&=' | '^=' | '|='
1727: * </pre>
1728: */
1729: public Java.Atom parseAssignmentExpression() throws ParseException,
1730: Scanner.ScanException, IOException {
1731: Java.Atom a = this .parseConditionalExpression();
1732: if (this .peekOperator(new String[] { "=", "+=", "-=", "*=",
1733: "/=", "&=", "|=", "^=", "%=", "<<=", ">>=", ">>>=" })) {
1734: Location location = this .location();
1735: String operator = this .readOperator();
1736: final Java.Lvalue lhs = a.toLvalueOrPE();
1737: final Java.Rvalue rhs = this .parseAssignmentExpression()
1738: .toRvalueOrPE();
1739: return new Java.Assignment(location, lhs, operator, rhs);
1740: }
1741: return a;
1742: }
1743:
1744: /**
1745: * <pre>
1746: * ConditionalExpression :=
1747: * ConditionalOrExpression [ '?' Expression ':' ConditionalExpression ]
1748: * </pre>
1749: */
1750: public Java.Atom parseConditionalExpression()
1751: throws ParseException, Scanner.ScanException, IOException {
1752: Java.Atom a = this .parseConditionalOrExpression();
1753: if (!this .peekOperator("?"))
1754: return a;
1755: Location location = this .location();
1756: this .eatToken();
1757:
1758: Java.Rvalue lhs = a.toRvalueOrPE();
1759: Java.Rvalue mhs = this .parseExpression().toRvalueOrPE();
1760: this .readOperator(":");
1761: Java.Rvalue rhs = this .parseConditionalExpression()
1762: .toRvalueOrPE();
1763: return new Java.ConditionalExpression(location, lhs, mhs, rhs);
1764: }
1765:
1766: /**
1767: * <pre>
1768: * ConditionalOrExpression :=
1769: * ConditionalAndExpression { '||' ConditionalAndExpression ]
1770: * </pre>
1771: */
1772: public Java.Atom parseConditionalOrExpression()
1773: throws ParseException, Scanner.ScanException, IOException {
1774: Java.Atom a = this .parseConditionalAndExpression();
1775: while (this .peekOperator("||")) {
1776: Location location = this .location();
1777: this .eatToken();
1778: a = new Java.BinaryOperation(location, a.toRvalueOrPE(),
1779: "||", this .parseConditionalAndExpression()
1780: .toRvalueOrPE());
1781: }
1782: return a;
1783: }
1784:
1785: /**
1786: * <pre>
1787: * ConditionalAndExpression :=
1788: * InclusiveOrExpression { '&&' InclusiveOrExpression }
1789: * </pre>
1790: */
1791: public Java.Atom parseConditionalAndExpression()
1792: throws ParseException, Scanner.ScanException, IOException {
1793: Java.Atom a = this .parseInclusiveOrExpression();
1794: while (this .peekOperator("&&")) {
1795: Location location = this .location();
1796: this .eatToken();
1797: a = new Java.BinaryOperation(location, a.toRvalueOrPE(),
1798: "&&", this .parseInclusiveOrExpression()
1799: .toRvalueOrPE());
1800: }
1801: return a;
1802: }
1803:
1804: /**
1805: * <pre>
1806: * InclusiveOrExpression :=
1807: * ExclusiveOrExpression { '|' ExclusiveOrExpression }
1808: * </pre>
1809: */
1810: public Java.Atom parseInclusiveOrExpression()
1811: throws ParseException, Scanner.ScanException, IOException {
1812: Java.Atom a = this .parseExclusiveOrExpression();
1813: while (this .peekOperator("|")) {
1814: Location location = this .location();
1815: this .eatToken();
1816: a = new Java.BinaryOperation(location, a.toRvalueOrPE(),
1817: "|", this .parseExclusiveOrExpression()
1818: .toRvalueOrPE());
1819: }
1820: return a;
1821: }
1822:
1823: /**
1824: * <pre>
1825: * ExclusiveOrExpression :=
1826: * AndExpression { '^' AndExpression }
1827: * </pre>
1828: */
1829: public Java.Atom parseExclusiveOrExpression()
1830: throws ParseException, Scanner.ScanException, IOException {
1831: Java.Atom a = this .parseAndExpression();
1832: while (this .peekOperator("^")) {
1833: Location location = this .location();
1834: this .eatToken();
1835: a = new Java.BinaryOperation(location, a.toRvalueOrPE(),
1836: "^", this .parseAndExpression().toRvalueOrPE());
1837: }
1838: return a;
1839: }
1840:
1841: /**
1842: * <pre>
1843: * AndExpression :=
1844: * EqualityExpression { '&' EqualityExpression }
1845: * </pre>
1846: */
1847: public Java.Atom parseAndExpression() throws ParseException,
1848: Scanner.ScanException, IOException {
1849: Java.Atom a = this .parseEqualityExpression();
1850: while (this .peekOperator("&")) {
1851: Location location = this .location();
1852: this .eatToken();
1853: a = new Java.BinaryOperation(location, a.toRvalueOrPE(),
1854: "&", this .parseEqualityExpression().toRvalueOrPE());
1855: }
1856: return a;
1857: }
1858:
1859: /**
1860: * <pre>
1861: * EqualityExpression :=
1862: * RelationalExpression { ( '==' | '!=' ) RelationalExpression }
1863: * </pre>
1864: */
1865: public Java.Atom parseEqualityExpression() throws ParseException,
1866: Scanner.ScanException, IOException {
1867: Java.Atom a = this .parseRelationalExpression();
1868:
1869: while (this .peekOperator(new String[] { "==", "!=" })) {
1870: a = new Java.BinaryOperation(this .location(), // location
1871: a.toRvalueOrPE(), // lhs
1872: this .readOperator(), // op
1873: this .parseRelationalExpression().toRvalueOrPE() // rhs
1874: );
1875: }
1876: return a;
1877: }
1878:
1879: /**
1880: * <pre>
1881: * RelationalExpression :=
1882: * ShiftExpression {
1883: * ( ( '<' | '>' | '<=' | '>=' ) ShiftExpression ) |
1884: * ( 'instanceof' ReferenceType )
1885: * }
1886: * </pre>
1887: */
1888: public Java.Atom parseRelationalExpression() throws ParseException,
1889: Scanner.ScanException, IOException {
1890: Java.Atom a = this .parseShiftExpression();
1891:
1892: for (;;) {
1893: if (this .peekKeyword("instanceof")) {
1894: Location location = this .location();
1895: this .eatToken();
1896: a = new Java.Instanceof(location, a.toRvalueOrPE(),
1897: this .parseType());
1898: } else if (this .peekOperator(new String[] { "<", ">", "<=",
1899: ">=" })) {
1900: a = new Java.BinaryOperation(this .location(), // location
1901: a.toRvalueOrPE(), // lhs
1902: this .readOperator(), // op
1903: this .parseShiftExpression().toRvalueOrPE() // rhs
1904: );
1905: } else {
1906: return a;
1907: }
1908: }
1909: }
1910:
1911: /**
1912: * <pre>
1913: * ShiftExpression :=
1914: * AdditiveExpression { ( '<<' | '>>' | '>>>' ) AdditiveExpression }
1915: * </pre>
1916: */
1917: public Java.Atom parseShiftExpression() throws ParseException,
1918: Scanner.ScanException, IOException {
1919: Java.Atom a = this .parseAdditiveExpression();
1920:
1921: while (this .peekOperator(new String[] { "<<", ">>", ">>>" })) {
1922: a = new Java.BinaryOperation(this .location(), // location
1923: a.toRvalueOrPE(), // lhs
1924: this .readOperator(), // op
1925: this .parseAdditiveExpression().toRvalueOrPE() // rhs
1926: );
1927: }
1928: return a;
1929: }
1930:
1931: /**
1932: * <pre>
1933: * AdditiveExpression :=
1934: * MultiplicativeExpression { ( '+' | '-' ) MultiplicativeExpression }
1935: * </pre>
1936: */
1937: public Java.Atom parseAdditiveExpression() throws ParseException,
1938: Scanner.ScanException, IOException {
1939: Java.Atom a = this .parseMultiplicativeExpression();
1940:
1941: while (this .peekOperator(new String[] { "+", "-" })) {
1942: a = new Java.BinaryOperation(this .location(), // location
1943: a.toRvalueOrPE(), // lhs
1944: this .readOperator(), // op
1945: this .parseMultiplicativeExpression().toRvalueOrPE() // rhs
1946: );
1947: }
1948: return a;
1949: }
1950:
1951: /**
1952: * <pre>
1953: * MultiplicativeExpression :=
1954: * UnaryExpression { ( '*' | '/' | '%' ) UnaryExpression }
1955: * </pre>
1956: */
1957: public Java.Atom parseMultiplicativeExpression()
1958: throws ParseException, Scanner.ScanException, IOException {
1959: Java.Atom a = this .parseUnaryExpression();
1960:
1961: while (this .peekOperator(new String[] { "*", "/", "%" })) {
1962: a = new Java.BinaryOperation(this .location(), // location
1963: a.toRvalueOrPE(), // lhs
1964: this .readOperator(), // op
1965: this .parseUnaryExpression().toRvalueOrPE() // rhs
1966: );
1967: }
1968: return a;
1969: }
1970:
1971: /**
1972: * <pre>
1973: * UnaryExpression :=
1974: * { PrefixOperator } Primary { Selector } { PostfixOperator }
1975: *
1976: * PrefixOperator := '++' | '--' | '+' | '-' | '~' | '!'
1977: *
1978: * PostfixOperator := '++' | '--'
1979: * </pre>
1980: */
1981: public Java.Atom parseUnaryExpression() throws ParseException,
1982: Scanner.ScanException, IOException {
1983: if (this .peekOperator(new String[] { "++", "--" })) {
1984: return new Java.Crement(this .location(), // location
1985: this .readOperator(), // operator
1986: this .parseUnaryExpression().toLvalueOrPE() // operand
1987: );
1988: }
1989:
1990: if (this .peekOperator(new String[] { "+", "-", "~", "!" })) {
1991: return new Java.UnaryOperation(this .location(), // location
1992: this .readOperator(), // operator
1993: this .parseUnaryExpression().toRvalueOrPE() // operand
1994: );
1995: }
1996:
1997: Java.Atom a = this .parsePrimary();
1998:
1999: while (this .peekOperator(new String[] { ".", "[" })) {
2000: a = this .parseSelector(a);
2001: }
2002:
2003: while (this .peekOperator(new String[] { "++", "--" })) {
2004: a = new Java.Crement(this .location(), // location
2005: a.toLvalueOrPE(), // operand
2006: this .readOperator() // operator
2007: );
2008: }
2009:
2010: return a;
2011: }
2012:
2013: /**
2014: * <pre>
2015: * Primary :=
2016: * CastExpression | // CastExpression 15.16
2017: * '(' Expression ')' | // ParenthesizedExpression 15.8.5
2018: * Literal | // Literal 15.8.1
2019: * Name | // AmbiguousName
2020: * Name Arguments | // MethodInvocation
2021: * Name '[]' { '[]' } | // ArrayType 10.1
2022: * Name '[]' { '[]' } '.' 'class' | // ClassLiteral 15.8.2
2023: * 'this' | // This 15.8.3
2024: * 'this' Arguments | // Alternate constructor invocation 8.8.5.1
2025: * 'super' Arguments | // Unqualified superclass constructor invocation 8.8.5.1
2026: * 'super' '.' Identifier | // SuperclassFieldAccess 15.11.2
2027: * 'super' '.' Identifier Arguments | // SuperclassMethodInvocation 15.12.4.9
2028: * NewClassInstance |
2029: * NewAnonymousClassInstance | // ClassInstanceCreationExpression 15.9
2030: * NewArray | // ArrayCreationExpression 15.10
2031: * NewInitializedArray | // ArrayInitializer 10.6
2032: * BasicType { '[]' } | // Type
2033: * BasicType { '[]' } '.' 'class' | // ClassLiteral 15.8.2
2034: * 'void' '.' 'class' // ClassLiteral 15.8.2
2035: *
2036: * CastExpression :=
2037: * '(' PrimitiveType { '[]' } ')' UnaryExpression |
2038: * '(' Expression ')' UnaryExpression
2039: *
2040: * NewClassInstance := 'new' ReferenceType Arguments
2041: *
2042: * NewAnonymousClassInstance := 'new' ReferenceType Arguments [ ClassBody ]
2043: *
2044: * NewArray := 'new' Type DimExprs { '[]' }
2045: *
2046: * NewInitializedArray := 'new' ArrayType ArrayInitializer
2047: * </pre>
2048: */
2049: public Java.Atom parsePrimary() throws ParseException,
2050: Scanner.ScanException, IOException {
2051: if (this .peekOperator("(")) {
2052: this .eatToken();
2053: if (this
2054: .peekKeyword(new String[] { "boolean", "char",
2055: "byte", "short", "int", "long", "float",
2056: "double", })) {
2057: // '(' PrimitiveType { '[]' } ')' UnaryExpression
2058: Java.Type type = this .parseType();
2059: int brackets = this .parseBracketsOpt();
2060: this .readOperator(")");
2061: for (int i = 0; i < brackets; ++i)
2062: type = new Java.ArrayType(type);
2063: return new Java.Cast(this .location(), // location
2064: type, // targetType
2065: this .parseUnaryExpression().toRvalueOrPE() // value
2066: );
2067: }
2068: Java.Atom a = this .parseExpression();
2069: this .readOperator(")");
2070:
2071: if (this .scanner.peek().isLiteral()
2072: || this .scanner.peek().isIdentifier()
2073: || this
2074: .peekOperator(new String[] { "(", "~", "!", })
2075: || this .peekKeyword(new String[] { "this", "super",
2076: "new", })) {
2077: // '(' Expression ')' UnaryExpression
2078: return new Java.Cast(this .location(), // location
2079: a.toTypeOrPE(), // targetType
2080: this .parseUnaryExpression().toRvalueOrPE() // value
2081: );
2082: }
2083:
2084: // '(' Expression ')'
2085: return new Java.ParenthesizedExpression(a.getLocation(), a
2086: .toRvalueOrPE());
2087: }
2088:
2089: if (this .scanner.peek().isLiteral()) {
2090: // Literal
2091: return this .parseLiteral();
2092: }
2093:
2094: if (this .scanner.peek().isIdentifier()) {
2095: Location location = this .location();
2096: String[] qi = this .parseQualifiedIdentifier();
2097: if (this .peekOperator("(")) {
2098: // Name Arguments
2099: return new Java.MethodInvocation(this .location(), // location
2100: qi.length == 1 ? null : new Java.AmbiguousName( // optionalTarget
2101: location, // location
2102: qi, // identifiers
2103: qi.length - 1 // n
2104: ), qi[qi.length - 1], // methodName
2105: this .parseArguments() // arguments
2106: );
2107: }
2108: if (this .peekOperator("[")
2109: && this .scanner.peekNextButOne().isOperator("]")) {
2110: // Name '[]' { '[]' }
2111: // Name '[]' { '[]' } '.' 'class'
2112: Java.Type res = new Java.ReferenceType(location, // location
2113: qi // identifiers
2114: );
2115: int brackets = this .parseBracketsOpt();
2116: for (int i = 0; i < brackets; ++i)
2117: res = new Java.ArrayType(res);
2118: if (this .peekOperator(".")
2119: && this .scanner.peekNextButOne().isKeyword(
2120: "class")) {
2121: this .eatToken();
2122: Location location2 = this .location();
2123: this .eatToken();
2124: return new Java.ClassLiteral(location2, res);
2125: } else {
2126: return res;
2127: }
2128: }
2129: // Name
2130: return new Java.AmbiguousName(this .location(), // location
2131: qi // identifiers
2132: );
2133: }
2134:
2135: if (this .peekKeyword("this")) {
2136: Location location = this .location();
2137: this .eatToken();
2138: if (this .peekOperator("(")) {
2139:
2140: // 'this' Arguments
2141: // Alternate constructor invocation (JLS 8.8.5.1)
2142: return new Java.AlternateConstructorInvocation(
2143: location, // location
2144: this .parseArguments() // arguments
2145: );
2146: } else {
2147:
2148: // 'this'
2149: return new Java.ThisReference(location);
2150: }
2151: }
2152:
2153: if (this .peekKeyword("super")) {
2154: this .eatToken();
2155: if (this .peekOperator("(")) {
2156:
2157: // 'super' Arguments
2158: // Unqualified superclass constructor invocation (JLS 8.8.5.1)
2159: return new Java.SuperConstructorInvocation(this
2160: .location(), // location
2161: (Java.Rvalue) null, // optionalQualification
2162: this .parseArguments() // arguments
2163: );
2164: }
2165: this .readOperator(".");
2166: String name = this .readIdentifier();
2167: if (this .peekOperator("(")) {
2168:
2169: // 'super' '.' Identifier Arguments
2170: return new Java.SuperclassMethodInvocation(this
2171: .location(), // location
2172: name, // methodName
2173: this .parseArguments() // arguments
2174: );
2175: } else {
2176:
2177: // 'super' '.' Identifier
2178: return new Java.SuperclassFieldAccessExpression(this
2179: .location(), // location
2180: (Java.Type) null, // optionalQualification
2181: name // fieldName
2182: );
2183: }
2184: }
2185:
2186: // 'new'
2187: if (this .peekKeyword("new")) {
2188: Location location = this .location();
2189: this .eatToken();
2190: Java.Type type = this .parseType();
2191: if (type instanceof Java.ArrayType) {
2192: // 'new' ArrayType ArrayInitializer
2193: return new Java.NewInitializedArray(location,
2194: (Java.ArrayType) type, this
2195: .parseArrayInitializer());
2196: }
2197: if (type instanceof Java.ReferenceType
2198: && this .peekOperator("(")) {
2199: // 'new' ReferenceType Arguments [ ClassBody ]
2200: Java.Rvalue[] arguments = this .parseArguments();
2201: if (this .peekOperator("{")) {
2202: // 'new' ReferenceType Arguments ClassBody
2203: final Java.AnonymousClassDeclaration anonymousClassDeclaration = new Java.AnonymousClassDeclaration(
2204: this .location(), // location
2205: type // baseType
2206: );
2207: this .parseClassBody(anonymousClassDeclaration);
2208: return new Java.NewAnonymousClassInstance(location, // location
2209: (Java.Rvalue) null, // optionalQualification
2210: anonymousClassDeclaration, // anonymousClassDeclaration
2211: arguments // arguments
2212: );
2213: } else {
2214: // 'new' ReferenceType Arguments
2215: return new Java.NewClassInstance(location, // location
2216: (Java.Rvalue) null, // optionalQualification
2217: type, // type
2218: arguments // arguments
2219: );
2220: }
2221: }
2222: // 'new' Type DimExprs { '[]' }
2223: return new Java.NewArray(location, // location
2224: type, // type
2225: this .parseDimExprs(), // dimExprs
2226: this .parseBracketsOpt() // dims
2227: );
2228: }
2229:
2230: // BasicType
2231: if (this .peekKeyword(new String[] { "boolean", "char", "byte",
2232: "short", "int", "long", "float", "double", })) {
2233: Java.Type res = this .parseType();
2234: int brackets = this .parseBracketsOpt();
2235: for (int i = 0; i < brackets; ++i)
2236: res = new Java.ArrayType(res);
2237: if (this .peekOperator(".")
2238: && this .scanner.peekNextButOne().isKeyword("class")) {
2239: // BasicType { '[]' } '.' 'class'
2240: this .eatToken();
2241: Location location = this .location();
2242: this .eatToken();
2243: return new Java.ClassLiteral(location, res);
2244: }
2245: // BasicType { '[]' }
2246: return res;
2247: }
2248:
2249: // 'void'
2250: if (this .peekKeyword("void")) {
2251: this .eatToken();
2252: if (this .peekOperator(".")
2253: && this .scanner.peekNextButOne().isKeyword("class")) {
2254: // 'void' '.' 'class'
2255: this .eatToken();
2256: Location location = this .location();
2257: this .eatToken();
2258: return new Java.ClassLiteral(location,
2259: new Java.BasicType(location,
2260: Java.BasicType.VOID));
2261: }
2262: this
2263: .throwParseException("\"void\" encountered in wrong context");
2264: }
2265:
2266: this .throwParseException("Unexpected token \""
2267: + this .scanner.peek() + "\" in primary");
2268: /* NEVER REACHED */return null;
2269: }
2270:
2271: /**
2272: * <pre>
2273: * Selector :=
2274: * '.' Identifier | // FieldAccess 15.11.1
2275: * '.' Identifier Arguments | // MethodInvocation
2276: * '.' 'this' // QualifiedThis 15.8.4
2277: * '.' 'super' Arguments // Qualified superclass constructor invocation (JLS 8.8.5.1)
2278: * '.' 'super' '.' Identifier | // SuperclassFieldReference (JLS 15.11.2)
2279: * '.' 'super' '.' Identifier Arguments | // SuperclassMethodInvocation (JLS 15.12.4.9)
2280: * '.' 'new' Identifier Arguments [ ClassBody ] | // QualifiedClassInstanceCreationExpression 15.9
2281: * '.' 'class'
2282: * '[' Expression ']' // ArrayAccessExpression 15.13
2283: * </pre>
2284: */
2285: public Java.Atom parseSelector(Java.Atom atom)
2286: throws ParseException, Scanner.ScanException, IOException {
2287: if (this .peekOperator(".")) {
2288: this .eatToken();
2289: if (this .scanner.peek().isIdentifier()) {
2290: String identifier = this .readIdentifier();
2291: if (this .peekOperator("(")) {
2292: // '.' Identifier Arguments
2293: return new Java.MethodInvocation(this .location(), // location
2294: atom.toRvalueOrPE(), // optionalTarget
2295: identifier, // methodName
2296: this .parseArguments() // arguments
2297: );
2298: }
2299: // '.' Identifier
2300: return new Java.FieldAccessExpression(this .location(), // location
2301: atom.toRvalueOrPE(), // lhs
2302: identifier // fieldName
2303: );
2304: }
2305: if (this .peekKeyword("this")) {
2306: // '.' 'this'
2307: Location location = this .location();
2308: this .eatToken();
2309: return new Java.QualifiedThisReference(location, // location
2310: atom.toTypeOrPE() // qualification
2311: );
2312: }
2313: if (this .peekKeyword("super")) {
2314: Location location = this .location();
2315: this .eatToken();
2316: if (this .peekOperator("(")) {
2317:
2318: // '.' 'super' Arguments
2319: // Qualified superclass constructor invocation (JLS 8.8.5.1) (LHS is an Rvalue)
2320: return new Java.SuperConstructorInvocation(
2321: location, // location
2322: atom.toRvalueOrPE(), // optionalQualification
2323: this .parseArguments() // arguments
2324: );
2325: }
2326: this .readOperator(".");
2327: String identifier = this .readIdentifier();
2328:
2329: if (this .peekOperator("(")) {
2330:
2331: // '.' 'super' '.' Identifier Arguments
2332: // Qualified superclass method invocation (JLS 15.12) (LHS is a ClassName)
2333: // TODO: Qualified superclass method invocation
2334: this
2335: .throwParseException("Qualified superclass method invocation NYI");
2336: } else {
2337:
2338: // '.' 'super' '.' Identifier
2339: // Qualified superclass field access (JLS 15.11.2) (LHS is an Rvalue)
2340: return new Java.SuperclassFieldAccessExpression(
2341: location, // location
2342: atom.toTypeOrPE(), // optionalQualification
2343: identifier // fieldName
2344: );
2345: }
2346: }
2347: if (this .peekKeyword("new")) {
2348: // '.' 'new' Identifier Arguments [ ClassBody ]
2349: Java.Rvalue lhs = atom.toRvalue();
2350: Location location = this .location();
2351: this .eatToken();
2352: String identifier = this .readIdentifier();
2353: Java.Type type = new Java.RvalueMemberType(location, // location
2354: lhs, // rValue
2355: identifier // identifier
2356: );
2357: Java.Rvalue[] arguments = this .parseArguments();
2358: if (this .peekOperator("{")) {
2359: // '.' 'new' Identifier Arguments ClassBody (LHS is an Rvalue)
2360: final Java.AnonymousClassDeclaration anonymousClassDeclaration = new Java.AnonymousClassDeclaration(
2361: this .location(), // location
2362: type // baseType
2363: );
2364: this .parseClassBody(anonymousClassDeclaration);
2365: return new Java.NewAnonymousClassInstance(location, // location
2366: lhs, // optionalQualification
2367: anonymousClassDeclaration, // anonymousClassDeclaration
2368: arguments // arguments
2369: );
2370: } else {
2371: // '.' 'new' Identifier Arguments (LHS is an Rvalue)
2372: return new Java.NewClassInstance(location, // location
2373: lhs, // optionalQualification
2374: type, // referenceType
2375: arguments // arguments
2376: );
2377: }
2378: }
2379: if (this .peekKeyword("class")) {
2380: // '.' 'class'
2381: Location location = this .location();
2382: this .eatToken();
2383: return new Java.ClassLiteral(location, atom
2384: .toTypeOrPE());
2385: }
2386: this .throwParseException("Unexpected selector \""
2387: + this .scanner.peek() + "\" after \".\"");
2388: }
2389: if (this .peekOperator("[")) {
2390: // '[' Expression ']'
2391: Location location = this .location();
2392: this .eatToken();
2393: Java.Rvalue index = this .parseExpression().toRvalueOrPE();
2394: this .readOperator("]");
2395: return new Java.ArrayAccessExpression(location, // location
2396: atom.toRvalueOrPE(), // lhs
2397: index // index
2398: );
2399: }
2400: this .throwParseException("Unexpected token \""
2401: + this .scanner.peek() + "\" in selector");
2402: /* NEVER REACHED */return null;
2403: }
2404:
2405: /**
2406: * <pre>
2407: * DimExprs := DimExpr { DimExpr }
2408: * </pre>
2409: */
2410: public Java.Rvalue[] parseDimExprs() throws ParseException,
2411: Scanner.ScanException, IOException {
2412: List l = new ArrayList();
2413: l.add(this .parseDimExpr());
2414: while (this .peekOperator("[")
2415: && !this .scanner.peekNextButOne().isOperator("]"))
2416: l.add(this .parseDimExpr());
2417: return (Java.Rvalue[]) l.toArray(new Java.Rvalue[l.size()]);
2418: }
2419:
2420: /**
2421: * <pre>
2422: * DimExpr := '[' Expression ']'
2423: * </pre>
2424: */
2425: public Java.Rvalue parseDimExpr() throws Scanner.ScanException,
2426: ParseException, IOException {
2427: this .readOperator("[");
2428: Java.Rvalue res = this .parseExpression().toRvalueOrPE();
2429: this .readOperator("]");
2430: return res;
2431: }
2432:
2433: /**
2434: * <pre>
2435: * Arguments := '(' [ ArgumentList ] ')'
2436: * </pre>
2437: */
2438: public Java.Rvalue[] parseArguments() throws ParseException,
2439: Scanner.ScanException, IOException {
2440: this .readOperator("(");
2441: if (this .peekOperator(")")) {
2442: this .eatToken();
2443: return new Java.Rvalue[0];
2444: }
2445: Java.Rvalue[] arguments = this .parseArgumentList();
2446: this .readOperator(")");
2447: return arguments;
2448: }
2449:
2450: /**
2451: * <pre>
2452: * ArgumentList := Expression { ',' Expression }
2453: * </pre>
2454: */
2455: public Java.Rvalue[] parseArgumentList() throws ParseException,
2456: Scanner.ScanException, IOException {
2457: List l = new ArrayList();
2458: for (;;) {
2459: l.add(this .parseExpression().toRvalueOrPE());
2460: if (!this .peekOperator(","))
2461: break;
2462: this .eatToken();
2463: }
2464: return (Java.Rvalue[]) l.toArray(new Java.Rvalue[l.size()]);
2465: }
2466:
2467: public Java.Atom parseLiteral() throws ParseException,
2468: Scanner.ScanException, IOException {
2469: Scanner.Token t = this .scanner.read();
2470: if (!t.isLiteral())
2471: this .throwParseException("Literal expected");
2472: return new Java.Literal(t.getLocation(), t.getLiteralValue());
2473: }
2474:
2475: // Simplified access to the scanner.
2476:
2477: public Location location() {
2478: return this .scanner.location();
2479: }
2480:
2481: public void eatToken() throws Scanner.ScanException, IOException {
2482: this .scanner.read();
2483: }
2484:
2485: // Keyword-related.
2486: public boolean peekKeyword() {
2487: return this .scanner.peek().isKeyword();
2488: }
2489:
2490: public boolean peekKeyword(String keyword) {
2491: return this .scanner.peek().isKeyword(keyword);
2492: }
2493:
2494: public boolean peekKeyword(String[] keywords) {
2495: return this .scanner.peek().isKeyword(keywords);
2496: }
2497:
2498: public void readKeyword(String keyword) throws ParseException,
2499: Scanner.ScanException, IOException {
2500: if (!this .scanner.read().isKeyword(keyword))
2501: this .throwParseException("\"" + keyword + "\" expected"); // We want a ParseException, not a ScanException
2502: }
2503:
2504: // Operator-related.
2505: public boolean peekOperator(String operator) {
2506: return this .scanner.peek().isOperator(operator);
2507: }
2508:
2509: public boolean peekOperator(String[] operators) {
2510: return this .scanner.peek().isOperator(operators);
2511: }
2512:
2513: public String readOperator() throws ParseException,
2514: Scanner.ScanException, IOException {
2515: Scanner.Token t = this .scanner.read();
2516: if (!t.isOperator())
2517: this .throwParseException("Operator expected"); // We want a ParseException, not a ScanException
2518: return t.getOperator();
2519: }
2520:
2521: public void readOperator(String operator) throws ParseException,
2522: Scanner.ScanException, IOException {
2523: if (!this .scanner.read().isOperator(operator))
2524: this .throwParseException("Operator \"" + operator
2525: + "\" expected"); // We want a ParseException, not a ScanException
2526: }
2527:
2528: // Identifier-related.
2529: public boolean peekIdentifier() {
2530: return this .scanner.peek().isIdentifier();
2531: }
2532:
2533: public String readIdentifier() throws ParseException,
2534: Scanner.ScanException, IOException {
2535: Scanner.Token t = this .scanner.read();
2536: if (!t.isIdentifier())
2537: this .throwParseException("Identifier expected"); // We want a ParseException, not a ScanException
2538: return t.getIdentifier();
2539: }
2540:
2541: /**
2542: * <pre>
2543: * ExpressionStatement := Expression ';'
2544: * </pre>
2545: */
2546: public Java.Statement parseExpressionStatement()
2547: throws ParseException, Scanner.ScanException, IOException {
2548: Java.Rvalue rv = this .parseExpression().toRvalueOrPE();
2549: this .readOperator(";");
2550:
2551: return new Java.ExpressionStatement(rv);
2552: }
2553:
2554: /**
2555: * Issue a warning if the given string does not comply with the package naming conventions
2556: * (JLS2 6.8.1).
2557: */
2558: private void verifyStringIsConventionalPackageName(String s,
2559: Location loc) {
2560: if (!Character.isLowerCase(s.charAt(0))) {
2561: this
2562: .warning(
2563: "UPN",
2564: "Package name \""
2565: + s
2566: + "\" does not begin with a lower-case letter (see JLS2 6.8.1)",
2567: loc);
2568: return;
2569: }
2570:
2571: for (int i = 0; i < s.length(); ++i) {
2572: char c = s.charAt(i);
2573: if (!Character.isLowerCase(c) && c != '_' && c != '.') {
2574: this .warning("PPN", "Poorly chosen package name \"" + s
2575: + "\" contains bad character '" + c + "'", loc);
2576: return;
2577: }
2578: }
2579: }
2580:
2581: /**
2582: * Issue a warning if the given identifier does not comply with the class and interface type
2583: * naming conventions (JLS2 6.8.2).
2584: */
2585: private void verifyIdentifierIsConventionalClassOrInterfaceName(
2586: String id, Location loc) {
2587: if (!Character.isUpperCase(id.charAt(0))) {
2588: this
2589: .warning(
2590: "UCOIN1",
2591: "Class or interface name \""
2592: + id
2593: + "\" does not begin with an upper-case letter (see JLS2 6.8.2)",
2594: loc);
2595: return;
2596: }
2597: for (int i = 0; i < id.length(); ++i) {
2598: char c = id.charAt(i);
2599: if (!Character.isLetter(c) && !Character.isDigit(c)) {
2600: this .warning("UCOIN", "Class or interface name \"" + id
2601: + "\" contains unconventional character \"" + c
2602: + "\" (see JLS2 6.8.2)", loc);
2603: return;
2604: }
2605: }
2606: }
2607:
2608: /**
2609: * Issue a warning if the given identifier does not comply with the method naming conventions
2610: * (JLS2 6.8.3).
2611: */
2612: private void verifyIdentifierIsConventionalMethodName(String id,
2613: Location loc) {
2614: if (!Character.isLowerCase(id.charAt(0))) {
2615: this
2616: .warning(
2617: "UMN1",
2618: "Method name \""
2619: + id
2620: + "\" does not begin with a lower-case letter (see JLS2 6.8.3)",
2621: loc);
2622: return;
2623: }
2624: for (int i = 0; i < id.length(); ++i) {
2625: char c = id.charAt(i);
2626: if (!Character.isLetter(c) && !Character.isDigit(c)) {
2627: this .warning("UMN", "Method name \"" + id
2628: + "\" contains unconventional character \"" + c
2629: + "\" (see JLS2 6.8.3)", loc);
2630: return;
2631: }
2632: }
2633: }
2634:
2635: /**
2636: * Issue a warning if the given identifier does not comply with the field naming conventions
2637: * (JLS2 6.8.4) and constant naming conventions (JLS2 6.8.5).
2638: */
2639: private void verifyIdentifierIsConventionalFieldName(String id,
2640: Location loc) {
2641:
2642: // In practice, a field is not always a constant iff it is static-final. So let's
2643: // always tolerate both field and constant names.
2644:
2645: if (Character.isUpperCase(id.charAt(0))) {
2646: for (int i = 0; i < id.length(); ++i) {
2647: char c = id.charAt(i);
2648: if (!Character.isUpperCase(c) && !Character.isDigit(c)
2649: && c != '_') {
2650: this .warning("UCN", "Constant name \"" + id
2651: + "\" contains unconventional character \""
2652: + c + "\" (see JLS2 6.8.5)", loc);
2653: return;
2654: }
2655: }
2656: } else if (Character.isLowerCase(id.charAt(0))) {
2657: for (int i = 0; i < id.length(); ++i) {
2658: char c = id.charAt(i);
2659: if (!Character.isLetter(c) && !Character.isDigit(c)) {
2660: this .warning("UFN", "Field name \"" + id
2661: + "\" contains unconventional character \""
2662: + c + "\" (see JLS2 6.8.4)", loc);
2663: return;
2664: }
2665: }
2666: } else {
2667: this
2668: .warning(
2669: "UFN1",
2670: "\""
2671: + id
2672: + "\" is neither a conventional field name (JLS2 6.8.4) nor a conventional constant name (JLS2 6.8.5)",
2673: loc);
2674: }
2675: }
2676:
2677: /**
2678: * Issue a warning if the given identifier does not comply with the local variable and
2679: * parameter naming conventions (JLS2 6.8.6).
2680: */
2681: private void verifyIdentifierIsConventionalLocalVariableOrParameterName(
2682: String id, Location loc) {
2683: if (!Character.isLowerCase(id.charAt(0))) {
2684: this
2685: .warning(
2686: "ULVN1",
2687: "Local variable name \""
2688: + id
2689: + "\" does not begin with a lower-case letter (see JLS2 6.8.6)",
2690: loc);
2691: return;
2692: }
2693: for (int i = 0; i < id.length(); ++i) {
2694: char c = id.charAt(i);
2695: if (!Character.isLetter(c) && !Character.isDigit(c)) {
2696: this .warning("ULVN", "Local variable name \"" + id
2697: + "\" contains unconventional character \"" + c
2698: + "\" (see JLS2 6.8.6)", loc);
2699: return;
2700: }
2701: }
2702: }
2703:
2704: /**
2705: * By default, warnings are discarded, but an application my install a
2706: * {@link WarningHandler}.
2707: * <p>
2708: * Notice that there is no <code>Parser.setErrorHandler()</code> method, but parse errors
2709: * always throw a {@link ParseException}. The reason being is that there is no reasonable
2710: * way to recover from parse errors and continue parsing, so there is no need to install
2711: * a custom parse error handler.
2712: *
2713: * @param optionalWarningHandler <code>null</code> to indicate that no warnings be issued
2714: */
2715: public void setWarningHandler(WarningHandler optionalWarningHandler) {
2716: this .optionalWarningHandler = optionalWarningHandler;
2717: }
2718:
2719: // Used for elaborate warning handling.
2720: private WarningHandler optionalWarningHandler = null;
2721:
2722: /**
2723: * Issues a warning with the given message and location and returns. This is done through
2724: * a {@link WarningHandler} that was installed through
2725: * {@link #setWarningHandler(WarningHandler)}.
2726: * <p>
2727: * The <code>handle</code> argument qulifies the warning and is typically used by
2728: * the {@link WarningHandler} to suppress individual warnings.
2729: */
2730: private void warning(String handle, String message,
2731: Location optionalLocation) {
2732: if (this .optionalWarningHandler != null)
2733: this .optionalWarningHandler.handleWarning(handle, message,
2734: optionalLocation);
2735: }
2736:
2737: /**
2738: * An exception that reflects an error during parsing.
2739: *
2740: * This exception is associated with a particular {@link Location
2741: * Location} in the source code.
2742: */
2743: public static class ParseException extends LocatedException {
2744: public ParseException(String message, Location location) {
2745: super (message, location);
2746: }
2747: }
2748:
2749: /**
2750: * Convenience method for throwing a ParseException.
2751: */
2752: protected final void throwParseException(String message)
2753: throws ParseException {
2754: throw new ParseException(message, this .location());
2755: }
2756:
2757: private static String join(String[] sa, String separator) {
2758: if (sa == null)
2759: return ("(null)");
2760: if (sa.length == 0)
2761: return ("(zero length array)");
2762: StringBuffer sb = new StringBuffer(sa[0]);
2763: for (int i = 1; i < sa.length; ++i) {
2764: sb.append(separator).append(sa[i]);
2765: }
2766: return sb.toString();
2767: }
2768: }
|