remove compile_fsio_file fix #3866

This commit is contained in:
rusefillc 2022-02-01 11:07:05 -05:00
parent 000fb3372f
commit 87930f540f
17 changed files with 1 additions and 2147 deletions

View File

@ -1,486 +0,0 @@
package com.fathzer.soft.javaluator;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;
/** An abstract evaluator, able to evaluate infix expressions.
* <br>Some standard evaluators are included in the library, you can define your own by subclassing this class.
* <br>This class is thread safe.
* @param <T> The type of values handled by the evaluator
* @author Jean-Marc Astesana
* @see <a href="../../../license.html">License information</a>
*/
public abstract class AbstractEvaluator<T> {
private static final boolean DETAILED_LOGGER = false;
private final Tokenizer tokenizer;
private final Map<String, Function> functions;
private final Map<String, List<Operator>> operators;
private final Map<String, Constant> constants;
private final String functionArgumentSeparator;
private final Map<String, BracketPair> functionBrackets;
private final Map<String, BracketPair> expressionBrackets;
public final Stack<String> stackRPN = new Stack<String>() {
@Override
public String push(String t) {
return super.push(t);
}
};
/** Constructor.
* @param parameters The evaluator parameters.
* <br>Please note that there's no side effect between the evaluator and the parameters.
* So, changes made to the parameters after the call to this constructor are ignored by the instance.
*/
protected AbstractEvaluator(Parameters parameters) {
//TODO if constants, operators, functions are duplicated => error
final ArrayList<String> tokenDelimitersBuilder = new ArrayList<String>();
this.functions = new HashMap<String, Function>();
this.operators = new HashMap<String, List<Operator>>();
this.constants = new HashMap<String, Constant>();
this.functionBrackets = new HashMap<String, BracketPair>();
for (final BracketPair pair : parameters.getFunctionBrackets()) {
functionBrackets.put(pair.getOpen(), pair);
functionBrackets.put(pair.getClose(), pair);
tokenDelimitersBuilder.add(pair.getOpen());
tokenDelimitersBuilder.add(pair.getClose());
}
this.expressionBrackets = new HashMap<String, BracketPair>();
for (final BracketPair pair : parameters.getExpressionBrackets()) {
expressionBrackets.put(pair.getOpen(), pair);
expressionBrackets.put(pair.getClose(), pair);
tokenDelimitersBuilder.add(pair.getOpen());
tokenDelimitersBuilder.add(pair.getClose());
}
if (operators!=null) {
for (Operator ope : parameters.getOperators()) {
tokenDelimitersBuilder.add(ope.getSymbol());
List<Operator> known = this.operators.get(ope.getSymbol());
if (known==null) {
known = new ArrayList<Operator>();
this.operators.put(ope.getSymbol(), known);
}
known.add(ope);
if (known.size()>1) {
validateHomonyms(known);
}
}
}
boolean needFunctionSeparator = false;
if (parameters.getFunctions()!=null) {
for (Function function : parameters.getFunctions()) {
this.functions.put(parameters.getTranslation(function.getName()), function);
if (function.getMaximumArgumentCount()>1) {
needFunctionSeparator = true;
}
}
}
if (parameters.getConstants()!=null) {
for (Constant constant : parameters.getConstants()) {
this.constants.put(parameters.getTranslation(constant.getName()), constant);
}
}
functionArgumentSeparator = parameters.getFunctionArgumentSeparator();
if (needFunctionSeparator) {
tokenDelimitersBuilder.add(functionArgumentSeparator);
}
tokenizer = new Tokenizer(tokenDelimitersBuilder);
}
/** Validates that homonym operators are valid.
* <br>Homonym operators are operators with the same name (like the unary - and the binary - operators)
* <br>This method is called when homonyms are passed to the constructor.
* <br>This default implementation only allows the case where there's two operators, one binary and one unary.
* Subclasses can override this method in order to accept others configurations.
* @param operators The operators to validate.
* @throws IllegalArgumentException if the homonyms are not compatibles.
* @see #guessOperator(Token, List)
*/
protected void validateHomonyms(List<Operator> operators) {
if (operators.size()>2) {
throw new IllegalArgumentException();
}
}
/** When a token can be more than one operator (homonym operators), this method guesses the right operator.
* <br>A very common case is the - sign in arithmetic computation which can be an unary or a binary operator, depending
* on what was the previous token.
* <br><b>Warning:</b> maybe the arguments of this function are not enough to deal with all the cases.
* So, this part of the evaluation is in alpha state (method may change in the future).
* @param previous The last parsed tokens (the previous token in the infix expression we are evaluating).
* @param candidates The candidate tokens.
* @return A token
* @see #validateHomonyms(List)
*/
protected Operator guessOperator(Token previous, List<Operator> candidates) {
final int argCount = ((previous!=null) && (previous.isCloseBracket() || previous.isLiteral())) ? 2 : 1;
for (Operator operator : candidates) {
if (operator.getOperandCount()==argCount) {
return operator;
}
}
return null;
}
@SuppressWarnings("unchecked")
private void output(Deque<T> values, Token token, Object evaluationContext) {
if (token.isLiteral()) { // If the token is a literal, a constant, or a variable name
String literal = token.getLiteral();
Constant ct = this.constants.get(literal);
T value = ct==null?null:evaluate(ct, evaluationContext);
if (value==null && evaluationContext!=null && (evaluationContext instanceof AbstractVariableSet)) {
value = ((AbstractVariableSet<T>)evaluationContext).get(literal);
}
values.push(value!=null ? value : toValue(literal, evaluationContext));
} else if (token.isOperator()) {
Operator operator = token.getOperator();
rpnPush("deq", operator.getSymbol());
values.push(evaluate(operator, getArguments(values, operator.getOperandCount()), evaluationContext));
} else {
throw new IllegalArgumentException(token.toString());
}
}
/** Evaluates a constant.
* <br>Subclasses that support constants must override this method.
* The default implementation throws a RuntimeException meaning that implementor forget to implement this method
* while creating a subclass that accepts constants.
* @param constant The constant
* @param evaluationContext The context of the evaluation
* @return The constant's value
*/
protected T evaluate(Constant constant, Object evaluationContext) {
throw new RuntimeException("evaluate(Constant) is not implemented for "+constant.getName());
}
/** Evaluates an operation.
* <br>Subclasses that support operators must override this method.
* The default implementation throws a RuntimeException meaning that implementor forget to implement this method
* while creating a subclass that accepts operators.
* @param operator The operator
* @param operands The operands
* @param evaluationContext The context of the evaluation
* @return The result of the operation
*/
protected T evaluate(Operator operator, Iterator<T> operands, Object evaluationContext) {
throw new RuntimeException("evaluate(Operator, Iterator) is not implemented for "+operator.getSymbol());
}
/** Evaluates a function.
* <br>Subclasses that support functions must override this method.
* The default implementation throws a RuntimeException meaning that implementor forget to implement this method
* while creating a subclass that accepts functions.
* @param function The function
* @param arguments The function's arguments
* @param evaluationContext The context of the evaluation
* @return The result of the function
*/
protected T evaluate(Function function, Iterator<T> arguments, Object evaluationContext) {
throw new RuntimeException("evaluate(Function, Iterator) is not implemented for "+function.getName());
}
private void doFunction(Deque<T> values, Function function, int argCount, Object evaluationContext) {
System.out.println("doFunction " + function + " " + argCount);
if (function.getMinimumArgumentCount()>argCount || function.getMaximumArgumentCount()<argCount) {
Iterator<?> a = getArguments(values, argCount);
Iterable iterable = () -> a;
List actualList = (List) StreamSupport
.stream(iterable.spliterator(), false)
.collect(Collectors.toList());
throw new IllegalArgumentException("Invalid argument count for "+function.getName() + " while " + argCount + ": " + actualList);
}
values.push(evaluate(function, getArguments(values, argCount), evaluationContext));
}
private Iterator<T> getArguments(Deque<T> values, int nb) {
// Be aware that arguments are in reverse order on the values stack.
// Don't forget to reorder them in the original order (the one they appear in the evaluated formula)
if (values.size()<nb) {
throw new IllegalArgumentException("Expected " + nb + " got " + values.size());
}
LinkedList<T> result = new LinkedList<T>();
for (int i = 0; i <nb ; i++) {
result.addFirst(values.pop());
}
return result.iterator();
}
/** Evaluates a literal (Converts it to a value).
* @param literal The literal to evaluate.
* @return an instance of T.
* @param evaluationContext The context of the evaluation
* @throws IllegalArgumentException if the literal can't be converted to a value.
*/
protected abstract T toValue(String literal, Object evaluationContext);
/** Evaluates an expression.
* @param expression The expression to evaluate.
* @return the result of the evaluation.
* @throws IllegalArgumentException if the expression is not correct.
*/
public T evaluate(String expression) {
return evaluate(expression, null);
}
/** Evaluates an expression that contains variables.
* @param expression The expression to evaluate.
* @param evaluationContext The context of the evaluation.
* <br>This context is an object that can contain useful dynamic data, for example the values of the variables
* used in the expression (Use an AbstractVariableSet to do that).<br>The context is not limited to variable values but
* can be used for any dynamic information. A good example is the <a href="http://javaluator.sourceforge.net/en/doc/tutorial.php?chapter=creatingComplex">BooleanSetEvaluator</a> one.
* @return the result of the evaluation.
* @throws IllegalArgumentException if the expression is not correct.
* @see AbstractVariableSet
*/
public T evaluate(String expression, Object evaluationContext) {
stackRPN.clear();
final Deque<T> values = new ArrayDeque<T>() {
@Override
public void push(T t) {
if (DETAILED_LOGGER)
System.out.println("v push " + t);
super.push(t);
}
}; // values stack
final Deque<Token> stack = new ArrayDeque<Token>() {
@Override
public void push(Token t) {
if (DETAILED_LOGGER)
System.out.println("fun push " + t);
super.push(t);
}
}; // operator stack
final Deque<Integer> previousValuesSize = functions.isEmpty()?null:new ArrayDeque<Integer>();
final Iterator<String> tokens = tokenize(expression);
Token previous = null;
while (tokens.hasNext()) {
// read one token from the input stream
String strToken = tokens.next();
final Token token = toToken(previous, strToken);
if (token.isOpenBracket()) {
// If the token is a left parenthesis, then push it onto the stack.
stack.push(token);
if (previous!=null && previous.isFunction()) {
if (!functionBrackets.containsKey(token.getBrackets().getOpen())) {
throw new IllegalArgumentException("Invalid bracket after function: "+strToken);
}
} else {
if (!expressionBrackets.containsKey(token.getBrackets().getOpen())) {
throw new IllegalArgumentException("Invalid bracket in expression: "+strToken);
}
}
} else if (token.isCloseBracket()) {
if (DETAILED_LOGGER)
System.out.println("isCloseBracket");
if (previous==null) {
throw new IllegalArgumentException("expression can't start with a close bracket");
}
if (previous.isFunctionArgumentSeparator()) {
throw new IllegalArgumentException("argument is missing");
}
BracketPair brackets = token.getBrackets();
// If the token is a right parenthesis:
boolean openBracketFound = false;
// Until the token at the top of the stack is a left parenthesis,
// pop operators off the stack onto the output queue
while (!stack.isEmpty()) {
Token sc = stack.pop();
if (sc.isOpenBracket()) {
if (sc.getBrackets().equals(brackets)) {
if (DETAILED_LOGGER)
System.out.println("close>isOpenBracket");
openBracketFound = true;
break;
} else {
throw new IllegalArgumentException("Invalid parenthesis match "+sc.getBrackets().getOpen()+brackets.getClose());
}
} else {
output(values, sc, evaluationContext);
}
}
if (!openBracketFound) {
// If the stack runs out without finding a left parenthesis, then
// there are mismatched parentheses.
throw new IllegalArgumentException("Parentheses mismatched");
}
if (!stack.isEmpty() && stack.peek().isFunction()) {
rpnPush("function", stack.peek().getFunction().getName());
// If the token at the top of the stack is a function token, pop it
// onto the output queue.
int argCount = values.size()-previousValuesSize.pop();
doFunction(values, stack.pop().getFunction(), argCount, evaluationContext);
}
} else if (token.isFunctionArgumentSeparator()) {
if (previous==null) {
throw new IllegalArgumentException("expression can't start with a function argument separator");
}
// Verify that there was an argument before this separator
if (previous.isOpenBracket() || previous.isFunctionArgumentSeparator()) {
// The cases were operator miss an operand are detected elsewhere.
throw new IllegalArgumentException("argument is missing");
}
// If the token is a function argument separator
boolean pe = false;
while (!stack.isEmpty()) {
if (stack.peek().isOpenBracket()) {
pe = true;
break;
} else {
// Until the token at the top of the stack is a left parenthesis,
// pop operators off the stack onto the output queue.
output(values, stack.pop(), evaluationContext);
}
}
if (!pe) {
// If no left parentheses are encountered, either the separator was misplaced
// or parentheses were mismatched.
throw new IllegalArgumentException("Separator or parentheses mismatched");
}
} else if (token.isFunction()) {
// If the token is a function token, then push it onto the stack.
stack.push(token);
previousValuesSize.push(values.size());
} else if (token.isOperator()) {
// If the token is an operator, op1, then:
while (!stack.isEmpty()) {
Token sc = stack.peek();
// While there is an operator token, o2, at the top of the stack
// op1 is left-associative and its precedence is less than or equal
// to that of op2,
// or op1 has precedence less than that of op2,
// Let + and ^ be right associative.
// Correct transformation from 1^2+3 is 12^3+
// The differing operator priority decides pop / push
// If 2 operators have equal priority then associativity decides.
if (sc.isOperator()
&& ((token.getAssociativity().equals(Operator.Associativity.LEFT) && (token.getPrecedence() <= sc.getPrecedence())) ||
(token.getPrecedence() < sc.getPrecedence()))) {
// rpnPush("op1", sc.getOperator().getRpnSymbol());
// Pop o2 off the stack, onto the output queue;
output(values, stack.pop(), evaluationContext);
} else {
break;
}
}
// push op1 onto the stack.
stack.push(token);
} else {
// If the token is a number (identifier), a constant or a variable, then add its value to the output queue.
if ((previous!=null) && previous.isLiteral()) {
throw new IllegalArgumentException("A literal can't follow another literal");
}
rpnPush("boolean", token.getBooleanHackedLiteral());
output(values, token, evaluationContext);
}
previous = token;
}
// When there are no more tokens to read:
// While there are still operator tokens in the stack:
while (!stack.isEmpty()) {
Token sc = stack.pop();
if (sc.isOpenBracket() || sc.isCloseBracket()) {
throw new IllegalArgumentException("Parentheses mismatched");
}
if (sc.isOperator()) {
// stackRPN.push(sc.getOperator().getRpnSymbol());
}
output(values, sc, evaluationContext);
}
if (values.size() != 1) {
throw new IllegalArgumentException(expression + ": Only one element expected "+ values);
}
Collections.reverse(stackRPN);
return values.pop();
}
private void rpnPush(String msg, String s) {
if (DETAILED_LOGGER)
System.out.println("RPN push " + msg + ": " + s);
stackRPN.push(s);
}
public String getPosftfixExpression() {
List<String> list = new ArrayList<>(stackRPN);
ListIterator<String> li = list.listIterator(list.size());
// List<String> reverse = new ArrayList<>();
StringBuilder sb = new StringBuilder();
while (li.hasPrevious()) {
if (sb.length() > 0)
sb.append(" ");
sb.append(li.previous());
}
// String result = reverse.toString();
return sb.toString();
}
private Token toToken(Token previous, String token) {
if (token.equals(functionArgumentSeparator)) {
return Token.FUNCTION_ARG_SEPARATOR;
} else if (functions.containsKey(token)) {
return Token.buildFunction(functions.get(token));
} else if (operators.containsKey(token)) {
List<Operator> list = operators.get(token);
return (list.size()==1) ? Token.buildOperator(list.get(0)) : Token.buildOperator(guessOperator(previous, list));
} else {
final BracketPair brackets = getBracketPair(token);
if (brackets!=null) {
if (brackets.getOpen().equals(token)) {
return Token.buildOpenToken(brackets);
} else {
return Token.buildCloseToken(brackets);
}
} else {
return Token.buildLiteral(token);
}
}
}
private BracketPair getBracketPair(String token) {
BracketPair result = expressionBrackets.get(token);
return result==null ? functionBrackets.get(token) : result;
}
/** Gets the operators supported by this evaluator.
* @return a collection of operators.
*/
public Collection<Operator> getOperators() {
ArrayList<Operator> result = new ArrayList<Operator>();
Collection<List<Operator>> values = this.operators.values();
for (List<Operator> list : values) {
result.addAll(list);
}
return result;
}
/** Gets the functions supported by this evaluator.
* @return a collection of functions.
*/
public Collection<Function> getFunctions() {
return this.functions.values();
}
/** Gets the constants supported by this evaluator.
* @return a collection of constants.
*/
public Collection<Constant> getConstants() {
return this.constants.values();
}
/** Converts the evaluated expression into tokens.
* <br>Example: The result for the expression "<i>-1+min(10,3)</i>" is an iterator on "-", "1", "+", "min", "(", "10", ",", "3", ")".
* <br>By default, the operators symbols, the brackets and the function argument separator are used as delimiter in the string.
* @param expression The expression that is evaluated
* @return A string iterator.
*/
protected Iterator<String> tokenize(String expression) {
return tokenizer.tokenize(expression);
}
}

