/* * Copyright (c) 1998-2011 Caucho Technology -- all rights reserved * * This file is part of Resin(R) Open Source * * Each copy or derived work must preserve the copyright notice and this * notice unmodified. * * Resin Open Source is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Resin Open Source is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty * of NON-INFRINGEMENT. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License * along with Resin Open Source; if not, write to the * Free SoftwareFoundation, Inc. * 59 Temple Place, Suite 330 * Boston, MA 02111-1307 USA * * @author Scott Ferguson */ package com.caucho.es.parser; import com.caucho.es.ESBase; import com.caucho.es.ESBoolean; import com.caucho.es.ESException; import com.caucho.es.ESId; import com.caucho.es.ESNumber; import java.io.IOException; /** * Expr is an intermediate form representing an expression. */ class Expr { protected final static int TYPE_UNKNOWN = 0; protected final static int TYPE_ES = TYPE_UNKNOWN + 1; protected final static int TYPE_STRING = TYPE_ES + 1; protected final static int TYPE_NUMBER = TYPE_STRING + 1; protected final static int TYPE_LONG = TYPE_NUMBER; protected final static int TYPE_INTEGER = TYPE_LONG + 1; protected final static int TYPE_BOOLEAN = TYPE_INTEGER + 1; protected final static int TYPE_JAVA = TYPE_BOOLEAN + 1; protected final static int TYPE_VOID = TYPE_JAVA + 1; protected ParseClass cl; protected Block block; protected Function function; protected int type; protected Class javaType; protected boolean isTop; protected boolean noValue; private String filename; private int line; protected int withDepth; Expr(Block block) { this.block = block; this.withDepth = block.getWithDepth(); this.function = block.function; this.cl = function.cl; this.filename = block.getFilename(); this.line = block.getLine(); type = TYPE_UNKNOWN; } String getFilename() { return filename; } int getLine() { return line; } void killValue() { noValue = true; } void setUsed() { getType(); } void setTop() { noValue = true; isTop = true; } /** * Returns the javascript type of the expression. */ int getType() { return type; } Expr getTypeExpr() { return null; } /** * Returns the Java class representing this type. */ Class getJavaClass() { if (javaType != null) return javaType; Expr type = getTypeExpr(); if (! (type instanceof TypeExpr)) { switch (getType()) { case TYPE_STRING: return String.class; case TYPE_INTEGER: return int.class; case TYPE_NUMBER: return double.class; case TYPE_BOOLEAN: return boolean.class; default: return ESBase.class; } } TypeExpr javaType = (TypeExpr) type; return javaType.getJavaClass(); } boolean isSimple() { return false; } /** * True if the type of this expression is easily converted to a number. */ boolean isNumeric() { int type = getType(); return type >= TYPE_NUMBER && type <= TYPE_BOOLEAN; } boolean isNum() { int type = getType(); return type == TYPE_NUMBER || type == TYPE_INTEGER; } /** * This expression will be used in a boolean context. */ Expr setBoolean() { return new BooleanExpr(block, this); } Expr next(String iter, Expr lhs) throws ESException { return lhs.assign(new SpecialExpr(block, SpecialExpr.NEXT, iter)); } /** * Gets the field of the current expr */ Expr fieldReference(Expr expr) { return new FieldExpr(block, this, expr); } /** * Gets the field of the current expr */ Expr fieldReference(ESId id) throws ESException { return new FieldExpr(block, this, new LiteralExpr(block, id)); } /** * A unary op */ Expr unaryOp(int op) { return new UnaryExpr(block, this, op); } /** * A unary op */ Expr doVoid() { return new UnaryExpr(block, this, 'v'); } /** * The typeof operator */ Expr typeof() { return new UnaryExpr(block, this, 't'); } /** * The delete operator */ Expr delete() throws ESException { return BinaryExpr.create(block, this, new LiteralExpr(block, ESBoolean.TRUE), ','); } /** * The assignment operator */ Expr assign(Expr value) throws ESException { throw error("illegal left-hand-side of assignment"); } CallExpr startCall() throws ESException { return new CallExpr(block, this, null, false); } CallExpr startNew() throws ESException { return new CallExpr(block, this, null, true); } /** * Handle autoincrement */ Expr prefix(int op) throws ESException { return unaryOp('+').binaryOp(op, op, new LiteralExpr(block, ESNumber.create(1.0))); } /** * Handle autoincrement */ Expr postfix(int op) { return unaryOp('+'); } /** * A binary op */ Expr binaryOp(int lex, int op, Expr rexpr) throws ESException { setUsed(); rexpr.setUsed(); if (lex != '=') { switch (op) { case '<': case '>': case Lexer.LEQ: case Lexer.GEQ: case Lexer.EQ: case Lexer.NEQ: case Lexer.STRICT_EQ: case Lexer.STRICT_NEQ: return BooleanBinaryExpr.create(block, this, rexpr, op); case '+': return PlusExpr.create(block, this, rexpr); default: return BinaryExpr.create(block, this, rexpr, op); } } else if (op == '=') return assign(rexpr); else return assign(binaryOp(op, op, rexpr)); } Expr cast(Expr castType) throws ESException { return CastExpr.create(block, this, (TypeExpr) castType); } /** * The conditional ? : operation */ Expr conditional(Expr mexpr, Expr rexpr) { return new ConditionalExpr(block, this, mexpr, rexpr); } void printExpr() throws IOException { print(); } void print() throws IOException { if (ESBase.class.isAssignableFrom(getJavaClass()) || this instanceof LiteralExpr) { printImpl(); if (isTop) cl.println(";"); return; } switch (getType()) { case TYPE_NUMBER: if (! noValue) cl.print("ESNumber.create("); printNumImpl(); if (! noValue) cl.print(")"); break; case TYPE_INTEGER: if (! noValue) cl.print("ESNumber.create("); printInt32Impl(); if (! noValue) cl.print(")"); break; case TYPE_BOOLEAN: if (! noValue) cl.print("("); printBooleanImpl(); if (! noValue) cl.print("?ESBoolean.TRUE:ESBoolean.FALSE)"); break; case TYPE_STRING: if (ESBase.class.isAssignableFrom(getJavaClass())) printImpl(); else { if (! noValue) cl.print("ESString.create("); printStringImpl(); if (! noValue) cl.print(")"); } break; case TYPE_JAVA: if (! noValue) cl.print("_env.wrap("); printJavaImpl(); if (! noValue) cl.print(")"); break; case TYPE_VOID: if (! noValue) cl.print("_env.wrap("); printJavaImpl(); if (! noValue) cl.print(")"); break; default: printImpl(); } if (isTop) cl.println(";"); } void printBoolean() throws IOException { switch (getType()) { case TYPE_NUMBER: cl.print("("); printNumImpl(); cl.print("!=0.0)"); break; case TYPE_INTEGER: cl.print("("); printInt32Impl(); cl.print("!=0)"); break; case TYPE_BOOLEAN: printBooleanImpl(); break; case TYPE_JAVA: cl.print("("); printJava(); cl.print("!=null)"); break; default: print(); cl.print(".toBoolean()"); } if (isTop) cl.println(";"); } void printInt32() throws IOException { switch (getType()) { case TYPE_INTEGER: printInt32Impl(); break; case TYPE_NUMBER: cl.print("((int)"); printNumImpl(); cl.print(")"); break; case TYPE_BOOLEAN: cl.print("("); printBooleanImpl(); cl.print("?1:0)"); break; default: printImpl(); cl.print(".toInt32()"); } } void printInt64() throws IOException { printInt32(); } void printNum() throws IOException { switch (getType()) { case TYPE_NUMBER: printNumImpl(); break; case TYPE_INTEGER: cl.print("((double)"); printInt32Impl(); cl.print(")"); break; case TYPE_BOOLEAN: cl.print("("); printBooleanImpl(); cl.print("?1.0:0.0)"); break; default: printImpl(); cl.print(".toNum()"); } } /** * Prints the expression as a java object. */ void printJava() throws IOException { switch (getType()) { case TYPE_INTEGER: printInt32Impl(); break; case TYPE_BOOLEAN: printBooleanImpl(); break; case TYPE_STRING: printStringImpl(); break; case TYPE_NUMBER: printNumImpl(); break; case TYPE_JAVA: printJavaImpl(); break; default: print(); cl.print(".toJavaObject()"); break; } } /** * Prints a string value */ void printStr() throws IOException { print(); cl.print(".toStr()"); } void printJavaString() throws IOException { switch (getType()) { case TYPE_STRING: if (this instanceof LiteralExpr) { printStringImpl(); } else { cl.print("String.valueOf("); printStringImpl(); cl.print(")"); } break; case TYPE_JAVA: if (getJavaClass().equals(String.class)) printJavaImpl(); else { cl.print("String.valueOf("); printJavaImpl(); cl.print(")"); } break; // JavaScript's double printing differs from Java's, so // we need to convert to the JavaScript object. default: print(); cl.print(".toStr().toString()"); break; } } void printJavaClass(Class type) throws IOException { if (type.isArray()) { printJavaClass(type.getComponentType()); cl.print("[]"); } else cl.print(type.getName()); } /** * Print where the result is a string. */ void printString() throws IOException { switch (getType()) { case TYPE_INTEGER: printInt32Impl(); break; case TYPE_BOOLEAN: printBooleanImpl(); break; case TYPE_STRING: printStringImpl(); break; case TYPE_JAVA: printJavaImpl(); break; // JavaScript's double printing differs from Java's, so // we need to convert to the JavaScript object. case TYPE_NUMBER: default: print(); cl.print(".valueOf()"); break; } } void printImpl() throws IOException { throw new RuntimeException("" + this); } void printBooleanImpl() throws IOException { throw new RuntimeException("" + this); } void printNumImpl() throws IOException { throw new RuntimeException("" + this); } void printInt32Impl() throws IOException { throw new RuntimeException("" + this); } void printInt64Impl() throws IOException { throw new RuntimeException("" + this); } void printStringImpl() throws IOException { throw new RuntimeException("no string impl for " + getClass()); } void printJavaImpl() throws IOException { throw new RuntimeException("" + this); } void printLiteral(ESBase literal) throws IOException { cl.printLiteral(literal); } void exprStatement(Function fun) throws ESException { } private ESException error(String msg) { return block.error(msg); } }