summaryrefslogtreecommitdiff
path: root/src/main/java/org/unitConverter/math/ExpressionParser.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/org/unitConverter/math/ExpressionParser.java')
-rw-r--r--src/main/java/org/unitConverter/math/ExpressionParser.java537
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();
}
}