/*
* Javassist, a Java-bytecode translator toolkit.
* Copyright (C) 1999- 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,
* or the Apache License Version 2.0.
*
* 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 org.googlecode.perftrace.javassist.compiler;
import org.googlecode.perftrace.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;
}
}