diff --git a/src/main/java/com/refinedmods/refinedstorage/util/EquationEvaluator.java b/src/main/java/com/refinedmods/refinedstorage/util/EquationEvaluator.java index ef5e59d85..ab5081dab 100644 --- a/src/main/java/com/refinedmods/refinedstorage/util/EquationEvaluator.java +++ b/src/main/java/com/refinedmods/refinedstorage/util/EquationEvaluator.java @@ -6,8 +6,6 @@ import java.util.NoSuchElementException; import java.util.StringTokenizer; import java.util.function.Predicate; -import javax.annotation.Nullable; - /** * Implements evaluation for simple math expressions using the shunting yard * algorithm. @@ -15,62 +13,47 @@ import javax.annotation.Nullable; public class EquationEvaluator { public static double evaluate(String input) { - final StringTokenizer tokens = new StringTokenizer(input, "+-*/()", true); - final Deque operators = new ArrayDeque<>(); - final Deque operands = new ArrayDeque<>(); + StringTokenizer tokens = new StringTokenizer(input, "+-*/()", true); + Deque operators = new ArrayDeque<>(); + Deque operands = new ArrayDeque<>(); String lastToken = null; while (tokens.hasMoreTokens()) { String rawToken = tokens.nextToken().trim(); if (rawToken.isEmpty()) continue; - if (isUnaryMinus(rawToken, lastToken)) { - rawToken = "u"; - } else if (isImplicitMultiply(rawToken, lastToken)) { - lastToken = processToken(input, "*", operators, operands); + + boolean isUnaryMinus = rawToken.equals("-") && (lastToken == null + || (!lastToken.equals("u") && isOperator(lastToken)) || lastToken.equals("(")); + final String token = isUnaryMinus ? "u" : rawToken; + if (isOperator(token)) { + popOperators(input, operators, operands, + top -> (!top.equals("(") && getPrecedence(token) <= getPrecedence(top))); + operators.push(token); + } else if (token.equals("(")) { + operators.push(token); + } else if (token.equals(")")) { + popOperators(input, operators, operands, top -> !top.equals("(")); + if (!operators.pop().equals("(")) { + throw new IllegalArgumentException("Unbalanced right parentheses in expression: " + input); + } + } else { + try { + operands.push(Double.parseDouble(token)); + } catch (NumberFormatException exception) { + throw new IllegalArgumentException("Could not evaluate expression: " + input); + } } - lastToken = processToken(input, rawToken, operators, operands); + lastToken = token; } popOperators(input, operators, operands, top -> true); return operands.pop(); } - private static boolean isUnaryMinus(String rawToken, @Nullable String lastToken) { - return rawToken.equals("-") - && (lastToken == null || (isOperator(lastToken) && !lastToken.equals("u")) || lastToken.equals("(")); - } - - private static boolean isImplicitMultiply(String rawToken, @Nullable String lastToken) { - return rawToken.equals("(") - && (lastToken != null && !isOperator(lastToken) && !lastToken.equals("(") && !lastToken.equals(")")); - } - - private static String processToken(String input, String token, Deque operators, Deque operands) { - if (isOperator(token)) { - popOperators(input, operators, operands, - top -> (!top.equals("(") && getPrecedence(token) <= getPrecedence(top))); - operators.push(token); - } else if (token.equals("(")) { - operators.push(token); - } else if (token.equals(")")) { - popOperators(input, operators, operands, top -> !top.equals("(")); - if (!operators.pop().equals("(")) { - throw new IllegalArgumentException("Unbalanced right parentheses in expression: " + input); - } - } else { - try { - operands.push(Double.parseDouble(token)); - } catch (NumberFormatException exception) { - throw new IllegalArgumentException("Could not parse double from:" + token); - } - } - return token; - } - private static void popOperators(String input, Deque operators, Deque operands, Predicate predicate) { try { while (!operators.isEmpty() && predicate.test(operators.peek())) { - final String op = operators.pop(); + String op = operators.pop(); if (op.equals("u")) { operands.push(-operands.pop()); continue; @@ -96,8 +79,8 @@ public class EquationEvaluator { throw new IllegalArgumentException("Unexpected arithmetic operation " + op); } - private static boolean isOperator(String token) { - switch (token) { + private static boolean isOperator(String op) { + switch (op) { case "+": case "-": case "*": diff --git a/src/test/java/com/refinedmods/refinedstorage/util/EquationEvaluatorTest.java b/src/test/java/com/refinedmods/refinedstorage/util/EquationEvaluatorTest.java index 4e6c6b538..67f9f2295 100644 --- a/src/test/java/com/refinedmods/refinedstorage/util/EquationEvaluatorTest.java +++ b/src/test/java/com/refinedmods/refinedstorage/util/EquationEvaluatorTest.java @@ -61,17 +61,4 @@ class EquationEvaluatorTest { assertFuzzyEquals(1, EquationEvaluator.evaluate("1 - 2 + 3 * 4 / 6")); assertFuzzyEquals(4.0 / 3, EquationEvaluator.evaluate("(((1 - 2) + 3) * 4) / 6")); } - - @Test - void testDivideByZero() { - assertFuzzyEquals(Double.POSITIVE_INFINITY, EquationEvaluator.evaluate("1 / 0")); - assertFuzzyEquals(Double.POSITIVE_INFINITY, EquationEvaluator.evaluate("1 / (1 - 1)")); - } - - @Test - void testImplicitMultiply() { - assertFuzzyEquals(-2, EquationEvaluator.evaluate("2(3 + 6) - 5(6 - 2)")); - assertFuzzyEquals(1, EquationEvaluator.evaluate("6 / (2(4 - 1))")); - assertFuzzyEquals(6 * 5 * 4 * 3 * 2, EquationEvaluator.evaluate("6(5(4(3(2(1)))))")); - } }