View File

@ -1,16 +0,0 @@
package com.fathzer.soft.javaluator;
/** An abstract variable set.
* <br>Javaluator supports expression that contains variables (for example <i>sin(x)</i>).
* <br>An AbstractVariableSet converts, during the expression evaluation, each variable to its value.
* @param <T> The type of the values of the variable (the one handled by the evaluator).
* @author Jean-Marc Astesana
* @see <a href="../../../license.html">License information</a>
*/
public interface AbstractVariableSet<T> {
/** Gets the value of a variable.
* @param variableName The name of a variable
* @return the variable's value or null if the variable is unknown
*/
public T get(String variableName);
}

View File

@ -1,48 +0,0 @@
package com.fathzer.soft.javaluator;
/** A <a href="http://en.wikipedia.org/wiki/Bracket_(mathematics)">bracket pair</a>.
* @author Jean-Marc Astesana
* @see <a href="../../../license.html">License information</a>
*/
public class BracketPair {
/** The parentheses pair: ().*/
public static final BracketPair PARENTHESES = new BracketPair('(', ')');
/** The square brackets pair: [].*/
public static final BracketPair BRACKETS = new BracketPair('[', ']');
/** The braces pair: {}.*/
public static final BracketPair BRACES = new BracketPair('{', '}');
/** The angle brackets pair: <>.*/
public static final BracketPair ANGLES = new BracketPair('<', '>');
private String open;
private String close;
/** Constructor.
* @param open The character used to open the brackets.
* @param close The character used to close the brackets.
*/
public BracketPair(char open, char close) {
super();
this.open = new String(new char[]{open});
this.close = new String(new char[]{close});
}
/** Gets the open bracket character.
* @return a char
*/
public String getOpen() {
return open;
}
/** Gets the close bracket character.
* @return a char
*/
public String getClose() {
return close;
}
@Override
public String toString() {
return open + close;
}
}

