diff options
Diffstat (limited to 'src/main/javassist/compiler/Parser.java')
-rw-r--r-- | src/main/javassist/compiler/Parser.java | 1342 |
1 files changed, 1342 insertions, 0 deletions
diff --git a/src/main/javassist/compiler/Parser.java b/src/main/javassist/compiler/Parser.java new file mode 100644 index 0000000..d483814 --- /dev/null +++ b/src/main/javassist/compiler/Parser.java @@ -0,0 +1,1342 @@ +/* + * Javassist, a Java-bytecode translator toolkit. + * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. Alternatively, the contents of this file may be used under + * the terms of the GNU Lesser General Public License Version 2.1 or later. + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + */ + +package javassist.compiler; + +import javassist.compiler.ast.*; + +public final class Parser implements TokenId { + private Lex lex; + + public Parser(Lex lex) { + this.lex = lex; + } + + public boolean hasMore() { return lex.lookAhead() >= 0; } + + /* member.declaration + * : method.declaration | field.declaration + */ + public ASTList parseMember(SymbolTable tbl) throws CompileError { + ASTList mem = parseMember1(tbl); + if (mem instanceof MethodDecl) + return parseMethod2(tbl, (MethodDecl)mem); + else + return mem; + } + + /* A method body is not parsed. + */ + public ASTList parseMember1(SymbolTable tbl) throws CompileError { + ASTList mods = parseMemberMods(); + Declarator d; + boolean isConstructor = false; + if (lex.lookAhead() == Identifier && lex.lookAhead(1) == '(') { + d = new Declarator(VOID, 0); + isConstructor = true; + } + else + d = parseFormalType(tbl); + + if (lex.get() != Identifier) + throw new SyntaxError(lex); + + String name; + if (isConstructor) + name = MethodDecl.initName; + else + name = lex.getString(); + + d.setVariable(new Symbol(name)); + if (isConstructor || lex.lookAhead() == '(') + return parseMethod1(tbl, isConstructor, mods, d); + else + return parseField(tbl, mods, d); + } + + /* field.declaration + * : member.modifiers + * formal.type Identifier + * [ "=" expression ] ";" + */ + private FieldDecl parseField(SymbolTable tbl, ASTList mods, + Declarator d) throws CompileError + { + ASTree expr = null; + if (lex.lookAhead() == '=') { + lex.get(); + expr = parseExpression(tbl); + } + + int c = lex.get(); + if (c == ';') + return new FieldDecl(mods, new ASTList(d, new ASTList(expr))); + else if (c == ',') + throw new CompileError( + "only one field can be declared in one declaration", lex); + else + throw new SyntaxError(lex); + } + + /* method.declaration + * : member.modifiers + * [ formal.type ] + * Identifier "(" [ formal.parameter ( "," formal.parameter )* ] ")" + * array.dimension + * [ THROWS class.type ( "," class.type ) ] + * ( block.statement | ";" ) + * + * Note that a method body is not parsed. + */ + private MethodDecl parseMethod1(SymbolTable tbl, boolean isConstructor, + ASTList mods, Declarator d) + throws CompileError + { + if (lex.get() != '(') + throw new SyntaxError(lex); + + ASTList parms = null; + if (lex.lookAhead() != ')') + while (true) { + parms = ASTList.append(parms, parseFormalParam(tbl)); + int t = lex.lookAhead(); + if (t == ',') + lex.get(); + else if (t == ')') + break; + } + + lex.get(); // ')' + d.addArrayDim(parseArrayDimension()); + if (isConstructor && d.getArrayDim() > 0) + throw new SyntaxError(lex); + + ASTList throwsList = null; + if (lex.lookAhead() == THROWS) { + lex.get(); + while (true) { + throwsList = ASTList.append(throwsList, parseClassType(tbl)); + if (lex.lookAhead() == ',') + lex.get(); + else + break; + } + } + + return new MethodDecl(mods, new ASTList(d, + ASTList.make(parms, throwsList, null))); + } + + /* Parses a method body. + */ + public MethodDecl parseMethod2(SymbolTable tbl, MethodDecl md) + throws CompileError + { + Stmnt body = null; + if (lex.lookAhead() == ';') + lex.get(); + else { + body = parseBlock(tbl); + if (body == null) + body = new Stmnt(BLOCK); + } + + md.sublist(4).setHead(body); + return md; + } + + /* member.modifiers + * : ( FINAL | SYNCHRONIZED | ABSTRACT + * | PUBLIC | PROTECTED | PRIVATE | STATIC + * | VOLATILE | TRANSIENT | STRICT )* + */ + private ASTList parseMemberMods() { + int t; + ASTList list = null; + while (true) { + t = lex.lookAhead(); + if (t == ABSTRACT || t == FINAL || t == PUBLIC || t == PROTECTED + || t == PRIVATE || t == SYNCHRONIZED || t == STATIC + || t == VOLATILE || t == TRANSIENT || t == STRICT) + list = new ASTList(new Keyword(lex.get()), list); + else + break; + } + + return list; + } + + /* formal.type : ( build-in-type | class.type ) array.dimension + */ + private Declarator parseFormalType(SymbolTable tbl) throws CompileError { + int t = lex.lookAhead(); + if (isBuiltinType(t) || t == VOID) { + lex.get(); // primitive type + int dim = parseArrayDimension(); + return new Declarator(t, dim); + } + else { + ASTList name = parseClassType(tbl); + int dim = parseArrayDimension(); + return new Declarator(name, dim); + } + } + + private static boolean isBuiltinType(int t) { + return (t == BOOLEAN || t == BYTE || t == CHAR || t == SHORT + || t == INT || t == LONG || t == FLOAT || t == DOUBLE); + } + + /* formal.parameter : formal.type Identifier array.dimension + */ + private Declarator parseFormalParam(SymbolTable tbl) + throws CompileError + { + Declarator d = parseFormalType(tbl); + if (lex.get() != Identifier) + throw new SyntaxError(lex); + + String name = lex.getString(); + d.setVariable(new Symbol(name)); + d.addArrayDim(parseArrayDimension()); + tbl.append(name, d); + return d; + } + + /* statement : [ label ":" ]* labeled.statement + * + * labeled.statement + * : block.statement + * | if.statement + * | while.statement + * | do.statement + * | for.statement + * | switch.statement + * | try.statement + * | return.statement + * | thorw.statement + * | break.statement + * | continue.statement + * | declaration.or.expression + * | ";" + * + * This method may return null (empty statement). + */ + public Stmnt parseStatement(SymbolTable tbl) + throws CompileError + { + int t = lex.lookAhead(); + if (t == '{') + return parseBlock(tbl); + else if (t == ';') { + lex.get(); + return new Stmnt(BLOCK); // empty statement + } + else if (t == Identifier && lex.lookAhead(1) == ':') { + lex.get(); // Identifier + String label = lex.getString(); + lex.get(); // ':' + return Stmnt.make(LABEL, new Symbol(label), parseStatement(tbl)); + } + else if (t == IF) + return parseIf(tbl); + else if (t == WHILE) + return parseWhile(tbl); + else if (t == DO) + return parseDo(tbl); + else if (t == FOR) + return parseFor(tbl); + else if (t == TRY) + return parseTry(tbl); + else if (t == SWITCH) + return parseSwitch(tbl); + else if (t == SYNCHRONIZED) + return parseSynchronized(tbl); + else if (t == RETURN) + return parseReturn(tbl); + else if (t == THROW) + return parseThrow(tbl); + else if (t == BREAK) + return parseBreak(tbl); + else if (t == CONTINUE) + return parseContinue(tbl); + else + return parseDeclarationOrExpression(tbl, false); + } + + /* block.statement : "{" statement* "}" + */ + private Stmnt parseBlock(SymbolTable tbl) throws CompileError { + if (lex.get() != '{') + throw new SyntaxError(lex); + + Stmnt body = null; + SymbolTable tbl2 = new SymbolTable(tbl); + while (lex.lookAhead() != '}') { + Stmnt s = parseStatement(tbl2); + if (s != null) + body = (Stmnt)ASTList.concat(body, new Stmnt(BLOCK, s)); + } + + lex.get(); // '}' + if (body == null) + return new Stmnt(BLOCK); // empty block + else + return body; + } + + /* if.statement : IF "(" expression ")" statement + * [ ELSE statement ] + */ + private Stmnt parseIf(SymbolTable tbl) throws CompileError { + int t = lex.get(); // IF + ASTree expr = parseParExpression(tbl); + Stmnt thenp = parseStatement(tbl); + Stmnt elsep; + if (lex.lookAhead() == ELSE) { + lex.get(); + elsep = parseStatement(tbl); + } + else + elsep = null; + + return new Stmnt(t, expr, new ASTList(thenp, new ASTList(elsep))); + } + + /* while.statement : WHILE "(" expression ")" statement + */ + private Stmnt parseWhile(SymbolTable tbl) + throws CompileError + { + int t = lex.get(); // WHILE + ASTree expr = parseParExpression(tbl); + Stmnt body = parseStatement(tbl); + return new Stmnt(t, expr, body); + } + + /* do.statement : DO statement WHILE "(" expression ")" ";" + */ + private Stmnt parseDo(SymbolTable tbl) throws CompileError { + int t = lex.get(); // DO + Stmnt body = parseStatement(tbl); + if (lex.get() != WHILE || lex.get() != '(') + throw new SyntaxError(lex); + + ASTree expr = parseExpression(tbl); + if (lex.get() != ')' || lex.get() != ';') + throw new SyntaxError(lex); + + return new Stmnt(t, expr, body); + } + + /* for.statement : FOR "(" decl.or.expr expression ";" expression ")" + * statement + */ + private Stmnt parseFor(SymbolTable tbl) throws CompileError { + Stmnt expr1, expr3; + ASTree expr2; + int t = lex.get(); // FOR + + SymbolTable tbl2 = new SymbolTable(tbl); + + if (lex.get() != '(') + throw new SyntaxError(lex); + + if (lex.lookAhead() == ';') { + lex.get(); + expr1 = null; + } + else + expr1 = parseDeclarationOrExpression(tbl2, true); + + if (lex.lookAhead() == ';') + expr2 = null; + else + expr2 = parseExpression(tbl2); + + if (lex.get() != ';') + throw new CompileError("; is missing", lex); + + if (lex.lookAhead() == ')') + expr3 = null; + else + expr3 = parseExprList(tbl2); + + if (lex.get() != ')') + throw new CompileError(") is missing", lex); + + Stmnt body = parseStatement(tbl2); + return new Stmnt(t, expr1, new ASTList(expr2, + new ASTList(expr3, body))); + } + + /* switch.statement : SWITCH "(" expression ")" "{" switch.block "}" + * + * swtich.block : ( switch.label statement* )* + * + * swtich.label : DEFAULT ":" + * | CASE const.expression ":" + */ + private Stmnt parseSwitch(SymbolTable tbl) throws CompileError { + int t = lex.get(); // SWITCH + ASTree expr = parseParExpression(tbl); + Stmnt body = parseSwitchBlock(tbl); + return new Stmnt(t, expr, body); + } + + private Stmnt parseSwitchBlock(SymbolTable tbl) throws CompileError { + if (lex.get() != '{') + throw new SyntaxError(lex); + + SymbolTable tbl2 = new SymbolTable(tbl); + Stmnt s = parseStmntOrCase(tbl2); + if (s == null) + throw new CompileError("empty switch block", lex); + + int op = s.getOperator(); + if (op != CASE && op != DEFAULT) + throw new CompileError("no case or default in a switch block", + lex); + + Stmnt body = new Stmnt(BLOCK, s); + while (lex.lookAhead() != '}') { + Stmnt s2 = parseStmntOrCase(tbl2); + if (s2 != null) { + int op2 = s2.getOperator(); + if (op2 == CASE || op2 == DEFAULT) { + body = (Stmnt)ASTList.concat(body, new Stmnt(BLOCK, s2)); + s = s2; + } + else + s = (Stmnt)ASTList.concat(s, new Stmnt(BLOCK, s2)); + } + } + + lex.get(); // '}' + return body; + } + + private Stmnt parseStmntOrCase(SymbolTable tbl) throws CompileError { + int t = lex.lookAhead(); + if (t != CASE && t != DEFAULT) + return parseStatement(tbl); + + lex.get(); + Stmnt s; + if (t == CASE) + s = new Stmnt(t, parseExpression(tbl)); + else + s = new Stmnt(DEFAULT); + + if (lex.get() != ':') + throw new CompileError(": is missing", lex); + + return s; + } + + /* synchronized.statement : + * SYNCHRONIZED "(" expression ")" block.statement + */ + private Stmnt parseSynchronized(SymbolTable tbl) throws CompileError { + int t = lex.get(); // SYNCHRONIZED + if (lex.get() != '(') + throw new SyntaxError(lex); + + ASTree expr = parseExpression(tbl); + if (lex.get() != ')') + throw new SyntaxError(lex); + + Stmnt body = parseBlock(tbl); + return new Stmnt(t, expr, body); + } + + /* try.statement + * : TRY block.statement + * [ CATCH "(" class.type Identifier ")" block.statement ]* + * [ FINALLY block.statement ]* + */ + private Stmnt parseTry(SymbolTable tbl) throws CompileError { + lex.get(); // TRY + Stmnt block = parseBlock(tbl); + ASTList catchList = null; + while (lex.lookAhead() == CATCH) { + lex.get(); // CATCH + if (lex.get() != '(') + throw new SyntaxError(lex); + + SymbolTable tbl2 = new SymbolTable(tbl); + Declarator d = parseFormalParam(tbl2); + if (d.getArrayDim() > 0 || d.getType() != CLASS) + throw new SyntaxError(lex); + + if (lex.get() != ')') + throw new SyntaxError(lex); + + Stmnt b = parseBlock(tbl2); + catchList = ASTList.append(catchList, new Pair(d, b)); + } + + Stmnt finallyBlock = null; + if (lex.lookAhead() == FINALLY) { + lex.get(); // FINALLY + finallyBlock = parseBlock(tbl); + } + + return Stmnt.make(TRY, block, catchList, finallyBlock); + } + + /* return.statement : RETURN [ expression ] ";" + */ + private Stmnt parseReturn(SymbolTable tbl) throws CompileError { + int t = lex.get(); // RETURN + Stmnt s = new Stmnt(t); + if (lex.lookAhead() != ';') + s.setLeft(parseExpression(tbl)); + + if (lex.get() != ';') + throw new CompileError("; is missing", lex); + + return s; + } + + /* throw.statement : THROW expression ";" + */ + private Stmnt parseThrow(SymbolTable tbl) throws CompileError { + int t = lex.get(); // THROW + ASTree expr = parseExpression(tbl); + if (lex.get() != ';') + throw new CompileError("; is missing", lex); + + return new Stmnt(t, expr); + } + + /* break.statement : BREAK [ Identifier ] ";" + */ + private Stmnt parseBreak(SymbolTable tbl) + throws CompileError + { + return parseContinue(tbl); + } + + /* continue.statement : CONTINUE [ Identifier ] ";" + */ + private Stmnt parseContinue(SymbolTable tbl) + throws CompileError + { + int t = lex.get(); // CONTINUE + Stmnt s = new Stmnt(t); + int t2 = lex.get(); + if (t2 == Identifier) { + s.setLeft(new Symbol(lex.getString())); + t2 = lex.get(); + } + + if (t2 != ';') + throw new CompileError("; is missing", lex); + + return s; + } + + /* declaration.or.expression + * : [ FINAL ] built-in-type array.dimension declarators + * | [ FINAL ] class.type array.dimension declarators + * | expression ';' + * | expr.list ';' if exprList is true + * + * Note: FINAL is currently ignored. This must be fixed + * in future. + */ + private Stmnt parseDeclarationOrExpression(SymbolTable tbl, + boolean exprList) + throws CompileError + { + int t = lex.lookAhead(); + while (t == FINAL) { + lex.get(); + t = lex.lookAhead(); + } + + if (isBuiltinType(t)) { + t = lex.get(); + int dim = parseArrayDimension(); + return parseDeclarators(tbl, new Declarator(t, dim)); + } + else if (t == Identifier) { + int i = nextIsClassType(0); + if (i >= 0) + if (lex.lookAhead(i) == Identifier) { + ASTList name = parseClassType(tbl); + int dim = parseArrayDimension(); + return parseDeclarators(tbl, new Declarator(name, dim)); + } + } + + Stmnt expr; + if (exprList) + expr = parseExprList(tbl); + else + expr = new Stmnt(EXPR, parseExpression(tbl)); + + if (lex.get() != ';') + throw new CompileError("; is missing", lex); + + return expr; + } + + /* expr.list : ( expression ',')* expression + */ + private Stmnt parseExprList(SymbolTable tbl) throws CompileError { + Stmnt expr = null; + for (;;) { + Stmnt e = new Stmnt(EXPR, parseExpression(tbl)); + expr = (Stmnt)ASTList.concat(expr, new Stmnt(BLOCK, e)); + if (lex.lookAhead() == ',') + lex.get(); + else + return expr; + } + } + + /* declarators : declarator [ ',' declarator ]* ';' + */ + private Stmnt parseDeclarators(SymbolTable tbl, Declarator d) + throws CompileError + { + Stmnt decl = null; + for (;;) { + decl = (Stmnt)ASTList.concat(decl, + new Stmnt(DECL, parseDeclarator(tbl, d))); + int t = lex.get(); + if (t == ';') + return decl; + else if (t != ',') + throw new CompileError("; is missing", lex); + } + } + + /* declarator : Identifier array.dimension [ '=' initializer ] + */ + private Declarator parseDeclarator(SymbolTable tbl, Declarator d) + throws CompileError + { + if (lex.get() != Identifier || d.getType() == VOID) + throw new SyntaxError(lex); + + String name = lex.getString(); + Symbol symbol = new Symbol(name); + int dim = parseArrayDimension(); + ASTree init = null; + if (lex.lookAhead() == '=') { + lex.get(); + init = parseInitializer(tbl); + } + + Declarator decl = d.make(symbol, dim, init); + tbl.append(name, decl); + return decl; + } + + /* initializer : expression | array.initializer + */ + private ASTree parseInitializer(SymbolTable tbl) throws CompileError { + if (lex.lookAhead() == '{') + return parseArrayInitializer(tbl); + else + return parseExpression(tbl); + } + + /* array.initializer : + * '{' (( array.initializer | expression ) ',')* '}' + */ + private ArrayInit parseArrayInitializer(SymbolTable tbl) + throws CompileError + { + lex.get(); // '{' + ASTree expr = parseExpression(tbl); + ArrayInit init = new ArrayInit(expr); + while (lex.lookAhead() == ',') { + lex.get(); + expr = parseExpression(tbl); + ASTList.append(init, expr); + } + + if (lex.get() != '}') + throw new SyntaxError(lex); + + return init; + } + + /* par.expression : '(' expression ')' + */ + private ASTree parseParExpression(SymbolTable tbl) throws CompileError { + if (lex.get() != '(') + throw new SyntaxError(lex); + + ASTree expr = parseExpression(tbl); + if (lex.get() != ')') + throw new SyntaxError(lex); + + return expr; + } + + /* expression : conditional.expr + * | conditional.expr assign.op expression (right-to-left) + */ + public ASTree parseExpression(SymbolTable tbl) throws CompileError { + ASTree left = parseConditionalExpr(tbl); + if (!isAssignOp(lex.lookAhead())) + return left; + + int t = lex.get(); + ASTree right = parseExpression(tbl); + return AssignExpr.makeAssign(t, left, right); + } + + private static boolean isAssignOp(int t) { + return t == '=' || t == MOD_E || t == AND_E + || t == MUL_E || t == PLUS_E || t == MINUS_E || t == DIV_E + || t == EXOR_E || t == OR_E || t == LSHIFT_E + || t == RSHIFT_E || t == ARSHIFT_E; + } + + /* conditional.expr (right-to-left) + * : logical.or.expr [ '?' expression ':' conditional.expr ] + */ + private ASTree parseConditionalExpr(SymbolTable tbl) throws CompileError { + ASTree cond = parseBinaryExpr(tbl); + if (lex.lookAhead() == '?') { + lex.get(); + ASTree thenExpr = parseExpression(tbl); + if (lex.get() != ':') + throw new CompileError(": is missing", lex); + + ASTree elseExpr = parseExpression(tbl); + return new CondExpr(cond, thenExpr, elseExpr); + } + else + return cond; + } + + /* logical.or.expr 10 (operator precedence) + * : logical.and.expr + * | logical.or.expr OROR logical.and.expr left-to-right + * + * logical.and.expr 9 + * : inclusive.or.expr + * | logical.and.expr ANDAND inclusive.or.expr + * + * inclusive.or.expr 8 + * : exclusive.or.expr + * | inclusive.or.expr "|" exclusive.or.expr + * + * exclusive.or.expr 7 + * : and.expr + * | exclusive.or.expr "^" and.expr + * + * and.expr 6 + * : equality.expr + * | and.expr "&" equality.expr + * + * equality.expr 5 + * : relational.expr + * | equality.expr (EQ | NEQ) relational.expr + * + * relational.expr 4 + * : shift.expr + * | relational.expr (LE | GE | "<" | ">") shift.expr + * | relational.expr INSTANCEOF class.type ("[" "]")* + * + * shift.expr 3 + * : additive.expr + * | shift.expr (LSHIFT | RSHIFT | ARSHIFT) additive.expr + * + * additive.expr 2 + * : multiply.expr + * | additive.expr ("+" | "-") multiply.expr + * + * multiply.expr 1 + * : unary.expr + * | multiply.expr ("*" | "/" | "%") unary.expr + */ + private ASTree parseBinaryExpr(SymbolTable tbl) throws CompileError { + ASTree expr = parseUnaryExpr(tbl); + for (;;) { + int t = lex.lookAhead(); + int p = getOpPrecedence(t); + if (p == 0) + return expr; + else + expr = binaryExpr2(tbl, expr, p); + } + } + + private ASTree parseInstanceOf(SymbolTable tbl, ASTree expr) + throws CompileError + { + int t = lex.lookAhead(); + if (isBuiltinType(t)) { + lex.get(); // primitive type + int dim = parseArrayDimension(); + return new InstanceOfExpr(t, dim, expr); + } + else { + ASTList name = parseClassType(tbl); + int dim = parseArrayDimension(); + return new InstanceOfExpr(name, dim, expr); + } + } + + private ASTree binaryExpr2(SymbolTable tbl, ASTree expr, int prec) + throws CompileError + { + int t = lex.get(); + if (t == INSTANCEOF) + return parseInstanceOf(tbl, expr); + + ASTree expr2 = parseUnaryExpr(tbl); + for (;;) { + int t2 = lex.lookAhead(); + int p2 = getOpPrecedence(t2); + if (p2 != 0 && prec > p2) + expr2 = binaryExpr2(tbl, expr2, p2); + else + return BinExpr.makeBin(t, expr, expr2); + } + } + + // !"#$%&'( )*+,-./0 12345678 9:;<=>? + private static final int[] binaryOpPrecedence + = { 0, 0, 0, 0, 1, 6, 0, 0, + 0, 1, 2, 0, 2, 0, 1, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 4, 0, 4, 0 }; + + private int getOpPrecedence(int c) { + if ('!' <= c && c <= '?') + return binaryOpPrecedence[c - '!']; + else if (c == '^') + return 7; + else if (c == '|') + return 8; + else if (c == ANDAND) + return 9; + else if (c == OROR) + return 10; + else if (c == EQ || c == NEQ) + return 5; + else if (c == LE || c == GE || c == INSTANCEOF) + return 4; + else if (c == LSHIFT || c == RSHIFT || c == ARSHIFT) + return 3; + else + return 0; // not a binary operator + } + + /* unary.expr : "++"|"--" unary.expr + | "+"|"-" unary.expr + | "!"|"~" unary.expr + | cast.expr + | postfix.expr + + unary.expr.not.plus.minus is a unary expression starting without + "+", "-", "++", or "--". + */ + private ASTree parseUnaryExpr(SymbolTable tbl) throws CompileError { + int t; + switch (lex.lookAhead()) { + case '+' : + case '-' : + case PLUSPLUS : + case MINUSMINUS : + case '!' : + case '~' : + t = lex.get(); + if (t == '-') { + int t2 = lex.lookAhead(); + switch (t2) { + case LongConstant : + case IntConstant : + case CharConstant : + lex.get(); + return new IntConst(-lex.getLong(), t2); + case DoubleConstant : + case FloatConstant : + lex.get(); + return new DoubleConst(-lex.getDouble(), t2); + default : + break; + } + } + + return Expr.make(t, parseUnaryExpr(tbl)); + case '(' : + return parseCast(tbl); + default : + return parsePostfix(tbl); + } + } + + /* cast.expr : "(" builtin.type ("[" "]")* ")" unary.expr + | "(" class.type ("[" "]")* ")" unary.expr2 + + unary.expr2 is a unary.expr beginning with "(", NULL, StringL, + Identifier, THIS, SUPER, or NEW. + + Either "(int.class)" or "(String[].class)" is a not cast expression. + */ + private ASTree parseCast(SymbolTable tbl) throws CompileError { + int t = lex.lookAhead(1); + if (isBuiltinType(t) && nextIsBuiltinCast()) { + lex.get(); // '(' + lex.get(); // primitive type + int dim = parseArrayDimension(); + if (lex.get() != ')') + throw new CompileError(") is missing", lex); + + return new CastExpr(t, dim, parseUnaryExpr(tbl)); + } + else if (t == Identifier && nextIsClassCast()) { + lex.get(); // '(' + ASTList name = parseClassType(tbl); + int dim = parseArrayDimension(); + if (lex.get() != ')') + throw new CompileError(") is missing", lex); + + return new CastExpr(name, dim, parseUnaryExpr(tbl)); + } + else + return parsePostfix(tbl); + } + + private boolean nextIsBuiltinCast() { + int t; + int i = 2; + while ((t = lex.lookAhead(i++)) == '[') + if (lex.lookAhead(i++) != ']') + return false; + + return lex.lookAhead(i - 1) == ')'; + } + + private boolean nextIsClassCast() { + int i = nextIsClassType(1); + if (i < 0) + return false; + + int t = lex.lookAhead(i); + if (t != ')') + return false; + + t = lex.lookAhead(i + 1); + return t == '(' || t == NULL || t == StringL + || t == Identifier || t == THIS || t == SUPER || t == NEW + || t == TRUE || t == FALSE || t == LongConstant + || t == IntConstant || t == CharConstant + || t == DoubleConstant || t == FloatConstant; + } + + private int nextIsClassType(int i) { + int t; + while (lex.lookAhead(++i) == '.') + if (lex.lookAhead(++i) != Identifier) + return -1; + + while ((t = lex.lookAhead(i++)) == '[') + if (lex.lookAhead(i++) != ']') + return -1; + + return i - 1; + } + + /* array.dimension : [ "[" "]" ]* + */ + private int parseArrayDimension() throws CompileError { + int arrayDim = 0; + while (lex.lookAhead() == '[') { + ++arrayDim; + lex.get(); + if (lex.get() != ']') + throw new CompileError("] is missing", lex); + } + + return arrayDim; + } + + /* class.type : Identifier ( "." Identifier )* + */ + private ASTList parseClassType(SymbolTable tbl) throws CompileError { + ASTList list = null; + for (;;) { + if (lex.get() != Identifier) + throw new SyntaxError(lex); + + list = ASTList.append(list, new Symbol(lex.getString())); + if (lex.lookAhead() == '.') + lex.get(); + else + break; + } + + return list; + } + + /* postfix.expr : number.literal + * | primary.expr + * | method.expr + * | postfix.expr "++" | "--" + * | postfix.expr "[" array.size "]" + * | postfix.expr "." Identifier + * | postfix.expr ( "[" "]" )* "." CLASS + * | postfix.expr "#" Identifier + * + * "#" is not an operator of regular Java. It separates + * a class name and a member name in an expression for static member + * access. For example, + * java.lang.Integer.toString(3) in regular Java + * can be written like this: + * java.lang.Integer#toString(3) for this compiler. + */ + private ASTree parsePostfix(SymbolTable tbl) throws CompileError { + int token = lex.lookAhead(); + switch (token) { // see also parseUnaryExpr() + case LongConstant : + case IntConstant : + case CharConstant : + lex.get(); + return new IntConst(lex.getLong(), token); + case DoubleConstant : + case FloatConstant : + lex.get(); + return new DoubleConst(lex.getDouble(), token); + default : + break; + } + + String str; + ASTree index; + ASTree expr = parsePrimaryExpr(tbl); + int t; + while (true) { + switch (lex.lookAhead()) { + case '(' : + expr = parseMethodCall(tbl, expr); + break; + case '[' : + if (lex.lookAhead(1) == ']') { + int dim = parseArrayDimension(); + if (lex.get() != '.' || lex.get() != CLASS) + throw new SyntaxError(lex); + + expr = parseDotClass(expr, dim); + } + else { + index = parseArrayIndex(tbl); + if (index == null) + throw new SyntaxError(lex); + + expr = Expr.make(ARRAY, expr, index); + } + break; + case PLUSPLUS : + case MINUSMINUS : + t = lex.get(); + expr = Expr.make(t, null, expr); + break; + case '.' : + lex.get(); + t = lex.get(); + if (t == CLASS) { + expr = parseDotClass(expr, 0); + } + else if (t == Identifier) { + str = lex.getString(); + expr = Expr.make('.', expr, new Member(str)); + } + else + throw new CompileError("missing member name", lex); + break; + case '#' : + lex.get(); + t = lex.get(); + if (t != Identifier) + throw new CompileError("missing static member name", lex); + + str = lex.getString(); + expr = Expr.make(MEMBER, new Symbol(toClassName(expr)), + new Member(str)); + break; + default : + return expr; + } + } + } + + /* Parse a .class expression on a class type. For example, + * String.class => ('.' "String" "class") + * String[].class => ('.' "[LString;" "class") + */ + private ASTree parseDotClass(ASTree className, int dim) + throws CompileError + { + String cname = toClassName(className); + if (dim > 0) { + StringBuffer sbuf = new StringBuffer(); + while (dim-- > 0) + sbuf.append('['); + + sbuf.append('L').append(cname.replace('.', '/')).append(';'); + cname = sbuf.toString(); + } + + return Expr.make('.', new Symbol(cname), new Member("class")); + } + + /* Parses a .class expression on a built-in type. For example, + * int.class => ('#' "java.lang.Integer" "TYPE") + * int[].class => ('.' "[I", "class") + */ + private ASTree parseDotClass(int builtinType, int dim) + throws CompileError + { + if (dim > 0) { + String cname = CodeGen.toJvmTypeName(builtinType, dim); + return Expr.make('.', new Symbol(cname), new Member("class")); + } + else { + String cname; + switch(builtinType) { + case BOOLEAN : + cname = "java.lang.Boolean"; + break; + case BYTE : + cname = "java.lang.Byte"; + break; + case CHAR : + cname = "java.lang.Character"; + break; + case SHORT : + cname = "java.lang.Short"; + break; + case INT : + cname = "java.lang.Integer"; + break; + case LONG : + cname = "java.lang.Long"; + break; + case FLOAT : + cname = "java.lang.Float"; + break; + case DOUBLE : + cname = "java.lang.Double"; + break; + case VOID : + cname = "java.lang.Void"; + break; + default : + throw new CompileError("invalid builtin type: " + + builtinType); + } + + return Expr.make(MEMBER, new Symbol(cname), new Member("TYPE")); + } + } + + /* method.call : method.expr "(" argument.list ")" + * method.expr : THIS | SUPER | Identifier + * | postfix.expr "." Identifier + * | postfix.expr "#" Identifier + */ + private ASTree parseMethodCall(SymbolTable tbl, ASTree expr) + throws CompileError + { + if (expr instanceof Keyword) { + int token = ((Keyword)expr).get(); + if (token != THIS && token != SUPER) + throw new SyntaxError(lex); + } + else if (expr instanceof Symbol) // Identifier + ; + else if (expr instanceof Expr) { + int op = ((Expr)expr).getOperator(); + if (op != '.' && op != MEMBER) + throw new SyntaxError(lex); + } + + return CallExpr.makeCall(expr, parseArgumentList(tbl)); + } + + private String toClassName(ASTree name) + throws CompileError + { + StringBuffer sbuf = new StringBuffer(); + toClassName(name, sbuf); + return sbuf.toString(); + } + + private void toClassName(ASTree name, StringBuffer sbuf) + throws CompileError + { + if (name instanceof Symbol) { + sbuf.append(((Symbol)name).get()); + return; + } + else if (name instanceof Expr) { + Expr expr = (Expr)name; + if (expr.getOperator() == '.') { + toClassName(expr.oprand1(), sbuf); + sbuf.append('.'); + toClassName(expr.oprand2(), sbuf); + return; + } + } + + throw new CompileError("bad static member access", lex); + } + + /* primary.expr : THIS | SUPER | TRUE | FALSE | NULL + * | StringL + * | Identifier + * | NEW new.expr + * | "(" expression ")" + * | builtin.type ( "[" "]" )* "." CLASS + * + * Identifier represents either a local variable name, a member name, + * or a class name. + */ + private ASTree parsePrimaryExpr(SymbolTable tbl) throws CompileError { + int t; + String name; + Declarator decl; + ASTree expr; + + switch (t = lex.get()) { + case THIS : + case SUPER : + case TRUE : + case FALSE : + case NULL : + return new Keyword(t); + case Identifier : + name = lex.getString(); + decl = tbl.lookup(name); + if (decl == null) + return new Member(name); // this or static member + else + return new Variable(name, decl); // local variable + case StringL : + return new StringL(lex.getString()); + case NEW : + return parseNew(tbl); + case '(' : + expr = parseExpression(tbl); + if (lex.get() == ')') + return expr; + else + throw new CompileError(") is missing", lex); + default : + if (isBuiltinType(t) || t == VOID) { + int dim = parseArrayDimension(); + if (lex.get() == '.' && lex.get() == CLASS) + return parseDotClass(t, dim); + } + + throw new SyntaxError(lex); + } + } + + /* new.expr : class.type "(" argument.list ")" + * | class.type array.size [ array.initializer ] + * | primitive.type array.size [ array.initializer ] + */ + private NewExpr parseNew(SymbolTable tbl) throws CompileError { + ArrayInit init = null; + int t = lex.lookAhead(); + if (isBuiltinType(t)) { + lex.get(); + ASTList size = parseArraySize(tbl); + if (lex.lookAhead() == '{') + init = parseArrayInitializer(tbl); + + return new NewExpr(t, size, init); + } + else if (t == Identifier) { + ASTList name = parseClassType(tbl); + t = lex.lookAhead(); + if (t == '(') { + ASTList args = parseArgumentList(tbl); + return new NewExpr(name, args); + } + else if (t == '[') { + ASTList size = parseArraySize(tbl); + if (lex.lookAhead() == '{') + init = parseArrayInitializer(tbl); + + return NewExpr.makeObjectArray(name, size, init); + } + } + + throw new SyntaxError(lex); + } + + /* array.size : [ array.index ]* + */ + private ASTList parseArraySize(SymbolTable tbl) throws CompileError { + ASTList list = null; + while (lex.lookAhead() == '[') + list = ASTList.append(list, parseArrayIndex(tbl)); + + return list; + } + + /* array.index : "[" [ expression ] "]" + */ + private ASTree parseArrayIndex(SymbolTable tbl) throws CompileError { + lex.get(); // '[' + if (lex.lookAhead() == ']') { + lex.get(); + return null; + } + else { + ASTree index = parseExpression(tbl); + if (lex.get() != ']') + throw new CompileError("] is missing", lex); + + return index; + } + } + + /* argument.list : "(" [ expression [ "," expression ]* ] ")" + */ + private ASTList parseArgumentList(SymbolTable tbl) throws CompileError { + if (lex.get() != '(') + throw new CompileError("( is missing", lex); + + ASTList list = null; + if (lex.lookAhead() != ')') + for (;;) { + list = ASTList.append(list, parseExpression(tbl)); + if (lex.lookAhead() == ',') + lex.get(); + else + break; + } + + if (lex.get() != ')') + throw new CompileError(") is missing", lex); + + return list; + } +} + |