Evaluator.java
/**
*
*/
package edu.udel.cis.vsl.civl.semantics;
import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import java.util.List;
import java.util.Vector;
import edu.udel.cis.vsl.civl.log.ErrorLog;
import edu.udel.cis.vsl.civl.log.ExecutionException;
import edu.udel.cis.vsl.civl.model.IF.expression.ArrayIndexExpression;
import edu.udel.cis.vsl.civl.model.IF.expression.BinaryExpression;
import edu.udel.cis.vsl.civl.model.IF.expression.BooleanLiteralExpression;
import edu.udel.cis.vsl.civl.model.IF.expression.CastExpression;
import edu.udel.cis.vsl.civl.model.IF.expression.ConditionalExpression;
import edu.udel.cis.vsl.civl.model.IF.expression.Expression;
import edu.udel.cis.vsl.civl.model.IF.expression.IntegerLiteralExpression;
import edu.udel.cis.vsl.civl.model.IF.expression.RealLiteralExpression;
import edu.udel.cis.vsl.civl.model.IF.expression.StringLiteralExpression;
import edu.udel.cis.vsl.civl.model.IF.expression.UnaryExpression;
import edu.udel.cis.vsl.civl.model.IF.expression.UnaryExpression.UNARY_OPERATOR;
import edu.udel.cis.vsl.civl.model.IF.expression.VariableExpression;
import edu.udel.cis.vsl.civl.model.IF.type.ArrayType;
import edu.udel.cis.vsl.civl.model.IF.type.PointerType;
import edu.udel.cis.vsl.civl.model.IF.type.PrimitiveType;
import edu.udel.cis.vsl.civl.model.IF.type.Type;
import edu.udel.cis.vsl.civl.model.IF.variable.Variable;
import edu.udel.cis.vsl.civl.state.DynamicScope;
import edu.udel.cis.vsl.civl.state.State;
import edu.udel.cis.vsl.civl.util.ExecutionProblem.Certainty;
import edu.udel.cis.vsl.civl.util.ExecutionProblem.ErrorKind;
import edu.udel.cis.vsl.sarl.IF.SymbolicUniverse;
import edu.udel.cis.vsl.sarl.IF.expr.BooleanExpression;
import edu.udel.cis.vsl.sarl.IF.expr.NumericExpression;
import edu.udel.cis.vsl.sarl.IF.expr.SymbolicExpression;
import edu.udel.cis.vsl.sarl.IF.expr.SymbolicExpression.SymbolicOperator;
import edu.udel.cis.vsl.sarl.IF.number.IntegerNumber;
import edu.udel.cis.vsl.sarl.IF.number.Number;
import edu.udel.cis.vsl.sarl.IF.type.SymbolicTupleType;
import edu.udel.cis.vsl.sarl.IF.type.SymbolicType;
import edu.udel.cis.vsl.sarl.collections.IF.SymbolicSequence;
import edu.udel.cis.vsl.sarl.number.real.RealNumberFactory;
/**
* An evaluator is used to evaluate expressions.
*
* @author Timothy K. Zirkel (zirkel)
*
*/
public class Evaluator {
private SymbolicUniverse symbolicUniverse;
private RealNumberFactory numberFactory = new RealNumberFactory();
private SymbolicTupleType pointerType;
private ErrorLog log;
/**
* An evaluator is used to evaluate expressions.
*
* @param symbolicUniverse
* The symbolic universe for the expressions.
*/
public Evaluator(SymbolicUniverse symbolicUniverse, ErrorLog log) {
List<SymbolicType> pointerComponents = new Vector<SymbolicType>();
this.symbolicUniverse = symbolicUniverse;
pointerComponents.add(symbolicUniverse.integerType());
pointerComponents.add(symbolicUniverse.integerType());
pointerComponents.add(symbolicUniverse.arrayType(symbolicUniverse
.integerType()));
pointerType = symbolicUniverse.tupleType(
symbolicUniverse.stringObject("pointer"), pointerComponents);
this.log = log;
}
public ErrorLog log() {
return log;
}
/**
* Evaluate a generic expression. One of the overloaded evaluate methods for
* specific expressions should always be used instead.
*/
public SymbolicExpression evaluate(State state, int pid,
Expression expression) {
SymbolicExpression result = null;
if (expression instanceof ArrayIndexExpression) {
result = evaluate(state, pid, (ArrayIndexExpression) expression);
} else if (expression instanceof BinaryExpression) {
result = evaluate(state, pid, (BinaryExpression) expression);
} else if (expression instanceof BooleanLiteralExpression) {
result = evaluate(state, pid, (BooleanLiteralExpression) expression);
} else if (expression instanceof ConditionalExpression) {
result = evaluate(state, pid, (ConditionalExpression) expression);
} else if (expression instanceof CastExpression) {
result = evaluate(state, pid, (CastExpression) expression);
} else if (expression instanceof IntegerLiteralExpression) {
result = evaluate(state, pid, (IntegerLiteralExpression) expression);
} else if (expression instanceof RealLiteralExpression) {
result = evaluate(state, pid, (RealLiteralExpression) expression);
} else if (expression instanceof StringLiteralExpression) {
result = evaluate(state, pid, (StringLiteralExpression) expression);
} else if (expression instanceof UnaryExpression) {
result = evaluate(state, pid, (UnaryExpression) expression);
} else if (expression instanceof VariableExpression) {
result = evaluate(state, pid, (VariableExpression) expression);
}
if (result != null) {
result = (SymbolicExpression) symbolicUniverse.canonic(result);
}
return result;
}
/**
* Evaluate a conditional expression.
*
* @param state
* The state of the program.
* @param pid
* the pid of the currently executing process.
* @param expression
* The conditional expression.
* @return A symbolic expression for the result of the conditional.
*/
public SymbolicExpression evaluate(State state, int pid,
ConditionalExpression expression) {
SymbolicExpression condition = evaluate(state, pid,
expression.getCondition());
SymbolicExpression trueBranch = evaluate(state, pid,
expression.getTrueBranch());
SymbolicExpression falseBranch = evaluate(state, pid,
expression.getFalseBranch());
assert condition instanceof BooleanExpression;
return symbolicUniverse.cond((BooleanExpression) condition, trueBranch,
falseBranch);
}
/**
* Evaluate an array index expression.
*
* @param state
* The state of the program.
* @param pid
* The pid of the currently executing process.
* @param expression
* The array index expression.
* @return A symbolic expression for an array read.
*/
public SymbolicExpression evaluate(State state, int pid,
ArrayIndexExpression expression) {
SymbolicExpression array = evaluate(state, pid, expression.array());
SymbolicExpression index = evaluate(state, pid, expression.index());
while (array.type().equals(pointerType)) {
array = dereference(state, pid, array);
}
// TODO: simplify index?
return symbolicUniverse.arrayRead(array, (NumericExpression) index);
}
/**
* Evaluate a binary expression.
*
* @param state
* The state of the program.
* @param pid
* The pid of the currently executing process.
* @param expression
* The binary expression.
* @return A symbolic expression for the binary operation.
*/
public SymbolicExpression evaluate(State state, int pid,
BinaryExpression expression) {
SymbolicExpression left = evaluate(state, pid, expression.left());
SymbolicExpression right = evaluate(state, pid, expression.right());
// TODO: Check all expression types.
switch (expression.operator()) {
case PLUS:
return symbolicUniverse.add((NumericExpression) left,
(NumericExpression) right);
case MINUS:
return symbolicUniverse.subtract((NumericExpression) left,
(NumericExpression) right);
case TIMES:
return symbolicUniverse.multiply((NumericExpression) left,
(NumericExpression) right);
case DIVIDE:
return symbolicUniverse.divide((NumericExpression) left,
(NumericExpression) right);
case LESS_THAN:
return symbolicUniverse.lessThan((NumericExpression) left,
(NumericExpression) right);
case LESS_THAN_EQUAL:
return symbolicUniverse.lessThanEquals((NumericExpression) left,
(NumericExpression) right);
case EQUAL:
return symbolicUniverse.equals(left, right);
case NOT_EQUAL:
return symbolicUniverse.not(symbolicUniverse.equals(left, right));
case AND:
return symbolicUniverse.and((BooleanExpression) left,
(BooleanExpression) right);
case OR:
return symbolicUniverse.or((BooleanExpression) left,
(BooleanExpression) right);
case MODULO:
return symbolicUniverse.modulo((NumericExpression) left,
(NumericExpression) right);
}
return null;
}
/**
* Evaluate a boolean literal expression.
*
* @param state
* The state of the program.
* @param pid
* The pid of the currently executing process.
* @param expression
* The boolean literal expression.
* @return The symbolic representation of the boolean literal expression.
*/
public SymbolicExpression evaluate(State state, int pid,
BooleanLiteralExpression expression) {
return symbolicUniverse.bool(expression.value());
}
/**
*
* @param state
* The state of the program.
* @param pid
* The pid of the currently executing process.
* @param expression
* The cast expression.
* @return The symbolic representation of the cast expression.
*/
public SymbolicExpression evaluate(State state, int pid,
CastExpression expression) {
SymbolicExpression uncastExpression = evaluate(state, pid,
expression.getExpression());
Type castType = expression.getCastType();
SymbolicType symbolicType = symbolicType(castType);
SymbolicExpression result;
if (castType == null) {
log.report(new ExecutionException(ErrorKind.OTHER,
Certainty.CONCRETE, "Unable to perform cast : "
+ expression + ". Not implemented."));
}
result = symbolicUniverse.cast(symbolicType, uncastExpression);
return result;
}
private SymbolicType symbolicType(Type type) {
SymbolicType result = null;
if (type instanceof PrimitiveType) {
switch (((PrimitiveType) type).primitiveType()) {
case BOOL:
result = symbolicUniverse.booleanType();
break;
case INT:
result = symbolicUniverse.integerType();
break;
case REAL:
result = symbolicUniverse.realType();
break;
case STRING:
default:
result = null;
}
} else if (type instanceof ArrayType) {
result = symbolicUniverse.arrayType(symbolicType(((ArrayType) type)
.baseType()));
}
return result;
}
/**
* Evalute an integer literal expression.
*
* @param state
* The state of the program.
* @param pid
* The pid of the currently executing process.
* @param expression
* The integer literal expression.
* @return The symbolic representation of the integer literal expression.
*/
public SymbolicExpression evaluate(State state, int pid,
IntegerLiteralExpression expression) {
return symbolicUniverse.integer(expression.value().intValue());
}
/**
* Evaluate a real literal expression.
*
* @param state
* The state of the program.
* @param pid
* The pid of the currently executing process.
* @param expression
* The real literal expression.
* @return The symbolic representation of the real literal expression.
*/
public SymbolicExpression evaluate(State state, int pid,
RealLiteralExpression expression) {
return symbolicUniverse.number(symbolicUniverse
.numberObject(numberFactory.rational(expression.value()
.toPlainString())));
}
/**
* Evaluate a string literal expression.
*
* @param state
* The state of the program.
* @param pid
* The pid of the currently executing process.
* @param expression
* The string literal expression.
* @return The symbolic representation of the string literal expression.
*/
public SymbolicExpression evaluate(State state, int pid,
StringLiteralExpression expression) {
// StringObject result =
// symbolicUniverse.stringObject(expression.value());
// TODO: Figure this out.
// Right now, strings are intercepted in the executor.
// They are used in write statements, and are
// just passed to the PrintStream.
return null;
}
/**
* Evaluate a unary expression.
*
* @param state
* The state of the program.
* @param pid
* The pid of the currently executing process.
* @param expression
* The unary expression.
* @return The symbolic representation of the unary expression.
*/
public SymbolicExpression evaluate(State state, int pid,
UnaryExpression expression) {
switch (expression.operator()) {
case NEGATIVE:
return symbolicUniverse.minus((NumericExpression) evaluate(state,
pid, expression.operand()));
case NOT:
return symbolicUniverse.not((BooleanExpression) evaluate(state,
pid, expression.operand()));
case ADDRESSOF:
return reference(state, pid, expression.operand());
case DEREFERENCE:
return dereference(state, pid, expression.operand());
}
return null;
}
SymbolicExpression reference(State state, int pid, Expression operand) {
SymbolicExpression result = null;
List<SymbolicExpression> navigationSequence = navigationSequence(state,
pid, operand);
List<SymbolicExpression> components = new Vector<SymbolicExpression>();
Variable variable = pointerTarget(operand);
int scope = state.getScopeId(pid, variable);
components.add(symbolicUniverse.integer(scope));
components.add(symbolicUniverse.integer(variable.vid()));
components.add(symbolicUniverse.array(symbolicUniverse.integerType(),
navigationSequence));
result = symbolicUniverse.tuple(pointerType, components);
return result;
}
private Variable pointerTarget(Expression target) {
Variable result = null;
if (target instanceof VariableExpression) {
result = ((VariableExpression) target).variable();
} else if (target instanceof ArrayIndexExpression) {
result = pointerTarget(((ArrayIndexExpression) target).array());
}
return result;
}
private List<SymbolicExpression> navigationSequence(State state, int pid,
Expression operand) {
List<SymbolicExpression> result = new Vector<SymbolicExpression>();
// TODO: Variable expressions return empty sequence, but need to error
// check otherwise.
if (operand instanceof ArrayIndexExpression) {
result.addAll(navigationSequence(state, pid,
((ArrayIndexExpression) operand).array()));
result.add(evaluate(state, pid,
((ArrayIndexExpression) operand).index()));
}
return result;
}
private SymbolicExpression dereference(State state, int pid,
Expression operand) {
SymbolicExpression pointer = evaluate(state, pid, operand);
return dereference(state, pid, pointer);
}
SymbolicExpression dereference(State state, int pid,
SymbolicExpression pointer) {
SymbolicExpression result = null;
SymbolicSequence<?> pointerTuple;
Number scopeNumber;
Number variableNumber;
int scopeID;
int variableID;
SymbolicExpression value;
assert pointer.type().equals(pointerType);
assert pointer.numArguments() == 1;
assert pointer.argument(0) instanceof SymbolicSequence;
assert ((SymbolicSequence<?>) pointer.argument(0)).size() == 3;
pointerTuple = (SymbolicSequence<?>) pointer.argument(0);
scopeNumber = symbolicUniverse
.extractNumber((NumericExpression) pointerTuple.get(0));
variableNumber = symbolicUniverse
.extractNumber((NumericExpression) pointerTuple.get(1));
scopeID = ((IntegerNumber) scopeNumber).intValue();
variableID = ((IntegerNumber) variableNumber).intValue();
value = state.getScope(scopeID).getValue(variableID);
result = navigateReference(state, pid, value,
(SymbolicExpression) pointerTuple.get(2));
return result;
}
private SymbolicExpression navigateReference(State state, int pid,
SymbolicExpression base, SymbolicExpression sequence) {
SymbolicExpression result = base;
SymbolicSequence<?> innerSequence;
assert sequence.argument(0) instanceof SymbolicSequence;
innerSequence = (SymbolicSequence<?>) sequence.argument(0);
for (int i = 0; i < innerSequence.size(); i++) {
NumericExpression argumentNumber;
assert innerSequence.get(i) instanceof NumericExpression;
argumentNumber = (NumericExpression) innerSequence.get(i);
if (result.operator() == SymbolicOperator.DENSE_ARRAY_WRITE
|| result.operator() == SymbolicOperator.ARRAY_WRITE) {
result = symbolicUniverse.arrayRead(result, argumentNumber);
}
// TODO: handle otherwise
}
return result;
}
/**
* Evaluate a variable expression.
*
* @param state
* The state of the program.
* @param pid
* The pid of the currently executing process.
* @param expression
* The variable expression.
* @return
*/
public SymbolicExpression evaluate(State state, int pid,
VariableExpression expression) {
SymbolicExpression currentValue = state.valueOf(pid,
expression.variable());
if (currentValue == null) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
PrintStream ps = new PrintStream(baos);
state.print(ps);
log.report(new ExecutionException(ErrorKind.UNDEFINED_VALUE,
Certainty.PROVEABLE,
"Attempt to read unitialized variable: "
+ expression.variable() + "\n\n" + baos.toString()));
}
return symbolicUniverse.reasoner(
(BooleanExpression) state.pathCondition()).simplify(
currentValue);
}
/**
* Evaluate an expression that returns a variable. This method gets the
* actual variable, not the value stored in the variable. It is used for
* finding the correct location to perform a write.
*
* @param state
* The state of the program.
* @param pid
* The pid of the currently executing process.
* @param expression
* The expression for the variable.
* @return The variable.
*/
public Variable getVariable(State state, int pid, Expression expression) {
DynamicScope scope = state.getScope(state.process(pid).scope());
if (expression instanceof VariableExpression) {
return ((VariableExpression) expression).variable();
} else if (expression instanceof ArrayIndexExpression) {
return baseArray(scope, (ArrayIndexExpression) expression);
} else if (expression instanceof UnaryExpression) {
SymbolicExpression pointer;
SymbolicSequence<?> pointerTuple;
Number scopeNumber;
Number variableNumber;
int scopeID;
int variableID;
Variable variable;
assert ((UnaryExpression) expression).operator() == UNARY_OPERATOR.DEREFERENCE;
pointer = evaluate(state, pid,
((UnaryExpression) expression).operand());
do {
assert pointer.type().equals(pointerType);
assert pointer.numArguments() == 1;
assert pointer.argument(0) instanceof SymbolicSequence;
assert ((SymbolicSequence<?>) pointer.argument(0)).size() == 3;
pointerTuple = (SymbolicSequence<?>) pointer.argument(0);
scopeNumber = symbolicUniverse
.extractNumber((NumericExpression) pointerTuple.get(0));
variableNumber = symbolicUniverse
.extractNumber((NumericExpression) pointerTuple.get(1));
scopeID = ((IntegerNumber) scopeNumber).intValue();
variableID = ((IntegerNumber) variableNumber).intValue();
variable = state.getScope(scopeID).lexicalScope()
.getVariable(variableID);
pointer = state.getScope(scopeID).getValue(variableID);
} while (variable.type() instanceof PointerType);
return variable;
}
throw new RuntimeException("Retrieving variable from " + expression
+ " not implemented.");
}
int getPointerTargetScope(State state, int pid, Expression expression) {
SymbolicExpression pointer;
SymbolicSequence<?> pointerTuple;
Number scopeNumber;
int scopeID;
assert ((UnaryExpression) expression).operator() == UNARY_OPERATOR.DEREFERENCE;
pointer = evaluate(state, pid, ((UnaryExpression) expression).operand());
assert pointer.type().equals(pointerType);
assert pointer.numArguments() == 1;
assert pointer.argument(0) instanceof SymbolicSequence;
assert ((SymbolicSequence<?>) pointer.argument(0)).size() == 3;
pointerTuple = (SymbolicSequence<?>) pointer.argument(0);
scopeNumber = symbolicUniverse
.extractNumber((NumericExpression) pointerTuple.get(0));
scopeID = ((IntegerNumber) scopeNumber).intValue();
return scopeID;
}
/**
* Get the variable at the base of a (possibly multi-dimensional) array.
*
* @param scope
* The dynamic scope containing this array reference.
* @param expression
* The array index expression.
* @return The variable corresponding to the base of this array.
*/
private Variable baseArray(DynamicScope scope,
ArrayIndexExpression expression) {
if (expression.array() instanceof ArrayIndexExpression) {
return baseArray(scope, ((ArrayIndexExpression) expression.array()));
} else if (expression.array() instanceof VariableExpression) {
return ((VariableExpression) expression.array()).variable();
}
return null;
}
SymbolicType pointerType() {
return pointerType;
}
}