You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

537 lines
13 KiB

//==============================================================================
//
// Copyright (c) 2002-
// Authors:
// * Dave Parker <david.parker@comlab.ox.ac.uk> (University of Oxford, formerly University of Birmingham)
//
//------------------------------------------------------------------------------
//
// This file is part of PRISM.
//
// PRISM is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// PRISM is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with PRISM; if not, write to the Free Software Foundation,
// Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//
//==============================================================================
package parser.ast;
import java.util.ArrayList;
import param.BigRational;
import parser.*;
import parser.visitor.*;
import prism.PrismLangException;
import prism.PrismUtils;
import parser.type.*;
public class ExpressionFunc extends Expression
{
// Built-in function name constants
public static final int MIN = 0;
public static final int MAX = 1;
public static final int FLOOR = 2;
public static final int CEIL = 3;
public static final int POW = 4;
public static final int MOD = 5;
public static final int LOG = 6;
public static final int MULTI = 7;
// Built-in function names
public static final String names[] = { "min", "max", "floor", "ceil", "pow", "mod", "log", "multi" };
// Min/max function arities
public static final int minArities[] = { 2, 2, 1, 1, 2, 2, 2, 1 };
public static final int maxArities[] = { -1, -1, 1, 1, 2, 2, 2, -1 };
// Function name
private String name = "";
private int code = -1;
// Operands
private ArrayList<Expression> operands;
// Was function written in old style notation (using "func" keyword)?
private boolean oldStyle = false;
// Constructors
public ExpressionFunc()
{
operands = new ArrayList<Expression>();
}
public ExpressionFunc(String name)
{
setName(name);
operands = new ArrayList<Expression>();
}
// Set methods
public void setName(String s)
{
int i, n;
// Set string
name = s;
// Determine and set code
n = names.length;
code = -1;
for (i = 0; i < n; i++) {
if (s.equals(names[i])) {
code = i;
break;
}
}
}
public void addOperand(Expression e)
{
operands.add(e);
}
public void setOperand(int i, Expression e)
{
operands.set(i, e);
}
public void setOldStyle(boolean b)
{
oldStyle = b;
}
// Get methods
public String getName()
{
return name;
}
public int getNameCode()
{
return code;
}
public int getNumOperands()
{
return operands.size();
}
public Expression getOperand(int i)
{
return operands.get(i);
}
public boolean getOldStyle()
{
return oldStyle;
}
public int getMinArity()
{
return code == -1 ? Integer.MAX_VALUE : minArities[code];
}
public int getMaxArity()
{
return code == -1 ? -1 : maxArities[code];
}
// Methods required for Expression:
@Override
public boolean isConstant()
{
int i, n;
n = getNumOperands();
for (i = 0; i < n; i++) {
if (!getOperand(i).isConstant())
return false;
}
return true;
}
@Override
public boolean isProposition()
{
int i, n;
n = getNumOperands();
for (i = 0; i < n; i++) {
if (!getOperand(i).isProposition())
return false;
}
return true;
}
@Override
public Object evaluate(EvaluateContext ec) throws PrismLangException
{
switch (code) {
case MIN:
return evaluateMin(ec);
case MAX:
return evaluateMax(ec);
case FLOOR:
return evaluateFloor(ec);
case CEIL:
return evaluateCeil(ec);
case POW:
return evaluatePow(ec);
case MOD:
return evaluateMod(ec);
case LOG:
return evaluateLog(ec);
}
throw new PrismLangException("Unknown function \"" + name + "\"", this);
}
@Override
public BigRational evaluateExact(EvaluateContext ec) throws PrismLangException
{
switch (code) {
case MIN:
return evaluateMinExact(ec);
case MAX:
return evaluateMaxExact(ec);
case FLOOR:
return evaluateFloorExact(ec);
case CEIL:
return evaluateCeilExact(ec);
case POW:
return evaluatePowExact(ec);
case MOD:
return evaluateModExact(ec);
case LOG:
return evaluateLogExact(ec);
}
throw new PrismLangException("Unknown function \"" + name + "\"", this);
}
private Object evaluateMin(EvaluateContext ec) throws PrismLangException
{
int i, j, n, iMin;
double d, dMin;
if (type instanceof TypeInt) {
iMin = getOperand(0).evaluateInt(ec);
n = getNumOperands();
for (i = 1; i < n; i++) {
j = getOperand(i).evaluateInt(ec);
iMin = (j < iMin) ? j : iMin;
}
return new Integer(iMin);
} else {
dMin = getOperand(0).evaluateDouble(ec);
n = getNumOperands();
for (i = 1; i < n; i++) {
d = getOperand(i).evaluateDouble(ec);
dMin = (d < dMin) ? d : dMin;
}
return new Double(dMin);
}
}
private BigRational evaluateMinExact(EvaluateContext ec) throws PrismLangException
{
BigRational min;
min = getOperand(0).evaluateExact(ec);
for (int i = 1, n = getNumOperands(); i < n; i++) {
min = min.min(getOperand(i).evaluateExact(ec));
}
return min;
}
private Object evaluateMax(EvaluateContext ec) throws PrismLangException
{
int i, j, n, iMax;
double d, dMax;
if (type instanceof TypeInt) {
iMax = getOperand(0).evaluateInt(ec);
n = getNumOperands();
for (i = 1; i < n; i++) {
j = getOperand(i).evaluateInt(ec);
iMax = (j > iMax) ? j : iMax;
}
return new Integer(iMax);
} else {
dMax = getOperand(0).evaluateDouble(ec);
n = getNumOperands();
for (i = 1; i < n; i++) {
d = getOperand(i).evaluateDouble(ec);
dMax = (d > dMax) ? d : dMax;
}
return new Double(dMax);
}
}
private BigRational evaluateMaxExact(EvaluateContext ec) throws PrismLangException
{
BigRational max;
max = getOperand(0).evaluateExact(ec);
for (int i = 1, n = getNumOperands(); i < n; i++) {
max = max.max(getOperand(i).evaluateExact(ec));
}
return max;
}
public Integer evaluateFloor(EvaluateContext ec) throws PrismLangException
{
try {
return evaluateFloor(getOperand(0).evaluateDouble(ec));
} catch (PrismLangException e) {
e.setASTElement(this);
throw e;
}
}
public static int evaluateFloor(double arg) throws PrismLangException
{
// Check for NaN or +/-inf, otherwise possible errors lost in cast to int
if (Double.isNaN(arg) || Double.isInfinite(arg))
throw new PrismLangException("Cannot take floor() of " + arg);
return (int) Math.floor(arg);
}
public Integer evaluateCeil(EvaluateContext ec) throws PrismLangException
{
try {
return evaluateCeil(getOperand(0).evaluateDouble(ec));
} catch (PrismLangException e) {
e.setASTElement(this);
throw e;
}
}
public static int evaluateCeil(double arg) throws PrismLangException
{
double d = Math.ceil(arg);
// Check for NaN or +/-inf, otherwise possible errors lost in cast to int
if (Double.isNaN(d) || Double.isInfinite(d))
throw new PrismLangException("Cannot take ceil() of " + d);
return (int) d;
}
public BigRational evaluateCeilExact(EvaluateContext ec) throws PrismLangException
{
return getOperand(0).evaluateExact(ec).ceil();
}
public BigRational evaluateFloorExact(EvaluateContext ec) throws PrismLangException
{
return getOperand(0).evaluateExact(ec).floor();
}
public Object evaluatePow(EvaluateContext ec) throws PrismLangException
{
try {
if (type instanceof TypeInt) {
return new Integer(evaluatePowInt(getOperand(0).evaluateInt(ec), getOperand(1).evaluateInt(ec)));
} else {
return new Double(evaluatePowDouble(getOperand(0).evaluateDouble(ec), getOperand(1).evaluateDouble(ec)));
}
} catch (PrismLangException e) {
e.setASTElement(this);
throw e;
}
}
public static int evaluatePowInt(int base, int exp) throws PrismLangException
{
// Not allowed to do e.g. pow(2,-2) because of typing (should be pow(2.0,-2) instead)
if (exp < 0)
throw new PrismLangException("Negative exponent not allowed for integer power");
double res = Math.pow(base, exp);
// Check for overflow
if (res > Integer.MAX_VALUE)
throw new PrismLangException("Overflow evaluating integer power");
// Check for underflow
if (res < Integer.MIN_VALUE)
throw new PrismLangException("Underflow evaluating integer power");
return (int) res;
}
public static double evaluatePowDouble(double base, double exp) throws PrismLangException
{
return Math.pow(base, exp);
}
public BigRational evaluatePowExact(EvaluateContext ec) throws PrismLangException
{
BigRational base = getOperand(0).evaluateExact(ec);
BigRational exp = getOperand(1).evaluateExact(ec);
try {
int expInt = exp.toInt();
return base.pow(expInt);
} catch (PrismLangException e) {
throw new PrismLangException("Can not compute pow exactly, as there is a problem with the exponent: " + e.getMessage(), this);
}
}
public Object evaluateMod(EvaluateContext ec) throws PrismLangException
{
try {
return new Integer(evaluateMod(getOperand(0).evaluateInt(ec), getOperand(1).evaluateInt(ec)));
} catch (PrismLangException e) {
e.setASTElement(this);
throw e;
}
}
public static int evaluateMod(int i, int j) throws PrismLangException
{
// Non-positive divisor not allowed
if (j <= 0)
throw new PrismLangException("Attempt to compute modulo with non-positive divisor");
// Take care of negative case (% is remainder, not modulo)
int rem = i % j;
return (rem < 0) ? rem + j : rem;
}
public BigRational evaluateModExact(EvaluateContext ec) throws PrismLangException
{
BigRational a = getOperand(0).evaluateExact(ec);
BigRational b = getOperand(1).evaluateExact(ec);
if (!a.isInteger() && !b.isInteger()) {
throw new PrismLangException("Can not compute mod for non-integer arguments", this);
}
return new BigRational(a.getNum().mod(b.getNum()));
}
public Object evaluateLog(EvaluateContext ec) throws PrismLangException
{
try {
return new Double(evaluateLog(getOperand(0).evaluateDouble(ec), getOperand(1).evaluateDouble(ec)));
} catch (PrismLangException e) {
e.setASTElement(this);
throw e;
}
}
public static double evaluateLog(double x, double b) throws PrismLangException
{
return PrismUtils.log(x, b);
}
public BigRational evaluateLogExact(EvaluateContext ec) throws PrismLangException
{
throw new PrismLangException("Currently, can not compute log exactly", this);
}
@Override
public boolean returnsSingleValue()
{
int i, n;
// Otherwise, true iff all operands true
n = getNumOperands();
for (i = 0; i < n; i++) {
if (!getOperand(i).returnsSingleValue())
return false;
}
return true;
}
// Methods required for ASTElement:
@Override
public Object accept(ASTVisitor v) throws PrismLangException
{
return v.visit(this);
}
@Override
public Expression deepCopy()
{
int i, n;
ExpressionFunc e;
e = new ExpressionFunc(name);
e.setOldStyle(oldStyle);
n = getNumOperands();
for (i = 0; i < n; i++) {
e.addOperand((Expression) getOperand(i).deepCopy());
}
e.setType(type);
e.setPosition(this);
return e;
}
// Standard methods
@Override
public String toString()
{
int i, n;
String s = "";
boolean first = true;
if (!oldStyle)
s += name + "(";
else
s += "func(" + name + ", ";
n = operands.size();
for (i = 0; i < n; i++) {
if (!first)
s += ", ";
else
first = false;
s = s + getOperand(i);
}
s += ")";
return s;
}
@Override
public int hashCode()
{
final int prime = 31;
int result = 1;
result = prime * result + code;
result = prime * result + ((name == null) ? 0 : name.hashCode());
result = prime * result + (oldStyle ? 1231 : 1237);
result = prime * result + ((operands == null) ? 0 : operands.hashCode());
return result;
}
@Override
public boolean equals(Object obj)
{
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
ExpressionFunc other = (ExpressionFunc) obj;
if (code != other.code)
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
if (oldStyle != other.oldStyle)
return false;
if (operands == null) {
if (other.operands != null)
return false;
} else if (!operands.equals(other.operands))
return false;
return true;
}
}
// ------------------------------------------------------------------------------