summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/main/java/sevenUnits/utils/ExpressionParser.java188
1 files changed, 93 insertions, 95 deletions
diff --git a/src/main/java/sevenUnits/utils/ExpressionParser.java b/src/main/java/sevenUnits/utils/ExpressionParser.java
index 051082d..783a135 100644
--- a/src/main/java/sevenUnits/utils/ExpressionParser.java
+++ b/src/main/java/sevenUnits/utils/ExpressionParser.java
@@ -56,7 +56,7 @@ public final class ExpressionParser<T> {
* @since v0.2.0
*/
private final Function<String, ? extends T> objectObtainer;
-
+
/**
* The function of the space as an operator (like 3 x y)
*
@@ -64,7 +64,7 @@ public final class ExpressionParser<T> {
* @since v0.2.0
*/
private String spaceFunction = null;
-
+
/**
* A map mapping operator strings to operator functions, for unary
* operators.
@@ -73,7 +73,7 @@ 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.
@@ -82,7 +82,7 @@ public final class ExpressionParser<T> {
* @since v0.2.0
*/
private final Map<String, PriorityBinaryOperator<T>> binaryOperators;
-
+
/**
* A map mapping operator strings to numeric functions.
*
@@ -90,7 +90,7 @@ public final class ExpressionParser<T> {
* @since v0.5.0
*/
private final Map<String, PriorityBiFunction<T, UncertainDouble, T>> numericOperators;
-
+
/**
* Creates the {@code Builder}.
*
@@ -107,7 +107,7 @@ public final class ExpressionParser<T> {
this.binaryOperators = new HashMap<>();
this.numericOperators = new HashMap<>();
}
-
+
/**
* Adds a binary operator to the builder.
*
@@ -125,7 +125,7 @@ public final class ExpressionParser<T> {
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<>(
@@ -134,12 +134,12 @@ public final class ExpressionParser<T> {
public T apply(final T t, final T u) {
return operator.apply(t, u);
}
-
+
};
this.binaryOperators.put(text, priorityOperator);
return this;
}
-
+
/**
* Adds a two-argument operator where the second operator is a number.
* This is used for operations like vector scaling and exponentation.
@@ -155,7 +155,7 @@ public final class ExpressionParser<T> {
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 PriorityBiFunction<T, UncertainDouble, T> priorityOperator = new PriorityBiFunction<>(
@@ -164,12 +164,12 @@ public final class ExpressionParser<T> {
public T apply(final T t, final UncertainDouble u) {
return operator.apply(t, u);
}
-
+
};
this.numericOperators.put(text, priorityOperator);
return this;
}
-
+
/**
* Adds a function for spaces. You must use the text of an existing binary
* operator.
@@ -181,15 +181,15 @@ public final class ExpressionParser<T> {
*/
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));
-
+
this.spaceFunction = operator;
return this;
}
-
+
/**
* Adds a unary operator to the builder.
*
@@ -207,7 +207,7 @@ public final class ExpressionParser<T> {
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<>(
@@ -220,7 +220,7 @@ 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
@@ -232,7 +232,7 @@ public final class ExpressionParser<T> {
this.binaryOperators, this.numericOperators, this.spaceFunction);
}
}
-
+
/**
* A binary operator with a priority field that determines which operators
* apply first.
@@ -242,8 +242,8 @@ public final class ExpressionParser<T> {
* @since 2019-03-17
* @since v0.2.0
*/
- private static abstract class PriorityBinaryOperator<T>
- implements BinaryOperator<T>, Comparable<PriorityBinaryOperator<T>> {
+ private static abstract class PriorityBiFunction<T, U, R> implements
+ BiFunction<T, U, R>, Comparable<PriorityBiFunction<T, U, R>> {
/**
* The operator's priority. Higher-priority operators are applied before
* lower-priority operators
@@ -252,7 +252,7 @@ public final class ExpressionParser<T> {
* @since v0.2.0
*/
private final int priority;
-
+
/**
* Creates the {@code PriorityBinaryOperator}.
*
@@ -260,10 +260,10 @@ public final class ExpressionParser<T> {
* @since 2019-03-17
* @since v0.2.0
*/
- public PriorityBinaryOperator(final int priority) {
+ public PriorityBiFunction(final int priority) {
this.priority = priority;
}
-
+
/**
* Compares this object to another by priority.
*
@@ -275,7 +275,7 @@ public final class ExpressionParser<T> {
* @since v0.2.0
*/
@Override
- public int compareTo(final PriorityBinaryOperator<T> o) {
+ public int compareTo(final PriorityBiFunction<T, U, R> o) {
if (this.priority < o.priority)
return -1;
else if (this.priority > o.priority)
@@ -283,7 +283,7 @@ public final class ExpressionParser<T> {
else
return 0;
}
-
+
/**
* @return priority
* @since 2019-03-22
@@ -293,7 +293,7 @@ public final class ExpressionParser<T> {
return this.priority;
}
}
-
+
/**
* A binary operator with a priority field that determines which operators
* apply first.
@@ -303,8 +303,8 @@ public final class ExpressionParser<T> {
* @since 2019-03-17
* @since v0.2.0
*/
- private static abstract class PriorityBiFunction<T, U, R> implements
- BiFunction<T, U, R>, Comparable<PriorityBiFunction<T, U, R>> {
+ 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
@@ -313,7 +313,7 @@ public final class ExpressionParser<T> {
* @since v0.2.0
*/
private final int priority;
-
+
/**
* Creates the {@code PriorityBinaryOperator}.
*
@@ -321,10 +321,10 @@ public final class ExpressionParser<T> {
* @since 2019-03-17
* @since v0.2.0
*/
- public PriorityBiFunction(final int priority) {
+ public PriorityBinaryOperator(final int priority) {
this.priority = priority;
}
-
+
/**
* Compares this object to another by priority.
*
@@ -336,7 +336,7 @@ public final class ExpressionParser<T> {
* @since v0.2.0
*/
@Override
- public int compareTo(final PriorityBiFunction<T, U, R> o) {
+ public int compareTo(final PriorityBinaryOperator<T> o) {
if (this.priority < o.priority)
return -1;
else if (this.priority > o.priority)
@@ -344,7 +344,7 @@ public final class ExpressionParser<T> {
else
return 0;
}
-
+
/**
* @return priority
* @since 2019-03-22
@@ -354,7 +354,7 @@ public final class ExpressionParser<T> {
return this.priority;
}
}
-
+
/**
* A unary operator with a priority field that determines which operators
* apply first.
@@ -374,7 +374,7 @@ public final class ExpressionParser<T> {
* @since v0.2.0
*/
private final int priority;
-
+
/**
* Creates the {@code PriorityUnaryOperator}.
*
@@ -385,7 +385,7 @@ public final class ExpressionParser<T> {
public PriorityUnaryOperator(final int priority) {
this.priority = priority;
}
-
+
/**
* Compares this object to another by priority.
*
@@ -405,7 +405,7 @@ public final class ExpressionParser<T> {
else
return 0;
}
-
+
/**
* @return priority
* @since 2019-03-22
@@ -415,7 +415,7 @@ public final class ExpressionParser<T> {
return this.priority;
}
}
-
+
/**
* The types of tokens that are available.
*
@@ -426,7 +426,7 @@ public final class ExpressionParser<T> {
private static enum TokenType {
OBJECT, UNARY_OPERATOR, BINARY_OPERATOR, NUMERIC_OPERATOR;
}
-
+
/**
* The opening bracket.
*
@@ -434,7 +434,7 @@ public final class ExpressionParser<T> {
* @since v0.2.0
*/
public static final char OPENING_BRACKET = '(';
-
+
/**
* The closing bracket.
*
@@ -442,7 +442,7 @@ 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.
*
@@ -456,9 +456,9 @@ public final class ExpressionParser<T> {
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) {
@@ -475,16 +475,16 @@ public final class ExpressionParser<T> {
throw new IllegalArgumentException(
String.format("Invalid bracket '%s'", openingBracket));
}
-
+
// 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++) {
final char currentCharacter = string.charAt(currentPosition);
-
+
if (currentCharacter == openingBracket) {
bracketLevel++;
} else if (currentCharacter == closingBracket) {
@@ -493,10 +493,10 @@ 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}.
@@ -505,7 +505,7 @@ public final class ExpressionParser<T> {
* @since v0.2.0
*/
private final Function<String, ? extends T> objectObtainer;
-
+
/**
* A map mapping operator strings to operator functions, for unary operators.
*
@@ -513,7 +513,7 @@ 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.
@@ -522,7 +522,7 @@ public final class ExpressionParser<T> {
* @since v0.2.0
*/
private final Map<String, PriorityBinaryOperator<T>> binaryOperators;
-
+
/**
* A map mapping operator strings to numeric functions.
*
@@ -530,7 +530,7 @@ public final class ExpressionParser<T> {
* @since v0.5.0
*/
private final Map<String, PriorityBiFunction<T, UncertainDouble, T>> numericOperators;
-
+
/**
* The operator for space, or null if spaces have no function.
*
@@ -538,7 +538,7 @@ public final class ExpressionParser<T> {
* @since v0.2.0
*/
private final String spaceOperator;
-
+
/**
* Creates the {@code ExpressionParser}.
*
@@ -561,7 +561,7 @@ public final class ExpressionParser<T> {
this.numericOperators = numericOperators;
this.spaceOperator = spaceOperator;
}
-
+
/**
* Converts a given mathematical expression to reverse Polish notation
* (operators after operands).
@@ -578,22 +578,21 @@ public final class ExpressionParser<T> {
* @since 2019-03-17
* @since v0.2.0
*/
- // TODO revert to package private
- public String convertExpressionToReversePolish(final String expression) {
+ 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);
-
+
// check for function
if (openingBracketPosition > 0
&& partialExpression.charAt(openingBracketPosition - 1) != ' ') {
@@ -623,15 +622,15 @@ public final class ExpressionParser<T> {
.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++) {
@@ -641,7 +640,7 @@ public final class ExpressionParser<T> {
}
}
}
-
+
// turn the expression into reverse Polish
while (true) {
final int highestPriorityOperatorPosition = this
@@ -649,7 +648,7 @@ public final class ExpressionParser<T> {
if (highestPriorityOperatorPosition == -1) {
break;
}
-
+
// swap components based on what kind of operator there is
// 1 + 2 becomes 2 1 +
// - 1 becomes 1 -
@@ -684,16 +683,15 @@ public final class ExpressionParser<T> {
throw new AssertionError("Expected operator, found non-operator.");
}
}
-
+
// join all of the components together, then ensure there is only one
// space in a row
- if (components.size() != 1) {
+ if (components.size() != 1)
throw new IllegalArgumentException(
"Invalid expression \"" + expression + "\".");
- }
return components.get(0).replaceAll(" +", " ").trim();
}
-
+
/**
* Finds the position of the highest-priority operator in a list
*
@@ -711,18 +709,18 @@ public final class ExpressionParser<T> {
// 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;
@@ -732,7 +730,7 @@ public final class ExpressionParser<T> {
final PriorityBinaryOperator<T> binaryOperator = this.binaryOperators
.get(components.get(i));
final int binaryPriority = binaryOperator.getPriority();
-
+
if (binaryPriority > maxPriority) {
maxPriority = binaryPriority;
maxPriorityPosition = i;
@@ -742,7 +740,7 @@ public final class ExpressionParser<T> {
final PriorityBiFunction<T, UncertainDouble, T> numericOperator = this.numericOperators
.get(components.get(i));
final int numericPriority = numericOperator.getPriority();
-
+
if (numericPriority > maxPriority) {
maxPriority = numericPriority;
maxPriorityPosition = i;
@@ -752,11 +750,11 @@ public final class ExpressionParser<T> {
break;
}
}
-
+
// max priority position found
return maxPriorityPosition;
}
-
+
/**
* Determines whether an inputted string is an object or an operator
*
@@ -768,7 +766,7 @@ public final class ExpressionParser<T> {
*/
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))
@@ -778,7 +776,7 @@ public final class ExpressionParser<T> {
else
return TokenType.OBJECT;
}
-
+
/**
* Parses an expression.
*
@@ -792,7 +790,7 @@ public final class ExpressionParser<T> {
return this.parseReversePolishExpression(
this.convertExpressionToReversePolish(expression));
}
-
+
/**
* Parses an expression expressed in reverse Polish notation.
*
@@ -804,43 +802,43 @@ public final class ExpressionParser<T> {
*/
T parseReversePolishExpression(final String expression) {
Objects.requireNonNull(expression, "expression must not be null.");
-
+
final Deque<T> stack = new ArrayDeque<>();
final Deque<UncertainDouble> doubleStack = 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(o2, o1));
break;
-
+
case NUMERIC_OPERATOR:
if (stack.size() < 1 || doubleStack.size() < 1)
throw new IllegalStateException(String.format(
"Attempted to call binary operator %s with insufficient arguments.",
item));
-
+
final T ot = stack.pop();
final UncertainDouble on = doubleStack.pop();
final BiFunction<T, UncertainDouble, T> op = this.numericOperators
.get(item);
stack.push(op.apply(ot, on));
break;
-
+
case OBJECT:
// just add it to the stack
// these try-catch statements are necessary
@@ -849,41 +847,41 @@ public final class ExpressionParser<T> {
// that's the only way to tell if an expression is a number or not.
try {
stack.push(this.objectObtainer.apply(item));
- } catch (Exception e) {
+ } catch (final Exception e) {
try {
doubleStack.push(UncertainDouble.fromString(item));
- } catch (IllegalArgumentException e2) {
+ } catch (final IllegalArgumentException e2) {
try {
doubleStack.push(
UncertainDouble.of(Double.parseDouble(item), 0));
- } catch (NumberFormatException e3) {
+ } catch (final NumberFormatException e3) {
throw e;
}
}
}
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(