Untersuchen Sie zunächst die nachfolgenden Definitionen und versuchen Sie diese nachzuvollziehen:
Self-compiling Compiler:
Ein Compiler C, der die Sprache S1 nach S2 übersetzt und selbst in der Sprache S1 geschrieben ist, wird als Self-compiling Compiler bezeichnet, wenn bei der Selbstkompilation der identische (Byte genaue) Compiler erneut entsteht. In der nachfolgenden Abbildung ist ein Pascal Selbst-Compiler gezeigt:
Cross-Compiler:
Ein Compiler, der als Ausgabe Maschinensprache eines anderen Rechnertyps liefert, der auf der Maschine des Compilers selbst nicht lauffähig sein muss. In nachfolgender Abbildung wird ein Cross-compiler Pascal zu M' gezeigt. Ein Pascal-Programm wird mit diesem kompiliert um ein lauffähiges Programm für die Zielmaschine M' zu erzeugen:
Portierung für verschiedene Maschinen:
Ausgangspunkt ist ein Selbst-Compiler C1, der S1 => M übersetzt und auf der Maschine M läuft. Dieser wird so modifiziert, dass seine Ausgabesprache S2' der Maschinencode für die Zielmaschine M' darstellt. Kompiliert man nun diesen Compiler (C2) mit dem vorhandenen Compiler (C1), erhält man einen S1 => M' Compiler (lauffähig auf M). Kompiliert man nun den Quellcode von C2 noch einmal mit C2 selbst, erhält man C3 - ein S1 => M' Compiler der auf der Maschine M' läuft. Die nachfolgende Abbildung zeigt diese Schritte wieder anhand eines Pascal-Compilers:
P-Code:
1969 wurde der erste Pascal-Compiler von N. Wirth entwickelt. Seine Idee war es, den Pascalcode nicht direkt in Maschinensprache eines Rechners M zu überführen, sondern zunächst in eine Zwischensprache - den P-Code zu übersetzen. Dazu entwickelte er einen komplexen Pascal zu P-Code Compiler der selbst in P-Code geschrieben war. Mit Hilfe eines P-Code Interpreters, wurde dieser Compiler lauffähig gemacht.
Nun konnten Pascal => M-Code (Maschinensprache eines beliebigen Rechners) Compiler komfortabel in Pascal verfasst werden. Mit Hilfe des Pascal => P-Code Compilers wurden diese zu Pascal => M-Code Compilern (geschrieben in P-Code) übersetzt, die anschließend in einem letzten Schritt, wiederum mit Hilfe des Interpreters, in den Pascal => M-Code Compiler (einen echten native Compiler) übersetz werden konnten.
Der große Vorteil: Nur der relativ einfache P-Code Interpreter musste für jeden Zielrechner entwickelt werden. Das Prinzip dieser virtuellen Maschine findet man heute auch in Java oder .NET wieder.
Boot-strapping:
Eine Frage stellt sich - wie wurde der erste Hochsprachen-Compiler entwickelt? In Assembler vielleicht? Aber wenn, wie wurde dann wiederum dieser Assembler entwickelt?
Die Antwort ist Bootstrapping. Man entwickelt einen einfachen Compiler in der eigenen Zielsprache (also einen Selbst-Compiler). Nun beginnt man neue Funktionen (Sprachelemente) im Compiler-Quellcode hinzuzufügen und kompiliert diesen erneut. Nun stehen dem Compiler auch diese neuen Sprachelemente zur Verfügung. In jedem Entwicklungsschritt entsteht somit mehr von der angestrebten Hochsprache. Sobald neue Sprachelemente eingebaut wurden, können diese im nächsten Schritt im Compiler-Quellcode auch schon wieder verbaut werden.
Wichtig bei der Entwicklung, ist ein Backup des alten Compilers, da es passieren kann, dass ein fehlerhafter Compiler entsteht und somit kein Compiler mehr vorhanden ist, der den Quellcode kompilieren kann. Die Folge wäre, dass man die Entwicklung von vorn beginnen müsste.
Ein Beispiel aus der Praxis:
Ein open-source Compiler wurde einst von mehreren Entwicklern betreut. Einer dieser entschied sich, einen Trojaner in den Compiler-Quellcode zu integrieren und kompilierte diesen anschließend zum ausführbaren Compiler der nun diesen Trojaner enthielt. Dieser arbeitete so, dass nun allen übersetzten Programmen die mit ihm kompiliert wurden, ebenfalls diesen Trojaner enthielten. Der Entwickler entfernte nun wieder den Trojanercode aus dem originalen Quellcode, aber lies die "infizierte" ausführbare Version des Compilers zurück. Die anderen Entwickler bemerkten zunächst nicht, und somit verschleppte sich der Trojaner immer weiter.
Aufgabe:
Laden Sie blue.zip herunter und dekomprimieren Sie den Inhalt in einen Ordner. Im Archiv finden Sie den C# Compiler Blue, der selbst in C# vorliegt.
Entwickeln Sie ein T-Diagramm für das Kompilieren von Blue mit dem .NET csc.exe Compiler und ein anschließendes Selbst-Kompilieren von Blue mit Blue. Der entstandene "BlueSelbst" Compiler soll anschließend ein Hallo Welt Programm in C# übersetzen.
Hinweise:
Bei der Konstruktion mit TDiag kann zunächst der vordefinierte C# Compiler verwendet werden. Der Befehl für die Übersetzung lautet:
csc.exe /out:blue.exe @blue_source_core
hierfür muss in TDiag der Eingabecompiler mit "@blue_source_core" als Filename bezeichnet werden (mit @ am Anfang). Beim Ausgabecompiler muss an selber Stelle nur "blue.exe" eingetragen werden.
Speichern Sie das T-Diagramm im selben Ordner wie Blue und führen Sie es aus, um festzustellen ob Blue.exe korrekt erzeugt wird.
Entwickeln Sie das Diagramm entsprechend der Aufgabenstellung weiter und testen Sie es abschließend mit "Diagramm ausführen".
Ergebnis:
Der entstandene Compiler blue.exe ist nun in der Lage seinen eigenen Quellcode zu kompilieren. Durch Veränderung des Blue Quellcode können nun neue Sprachelemente hinzugefügt werden. Somit liegt die Basis für Bootstrapping vor.
[Lösung] ("Ziel speichern unter" verwenden - im Blue Verzeichnis ablegen)
|