View File

@ -1,31 +0,0 @@
package com.fathzer.soft.javaluator;
/**
* A constant in an expression.
* <br>Some expressions needs constants. For instance it is impossible to perform trigonometric calculus without using pi.
* A constant allows you to use mnemonic in your expressions instead of the raw value of the constant.
* <br>A constant for pi would be defined by :<br>
* <code>Constant<Double> pi = new Constant<Double>("pi");</code>
* <br>With such a constant, you will be able to evaluate the expression "sin(pi/4)"
* @author Jean-Marc Astesana
* @see <a href="../../../license.html">License information</a>
* @see AbstractEvaluator#evaluate(Constant, Object)
*/
public class Constant {
private String name;
/** Constructor
* @param name The mnemonic of the constant.
* <br>The name is used in expressions to identified the constants.
*/
public Constant(String name) {
this.name = name;
}
/** Gets the mnemonic of the constant.
* @return the id
*/
public String getName() {
return name;
}
}

View File

@ -1,395 +0,0 @@
package com.fathzer.soft.javaluator;
import org.jetbrains.annotations.NotNull;
import java.text.NumberFormat;
import java.text.ParsePosition;
import java.util.*;
import static com.rusefi.config.generated.Fields.*;
/** An evaluator that is able to evaluate arithmetic expressions on real numbers.
* <br>Built-in operators:<ul>
* <li>+: Addition</li>
* <li>-: Subtraction</li>
* <li>-: Unary minus</li>
* <li>*: Multiplication</li>
* <li>/: Division</li>
* <li>^: Exponentiation.<br>Warning: Exponentiation is implemented using java.lang.Math.pow which has some limitations (please read oracle documentation about this method to known details).<br>For example (-1)^(1/3) returns NaN.</li>
* <li>%: Modulo</li>
* </ul>
* Built-in functions:<ul>
* <li>abs: absolute value</li>
* <li>acos: arc cosine</li>
* <li>asin: arc sine</li>
* <li>atan: arc tangent</li>
* <li>average: average of arguments</li>
* <li>ceil: nearest upper integer</li>
* <li>cos: cosine</li>
* <li>cosh: hyperbolic cosine</li>
* <li>floor: nearest lower integer</li>
* <li>ln: natural logarithm (base e)</li>
* <li>log: base 10 logarithm</li>
* <li>max: maximum of arguments</li>
* <li>min: minimum of arguments</li>
* <li>round: nearest integer</li>
* <li>sin: sine</li>
* <li>sinh: hyperbolic sine</li>
* <li>sum: sum of arguments</li>
* <li>tan: tangent</li>
* <li>tanh: hyperbolic tangent</li>
* <li>random: pseudo-random number (between 0 and 1)</li>
* </ul>
* Built-in constants:<ul>
* <li>e: Base of natural algorithms</li>
* <li>pi: Ratio of the circumference of a circle to its diameter</li>
* </ul>
* @author Jean-Marc Astesana
* @see <a href="../../../license.html">License information</a>
*/
public class DoubleEvaluator extends AbstractEvaluator<Double> {
@NotNull
public static DoubleEvaluator process(String expression) {
DoubleEvaluator evaluator = new DoubleEvaluator();
evaluator.evaluate(expression.toLowerCase());
return evaluator;
}
/** The order or operations (operator precedence) is not clearly defined, especially between the unary minus operator and exponentiation
* operator (see <a href="http://en.wikipedia.org/wiki/Order_of_operations#Exceptions_to_the_standard">http://en.wikipedia.org/wiki/Order_of_operations</a>).
* These constants define the operator precedence styles.
*/
public static enum Style {
/** The most commonly operator precedence, where the unary minus as a lower precedence than the exponentiation.
* <br>With this style, used by Google, Wolfram alpha, and many others, -2^2=-4.
*/
STANDARD,
}
/** The true constant. */
public final static Constant TRUE = new Constant("true");
/** The false constant. */
public final static Constant FALSE = new Constant("false");
/** Returns the smallest integer >= argument */
public static final Function CEIL = new Function("ceil", 1);
/** Returns the largest integer <= argument */
public static final Function FLOOR = new Function("floor", 1);
/** Returns the closest integer of a number */
public static final Function ROUND = new Function("round", 1);
/** Returns the absolute value of a number */
public static final Function ABS = new Function("abs", 1);
/** Returns the trigonometric sine of an angle. The angle is expressed in radian.*/
public static final Function SINE = new Function("sin", 1);
/** Returns the trigonometric cosine of an angle. The angle is expressed in radian.*/
public static final Function COSINE = new Function("cos", 1);
/** Returns the trigonometric tangent of an angle. The angle is expressed in radian.*/
public static final Function TANGENT = new Function("tan", 1);
/** Returns the trigonometric arc-cosine of an angle. The angle is expressed in radian.*/
public static final Function ACOSINE = new Function("acos", 1);
/** Returns the trigonometric arc-sine of an angle. The angle is expressed in radian.*/
public static final Function ASINE = new Function("asin", 1);
/** Returns the trigonometric arc-tangent of an angle. The angle is expressed in radian.*/
public static final Function ATAN = new Function("atan", 1);
/** Returns the hyperbolic sine of a number.*/
public static final Function SINEH = new Function("sinh", 1);
/** Returns the hyperbolic cosine of a number.*/
public static final Function COSINEH = new Function("cosh", 1);
/** Returns the hyperbolic tangent of a number.*/
public static final Function TANGENTH = new Function("tanh", 1);
/** Returns the minimum of n numbers (n>=1) */
public static final Function MIN = new Function("min", 2);
/** Returns the maximum of n numbers (n>=1) */
public static final Function MAX = new Function("max", 2);
/** Returns the sum of n numbers (n>=1) */
// public static final Function SUM = new Function("sum", 2);
/** Returns the average of n numbers (n>=1) */
// public static final Function AVERAGE = new Function("avg", 2);
/** Returns the natural logarithm of a number */
public static final Function LN = new Function("ln", 1);
/** Returns the decimal logarithm of a number */
public static final Function LOG = new Function("log", 1);
public static final Function fsio_setting = new Function(FSIO_METHOD_FSIO_SETTING, 1);
public static final Function fsio_analog_input = new Function(FSIO_METHOD_FSIO_ANALOG_INPUT, 1);
public static final Function fsio_digital_input = new Function(FSIO_METHOD_FSIO_DIGITAL_INPUT, 1);
public static final Function if_function = new Function("if", 3);
public static final Function table_function = new Function(FSIO_METHOD_FSIO_TABLE, 3);
/** Returns a pseudo random number */
public static final Function RANDOM = new Function("random", 0);
/** The negate unary operator in the standard operator precedence.*/
public static final Operator NEGATE = new Operator("negate", 1, Operator.Associativity.RIGHT, 3, "negate");
/** The negate unary operator in the Excel like operator precedence.*/
// public static final Operator NEGATE_HIGH = new Operator("-", 1, Operator.Associativity.RIGHT, 5);
/** The negate unary operator in the standard operator precedence.*/
public static final Operator NOT = new Operator("!", 1, Operator.Associativity.RIGHT, 3);
public static final Operator NOT2 = new Operator("not", 1, Operator.Associativity.RIGHT, 3, "!");
public static final Operator MORE = new Operator(">", 2, Operator.Associativity.LEFT, 6);
public static final Operator MORE_EQ = new Operator(">=", 2, Operator.Associativity.LEFT, 6);
public static final Operator LESS = new Operator("<", 2, Operator.Associativity.LEFT, 6);
public static final Operator LESS_EQ = new Operator("<=", 2, Operator.Associativity.LEFT, 6);
public static final Operator OR2 = new Operator("or", 2, Operator.Associativity.LEFT, 12, "|");
public static final Operator OR = new Operator("|", 2, Operator.Associativity.LEFT, 12);
// https://en.wikipedia.org/wiki/Order_of_operations
public static final Operator AND = new Operator("&", 2, Operator.Associativity.LEFT, 11);
public static final Operator AND2 = new Operator("and", 2, Operator.Associativity.LEFT, 11, "&");
/** The substraction operator.*/
public static final Operator MINUS = new Operator("-", 2, Operator.Associativity.LEFT, 1);
/** The addition operator.*/
public static final Operator PLUS = new Operator("+", 2, Operator.Associativity.LEFT, 1);
/** The multiplication operator.*/
public static final Operator MULTIPLY = new Operator("*", 2, Operator.Associativity.LEFT, 2);
/** The division operator.*/
public static final Operator DIVIDE = new Operator("/", 2, Operator.Associativity.LEFT, 2);
/** The exponentiation operator.*/
public static final Operator EXPONENT = new Operator("^", 2, Operator.Associativity.LEFT, 4);
/** The <a href="http://en.wikipedia.org/wiki/Modulo_operation">modulo operator</a>.*/
public static final Operator MODULO = new Operator("%", 2, Operator.Associativity.LEFT, 2);
/** The standard whole set of predefined operators */
private static final Operator[] OPERATORS = new Operator[]{NEGATE, NOT, NOT2, MORE, MORE_EQ, AND, AND2, OR, OR2, LESS, LESS_EQ, MINUS, PLUS, MULTIPLY, DIVIDE, EXPONENT, MODULO};
private static final List<Function> MISC_FUNCTIONS = Arrays.asList(if_function,
fsio_setting,
fsio_analog_input,
fsio_digital_input,
table_function);
/** The whole set of predefined functions */
private static final List<Function> FUNCTIONS = new ArrayList<>(
Arrays.asList(
new Function[]{
SINE, COSINE, TANGENT, ASINE, ACOSINE, ATAN, SINEH, COSINEH, TANGENTH, MIN, MAX, LN, LOG, ROUND, CEIL, FLOOR, ABS, RANDOM,
}));
static {
FUNCTIONS.addAll(MISC_FUNCTIONS);
}
/** The whole set of predefined constants */
private static final Constant[] CONSTANTS = new Constant[]{TRUE, FALSE};
private static Parameters DEFAULT_PARAMETERS;
private static final ThreadLocal<NumberFormat> FORMATTER = new ThreadLocal<NumberFormat>() {
@Override
protected NumberFormat initialValue() {
return NumberFormat.getNumberInstance(Locale.US);
}
};
/** Gets a copy of DoubleEvaluator standard default parameters.
* <br>The returned parameters contains all the predefined operators, functions and constants.
* <br>Each call to this method create a new instance of Parameters.
* @return a Paramaters instance
* @see Style
*/
/** Gets a copy of DoubleEvaluator default parameters.
* <br>The returned parameters contains all the predefined operators, functions and constants.
* <br>Each call to this method create a new instance of Parameters.
* @return a Paramaters instance
*/
public static Parameters getDefaultParameters() {
Parameters result = new Parameters();
result.addOperators(Arrays.asList(OPERATORS));
result.addFunctions(FUNCTIONS);
result.addConstants(Arrays.asList(CONSTANTS));
result.addFunctionBracket(BracketPair.PARENTHESES);
result.addExpressionBracket(BracketPair.PARENTHESES);
return result;
}
public static Function getFunction(String token) {
for (Function function : FUNCTIONS) {
if (function.getName().equalsIgnoreCase(token)) {
return function;
}
}
return null;
}
private static Parameters getParameters() {
if (DEFAULT_PARAMETERS == null) {
DEFAULT_PARAMETERS = getDefaultParameters();
}
return DEFAULT_PARAMETERS;
}
/** Constructor.
* <br>This default constructor builds an instance with all predefined operators, functions and constants.
*/
public DoubleEvaluator() {
this(getParameters());
}
/** Constructor.
* <br>This constructor can be used to reduce the set of supported operators, functions or constants,
* or to localize some function or constant's names.
* @param parameters The parameters of the evaluator.
*/
public DoubleEvaluator(Parameters parameters) {
super(parameters);
}
@Override
protected Double toValue(String literal, Object evaluationContext) {
ParsePosition p = new ParsePosition(0);
Number result = FORMATTER.get().parse(literal, p);
if (p.getIndex() == 0 || p.getIndex() != literal.length()) {
/**
* We are here if function is not know
* TODO: shall we throw exception?
*/
System.err.println("Not known " + literal);
return 6666666.0; // let's return this magic in case of any function call
// throw new IllegalArgumentException(literal + " is not a number");
}
return result.doubleValue();
}
/* (non-Javadoc)
* @see net.astesana.javaluator.AbstractEvaluator#evaluate(net.astesana.javaluator.Constant)
*/
@Override
protected Double evaluate(Constant constant, Object evaluationContext) {
if (TRUE.equals(constant)) {
return 0.0;
} else if (FALSE.equals(constant)) {
return 0.0;
} else {
return super.evaluate(constant, evaluationContext);
}
}
/* (non-Javadoc)
* @see net.astesana.javaluator.AbstractEvaluator#evaluate(net.astesana.javaluator.Operator, java.util.Iterator)
*/
@Override
protected Double evaluate(Operator operator, Iterator<Double> operands, Object evaluationContext) {
if (NEGATE.equals(operator)) {
return -operands.next();
} else if (NOT.equals(operator) || NOT2.equals(operator)) {
return boolean2double(operands.next() != 1.0);
} else if (AND.equals(operator) || AND2.equals(operator)) {
return boolean2double((operands.next() == 1.0 ) && (operands.next() == 1.0));
} else if (OR.equals(operator) || OR2.equals(operator)) {
return boolean2double((operands.next() == 1.0 ) || (operands.next() == 1.0));
} else if (MORE.equals(operator)) {
return boolean2double(operands.next() > operands.next());
} else if (MORE_EQ.equals(operator)) {
return boolean2double(operands.next() >= operands.next());
} else if (LESS.equals(operator)) {
return boolean2double(operands.next() < operands.next());
} else if (LESS_EQ.equals(operator)) {
return boolean2double(operands.next() <= operands.next());
} else if (MINUS.equals(operator)) {
return operands.next() - operands.next();
} else if (PLUS.equals(operator)) {
return operands.next() + operands.next();
} else if (MULTIPLY.equals(operator)) {
return operands.next() * operands.next();
} else if (DIVIDE.equals(operator)) {
return operands.next() / operands.next();
} else if (EXPONENT.equals(operator)) {
return Math.pow(operands.next(),operands.next());
} else if (MODULO.equals(operator)) {
return operands.next() % operands.next();
} else {
return super.evaluate(operator, operands, evaluationContext);
}
}
private double boolean2double(boolean value) {
return value ? 1.0 : 0.0;
}
/* (non-Javadoc)
* @see net.astesana.javaluator.AbstractEvaluator#evaluate(net.astesana.javaluator.Function, java.util.Iterator)
*/
@Override
protected Double evaluate(Function function, Iterator<Double> arguments, Object evaluationContext) {
Double result;
if (ABS.equals(function)) {
result = Math.abs(arguments.next());
} else if (CEIL.equals(function)) {
result = Math.ceil(arguments.next());
} else if (FLOOR.equals(function)) {
result = Math.floor(arguments.next());
} else if (ROUND.equals(function)) {
Double arg = arguments.next();
if (arg==Double.NEGATIVE_INFINITY || arg==Double.POSITIVE_INFINITY) {
result = arg;
} else {
result = (double) Math.round(arg);
}
} else if (SINEH.equals(function)) {
result = Math.sinh(arguments.next());
} else if (COSINEH.equals(function)) {
result = Math.cosh(arguments.next());
} else if (TANGENTH.equals(function)) {
result = Math.tanh(arguments.next());
} else if (SINE.equals(function)) {
result = Math.sin(arguments.next());
} else if (COSINE.equals(function)) {
result = Math.cos(arguments.next());
} else if (TANGENT.equals(function)) {
result = Math.tan(arguments.next());
} else if (ACOSINE.equals(function)) {
result = Math.acos(arguments.next());
} else if (ASINE.equals(function)) {
result = Math.asin(arguments.next());
} else if (ATAN.equals(function)) {
result = Math.atan(arguments.next());
} else if (MIN.equals(function)) {
result = arguments.next();
while (arguments.hasNext()) {
result = Math.min(result, arguments.next());
}
} else if (MAX.equals(function)) {
result = arguments.next();
while (arguments.hasNext()) {
result = Math.max(result, arguments.next());
}
// } else if (SUM.equals(function)) {
// result = 0.;
// while (arguments.hasNext()) {
// result = result + arguments.next();
// }
// } else if (AVERAGE.equals(function)) {
// result = 0.;
// int nb = 0;
// while (arguments.hasNext()) {
// result = result + arguments.next();
// nb++;
// }
// result = result/nb;
} else if (LN.equals(function)) {
result = Math.log(arguments.next());
} else if (LOG.equals(function)) {
result = Math.log10(arguments.next());
} else if (RANDOM.equals(function)) {
result = Math.random();
} else if (MISC_FUNCTIONS.contains(function)) {
result = 333333.0;
} else {
result = super.evaluate(function, arguments, evaluationContext);
}
errIfNaN(result, function);
return result;
}
private void errIfNaN(Double result, Function function) {
if (result.equals(Double.NaN)) {
throw new IllegalArgumentException("Invalid argument passed to "+function.getName());
}
}
}

