package ai.domain.expressions.bool;
import org.eclipse.jdt.core.dom.ArrayAccess;
import org.eclipse.jdt.core.dom.ArrayCreation;
import org.eclipse.jdt.core.dom.ArrayInitializer;
import org.eclipse.jdt.core.dom.Assignment;
import org.eclipse.jdt.core.dom.BooleanLiteral;
import org.eclipse.jdt.core.dom.CastExpression;
import org.eclipse.jdt.core.dom.CharacterLiteral;
import org.eclipse.jdt.core.dom.ClassInstanceCreation;
import org.eclipse.jdt.core.dom.ConditionalExpression;
import org.eclipse.jdt.core.dom.Expression;
import org.eclipse.jdt.core.dom.FieldAccess;
import org.eclipse.jdt.core.dom.InfixExpression;
import org.eclipse.jdt.core.dom.InstanceofExpression;
import org.eclipse.jdt.core.dom.MethodInvocation;
import org.eclipse.jdt.core.dom.NullLiteral;
import org.eclipse.jdt.core.dom.NumberLiteral;
import org.eclipse.jdt.core.dom.ParenthesizedExpression;
import org.eclipse.jdt.core.dom.PostfixExpression;
import org.eclipse.jdt.core.dom.PrefixExpression;
import org.eclipse.jdt.core.dom.QualifiedName;
import org.eclipse.jdt.core.dom.SimpleName;
import org.eclipse.jdt.core.dom.StringLiteral;
import org.eclipse.jdt.core.dom.SuperFieldAccess;
import org.eclipse.jdt.core.dom.SuperMethodInvocation;
import org.eclipse.jdt.core.dom.ThisExpression;
import org.eclipse.jdt.core.dom.TypeLiteral;
import org.eclipse.jdt.core.dom.VariableDeclaration;
import org.eclipse.jdt.core.dom.VariableDeclarationExpression;
import ai.common.Pair;
import ai.domain.DomainException;
import ai.domain.DomainIntf;
import ai.domain.Variable;
import ai.domain.bool.Bool;
import ai.domain.expressions.AbstractExpressionVisitor;
import ai.domain.expressions.ExpressionEvaluatorIntf;
import ai.domain.intervals.eval.EvaluationUtils;
public class BooleanEvaluator<DI extends DomainIntf<DI>> implements ExpressionEvaluatorIntf<DI>, BooleanConditionEvaluatorIntf<DI>{
public static class BooleanEvaluatorException extends RuntimeException {
private static final long serialVersionUID = -5548898388891577924L;
public BooleanEvaluatorException(String format, Object... args) {
super(String.format(format, args));
}
}
private BooleanEvaluationState<DI> createState(DI conditionMet, DI conditionNotMet) {
return new BooleanEvaluationState<DI>(conditionMet, conditionNotMet);
}
protected class BooleanVisitor extends AbstractExpressionVisitor {
private BooleanEvaluationState<DI> state;
private DI input;
protected void ensureBooleanType(Expression expr) {
if (!EvaluationUtils.isBooleanType(expr))
throw new BooleanEvaluatorException("I shouldn't be here? '%s'", expr);
}
protected void ensureBooleanTypeOrBoxing(Expression expr) {
if (!EvaluationUtils.isBooleanType(expr) && !EvaluationUtils.isBooleanTypeBoxing(expr))
throw new BooleanEvaluatorException("I shouldn't be here? '%s'", expr);
}
private BooleanEvaluationState<DI> acceptChild(Expression child, DI input) {
if (input.isBottom())
return BooleanEvaluationState.create(input, input);
this.input = input;
child.accept(this);
if (state == null)
throw new BooleanEvaluatorException("Child value not calculated? '%s'", child);
if (state.conditionMet == null)
throw new BooleanEvaluatorException("Child value not calculated? '%s'", child);
if (state.conditionMet == null)
throw new BooleanEvaluatorException("Child value not calculated? '%s'", child);
return this.state;
}
protected void setNewState(Expression expr, BooleanEvaluationState<DI> newState) {
if (newState.conditionMet == null || newState.conditionNotMet == null)
throw new BooleanEvaluatorException("Invalid expression semantics? '%s'", expr);
state = newState;
}
@Override
public boolean visit(ArrayAccess node) {
ensureBooleanTypeOrBoxing(node);
setNewState(node, expressionSemantics.processBooleanNode(input, node));
return false;
}
@Override
public boolean visit(ArrayCreation node) {
throw unhandledNodeException(node);
}
@Override
public boolean visit(ArrayInitializer node) {
throw unhandledNodeException(node);
}
@Override
public boolean visit(Assignment node) {
if (EvaluationUtils.isBooleanType(node)){
Assignment.Operator op = node.getOperator();
BooleanEvaluationState<DI> res;
if (op == Assignment.Operator.ASSIGN)
res = acceptChild(node.getRightHandSide(), input);
else if (op == Assignment.Operator.BIT_AND_ASSIGN)
res = handleAnd(node.getLeftHandSide(), node.getRightHandSide());
else if (op == Assignment.Operator.BIT_OR_ASSIGN)
res = handleOr(node.getLeftHandSide(), node.getRightHandSide());
else if (op == Assignment.Operator.BIT_XOR_ASSIGN)
res = handleXor(node.getLeftHandSide(), node.getRightHandSide());
else
throw new BooleanEvaluatorException("Unhandled operator '%s'", op);
DI positive = res.conditionMet.isBottom()? res.conditionMet : expressionSemantics.processBooleanAssignment(res.conditionMet, node.getLeftHandSide(), Bool.TRUE);
DI negative = res.conditionNotMet.isBottom() ? res.conditionNotMet : expressionSemantics.processBooleanAssignment(res.conditionNotMet, node.getLeftHandSide(), Bool.FALSE);
setNewState(node, createState(positive, negative));
} else if (EvaluationUtils.isBooleanTypeBoxing(node))
setNewState(node, expressionSemantics.processBooleanBoxing(input, node));
else
throw new BooleanEvaluatorException("I shouldn't be here? '%s'", node);
return false;
}
@Override
public boolean visit(BooleanLiteral node) {
boolean val = EvaluationUtils.getBooleanValue(node);
setNewState(node, val ? createState(input, input.getBottom()) : createState(input.getBottom(), input));
return false;
}
@Override
public boolean visit(CastExpression node) {
ensureBooleanTypeOrBoxing(node);
setNewState(node, expressionSemantics.processBooleanNode(input, node));
return false;
}
@Override
public boolean visit(CharacterLiteral node) {
throw unhandledNodeException(node);
}
@Override
public boolean visit(ClassInstanceCreation node) {
ensureBooleanTypeOrBoxing(node);
setNewState(node, expressionSemantics.processBooleanNode(input, node));
return false;
}
@Override
public boolean visit(ConditionalExpression node) {
ensureBooleanTypeOrBoxing(node);
//evaluate condition
BooleanEvaluationState<DI> conditionEvalResult = acceptChild(node.getExpression(), input);
BooleanEvaluationState<DI> thenResult = null;
if (!conditionEvalResult.conditionMet.isBottom())
thenResult = acceptChild(node.getThenExpression(), conditionEvalResult.conditionMet);
BooleanEvaluationState<DI> elseResult = null;
if (!conditionEvalResult.conditionNotMet.isBottom())
elseResult = acceptChild(node.getElseExpression(), conditionEvalResult.conditionNotMet);
if (thenResult == null)
setNewState(node, elseResult);
else if (elseResult == null)
setNewState(node, thenResult);
else // both have results
setNewState(node, BooleanEvaluationState.create(thenResult.conditionMet.join(elseResult.conditionMet),
thenResult.conditionNotMet.join(elseResult.conditionNotMet)));
return false;
}
@Override
public boolean visit(FieldAccess node) {
ensureBooleanTypeOrBoxing(node);
setNewState(node, expressionSemantics.processBooleanNode(input, node));
return false;
}
private BooleanEvaluationState<DI> handleXor(Expression leftOperand, Expression rightOperand) {
BooleanEvaluationState<DI> left = acceptChild(leftOperand, input);
BooleanEvaluationState<DI> rightIfLeft = acceptChild(rightOperand, left.conditionMet);
BooleanEvaluationState<DI> rightIfNotLeft = acceptChild(rightOperand, left.conditionNotMet);
return createState(
//(LEFT and not RIGHT) or (not LEFT and RIGHT)
rightIfLeft.conditionNotMet.join(rightIfNotLeft.conditionMet),
//(LEFT and RIGHT) or (not LEFT and not RIGHT)
rightIfLeft.conditionMet.join(rightIfNotLeft.conditionNotMet));
}
private BooleanEvaluationState<DI> handleAnd(Expression leftOperand, Expression rightOperand) {
BooleanEvaluationState<DI> left = acceptChild(leftOperand, input);
BooleanEvaluationState<DI> rightIfLeft = acceptChild(rightOperand, left.conditionMet);
return createState(
//LEFT and RIGHT
rightIfLeft.conditionMet,
//NOT LEFT or NOT RIGHT
left.conditionNotMet.join(rightIfLeft.conditionNotMet)
);
}
private BooleanEvaluationState<DI> handleOr(Expression leftOperand, Expression rightOperand) {
BooleanEvaluationState<DI> left = acceptChild(leftOperand, input);
BooleanEvaluationState<DI> rightIfNotLeft = acceptChild(rightOperand, left.conditionNotMet);
return createState(
//LEFT or RIGHT
left.conditionMet.join(rightIfNotLeft.conditionMet),
//NOT LEFT and NOT RIGHT
rightIfNotLeft.conditionNotMet
);
}
@Override
public boolean visit(InfixExpression node) {
ensureBooleanType(node);
InfixExpression.Operator op = node.getOperator();
if (op == InfixExpression.Operator.XOR)
setNewState(node, handleXor(node.getLeftOperand(), node.getRightOperand()));
else if (op == InfixExpression.Operator.CONDITIONAL_AND)
setNewState(node, handleAnd(node.getLeftOperand(), node.getRightOperand()));
else if (op == InfixExpression.Operator.CONDITIONAL_OR)
setNewState(node, handleOr(node.getLeftOperand(), node.getRightOperand()));
else
setNewState(node, expressionSemantics.processComparison(input, node));
//
// else if (op == InfixExpression.Operator.EQUALS || op == InfixExpression.Operator.NOT_EQUALS){
// setNewState(node, expressionSemantics.processComparison(input, node));
// } else
// throw new BooleanEvaluatorException("Unhandled boolean operator '%s'", op);
return false;
}
@Override
public boolean visit(InstanceofExpression node) {
ensureBooleanType(node);
setNewState(node, expressionSemantics.processBooleanNode(input, node));
return false;
}
@Override
public boolean visit(MethodInvocation node) {
ensureBooleanTypeOrBoxing(node);
setNewState(node, expressionSemantics.processBooleanNode(input, node));
return false;
}
@Override
public boolean visit(SimpleName node) {
ensureBooleanTypeOrBoxing(node);
setNewState(node, expressionSemantics.processBooleanNode(input, node));
return false;
}
@Override
public boolean visit(QualifiedName node) {
ensureBooleanTypeOrBoxing(node);
setNewState(node, expressionSemantics.processBooleanNode(input, node));
return false;
}
@Override
public boolean visit(NullLiteral node) {
setNewState(node, expressionSemantics.processBooleanNode(input, node));
return false;
}
@Override
public boolean visit(NumberLiteral node) {
throw unhandledNodeException(node);
}
@Override
public boolean visit(ParenthesizedExpression node) {
ensureBooleanTypeOrBoxing(node);
setNewState(node, acceptChild(node.getExpression(), input));
return false;
}
@Override
public boolean visit(PostfixExpression node) {
throw unhandledNodeException(node);
}
@Override
public boolean visit(PrefixExpression node) {
ensureBooleanType(node);
PrefixExpression.Operator op = node.getOperator();
if (op == PrefixExpression.Operator.NOT) {
BooleanEvaluationState<DI> res = acceptChild(node.getOperand(), input);
setNewState(node, createState(res.conditionNotMet, res.conditionMet));
} else
throw new BooleanEvaluatorException("Unhandled prefix boolean operator '%s'", op);
return false;
}
@Override
public boolean visit(StringLiteral node) {
throw unhandledNodeException(node);
}
@Override
public boolean visit(SuperFieldAccess node) {
ensureBooleanTypeOrBoxing(node);
setNewState(node, expressionSemantics.processBooleanNode(input, node));
return false;
}
@Override
public boolean visit(SuperMethodInvocation node) {
ensureBooleanTypeOrBoxing(node);
setNewState(node, expressionSemantics.processBooleanNode(input, node));
return false;
}
@Override
public boolean visit(ThisExpression node) {
throw unhandledNodeException(node);
}
@Override
public boolean visit(TypeLiteral node) {
throw unhandledNodeException(node);
}
@Override
public boolean visit(VariableDeclarationExpression node) {
throw unhandledNodeException(node);
}
}
private final BooleanExpressionSemantics<DI> expressionSemantics;
public BooleanEvaluator(BooleanExpressionSemantics<DI> expressionSemantics){
this.expressionSemantics = expressionSemantics;
}
protected final RuntimeException unhandledNodeException(Expression node) {
return new BooleanEvaluatorException("Unhandled node '%s(%s)'", node.getClass(), node);
}
protected boolean isInterestingType(Expression expr) {
try {
return EvaluationUtils.isBooleanType(expr);
} catch (RuntimeException e) {
System.err.println("Error evaluating: " + expr);
throw e;
}
}
private DI processNewVariable(DI input, Variable var, Bool initialValueOrNull, boolean isArgument) {
return expressionSemantics.newBooleanVariable(input, var, initialValueOrNull, isArgument);
}
public BooleanEvaluationState<DI> evaluateCondition(DI input, Expression expr) {
BooleanVisitor eval = new BooleanVisitor();
try {
return eval.acceptChild(expr, input);
} catch (RuntimeException e) {
System.err.println("ERROR evaluating: " + expr);
e.printStackTrace(System.err);
throw e;
}
}
// public Pair<T, DI> evaluateExpression(Expression expr, DI input) {
// ExpressionVisitorBase eval = getEvaluator(input);
// expr.accept(eval);
// Pair<T, DI> result = eval.result;
// if (result == null)
// throw new BooleanEvaluatorException("NO RESULT!! '%s'", expr);
// if (result.left == null)
// throw new BooleanEvaluatorException("NO RESULT!! '%s'", expr);
// if (result.right == null)
// throw new BooleanEvaluatorException("NO RESULT!! '%s'", expr);
// return result;
// }
//
@Override
public DI evaluate(Expression expr, DI input) {
if (!isInterestingType(expr))
return null;
BooleanEvaluationState<DI> x = evaluateCondition(input, expr);
return x.conditionMet.join(x.conditionNotMet);
}
private Pair<Bool, DI> computeInitialValue(Expression exprOrNull, DI input) {
if (exprOrNull == null)
return Pair.create(null, input);
BooleanEvaluationState<DI> x = evaluateCondition(input, exprOrNull);
Bool resultVal = Bool.BOTTOM;
DI resultState = x.conditionMet;
if (!x.conditionMet.isBottom())
resultVal = resultVal.join(Bool.TRUE);
if (!x.conditionNotMet.isBottom()) {
resultVal = resultVal.join(Bool.FALSE);
resultState = resultState.join(x.conditionNotMet);
}
return Pair.create(resultVal, resultState);
}
@Override
public DI evaluate(SimpleName name, Expression initializerOrNull, DI input,
boolean asAssignment) {
if (!isInterestingType(name))
return null;
Pair<Bool, DI> initializer = computeInitialValue(initializerOrNull, input);
Variable variable = EvaluationUtils.tryGetVariable(name);
if (variable == null)
throw new DomainException("Error: '%s'", variable);
if (asAssignment && initializerOrNull != null)
return expressionSemantics.processBooleanAssignment(initializer.right, name, initializer.left);
else
return processNewVariable(initializer.right, variable, initializer.left, asAssignment);
}
@Override
public DI evaluateNewArgument(SimpleName argument, DI input) {
if (!isInterestingType(argument))
return null;
Variable variable = EvaluationUtils.tryGetVariable(argument);
if (variable == null)
throw new DomainException("Error: '%s'", variable);
return processNewVariable(input, variable, null, true);
}
public static <DI extends DomainIntf<DI>> BooleanEvaluator<DI> create(BooleanExpressionSemantics<DI> semantics) {
return new BooleanEvaluator<DI>(semantics);
}
public static <DI extends DomainIntf<DI>> BooleanEvaluationState<DI> createOutput(DI input, Bool value) {
DI positive = input.getBottom();
DI negative = input.getBottom();
if (!value.meet(Bool.TRUE).isBottom())
positive = input;
if (!value.meet(Bool.FALSE).isBottom())
negative = input;
return BooleanEvaluationState.create(positive, negative);
}
}