122 lines
3.8 KiB
Java
122 lines
3.8 KiB
Java
|
package world.bentobox.level.calculators;
|
||
|
|
||
|
import java.text.ParseException;
|
||
|
|
||
|
/**
|
||
|
* Utility class to evaluate equations
|
||
|
*/
|
||
|
public class EquationEvaluator {
|
||
|
|
||
|
private static class Parser {
|
||
|
private final String input;
|
||
|
private int pos = -1;
|
||
|
private int currentChar;
|
||
|
|
||
|
@SuppressWarnings("unused")
|
||
|
private Parser() {
|
||
|
throw new IllegalStateException("Utility class");
|
||
|
}
|
||
|
|
||
|
public Parser(String input) {
|
||
|
this.input = input;
|
||
|
moveToNextChar();
|
||
|
}
|
||
|
|
||
|
private void moveToNextChar() {
|
||
|
currentChar = (++pos < input.length()) ? input.charAt(pos) : -1;
|
||
|
}
|
||
|
|
||
|
private boolean tryToEat(int charToEat) {
|
||
|
while (currentChar == ' ') {
|
||
|
moveToNextChar();
|
||
|
}
|
||
|
if (currentChar == charToEat) {
|
||
|
moveToNextChar();
|
||
|
return true;
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
public double evaluate() throws ParseException {
|
||
|
double result = parseExpression();
|
||
|
if (pos < input.length()) {
|
||
|
throw new ParseException("Unexpected character: " + (char) currentChar, pos);
|
||
|
}
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
private double parseExpression() throws ParseException {
|
||
|
double result = parseTerm();
|
||
|
while (true) {
|
||
|
if (tryToEat('+')) {
|
||
|
result += parseTerm();
|
||
|
} else if (tryToEat('-')) {
|
||
|
result -= parseTerm();
|
||
|
} else {
|
||
|
return result;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private double parseFactor() throws ParseException {
|
||
|
if (tryToEat('+')) {
|
||
|
return parseFactor(); // unary plus
|
||
|
}
|
||
|
if (tryToEat('-')) {
|
||
|
return -parseFactor(); // unary minus
|
||
|
}
|
||
|
double x;
|
||
|
int startPos = this.pos;
|
||
|
if (tryToEat('(')) { // parentheses
|
||
|
x = parseExpression();
|
||
|
tryToEat(')');
|
||
|
} else if ((currentChar >= '0' && currentChar <= '9') || currentChar == '.') { // numbers
|
||
|
while ((currentChar >= '0' && currentChar <= '9') || currentChar == '.') {
|
||
|
moveToNextChar();
|
||
|
}
|
||
|
x = Double.parseDouble(input.substring(startPos, this.pos));
|
||
|
} else if (currentChar >= 'a' && currentChar <= 'z') { // functions
|
||
|
while (currentChar >= 'a' && currentChar <= 'z') {
|
||
|
moveToNextChar();
|
||
|
}
|
||
|
String func = input.substring(startPos, this.pos);
|
||
|
x = parseFactor();
|
||
|
x = switch (func) {
|
||
|
case "sqrt" -> Math.sqrt(x);
|
||
|
case "sin" -> Math.sin(Math.toRadians(x));
|
||
|
case "cos" -> Math.cos(Math.toRadians(x));
|
||
|
case "tan" -> Math.tan(Math.toRadians(x));
|
||
|
case "log" -> Math.log(x);
|
||
|
default -> throw new ParseException("Unknown function: " + func, startPos);
|
||
|
};
|
||
|
} else {
|
||
|
throw new ParseException("Unexpected: " + (char) currentChar, startPos);
|
||
|
}
|
||
|
|
||
|
if (tryToEat('^')) {
|
||
|
x = Math.pow(x, parseFactor()); // exponentiation
|
||
|
}
|
||
|
|
||
|
return x;
|
||
|
}
|
||
|
|
||
|
private double parseTerm() throws ParseException {
|
||
|
double x = parseFactor();
|
||
|
for (;;) {
|
||
|
if (tryToEat('*'))
|
||
|
x *= parseFactor(); // multiplication
|
||
|
else if (tryToEat('/'))
|
||
|
x /= parseFactor(); // division
|
||
|
else
|
||
|
return x;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
public static double eval(final String equation) throws ParseException {
|
||
|
return new Parser(equation).evaluate();
|
||
|
}
|
||
|
|
||
|
}
|