TECHNISCHE UNIVERSITÄT CAROLO-WILHELMINA ZU BRAUNSCHWEIG Institut für Programmierung und Reaktive Systeme Prof. Dr. Ursula Goltz [email protected] Dipl.-Inform. Malte Lochau [email protected] 19. April Sommersemester 2012 Compiler I 1. Übungsblatt Compiler Grundlagen, Reguläre Ausdrücke und Endliche Automaten, Lexikalische Analyse Aufgabe 1: Grundlagen eines Compilers a) Nennen Sie die Phasen eines Compilers. Beschreiben Sie diese jeweils mit Hilfe der folgenden Eigenschaften: Programmteil des Compilers, der die Phase implementiert Aufgabe(n) der Phase Erwartete Darstellungsform des zu verarbeitenden Programms (Eingabe der Phase) Erzeugte Darstellungsform des zu verarbeitenden Programms (Ausgabe der Phase) b) Was bezeichnet das Frontend und das Backend eines Compilers? Welche Phasen sind dem Frontend, welche dem Backend zuzuordnen? c) Vergleichen Sie die Funktionsweise eines Interpreters und eines Compilers anhand der Verarbeitung und Ausführung von Eingabeprogrammen der Sprache L. Ist Java eine interpretierte oder kompilierte Sprache? Aufgabe 2: Bootstrapping Es wurde ein Compiler für die neue Programmiersprache Java+ entwickelt. Dieser Compiler kompiliert in den Java Bytecode und wurde in Java geschrieben. Aus Gründen der Performanz besteht der Wunsch, dass dieser Compiler in Maschinensprache geschrieben ist und in Maschinensprache kompiliert. Zur Umsetzung dieser Aufgabe stehen noch weitere Compiler zur Verfügung: Ein Java Compiler im Java Bytecode, der Java Bytecode erzeugt, ein Compiler, der Java Bytecode in Maschinensprache übersetzt und der in C geschrieben ist, und ein C-Compiler in Maschinensprache, der in Maschinensprache übersetzt. Auÿerdem gibt es auf dem Rechner, auf dem der Compiler erzeugt werden soll, eine Java Virtual Machine (JVM), mit der sich Java Bytecode ausführen lässt. Denieren Sie die Entwicklungsschritte (Bootstrap) zur Erzeugung des gewünschten Compilers für Java+ durch Kombination der gegebenen Komponenten. Stellen Sie diese in Form von T-Diagrammen wie folgt dar: Q Z I wobei Q für die Quellsprache, Z für die Zielsprache und I für die Implementierungssprache des Compilers steht. Aufgabe 3: Reguläre Ausdrücke und Endliche Automaten Zeigen oder widerlegen Sie die folgenden Aussagen: a) Für alle regulären Ausdrücke r und s gilt (r∗ s∗ )∗ = (r|s)∗ b) Für alle regulären Ausdrücke r und s gilt r∗ |s∗ = (r|s)∗ Aufgabe 4: Reguläre Ausdrücke und Endliche Automaten Geben Sie reguläre Ausdrücke an, die die folgenden Sprachen beschreiben: a) Die Menge aller Wörter w ∈ {a, b}∗ , in denen jedem a direkt zwei b's vorangehen. b) Die Menge aller Wörter w ∈ {a, b}∗ , die nicht das Teilwort bab enthalten. c) Die Menge der DecimalIntegerLiterals in Java bestehen aus beliebig vielen Ziern ohne führende Null, optional abgeschlossen durch ein Sux L oder l, falls das Literal vom Typ long ist. Aufgabe 5: Reguläre Ausdrücke und Endliche Automaten Konstruieren Sie nichtdeterministische endliche Automaten, die die folgenden Sprachen erkennen: a) Die Menge aller Wörter w ∈ {a, b, c}∗ , die mindestens ein a und ein b enthalten. b) Die Menge aller Wörter w ∈ {a, b, c}∗ , die eine gerade Anzahl a's, eine ungerade Anzahl b's und beliebig viele c's enthalten. c) Die Menge der DocumentationComments (Javadoc-Kommentare) in Java mit der Form: 2 /** * documentation text <LineTerminator> * documentation text <LineTerminator> * ... */ Für diese Art Kommentare gilt: Die Kommentarblöcke beginnen mit /** und werden abgeschlossen durch */. Kommentare können sich über mehrere Zeilen erstrecken, die jeweils mit einem * beginnen. Das Auftreten von / im Kommentartext ohne vorherigen * ist zulässig und wird ignoriert. Aufgabe 6: Reguläre Ausdrücke und Endliche Automaten Aus jedem regulären Ausdruck r kann schrittweise ein minimaler deterministischer Automat konstruiert werden, der die von r beschriebene reguläre Sprache L(r) erkennt. Führen Sie diese Konstruktion für den regulären Ausdruck r = b(a|b)∗ bc durch. a) Regulärer Ausdruck ⇒ NEA: Konstruieren Sie einen nichtdeterministischen endlichen Automaten M1 , der die von r beschriebene Sprache akzeptiert. b) NEA ⇒ DEA: Konstruieren Sie aus M1 einen deterministischen Automaten M2 , der dieselbe Sprache wie M1 akzeptiert. c) DEA ⇒ minimaler DEA: Konstruieren Sie aus M2 einen DEA M3 , der dieselbe Sprache wie M2 akzeptiert und eine minimale Zustandsmenge besitzt. Geben Sie auch die jeweiligen Zwischenschritte an. Enthält der Automat unproduktive oder unerreichbare Zustände? Aufgabe 7: Lexikalische Analyse Der aus einem regulären Ausdruck konstruierte (minimale) deterministische endliche Automat kann zur Implementierung eines Scanners, der die durch den Ausdruck beschriebene Sprache erkennt, verwendet werden. Der Scanner wird so implementiert, dass er den endlichen Automaten simuliert, also die Zustände geeignet codiert und die Zustandsübergänge auf der Zustandsmenge ausführt. Eine Möglichkeit besteht darin, den Automaten direkt mittels geschachtelter if und switch Anweisungen zu implementieren. Schreiben Sie eine Java-Methode scan(), die diese Umsetzung eines Parsers für die Sprache von r = b(a|b)∗ bc auf Grundlage des Automaten M3 aus Aufgabe 6 realisiert. Dabei können Sie von folgenden Vorgaben ausgehen Es existiert bereits eine Methode char nextChar(), die jeweils das nächste Eingabezeichen liefert. 3 Ist das Dateiende erreicht, liefert nextChar das Zeichen '!'. Das Eingabealphabet des Scanners umfasst also die Zeichen a, b, c und !. Aufgabe 8: Lexikalische Analyse a) Nennen Sie die sieben lexikalischen Grundeinheiten der Sprache Java und geben Sie jeweils zu jeder Einheit ein Beispiel an. b) Welche dieser Einheiten sind weitergegeben? Token und werden vom Scanner an den Parser c) Erläutern sie an diesem Beispiel die Aufgaben eines Siebers. 4