Hinweise zur Übung 2 (PID)
- Syntaxanalyse von Ausdrücken
-
Schreiben Sie zuerst den Parser (Methode parseExpression) ohne einen Baum aufzubauen (siehe Folienbeispiel,
ohne rot gefärbten Code). Erkennt der Parser arithmetische Beispiel-Ausdrücke
korrekt? Geben Sie zuerst an "interessanten" Positionen entsprechenden
Text aus und vergleichen Sie die Ausgabe mit dem von Ihnen erwarteten Ausgabe.
Wenn der Parser korrekt funktioniert, können Sie den Programmcode
für das Syntaxbaum-Erzeugen hinzufügen.
-
Der Parser ("rekursiver Abstieg") kann direkt von der Grammatik abgeleitet
werden (siehe Folienbeispiel). Der Begriff "Symbol" entspricht bei dieser
Aufgabe einem Operator/Funktion/Klammer oder einer Zahl. Die Vorgehensweise ist folgende:
-
Jede Regel entspricht einer Methode (ABC => public static ... parseABC() {...})
-
Jede Regel wird von links nach rechts gelesen und in entsprechenden Parser-Code
umgewandelt
-
Hintereinander stehende Elemente ( X Y
) ergeben eine sequentielle Anweisungsfolge:
parseX();
parseY();
-
Aus einer Wiederholung ( X { A } Y
) wird eine while-Schleife:
parseX();
while (next character is a start of
A)
parseA();
parseY();
-
Aus Alternativen ( X ( A | B ) Y
) wird eine if-Anweisung:
parseX();
if (next character is a start of A)
parseA();
else if (next character is a start of
B)
parseB();
else
throw new RuntimeException("start of A or B expected")
parseY();
-
Eine Option ( X [ A ] Y )
ist eine versteckte Alternative ( X ( A |
) Y ) und daraus wird ebenfalls eine if-Anweisung:
parseX();
if (next character is a start of A)
parseA();
parseY();
-
Vergessen Sie nicht die Fehlerbehandlung. Falls ein fehlerhaftes (weil
nicht der Grammatik entsprechendes) Symbol auftaucht, muss ein
Fehler ausgegeben werden! Weiters sollte das Parsen abgebrochen werden.
Dazu eignen sich besonders Exceptions (z.B.: throw new ParseException("Detailed
error message"); siehe API-Dokumentation des JDK unter java.lang.Throwable,
...). Versuchen Sie möglichst genaue Fehlermeldungen anzugeben (vereinfacht das Fehlerfinden).
-
Das bzw. die Startsymbole (bzw. Startzeichen) einer (Teil-)Regel sind alle Zeichen, die am
Anfang dieser (Teil-)Regel stehen können
-
Die gegebene Grammatik bitte nicht ändern! Die Grammatik ist LL(1)
und diese Eigenschaft ist Voraussetzung um einen rekursiven Abstieg überhaupt
programmieren zu können.
-
Syntaxbaum: Achten Sie darauf, dass sich die Klammerung bzw. die Auswertungsreihenfolge
des arithmetischen Ausdrucks im Syntaxbaum widerspiegelt.
-
Ausführlich Testen! Es sollten alle Alternativen, alle Regeln, alle
Wiederholungen, etc. mindestens einmal getestet werden. Um den Syntaxbaum
zu visualisieren ist es empfehlenswert den Syntaxbaum (mit Klammerung!)
InOrder auszugeben.
-
Achten Sie darauf, dass Sie das Prinzip des Information hiding einhalten.
D.h. die interne (Baum-)Datenstruktur soll für andere Klassen nicht
erreichbar/manipulierbar/zerstörbar sein.
-
Um Zeichen/Symbole zu lesen, ist es am einfachsten die Methoden char
ch = In.read(); und String identifier = In.readIdentifier(); zu verwenden. Für Fließkommazahlen eignet sich die Methode double val = In.readDouble();.
Mit char ch = In.peek();
kann das nächste Zeichen gelesen werden, ohne es aus dem Eingabestrom
zu entfernen (Leerzeichen, Tabulatoren, etc. werden dabei überlesen
und aus dem Eingabestrom entfernt).
- Legen Sie bei Ihrer Lösung Wert auf Lesbarkeit und Effizienz. Eine
gute Lösung muss nicht lang sein. In der Kürze liegt die Würze!
- Laden
Sie sich die neue Version von In.java von der "Sprechen Sie Java?"-Buch oder
der Übungsseite herunter. Ansonsten werden die Zeichen '+' und '-' gleich
nach einer Zahl nicht richtig erkannt.