diff options
Diffstat (limited to 'src/main/java/org/unitConverter/math/ExpressionParser.java')
-rw-r--r-- | src/main/java/org/unitConverter/math/ExpressionParser.java | 537 |
1 files changed, 282 insertions, 255 deletions
diff --git a/src/main/java/org/unitConverter/math/ExpressionParser.java b/src/main/java/org/unitConverter/math/ExpressionParser.java index 8a0e97d..deee51d 100644 --- a/src/main/java/org/unitConverter/math/ExpressionParser.java +++ b/src/main/java/org/unitConverter/math/ExpressionParser.java @@ -32,8 +32,7 @@ import java.util.function.UnaryOperator; * An object that can parse expressions with unary or binary operators. * * @author Adrien Hopkins - * @param <T> - * type of object that exists in parsed expressions + * @param <T> type of object that exists in parsed expressions * @since 2019-03-14 * @since v0.2.0 */ @@ -42,21 +41,21 @@ public final class ExpressionParser<T> { * A builder that can create {@code ExpressionParser<T>} instances. * * @author Adrien Hopkins - * @param <T> - * type of object that exists in parsed expressions + * @param <T> type of object that exists in parsed expressions * @since 2019-03-17 * @since v0.2.0 */ public static final class Builder<T> { /** - * A function that obtains a parseable object from a string. For example, an integer {@code ExpressionParser} - * would use {@code Integer::parseInt}. + * A function that obtains a parseable object from a string. For example, + * an integer {@code ExpressionParser} would use + * {@code Integer::parseInt}. * * @since 2019-03-14 * @since v0.2.0 */ private final Function<String, ? extends T> objectObtainer; - + /** * The function of the space as an operator (like 3 x y) * @@ -64,110 +63,115 @@ public final class ExpressionParser<T> { * @since v0.2.0 */ private String spaceFunction = null; - + /** - * A map mapping operator strings to operator functions, for unary operators. + * A map mapping operator strings to operator functions, for unary + * operators. * * @since 2019-03-14 * @since v0.2.0 */ private final Map<String, PriorityUnaryOperator<T>> unaryOperators; - + /** - * A map mapping operator strings to operator functions, for binary operators. + * A map mapping operator strings to operator functions, for binary + * operators. * * @since 2019-03-14 * @since v0.2.0 */ private final Map<String, PriorityBinaryOperator<T>> binaryOperators; - + /** * Creates the {@code Builder}. * - * @param objectObtainer - * a function that can turn strings into objects of the type handled by the parser. - * @throws NullPointerException - * if {@code objectObtainer} is null + * @param objectObtainer a function that can turn strings into objects of + * the type handled by the parser. + * @throws NullPointerException if {@code objectObtainer} is null * @since 2019-03-17 * @since v0.2.0 */ public Builder(final Function<String, ? extends T> objectObtainer) { - this.objectObtainer = Objects.requireNonNull(objectObtainer, "objectObtainer must not be null."); + this.objectObtainer = Objects.requireNonNull(objectObtainer, + "objectObtainer must not be null."); this.unaryOperators = new HashMap<>(); this.binaryOperators = new HashMap<>(); } - + /** * Adds a binary operator to the builder. * - * @param text - * text used to reference the operator, like '+' - * @param operator - * operator to add - * @param priority - * operator's priority, which determines which operators are applied first + * @param text text used to reference the operator, like '+' + * @param operator operator to add + * @param priority operator's priority, which determines which operators + * are applied first * @return this builder - * @throws NullPointerException - * if {@code text} or {@code operator} is null + * @throws NullPointerException if {@code text} or {@code operator} is + * null * @since 2019-03-17 * @since v0.2.0 */ - public Builder<T> addBinaryOperator(final String text, final BinaryOperator<T> operator, final int priority) { + public Builder<T> addBinaryOperator(final String text, + final BinaryOperator<T> operator, final int priority) { Objects.requireNonNull(text, "text must not be null."); Objects.requireNonNull(operator, "operator must not be null."); - - // Unfortunately, I cannot use a lambda because the PriorityBinaryOperator requires arguments. - final PriorityBinaryOperator<T> priorityOperator = new PriorityBinaryOperator<T>(priority) { + + // Unfortunately, I cannot use a lambda because the + // PriorityBinaryOperator requires arguments. + final PriorityBinaryOperator<T> priorityOperator = new PriorityBinaryOperator<>( + priority) { @Override public T apply(final T t, final T u) { return operator.apply(t, u); } - + }; this.binaryOperators.put(text, priorityOperator); return this; } - + /** - * Adds a function for spaces. You must use the text of an existing binary operator. + * Adds a function for spaces. You must use the text of an existing binary + * operator. * - * @param operator - * text of operator to use + * @param operator text of operator to use * @return this builder * @since 2019-03-22 * @since v0.2.0 */ public Builder<T> addSpaceFunction(final String operator) { Objects.requireNonNull(operator, "operator must not be null."); - + if (!this.binaryOperators.containsKey(operator)) - throw new IllegalArgumentException(String.format("Could not find binary operator '%s'", operator)); - + throw new IllegalArgumentException(String + .format("Could not find binary operator '%s'", operator)); + this.spaceFunction = operator; return this; } - + /** * Adds a unary operator to the builder. * - * @param text - * text used to reference the operator, like '-' - * @param operator - * operator to add - * @param priority - * operator's priority, which determines which operators are applied first + * @param text text used to reference the operator, like '-' + * @param operator operator to add + * @param priority operator's priority, which determines which operators + * are applied first * @return this builder - * @throws NullPointerException - * if {@code text} or {@code operator} is null + * @throws NullPointerException if {@code text} or {@code operator} is + * null * @since 2019-03-17 * @since v0.2.0 */ - public Builder<T> addUnaryOperator(final String text, final UnaryOperator<T> operator, final int priority) { + public Builder<T> addUnaryOperator(final String text, + final UnaryOperator<T> operator, final int priority) { Objects.requireNonNull(text, "text must not be null."); Objects.requireNonNull(operator, "operator must not be null."); - - // Unfortunately, I cannot use a lambda because the PriorityUnaryOperator requires arguments. - final PriorityUnaryOperator<T> priorityOperator = new PriorityUnaryOperator<T>(priority) { + + // Unfortunately, I cannot use a lambda because the + // PriorityUnaryOperator requires arguments. + final PriorityUnaryOperator<T> priorityOperator = new PriorityUnaryOperator<>( + priority) { @Override public T apply(final T t) { return operator.apply(t); @@ -176,49 +180,50 @@ public final class ExpressionParser<T> { this.unaryOperators.put(text, priorityOperator); return this; } - + /** - * @return an {@code ExpressionParser<T>} instance with the properties given to this builder + * @return an {@code ExpressionParser<T>} instance with the properties + * given to this builder * @since 2019-03-17 * @since v0.2.0 */ public ExpressionParser<T> build() { - return new ExpressionParser<>(this.objectObtainer, this.unaryOperators, this.binaryOperators, - this.spaceFunction); + return new ExpressionParser<>(this.objectObtainer, this.unaryOperators, + this.binaryOperators, this.spaceFunction); } } - + /** - * A binary operator with a priority field that determines which operators apply first. + * A binary operator with a priority field that determines which operators + * apply first. * * @author Adrien Hopkins - * @param <T> - * type of operand and result + * @param <T> type of operand and result * @since 2019-03-17 * @since v0.2.0 */ private static abstract class PriorityBinaryOperator<T> implements BinaryOperator<T>, Comparable<PriorityBinaryOperator<T>> { /** - * The operator's priority. Higher-priority operators are applied before lower-priority operators + * The operator's priority. Higher-priority operators are applied before + * lower-priority operators * * @since 2019-03-17 * @since v0.2.0 */ private final int priority; - + /** * Creates the {@code PriorityBinaryOperator}. * - * @param priority - * operator's priority + * @param priority operator's priority * @since 2019-03-17 * @since v0.2.0 */ public PriorityBinaryOperator(final int priority) { this.priority = priority; } - + /** * Compares this object to another by priority. * @@ -238,7 +243,7 @@ public final class ExpressionParser<T> { else return 0; } - + /** * @return priority * @since 2019-03-22 @@ -248,38 +253,38 @@ public final class ExpressionParser<T> { return this.priority; } } - + /** - * A unary operator with a priority field that determines which operators apply first. + * A unary operator with a priority field that determines which operators + * apply first. * * @author Adrien Hopkins - * @param <T> - * type of operand and result + * @param <T> type of operand and result * @since 2019-03-17 * @since v0.2.0 */ private static abstract class PriorityUnaryOperator<T> implements UnaryOperator<T>, Comparable<PriorityUnaryOperator<T>> { /** - * The operator's priority. Higher-priority operators are applied before lower-priority operators + * The operator's priority. Higher-priority operators are applied before + * lower-priority operators * * @since 2019-03-17 * @since v0.2.0 */ private final int priority; - + /** * Creates the {@code PriorityUnaryOperator}. * - * @param priority - * operator's priority + * @param priority operator's priority * @since 2019-03-17 * @since v0.2.0 */ public PriorityUnaryOperator(final int priority) { this.priority = priority; } - + /** * Compares this object to another by priority. * @@ -299,7 +304,7 @@ public final class ExpressionParser<T> { else return 0; } - + /** * @return priority * @since 2019-03-22 @@ -309,7 +314,7 @@ public final class ExpressionParser<T> { return this.priority; } } - + /** * The types of tokens that are available. * @@ -320,7 +325,7 @@ public final class ExpressionParser<T> { private static enum TokenType { OBJECT, UNARY_OPERATOR, BINARY_OPERATOR; } - + /** * The opening bracket. * @@ -328,7 +333,7 @@ public final class ExpressionParser<T> { * @since v0.2.0 */ public static final char OPENING_BRACKET = '('; - + /** * The closing bracket. * @@ -336,48 +341,49 @@ public final class ExpressionParser<T> { * @since v0.2.0 */ public static final char CLOSING_BRACKET = ')'; - + /** * Finds the other bracket in a pair of brackets, given the position of one. * - * @param string - * string that contains brackets - * @param bracketPosition - * position of first bracket + * @param string string that contains brackets + * @param bracketPosition position of first bracket * @return position of matching bracket - * @throws NullPointerException - * if string is null + * @throws NullPointerException if string is null * @since 2019-03-22 * @since v0.2.0 */ - private static int findBracketPair(final String string, final int bracketPosition) { + private static int findBracketPair(final String string, + final int bracketPosition) { Objects.requireNonNull(string, "string must not be null."); - + final char openingBracket = string.charAt(bracketPosition); - + // figure out what closing bracket to look for final char closingBracket; switch (openingBracket) { - case '(': - closingBracket = ')'; - break; - case '[': - closingBracket = ']'; - break; - case '{': - closingBracket = '}'; - break; - default: - throw new IllegalArgumentException(String.format("Invalid bracket '%s'", openingBracket)); + case '(': + closingBracket = ')'; + break; + case '[': + closingBracket = ']'; + break; + case '{': + closingBracket = '}'; + break; + default: + throw new IllegalArgumentException( + String.format("Invalid bracket '%s'", openingBracket)); } - - // level of brackets. every opening bracket increments this; every closing bracket decrements it + + // level of brackets. every opening bracket increments this; every closing + // bracket decrements it int bracketLevel = 0; - + // iterate over the string to find the closing bracket - for (int currentPosition = bracketPosition; currentPosition < string.length(); currentPosition++) { + for (int currentPosition = bracketPosition; currentPosition < string + .length(); currentPosition++) { final char currentCharacter = string.charAt(currentPosition); - + if (currentCharacter == openingBracket) { bracketLevel++; } else if (currentCharacter == closingBracket) { @@ -386,19 +392,19 @@ public final class ExpressionParser<T> { return currentPosition; } } - + throw new IllegalArgumentException("No matching bracket found."); } - + /** - * A function that obtains a parseable object from a string. For example, an integer {@code ExpressionParser} would - * use {@code Integer::parseInt}. + * A function that obtains a parseable object from a string. For example, an + * integer {@code ExpressionParser} would use {@code Integer::parseInt}. * * @since 2019-03-14 * @since v0.2.0 */ private final Function<String, ? extends T> objectObtainer; - + /** * A map mapping operator strings to operator functions, for unary operators. * @@ -406,15 +412,16 @@ public final class ExpressionParser<T> { * @since v0.2.0 */ private final Map<String, PriorityUnaryOperator<T>> unaryOperators; - + /** - * A map mapping operator strings to operator functions, for binary operators. + * A map mapping operator strings to operator functions, for binary + * operators. * * @since 2019-03-14 * @since v0.2.0 */ private final Map<String, PriorityBinaryOperator<T>> binaryOperators; - + /** * The operator for space, or null if spaces have no function. * @@ -422,127 +429,144 @@ public final class ExpressionParser<T> { * @since v0.2.0 */ private final String spaceOperator; - + /** * Creates the {@code ExpressionParser}. * - * @param objectObtainer - * function to get objects from strings - * @param unaryOperators - * unary operators available to the parser - * @param binaryOperators - * binary operators available to the parser - * @param spaceOperator - * operator used by spaces + * @param objectObtainer function to get objects from strings + * @param unaryOperators unary operators available to the parser + * @param binaryOperators binary operators available to the parser + * @param spaceOperator operator used by spaces * @since 2019-03-14 * @since v0.2.0 */ private ExpressionParser(final Function<String, ? extends T> objectObtainer, final Map<String, PriorityUnaryOperator<T>> unaryOperators, - final Map<String, PriorityBinaryOperator<T>> binaryOperators, final String spaceOperator) { + final Map<String, PriorityBinaryOperator<T>> binaryOperators, + final String spaceOperator) { this.objectObtainer = objectObtainer; this.unaryOperators = unaryOperators; this.binaryOperators = binaryOperators; this.spaceOperator = spaceOperator; } - + /** - * Converts a given mathematical expression to reverse Polish notation (operators after operands). + * Converts a given mathematical expression to reverse Polish notation + * (operators after operands). * <p> * For example,<br> * {@code 2 * (3 + 4)}<br> * becomes<br> * {@code 2 3 4 + *}. * - * @param expression - * expression + * @param expression expression * @return expression in RPN * @since 2019-03-17 * @since v0.2.0 */ private String convertExpressionToReversePolish(final String expression) { Objects.requireNonNull(expression, "expression must not be null."); - + final List<String> components = new ArrayList<>(); - + // the part of the expression remaining to parse String partialExpression = expression; - + // find and deal with brackets while (partialExpression.indexOf(OPENING_BRACKET) != -1) { - final int openingBracketPosition = partialExpression.indexOf(OPENING_BRACKET); - final int closingBracketPosition = findBracketPair(partialExpression, openingBracketPosition); - + final int openingBracketPosition = partialExpression + .indexOf(OPENING_BRACKET); + final int closingBracketPosition = findBracketPair(partialExpression, + openingBracketPosition); + // check for function - if (openingBracketPosition > 0 && partialExpression.charAt(openingBracketPosition - 1) != ' ') { + if (openingBracketPosition > 0 + && partialExpression.charAt(openingBracketPosition - 1) != ' ') { // function like sin(2) or tempF(32) // find the position of the last space int spacePosition = openingBracketPosition; - while (spacePosition >= 0 && partialExpression.charAt(spacePosition) != ' ') { + while (spacePosition >= 0 + && partialExpression.charAt(spacePosition) != ' ') { spacePosition--; } - // then split the function into pre-function and function, using the space position - components.addAll(Arrays.asList(partialExpression.substring(0, spacePosition + 1).split(" "))); - components.add(partialExpression.substring(spacePosition + 1, closingBracketPosition + 1)); - partialExpression = partialExpression.substring(closingBracketPosition + 1); + // then split the function into pre-function and function, using the + // space position + components.addAll(Arrays.asList(partialExpression + .substring(0, spacePosition + 1).split(" "))); + components.add(partialExpression.substring(spacePosition + 1, + closingBracketPosition + 1)); + partialExpression = partialExpression + .substring(closingBracketPosition + 1); } else { // normal brackets like (1 + 2) * (3 / 5) - components.addAll(Arrays.asList(partialExpression.substring(0, openingBracketPosition).split(" "))); + components.addAll(Arrays.asList(partialExpression + .substring(0, openingBracketPosition).split(" "))); components.add(this.convertExpressionToReversePolish( - partialExpression.substring(openingBracketPosition + 1, closingBracketPosition))); - partialExpression = partialExpression.substring(closingBracketPosition + 1); + partialExpression.substring(openingBracketPosition + 1, + closingBracketPosition))); + partialExpression = partialExpression + .substring(closingBracketPosition + 1); } } - + // add everything else components.addAll(Arrays.asList(partialExpression.split(" "))); - + // remove empty entries while (components.contains("")) { components.remove(""); } - + // deal with space multiplication (x y) if (this.spaceOperator != null) { for (int i = 0; i < components.size() - 1; i++) { - if (this.getTokenType(components.get(i)) == TokenType.OBJECT - && this.getTokenType(components.get(i + 1)) == TokenType.OBJECT) { + if (this.getTokenType(components.get(i)) == TokenType.OBJECT && this + .getTokenType(components.get(i + 1)) == TokenType.OBJECT) { components.add(++i, this.spaceOperator); } } } - + // turn the expression into reverse Polish while (true) { - final int highestPriorityOperatorPosition = this.findHighestPriorityOperatorPosition(components); + final int highestPriorityOperatorPosition = this + .findHighestPriorityOperatorPosition(components); if (highestPriorityOperatorPosition == -1) { break; } - + // swap components based on what kind of operator there is // 1 + 2 becomes 2 1 + // - 1 becomes 1 - - switch (this.getTokenType(components.get(highestPriorityOperatorPosition))) { - case UNARY_OPERATOR: - final String unaryOperator = components.remove(highestPriorityOperatorPosition); - final String operand = components.remove(highestPriorityOperatorPosition); - components.add(highestPriorityOperatorPosition, operand + " " + unaryOperator); - break; - case BINARY_OPERATOR: - final String binaryOperator = components.remove(highestPriorityOperatorPosition); - final String operand1 = components.remove(highestPriorityOperatorPosition - 1); - final String operand2 = components.remove(highestPriorityOperatorPosition - 1); - components.add(highestPriorityOperatorPosition - 1, - operand2 + " " + operand1 + " " + binaryOperator); - break; - default: - throw new AssertionError("Expected operator, found non-operator."); + switch (this + .getTokenType(components.get(highestPriorityOperatorPosition))) { + case UNARY_OPERATOR: + final String unaryOperator = components + .remove(highestPriorityOperatorPosition); + final String operand = components + .remove(highestPriorityOperatorPosition); + components.add(highestPriorityOperatorPosition, + operand + " " + unaryOperator); + break; + case BINARY_OPERATOR: + final String binaryOperator = components + .remove(highestPriorityOperatorPosition); + final String operand1 = components + .remove(highestPriorityOperatorPosition - 1); + final String operand2 = components + .remove(highestPriorityOperatorPosition - 1); + components.add(highestPriorityOperatorPosition - 1, + operand2 + " " + operand1 + " " + binaryOperator); + break; + default: + throw new AssertionError("Expected operator, found non-operator."); } } - - // join all of the components together, then ensure there is only one space in a row + + // join all of the components together, then ensure there is only one + // space in a row String expressionRPN = String.join(" ", components).replaceAll(" +", " "); - + while (expressionRPN.charAt(0) == ' ') { expressionRPN = expressionRPN.substring(1); } @@ -551,73 +575,72 @@ public final class ExpressionParser<T> { } return expressionRPN; } - + /** * Finds the position of the highest-priority operator in a list * - * @param components - * components to test - * @param blacklist - * positions of operators that should be ignored - * @return position of highest priority, or -1 if the list contains no operators - * @throws NullPointerException - * if components is null + * @param components components to test + * @param blacklist positions of operators that should be ignored + * @return position of highest priority, or -1 if the list contains no + * operators + * @throws NullPointerException if components is null * @since 2019-03-22 * @since v0.2.0 */ - private int findHighestPriorityOperatorPosition(final List<String> components) { + private int findHighestPriorityOperatorPosition( + final List<String> components) { Objects.requireNonNull(components, "components must not be null."); // find highest priority int maxPriority = Integer.MIN_VALUE; int maxPriorityPosition = -1; - + // go over components one by one // if it is an operator, test its priority to see if it's max // if it is, update maxPriority and maxPriorityPosition for (int i = 0; i < components.size(); i++) { - + switch (this.getTokenType(components.get(i))) { - case UNARY_OPERATOR: - final PriorityUnaryOperator<T> unaryOperator = this.unaryOperators.get(components.get(i)); - final int unaryPriority = unaryOperator.getPriority(); - - if (unaryPriority > maxPriority) { - maxPriority = unaryPriority; - maxPriorityPosition = i; - } - break; - case BINARY_OPERATOR: - final PriorityBinaryOperator<T> binaryOperator = this.binaryOperators.get(components.get(i)); - final int binaryPriority = binaryOperator.getPriority(); - - if (binaryPriority > maxPriority) { - maxPriority = binaryPriority; - maxPriorityPosition = i; - } - break; - default: - break; + case UNARY_OPERATOR: + final PriorityUnaryOperator<T> unaryOperator = this.unaryOperators + .get(components.get(i)); + final int unaryPriority = unaryOperator.getPriority(); + + if (unaryPriority > maxPriority) { + maxPriority = unaryPriority; + maxPriorityPosition = i; + } + break; + case BINARY_OPERATOR: + final PriorityBinaryOperator<T> binaryOperator = this.binaryOperators + .get(components.get(i)); + final int binaryPriority = binaryOperator.getPriority(); + + if (binaryPriority > maxPriority) { + maxPriority = binaryPriority; + maxPriorityPosition = i; + } + break; + default: + break; } } - + // max priority position found return maxPriorityPosition; } - + /** * Determines whether an inputted string is an object or an operator * - * @param token - * string to input + * @param token string to input * @return type of token it is - * @throws NullPointerException - * if {@code expression} is null + * @throws NullPointerException if {@code expression} is null * @since 2019-03-14 * @since v0.2.0 */ private TokenType getTokenType(final String token) { Objects.requireNonNull(token, "token must not be null."); - + if (this.unaryOperators.containsKey(token)) return TokenType.UNARY_OPERATOR; else if (this.binaryOperators.containsKey(token)) @@ -625,84 +648,88 @@ public final class ExpressionParser<T> { else return TokenType.OBJECT; } - + /** * Parses an expression. * - * @param expression - * expression to parse + * @param expression expression to parse * @return result - * @throws NullPointerException - * if {@code expression} is null + * @throws NullPointerException if {@code expression} is null * @since 2019-03-14 * @since v0.2.0 */ public T parseExpression(final String expression) { - return this.parseReversePolishExpression(this.convertExpressionToReversePolish(expression)); + return this.parseReversePolishExpression( + this.convertExpressionToReversePolish(expression)); } - + /** * Parses an expression expressed in reverse Polish notation. * - * @param expression - * expression to parse + * @param expression expression to parse * @return result - * @throws NullPointerException - * if {@code expression} is null + * @throws NullPointerException if {@code expression} is null * @since 2019-03-14 * @since v0.2.0 */ private T parseReversePolishExpression(final String expression) { Objects.requireNonNull(expression, "expression must not be null."); - + final Deque<T> stack = new ArrayDeque<>(); - + // iterate over every item in the expression, then for (final String item : expression.split(" ")) { // choose a path based on what kind of thing was just read switch (this.getTokenType(item)) { - - case BINARY_OPERATOR: - if (stack.size() < 2) - throw new IllegalStateException(String.format( - "Attempted to call binary operator %s with only %d arguments.", item, stack.size())); - - // get two arguments and operator, then apply! - final T o1 = stack.pop(); - final T o2 = stack.pop(); - final BinaryOperator<T> binaryOperator = this.binaryOperators.get(item); - - stack.push(binaryOperator.apply(o1, o2)); - break; - - case OBJECT: - // just add it to the stack - stack.push(this.objectObtainer.apply(item)); - break; - - case UNARY_OPERATOR: - if (stack.size() < 1) - throw new IllegalStateException(String.format( - "Attempted to call unary operator %s with only %d arguments.", item, stack.size())); - - // get one argument and operator, then apply! - final T o = stack.pop(); - final UnaryOperator<T> unaryOperator = this.unaryOperators.get(item); - - stack.push(unaryOperator.apply(o)); - break; - default: - throw new AssertionError( - String.format("Internal error: Invalid token type %s.", this.getTokenType(item))); - + + case BINARY_OPERATOR: + if (stack.size() < 2) + throw new IllegalStateException(String.format( + "Attempted to call binary operator %s with only %d arguments.", + item, stack.size())); + + // get two arguments and operator, then apply! + final T o1 = stack.pop(); + final T o2 = stack.pop(); + final BinaryOperator<T> binaryOperator = this.binaryOperators + .get(item); + + stack.push(binaryOperator.apply(o1, o2)); + break; + + case OBJECT: + // just add it to the stack + stack.push(this.objectObtainer.apply(item)); + break; + + case UNARY_OPERATOR: + if (stack.size() < 1) + throw new IllegalStateException(String.format( + "Attempted to call unary operator %s with only %d arguments.", + item, stack.size())); + + // get one argument and operator, then apply! + final T o = stack.pop(); + final UnaryOperator<T> unaryOperator = this.unaryOperators + .get(item); + + stack.push(unaryOperator.apply(o)); + break; + default: + throw new AssertionError( + String.format("Internal error: Invalid token type %s.", + this.getTokenType(item))); + } } - + // return answer, or throw an exception if I can't if (stack.size() > 1) - throw new IllegalStateException("Computation ended up with more than one answer."); + throw new IllegalStateException( + "Computation ended up with more than one answer."); else if (stack.size() == 0) - throw new IllegalStateException("Computation ended up without an answer."); + throw new IllegalStateException( + "Computation ended up without an answer."); return stack.pop(); } } |