aboutsummaryrefslogtreecommitdiff
path: root/src/main/javassist/compiler/Parser.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/javassist/compiler/Parser.java')
-rw-r--r--src/main/javassist/compiler/Parser.java1342
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;
+ }
+}
+