/*******************************************************************************
* Copyright (c) 2009 University of Edinburgh.
* All rights reserved. This program and the accompanying materials are made
* available under the terms of the BSD Licence, which accompanies this feature
* and can be downloaded from http://groups.inf.ed.ac.uk/pepa/update/licence.txt
******************************************************************************/
package uk.ac.ed.inf.biopepa.core.compiler;
import uk.ac.ed.inf.biopepa.core.BioPEPAException;
import uk.ac.ed.inf.biopepa.core.compiler.CompiledFunction.Function;
import uk.ac.ed.inf.biopepa.core.compiler.ProblemInfo.Severity;
import uk.ac.ed.inf.biopepa.core.dom.*;
public class ExpressionEvaluatorVisitor extends DefaultCompilerVisitor {
protected CompiledExpression node = null;
public ExpressionEvaluatorVisitor(ModelCompiler compiler) {
super(compiler);
}
public CompiledExpression getExpressionNode() {
return node;
}
@Override
public boolean visit(Name name) throws BioPEPAException {
String identifier = name.getIdentifier();
if (identifier == null)
throw new IllegalArgumentException();
if (identifier == compiler.getCurrentlyVisitedVariable()) {
compiler.problemRequestor.accept(ProblemKind.CIRCULAR_USAGE, name);
throw new CompilerException();
}
/* Check if the variable is dynamic in which case it cannot be
* compiled.
*/
if (compiler.isDynamic(identifier)) {
// System.out.println ("The identifier: " + identifier + " is dynamic");
node = new CompiledDynamicComponent(identifier);
return true;
} else {
VariableData data = compiler.checkAndGetVariableData(identifier);
if (data == null) {
// variable not yet defined
ProblemKind pKind = ProblemKind.VARIABLE_USED_BUT_NOT_DEFINED;
pKind.setMessage("Variable: " + identifier +
" used but not defined");
compiler.problemRequestor.accept(pKind, name);
throw new CompilerException();
}
/*
* So if the variable is not dynamic then we still set the expanded form
* to CompiledDynamicComponent.
*/
CompiledExpression nodeExp = data.getValue();
if (nodeExp != null){
node = data.getValue().clone();
node.setExpandedForm(new CompiledDynamicComponent(identifier));
} else {
// It's unclear what to do here, this probably means that there
// was an error in the definition of this variable, so that the
// variable itself has been defined but with an expression
// which itself contains an error. eg if 'b' is undefined then
// a = b + 1;
// c = a;
// We'll get here when evaluating the expression in the def of
// 'c', since 'a' is defined but by an errorful expression.
// I think the best thing to do here is nothing since we should
// have caught the original error and will report it there and
// the current 'error' will be automatically fixed.
// System.out.println ("Sheesh, nodeExp is null for: " + identifier);
}
return true;
}
}
@Override
public boolean visit(InfixExpression infixExpression) throws BioPEPAException {
ExpressionEvaluatorVisitor vlhs = new ExpressionEvaluatorVisitor(compiler);
infixExpression.getLeftHandSide().accept(vlhs);
ExpressionEvaluatorVisitor vrhs = new ExpressionEvaluatorVisitor(compiler);
infixExpression.getRightHandSide().accept(vrhs);
if (vlhs.node instanceof CompiledNumber && vrhs.node instanceof CompiledNumber) {
Number number;
CompiledNumber lenn = ((CompiledNumber) vlhs.node);
CompiledNumber renn = ((CompiledNumber) vrhs.node);
switch (infixExpression.getOperator()) {
case PLUS:
if (lenn.evaluatesToDouble() || renn.evaluatesToDouble())
number = new Double(lenn.doubleValue() + renn.doubleValue());
else
number = new Long(lenn.longValue() + renn.longValue());
break;
case MINUS:
if (lenn.evaluatesToDouble() || renn.evaluatesToDouble())
number = new Double(lenn.doubleValue() - renn.doubleValue());
else
number = new Long(lenn.longValue() - renn.longValue());
break;
case TIMES:
if (lenn.evaluatesToDouble() || renn.evaluatesToDouble())
number = new Double(lenn.doubleValue() * renn.doubleValue());
else
number = new Long(lenn.longValue() * renn.longValue());
break;
case DIVIDE:
if (renn.doubleValue() == 0.00) {
compiler.problemRequestor.accept(Severity.ERROR, "Divide by zero.", infixExpression);
throw new CompilerException();
}
if (lenn.evaluatesToLong() && renn.evaluatesToLong()) {
long l = lenn.longValue(), r = renn.longValue();
long a = l / r;
if (a * r == l) {
number = new Long(a);
break;
}
}
number = new Double(lenn.doubleValue() / renn.doubleValue());
break;
case POWER:
double d = Math.pow(lenn.doubleValue(), renn.doubleValue());
if (Double.isNaN(d) || Double.isInfinite(d)) {
compiler.problemRequestor
.accept(Severity.ERROR, "Cannot evaluate the expression.", infixExpression);
throw new CompilerException();
}
if ((d - ((long) d)) != 0.00)
number = new Double(d);
else
number = new Long(((long) d));
break;
default:
compiler.problemRequestor.accept(ProblemKind.INVALID_OPERATOR_FOR_DOUBLE, infixExpression);
throw new CompilerException();
}
node = new CompiledNumber(number);
// Set expandedForm if required
if (lenn.hasExpandedForm() || renn.hasExpandedForm()) {
CompiledOperatorNode eNode = new CompiledOperatorNode();
eNode.setOperator(infixExpression.getOperator());
if (lenn.hasExpandedForm())
eNode.left = lenn.expandedForm;
else
eNode.left = lenn;
if (renn.hasExpandedForm())
eNode.right = renn.expandedForm;
else
eNode.right = renn;
node.setExpandedForm(eNode);
}
return true;
}
CompiledOperatorNode tnode = new CompiledOperatorNode();
tnode.setOperator(infixExpression.getOperator());
tnode.setLeft(vlhs.node);
tnode.setRight(vrhs.node);
node = tnode;
return true;
}
@Override
public boolean visit(NumberLiteral numberLiteral) throws BioPEPAException {
Number number;
try {
number = Long.parseLong(numberLiteral.getToken());
} catch (NumberFormatException e1) {
try {
number = Double.parseDouble(numberLiteral.getToken());
} catch (NumberFormatException e2) {
compiler.problemRequestor.accept(ProblemKind.INVALID_NUMBER_LITERAL, numberLiteral);
throw new CompilerException();
}
}
node = new CompiledNumber(number);
return true;
}
@Override
public boolean visit(FunctionCall functionCall) throws BioPEPAException {
// The code contained within the 'if' statement here was added by
// me (allan) in order to potentially allow rate functions with
// predefined rate laws contained within an expression, ie. that are
// not at the top-level. We were certainly handling these incorrectly
// we should at least add a problem saying you're not allowed to
// have these at the non-top level. So for example if you write
// r = [ 1 + fMA(a) ]
// Then before this code was added you would silently get an error
// when the function was attempted to be evaluated. Now you at least
// do not get that error, however I'm not sure that all is well,
// what happens if you write D[fMA(r)] in the system equation?
Function f = CompiledFunction.checkFunction(compiler, functionCall);
if (f.isRateLaw()) {
// Hmm, not sure about this, basically I'm worried that if I
// write r = [ 1 + fMA(a) ]
// then I'll get a warning that the reaction rate does not depend
// on its Reactants.
// predefinedLaw = true;
CompiledFunction efn = new CompiledFunction();
efn.setFunction(f);
int i = 0;
ExpressionEvaluatorVisitor eev;
for (Expression e : functionCall.arguments()) {
eev = new ExpressionEvaluatorVisitor(compiler);
e.accept(eev);
efn.setArgument(i++, eev.getExpressionNode());
node = efn;
}
return true;
}
IPredefinedFunctionEvaluator evaluator;
evaluator = CompiledFunction.getFunctionEvaluator(compiler, functionCall);
try {
node = evaluator.evaluate();
} catch (EvaluationException e) {
// Already handled by the code throwing the EvaluationException
}
return true;
}
@Override
public boolean visit(SystemVariable variable) throws BioPEPAException {
switch (variable.getVariable()) {
case TIME:
node = new CompiledSystemVariable(CompiledSystemVariable.Variable.TIME);
break;
default:
}
return true;
}
}