View File

@ -1,60 +0,0 @@
package com.fathzer.soft.javaluator;
/** A <a href="http://en.wikipedia.org/wiki/Function_(mathematics)">function</a>.
* @author Jean-Marc Astesana
* @see <a href="../../../license.html">License information</a>
*/
public class Function {
private final String name;
private final int minArgumentCount;
private final int maxArgumentCount;
/** Constructor.
* <br>This constructor builds a function with a variable arguments count.
* <br>For instance, a minimum function may have at least one argument.
* @param name The function's name
* @param argumentCount The function's argument count.
* @throws IllegalArgumentException if minArgumentCount is less than 0 or greater than maxArgumentCount or if the function name is null or empty.
*/
public Function(String name, int argumentCount) {
this.minArgumentCount = argumentCount;
this.maxArgumentCount = argumentCount;
if ((argumentCount<0) || (minArgumentCount>maxArgumentCount)) {
throw new IllegalArgumentException("Invalid argument count");
}
if (name==null || name.length()==0) {
throw new IllegalArgumentException("Invalid function name");
}
this.name = name;
}
/** Gets the function's name.
* @return the name of the function
*/
public String getName() {
return this.name;
}
/** Gets the function's minimum argument count.
* @return an integer
*/
public int getMinimumArgumentCount() {
return this.minArgumentCount;
}
/** Gets the function's maximum argument count.
* @return an integer
*/
public int getMaximumArgumentCount() {
return this.maxArgumentCount;
}
@Override
public String toString() {
return "Function{" +
"name='" + name + '\'' +
", minArgumentCount=" + minArgumentCount +
", maxArgumentCount=" + maxArgumentCount +
'}';
}
}

