====== Interpreter ====== Im folgenden findest Du die Beschreibung des Interpreters. Den [[.test:start|fertigen Interpreter zusammen mit einem direkt im Browser ausführbaren Testprogramm findest Du hier]]. Ein Interpreter nimmt das Ergebnis des Parsevorgangs (i.d.R. einen Abstract Syntax Tree) und führt es aus. Für unsere einfache Programmiersprache (mathematische Terme mit +, -, *, /, (, ), Variablen und Zahlen) bedeutet dies, den Wert des gesamten Terms zu berechnen. Dazu muss der Interpreter natürlich die Belegungen der Variablen kennen, die im Term enthalten sind. Gespeichert werden sie in der gleichnamigen ''HashMap'', die Zuordnungen von String-Werten zu Double-Werten speichern kann (Methode ''put(String s, Double d)'') und zu einem gegebenen String-Wert den zugehörigen Double-Wert wieder finden kann (Methode ''get(String)''): public class Interpreter { /** * Speichert Zuordnungen von Variablenbezeichnern zu Werten: */ private HashMap variablenbelegung = new HashMap<>(); /** * Belegt die Variable mit Bezeichner bezeichner mit dem Wert wert. * * @param bezeichner * Bezeichner der Variablen * @param wert * Wert der Variablen */ public void belegeVariable(String bezeichner, double wert) { variablenbelegung.put(bezeichner, wert); } Befüllt wird die Variablenliste vor der Termberechnung mit der Methode ''belegeVariable''. Die eigentliche Funktionalität zur Berechnung des Termwerts steckt in der Methode ''interpretiere''. Ihr wird ein Knoten des Abstract Syntax Tree übergeben. Sie berechnet den Wert des Teilbaums, dessen Wurzel der übergebene Knoten ist (also der Teilbaum, der durch den Knoten und alles darunter gebildet wird). /** * Berechnet - ausgehend vom übergebenen Knoten - den Wert des Terms, der * durch den Knoten und den darunterhängenden Teilbaum gegeben ist. * * @param knoten * Wurzel des Teilbaums, dessen Termwert berechnet werden soll * @return Wert des Terms */ public double interpretiere(Knoten knoten) { switch(knoten.getToken().getTokenType()) { case plus : return interpretiere(knoten.getLinks()) + interpretiere(knoten.getRechts()); case minus : return interpretiere(knoten.getLinks()) - interpretiere(knoten.getRechts()); case mal : return interpretiere(knoten.getLinks()) * interpretiere(knoten.getRechts()); case geteilt : return interpretiere(knoten.getLinks()) / interpretiere(knoten.getRechts()); case negation : return - interpretiere(knoten.getLinks()); case text : String variablenbezeichner = knoten.getToken().getText(); Double wert = variablenbelegung.get(variablenbezeichner); if(wert == null) { println("Die Belegung der Variable " + variablenbezeichner + " ist nicht bekannt.", Color.red); System.exit(0); } return wert; case zahl : return knoten.getToken().getZahl(); default : return 0; // sollte nie vorkommen } } Die Methode sieht sich den ''TokenType'' des Knotens an. Handelt es sich beispielsweise um ''plus'', so ruft sich die Methode selbst auf, um den Wert des linken Teilbaums zu berechnen, der unter dem Knoten hängt. Dann ruft sie sich selbst auf, um den Wert des rechten Teilbaums zu berechnen. Anschließend addiert sie beide Werte. Im Programm sieht das so aus: switch(knoten.getToken().getTokenType()) { case plus : return interpretiere(knoten.getLinks()) + interpretiere(knoten.getRechts()); Ist der ''TokentType'' des Knotens "text", so handelt es sich um eine Variable. Das Programm holt in diesem Fall die Belegung der Variable aus der ''HashMap'' ''variablenbelegung'' und gibt sie als Wert des Teilbaums zurück: case text : String variablenbezeichner = knoten.getToken().getText(); Double wert = variablenbelegung.get(variablenbezeichner); if(wert == null) { println("Die Belegung der Variable " + variablenbezeichner + " ist nicht bekannt.", Color.red); System.exit(0); } return wert; Ist der ''TokentType'' des Knotens "zahl", so ist der Wert dieser Zahl im Knoten selbst hinterlegt und kann direkt entnommen und zurückgegeben werden: case zahl : return knoten.getToken().getZahl(); Die Methode ''getVariablenBelegung()'' dient nur zu Debuggingzwecken, damit aktuell gespeicherte Variablenbelegung am Bildschirm ausgegeben werden kann: public String getVariablenBelegung() { return variablenbelegung.toString(); } [[..ebnf:start|Hier erkläre ich Dir, wie Du die Grammatik einer Programmiersprache mittels einer Grammatik in EBNF (erweiterte Backus-Naur-Form) beschreiben kannst!]]