Home > Informatik > Formale Sprachen> Folge 25

25.1 Was ist ein Compiler?

25.1 - 25.2 - 25.3 - 25.4 - 25.5 - 25.6 - 25.7

Arbeitsphasen eines Compilers

Ein Compiler ist ein Programm, das einen Text einer Quellsprache (zum Beispiel Java) in einen Text einer Zielsprache (zum Beispiel Stackcode, Assemblercode oder Maschinencode) übersetzt. Bei diesem Übersetzungsvorgang, dem Compilieren oder Kompilieren, arbeitet ein Compiler in mehreren Durchgängen[1][2][3]:

Analysephase (Front-End)
Synthesephase (Back-End)

Analysephase

Lexikalische Analyse

In der Java-Programmzeile

umfang = radius^2 * 3,14; 

würde die lexikalische Analyse zwei Fehler finden:

  1. der Bezeichner "radius^2" enthält ein ungültiges Zeichen, nämlich "^",
  2. die Real-Zahl "3,14" enthält keinen Punkt, sondern ein Komma.

In der lexikalischen Analyse wird also festgestellt, ob Bezeichner, Zahlen, Schlüsselworte etc. richtig geschrieben sind. Die lexikalische Analyse ist mit der Rechtschreibkorrektur eines Textverarbeitungsprogramms vergleichbar.

Erzeugung eines Tokenstroms

Aber das ist noch nicht alles! Der lexikalische Analysator oder kurz Lexer eines Compilers "übersetzt" den Quellcode (Java, C++ etc.) in eine Sequenz von Token, in einen Tokenstrom. Ein Token besteht aus dem Token-Namen und dem Token-Wert. Die Zahl 3.14 wird zum Beispiel in ein Token mit dem Namen "num" (für number) und dem Wert 3.14 übersetzt. Die Variable "umfang" wird in ein Token mit dem Namen "id" (für identifier = Variable) und dem Wert "umfang" übersetzt. Das Gleichheitszeichen wird in ein Token mit dem Namen "op" (für operator) und dem Wert "=" übersetzt und so weiter. Dieser Tokenstrom dient dann als Eingabe für die nächste "Abteilung" des Compilers, die syntaktische Analyse.

Syntaktische Analyse

Eine lexikalisch korrekte Programm-Zeile wie z.B.

umfang = radius*radius * 3.14*

wird von dem Lexer in einen Tokenstrom wie

< id "umfang" >
< op "=" > 
< id "radius" >
< op "*" >
< id "radius" >
< op "*" > 
< num 3.14 >
< op "*" >

umgewandelt. Die syntaktische Analyse meldet nun aber einen syntaktischen Fehler, da auf das letzte Multiplikationszeichen keine Zahl (num) oder Variable (id) mehr folgt. Die syntaktische Analyse ist also mit der Grammatikprüfung eines Textverarbeitungsprogramms vergleichbar. Das Satz "Ich bin gestern nach." ist syntaktisch falsch, da ein wichtiger Satzteil fehlt, nämlich die entscheidende Information, wohin ich gegangen bin.

Semantische Analyse

Die Anweisung

a = 3 * wurzel(5);

ist lexikalisch und syntaktisch völlig korrekt, kann semantisch dagegen falsch sein, wenn z.B. die Methode wurzel nicht definiert wurde oder wenn der Rückgabewert der sondierenden Methode wurzelein String und keine double-Zahl ist.

Textverarbeitungsprogramme verfügen zur Zeit noch über keine semantische Korrektur, was aber vielleicht auch gar nicht gewünscht wird. Würde man z.B. eine Bundestags-Rede durch eine solche semantische Korrektur jagen, so blieben wahrscheinlich nur noch 10% des ursprünglichen Textes übrig. Die restlichen 90% würden als "überflüssig, unwichtig, widersprüchlich, nichtssagend oder redundant" gestrichen werden. Auch die drei letzten Sätze dieses Abschnitts würden wahrscheinlich von der semantischen Analyse stark zusammengestaucht werden.

Synthesephase

Zwischencode-Erzeugung
Betrachten wir die Programm-Zeile
p = i + r * 60;

Wir gehen mal davon aus, dass alle drei Analysephasen keinen Fehler gefunden haben. Der vom Compiler erzeugte Zwischencode könnte dann so aussehen:

temp1 := 60
temp2 := id3 * temp1
temp3 := id2 + temp2
id1 := temp3

temp1, temp2 und temp3 sind dabei temporäre Variablen, id1, id2 und id3 sollen die in der Analysephase erkannten Variablen sein.

Der Zwischencode mancher Compiler ist völlig unabhängig von der Maschine, auf der der endgültige Maschinencode ausgeführt werden soll, es ist quasi ein abstrakter, universeller Code.

Zwischencode-Optimierung

Die Zwischencode-Optimierung beseitigt unnötige Codezeilen und ordnet den Code vielleicht auch etwas um, so dass er schneller ausgeführt werden kann. Nach der Optimierung könnte der obige Zwischencode vielleicht so aussehen:

temp1 = id3 * 60
id1 = id2 + temp1

Der Code wurde also von vier Zeilen auf zwei Zeilen reduziert, die aber das Gleiche leisten.

Maschinencode-Erzeugung
Im letzten Teil des Übersetzungsvorganges, der Code-Generierung, wird aus dem optimierten Zwischencode der endgültige maschinennahe Code erzeugt, der z.B. wie folgt aussehen könnte:
MOV id3,R2
MUL 60,R2
MOV id2,R1
ADD R2,R1
MOV R1,id1

Bei dem oben gezeigten Code handelt es sich nicht um die endgültige, prozessorabhängige Maschinensprache, sondern um eine maschinennahe Assemblersprache. Diese Form wurde hier nur der besseren Lesbarkeit wegen verwendet; ein richtiger Compiler erzeugt keine Assemblersprache, sondern echte Maschinensprache, die nur aus Nullen und Einsen besteht.

Seitenanfang -
25.1 - 25.2 - 25.3 - 25.4 - 25.5 - 25.6 - 25.7