View File

@ -1,161 +0,0 @@
package com.fathzer.soft.javaluator;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;
/** An <a href="http://en.wikipedia.org/wiki/Operator_(mathematics)">operator</a>.
* @author Jean-Marc Astesana
* @see <a href="../../../license.html">License information</a>
*/
@SuppressWarnings("WeakerAccess")
public class Operator {
private final String rpnSymbol;
public static final List<String> _1_OPERATORS = new ArrayList<>();
public static final List<String> _2_OPERATORS = new ArrayList<>();
private static Set<String> symbols = new TreeSet<>(String.CASE_INSENSITIVE_ORDER);
/** An Operator's <a href="http://en.wikipedia.org/wiki/Operator_associativity">associativity</a>.
*/
public enum Associativity {
/** Left associativity.*/
LEFT,
/** Right associativity. */
RIGHT,
/** No associativity.*/
NONE
}
private String symbol;
private int precedence;
private int operandCount;
private Associativity associativity;
/** Constructor.
* @param symbol The operator name (Currently, the name's length must be one character).
* @param operandCount The number of operands of the operator (must be 1 or 2).
* @param associativity true if operator is left associative
* @param precedence The <a href="http://en.wikipedia.org/wiki/Order_of_operations">precedence</a> of the operator.
* <br>The precedence is the priority of the operator. An operator with an higher precedence will be executed before an operator with a lower precedence.
* Example : In "<i>1+3*4</i>" * has a higher precedence than +, so the expression is interpreted as 1+(3*4).
* @throws IllegalArgumentException if operandCount if not 1 or 2 or if associativity is none
* @throws NullPointerException if symbol or associativity are null
*/
Operator(String symbol, int operandCount, Associativity associativity, int precedence) {
this(symbol, operandCount, associativity, precedence, null);
}
Operator(String symbol, int operandCount, Associativity associativity, int precedence, String rpnSymbol) {
this.rpnSymbol = rpnSymbol;
if (symbol==null || associativity==null) {
throw new NullPointerException();
}
if (symbol.length()==0) {
throw new IllegalArgumentException("Operator symbol can't be null");
}
if ((operandCount<1) || (operandCount>2)) {
throw new IllegalArgumentException("Only unary and binary operators are supported");
}
if (Associativity.NONE.equals(associativity)) {
throw new IllegalArgumentException("None associativity operators are not supported");
}
this.symbol = symbol;
this.operandCount = operandCount;
this.associativity = associativity;
this.precedence = precedence;
if (operandCount == 1) {
_1_OPERATORS.add(symbol);
} else if (operandCount == 2) {
_2_OPERATORS.add(symbol);
} else {
throw new IllegalStateException("Unexpected operand count: " + symbol);
}
if (symbols.contains(symbol))
throw new IllegalStateException("Not unique symbol " + symbol);
symbols.add(symbol);
}
/** Gets the operator's symbol.
* @return a String
*/
public String getSymbol() {
return this.symbol;
}
/** Gets the operator's operand count.
* @return an integer
*/
public int getOperandCount() {
return this.operandCount;
}
/** Gets this operator's associativity.
* @return true if the operator is left associative.
* @see <a href="http://en.wikipedia.org/wiki/Operator_associativity">Operator's associativity in Wikipedia</a>
*/
public Associativity getAssociativity() {
return this.associativity;
}
/** Gets the operator's precedence.
* @return an integer
* @see <a href="http://en.wikipedia.org/wiki/Order_of_operations">Operator's associativity in Wikipedia</a>
*/
public int getPrecedence() {
return this.precedence;
}
/* (non-Javadoc)
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + operandCount;
result = prime * result + ((associativity == null) ? 0 : associativity.hashCode());
result = prime * result + ((symbol == null) ? 0 : symbol.hashCode());
result = prime * result + precedence;
return result;
}
/* (non-Javadoc)
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if ((obj == null) || (obj instanceof Operator)) {
return false;
}
Operator other = (Operator) obj;
if ((operandCount != other.operandCount) || (associativity != other.associativity)) {
return false;
}
if (symbol == null) {
if (other.symbol != null) {
return false;
}
} else if (!symbol.equals(other.symbol)) {
return false;
}
if (precedence != other.precedence) {
return false;
}
return true;
}
@Override
public String toString() {
return "Operator{" +
", symbol='" + symbol + '\'' +
", precedence=" + precedence +
", operandCount=" + operandCount +
", associativity=" + associativity +
'}';
}
}

View File

@ -1,186 +0,0 @@
package com.fathzer.soft.javaluator;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/** The parameters of an evaluator.
* <br>An evaluator may have different parameters as the supported operators, the supported functions, etc ...
* @author Jean-Marc Astesana
* @see <a href="../../../license.html">License information</a>
*/
public class Parameters {
private String functionSeparator;
private final List<Operator> operators;
private final List<Function> functions;
private final List<Constant> constants;
private final Map<String, String> translations;
private final List<BracketPair> expressionBrackets;
private final List<BracketPair> functionBrackets;
/** Constructor.
* <br>This method builds an instance with no operator, no function, no constant, no translation and no bracket
* <br>Function argument separator is set to ','.
*/
public Parameters() {
this.operators = new ArrayList<Operator>();
this.functions = new ArrayList<Function>();
this.constants = new ArrayList<Constant>();
this.translations = new HashMap<String, String>();
this.expressionBrackets = new ArrayList<BracketPair>();
this.functionBrackets = new ArrayList<BracketPair>();
setFunctionArgumentSeparator(',');
}
/** Gets the supported operators.
* @return a Collection of operators.
*/
public Collection<Operator> getOperators() {
return this.operators;
}
/** Gets the supported functions.
* @return a Collection of functions.
*/
public Collection<Function> getFunctions() {
return this.functions;
}
/** Gets the supported constants.
* @return a Collection of constants.
*/
public Collection<Constant> getConstants() {
return this.constants;
}
/** Gets the supported bracket pairs for expressions.
* @return a Collection of bracket pairs.
*/
public Collection<BracketPair> getExpressionBrackets() {
return this.expressionBrackets;
}
/** Gets the supported bracket pairs for functions.
* @return a Collection of bracket pairs.
*/
public Collection<BracketPair> getFunctionBrackets() {
return this.functionBrackets;
}
/** Adds operators to the supported ones.
* @param operators The operators to be added.
*/
public void addOperators(Collection<Operator> operators) {
this.operators.addAll(operators);
}
/** Adds an operator to the supported ones.
* @param operator The added operator
*/
public void add(Operator operator) {
this.operators.add(operator);
}
/** Adds functions to the supported ones.
* @param functions The functions to be added.
*/
public void addFunctions(Collection<Function> functions) {
this.functions.addAll(functions);
}
/** Adds a function to the supported ones.
* @param function The added function
*/
public void add(Function function) {
this.functions.add(function);
}
/** Adds constants to the supported ones.
* @param constants The constants to be added.
*/
public void addConstants(Collection<Constant> constants) {
this.constants.addAll(constants);
}
/** Adds a constant to the supported ones.
* @param constant The added constant
*/
public void add(Constant constant) {
this.constants.add(constant);
}
/** Adds a new bracket pair to the expression bracket list.
* @param pair A bracket pair
*/
public void addExpressionBracket(BracketPair pair) {
this.expressionBrackets.add(pair);
}
/** Adds bracket pairs to the expression bracket list.
* @param brackets The brackets to be added.
*/
public void addExpressionBrackets(Collection<BracketPair> brackets) {
this.expressionBrackets.addAll(brackets);
}
/** Adds a new bracket pair to the function bracket list.
* @param pair A bracket pair
*/
public void addFunctionBracket(BracketPair pair) {
this.functionBrackets.add(pair);
}
/** Adds bracket pairs to the function bracket list.
* @param brackets The brackets to be added.
*/
public void addFunctionBrackets(Collection<BracketPair> brackets) {
this.functionBrackets.addAll(brackets);
}
/** Sets the translated term for a function.
* <br>Using this method, you can localize the names of some built-in functions. For instance,
* for french people,you can use this method to use "somme" instead of "sum" with the SUM built-in
* function of DoubleEvaluator.
* @param function The function you want to translate the name
* @param translatedName The translated name
* @see DoubleEvaluator#SUM
*/
public void setTranslation(Function function, String translatedName) {
setTranslation(function.getName(), translatedName);
}
/** Sets the translated term for a constant.
* @param constant The constant you want to translate the name
* @param translatedName The translated name
* @see #setTranslation(Function, String)
*/
public void setTranslation(Constant constant, String translatedName) {
setTranslation(constant.getName(), translatedName);
}
private void setTranslation(String name, String translatedName) {
this.translations.put(name, translatedName);
}
String getTranslation(String originalName) {
String translation = this.translations.get(originalName);
return translation==null?originalName:translation;
}
/** Sets the function argument separator.
* <br>Its default value is ','.
* @param separator The new separator
*/
public void setFunctionArgumentSeparator(char separator) {
this.functionSeparator = new String(new char[]{separator});
}
/** Gets the function argument separator.
* @return a string
*/
public String getFunctionArgumentSeparator() {
return this.functionSeparator;
}
}

View File

@ -1,142 +0,0 @@
package com.fathzer.soft.javaluator;
/** A token.
* <br>When evaluating an expression, it is first split into tokens.
* These tokens can be operators, constants, etc ...
* @author Jean-Marc Astesana
* @see <a href="../../../license.html">License information</a>
*/
public class Token {
private enum Kind {
OPEN_BRACKET,
CLOSE_BRACKET,
FUNCTION_SEPARATOR,
FUNCTION,
OPERATOR,
LITERAL
}
static final Token FUNCTION_ARG_SEPARATOR = new Token(Kind.FUNCTION_SEPARATOR, null);
private Kind kind;
private Object content;
static Token buildLiteral(String literal) {
return new Token(Kind.LITERAL, literal);
}
static Token buildOperator(Operator ope) {
return new Token(Kind.OPERATOR, ope);
}
static Token buildFunction(Function function) {
return new Token(Kind.FUNCTION, function);
}
static Token buildOpenToken(BracketPair pair) {
return new Token(Kind.OPEN_BRACKET, pair);
}
static Token buildCloseToken(BracketPair pair) {
return new Token(Kind.CLOSE_BRACKET, pair);
}
private Token(Kind kind, Object content) {
super();
if ((kind.equals(Kind.OPERATOR) && !(content instanceof Operator)) ||
(kind.equals(Kind.FUNCTION) && !(content instanceof Function)) ||
(kind.equals(Kind.LITERAL) && !(content instanceof String))) {
throw new IllegalArgumentException();
}
this.kind = kind;
this.content = content;
}
BracketPair getBrackets() {
return (BracketPair) this.content;
}
Operator getOperator() {
return (Operator) this.content;
}
Function getFunction() {
return (Function) this.content;
}
Kind getKind() {
return kind;
}
/** Tests whether the token is an operator.
* @return true if the token is an operator
*/
public boolean isOperator() {
return kind.equals(Kind.OPERATOR);
}
/** Tests whether the token is a function.
* @return true if the token is a function
*/
public boolean isFunction() {
return kind.equals(Kind.FUNCTION);
}
/** Tests whether the token is an open bracket.
* @return true if the token is an open bracket
*/
public boolean isOpenBracket() {
return kind.equals(Kind.OPEN_BRACKET);
}
/** Tests whether the token is a close bracket.
* @return true if the token is a close bracket
*/
public boolean isCloseBracket() {
return kind.equals(Kind.CLOSE_BRACKET);
}
/** Tests whether the token is a function argument separator.
* @return true if the token is a function argument separator
*/
public boolean isFunctionArgumentSeparator() {
return kind.equals(Kind.FUNCTION_SEPARATOR);
}
/** Tests whether the token is a literal or a constant or a variable name.
* @return true if the token is a literal, a constant or a variable name
*/
public boolean isLiteral() {
return kind.equals(Kind.LITERAL);
}
Operator.Associativity getAssociativity() {
return getOperator().getAssociativity();
}
int getPrecedence() {
return getOperator().getPrecedence();
}
String getLiteral() {
if (!this.kind.equals(Kind.LITERAL)) {
throw new IllegalArgumentException("Literal expected but " + kind);
}
return (String)this.content;
}
String getBooleanHackedLiteral() {
if ("true".equalsIgnoreCase((String) content))
return "1";
if ("false".equalsIgnoreCase((String) content))
return "0";
return getLiteral();
}
@Override
public String toString() {
return "Token{" +
"kind=" + kind +
", content=" + content +
'}';
}
}

View File

@ -1,173 +0,0 @@
package com.fathzer.soft.javaluator;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.StringTokenizer;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/** A String tokenizer that accepts delimiters that are greater than one character.
* @author Jean-Marc Astesana
* @see <a href="../../../license.html">License information</a>
*/
public class Tokenizer {
private Pattern pattern;
private String tokenDelimiters;
private boolean trimTokens;
/** Constructor.
* <br>By default, this tokenizer trims all the tokens.
* @param delimiters the delimiters of the tokenizer, usually, the operators symbols, the brackets and the function argument separator are used as delimiter in the string.
*/
public Tokenizer(List<String> delimiters) {
if (onlyOneChar(delimiters)) {
StringBuilder builder = new StringBuilder();
for (String delimiter : delimiters) {
builder.append(delimiter);
}
tokenDelimiters = builder.toString();
} else {
this.pattern = delimitersToRegexp(delimiters);
}
trimTokens = true;
}
/** Tests whether this tokens trims the tokens returned by {@link #tokenize(String)} method.
* @return true if tokens are trimmed.
*/
public boolean isTrimTokens() {
return trimTokens;
}
/** Sets the trimTokens attribute.
* @param trimTokens true to have the tokens returned by {@link #tokenize(String)} method trimmed.
* <br>Note that empty tokens are always omitted by this class.
*/
public void setTrimTokens(boolean trimTokens) {
this.trimTokens = trimTokens;
}
/** Tests whether a String list contains only 1 character length elements.
* @param delimiters The list to test
* @return true if it contains only one char length elements (or no elements)
*/
private boolean onlyOneChar(List<String> delimiters) {
for (String delimiter : delimiters) {
if (delimiter.length()!=1) {
return false;
}
}
return true;
}
private static Pattern delimitersToRegexp(List<String> delimiters) {
// First, create a regular expression that match the union of the delimiters
// Be aware that, in case of delimiters containing others (example && and &),
// the longer may be before the shorter (&& should be before &) or the regexpr
// parser will recognize && as two &.
Collections.sort(delimiters, new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
return -o1.compareTo(o2);
}
});
// Build a string that will contain the regular expression
StringBuilder result = new StringBuilder();
result.append('(');
for (String delim : delimiters) {
// For each delimiter
if (result.length()!=1) {
// Add it to the union
result.append('|');
}
// Quote the delimiter as it could contain some regexpr reserved characters
result.append("\\Q").append(delim).append("\\E");
}
result.append(')');
return Pattern.compile(result.toString());
}
private void addToTokens (List<String> tokens, String token) {
if (trimTokens) {
token = token.trim();
}
if (!token.isEmpty()) {
tokens.add(token);
}
}
/** Converts a string into tokens.
* <br>Example: The result for the expression "<i>-1+min(10,3)</i>" evaluated for a DoubleEvaluator is an iterator on "-", "1", "+", "min", "(", "10", ",", "3", ")".
* @param string The string to be split into tokens
* @return The tokens
*/
public Iterator<String> tokenize(String string) {
if (pattern!=null) {
List<String> res = new ArrayList<String>();
Matcher m = pattern.matcher(string);
int pos = 0;
while (m.find()) {
// While there's a delimiter in the string
if (pos != m.start()) {
// If there's something between the current and the previous delimiter
// Add to the tokens list
addToTokens(res, string.substring(pos, m.start()));
}
addToTokens(res, m.group()); // add the delimiter
pos = m.end(); // Remember end of delimiter
}
if (pos != string.length()) {
// If it remains some characters in the string after last delimiter
addToTokens(res, string.substring(pos));
}
// Return the result
return res.iterator();
} else {
return new StringTokenizerIterator(new StringTokenizer(string, tokenDelimiters, true));
}
}
private class StringTokenizerIterator implements Iterator<String> {
private StringTokenizer tokens;
/** Constructor.
* @param tokens The Stringtokenizer on which is based this instance.
*/
public StringTokenizerIterator(StringTokenizer tokens) {
this.tokens = tokens;
}
private String nextToken = null;
@Override
public boolean hasNext() {
return buildNextToken();
}
@Override
public String next() {
if (!buildNextToken()) {
throw new NoSuchElementException();
}
String token = nextToken;
nextToken = null;
return token;
}
private boolean buildNextToken() {
while ((nextToken == null) && tokens.hasMoreTokens()) {
nextToken = tokens.nextToken();
if (trimTokens) {
nextToken = nextToken.trim();
}
if (nextToken.isEmpty()) {
nextToken = null;
}
}
return nextToken!=null;
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
}
}

