package polyglot.ext.jl.ast;
import polyglot.ast.*;
import polyglot.util.*;
import polyglot.visit.*;
import polyglot.types.*;
import polyglot.main.Options;
import java.util.*;
/**
* A <code>Binary</code> represents a Java binary expression, an
* immutable pair of expressions combined with an operator.
*/
public class Binary_c extends Expr_c implements Binary
{
protected Expr left;
protected Operator op;
protected Expr right;
protected Precedence precedence;
public Binary_c(Position pos, Expr left, Operator op, Expr right) {
super(pos);
this.left = left;
this.op = op;
this.right = right;
this.precedence = op.precedence();
}
/** Get the left operand of the expression. */
public Expr left() {
return this.left;
}
/** Set the left operand of the expression. */
public Binary left(Expr left) {
Binary_c n = (Binary_c) copy();
n.left = left;
return n;
}
/** Get the operator of the expression. */
public Operator operator() {
return this.op;
}
/** Set the operator of the expression. */
public Binary operator(Operator op) {
Binary_c n = (Binary_c) copy();
n.op = op;
return n;
}
/** Get the right operand of the expression. */
public Expr right() {
return this.right;
}
/** Set the right operand of the expression. */
public Binary right(Expr right) {
Binary_c n = (Binary_c) copy();
n.right = right;
return n;
}
/** Get the precedence of the expression. */
public Precedence precedence() {
return this.precedence;
}
public Binary precedence(Precedence precedence) {
Binary_c n = (Binary_c) copy();
n.precedence = precedence;
return n;
}
/** Reconstruct the expression. */
protected Binary_c reconstruct(Expr left, Expr right) {
if (left != this.left || right != this.right) {
Binary_c n = (Binary_c) copy();
n.left = left;
n.right = right;
return n;
}
return this;
}
/** Visit the children of the expression. */
public Node visitChildren(NodeVisitor v) {
Expr left = (Expr) visitChild(this.left, v);
Expr right = (Expr) visitChild(this.right, v);
return reconstruct(left, right);
}
public boolean isConstant() {
return left.isConstant() && right.isConstant();
}
public Object constantValue() {
Object lv = left.constantValue();
Object rv = right.constantValue();
if (! isConstant()) {
return null;
}
if (op == ADD && (lv instanceof String || rv instanceof String)) {
// toString() does what we want for String, Number, and Boolean
if (lv == null) lv = "null";
if (rv == null) rv = "null";
return lv.toString() + rv.toString();
}
if (op == EQ && (lv instanceof String && rv instanceof String)) {
return Boolean.valueOf(((String) lv).intern() == ((String) rv).intern());
}
if (op == NE && (lv instanceof String && rv instanceof String)) {
return Boolean.valueOf(((String) lv).intern() != ((String) rv).intern());
}
// promote chars to ints.
if (lv instanceof Character) {
lv = new Integer(((Character) lv).charValue());
}
if (rv instanceof Character) {
rv = new Integer(((Character) rv).charValue());
}
try {
if (lv instanceof Number && rv instanceof Number) {
if (lv instanceof Double || rv instanceof Double) {
double l = ((Number) lv).doubleValue();
double r = ((Number) rv).doubleValue();
if (op == ADD) return new Double(l + r);
if (op == SUB) return new Double(l - r);
if (op == MUL) return new Double(l * r);
if (op == DIV) return new Double(l / r);
if (op == MOD) return new Double(l % r);
if (op == EQ) return Boolean.valueOf(l == r);
if (op == NE) return Boolean.valueOf(l != r);
if (op == LT) return Boolean.valueOf(l < r);
if (op == LE) return Boolean.valueOf(l <= r);
if (op == GE) return Boolean.valueOf(l >= r);
if (op == GT) return Boolean.valueOf(l > r);
return null;
}
if (lv instanceof Float || rv instanceof Float) {
float l = ((Number) lv).floatValue();
float r = ((Number) rv).floatValue();
if (op == ADD) return new Float(l + r);
if (op == SUB) return new Float(l - r);
if (op == MUL) return new Float(l * r);
if (op == DIV) return new Float(l / r);
if (op == MOD) return new Float(l % r);
if (op == EQ) return Boolean.valueOf(l == r);
if (op == NE) return Boolean.valueOf(l != r);
if (op == LT) return Boolean.valueOf(l < r);
if (op == LE) return Boolean.valueOf(l <= r);
if (op == GE) return Boolean.valueOf(l >= r);
if (op == GT) return Boolean.valueOf(l > r);
return null;
}
if (lv instanceof Long && rv instanceof Number) {
long l = ((Long) lv).longValue();
long r = ((Number) rv).longValue();
if (op == SHL) return new Long(l << r);
if (op == SHR) return new Long(l >> r);
if (op == USHR) return new Long(l >>> r);
}
if (lv instanceof Long || rv instanceof Long) {
long l = ((Number) lv).longValue();
long r = ((Number) rv).longValue();
if (op == ADD) return new Long(l + r);
if (op == SUB) return new Long(l - r);
if (op == MUL) return new Long(l * r);
if (op == DIV) return new Long(l / r);
if (op == MOD) return new Long(l % r);
if (op == EQ) return Boolean.valueOf(l == r);
if (op == NE) return Boolean.valueOf(l != r);
if (op == LT) return Boolean.valueOf(l < r);
if (op == LE) return Boolean.valueOf(l <= r);
if (op == GE) return Boolean.valueOf(l >= r);
if (op == GT) return Boolean.valueOf(l > r);
if (op == BIT_AND) return new Long(l & r);
if (op == BIT_OR) return new Long(l | r);
if (op == BIT_XOR) return new Long(l ^ r);
return null;
}
// At this point, both lv and rv must be ints.
int l = ((Number) lv).intValue();
int r = ((Number) rv).intValue();
if (op == ADD) return new Integer(l + r);
if (op == SUB) return new Integer(l - r);
if (op == MUL) return new Integer(l * r);
if (op == DIV) return new Integer(l / r);
if (op == MOD) return new Integer(l % r);
if (op == EQ) return Boolean.valueOf(l == r);
if (op == NE) return Boolean.valueOf(l != r);
if (op == LT) return Boolean.valueOf(l < r);
if (op == LE) return Boolean.valueOf(l <= r);
if (op == GE) return Boolean.valueOf(l >= r);
if (op == GT) return Boolean.valueOf(l > r);
if (op == BIT_AND) return new Integer(l & r);
if (op == BIT_OR) return new Integer(l | r);
if (op == BIT_XOR) return new Integer(l ^ r);
if (op == SHL) return new Integer(l << r);
if (op == SHR) return new Integer(l >> r);
if (op == USHR) return new Integer(l >>> r);
return null;
}
}
catch (ArithmeticException e) {
// ignore div by 0
return null;
}
if (lv instanceof Boolean && rv instanceof Boolean) {
boolean l = ((Boolean) lv).booleanValue();
boolean r = ((Boolean) rv).booleanValue();
if (op == EQ) return Boolean.valueOf(l == r);
if (op == NE) return Boolean.valueOf(l != r);
if (op == BIT_AND) return Boolean.valueOf(l & r);
if (op == BIT_OR) return Boolean.valueOf(l | r);
if (op == BIT_XOR) return Boolean.valueOf(l ^ r);
if (op == COND_AND) return Boolean.valueOf(l && r);
if (op == COND_OR) return Boolean.valueOf(l || r);
}
return null;
}
/** Type check the expression. */
public Node typeCheck(TypeChecker tc) throws SemanticException {
Type l = left.type();
Type r = right.type();
TypeSystem ts = tc.typeSystem();
if (op == GT || op == LT || op == GE || op == LE) {
if (! l.isNumeric()) {
throw new SemanticException("The " + op +
" operator must have numeric operands.", left.position());
}
if (! r.isNumeric()) {
throw new SemanticException("The " + op +
" operator must have numeric operands.", right.position());
}
return type(ts.Boolean());
}
if (op == EQ || op == NE) {
if (! ts.isCastValid(l, r) && ! ts.isCastValid(r, l)) {
throw new SemanticException("The " + op +
" operator must have operands of similar type.",
position());
}
return type(ts.Boolean());
}
if (op == COND_OR || op == COND_AND) {
if (! l.isBoolean()) {
throw new SemanticException("The " + op +
" operator must have boolean operands.", left.position());
}
if (! r.isBoolean()) {
throw new SemanticException("The " + op +
" operator must have boolean operands.", right.position());
}
return type(ts.Boolean());
}
if (op == ADD) {
if (ts.equals(l, ts.String()) || ts.equals(r, ts.String())) {
if (!ts.canCoerceToString(r, tc.context())) {
throw new SemanticException("Cannot coerce an expression " +
"of type " + r + " to a String.",
right.position());
}
if (!ts.canCoerceToString(l, tc.context())) {
throw new SemanticException("Cannot coerce an expression " +
"of type " + l + " to a String.",
left.position());
}
return precedence(Precedence.STRING_ADD).type(ts.String());
}
}
if (op == BIT_AND || op == BIT_OR || op == BIT_XOR) {
if (l.isBoolean() && r.isBoolean()) {
return type(ts.Boolean());
}
}
if (op == ADD) {
if (! l.isNumeric()) {
throw new SemanticException("The " + op +
" operator must have numeric or String operands.",
left.position());
}
if (! r.isNumeric()) {
throw new SemanticException("The " + op +
" operator must have numeric or String operands.",
right.position());
}
}
if (op == BIT_AND || op == BIT_OR || op == BIT_XOR) {
if (! ts.isImplicitCastValid(l, ts.Long())) {
throw new SemanticException("The " + op +
" operator must have numeric or boolean operands.",
left.position());
}
if (! ts.isImplicitCastValid(r, ts.Long())) {
throw new SemanticException("The " + op +
" operator must have numeric or boolean operands.",
right.position());
}
}
if (op == SUB || op == MUL || op == DIV || op == MOD) {
if (! l.isNumeric()) {
throw new SemanticException("The " + op +
" operator must have numeric operands.", left.position());
}
if (! r.isNumeric()) {
throw new SemanticException("The " + op +
" operator must have numeric operands.", right.position());
}
}
if (op == SHL || op == SHR || op == USHR) {
if (! ts.isImplicitCastValid(l, ts.Long())) {
throw new SemanticException("The " + op +
" operator must have numeric operands.", left.position());
}
if (! ts.isImplicitCastValid(r, ts.Long())) {
throw new SemanticException("The " + op +
" operator must have numeric operands.", right.position());
}
}
if (op == SHL || op == SHR || op == USHR) {
// For shift, only promote the left operand.
return type(ts.promote(l));
}
return type(ts.promote(l, r));
}
public Type childExpectedType(Expr child, AscriptionVisitor av) {
Expr other;
if (child == left) {
other = right;
}
else if (child == right) {
other = left;
}
else {
return child.type();
}
TypeSystem ts = av.typeSystem();
try {
if (op == EQ || op == NE) {
// Coercion to compatible types.
if ((child.type().isReference() || child.type().isNull()) &&
(other.type().isReference() || other.type().isNull())) {
return ts.leastCommonAncestor(child.type(), other.type());
}
if (child.type().isBoolean() && other.type().isBoolean()) {
return ts.Boolean();
}
if (child.type().isNumeric() && other.type().isNumeric()) {
return ts.promote(child.type(), other.type());
}
if (child.type().isImplicitCastValid(other.type())) {
return other.type();
}
return child.type();
}
if (op == ADD && ts.equals(type, ts.String())) {
// Implicit coercion to String.
return ts.String();
}
if (op == GT || op == LT || op == GE || op == LE) {
if (child.type().isNumeric() && other.type().isNumeric()) {
return ts.promote(child.type(), other.type());
}
return child.type();
}
if (op == COND_OR || op == COND_AND) {
return ts.Boolean();
}
if (op == BIT_AND || op == BIT_OR || op == BIT_XOR) {
if (other.type().isBoolean()) {
return ts.Boolean();
}
if (child.type().isNumeric() && other.type().isNumeric()) {
return ts.promote(child.type(), other.type());
}
return child.type();
}
if (op == ADD || op == SUB || op == MUL || op == DIV || op == MOD) {
if (child.type().isNumeric() && other.type().isNumeric()) {
Type t = ts.promote(child.type(), other.type());
if (ts.isImplicitCastValid(t, av.toType())) {
return t;
}
else {
return av.toType();
}
}
return child.type();
}
if (op == SHL || op == SHR || op == USHR) {
if (child.type().isNumeric() && other.type().isNumeric()) {
if (child == left) {
Type t = ts.promote(child.type());
if (ts.isImplicitCastValid(t, av.toType())) {
return t;
}
else {
return av.toType();
}
}
else {
return ts.promote(child.type());
}
}
return child.type();
}
return child.type();
}
catch (SemanticException e) {
}
return child.type();
}
/** Get the throwsArithmeticException of the expression. */
public boolean throwsArithmeticException() {
// conservatively assume that any division or mod may throw
// ArithmeticException this is NOT true-- floats and doubles don't
// throw any exceptions ever...
return op == DIV || op == MOD;
}
public String toString() {
return left + " " + op + " " + right;
}
/** Write the expression to an output file. */
public void prettyPrint(CodeWriter w, PrettyPrinter tr) {
printSubExpr(left, true, w, tr);
w.write(" ");
w.write(op.toString());
w.allowBreak(type() == null || type().isPrimitive() ? 2 : 0, " ");
printSubExpr(right, false, w, tr);
}
public void dump(CodeWriter w) {
super.dump(w);
if (type != null) {
w.allowBreak(4, " ");
w.begin(0);
w.write("(type " + type + ")");
w.end();
}
w.allowBreak(4, " ");
w.begin(0);
w.write("(operator " + op + ")");
w.end();
}
public Term entry() {
return left.entry();
}
public List acceptCFG(CFGBuilder v, List succs) {
if (op == COND_AND || op == COND_OR) {
// short-circuit
if (left instanceof BooleanLit) {
BooleanLit b = (BooleanLit) left;
if ((b.value() && op == COND_OR) || (! b.value() && op == COND_AND)) {
v.visitCFG(left, this);
}
else {
v.visitCFG(left, right.entry());
v.visitCFG(right, this);
}
}
else {
if (op == COND_AND) {
// AND operator
// short circuit means that left is false
v.visitCFG(left, FlowGraph.EDGE_KEY_TRUE, right.entry(),
FlowGraph.EDGE_KEY_FALSE, this);
}
else {
// OR operator
// short circuit means that left is true
v.visitCFG(left, FlowGraph.EDGE_KEY_FALSE, right.entry(),
FlowGraph.EDGE_KEY_TRUE, this);
}
v.visitCFG(right, FlowGraph.EDGE_KEY_TRUE, this,
FlowGraph.EDGE_KEY_FALSE, this);
}
}
else {
if (left.type().isBoolean() && right.type().isBoolean()) {
v.visitCFG(left, FlowGraph.EDGE_KEY_TRUE, right.entry(),
FlowGraph.EDGE_KEY_FALSE, right.entry());
v.visitCFG(right, FlowGraph.EDGE_KEY_TRUE, this,
FlowGraph.EDGE_KEY_FALSE, this);
}
else {
v.visitCFG(left, right.entry());
v.visitCFG(right, this);
}
}
return succs;
}
public List throwTypes(TypeSystem ts) {
if (throwsArithmeticException()) {
return Collections.singletonList(ts.ArithmeticException());
}
return Collections.EMPTY_LIST;
}
}