Unterschiede

Hier werden die Unterschiede zwischen zwei Versionen angezeigt.

Link zu dieser Vergleichsansicht

Beide Seiten der vorigen RevisionVorhergehende Überarbeitung
programmieren:schritte:interpreter:start [2016/02/18 06:39] martinprogrammieren:schritte:interpreter:start [2021/12/29 10:40] (aktuell) – Externe Bearbeitung 127.0.0.1
Zeile 1: Zeile 1:
 +====== Interpreter ======
  
 +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)''):
 +
 +<code java>
 +
 +public class Interpreter {
 +
 + /**
 + * Speichert Zuordnungen von Variablenbezeichnern zu Werten:
 + */
 + private HashMap<String, Double> 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);
 + }
 +
 +
 +</code>
 +
 +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).
 +
 +<code java>
 + /**
 + * 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
 + * @throws Exception
 + */
 + public double interpretiere(Knoten knoten) throws Exception {
 +
 + 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) {
 + throw new Exception("Die Belegung der Variable "
 + + variablenbezeichner + " ist nicht bekannt.");
 + }
 +
 + return wert;
 +
 + case zahl:
 + return knoten.getToken().getZahl();
 +
 + default:
 + return 0; // sollte nie vorkommen
 + }
 +
 + }
 +</code>
 +
 +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:
 +
 +<code java>
 +switch (knoten.getToken().getTokenType()) {
 + case plus:
 + return interpretiere(knoten.getLinks())
 + + interpretiere(knoten.getRechts());
 +</code>
 +
 +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:
 +
 +<code java>
 + case text:
 +
 + String variablenbezeichner = knoten.getToken().getText();
 +
 + Double wert = variablenbelegung.get(variablenbezeichner);
 +
 + if (wert == null) {
 + throw new Exception("Die Belegung der Variable "
 + + variablenbezeichner + " ist nicht bekannt.");
 + }
 +
 + return wert;
 +</code>
 +
 +Ist der ''TokentType'' des Knotens "zahl", so ist der Wert dieser Zahl im Knoten selbst hinterlegt und kann direkt entnommen und zurückgegeben werden:
 +
 +<code java>
 + case zahl:
 + return knoten.getToken().getZahl();
 +</code>
 +
 +Die Methode ''getBelegungAlsString()'' dient nur zu Debuggingzwecken, damit aktuell gespeicherte Variablenbelegung am Bildschirm ausgegeben werden kann:
 +
 +<code java>
 + /**
 + * Nur zu Debuggingzwecken
 +
 + * @return
 + *    String, der alle Variablen zusammen mit ihrer Belegung in der Form
 + *    variablenbezeichner = wert
 + *    enthält.
 + */
 + public String getBelegungAlsString() {
 +
 + String s = "";
 +
 + for (String bezeichner : variablenbelegung.keySet().toArray(
 + new String[0])) {
 + s += bezeichner + " = " + variablenbelegung.get(bezeichner) + "\n";
 + }
 +
 + return s;
 + }
 +</code>
 +
 +Hier geht's weiter mit einer Beschreibung des [[programmieren:verwendung:start|Aufrufs/der Verwendung von Lexer, Parser und Interpreter]].
Drucken/exportieren
QR-Code
QR-Code programmieren:schritte:interpreter:start (erstellt für aktuelle Seite)