CommonBinaryExpression.java

/**
 * 
 */
package dev.civl.mc.model.common.expression;

import java.util.HashSet;
import java.util.Set;

import dev.civl.mc.model.IF.CIVLInternalException;
import dev.civl.mc.model.IF.CIVLSource;
import dev.civl.mc.model.IF.Scope;
import dev.civl.mc.model.IF.expression.BinaryExpression;
import dev.civl.mc.model.IF.expression.ConditionalExpression;
import dev.civl.mc.model.IF.expression.Expression;
import dev.civl.mc.model.IF.expression.VariableExpression;
import dev.civl.mc.model.IF.type.CIVLType;
import dev.civl.mc.model.IF.variable.Variable;
import dev.civl.sarl.IF.SymbolicUniverse;
import dev.civl.sarl.IF.expr.BooleanExpression;
import dev.civl.sarl.IF.expr.NumericExpression;
import dev.civl.sarl.IF.expr.SymbolicExpression;

/**
 * A binary operation.
 * 
 * @author Timothy K. Zirkel (zirkel)
 * 
 */
public class CommonBinaryExpression extends CommonExpression
		implements
			BinaryExpression {

	/* ************************** Private Fields *************************** */

	/**
	 * The operator of this binary expression.
	 * 
	 * @see BINARY_OPERATOR
	 */
	private BINARY_OPERATOR operator;

	/**
	 * The left-hand-side operand.
	 */
	private Expression left;

	/**
	 * The right-hand-side operand.
	 */
	private Expression right;

	/**
	 * True iff the value of this expression is to be assigned to the left
	 * operand.
	 */
	private boolean isAssignToLeft;

	/* **************************** Constructor **************************** */

	/**
	 * Create a new instance of a binary expression.
	 * 
	 * @param source
	 *            The source information corresponding to this expression.
	 * @param scope
	 *            The highest scope that this expression accessed through its
	 *            operands.
	 * @param type
	 *            The type of this expression.
	 * @param operator
	 *            The operator of this expression.
	 * @param left
	 *            The left-hand-side operand.
	 * @param right
	 *            The right-hand-side operand.
	 */
	public CommonBinaryExpression(CIVLSource source, Scope hscope, Scope lscope,
			CIVLType type, BINARY_OPERATOR operator, Expression left,
			Expression right) {
		super(source, hscope, lscope, type);
		this.operator = operator;
		this.left = left;
		this.right = right;
	}

	/* ******************* Methods from BinaryExpression ******************* */

	/**
	 * @return The binary operator
	 */
	@Override
	public BINARY_OPERATOR operator() {
		return operator;
	}

	/**
	 * @return The left operand.
	 */
	@Override
	public Expression left() {
		return left;
	}

	/**
	 * @return The right operand.
	 */
	@Override
	public Expression right() {
		return right;
	}

	/* ********************** Methods from Expression ********************** */

	@Override
	public ExpressionKind expressionKind() {
		return ExpressionKind.BINARY;
	}

	@Override
	public void calculateDerefs() {
		this.left.calculateDerefs();
		this.right.calculateDerefs();
		this.hasDerefs = this.left.hasDerefs() || this.right.hasDerefs();
	}

	@Override
	public void purelyLocalAnalysisOfVariables(Scope funcScope) {
		this.left.purelyLocalAnalysisOfVariables(funcScope);
		this.right.purelyLocalAnalysisOfVariables(funcScope);
	}

	@Override
	public void purelyLocalAnalysis() {
		if (this.hasDerefs) {
			this.purelyLocal = false;
			return;
		}
		this.left.purelyLocalAnalysis();
		this.right.purelyLocalAnalysis();
		this.purelyLocal = this.left.isPurelyLocal()
				&& this.right.isPurelyLocal();
	}

	@Override
	public void replaceWith(ConditionalExpression oldExpression,
			VariableExpression newExpression) {
		if (left == oldExpression) {
			left = newExpression;
			return;
		}

		if (right == oldExpression) {
			right = newExpression;
			return;
		}

		left.replaceWith(oldExpression, newExpression);
		right.replaceWith(oldExpression, newExpression);
	}

	@Override
	public Expression replaceWith(ConditionalExpression oldExpression,
			Expression newExpression) {
		Expression newLeft = left.replaceWith(oldExpression, newExpression);
		CommonBinaryExpression result = null;

		if (newLeft != null) {
			result = new CommonBinaryExpression(this.getSource(),
					expressionScope(), lowestScope(), expressionType,
					this.operator, newLeft, right);
		} else {
			Expression newRight = right.replaceWith(oldExpression,
					newExpression);

			if (newRight != null)
				result = new CommonBinaryExpression(this.getSource(),
						expressionScope(), lowestScope(), expressionType,
						this.operator, left, newRight);
		}
		return result;
	}

	@Override
	public Set<Variable> variableAddressedOf(Scope scope) {
		Set<Variable> variableSet = new HashSet<>();
		Set<Variable> operandResult = left.variableAddressedOf(scope);

		if (operandResult != null)
			variableSet.addAll(operandResult);
		operandResult = right.variableAddressedOf(scope);
		if (operandResult != null)
			variableSet.addAll(operandResult);
		return variableSet;
	}

	@Override
	public Set<Variable> variableAddressedOf() {
		Set<Variable> variableSet = new HashSet<>();
		Set<Variable> operandResult = left.variableAddressedOf();

		if (operandResult != null)
			variableSet.addAll(operandResult);
		operandResult = right.variableAddressedOf();
		if (operandResult != null)
			variableSet.addAll(operandResult);
		return variableSet;
	}

	@Override
	protected void addFreeVariables(Set<Variable> result) {
		((CommonExpression) left).addFreeVariables(result);
		((CommonExpression) right).addFreeVariables(result);
	}

	/* ********************** Methods from Expression ********************** */

	@Override
	public String toString() {
		String op = this.operatorToString();

		return "(" + left + op + right + ")";
	}

	@Override
	public void calculateConstantValueWork(SymbolicUniverse universe) {
		SymbolicExpression leftValue, rightValue;

		left.calculateConstantValue(universe);
		right.calculateConstantValue(universe);
		leftValue = left.constantValue();
		rightValue = right.constantValue();
		if (leftValue == null || rightValue == null)
			return;
		switch (operator) {
			case BIT_AND :
			case BIT_OR :
			case BIT_XOR :
				break;
			case PLUS :
				constantValue = universe.add((NumericExpression) leftValue,
						(NumericExpression) rightValue);
				break;
			case MINUS :
				constantValue = universe.subtract((NumericExpression) leftValue,
						(NumericExpression) rightValue);
				break;
			case TIMES :
				constantValue = universe.multiply((NumericExpression) leftValue,
						(NumericExpression) rightValue);
				break;
			case DIVIDE :
				// try {
				// constantValue = universe.divide((NumericExpression)
				// leftValue,
				// (NumericExpression) rightValue);
				// } catch (Exception ex) {
				//
				// }
				break;
			case LESS_THAN :
				constantValue = universe.lessThan((NumericExpression) leftValue,
						(NumericExpression) rightValue);
				break;
			case LESS_THAN_EQUAL :
				constantValue = universe.lessThanEquals(
						(NumericExpression) leftValue,
						(NumericExpression) rightValue);
				break;
			case EQUAL :
				constantValue = universe.equals((NumericExpression) leftValue,
						(NumericExpression) rightValue);
				break;
			case NOT_EQUAL :
				constantValue = universe
						.not(universe.equals((NumericExpression) leftValue,
								(NumericExpression) rightValue));
				break;
			case AND :
				// TODO change to andTo
				constantValue = universe.and((BooleanExpression) leftValue,
						(BooleanExpression) rightValue);
				break;
			case OR :
				constantValue = universe.or((BooleanExpression) leftValue,
						(BooleanExpression) rightValue);
				break;
			case IMPLIES :
				constantValue = universe.implies((BooleanExpression) leftValue,
						(BooleanExpression) rightValue);
				break;
			case MODULO :
				constantValue = universe.modulo((NumericExpression) leftValue,
						(NumericExpression) rightValue);
				break;
			case POINTER_ADD :
			case POINTER_SUBTRACT :
			case SHIFTLEFT :
			case SHIFTRIGHT :
			case VALID :
				break;
			default :
				throw new CIVLInternalException("Unknown operator: " + operator,
						this);
		}
	}

	@Override
	protected boolean expressionEquals(Expression expression) {
		BinaryExpression that = (BinaryExpression) expression;

		return this.left.equals(that.left()) && this.right.equals(that.right());
	}

	@Override
	public String operatorToString() {
		String op = "";

		switch (operator) {
			case BIT_AND :
				op = "&";
				break;
			case BIT_OR :
				op = "|";
				break;
			case BIT_XOR :
				op = "^";
				break;
			case PLUS :
				op = "+";
				break;
			case MINUS :
				op = "-";
				break;
			case TIMES :
				op = "*";
				break;
			case DIVIDE :
				op = "/";
				break;
			case LESS_THAN :
				op = "<";
				break;
			case LESS_THAN_EQUAL :
				op = "<=";
				break;
			case EQUAL :
				op = "==";
				break;
			case NOT_EQUAL :
				op = "!=";
				break;
			case AND :
				op = "&&";
				break;
			case OR :
				op = "||";
				break;
			case IMPLIES :
				op = "=>";
				break;
			case MODULO :
				op = "%";
				break;
			case POINTER_ADD :
				op = "+";
				break;
			case POINTER_SUBTRACT :
				op = "-";
				break;
			case SHIFTLEFT :
				op = "<<";
				break;
			case SHIFTRIGHT :
				op = ">>";
				break;
			case VALID :
				op = " valid-operator ";
				break;
			default :
				throw new CIVLInternalException("Unknown operator: " + operator,
						this);
		}
		return op;
	}

	@Override
	public void setAssignToLeft(boolean value) {
		this.isAssignToLeft = value;
	}

	@Override
	public boolean isAssignToLeft() {
		return this.isAssignToLeft;
	}

	@Override
	public boolean switchOperands() {
		if (!isSymmetric(this.operator))
			return false;

		Expression tmp = left;

		this.left = right;
		this.right = tmp;
		return true;
	}

	/**
	 * AND, BITAND, BITCOMPLEMENT, BITOR, BITXOR, DIVIDE, EQUAL, IMPLIES,
	 * LESS_THAN, LESS_THAN_EQUAL, MINUS, MODULO, NOT_EQUAL, OR, PLUS,
	 * POINTER_ADD, POINTER_SUBTRACT, SHIFTLEFT, SHIFTRIGHT, TIMES
	 * 
	 * @param op
	 * @return
	 */
	private static boolean isSymmetric(BINARY_OPERATOR op) {
		switch (op) {
			case AND :
			case BIT_AND :
			case BIT_OR :
			case BIT_XOR :
			case EQUAL :
			case NOT_EQUAL :
			case OR :
			case PLUS :
			case TIMES :
				return true;
			default :
				return false;
		}
	}

	@Override
	public boolean containsHere() {
		return left.containsHere() || right.containsHere();
	}
}