View File

@ -1,73 +0,0 @@
package com.rusefi;
import com.fathzer.soft.javaluator.DoubleEvaluator;
import com.fathzer.soft.javaluator.Function;
import com.fathzer.soft.javaluator.Operator;
import java.util.Stack;
public class InfixConverter {
public static String getHumanInfixFormOrError(String rpn) {
try {
return getHumanInfixForm(rpn);
} catch (Throwable e) {
return "Failing to parse. Maybe invalid function was used? Valid functions include " + getReadableListOfFunctions();
}
}
public static String getHumanInfixForm(String rpn) {
String tokens[] = rpn.split(" ");
Stack<String> stack = new Stack<>();
for (String token : tokens) {
if (DoubleEvaluator.getFunction(token) != null) {
int pCount = DoubleEvaluator.getFunction(token).getMaximumArgumentCount();
StringBuilder sb = new StringBuilder();
for (int i = 0; i < pCount; i++) {
if (i != 0)
sb.insert(0, ", ");
if (stack.isEmpty())
throw new IllegalStateException("While getting " + pCount + "parameters for " + token);
sb.insert(0, stack.pop());
}
stack.push(token + "(" + sb + ")");
} else if (Operator._1_OPERATORS.contains(token)) {
String a = stack.pop();
stack.push("(" + token + " " + a + ")");
} else if (takesTwoParameters(token)) {
if (stack.size() < 2)
throw new IllegalStateException("Not enough " + token);
String a = stack.pop();
String b = stack.pop();
stack.push("(" + b + " " + token + " " + a + ")");
} else {
stack.push(token);
}
}
if (stack.size() != 1)
throw new IllegalStateException("Unexpected stack content: " + stack);
return stack.pop();
}
private static boolean takesTwoParameters(String token) {
return Operator._2_OPERATORS.contains(token);
}
public static String getReadableListOfFunctions() {
StringBuilder result = new StringBuilder();
DoubleEvaluator evaluator = new DoubleEvaluator();
for (Function function : evaluator.getFunctions()) {
if (result.length() > 0) {
result.append(", ");
}
result.append(function.getName());
}
return result.toString();
}
}

