/*
* Copyright 2008 Reg Whitton
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.java.dev.eval;
import java.math.BigDecimal;
import java.util.Map;
/**
* A simple expression evaluator.
* <P>
* This is intended for use in simple domain specific languages, and was originally written for test configuration.
* <P>
* Example of use:
*
* <PRE>
* Expression exp = new Expression("(x + y)/2");
*
* Map<String, BigDecimal> variables = new HashMap<String, BigDecimal>();
* variables.put("x", new BigDecimal("4.32"));
* variables.put("y", new BigDecimal("342.1"));
*
* BigDecimal result = exp.eval(variables);
*
* System.out.println(result);
* </PRE>
* <P>
* The following operators are supported:
* <UL>
* <LI>The basic arithmetic operations as provided by the {@link BigDecimal} class:
* <DL>
* <DT>+</DT>
* <DD>addition</DD>
* <DT>-</DT>
* <DD>subtraction</DD>
* <DT>*</DT>
* <DD>multiplication</DD>
* <DT>/</DT>
* <DD>division (rounds to 34 digits)</DD>
* <DT>%</DT>
* <DD>the remainder when dividing the preceding number by the following number (this is not module - the result can be negative,
* rounds to 34 digits)</DD>
* <DT>abs</DT>
* <DD>absolute of the following number (negative numbers have their sign reversed)</DD>
* <DT>pow</DT>
* <DD>raise the preceding number to the power of the following number (this can only raise to power of positive integers or zero)</DD>
* <DT>int</DT>
* <DD>round the following number to an integer</DD>
* </DL>
* <BR>
* </LI>
* <LI>Ternary (or conditional) expressions. <BR>
* <I>condition ? value-if-condition-is-true : value-if-condition-is-true</I><BR>
* For example:<BR>
* <CODE>x > y ? x : y</CODE><BR>
* Yields the larger of the two variables x and y<BR>
* <BR>
* </LI>
* <LI>The following comparison operations:
* <DL>
* <DT><</DT>
* <DD>less than</DD>
* <DT><=</DT>
* <DD>less than or equals</DD>
* <DT>==</DT>
* <DD>equals</DD>
* <DT>></DT>
* <DD>greater than</DD>
* <DT>>=</DT>
* <DD>greater than or equals</DD>
* <DT>!=</DT>
* <DT><></DT>
* <DD>not equals</DD>
* </DL>
* </LI> <BR>
* <LI>The following boolean operations:
* <DL>
* <DT>&&</DT>
* <DD>and</DD>
* <DT>||</DT>
* <DD>or</DD>
* </DL>
* </LI>
* </UL>
* <P>
* Comparison and boolean operation yield 1 for true, or 0 for false if used directly.
* </P>
* <P>
* Expressions are evaluated using the precedence rules found in Java, and parentheses can be used to control the evaluation order.
* <P>
* <P>
* Example expressions:
*
* <PRE>
* 2*2
* 2+2
* 100/2
* x/100 * 17.5
* 2 pow 32 - 1
* 2 pow (32 - 1)
* 2 pow int 21.5
* abs -1.23E-12
* x > y ? x : y
* x > y && x != 4 ? x : y
* y > 4*x ? 4*y : z/3
* </PRE>
*
* @author Reg Whitton
*/
public class Expression {
/**
* The root of the tree of arithmetic operations.
*/
private final Operation rootOperation;
/**
* Construct an {@link Expression} that may be used multiple times to evaluate the expression using different sets of variables.
* This holds the results of parsing the expression to minimise further work.
*
* @param expression
* the arithmetic expression to be parsed.
*/
public Expression(String expression) {
this.rootOperation = new Compiler(expression).compile();
}
/**
* Evaluate the expression with the given set of values.
*
* @param variables
* the values to use in the expression.
* @return the result of the evaluation
*/
public BigDecimal eval(Map<?,?> variables) {
return this.rootOperation.eval(variables);
}
/**
* Evaluate the expression which does not reference any variables.
*
* @return the result of the evaluation
*/
public BigDecimal eval() {
return this.eval((Map<?,?>) null);
}
/**
* A convenience method that constructs an {@link Expression} and evaluates it.
*
* @param expression
* the expression to evaluate.
* @param variables
* the values to use in the evaluation.
* @return the result of the evaluation
*/
public static BigDecimal eval(String expression, Map<?,?> variables) {
return new Expression(expression).eval(variables);
}
/**
* A convenience method that constructs an {@link Expression} that references no variables and evaluates it.
*
* @param expression
* the expression to evaluate.
* @return the result of the evaluation
*/
public static BigDecimal eval(String expression) {
return new Expression(expression).eval();
}
/**
* Creates a string showing expression as it has been parsed.
*/
@Override
public String toString() {
return this.rootOperation.toString();
}
}