From 78f89d7f5527bb6d4077deb98bd51c10b79ade4e Mon Sep 17 00:00:00 2001 From: kruland2607 Date: Fri, 10 Aug 2012 19:13:50 +0000 Subject: [PATCH] Exploded the exp4j source into our src/ directory. This allows us to more easily modify changes we make to the source. I inlined one call to Arrays.copyOf. git-svn-id: https://openrocket.svn.sourceforge.net/svnroot/openrocket/trunk@967 180e2498-e6e9-4542-8430-84ac67f01cd8 --- android/.classpath | 1 - android/libs/exp4j-rdg.jar | Bin 56364 -> 0 bytes core/.classpath | 1 - core/build.xml | 1 - .../de/congrace/exp4j/AbstractExpression.java | 77 ++++++ core/src/de/congrace/exp4j/Calculable.java | 31 +++ .../de/congrace/exp4j/CalculationToken.java | 76 ++++++ .../exp4j/CommandlineInterpreter.java | 59 +++++ .../src/de/congrace/exp4j/CustomFunction.java | 90 +++++++ core/src/de/congrace/exp4j/Example.java | 77 ++++++ .../de/congrace/exp4j/ExpressionBuilder.java | 135 ++++++++++ .../exp4j/FunctionSeparatorToken.java | 17 ++ core/src/de/congrace/exp4j/FunctionToken.java | 161 ++++++++++++ .../de/congrace/exp4j/InfixTranslator.java | 109 ++++++++ .../exp4j/InvalidCustomFunctionException.java | 9 + core/src/de/congrace/exp4j/NumberToken.java | 66 +++++ core/src/de/congrace/exp4j/OperatorToken.java | 243 ++++++++++++++++++ .../de/congrace/exp4j/ParenthesisToken.java | 69 +++++ .../de/congrace/exp4j/PostfixExpression.java | 116 +++++++++ core/src/de/congrace/exp4j/Token.java | 50 ++++ core/src/de/congrace/exp4j/Tokenizer.java | 222 ++++++++++++++++ .../exp4j/UnknownFunctionException.java | 37 +++ .../exp4j/UnparsableExpressionException.java | 47 ++++ core/src/de/congrace/exp4j/Variable.java | 107 ++++++++ core/src/de/congrace/exp4j/VariableSet.java | 42 +++ core/src/de/congrace/exp4j/VariableToken.java | 47 ++++ 26 files changed, 1887 insertions(+), 3 deletions(-) delete mode 100644 android/libs/exp4j-rdg.jar create mode 100644 core/src/de/congrace/exp4j/AbstractExpression.java create mode 100644 core/src/de/congrace/exp4j/Calculable.java create mode 100644 core/src/de/congrace/exp4j/CalculationToken.java create mode 100644 core/src/de/congrace/exp4j/CommandlineInterpreter.java create mode 100644 core/src/de/congrace/exp4j/CustomFunction.java create mode 100644 core/src/de/congrace/exp4j/Example.java create mode 100644 core/src/de/congrace/exp4j/ExpressionBuilder.java create mode 100644 core/src/de/congrace/exp4j/FunctionSeparatorToken.java create mode 100644 core/src/de/congrace/exp4j/FunctionToken.java create mode 100644 core/src/de/congrace/exp4j/InfixTranslator.java create mode 100644 core/src/de/congrace/exp4j/InvalidCustomFunctionException.java create mode 100644 core/src/de/congrace/exp4j/NumberToken.java create mode 100644 core/src/de/congrace/exp4j/OperatorToken.java create mode 100644 core/src/de/congrace/exp4j/ParenthesisToken.java create mode 100644 core/src/de/congrace/exp4j/PostfixExpression.java create mode 100644 core/src/de/congrace/exp4j/Token.java create mode 100644 core/src/de/congrace/exp4j/Tokenizer.java create mode 100644 core/src/de/congrace/exp4j/UnknownFunctionException.java create mode 100644 core/src/de/congrace/exp4j/UnparsableExpressionException.java create mode 100644 core/src/de/congrace/exp4j/Variable.java create mode 100644 core/src/de/congrace/exp4j/VariableSet.java create mode 100644 core/src/de/congrace/exp4j/VariableToken.java diff --git a/android/.classpath b/android/.classpath index 4620a5b3..07677d89 100644 --- a/android/.classpath +++ b/android/.classpath @@ -7,6 +7,5 @@ - diff --git a/android/libs/exp4j-rdg.jar b/android/libs/exp4j-rdg.jar deleted file mode 100644 index 8a980164b31e6ac0d24c01d522f327daf0eba1f5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 56364 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 diff --git a/core/.classpath b/core/.classpath index fd1223fb..fb297f54 100644 --- a/core/.classpath +++ b/core/.classpath @@ -30,6 +30,5 @@ - diff --git a/core/build.xml b/core/build.xml index 11f2fc35..9b178def 100644 --- a/core/build.xml +++ b/core/build.xml @@ -112,7 +112,6 @@ - diff --git a/core/src/de/congrace/exp4j/AbstractExpression.java b/core/src/de/congrace/exp4j/AbstractExpression.java new file mode 100644 index 00000000..fc7f56b8 --- /dev/null +++ b/core/src/de/congrace/exp4j/AbstractExpression.java @@ -0,0 +1,77 @@ +/* + Copyright 2011 frank asseg + + 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 de.congrace.exp4j; + +import java.text.NumberFormat; +import java.util.ArrayList; +import java.util.List; + +/** + * Abstract base class for mathematical expressions + * + * @author fas@congrace.de + */ +abstract class AbstractExpression { + private final String expression; + private final Token[] tokens; + private final String[] variableStrings; + private final NumberFormat numberFormat = NumberFormat.getInstance(); + + /** + * Construct a new {@link AbstractExpression} + * + * @param expression + * the mathematical expression to be used + * @param tokens + * the {@link Token}s in the expression + * @param variableNames + * an array of variable names which are used in the expression + */ + AbstractExpression(String expression, Token[] tokens, String[] variableStrings) { + this.expression = expression; + this.tokens = tokens; + this.variableStrings = variableStrings; + } + + /** + * get the mathematical expression {@link String} + * + * @return the expression + */ + public String getExpression() { + return expression; + } + + /** + * get the used {@link NumberFormat} + * + * @return the used {@link NumberFormat} + */ + public NumberFormat getNumberFormat() { + return numberFormat; + } + + /** + * get the {@link Token}s + * + * @return the array of {@link Token}s + */ + Token[] getTokens() { + return tokens; + } + +} diff --git a/core/src/de/congrace/exp4j/Calculable.java b/core/src/de/congrace/exp4j/Calculable.java new file mode 100644 index 00000000..4009cb5d --- /dev/null +++ b/core/src/de/congrace/exp4j/Calculable.java @@ -0,0 +1,31 @@ +package de.congrace.exp4j; + +/** + * This is the basic result class of the exp4j {@link ExpressionBuilder} + * + * @author ruckus + * + */ +public interface Calculable { + /** + * calculate the result of the expression + * + * @return the result of the calculation + */ + public Variable calculate(); + + /** + * return the expression in reverse polish postfix notation + * + * @return the expression used to construct this {@link Calculable} + */ + public String getExpression(); + + /** + * set a variable value for the calculation + * + * @param value + * the value of the variable + */ + public void setVariable(Variable var); +} diff --git a/core/src/de/congrace/exp4j/CalculationToken.java b/core/src/de/congrace/exp4j/CalculationToken.java new file mode 100644 index 00000000..c40408aa --- /dev/null +++ b/core/src/de/congrace/exp4j/CalculationToken.java @@ -0,0 +1,76 @@ +/* + Copyright 2011 frank asseg + + 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 de.congrace.exp4j; + +import java.util.Arrays; +import java.util.Stack; + +abstract class CalculationToken extends Token { + + CalculationToken(String value) { + super(value); + } + + abstract void mutateStackForCalculation(Stack stack, VariableSet variables); + + /* + * Given an array of variables, check if any are arrays and if so expand any other of the given variables to arrays of the same length. + * Doubles are turned into arrays of all the same value as original. Arrays of other lengths are padded with zeros. + */ + public Variable[] expandVariables(Variable[] values){ + // Check if any variables have preferred representation as arrays + int maxLength = 0; + for (Variable v : values){ + if (v.getPrimary() == Variable.Primary.ARRAY && v.getArrayValue().length > maxLength){ + maxLength = v.getArrayValue().length; + } + } + + // if necessary, expand any non-array variables to maximum length + if (maxLength > 0) { + for (int n = 0; n + * java de.congrace.exp4j.CommandlineInterpreter "2 * log(2.2223) - ((2-3.221) * 14.232^2)" + * > 248.91042049521056 + * + * + * @author fas@congrace.de + * + */ +public class CommandlineInterpreter { + private static void calculateExpression(String string) { + try { + final PostfixExpression pe = PostfixExpression.fromInfix(string); + System.out.println(pe.calculate()); + } catch (UnparsableExpressionException e) { + e.printStackTrace(); + } catch (UnknownFunctionException e) { + e.printStackTrace(); + } + } + + public static void main(String[] args) { + if (args.length != 1) { + printUsage(); + } else { + calculateExpression(args[0]); + } + } + + private static void printUsage() { + final StringBuilder usage = new StringBuilder(); + usage.append("Commandline Expression Parser\n\n").append("Example: ").append("\n").append("java -jar exp4j.jar \"2.12 * log(23) * (12 - 4)\"\n\n") + .append("written by fas@congrace.de"); + System.err.println(usage.toString()); + } +} diff --git a/core/src/de/congrace/exp4j/CustomFunction.java b/core/src/de/congrace/exp4j/CustomFunction.java new file mode 100644 index 00000000..f49f9481 --- /dev/null +++ b/core/src/de/congrace/exp4j/CustomFunction.java @@ -0,0 +1,90 @@ +package de.congrace.exp4j; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Stack; + +import de.congrace.exp4j.FunctionToken.Function; + +/** + * this classed is used to create custom functions for exp4j
+ *
+ * Example
+ *
{@code 
+ * CustomFunction fooFunc = new CustomFunction("foo") {
+ * 		public double applyFunction(double value) {
+ * 			return value*Math.E;
+ * 		}
+ * };
+ * double varX=12d;
+ * Calculable calc = new ExpressionBuilder("foo(x)").withCustomFunction(fooFunc).withVariable("x",varX).build();
+ * assertTrue(calc.calculate() == Math.E * varX);
+ * }
+ * + * @author ruckus + * + */ +public abstract class CustomFunction extends CalculationToken { + private int argc=1; + + /** + * create a new single value input CustomFunction with a set name + * + * @param value + * the name of the function (e.g. foo) + */ + protected CustomFunction(String value) throws InvalidCustomFunctionException{ + super(value); + for (Function f:Function.values()) { + if (value.equalsIgnoreCase(f.toString())){ + throw new InvalidCustomFunctionException(value + " is already reserved as a function name"); + } + } + } + + /** + * create a new single value input CustomFunction with a set name + * + * @param value + * the name of the function (e.g. foo) + */ + protected CustomFunction(String value,int argumentCount) throws InvalidCustomFunctionException{ + super(value); + this.argc=argumentCount; + for (Function f:Function.values()) { + if (value.equalsIgnoreCase(f.toString())){ + throw new InvalidCustomFunctionException(value + " is already reserved as a function name"); + } + } + } + + /** + * apply the function to a value + * + * @param values + * the values to which the function should be applied. + * @return the function value + */ + public abstract Variable applyFunction(List vars); + + @Override + void mutateStackForCalculation(Stack stack, VariableSet variables) { + List args = new ArrayList(argc); + for (int i=0; i < argc; i++) { + args.add(i, stack.pop() ); + } + Collections.reverse(args); // Put elements in logical order + + stack.push(this.applyFunction(args)); + } + + @Override + void mutateStackForInfixTranslation(Stack operatorStack, StringBuilder output) { + operatorStack.push(this); + } + public int getArgumentCount() { + return argc; + } +} diff --git a/core/src/de/congrace/exp4j/Example.java b/core/src/de/congrace/exp4j/Example.java new file mode 100644 index 00000000..0d70b0b0 --- /dev/null +++ b/core/src/de/congrace/exp4j/Example.java @@ -0,0 +1,77 @@ +package de.congrace.exp4j; + +import java.util.List; + +public class Example { + + public static void main(String[] args) throws UnknownFunctionException, UnparsableExpressionException, InvalidCustomFunctionException { + + // Test 1 + // ====== + + Calculable calc1 = new ExpressionBuilder("x * y - 2").withVariableNames("x", "y").build(); + calc1.setVariable(new Variable("x", 1.2)); + calc1.setVariable(new Variable("y", 2.2)); + + System.out.println(calc1.calculate().toString()); + //double result = calc1.calculate().getDoubleValue(); + //System.out.println(result); + + // Test 2 + // ====== + + // A function which calculates the mean of an array and scales it + CustomFunction meanFn = new CustomFunction("mean",2) { + public Variable applyFunction(List vars) { + + double[] vals; + double scale; + + try{ + vals = vars.get(0).getArrayValue(); + scale = vars.get(1).getDoubleValue(); + } catch (Exception e) { + return new Variable("Invalid"); + } + + double subtotal = 0; + for (int i = 0; i < vals.length; i++ ){ + subtotal += vals[i]; + } + + subtotal = scale * subtotal / vals.length; + return new Variable("double MEAN result, ", subtotal); + + } + }; + + ExpressionBuilder b = new ExpressionBuilder("mean(x,y)"); + b.withCustomFunction(meanFn); + b.withVariable(new Variable("x", new double[] {1.1,2,10,3,2.4,10.2})); + b.withVariable(new Variable("y", 2)); + Calculable calc2 = b.build(); + + System.out.println( calc2.calculate().toString() ); + + // Test 3 + // ====== + + Calculable calc3 = new ExpressionBuilder("x * y - 2").withVariableNames("x", "y").build(); + calc3.setVariable(new Variable("x", new double[]{1.2, 10, 20, 15})); + calc3.setVariable(new Variable("y", new double[]{2.2, 5.2, 12, 9 })); + + //double result3 = calc3.calculate().getDoubleValue(); + System.out.println(calc3.calculate().toString()); + + + // Test 4 + // ====== + + Calculable calc4 = new ExpressionBuilder("log10(sqrt(x) * abs(y))").withVariableNames("x", "y").build(); + calc4.setVariable(new Variable("x", new double[]{1.2, 10, 10, 15})); + calc4.setVariable(new Variable("y", new double[]{2.2, -5.2, 5.2, 9 })); + + //double result3 = calc3.calculate().getDoubleValue(); + System.out.println(calc4.calculate().toString()); + } +} diff --git a/core/src/de/congrace/exp4j/ExpressionBuilder.java b/core/src/de/congrace/exp4j/ExpressionBuilder.java new file mode 100644 index 00000000..8ecf1b5c --- /dev/null +++ b/core/src/de/congrace/exp4j/ExpressionBuilder.java @@ -0,0 +1,135 @@ +package de.congrace.exp4j; + +import java.util.Collection; +import java.util.HashSet; +import java.util.Set; + +/** + * This is Builder implementation for the exp4j API used to create a Calculable + * instance for the user + * + * @author ruckus + * + */ +public class ExpressionBuilder { + private VariableSet variables = new VariableSet(); + private final Set customFunctions = new HashSet(); + + private String expression; + + /** + * Create a new ExpressionBuilder + * + * @param expression + * the expression to evaluate + */ + public ExpressionBuilder(String expression) { + this.expression = expression; + } + + /** + * build a new {@link Calculable} from the expression using the supplied + * variables + * + * @return the {@link Calculable} which can be used to evaluate the + * expression + * @throws UnknownFunctionException + * when an unrecognized function name is used in the expression + * @throws UnparsableExpressionException + * if the expression could not be parsed + */ + public Calculable build() throws UnknownFunctionException, UnparsableExpressionException { + if (expression.indexOf('=') == -1 && !variables.isEmpty()) { + + // User supplied an expression without leading "f(...)=" + // so we just append the user function to a proper "f()=" + // for PostfixExpression.fromInfix() + StringBuilder function = new StringBuilder("f("); + for (String name : variables.getVariableNames()) { + function.append(name).append(','); + } + expression = function.deleteCharAt(function.length() - 1).toString() + ")=" + expression; + } + // create the PostfixExpression and return it as a Calculable + PostfixExpression delegate = PostfixExpression.fromInfix(expression, customFunctions); + for (Variable var : variables ) { + delegate.setVariable(var); + for (CustomFunction fn:customFunctions){ + if (fn.getValue().equalsIgnoreCase(var.getName())){ + throw new UnparsableExpressionException("variable '" + var + "' cannot have the same name as a custom function " + fn.getValue()); + } + } + } + return delegate; + } + + /** + * add a custom function instance for the evaluator to recognize + * + * @param function + * the {@link CustomFunction} to add + * @return the {@link ExpressionBuilder} instance + */ + public ExpressionBuilder withCustomFunction(CustomFunction function) { + customFunctions.add(function); + return this; + } + + public ExpressionBuilder withCustomFunctions(Collection functions) { + customFunctions.addAll(functions); + return this; + } + + /** + * set the value for a variable + * + * @param variableName + * the variable name e.g. "x" + * @param value + * the value e.g. 2.32d + * @return the {@link ExpressionBuilder} instance + */ + public ExpressionBuilder withVariable(Variable value) { + variables.add(value); + return this; + } + + /* + * Provided for backwards compatibility + */ + @Deprecated + public ExpressionBuilder withVariable(String variableName, double value) { + variables.add(new Variable(variableName, value)); + return this; + } + + /** + * set the variables names used in the expression without setting their + * values. Usefull for building an expression before you know the variable values. + * + * @param variableNames + * vararg {@link String} of the variable names used in the + * expression + * @return the ExpressionBuilder instance + */ + + public ExpressionBuilder withVariableNames(String... variableNames) { + for (String name : variableNames) { + variables.add( new Variable(name, Double.NaN) ); + } + return this; + } + + + /** + * set the values for variables + * + * @param variableMap + * a map of variable names to variable values + * @return the {@link ExpressionBuilder} instance + */ + public ExpressionBuilder withVariables(VariableSet variables) { + this.variables = variables; + return this; + } +} diff --git a/core/src/de/congrace/exp4j/FunctionSeparatorToken.java b/core/src/de/congrace/exp4j/FunctionSeparatorToken.java new file mode 100644 index 00000000..24b1109d --- /dev/null +++ b/core/src/de/congrace/exp4j/FunctionSeparatorToken.java @@ -0,0 +1,17 @@ +package de.congrace.exp4j; + +import java.util.Stack; + +public class FunctionSeparatorToken extends Token{ + public FunctionSeparatorToken() { + super(","); + } + @Override + void mutateStackForInfixTranslation(Stack operatorStack, StringBuilder output) { + Token token; + while (!((token=operatorStack.peek()) instanceof ParenthesisToken) && !token.getValue().equals("(")){ + output.append(operatorStack.pop().getValue()).append(" "); + } + } + +} diff --git a/core/src/de/congrace/exp4j/FunctionToken.java b/core/src/de/congrace/exp4j/FunctionToken.java new file mode 100644 index 00000000..6bc620ce --- /dev/null +++ b/core/src/de/congrace/exp4j/FunctionToken.java @@ -0,0 +1,161 @@ +/* +Copyright 2011 frank asseg + +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 de.congrace.exp4j; + +import java.util.Stack; + +/** + * A {@link Token} for functions + * + * @author fas@congrace.de + * + */ +class FunctionToken extends CalculationToken { + /** + * the functionNames that can be used in an expression + * + * @author ruckus + * + */ + enum Function { + ABS, ACOS, ASIN, ATAN, CBRT, CEIL, COS, COSH, EXP, EXPM1, FLOOR, ROUND, RANDOM, LOG, SIN, SINH, SQRT, TAN, TANH, LOG10 + } + + private Function function; + + /** + * construct a new {@link FunctionToken} + * + * @param value + * the name of the function + * @throws UnknownFunctionException + * if an unknown function name is encountered + */ + FunctionToken(String value) throws UnknownFunctionException { + super(value); + try { + function = Function.valueOf(value.toUpperCase()); + } catch (IllegalArgumentException e) { + throw new UnknownFunctionException(value); + } + if (function == null) { + throw new UnknownFunctionException(value); + } + } + + /** + * apply a function to a variable + * + * @param x + * the value the function should be applied to + * @return the result of the function + */ + public Variable applyFunction(Variable var) { + + // The names here are strictly unused, but are useful for debugging + String name = function.name() + " result (#"+var.hashCode()+"), "; + + switch (var.getPrimary()) { + case DOUBLE: + name = "double "+name; + double x = var.getDoubleValue(); + return new Variable(name, applyFunction(x) ); + + case ARRAY: + name = "array "+name; + double[] input = var.getArrayValue(); + double[] result = new double[input.length]; + for (int i = 0; i < input.length; i++){ + result[i] = applyFunction(input[i]); + } + return new Variable(name, result); + + default: + return new Variable("Invalid"); + } + } + + /* + * The actual function application on a double + */ + private double applyFunction(double x){ + switch (function) { + case ABS: + return Math.abs(x); + case ACOS: + return Math.acos(x); + case ASIN: + return Math.asin(x); + case ATAN: + return Math.atan(x); + case CBRT: + return Math.cbrt(x); + case CEIL: + return Math.ceil(x); + case COS: + return Math.cos(x); + case COSH: + return Math.cosh(x); + case EXP: + return Math.exp(x); + case EXPM1: + return Math.expm1(x); + case FLOOR: + return Math.floor(x); + case ROUND: + return Math.round(x); + case RANDOM: + return Math.random()*x; + case LOG: + return Math.log(x); + case LOG10: + return Math.log10(x); + case SIN: + return Math.sin(x); + case SINH: + return Math.sinh(x); + case SQRT: + return Math.sqrt(x); + case TAN: + return Math.tan(x); + case TANH: + return Math.tanh(x); + default: + return Double.NaN; // should not happen ;) + } + } + + /** + * + * get the {@link Function} + * + * @return the correspoding {@link Function} + */ + Function getFunction() { + return function; + } + + @Override + void mutateStackForCalculation(Stack stack, VariableSet variableValues) { + stack.push(this.applyFunction(stack.pop())); + } + + @Override + void mutateStackForInfixTranslation(Stack operatorStack, StringBuilder output) { + operatorStack.push(this); + } +} diff --git a/core/src/de/congrace/exp4j/InfixTranslator.java b/core/src/de/congrace/exp4j/InfixTranslator.java new file mode 100644 index 00000000..9712bce3 --- /dev/null +++ b/core/src/de/congrace/exp4j/InfixTranslator.java @@ -0,0 +1,109 @@ +/* + Copyright 2011 frank asseg + + 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 de.congrace.exp4j; + +import java.util.Set; +import java.util.Stack; + +/** + * Translate a mathematical expression in human readable infix notation to a + * Reverse Polish Notation (postfix) expression for easier parsing. by + * implementing the shunting yard algorithm by dijkstra + * + * @author fas@congrace.de + */ +class InfixTranslator { + + private static String substituteUnaryOperators(String expr) { + final StringBuilder exprBuilder = new StringBuilder(expr.length()); + final char[] data = expr.toCharArray(); + char lastChar = ' '; + for (int i = 0; i < expr.length(); i++) { + if (exprBuilder.length() > 0) { + lastChar = exprBuilder.charAt(exprBuilder.length() - 1); + } + final char c = data[i]; + switch (c) { + case '+': + if (i > 0 && lastChar != '(' && !(OperatorToken.isOperator(lastChar))) { + exprBuilder.append(c); + } + break; + case '-': + if (i > 0 && lastChar != '(' && !(OperatorToken.isOperator(lastChar))) { + exprBuilder.append(c); + } else { + exprBuilder.append('#'); + } + break; + default: + if (!Character.isWhitespace(c)) { + exprBuilder.append(c); + } + } + } + return exprBuilder.toString(); + } + + /** + * Delegation method for simple expression without variables or custom + * functions + * + * @param infixExpression + * the infix expression to be translated + * @return translated RNP postfix expression + * @throws UnparsableExpressionException + * when the expression is invalid + * @throws UnknownFunctionException + * when an unknown function has been used in the input. + */ + static String toPostfixExpression(String infixExpression) throws UnparsableExpressionException, UnknownFunctionException { + return toPostfixExpression(infixExpression, null, null); + } + + /** + * implement the shunting yard algorithm + * + * @param infixExpression + * the human readable expression which should be translated to + * RPN + * @param variableNames + * the variable names used in the expression + * @param customFunctions + * the CustomFunction implementations used + * @return the expression in postfix format + * @throws UnparsableExpressionException + * if the expression could not be translated to RPN + * @throws UnknownFunctionException + * if an unknown function was encountered + */ + static String toPostfixExpression(String infixExpression, String[] variableStrings, Set customFunctions) + throws UnparsableExpressionException, UnknownFunctionException { + infixExpression = substituteUnaryOperators(infixExpression); + final Token[] tokens = new Tokenizer(variableStrings, customFunctions).tokenize(infixExpression); + final StringBuilder output = new StringBuilder(tokens.length); + final Stack operatorStack = new Stack(); + for (final Token token : tokens) { + token.mutateStackForInfixTranslation(operatorStack, output); + } + // all tokens read, put the rest of the operations on the output; + while (operatorStack.size() > 0) { + output.append(operatorStack.pop().getValue()).append(" "); + } + return output.toString().trim(); + } +} diff --git a/core/src/de/congrace/exp4j/InvalidCustomFunctionException.java b/core/src/de/congrace/exp4j/InvalidCustomFunctionException.java new file mode 100644 index 00000000..e3810db0 --- /dev/null +++ b/core/src/de/congrace/exp4j/InvalidCustomFunctionException.java @@ -0,0 +1,9 @@ +package de.congrace.exp4j; + +public class InvalidCustomFunctionException extends Exception{ + private static final long serialVersionUID = 1L; + + public InvalidCustomFunctionException(String message) { + super(message); + } +} diff --git a/core/src/de/congrace/exp4j/NumberToken.java b/core/src/de/congrace/exp4j/NumberToken.java new file mode 100644 index 00000000..b765d451 --- /dev/null +++ b/core/src/de/congrace/exp4j/NumberToken.java @@ -0,0 +1,66 @@ +/* + Copyright 2011 frank asseg + + 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 de.congrace.exp4j; + +import java.util.Map; +import java.util.Stack; + +/** + * A {@link Token} for Numbers + * + * @author fas@congrace.de + * + */ +class NumberToken extends CalculationToken { + + private final double doubleValue; + + /** + * construct a new {@link NumberToken} + * + * @param value + * the value of the number as a {@link String} + */ + NumberToken(String value) { + super(value); + this.doubleValue = Double.parseDouble(value); + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof NumberToken) { + final NumberToken t = (NumberToken) obj; + return t.getValue().equals(this.getValue()); + } + return false; + } + + @Override + public int hashCode() { + return getValue().hashCode(); + } + + @Override + void mutateStackForCalculation(Stack stack, VariableSet variables) { + stack.push(new Variable("From number "+getValue()+" : "+hashCode(), this.doubleValue)); + } + + @Override + void mutateStackForInfixTranslation(Stack operatorStack, StringBuilder output) { + output.append(this.getValue()).append(' '); + } +} diff --git a/core/src/de/congrace/exp4j/OperatorToken.java b/core/src/de/congrace/exp4j/OperatorToken.java new file mode 100644 index 00000000..765f4508 --- /dev/null +++ b/core/src/de/congrace/exp4j/OperatorToken.java @@ -0,0 +1,243 @@ +/* + Copyright 2011 frank asseg + + 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 de.congrace.exp4j; + +import java.util.Stack; + +/** + * {@link Token} for Operations like +,-,*,/,% and ^ + * + * @author fas@congrace.de + */ +class OperatorToken extends CalculationToken { + + /** + * the valid {@link Operation}s for the {@link OperatorToken} + * + * @author fas@congrace.de + */ + enum Operation { + ADDITION(1, true), SUBTRACTION(1, true), MULTIPLICATION(2, true), DIVISION(2, true), MODULO(2, true), EXPONENTIATION(3, false), UNARY_MINUS(4, false), UNARY_PLUS( + 4, false); + private final int precedence; + private final boolean leftAssociative; + + private Operation(int precedence, boolean leftAssociative) { + this.precedence = precedence; + this.leftAssociative = leftAssociative; + } + } + + /** + * return a corresponding {@link Operation} for a symbol + * + * @param c + * the symbol of the operation + * @return the corresponding {@link Operation} + */ + static Operation getOperation(char c) { + switch (c) { + case '+': + return Operation.ADDITION; + case '-': + return Operation.SUBTRACTION; + case '*': + return Operation.MULTIPLICATION; + case '/': + return Operation.DIVISION; + case '^': + return Operation.EXPONENTIATION; + case '#': + return Operation.UNARY_MINUS; + case '%': + return Operation.MODULO; + default: + return null; + } + } + + static boolean isOperator(char c) { + return getOperation(c) != null; + } + + private final Operation operation; + + /** + * construct a new {@link OperatorToken} + * + * @param value + * the symbol (e.g.: '+') + * @param operation + * the {@link Operation} of this {@link Token} + */ + OperatorToken(String value, Operation operation) { + super(value); + this.operation = operation; + } + + /** + * apply the {@link Operation} + * + * @param values + * the doubles to operate on + * @return the result of the {@link Operation} + * @throws UnparsableExpressionException + */ + public Variable applyOperation(Variable... values) { + + values = expandVariables(values); + + double[] inputs = new double[values.length]; + switch (values[0].getPrimary()){ + + case DOUBLE: + for (int i = 0; i stack, VariableSet variables) { + if (this.getOperandCount() == 2) { + final Variable n2 = stack.pop(); + final Variable n1 = stack.pop(); + stack.push(this.applyOperation(n1, n2)); + } else if (this.getOperandCount() == 1) { + final Variable n1 = stack.pop(); + stack.push(this.applyOperation(n1)); + } + } + + @Override + void mutateStackForInfixTranslation(Stack operatorStack, StringBuilder output) { + Token before; + while (!operatorStack.isEmpty() && (before = operatorStack.peek()) != null && (before instanceof OperatorToken || before instanceof FunctionToken)) { + if (before instanceof FunctionToken) { + operatorStack.pop(); + output.append(before.getValue()).append(" "); + } else { + final OperatorToken stackOperator = (OperatorToken) before; + if (this.isLeftAssociative() && this.getPrecedence() <= stackOperator.getPrecedence()) { + output.append(operatorStack.pop().getValue()).append(" "); + } else if (!this.isLeftAssociative() && this.getPrecedence() < stackOperator.getPrecedence()) { + output.append(operatorStack.pop().getValue()).append(" "); + } else { + break; + } + } + } + operatorStack.push(this); + } +} diff --git a/core/src/de/congrace/exp4j/ParenthesisToken.java b/core/src/de/congrace/exp4j/ParenthesisToken.java new file mode 100644 index 00000000..ad8ab0f6 --- /dev/null +++ b/core/src/de/congrace/exp4j/ParenthesisToken.java @@ -0,0 +1,69 @@ +/* + Copyright 2011 frank asseg + + 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 de.congrace.exp4j; + +import java.util.Stack; + +/** + * Token for parenthesis + * + * @author fas@congrace.de + */ +class ParenthesisToken extends Token { + + ParenthesisToken(String value) { + super(value); + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof ParenthesisToken) { + final ParenthesisToken t = (ParenthesisToken) obj; + return t.getValue().equals(this.getValue()); + } + return false; + } + + @Override + public int hashCode() { + return getValue().hashCode(); + } + + /** + * check the direction of the parenthesis + * + * @return true if it's a left parenthesis (open) false if it is a right + * parenthesis (closed) + */ + boolean isOpen() { + return getValue().equals("(") || getValue().equals("[") || getValue().equals("{"); + } + + @Override + void mutateStackForInfixTranslation(Stack operatorStack, StringBuilder output) { + if (this.isOpen()) { + operatorStack.push(this); + } else { + Token next; + while ((next = operatorStack.peek()) instanceof OperatorToken || next instanceof FunctionToken || next instanceof CustomFunction + || (next instanceof ParenthesisToken && !((ParenthesisToken) next).isOpen())) { + output.append(operatorStack.pop().getValue()).append(" "); + } + operatorStack.pop(); + } + } +} diff --git a/core/src/de/congrace/exp4j/PostfixExpression.java b/core/src/de/congrace/exp4j/PostfixExpression.java new file mode 100644 index 00000000..0bbcc142 --- /dev/null +++ b/core/src/de/congrace/exp4j/PostfixExpression.java @@ -0,0 +1,116 @@ +/* + Copyright 2011 frank asseg + + 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 de.congrace.exp4j; + +import java.util.Set; +import java.util.Stack; + +/** + * Class for calculating values from a RPN postfix expression.
+ * The default way to create a new instance of {@link PostfixExpression} is by + * using the static factory method fromInfix() + * + * @author fas@congrace.de + */ +public final class PostfixExpression extends AbstractExpression implements Calculable { + + private VariableSet variables = new VariableSet(); + + /** + * Factory method for creating {@link PostfixExpression}s from human + * readable infix expressions + * + * @param expression + * the infix expression to be used + * @return an equivalent {@link PostfixExpression} + * @throws UnparsableExpressionException + * if the expression was invalid + * @throws UnknownFunctionException + * if an unknown function has been used + * @deprecated please use {@link ExpressionBuilder} API + */ + @Deprecated + public static PostfixExpression fromInfix(String expression) throws UnparsableExpressionException, UnknownFunctionException { + return fromInfix(expression, null); + } + + /** + * Factory method for creating {@link PostfixExpression}s from human + * readable infix expressions + * + * @param expression + * the infix expression to be used + * @param customFunctions + * the CustomFunction implementations used + * @return an equivalent {@link PostfixExpression} + * @throws UnparsableExpressionException + * if the expression was invalid + * @throws UnknownFunctionException + * if an unknown function has been used + * @deprecated please use {@link ExpressionBuilder} + */ + @Deprecated + public static PostfixExpression fromInfix(String expression, Set customFunctions) throws UnparsableExpressionException, + UnknownFunctionException { + String[] variableStrings = null; + int posStart, posEnd; + if ((posStart = expression.indexOf('=')) > 0) { + String functionDef = expression.substring(0, posStart); + expression = expression.substring(posStart + 1); + if ((posStart = functionDef.indexOf('(')) > 0 && (posEnd = functionDef.indexOf(')')) > 0) { + variableStrings = functionDef.substring(posStart + 1, posEnd).split(","); + } + } + return new PostfixExpression(InfixTranslator.toPostfixExpression(expression, variableStrings, customFunctions), variableStrings, customFunctions); + } + + /** + * Construct a new simple {@link PostfixExpression} + * + * @param expression + * the postfix expression to be calculated + * @param variableNames + * the variable names in the expression + * @param customFunctions + * the CustomFunction implementations used + * @throws UnparsableExpressionException + * when expression is invalid + * @throws UnknownFunctionException + * when an unknown function has been used + */ + private PostfixExpression(String expression, String[] variableStrings, Set customFunctions) throws UnparsableExpressionException, + UnknownFunctionException { + super(expression, new Tokenizer(variableStrings, customFunctions).tokenize(expression), variableStrings); + } + + /** + * delegate the calculation of a simple expression + */ + public Variable calculate() throws IllegalArgumentException { + + final Stack stack = new Stack(); + for (final Token t : getTokens()) { + ((CalculationToken) t).mutateStackForCalculation(stack, variables); + } + return stack.pop(); + + } + + public void setVariable(Variable value) { + variables.add(value); + } +} diff --git a/core/src/de/congrace/exp4j/Token.java b/core/src/de/congrace/exp4j/Token.java new file mode 100644 index 00000000..2ce4030a --- /dev/null +++ b/core/src/de/congrace/exp4j/Token.java @@ -0,0 +1,50 @@ +/* + Copyright 2011 frank asseg + + 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 de.congrace.exp4j; + +import java.util.Stack; + +/** + * Superclass for tokenized Strings + * + * @author fas@congrace.de + */ +abstract class Token { + private final String value; + + /** + * construct a new {@link Token} + * + * @param value + * the value of the {@link Token} + */ + Token(String value) { + super(); + this.value = value; + } + + /** + * get the value (String representation) of the token + * + * @return the value + */ + String getValue() { + return value; + } + + abstract void mutateStackForInfixTranslation(Stack operatorStack, StringBuilder output); +} diff --git a/core/src/de/congrace/exp4j/Tokenizer.java b/core/src/de/congrace/exp4j/Tokenizer.java new file mode 100644 index 00000000..1591995e --- /dev/null +++ b/core/src/de/congrace/exp4j/Tokenizer.java @@ -0,0 +1,222 @@ +/* + Copyright 2011 frank asseg + + 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 de.congrace.exp4j; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import de.congrace.exp4j.FunctionToken.Function; + +/** + * Class for tokenizing mathematical expressions by breaking an expression up + * into multiple different {@link Token}s + * + * @author fas@congrace.de + */ +class Tokenizer { + private String[] variableNames; + private final Set functionNames = new HashSet(); + private final Set customFunctions; + + { + functionNames.add("abs"); + functionNames.add("acos"); + functionNames.add("asin"); + functionNames.add("atan"); + functionNames.add("cbrt"); + functionNames.add("ceil"); + functionNames.add("cos"); + functionNames.add("cosh"); + functionNames.add("exp"); + functionNames.add("expm1"); + functionNames.add("floor"); + functionNames.add("log"); + functionNames.add("sin"); + functionNames.add("sinh"); + functionNames.add("sqrt"); + functionNames.add("tan"); + functionNames.add("tanh"); + } + + Tokenizer() { + super(); + customFunctions = null; + } + + /** + * construct a new Tokenizer that recognizes variable names + * + * @param variableNames + * the variable names in the expression + * @throws IllegalArgumentException + * if a variable has the name as a function + * @param customFunctions + * the CustomFunction implementations used if the variableNames + * are not valid + */ + Tokenizer(String[] variableNames, Set customFunctions) throws IllegalArgumentException { + super(); + this.variableNames = variableNames; + + if (variableNames != null) { + for (String varName : variableNames) { + if (functionNames.contains(varName.toLowerCase())) { + throw new IllegalArgumentException("Variable '" + varName + "' can not have the same name as a function"); + } + } + } + this.customFunctions = customFunctions; + } + + private Token getCustomFunctionToken(String name) throws UnknownFunctionException { + for (CustomFunction func : customFunctions) { + if (func.getValue().equals(name)) { + return func; + } + } + throw new UnknownFunctionException(name); + } + + private boolean isCustomFunction(String name) { + if (customFunctions == null) { + return false; + } + for (CustomFunction func : customFunctions) { + if (func.getValue().equals(name)) { + return true; + } + } + return false; + } + + /** + * check if a char is part of a number + * + * @param c + * the char to be checked + * @return true if the char is part of a number + */ + private boolean isDigit(char c) { + return Character.isDigit(c) || c == '.'; + } + + private boolean isFunction(String name) { + for (Function fn : Function.values()) { + if (fn.name().equals(name.toUpperCase())) { + return true; + } + } + return false; + } + + /** + * check if a String is a variable name + * + * @param name + * the variable name which is checked to be valid the char to be + * checked + * @return true if the char is a variable name (e.g. x) + */ + private boolean isVariable(String name) { + //String[] variableNames = variables.getVariableNames(); + + if (variableNames != null) { + for (String var : variableNames) { + if (name.equals(var)) { + return true; + } + } + } + return false; + } + + /** + * tokenize an infix expression by breaking it up into different + * {@link Token} that can represent operations,functions,numbers, + * parenthesis or variables + * + * @param infix + * the infix expression to be tokenized + * @return the {@link Token}s representing the expression + * @throws UnparsableExpressionException + * when the expression is invalid + * @throws UnknownFunctionException + * when an unknown function name has been used. + */ + Token[] tokenize(String infix) throws UnparsableExpressionException, UnknownFunctionException { + final List tokens = new ArrayList(); + final char[] chars = infix.toCharArray(); + // iterate over the chars and fork on different types of input + Token lastToken; + for (int i = 0; i < chars.length; i++) { + char c = chars[i]; + if (c == ' ') + continue; + if (isDigit(c)) { + final StringBuilder valueBuilder = new StringBuilder(1); + // handle the numbers of the expression + valueBuilder.append(c); + int numberLen = 1; + while (chars.length > i + numberLen && isDigit(chars[i + numberLen])) { + valueBuilder.append(chars[i + numberLen]); + numberLen++; + } + i += numberLen - 1; + lastToken = new NumberToken(valueBuilder.toString()); + } else if (Character.isLetter(c) || c == '_' || c == '#') { + // can be a variable or function + final StringBuilder nameBuilder = new StringBuilder(); + nameBuilder.append(c); + int offset = 1; + while (chars.length > i + offset && (Character.isLetter(chars[i + offset]) || Character.isDigit(chars[i + offset]) || chars[i + offset] == '_' || chars[i + offset] == '#')) { + nameBuilder.append(chars[i + offset++]); + } + String name = nameBuilder.toString(); + if (this.isVariable(name)) { + // a variable + i += offset - 1; + lastToken = new VariableToken(name); + } else if (this.isFunction(name)) { + // might be a function + i += offset - 1; + lastToken = new FunctionToken(name); + } else if (this.isCustomFunction(name)) { + // a custom function + i += offset - 1; + lastToken = getCustomFunctionToken(name); + } else { + // an unknown symbol was encountered + throw new UnparsableExpressionException(c, i); + } + }else if (c == ',') { + // a function separator, hopefully + lastToken=new FunctionSeparatorToken(); + } else if (OperatorToken.isOperator(c)) { + lastToken = new OperatorToken(String.valueOf(c), OperatorToken.getOperation(c)); + } else if (c == '(' || c == ')' || c == '[' || c == ']' || c == '{' || c == '}') { + lastToken = new ParenthesisToken(String.valueOf(c)); + } else { + // an unknown symbol was encountered + throw new UnparsableExpressionException(c, i); + } + tokens.add(lastToken); + } + return tokens.toArray(new Token[tokens.size()]); + } +} diff --git a/core/src/de/congrace/exp4j/UnknownFunctionException.java b/core/src/de/congrace/exp4j/UnknownFunctionException.java new file mode 100644 index 00000000..ae350932 --- /dev/null +++ b/core/src/de/congrace/exp4j/UnknownFunctionException.java @@ -0,0 +1,37 @@ +/* + Copyright 2011 frank asseg + + 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 de.congrace.exp4j; + +/** + * Exception for handling unknown Functions. + * + * @see FunctionToken + * @author fas@congrace.de + */ +public class UnknownFunctionException extends Exception { + private static final long serialVersionUID = 1L; + + /** + * construct a new {@link UnknownFunctionException} + * + * @param functionName + * the function name which could not be found + */ + public UnknownFunctionException(String functionName) { + super("Unknown function: " + functionName); + } +} diff --git a/core/src/de/congrace/exp4j/UnparsableExpressionException.java b/core/src/de/congrace/exp4j/UnparsableExpressionException.java new file mode 100644 index 00000000..fcbf7da2 --- /dev/null +++ b/core/src/de/congrace/exp4j/UnparsableExpressionException.java @@ -0,0 +1,47 @@ +/* + Copyright 2011 frank asseg + + 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 de.congrace.exp4j; + +/** + * Exception for invalid expressions + * + * @author fas@congrace.de + */ +public class UnparsableExpressionException extends Exception { + private static final long serialVersionUID = 1L; + + /** + * construct a new {@link UnparsableExpressionException} + * + * @param c + * the character which could not be parsed + * @param pos + * the position of the character in the expression + */ + public UnparsableExpressionException(char c, int pos) { + super("Unable to parse character at position " + pos + ": '" + String.valueOf(c) + "'"); + } + /** + * construct a new {@link UnparsableExpressionException} + * + * @param msg + * the error message + */ + public UnparsableExpressionException(String msg) { + super(msg); + } +} diff --git a/core/src/de/congrace/exp4j/Variable.java b/core/src/de/congrace/exp4j/Variable.java new file mode 100644 index 00000000..0030e70b --- /dev/null +++ b/core/src/de/congrace/exp4j/Variable.java @@ -0,0 +1,107 @@ +package de.congrace.exp4j; + +/* + * Represents a generic variable which can have double or array values. + * Optionally the start and step values corresponding to each array index can be specified for array values + * Tries to do something sensible if you try and apply a regular function / operator to an array + * and vice-versa. + */ +public class Variable { + + // The primary or preferred representation + public enum Primary {DOUBLE, ARRAY, PLACEHOLDER}; + private final Primary primary; + + private final String name; + + private final double doubleValue; + private final double[] arrayValue; + + private final double start, step; + + /* + * Initialize a new variable with a name only. This can be used as a place holder + */ + public Variable(String name){ + this.name = name; + this.primary = Primary.PLACEHOLDER; + this.doubleValue = Double.NaN; + this.arrayValue = new double[] {Double.NaN}; + this.start = Double.NaN; + this.step = Double.NaN; + } + + /* + * Initialize a new double variable + */ + public Variable(String name, double d){ + this.doubleValue = d; + this.arrayValue = new double[] {d}; + this.name = name; + this.primary = Primary.DOUBLE; + this.start = Double.NaN; + this.step = Double.NaN; + } + + /* + * Initialize a new array variable + */ + public Variable(String name, double[] d){ + this.arrayValue = d; + this.doubleValue = d[0]; + this.name = name; + this.primary = Primary.ARRAY; + this.start = Double.NaN; + this.step = Double.NaN; + } + + /* + * Initialize a new array variable, specifying the start and step values + */ + public Variable(String name, double[] d, double start, double step){ + this.arrayValue = d; + this.doubleValue = d[0]; + this.name = name; + this.primary = Primary.ARRAY; + this.start = start; + this.step = step; + } + + public String getName(){ + return name; + } + + public Primary getPrimary(){ + return this.primary; + } + + public double getDoubleValue(){ + return doubleValue; + } + + public double[] getArrayValue(){ + return arrayValue; + } + + public double getStep(){ + return step; + } + + public double getStart(){ + return start; + } + + public String toString(){ + if ( arrayValue.length > 1 ){ + String out = name + " is Array (length " + new Integer(arrayValue.length).toString() + ") : {"; + for (double x : arrayValue){ + out = out + x + ","; + } + out = out.substring(0, out.length()-1); + return out + "}"; + } + else{ + return name + " is double : {" + new Double(doubleValue).toString() + "}"; + } + } +} \ No newline at end of file diff --git a/core/src/de/congrace/exp4j/VariableSet.java b/core/src/de/congrace/exp4j/VariableSet.java new file mode 100644 index 00000000..f567006f --- /dev/null +++ b/core/src/de/congrace/exp4j/VariableSet.java @@ -0,0 +1,42 @@ +package de.congrace.exp4j; + +import java.util.HashSet; + +public class VariableSet extends HashSet { + + /** + * + */ + private static final long serialVersionUID = -4212803364398351279L; + + public boolean add(Variable v){ + Variable previous = getVariableNamed(v.getName()); + if ( previous != null ){ + this.remove( previous ); + } + + return super.add(v); + } + + public Variable getVariableNamed(String name){ + for (Variable var : this){ + if (var.getName().equals(name) ){ + return var; + } + } + return null; + } + + public String[] getVariableNames(){ + if (this.size() == 0){ + return null; + } + String names[] = new String[this.size()]; + int i = 0; + for (Variable var : this){ + names[i] = var.getName(); + i++; + } + return names; + } +} diff --git a/core/src/de/congrace/exp4j/VariableToken.java b/core/src/de/congrace/exp4j/VariableToken.java new file mode 100644 index 00000000..716ef2ee --- /dev/null +++ b/core/src/de/congrace/exp4j/VariableToken.java @@ -0,0 +1,47 @@ +/* + Copyright 2011 frank asseg + + 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 de.congrace.exp4j; + +import java.util.Stack; + +/** + * A {@link Token} for representing variables + * + * @author fas + */ +class VariableToken extends CalculationToken { + /** + * construct a new {@link VariableToken} + * + * @param value + * the value of the token + */ + VariableToken(String value) { + super(value); + } + + @Override + void mutateStackForCalculation(Stack stack, VariableSet variableValues) { + Variable value = variableValues.getVariableNamed(this.getValue()); + stack.push(value); + } + + @Override + void mutateStackForInfixTranslation(Stack operatorStack, StringBuilder output) { + output.append(this.getValue()).append(" "); + } +} -- 2.30.2