Nehmen wir an, ich kaufe eine neue CPU, auf der nichts drauf ist. Bei der ersten Inbetriebnahme möchte ich ein Betriebssystem installieren, auf dem C läuft. Wie läuft der C-Compiler? Gibt es einen Miniatur-C-Compiler im BIOS?
Ich verstehe, was Sie fragen... was würde passieren, wenn wir keinen C-Compiler hätten und von vorne anfangen müssten?
Die Antwort ist, dass Sie mit der Montage oder der Hardware beginnen müssen. Das heißt, man kann einen Compiler entweder in Software oder in Hardware bauen. Wenn es keine Compiler auf der Welt gäbe, könnte man es heutzutage wahrscheinlich schneller mit Assembler machen; aber ich glaube, früher waren Compiler tatsächlich spezielle Hardware. Die Wikipedia-Artikel ist etwas kurz und bestätigt mich in dieser Hinsicht nicht, aber das macht nichts.
Die nächste Frage ist wohl: Was passiert heute? Nun, die Compilerautoren sind seit Jahren damit beschäftigt, portables C zu schreiben, also sollte der Compiler in der Lage sein, sich selbst zu kompilieren. Es lohnt sich, auf einer sehr hohen Ebene zu diskutieren, was Kompilierung ist. Im Grunde genommen nimmt man eine Reihe von Anweisungen und erzeugt daraus eine Baugruppe. Das war's. Nun, es ist eigentlich komplizierter als das - man kann alle möglichen Dinge mit Lexern und Parsern machen und ich verstehe nur einen kleinen Teil davon, aber im Wesentlichen geht es darum, C in Assembler abzubilden.
Im Normalfall erzeugt der Compiler Assemblercode, der zu Ihrer Plattform passt, aber das muss er nicht. Er kann Assemblercode für jede beliebige Plattform erzeugen, vorausgesetzt, er weiß, wie das geht. Der erste Schritt, um C auf Ihrer Plattform zum Laufen zu bringen, besteht also darin, ein Ziel in einem vorhandenen Compiler zu erstellen, Anweisungen hinzuzufügen und den Basiscode zum Laufen zu bringen.
Sobald dies geschehen ist, können Sie nun theoretisch Crosskompilierung von einer Plattform zur anderen. Die nächsten Schritte sind: Erstellung eines Kernels, eines Bootloaders und einiger grundlegender Dienstprogramme für die jeweilige Plattform.
Dann können Sie versuchen, den Compiler für diese Plattform zu kompilieren (sobald Sie ein funktionierendes Userland und alles, was Sie für den Build-Prozess benötigen, haben). Wenn das gelingt, haben Sie grundlegende Dienstprogramme, einen funktionierenden Kernel, Userland und ein Compilersystem. Sie sind jetzt auf einem guten Weg.
Beachten Sie, dass Sie im Zuge der Portierung des Compilers wahrscheinlich auch einen Assembler und Linker für diese Plattform schreiben mussten. Um die Beschreibung einfach zu halten, habe ich sie weggelassen.
Falls dies von Interesse ist, Linux von Grund auf neu ist eine interessante Lektüre. Es wird nicht erklärt, wie man ein neues Ziel von Grund auf erstellt (was deutlich nicht trivial ist) - es wird davon ausgegangen, dass man für ein bestehendes bekanntes Ziel baut, aber es wird gezeigt, wie man das Wesentliche kreuzkompiliert und mit dem Aufbau des Systems beginnt.
Python wird nicht wirklich zu einer Baugruppe zusammengesetzt. Zunächst einmal verfolgt das laufende Python-Programm die Anzahl der Verweise auf Objekte, etwas, das eine CPU nicht für Sie tun wird. Das Konzept des anweisungsbasierten Codes ist jedoch auch der Kern von Python. Probieren Sie das mal aus:
>>> def hello(x, y, z, q):
... print "Hello, world"
... q()
... return x+y+z
...
>>> import dis
dis.dis(hello)
2 0 LOAD_CONST 1 ('Hello, world')
3 PRINT_ITEM
4 PRINT_NEWLINE
3 5 LOAD_FAST 3 (q)
8 CALL_FUNCTION 0
11 POP_TOP
4 12 LOAD_FAST 0 (x)
15 LOAD_FAST 1 (y)
18 BINARY_ADD
19 LOAD_FAST 2 (z)
22 BINARY_ADD
23 RETURN_VALUE
Dort können Sie sehen, wie Python den von Ihnen eingegebenen Code auffasst. Dies ist Python-Bytecode, d.h. die Assemblersprache von Python. Sie hat sozusagen ihren eigenen "Befehlssatz" für die Implementierung der Sprache. Dies ist das Konzept einer virtuellen Maschine.
Java hat genau die gleiche Idee. Ich nahm eine Klassenfunktion und führte javap -c class
um dies zu erhalten:
invalid.site.ningefingers.main:();
Code:
0: aload_0
1: invokespecial #1; //Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: iconst_0
1: istore_1
2: iconst_0
3: istore_1
4: iload_1
5: aload_0
6: arraylength
7: if_icmpge 57
10: getstatic #2;
13: new #3;
16: dup
17: invokespecial #4;
20: ldc #5;
22: invokevirtual #6;
25: iload_1
26: invokevirtual #7;
//.......
}
Ich nehme an, Sie haben es verstanden. Dies sind die Assemblersprachen der Python und Java Welten, d.h. wie der Python Interpreter und der Java Compiler jeweils denken.
Es lohnt sich auch, folgende Informationen zu lesen JonesForth . Dies ist sowohl ein funktionierender Interpreter als auch ein Tutorial und ich kann es nicht genug empfehlen, um darüber nachzudenken, "wie Dinge ausgeführt werden" und wie man eine einfache, leichte Sprache schreibt.