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