Ich recherchiere CoffeeScript auf der Website http://coffeescript.org/ und es hat den Text
Der CoffeeScript-Compiler ist selbst in CoffeeScript geschrieben
Wie kann sich ein Compiler selbst kompilieren oder was bedeutet diese Aussage?
Ich recherchiere CoffeeScript auf der Website http://coffeescript.org/ und es hat den Text
Der CoffeeScript-Compiler ist selbst in CoffeeScript geschrieben
Wie kann sich ein Compiler selbst kompilieren oder was bedeutet diese Aussage?
Antworten:
Die erste Ausgabe eines Compilers kann nicht maschinell aus einer bestimmten Programmiersprache generiert werden. Ihre Verwirrung ist verständlich. Eine spätere Version des Compilers mit mehr Sprachfunktionen (wobei die Quelle in der ersten Version der neuen Sprache neu geschrieben wurde) könnte vom ersten Compiler erstellt werden. Diese Version könnte dann den nächsten Compiler kompilieren und so weiter. Hier ist ein Beispiel:
Hinweis: Ich bin mir nicht sicher, wie die CoffeeScript-Versionen genau nummeriert sind. Dies war nur ein Beispiel.
Dieser Vorgang wird normalerweise als Bootstrapping bezeichnet . Ein weiteres Beispiel für einen Bootstrapping-Compiler ist rustc
der Compiler für die Rust-Sprache .
Ken Thompson, einer der Urheber von Unix, schreibt in dem Artikel Reflections on Trusting Trust einen faszinierenden (und leicht lesbaren) Überblick darüber, wie sich der C-Compiler selbst kompiliert. Ähnliche Konzepte können auf CoffeeScript oder eine andere Sprache angewendet werden.
Die Idee eines Compilers, der seinen eigenen Code kompiliert, ähnelt vage einem Quine : Quellcode, der bei seiner Ausführung den ursprünglichen Quellcode als Ausgabe erzeugt. Hier ist ein Beispiel für eine CoffeeScript-Quine. Thompson gab dieses Beispiel einer C-Quine:
char s[] = {
'\t',
'0',
'\n',
'}',
';',
'\n',
'\n',
'/',
'*',
'\n',
… 213 lines omitted …
0
};
/*
* The string s is a representation of the body
* of this program from '0'
* to the end.
*/
main()
{
int i;
printf("char\ts[] = {\n");
for(i = 0; s[i]; i++)
printf("\t%d,\n", s[i]);
printf("%s", s);
}
Als nächstes fragen Sie sich vielleicht, wie dem Compiler beigebracht wird, dass eine Escape-Sequenz wie '\n'
ASCII-Code 10 darstellt. Die Antwort lautet, dass es irgendwo im C-Compiler eine Routine gibt, die Zeichenliterale interpretiert und einige Bedingungen wie diese enthält, um Backslash-Sequenzen zu erkennen:
…
c = next();
if (c != '\\') return c; /* A normal character */
c = next();
if (c == '\\') return '\\'; /* Two backslashes in the code means one backslash */
if (c == 'r') return '\r'; /* '\r' is a carriage return */
…
Also können wir dem obigen Code eine Bedingung hinzufügen ...
if (c == 'n') return 10; /* '\n' is a newline */
… Um einen Compiler zu erstellen, der weiß, dass '\n'
er ASCII 10 darstellt. Interessanterweise "kennen" dieser Compiler und alle nachfolgenden Compiler, die von ihm kompiliert wurden , diese Zuordnung, sodass Sie in der nächsten Generation des Quellcodes diese letzte Zeile in ändern können
if (c == 'n') return '\n';
… Und es wird das Richtige tun! Das 10
kommt vom Compiler und muss nicht mehr explizit im Quellcode des Compilers definiert werden. 1
Dies ist ein Beispiel für eine C-Sprachfunktion, die in C-Code implementiert wurde. Wiederholen Sie diesen Vorgang nun für jedes einzelne Sprachfeature, und Sie haben einen "selbsthostenden" Compiler: einen C-Compiler, der in C geschrieben ist.
1 Der in diesem Artikel beschriebene Plot Twist besteht darin, dass dem Compiler, da er solche Fakten "lernen" kann, auch falsch gelehrt werden kann, trojanische ausführbare Dateien auf eine Weise zu generieren, die schwer zu erkennen ist, und ein solcher Sabotageakt fortbestehen kann in allen vom verdorbenen Compiler produzierten Compilern.
Sie haben bereits eine sehr gute Antwort erhalten, aber ich möchte Ihnen eine andere Perspektive bieten, die Sie hoffentlich aufklären wird. Lassen Sie uns zunächst zwei Tatsachen feststellen, auf die wir uns beide einigen können:
Ich bin sicher, Sie können zustimmen, dass sowohl # 1 als auch # 2 wahr sind. Schauen Sie sich nun die beiden Aussagen an. Sehen Sie jetzt, dass es für den CoffeeScript-Compiler völlig normal ist, den CoffeeScript-Compiler kompilieren zu können?
Dem Compiler ist es egal, was er kompiliert. Solange es sich um ein in CoffeeScript geschriebenes Programm handelt, kann es kompiliert werden. Und der CoffeeScript-Compiler selbst ist zufällig ein solches Programm. Dem CoffeeScript-Compiler ist es egal, dass es sich um den CoffeeScript-Compiler handelt, den er selbst kompiliert. Alles, was es sieht, ist ein CoffeeScript-Code. Zeitraum.
Wie kann sich ein Compiler selbst kompilieren oder was bedeutet diese Aussage?
Ja, genau das bedeutet diese Aussage, und ich hoffe, Sie können jetzt sehen, wie diese Aussage wahr ist.
Wie kann sich ein Compiler selbst kompilieren oder was bedeutet diese Aussage?
Es bedeutet genau das. Zunächst einige Dinge zu beachten. Es gibt vier Objekte, die wir betrachten müssen:
Nun sollte es offensichtlich sein, dass Sie die generierte Assembly - die ausführbare Datei - des CoffeScript-Compilers verwenden können, um ein beliebiges CoffeScript-Programm zu kompilieren und die Assembly für dieses Programm zu generieren.
Jetzt ist der CoffeScript-Compiler selbst nur ein beliebiges CoffeScript-Programm und kann daher vom CoffeScript-Compiler kompiliert werden.
Es scheint , dass Ihre Verwirrung rührt von der Tatsache , dass , wenn Sie Ihre eigene neue Sprache erstellen, die Sie nicht haben einen Compiler noch können Sie Ihre Compiler kompilieren. Das sieht doch nach einem Hühnerei-Problem aus , oder?
Führen Sie den Prozess namens Bootstrapping ein .
Jetzt müssen Sie neue Funktionen hinzufügen. while
Angenommen, Sie haben nur -loops implementiert, möchten aber auch for
-loops. Dies ist kein Problem, da Sie jede for
-schleife so umschreiben können , dass es sich um eine while
-schleife handelt. Dies bedeutet, dass Sie nur while
-loops im Quellcode Ihres Compilers verwenden können, da die vorhandene Assembly nur diese kompilieren kann. Sie können jedoch Funktionen in Ihrem Compiler erstellen, for
mit denen Sie -loops pasen und kompilieren können. Anschließend verwenden Sie die bereits vorhandene Assembly und kompilieren die neue Compilerversion. Und jetzt haben Sie eine Assembly eines Compilers, der auch for
-loops analysieren und kompilieren kann! Sie können jetzt zur Quelldatei Ihres Compilers zurückkehren und alle while
-loops, die Sie nicht möchten, in for
-loops umschreiben.
Spülen und wiederholen, bis alle gewünschten Sprachfunktionen mit dem Compiler kompiliert werden können.
while
und waren for
natürlich nur Beispiele, aber dies funktioniert für jede neue Sprachfunktion, die Sie wollen. Und dann sind Sie in der Situation, in der sich CoffeScript gerade befindet: Der Compiler kompiliert sich selbst.
Es gibt viel Literatur da draußen. Überlegungen zum Vertrauen Vertrauen ist ein Klassiker, den jeder, der sich für dieses Thema interessiert, mindestens einmal lesen sollte.
Hier beschönigt der Begriff Compiler die Tatsache, dass es sich um zwei Dateien handelt. Eine ist eine ausführbare Datei, die als in CoffeScript geschriebene Eingabedateien eine andere ausführbare Datei, eine verknüpfbare Objektdatei oder eine gemeinsam genutzte Bibliothek als Ausgabedatei erstellt. Die andere ist eine CoffeeScript-Quelldatei, die gerade das Verfahren zum Kompilieren von CoffeeScript beschreibt.
Sie wenden die erste Datei auf die zweite an und erzeugen eine dritte, die denselben Kompilierungsvorgang wie die erste ausführen kann (möglicherweise mehr, wenn die zweite Datei Funktionen definiert, die von der ersten nicht implementiert wurden), und können daher die erste ersetzen, wenn Sie dies tun so Verlangen.
Da die Ruby-Version des CoffeeScript-Compilers bereits vorhanden war, wurde sie zum Erstellen der CoffeeScript-Version des CoffeeScript-Compilers verwendet.
Dies wird als selbsthostender Compiler bezeichnet .
Es ist sehr häufig und resultiert normalerweise aus dem Wunsch eines Autors, seine eigene Sprache zu verwenden, um das Wachstum dieser Sprache aufrechtzuerhalten.
Hier geht es nicht um Compiler, sondern um die Ausdruckskraft der Sprache, da ein Compiler nur ein Programm ist, das in einer bestimmten Sprache geschrieben ist.
Wenn wir sagen, dass "eine Sprache geschrieben / implementiert ist", meinen wir tatsächlich, dass ein Compiler oder Interpreter für diese Sprache implementiert ist. Es gibt Programmiersprachen, in denen Sie Programme schreiben können, die die Sprache implementieren (sind Compiler / Interpreter für dieselbe Sprache). Diese Sprachen werden universelle Sprachen genannt .
Um dies verstehen zu können, denken Sie an eine Metalldrehmaschine. Es ist ein Werkzeug zum Formen von Metall. Mit nur diesem Werkzeug ist es möglich, ein anderes, identisches Werkzeug zu erstellen, indem seine Teile erstellt werden. Somit ist dieses Werkzeug eine universelle Maschine. Natürlich wurde der erste mit anderen Mitteln (anderen Werkzeugen) erstellt und war wahrscheinlich von geringerer Qualität. Aber der erste wurde verwendet, um neue mit höherer Präzision zu bauen.
Ein 3D-Drucker ist fast eine universelle Maschine. Sie können den gesamten 3D-Drucker mit einem 3D-Drucker drucken (Sie können nicht die Spitze bauen, die den Kunststoff schmilzt).
Die n + 1-te Version des Compilers ist in X geschrieben.
Somit kann es von der n-ten Version des Compilers kompiliert werden (auch in X geschrieben).
Die erste Version des in X geschriebenen Compilers muss jedoch von einem Compiler für X kompiliert werden, der in einer anderen Sprache als X geschrieben ist. Dieser Schritt wird als Bootstrapping des Compilers bezeichnet.
Compiler verwenden eine Spezifikation auf hoher Ebene und verwandeln sie in eine Implementierung auf niedriger Ebene, wie sie auf Hardware ausgeführt werden kann. Daher besteht außer der Semantik der Zielsprache keine Beziehung zwischen dem Format der Spezifikation und der tatsächlichen Ausführung.
Cross-Compiler wechseln von einem System zu einem anderen System. Cross-Language-Compiler kompilieren eine Sprachspezifikation in eine andere Sprachspezifikation.
Grundsätzlich ist das Kompilieren eine gerechte Übersetzung, und das Niveau ist normalerweise ein höheres Sprachniveau zu einem niedrigeren Sprachniveau, aber es gibt viele Varianten.
Bootstrapping-Compiler sind natürlich am verwirrendsten, da sie die Sprache kompilieren, in der sie geschrieben sind. Vergessen Sie nicht den ersten Schritt beim Bootstrapping, für den mindestens eine ausführbare Version erforderlich ist, die ausführbar ist. Viele Bootstrap-Compiler arbeiten zuerst an den minimalen Funktionen einer Programmiersprache und fügen künftig zusätzliche komplexe Sprachfunktionen hinzu, solange die neue Funktion mit den vorherigen Funktionen ausgedrückt werden kann. Wenn dies nicht der Fall wäre, müsste dieser Teil des "Compilers" zuvor in einer anderen Sprache entwickelt werden.
self-hosting
Compiler. Siehe programmers.stackexchange.com/q/263651/6221