From f23e1efe73f59a0abe3765de42c2238136d63adf Mon Sep 17 00:00:00 2001 From: Ron C Date: Thu, 6 Aug 2015 23:00:12 +0300 Subject: [PATCH 1/7] Added toString function to Expression class --- .../net/objecthunter/exp4j/Expression.java | 113 +++++++++++++++++- 1 file changed, 109 insertions(+), 4 deletions(-) diff --git a/src/main/java/net/objecthunter/exp4j/Expression.java b/src/main/java/net/objecthunter/exp4j/Expression.java index 11069bef..46dc6945 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,110 @@ private double[] reverseInPlace(double[] args) { } return args; } -} + + + public String toString() { + return toString(new ArrayList(Arrays.asList(tokens))); + } + + private String toString(List tokens) { + 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> args = getTokensArguments(tokens.subList(0, tokens.size()-1), operator.getNumOperands()); + List leftTokens = args.get(0), + reightTokens = args.get(1); + if(operator.getNumOperands() == 1 && !operator.isLeftAssociative()) { + leftTokens = args.get(1); + reightTokens = args.get(0); + } + + boolean parenthesis_left = leftTokens.size() > 1 && leftTokens.get(leftTokens.size()-1).getType() != Token.TOKEN_FUNCTION, + parenthesis_right = reightTokens.size() > 1 && reightTokens.get(reightTokens.size()-1).getType() != Token.TOKEN_FUNCTION; + if(parenthesis_left && leftTokens.get(leftTokens.size()-1).getType() == Token.TOKEN_OPERATOR) { + parenthesis_left = operator.getPrecedence() > ((OperatorToken) leftTokens.get(leftTokens.size()-1)).getOperator().getPrecedence() + || ((OperatorToken) leftTokens.get(leftTokens.size()-1)).getOperator().getNumOperands() == 1; + } + if(parenthesis_right && reightTokens.get(reightTokens.size()-1).getType() == Token.TOKEN_OPERATOR) { + parenthesis_right = operator.getPrecedence() > ((OperatorToken) reightTokens.get(reightTokens.size()-1)).getOperator().getPrecedence() + || ((OperatorToken) reightTokens.get(reightTokens.size()-1)).getOperator().getNumOperands() == 1; + } + + if(!parenthesis_left && leftTokens.size() == 1 && leftTokens.get(0).getType() == Token.TOKEN_NUMBER) { + parenthesis_left = ((NumberToken) leftTokens.get(0)).getValue() < 0; + } + if(!parenthesis_right && reightTokens.size() == 1 && reightTokens.get(0).getType() == Token.TOKEN_NUMBER) { + parenthesis_right = ((NumberToken) reightTokens.get(0)).getValue() < 0; + } + + return (parenthesis_left?"(":"")+toString(leftTokens)+(parenthesis_left?")":"")+operator.getSymbol()+(parenthesis_right?"(":"")+toString(reightTokens)+(parenthesis_right?")":""); + + case Token.TOKEN_FUNCTION: + return ((FunctionToken) token).getFunction().getName()+"("+toString(tokens.subList(0, tokens.size()-1))+")"; + + case Token.TOKEN_VARIABLE: + return ((VariableToken) token).getName(); + + case Token.TOKEN_NUMBER: + return ""+((NumberToken) token).getValue(); + + 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); + tArgs.add(new ArrayList(0)); + } + else { + int last = 0; + final ArrayStack output = new ArrayStack(); + for (int i = 0; i < tokens.size()-1; i++) { + Token t = tokens.get(i); + switch (t.getType()) { + case Token.TOKEN_NUMBER: + output.push(((NumberToken) t).getValue()); + break; + + case Token.TOKEN_VARIABLE: + output.push(1d); + break; + + case Token.TOKEN_OPERATOR: + Operator operator = ((OperatorToken) t).getOperator(); + if (operator.getNumOperands() == 2) + output.push(operator.apply(output.pop(), output.pop())); + else if (operator.getNumOperands() == 1) + output.push(operator.apply(output.pop())); + break; + + case Token.TOKEN_FUNCTION: + FunctionToken func = (FunctionToken) t; + double[] args = new double[func.getFunction().getNumArguments()]; + for (int j = 0; j < func.getFunction().getNumArguments(); j++) { + args[j] = output.pop(); + } + output.push(func.getFunction().apply(this.reverseInPlace(args))); + break; + } + if(output.size() == 1) { + last = i; + } + } + + tArgs.add(tokens.subList(0, last+1)); + tArgs.add(tokens.subList(last+1, tokens.size())); + } + + return tArgs; + } + +} \ No newline at end of file From 3a77d0d98a5ee3930b372ed707bf3f94d7ab251d Mon Sep 17 00:00:00 2001 From: Ron C Date: Sat, 8 Aug 2015 01:48:59 +0300 Subject: [PATCH 2/7] Added documentation and some fixes --- .../net/objecthunter/exp4j/Expression.java | 28 ++++++++++++++++--- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/src/main/java/net/objecthunter/exp4j/Expression.java b/src/main/java/net/objecthunter/exp4j/Expression.java index 46dc6945..f1598738 100644 --- a/src/main/java/net/objecthunter/exp4j/Expression.java +++ b/src/main/java/net/objecthunter/exp4j/Expression.java @@ -209,8 +209,16 @@ private double[] reverseInPlace(double[] args) { } - public String toString() { - return toString(new ArrayList(Arrays.asList(tokens))); + /** + * Convert the Expression back to string expression. + *

Functions with more then one argument are not supported yet, + * but all built-in functions are supported (the 'pow' Function will be translate to '^')

+ * + * @return a string representation of the Expression. + * @throws UnsupportedOperationException if the Expression contains a function with more then one argument + */ + public String toString() throws UnsupportedOperationException{ + return toString(Arrays.asList(tokens)); } private String toString(List tokens) { @@ -250,13 +258,25 @@ private String toString(List tokens) { return (parenthesis_left?"(":"")+toString(leftTokens)+(parenthesis_left?")":"")+operator.getSymbol()+(parenthesis_right?"(":"")+toString(reightTokens)+(parenthesis_right?")":""); case Token.TOKEN_FUNCTION: - return ((FunctionToken) token).getFunction().getName()+"("+toString(tokens.subList(0, tokens.size()-1))+")"; + Function function = ((FunctionToken) token).getFunction(); + + if(function.getNumArguments() > 1) { + if(function.getName().equals("pow")) { + tokens.set(tokens.size()-1, new OperatorToken(Operators.getBuiltinOperator('^', 2))); + return toString(tokens); + } + else { + throw new UnsupportedOperationException("Functions with more then one argument are not supported yet"); + } + } + + return function.getName()+"("+toString(tokens.subList(0, tokens.size()-1))+")"; case Token.TOKEN_VARIABLE: return ((VariableToken) token).getName(); case Token.TOKEN_NUMBER: - return ""+((NumberToken) token).getValue(); + return String.valueOf(((NumberToken) token).getValue()); default: throw new UnsupportedOperationException("The token type '"+token.getClass().getName()+"' is not supported in this function yet"); From a99d8c6cf4fb4e1121bd5a76d8a118a8dc5ca54c Mon Sep 17 00:00:00 2001 From: Ron C Date: Sat, 8 Aug 2015 02:22:00 +0300 Subject: [PATCH 3/7] spelling fix --- .../net/objecthunter/exp4j/Expression.java | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/main/java/net/objecthunter/exp4j/Expression.java b/src/main/java/net/objecthunter/exp4j/Expression.java index f1598738..09d2739d 100644 --- a/src/main/java/net/objecthunter/exp4j/Expression.java +++ b/src/main/java/net/objecthunter/exp4j/Expression.java @@ -231,31 +231,31 @@ private String toString(List tokens) { Operator operator = ((OperatorToken) token).getOperator(); List> args = getTokensArguments(tokens.subList(0, tokens.size()-1), operator.getNumOperands()); List leftTokens = args.get(0), - reightTokens = args.get(1); + rightTokens = args.get(1); if(operator.getNumOperands() == 1 && !operator.isLeftAssociative()) { leftTokens = args.get(1); - reightTokens = args.get(0); + rightTokens = args.get(0); } boolean parenthesis_left = leftTokens.size() > 1 && leftTokens.get(leftTokens.size()-1).getType() != Token.TOKEN_FUNCTION, - parenthesis_right = reightTokens.size() > 1 && reightTokens.get(reightTokens.size()-1).getType() != Token.TOKEN_FUNCTION; + parenthesis_right = rightTokens.size() > 1 && rightTokens.get(rightTokens.size()-1).getType() != Token.TOKEN_FUNCTION; if(parenthesis_left && leftTokens.get(leftTokens.size()-1).getType() == Token.TOKEN_OPERATOR) { parenthesis_left = operator.getPrecedence() > ((OperatorToken) leftTokens.get(leftTokens.size()-1)).getOperator().getPrecedence() || ((OperatorToken) leftTokens.get(leftTokens.size()-1)).getOperator().getNumOperands() == 1; } - if(parenthesis_right && reightTokens.get(reightTokens.size()-1).getType() == Token.TOKEN_OPERATOR) { - parenthesis_right = operator.getPrecedence() > ((OperatorToken) reightTokens.get(reightTokens.size()-1)).getOperator().getPrecedence() - || ((OperatorToken) reightTokens.get(reightTokens.size()-1)).getOperator().getNumOperands() == 1; + if(parenthesis_right && rightTokens.get(rightTokens.size()-1).getType() == Token.TOKEN_OPERATOR) { + parenthesis_right = operator.getPrecedence() > ((OperatorToken) rightTokens.get(rightTokens.size()-1)).getOperator().getPrecedence() + || ((OperatorToken) rightTokens.get(rightTokens.size()-1)).getOperator().getNumOperands() == 1; } if(!parenthesis_left && leftTokens.size() == 1 && leftTokens.get(0).getType() == Token.TOKEN_NUMBER) { parenthesis_left = ((NumberToken) leftTokens.get(0)).getValue() < 0; } - if(!parenthesis_right && reightTokens.size() == 1 && reightTokens.get(0).getType() == Token.TOKEN_NUMBER) { - parenthesis_right = ((NumberToken) reightTokens.get(0)).getValue() < 0; + if(!parenthesis_right && rightTokens.size() == 1 && rightTokens.get(0).getType() == Token.TOKEN_NUMBER) { + parenthesis_right = ((NumberToken) rightTokens.get(0)).getValue() < 0; } - return (parenthesis_left?"(":"")+toString(leftTokens)+(parenthesis_left?")":"")+operator.getSymbol()+(parenthesis_right?"(":"")+toString(reightTokens)+(parenthesis_right?")":""); + return (parenthesis_left?"(":"")+toString(leftTokens)+(parenthesis_left?")":"")+operator.getSymbol()+(parenthesis_right?"(":"")+toString(rightTokens)+(parenthesis_right?")":""); case Token.TOKEN_FUNCTION: Function function = ((FunctionToken) token).getFunction(); From c5a80da6954bdec58b427a3398b6e974b969e87a Mon Sep 17 00:00:00 2001 From: Ron C Date: Sat, 8 Aug 2015 22:01:56 +0300 Subject: [PATCH 4/7] get arguments function improvments and bug fix --- .../net/objecthunter/exp4j/Expression.java | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/src/main/java/net/objecthunter/exp4j/Expression.java b/src/main/java/net/objecthunter/exp4j/Expression.java index 09d2739d..99a634a8 100644 --- a/src/main/java/net/objecthunter/exp4j/Expression.java +++ b/src/main/java/net/objecthunter/exp4j/Expression.java @@ -291,37 +291,33 @@ private List> getTokensArguments(List tokens, int numOperands tArgs.add(new ArrayList(0)); } else { - int last = 0; - final ArrayStack output = new ArrayStack(); + int size = 0, last = 0; for (int i = 0; i < tokens.size()-1; i++) { Token t = tokens.get(i); switch (t.getType()) { case Token.TOKEN_NUMBER: - output.push(((NumberToken) t).getValue()); + size++; break; case Token.TOKEN_VARIABLE: - output.push(1d); + size++; break; case Token.TOKEN_OPERATOR: Operator operator = ((OperatorToken) t).getOperator(); if (operator.getNumOperands() == 2) - output.push(operator.apply(output.pop(), output.pop())); - else if (operator.getNumOperands() == 1) - output.push(operator.apply(output.pop())); + size --; break; case Token.TOKEN_FUNCTION: FunctionToken func = (FunctionToken) t; - double[] args = new double[func.getFunction().getNumArguments()]; for (int j = 0; j < func.getFunction().getNumArguments(); j++) { - args[j] = output.pop(); + size--; } - output.push(func.getFunction().apply(this.reverseInPlace(args))); + size++; break; } - if(output.size() == 1) { + if(size == 1) { last = i; } } From 76bf2a33f610ba325d9acedb2d3855e5704f71b9 Mon Sep 17 00:00:00 2001 From: ron Date: Thu, 13 Aug 2015 19:04:52 +0300 Subject: [PATCH 5/7] added support for functions with multiple arguments and some more improvments --- .../net/objecthunter/exp4j/Expression.java | 92 ++++++++++++------- 1 file changed, 60 insertions(+), 32 deletions(-) diff --git a/src/main/java/net/objecthunter/exp4j/Expression.java b/src/main/java/net/objecthunter/exp4j/Expression.java index 99a634a8..6b5011d6 100644 --- a/src/main/java/net/objecthunter/exp4j/Expression.java +++ b/src/main/java/net/objecthunter/exp4j/Expression.java @@ -211,14 +211,11 @@ private double[] reverseInPlace(double[] args) { /** * Convert the Expression back to string expression. - *

Functions with more then one argument are not supported yet, - * but all built-in functions are supported (the 'pow' Function will be translate to '^')

* * @return a string representation of the Expression. - * @throws UnsupportedOperationException if the Expression contains a function with more then one argument */ - public String toString() throws UnsupportedOperationException{ - return toString(Arrays.asList(tokens)); + public String toString() { + return toString(Arrays.asList(tokens)).replaceAll("\\(\\((-|\\+)([^\\)]+)\\)", "($1$2").replaceAll(" \\(([^\\)]+)\\)", " $1"); } private String toString(List tokens) { @@ -229,23 +226,53 @@ private String toString(List tokens) { switch (token.getType()) { case Token.TOKEN_OPERATOR: Operator operator = ((OperatorToken) token).getOperator(); - List> args = getTokensArguments(tokens.subList(0, tokens.size()-1), operator.getNumOperands()); - List leftTokens = args.get(0), - rightTokens = args.get(1); - if(operator.getNumOperands() == 1 && !operator.isLeftAssociative()) { - leftTokens = args.get(1); - rightTokens = args.get(0); + 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 { + leftTokens = operands.get(0); + rightTokens = operands.get(1); } boolean parenthesis_left = leftTokens.size() > 1 && leftTokens.get(leftTokens.size()-1).getType() != Token.TOKEN_FUNCTION, parenthesis_right = rightTokens.size() > 1 && rightTokens.get(rightTokens.size()-1).getType() != Token.TOKEN_FUNCTION; if(parenthesis_left && leftTokens.get(leftTokens.size()-1).getType() == Token.TOKEN_OPERATOR) { - parenthesis_left = operator.getPrecedence() > ((OperatorToken) leftTokens.get(leftTokens.size()-1)).getOperator().getPrecedence() - || ((OperatorToken) leftTokens.get(leftTokens.size()-1)).getOperator().getNumOperands() == 1; + Operator leftOperator = ((OperatorToken) leftTokens.get(leftTokens.size()-1)).getOperator(); + if(leftOperator.getNumOperands() == 1 && leftOperator.getSymbol().matches("\\+|-")) { + parenthesis_left = true; + } + else { + if(leftOperator.getSymbol().matches("\\+|\\*")) { + parenthesis_left = operator.getPrecedence() > leftOperator.getPrecedence(); + } + else { + parenthesis_left = operator.getPrecedence() >= leftOperator.getPrecedence(); + } + } } if(parenthesis_right && rightTokens.get(rightTokens.size()-1).getType() == Token.TOKEN_OPERATOR) { - parenthesis_right = operator.getPrecedence() > ((OperatorToken) rightTokens.get(rightTokens.size()-1)).getOperator().getPrecedence() - || ((OperatorToken) rightTokens.get(rightTokens.size()-1)).getOperator().getNumOperands() == 1; + Operator rightOperator = ((OperatorToken) rightTokens.get(rightTokens.size()-1)).getOperator(); + if(rightOperator.getNumOperands() == 1 && rightOperator.getSymbol().matches("\\+|-")) { + parenthesis_right = true; + } + else { + if(operator.getSymbol().matches("\\+|\\*") && rightOperator.getSymbol().matches("\\+|\\*")) { + parenthesis_right = operator.getPrecedence() > rightOperator.getPrecedence(); + } + else { + parenthesis_right = operator.getPrecedence() >= rightOperator.getPrecedence(); + } + } } if(!parenthesis_left && leftTokens.size() == 1 && leftTokens.get(0).getType() == Token.TOKEN_NUMBER) { @@ -260,17 +287,13 @@ private String toString(List tokens) { case Token.TOKEN_FUNCTION: Function function = ((FunctionToken) token).getFunction(); - if(function.getNumArguments() > 1) { - if(function.getName().equals("pow")) { - tokens.set(tokens.size()-1, new OperatorToken(Operators.getBuiltinOperator('^', 2))); - return toString(tokens); - } - else { - throw new UnsupportedOperationException("Functions with more then one argument are not supported yet"); - } + String stringArgs = ""; + List> args = getTokensArguments(tokens.subList(0, tokens.size()-1), function.getNumArguments()); + for (List argument : args) { + stringArgs += ", "+toString(argument); } - - return function.getName()+"("+toString(tokens.subList(0, tokens.size()-1))+")"; + stringArgs = stringArgs.substring(2); + return function.getName()+"("+stringArgs+")"; case Token.TOKEN_VARIABLE: return ((VariableToken) token).getName(); @@ -288,10 +311,10 @@ private List> getTokensArguments(List tokens, int numOperands List> tArgs = new ArrayList>(2); if(numOperands == 1) { tArgs.add(tokens); - tArgs.add(new ArrayList(0)); } else { - int size = 0, last = 0; + 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()) { @@ -317,13 +340,18 @@ private List> getTokensArguments(List tokens, int numOperands size++; break; } - if(size == 1) { - last = i; - } + for (int j = 0; j < pos.length; j++) { + if(size == j+1) { + pos[j] = i; + } + } } - tArgs.add(tokens.subList(0, last+1)); - tArgs.add(tokens.subList(last+1, tokens.size())); + 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; From 228425497c911cb9590df1268ec33364e1575a50 Mon Sep 17 00:00:00 2001 From: ron Date: Fri, 14 Aug 2015 22:10:51 +0300 Subject: [PATCH 6/7] removed regex replaces because they not working well on nested parentheses --- .../net/objecthunter/exp4j/Expression.java | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/src/main/java/net/objecthunter/exp4j/Expression.java b/src/main/java/net/objecthunter/exp4j/Expression.java index 6b5011d6..d8bedf76 100644 --- a/src/main/java/net/objecthunter/exp4j/Expression.java +++ b/src/main/java/net/objecthunter/exp4j/Expression.java @@ -215,7 +215,7 @@ private double[] reverseInPlace(double[] args) { * @return a string representation of the Expression. */ public String toString() { - return toString(Arrays.asList(tokens)).replaceAll("\\(\\((-|\\+)([^\\)]+)\\)", "($1$2").replaceAll(" \\(([^\\)]+)\\)", " $1"); + return toString(Arrays.asList(tokens)); } private String toString(List tokens) { @@ -244,45 +244,45 @@ private String toString(List tokens) { rightTokens = operands.get(1); } - boolean parenthesis_left = leftTokens.size() > 1 && leftTokens.get(leftTokens.size()-1).getType() != Token.TOKEN_FUNCTION, - parenthesis_right = rightTokens.size() > 1 && rightTokens.get(rightTokens.size()-1).getType() != Token.TOKEN_FUNCTION; - if(parenthesis_left && leftTokens.get(leftTokens.size()-1).getType() == Token.TOKEN_OPERATOR) { + 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("\\+|-")) { - parenthesis_left = true; + parentheses_left = true; } else { if(leftOperator.getSymbol().matches("\\+|\\*")) { - parenthesis_left = operator.getPrecedence() > leftOperator.getPrecedence(); + parentheses_left = operator.getPrecedence() > leftOperator.getPrecedence(); } else { - parenthesis_left = operator.getPrecedence() >= leftOperator.getPrecedence(); + parentheses_left = operator.getPrecedence() >= leftOperator.getPrecedence(); } } } - if(parenthesis_right && rightTokens.get(rightTokens.size()-1).getType() == Token.TOKEN_OPERATOR) { + 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("\\+|-")) { - parenthesis_right = true; + parentheses_right = true; } else { if(operator.getSymbol().matches("\\+|\\*") && rightOperator.getSymbol().matches("\\+|\\*")) { - parenthesis_right = operator.getPrecedence() > rightOperator.getPrecedence(); + parentheses_right = operator.getPrecedence() > rightOperator.getPrecedence(); } else { - parenthesis_right = operator.getPrecedence() >= rightOperator.getPrecedence(); + parentheses_right = operator.getPrecedence() >= rightOperator.getPrecedence(); } } } - if(!parenthesis_left && leftTokens.size() == 1 && leftTokens.get(0).getType() == Token.TOKEN_NUMBER) { - parenthesis_left = ((NumberToken) leftTokens.get(0)).getValue() < 0; + if(!parentheses_left && leftTokens.size() == 1 && leftTokens.get(0).getType() == Token.TOKEN_NUMBER) { + parentheses_left = ((NumberToken) leftTokens.get(0)).getValue() < 0; } - if(!parenthesis_right && rightTokens.size() == 1 && rightTokens.get(0).getType() == Token.TOKEN_NUMBER) { - parenthesis_right = ((NumberToken) rightTokens.get(0)).getValue() < 0; + if(!parentheses_right && rightTokens.size() == 1 && rightTokens.get(0).getType() == Token.TOKEN_NUMBER) { + parentheses_right = ((NumberToken) rightTokens.get(0)).getValue() < 0; } - return (parenthesis_left?"(":"")+toString(leftTokens)+(parenthesis_left?")":"")+operator.getSymbol()+(parenthesis_right?"(":"")+toString(rightTokens)+(parenthesis_right?")":""); + return (parentheses_left?"(":"")+toString(leftTokens)+(parentheses_left?")":"")+operator.getSymbol()+(parentheses_right?"(":"")+toString(rightTokens)+(parentheses_right?")":""); case Token.TOKEN_FUNCTION: Function function = ((FunctionToken) token).getFunction(); From 89e707668b47d73461264f926e4825d4053062f0 Mon Sep 17 00:00:00 2001 From: ron99 Date: Fri, 27 Nov 2015 21:07:26 +0200 Subject: [PATCH 7/7] added implicit multiplication option (false by default) --- .../net/objecthunter/exp4j/Expression.java | 76 +++++++++++++++---- 1 file changed, 63 insertions(+), 13 deletions(-) diff --git a/src/main/java/net/objecthunter/exp4j/Expression.java b/src/main/java/net/objecthunter/exp4j/Expression.java index d8bedf76..80ab46b4 100644 --- a/src/main/java/net/objecthunter/exp4j/Expression.java +++ b/src/main/java/net/objecthunter/exp4j/Expression.java @@ -211,14 +211,37 @@ private double[] reverseInPlace(double[] 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(Arrays.asList(tokens)); + return toString(false); } - private String toString(List tokens) { + /** + * 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); @@ -240,19 +263,24 @@ private String toString(List tokens) { } } else { - leftTokens = operands.get(0); - rightTokens = operands.get(1); + 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("\\+|-")) { + if(leftOperator.getNumOperands() == 1 && leftOperator.getSymbol().matches("\\+|-") && !operator.getSymbol().matches("\\+|-")) { parentheses_left = true; } else { - if(leftOperator.getSymbol().matches("\\+|\\*")) { + if(leftOperator.getSymbol().matches("\\+|-|\\*")) { parentheses_left = operator.getPrecedence() > leftOperator.getPrecedence(); } else { @@ -260,7 +288,7 @@ private String toString(List tokens) { } } } - if(parentheses_right && rightTokens.get(rightTokens.size()-1).getType() == Token.TOKEN_OPERATOR) { + 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; @@ -275,31 +303,53 @@ private String toString(List tokens) { } } - if(!parentheses_left && leftTokens.size() == 1 && leftTokens.get(0).getType() == Token.TOKEN_NUMBER) { + 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() == 1 && rightTokens.get(0).getType() == Token.TOKEN_NUMBER) { + 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(); - return (parentheses_left?"(":"")+toString(leftTokens)+(parentheses_left?")":"")+operator.getSymbol()+(parentheses_right?"(":"")+toString(rightTokens)+(parentheses_right?")":""); + 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); + stringArgs += ", "+toString(argument, impMult); } stringArgs = stringArgs.substring(2); + return function.getName()+"("+stringArgs+")"; case Token.TOKEN_VARIABLE: return ((VariableToken) token).getName(); case Token.TOKEN_NUMBER: - return String.valueOf(((NumberToken) token).getValue()); + 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"); @@ -357,4 +407,4 @@ private List> getTokensArguments(List tokens, int numOperands return tArgs; } -} \ No newline at end of file +}