View File

@ -1,142 +0,0 @@
package com.rusefi.test;
import com.fathzer.soft.javaluator.DoubleEvaluator;
import com.fathzer.soft.javaluator.Operator;
import com.rusefi.InfixConverter;
import com.rusefi.config.generated.Fields;
import org.junit.Test;
import java.text.ParseException;
import static org.junit.Assert.*;
/**
* @see DoubleEvaluator
* @see Operator
*/
public class ReversePolishNotationParserTest {
@Test
public void testFunctionParameters() {
assertParseB("3 log", "log(3)");
assertParseB("1 2 3 if", "if(1, 2, 3)");
assertParseB("1 3 max", "max(1, 3)");
try {
assertHumanToRPN("max(1 3)", "3 1 max");
fail("Error expected");
} catch (Throwable ignored) {
// expected
}
assertHumanToRPN("log 3 ", "log 3"); // todo: better handling?
assertParseB("0 fsio_setting", "fsio_setting(0)");
assertParseB("rpm 2 fsio_setting >", "rpm > fsio_setting(2)");
}
@Test
public void testBooleanConversion() throws ParseException {
assertParseB("2 1 >", "2 > 1");
assertParseB("1 2 + 3 -", "1 + 2 - 3");
assertParseB("1 2 | 3 |", "1 | 2 | 3");
assertParseB("1 2 + 3 4 5 + / +", "1 + 2 + 3 / (4 +5 )");
assertHumanToRPN("rpm > false", "rpm 0 >");
assertHumanToRPN("(rpm > false)", "rpm 0 >");
assertParseB("rpm user0 > clt user2 > or", "(rpm > user0) or (clt > user2)");
assertParseB("1 2 | 3 |", "1 | 2 | 3");
assertParseB("rpm user0 > clt user2 > or vbatt user1 > or", "(rpm > user0) or (clt > user2) or (vbatt > user1)");
}
private void assertParseB(String rpn, String expression) {
assertHumanToRPN(expression, rpn);
// humans line infix notation for some reason
String humanInfixForm = InfixConverter.getHumanInfixForm(rpn);
System.out.println(humanInfixForm);
assertEquals("infix recovery", getReplace(expression).toLowerCase(), getReplace(humanInfixForm).toLowerCase());
}
private String getReplace(String h) {
return h.replaceAll("[\\(\\)]", "").replace(" ", "");
}
private void assertHumanToRPN(String inputExpression, String expectedRPN) {
DoubleEvaluator evaluator = new DoubleEvaluator();
evaluator.evaluate(inputExpression.toLowerCase());
assertEquals(expectedRPN, evaluator.getPosftfixExpression());
}
@Test
public void testRpnToHuman() {
assertEquals("if((fsio_analog_input(0) > 20), 0, 10)", InfixConverter.getHumanInfixForm("0 fsio_analog_input 20 > 0 10 if"));
assertTrue(InfixConverter.getHumanInfixFormOrError("0 fsoi_input 20 > 0 10 if").contains(Fields.FSIO_METHOD_FSIO_TABLE));
}
@Test
public void testListOfFunctions() {
String readableListOfFunctions = InfixConverter.getReadableListOfFunctions();
assertTrue(readableListOfFunctions.contains(Fields.FSIO_METHOD_FSIO_DIGITAL_INPUT));
assertTrue(readableListOfFunctions.contains(Fields.FSIO_METHOD_FSIO_TABLE));
}
@Test
public void testUnaryMinus() {
/**
* do we even need to and/or should to support unary minus?
*
* http://stackoverflow.com/questions/20246787/handling-unary-minus-for-shunting-yard-algorithm
*/
assertValue("3 negate", "negate3", -3);
assertValue("2 3 6 ^ negate +", "2+negate 3^6", -727.0);
}
@Test
public void testBooleanNot2() throws Exception {
assertValue("0 !", "! 0", 1);
assertValue("0 not", "not 0", 1);
assertValue("1 not", "not(1)", 0);
assertValue("0 not", "not(0)", 1);
// assertValue("1 0 | not 0 & 1 0 | |", "(((true | false) & not(false)) | (true | false))", 1);
}
private void assertValue(String expectedRpn, String expression, double expectedValue) {
DoubleEvaluator evaluator = new DoubleEvaluator();
assertEquals(expectedValue, evaluator.evaluate(expression), 0.001);
assertParseB(expectedRpn, expression);
}
@Test
public void testRusEfi() {
assertParseB("1 4 or", "1 or 4");
assertParseB("1 4 or", "1 OR 4");
assertParseB("time_since_boot 4 <", "(time_since_boot < 4)");
assertParseB("1 4 |", "1 | 4");
assertParseB("time_since_boot 4 < rpm 0 > |", "(time_since_boot < 4) | (rpm > 0)");
assertParseB("rpm 4500 > trottle 50 > and", "(rpm > 4500) and (trottle> 50)");
assertParseB("1 4 and", "1 and 4");
assertParseB("1 4 and", "1 AND 4");
assertParseB("1 4 &", "1 & 4");
assertParseB("coolant fan_off_setting >", "(coolant > fan_off_setting)");
assertParseB("1 coolant fan_on_setting > or", "1 OR (coolant > fan_on_setting)");
assertParseB("fan coolant fan_off_setting > and coolant fan_on_setting > or", "(fan and (coolant > fan_off_setting)) OR (coolant > fan_on_setting)");
assertParseB("time_since_boot 4 <= rpm 0 > |", "(time_since_boot <= 4) | (rpm > 0)");
assertParseB("time_since_boot 4 <= rpm 0 > |", "(time_since_boot <= 4) | (rpm > 0)");
assertParseB("time_since_boot 4 <= rpm 0 > or", "(time_since_boot <= 4) OR (rpm > 0)");
assertParseB("self rpm 4800 >= and rpm 5000 > or", "(self and (rpm >= 4800)) OR (rpm > 5000)");
}
}

View File

