95 lines
3.6 KiB
Java
95 lines
3.6 KiB
Java
package su.nightexpress.excellentenchants.enchantment.util;
|
|
|
|
import org.jetbrains.annotations.NotNull;
|
|
|
|
public class Evaluator {
|
|
|
|
public static double evaluate(@NotNull final String str) {
|
|
return new Object() {
|
|
int pos = -1, ch;
|
|
|
|
void nextChar() {
|
|
ch = (++pos < str.length()) ? str.charAt(pos) : -1;
|
|
}
|
|
|
|
boolean eat(int charToEat) {
|
|
while (ch == ' ') nextChar();
|
|
if (ch == charToEat) {
|
|
nextChar();
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
double parse() {
|
|
nextChar();
|
|
double x = parseExpression();
|
|
if (pos < str.length()) throw new RuntimeException("Unexpected: " + (char)ch);
|
|
return x;
|
|
}
|
|
|
|
// Grammar:
|
|
// expression = term | expression `+` term | expression `-` term
|
|
// term = factor | term `*` factor | term `/` factor
|
|
// factor = `+` factor | `-` factor | `(` expression `)` | number
|
|
// | functionName `(` expression `)` | functionName factor
|
|
// | factor `^` factor
|
|
|
|
double parseExpression() {
|
|
double x = parseTerm();
|
|
for (;;) {
|
|
if (eat('+')) x += parseTerm(); // addition
|
|
else if (eat('-')) x -= parseTerm(); // subtraction
|
|
else return x;
|
|
}
|
|
}
|
|
|
|
double parseTerm() {
|
|
double x = parseFactor();
|
|
for (;;) {
|
|
if (eat('*')) x *= parseFactor(); // multiplication
|
|
else if (eat('/')) x /= parseFactor(); // division
|
|
else return x;
|
|
}
|
|
}
|
|
|
|
double parseFactor() {
|
|
if (eat('+')) return +parseFactor(); // unary plus
|
|
if (eat('-')) return -parseFactor(); // unary minus
|
|
|
|
double x;
|
|
int startPos = this.pos;
|
|
if (eat('(')) { // parentheses
|
|
x = parseExpression();
|
|
if (!eat(')')) throw new RuntimeException("Missing ')'");
|
|
} else if ((ch >= '0' && ch <= '9') || ch == '.') { // numbers
|
|
while ((ch >= '0' && ch <= '9') || ch == '.') nextChar();
|
|
x = Double.parseDouble(str.substring(startPos, this.pos));
|
|
} else if (ch >= 'a' && ch <= 'z') { // functions
|
|
while (ch >= 'a' && ch <= 'z') nextChar();
|
|
String func = str.substring(startPos, this.pos);
|
|
if (eat('(')) {
|
|
x = parseExpression();
|
|
if (!eat(')')) throw new RuntimeException("Missing ')' after argument to " + func);
|
|
} else {
|
|
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));
|
|
default -> throw new RuntimeException("Unknown function: " + func);
|
|
};
|
|
} else {
|
|
throw new RuntimeException("Unexpected: " + (char)ch);
|
|
}
|
|
|
|
if (eat('^')) x = Math.pow(x, parseFactor()); // exponentiation
|
|
|
|
return x;
|
|
}
|
|
}.parse();
|
|
}
|
|
}
|