import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Stack;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class SimpleFormelParser {
// Zahlen, Wörter, einzelne non-Whitespace-Zeichen. Alles ggf. mit führenden Leerzeichen
private final static Pattern pattern = Pattern.compile("^\\s*([0-9\\.]+|\\w+|\\S)");
private final static String ENDSYMBOL = "end of input";
private Map<String,BiFunction<Double,Double,Double>> operators = new HashMap<>();
private Map<String,Function<Double,Double>> functions = new HashMap<>();
private Map<String,Double> constants = new HashMap<>();
private Stack<String> tokens = new Stack<>();
public SimpleFormelParser(String string) {
// den String zerlegen und in den Stack legen
int cursor = 0;
while (cursor < string.length()) {
Matcher m = pattern.matcher(string.substring(cursor));
if (m.find() && m.groupCount() > 0) {
tokens.push(m.group(1));
cursor += m.group().length();
} else {
cursor++;
}
}
tokens.push(ENDSYMBOL);
Collections.reverse(tokens);
// Hier Operatoren definieren. Die Zahl ist die Wertigkeit (je kleiner, desto höherwertiger, keine 0 verwenden). 'Punktrechnung geht vor Strichrechnung'
operators.put("1^", (a,b) -> Math.pow(a,b));
operators.put("2*", (a,b) -> a*b);
operators.put("2/", (a,b) -> a/b);
operators.put("3+", (a,b) -> a+b);
operators.put("3-", (a,b) -> a-b);
// Hier Funktionen definieren
functions = new HashMap<>();
functions.put("sin", x -> Math.sin(x));
functions.put("cos", x -> Math.cos(x));
functions.put("sqrt", x -> Math.sqrt(x));
// Hier Konstanten definieren;
constants.put("pi", Math.PI);
constants.put("e", Math.E);
}
/**
* Wertet die Formel aus.
*/
public double eval() {
double result = Ausdruck(3); // eine Formel ist ein Ausdruck
if (!tokens.isEmpty() && tokens.peek() != ENDSYMBOL) {
throw new RuntimeException(String.format("superfluous tokens: '%s'", tokens));
}
return result;
}
/**
* Parst einen mathematischen Ausdruck. Ein Ausdruck ist: <Ausdruck> <Operator> <Ausdruck> ...
* d.h., ein Ausdruck ist wiederum ein Ausdruck der Form <Ausdruck> <Operator> <Ausdruck> ... sofern die Hierarchy > 0 ist.<br>
* Ist sie 0, wird mit der Methode Operand() ein Operand geparst.<br>
* Entsprechend der Hierarchy wird in der Operator-Map nach passenden Operatoren gesucht.
*/
private double Ausdruck(int hierarchy) {
if (hierarchy <= 0) {
return Operand();
}
double result = Ausdruck(hierarchy-1);
do {
String token = tokens.pop();
BiFunction<Double, Double, Double> operator = operators.get(String.format("%d%s", hierarchy, token));
if (operator == null) {
tokens.push(token);
break;
} else {
result = operator.apply(result, Ausdruck(hierarchy-1));
}
} while (true);
return result;
}
/**
* Parst Negation, Klammerausdrücke, Funktionen, Konstanten, Zahlen
*/
private double Operand() {
String token = tokens.pop();
switch (token) {
case ENDSYMBOL: throw new RuntimeException("Unexpected end of input");
// Negation
case "-": return -Operand();
// Klammerausdrücke
case "(":
double result = Ausdruck(3);
token = tokens.pop();
if (!")".equals(token)) {
throw new RuntimeException(String.format(") expected. Found '%s' instead.", token));
}
return result;
default:
// Funktionen. In der Mathematik wird das nachfolgende Produkt als Argument verstanden, es muss nicht geklammert sein
if (functions.containsKey(token)) {
return functions.get(token).apply(Ausdruck(2));
}
// Konstanten
if (constants.containsKey(token)) {
return constants.get(token);
}
// alles andere versuchen wir als Zahl zu interpretieren
try {
return Double.parseDouble(token);
} catch (NumberFormatException e) {
throw new RuntimeException(String.format("Invalid operand: '%s'", token));
}
}
}
public static void main(String [] args) {
System.out.println(new SimpleFormelParser(" ( 1.7 * 10^2) * sin -1.6*pi + 1+2*4*0.5").eval());
}
}