@ -1,83 +0,0 @@
package com.rusefi;
import com.fathzer.soft.javaluator.DoubleEvaluator;
import com.opensr5.Logger;
import java.io.*;
import java.util.List;
/**
* http://rusefi.com/wiki/index.php?title=Manual:Flexible_Logic
* <p/>
* Andrey Belomutskiy, (c) 2013-2020
* 1/19/2017
*/
public class CompileTool {
private static String NEWLINE = "\n";
public static int run(List<String> args) throws IOException {
System.out.println("Params " + args);
if (args.size() != 2) {
System.out.println("Please specify input file and output file name");
return -1;
}
String inputFileName = args.get(0);
System.out.println("Reading from " + inputFileName);
String outputFileName = args.get(1);
System.out.println("Writing to " + outputFileName);
BufferedReader br = new BufferedReader(new FileReader(inputFileName));
try (BufferedWriter bw = new BufferedWriter(new FileWriter(outputFileName))) {
bw.write("// this https://en.wikipedia.org/wiki/Reverse_Polish_notation is generated automatically" + NEWLINE);
bw.write("// from " + inputFileName + NEWLINE);
bw.write("// on " + Logger.getDate() + NEWLINE + "//" + NEWLINE);
String line;
while ((line = br.readLine()) != null) {
line = line.trim();
// line = line.replaceAll("\\s+", " ");
bw.write(handleOneFsioLine(line));
}
}
System.out.println("Done!");
return 0;
}
public static String handleOneFsioLine(String line) throws IOException {
if (line.isEmpty())
return "";
StringBuilder result = new StringBuilder();
if (line.charAt(0) == '#') {
// forwarding comment into the output
result.append("//" + line.substring(1) + NEWLINE);
return result.toString();
}
int indexOfEquals = line.indexOf('=');
if (indexOfEquals == -1) {
System.err.println("Unexpected line: " + line);
System.exit(-1);
}
String name = line.substring(0, indexOfEquals).trim();
String expression = line.substring(indexOfEquals + 1).trim();
String rpn;
try {
rpn = DoubleEvaluator.process(expression).getPosftfixExpression();
} catch (Throwable e) {
throw new IllegalStateException("For " + expression, e);
}
result.append(NEWLINE + "// Human-readable: " + expression + NEWLINE);
result.append("#define " + name + " \"" + rpn + "\"" + NEWLINE);
return result.toString();
}
}

View File

@ -1,6 +1,5 @@
package com.rusefi.tools;
import com.fathzer.soft.javaluator.DoubleEvaluator;
import com.opensr5.ConfigurationImage;
import com.opensr5.ini.IniFileModel;
import com.opensr5.io.ConfigurationImageFile;
@ -59,10 +58,6 @@ public class ConsoleTools {
registerTool("get_image_tune_crc", ConsoleTools::calcBinaryImageTuneCrc, "Calculate tune CRC for given binary tune");
registerTool("get_xml_tune_crc", ConsoleTools::calcXmlImageTuneCrc, "Calculate tune CRC for given XML tune");
registerTool("compile_fsio_line", ConsoleTools::invokeCompileExpressionTool, "Convert a line to RPN form.");
registerTool("decompile_fsio_line", ConsoleTools::invokeDecompileExpressionTool, "Convert a line from RPN form.");
registerTool("compile_fsio_file", ConsoleTools::runCompileTool, "Convert all lines from a file to RPN form.");
registerTool("network_connector", strings -> NetworkConnectorStartup.start(), "Connect your rusEFI ECU to rusEFI Online");
registerTool("network_authenticator", strings -> LocalApplicationProxy.start(), "rusEFI Online Authenticator");
registerTool("elm327_connector", strings -> Elm327ConnectorStartup.start(), "Connect your rusEFI ECU using ELM327 CAN-bus adapter");
@ -201,11 +196,6 @@ public class ConsoleTools {
FiringOrderTSLogic.invoke(args[1]);
}
private static void runCompileTool(String[] args) throws IOException {
int returnCode = invokeCompileFileTool(args);
System.exit(returnCode);
}
private static void setAuthToken(String[] args) {
String newToken = args[1];
System.out.println("Saving auth token " + newToken);
@ -318,33 +308,6 @@ public class ConsoleTools {
}, "callback");
}
private static int invokeCompileFileTool(String[] args) throws IOException {
/**
* re-packaging array which contains input and output file names
*/
return CompileTool.run(Arrays.asList(args).subList(1, args.length));
}
private static void invokeDecompileExpressionTool(String[] args) {
if (args.length != 2) {
System.err.println("input RPN parameter expected");
System.exit(-1);
}
String rpn = args[1];
String humanForm = InfixConverter.getHumanInfixFormOrError(rpn);
System.out.println(humanForm);
}
private static void invokeCompileExpressionTool(String[] args) {
if (args.length != 2) {
System.err.println("input expression parameter expected");
System.exit(-1);
}
String expression = args[1];
System.out.println(DoubleEvaluator.process(expression).getPosftfixExpression());
}
public static boolean runTool(String[] args) throws Exception {
if (args == null || args.length == 0)
return false;

View File

@ -1,49 +0,0 @@
package com.rusefi.ui.fsio;
import com.fathzer.soft.javaluator.DoubleEvaluator;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
/**
* Andrey Belomutskiy, (c) 2013-2020
* 10/10/14
*/
public class FlexibleControls {
private final JPanel panel = new JPanel(new GridLayout(5, 1));
private final JTextField normalForm = new JTextField();
private final JTextField rpnForm = new JTextField();
public FlexibleControls() {
JButton human2rpm = new JButton("Convert");
human2rpm.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
process(FlexibleControls.this.normalForm.getText());
}
});
panel.add(new JLabel("Human-readable"));
panel.add(normalForm);
panel.add(human2rpm);
panel.add(new JLabel("RPN form"));
panel.add(rpnForm);
normalForm.setText("(time_since_boot < 4) | (rpm > 0)");
process(normalForm.getText());
}
private void process(String text) {
rpnForm.setText(DoubleEvaluator.process(text).getPosftfixExpression());
}
public JPanel getPanel() {
return panel;
}
}

View File

@ -1,8 +1,6 @@
package com.rusefi.ui.widgets;
import com.fathzer.soft.javaluator.DoubleEvaluator;
import com.rusefi.FileLog;
import com.rusefi.InfixConverter;
import com.rusefi.NamedThreadFactory;
import com.rusefi.core.MessagesCentral;
import com.rusefi.functional_tests.EcuTestHelper;
@ -132,23 +130,9 @@ public class AnyCommand {
public static String prepareCommand(String rawCommand, LinkManager linkManager) {
try {
if (rawCommand.startsWith("eval" + " ")) {
String result = prepareEvalCommand(rawCommand);
if (result.equals(rawCommand)) {
// result was not translated
MessagesCentral.getInstance().postMessage(AnyCommand.class, "Not valid expression");
MessagesCentral.getInstance().postMessage(AnyCommand.class, "Please try eval \"2 + 2\"");
MessagesCentral.getInstance().postMessage(AnyCommand.class, "For RPN use rpn_eval \"2 2 +\"");
}
return result;
} else if (rawCommand.toLowerCase().startsWith("stim_check" + " ")) {
if (rawCommand.toLowerCase().startsWith("stim_check" + " ")) {
handleStimulationSelfCheck(rawCommand, linkManager);
return null;
} else if (rawCommand.toLowerCase().startsWith(DECODE_RPN + " ")) {
handleDecodeRpn(rawCommand);
return null;
} else if (rawCommand.toLowerCase().startsWith("set_fsio_expression" + " ")) {
return prepareSetFsioCommand(rawCommand);
} else {
return rawCommand;
}
@ -191,44 +175,10 @@ public class AnyCommand {
}).start();
}
private static String prepareSetFsioCommand(String rawCommand) {
String[] parts = rawCommand.split(" ", 3);
if (parts.length != 3)
return rawCommand; // let's ignore invalid command
return "set_rpn_expression " + parts[1] + " " + quote(infix2postfix(unquote(parts[2])));
}
private static void handleDecodeRpn(String rawCommand) {
String[] parts = rawCommand.split(" ", 2);
if (parts.length != 2) {
MessagesCentral.getInstance().postMessage(AnyCommand.class, "Failed to parse, one argument expected");
return;
}
String argument = unquote(parts[1]);
String humanForm = InfixConverter.getHumanInfixFormOrError(argument);
MessagesCentral.getInstance().postMessage(AnyCommand.class, "Human form is \"" + humanForm + "\"");
}
public static String prepareEvalCommand(String rawCommand) {
String[] parts = rawCommand.split(" ", 2);
if (parts.length != 2)
return rawCommand; // let's ignore invalid command
try {
return "rpn_eval" + " " + quote(infix2postfix(unquote(parts[1])));
} catch (IllegalArgumentException e) {
return rawCommand;
}
}
private static String quote(String s) {
return "\"" + s + "\"";
}
private static String infix2postfix(String infixExpression) {
return DoubleEvaluator.process(infixExpression).getPosftfixExpression();
}
public static String unquote(String quoted) {
quoted = quoted.trim().replace('\u201C', '"').replace('\u201D', '"');
if (quoted.charAt(0) == '"')

View File

@ -1,14 +0,0 @@
package com.rusefi.ui.test;
import com.rusefi.ui.fsio.FlexibleControls;
import com.rusefi.ui.util.FrameHelper;
/**
* Andrey Belomutskiy, (c) 2013-2020
* 12/4/14
*/
public class FlexibleControlsSandbox {
public static void main(String[] args) {
new FrameHelper().showFrame(new FlexibleControls().getPanel());
}
}