diff --git a/src/main/java/net/objecthunter/exp4j/Expression.java b/src/main/java/net/objecthunter/exp4j/Expression.java
index 11069bef..80ab46b4 100644
--- a/src/main/java/net/objecthunter/exp4j/Expression.java
+++ b/src/main/java/net/objecthunter/exp4j/Expression.java
@@ -18,9 +18,8 @@
import java.util.*;
import java.util.concurrent.*;
-import net.objecthunter.exp4j.function.Function;
-import net.objecthunter.exp4j.function.Functions;
-import net.objecthunter.exp4j.operator.Operator;
+import net.objecthunter.exp4j.function.*;
+import net.objecthunter.exp4j.operator.*;
import net.objecthunter.exp4j.tokenizer.*;
public class Expression {
@@ -208,4 +207,204 @@ private double[] reverseInPlace(double[] args) {
}
return args;
}
+
+
+ /**
+ * Convert the Expression
back to string expression.
+ *
Calling this function returns exactly the same result as calling {@link #toString(boolean) toString}(false),
+ * so it uses the '*' sign in multiplications
+ *
+ * @return a string representation of the Expression
.
+ */
+ public String toString() {
+ return toString(false);
+ }
+
+ /**
+ * Convert the Expression
back to string expression.
+ * The argument implicitMultiplication
determines whether to use the '*' sign
+ * in the returned string expression.
+ * Every occurrence of the '*' sign will be removed, except when there is a number to the right of it.
+ *
+ * @param implicitMultiplication
+ * if true
, removes the '*' sign in multiplications (only when it's logical)
+ *
+ * @return a string representation of the Expression
.
+ */
+ public String toString(boolean implicitMultiplication) {
+ String expression = toString(Arrays.asList(tokens), implicitMultiplication);
+
+ if(implicitMultiplication) {
+ expression = expression.replaceAll("\\*(\\D)", "$1");
+ }
+
+ return expression;
+ }
+
+ private String toString(List tokens, boolean impMult) {
+ if(tokens.size() == 0)
+ return "";
+ Token token = tokens.get(tokens.size()-1);
+
+ switch (token.getType()) {
+ case Token.TOKEN_OPERATOR:
+ Operator operator = ((OperatorToken) token).getOperator();
+ List> operands = getTokensArguments(tokens.subList(0, tokens.size()-1), operator.getNumOperands());
+ List leftTokens,
+ rightTokens;
+ if(operator.getNumOperands() == 1) {
+ if(operator.isLeftAssociative()) {
+ leftTokens = operands.get(0);
+ rightTokens = new ArrayList();
+ }
+ else {
+ leftTokens = new ArrayList();
+ rightTokens = operands.get(0);
+ }
+ }
+ else {
+ if(operator.getSymbol().equals("*") && operands.get(1).size() == 1 && operands.get(0).get(operands.get(0).size()-1).getType() != Token.TOKEN_NUMBER) {
+ leftTokens = operands.get(1);
+ rightTokens = operands.get(0);
+ } else {
+ leftTokens = operands.get(0);
+ rightTokens = operands.get(1);
+ }
+ }
+
+ boolean parentheses_left = leftTokens.size() > 1 && leftTokens.get(leftTokens.size()-1).getType() != Token.TOKEN_FUNCTION,
+ parentheses_right = rightTokens.size() > 1 && rightTokens.get(rightTokens.size()-1).getType() != Token.TOKEN_FUNCTION;
+ if(parentheses_left && leftTokens.get(leftTokens.size()-1).getType() == Token.TOKEN_OPERATOR) {
+ Operator leftOperator = ((OperatorToken) leftTokens.get(leftTokens.size()-1)).getOperator();
+ if(leftOperator.getNumOperands() == 1 && leftOperator.getSymbol().matches("\\+|-") && !operator.getSymbol().matches("\\+|-")) {
+ parentheses_left = true;
+ }
+ else {
+ if(leftOperator.getSymbol().matches("\\+|-|\\*")) {
+ parentheses_left = operator.getPrecedence() > leftOperator.getPrecedence();
+ }
+ else {
+ parentheses_left = operator.getPrecedence() >= leftOperator.getPrecedence();
+ }
+ }
+ }
+ if(parentheses_right && rightTokens.get(rightTokens.size()-1).getType() == Token.TOKEN_OPERATOR) {
+ Operator rightOperator = ((OperatorToken) rightTokens.get(rightTokens.size()-1)).getOperator();
+ if(rightOperator.getNumOperands() == 1 && rightOperator.getSymbol().matches("\\+|-")) {
+ parentheses_right = true;
+ }
+ else {
+ if(operator.getSymbol().matches("\\+|\\*") && rightOperator.getSymbol().matches("\\+|\\*")) {
+ parentheses_right = operator.getPrecedence() > rightOperator.getPrecedence();
+ }
+ else {
+ parentheses_right = operator.getPrecedence() >= rightOperator.getPrecedence();
+ }
+ }
+ }
+
+ if(!parentheses_left && leftTokens.size() > 0 && leftTokens.get(leftTokens.size()-1).getType() == Token.TOKEN_NUMBER) {
+ parentheses_left = ((NumberToken) leftTokens.get(0)).getValue() < 0;
+ }
+ if(!parentheses_right && rightTokens.size() > 0 && rightTokens.get(rightTokens.size()-1).getType() == Token.TOKEN_NUMBER) {
+ parentheses_right = ((NumberToken) rightTokens.get(0)).getValue() < 0;
+ }
+
+ String leftOperand = toString(leftTokens, impMult),
+ rightOperand = toString(rightTokens, impMult),
+ symbol = operator.getSymbol();
+
+ if(parentheses_left) {
+ leftOperand = "("+leftOperand+")";
+ }
+ if(parentheses_right) {
+ rightOperand = "("+rightOperand+")";
+ }
+
+ return leftOperand + symbol + rightOperand;
+
+ case Token.TOKEN_FUNCTION:
+ Function function = ((FunctionToken) token).getFunction();
+
+ if(function.getName().equals("pow")) {
+ tokens.set(tokens.size()-1, new OperatorToken(Operators.getBuiltinOperator('^', 2)));
+ return toString(tokens, impMult);
+ }
+
+ String stringArgs = "";
+ List> args = getTokensArguments(tokens.subList(0, tokens.size()-1), function.getNumArguments());
+ for (List argument : args) {
+ stringArgs += ", "+toString(argument, impMult);
+ }
+ stringArgs = stringArgs.substring(2);
+
+ return function.getName()+"("+stringArgs+")";
+
+ case Token.TOKEN_VARIABLE:
+ return ((VariableToken) token).getName();
+
+ case Token.TOKEN_NUMBER:
+ double num = ((NumberToken) token).getValue();
+ if(num != (long) num) {
+ return String.valueOf(num);
+ } else {
+ return String.valueOf((long) num);
+ }
+
+ default:
+ throw new UnsupportedOperationException("The token type '"+token.getClass().getName()+"' is not supported in this function yet");
+ }
+ }
+
+
+ private List> getTokensArguments(List tokens, int numOperands) {
+ List> tArgs = new ArrayList>(2);
+ if(numOperands == 1) {
+ tArgs.add(tokens);
+ }
+ else {
+ int size = 0;
+ int[] pos = new int[numOperands-1];
+ for (int i = 0; i < tokens.size()-1; i++) {
+ Token t = tokens.get(i);
+ switch (t.getType()) {
+ case Token.TOKEN_NUMBER:
+ size++;
+ break;
+
+ case Token.TOKEN_VARIABLE:
+ size++;
+ break;
+
+ case Token.TOKEN_OPERATOR:
+ Operator operator = ((OperatorToken) t).getOperator();
+ if (operator.getNumOperands() == 2)
+ size --;
+ break;
+
+ case Token.TOKEN_FUNCTION:
+ FunctionToken func = (FunctionToken) t;
+ for (int j = 0; j < func.getFunction().getNumArguments(); j++) {
+ size--;
+ }
+ size++;
+ break;
+ }
+ for (int j = 0; j < pos.length; j++) {
+ if(size == j+1) {
+ pos[j] = i;
+ }
+ }
+ }
+
+ tArgs.add(tokens.subList(0, pos[0]+1));
+ for (int i = 1; i < pos.length; i++) {
+ tArgs.add(tokens.subList(pos[i-1]+1, pos[i]+1));
+ }
+ tArgs.add(tokens.subList(pos[pos.length-1]+1, tokens.size()));
+ }
+
+ return tArgs;
+ }
+
}