Compiler
Compile-Prozess

Compiler-Prozess: Quelltext
- Definition: Der Quelltext ist der vom Programmierer geschriebene Code, in einer höheren Programmiersprache.
- Bedeutung: Er ist der Ausgangspunkt des Compilationsprozesses.
-
Beispiel: Ein
.c
File für ein C-Programm oder ein.java
File für ein Java-Programm.
Compiler-Prozess: Lexikalische Analyse
- Definition: Die lexikalische Analyse zerlegt den Quelltext in Tokens.
- Bedeutung: Tokens sind die Grundbausteine des Quellcodes, wie Schlüsselwörter, Identifikatoren, Literale und Operatoren.
- Werkzeuge: Scanner oder Lexer.
Lexikalische Analyse
Die erste Phase des Compiler-Designs, bei der der Quelltext in Tokens zerlegt wird.
Beispiel für Lexikalische Analyse
Gegeben sei der Pseudocode:
function add(x, y) {
return x + y;
}
1. `function` - Schlüsselwort
2. `add` - Identifikator
3. `(` - Symbol
4. `x` - Identifikator
5. `,` - Symbol
6. `y` - Identifikator
7. `)` - Symbol
8. `{` - Symbol
9. `return` - Schlüsselwort
10. `x` - Identifikator
11. `+` - Operator
12. `y` - Identifikator
13. `;` - Symbol
14. `}` - Symbol
function add(x, y) {
return x + y;
}
Der obige Code wird in folgende Tokens aufgeteilt:
Beispiel für Lexikalische Analyse
Compiler-Prozess: Syntaktische Analyse
- Definition: Die syntaktische Analyse prüft, ob die Tokens den syntaktischen Regeln der Programmiersprache entsprechen.
- Ergebnis: Erstellung eines Syntaxbaums, der die Struktur des Codes repräsentiert.
- Werkzeuge: Parser.
Syntaxanalyse (Parsing)
Die zweite Phase des Compiler-Designs, bei der die Tokens auf ihre syntaktische Struktur geprüft werden.
Syntaxbaum für das Beispiel
Für den Pseudocode:
function add(x, y) {
return x + y;
}
ergibt sich folgender Syntaxbaum:
function
|
add
|
------------------------------
| |
params body
| |
------- ---------
| | | |
x y return expression
|
---------
| |
x y
Compiler-Prozess: Semantische Analyse
- Definition: Überprüfung der semantischen Korrektheit des Quelltextes.
- Bedeutung: Sicherstellung, dass der Code Sinn ergibt und ausführbar ist.
- Ergebnis: Anreicherung des Syntaxbaums mit semantischen Informationen.
Semantische Analyse für das Beispiel
Für den Pseudocode:
function add(x, y) {
return x + y;
}
führt die semantische Analyse folgende Prüfungen durch:
-
Variablendeklaration: Sind x und y deklariert?
-
Typüberprüfung: Können x und y addiert werden?
-
Rückgabetyp: Entspricht der Typ des Ausdrucks
x + y
dem erwarteten Rückgabetyp?
Semantische Analyse
Die semantische Analyse stellt sicher, dass der Quellcode nicht nur syntaktisch korrekt, sondern auch logisch sinnvoll ist. Es wird überprüft, ob Operationen, Datentypen, Variablen und andere Programmstrukturen in einem sinnvollen und zulässigen Zusammenhang stehen.
Überprüfung der Variablen- und Funktionsdeklarationen
- Ziel: Sicherstellen, dass alle Variablen und Funktionen vor ihrer Verwendung deklariert wurden.
-
Beispiel: Wenn eine Variable
x
in einem Ausdruck verwendet wird, überprüft der Compiler, obx
zuvor deklariert wurde.
Typüberprüfung (Type Checking)
- Ziel: Sicherstellen, dass die Datentypen in Ausdrücken und Anweisungen kompatibel sind.
-
Beispiel: Überprüfen, ob ein arithmetischer Operator wie
+
nur auf numerische Typen angewendet wird. Wennx
eine Ganzzahl (int
) undy
ein String (string
) ist, würde der Ausdruckx + y
einen Fehler verursachen.
Überprüfung der Typkompatibilität bei Zuweisungen
- Ziel: Sicherstellen, dass der Wert, der einer Variable zugewiesen wird, mit dem Typ der Variable kompatibel ist.
-
Beispiel: Einem
int
-Typ kann keinfloat
-Wert ohne Konvertierung zugewiesen werden.
Überprüfung der Funktionsaufrufe
- Ziel: Sicherstellen, dass Funktionsaufrufe mit der korrekten Anzahl und Art von Argumenten erfolgen.
- Beispiel: Eine Funktion, die zwei Parameter erwartet, sollte nicht mit drei Argumenten aufgerufen werden.
Überprüfung der Rückgabewerte
- Ziel: Sicherstellen, dass die Rückgabewerte von Funktionen mit dem deklarierten Rückgabetyp der Funktion übereinstimmen.
-
Beispiel: Eine Funktion, die als Rückgabewert
int
deklariert ist, sollte keinenstring
-Wert zurückgeben.
Namensauflösung (Name Resolution)
- Ziel: Überprüfen, ob alle Variablen, Funktionen und Methoden korrekt benannt und aufgelöst sind, d.h., dass sie im richtigen Gültigkeitsbereich deklariert sind.
-
Beispiel: Wenn eine Variable
x
innerhalb einer Funktion verwendet wird, muss sichergestellt werden, dass sie in einem sichtbaren Gültigkeitsbereich deklariert wurde.
Kontextabhängige Überprüfungen
-
Ziel: Überprüfen von Regeln, die vom Kontext abhängen, wie etwa dass ein
break
-Befehl nur innerhalb einer Schleife gültig ist. -
Beispiel: Sicherstellen, dass eine
return
-Anweisung nur innerhalb einer Funktion steht.
Verwendung einer Symboltabelle
- Ziel: Während der semantischen Analyse greift der Compiler auf eine Symboltabelle zu, in der Informationen über alle Bezeichner gespeichert sind, einschließlich ihrer Typen, Gültigkeitsbereiche und Speicherorte.
- Beispiel für eine Symboltabelle:
Name | Typ | Speicherort | Gültigkeitsbereich |
---|---|---|---|
x | int | Register 1 | Funktion add
|
y | int | Register 2 | Funktion add
|
z | int | Register 3 | Funktion main
|
Erstellung von Annotationsbäumen (Decorated Trees)
- Ziel: Der Compiler versieht den Syntaxbaum mit zusätzlichen semantischen Informationen, wie z.B. Typinformationen und Gültigkeitsbereichen.
- Beispiel für einen Decorated Tree:
function (add)
|
add
|
--------------------------------
| |
params:(int x, int y) body
| |
------- ---------
| | | |
int x int y return expression
|
---------
| |
int x int y
Compiler-Prozess: Zwischencode
- Definition: Transformation des angereicherten Syntaxbaums in eine abstrakte Maschinensprache.
- Bedeutung: Der Zwischencode ist unabhängig von der Zielplattform und ermöglicht Optimierungen.
- Beispiel: Drei-Adress-Code, Bytecode.
Zwischencode
Der Zwischencode ist eine maschinenunabhängige Darstellung des Quellcodes, die oft nach der semantischen Analyse generiert wird.
Beispiel für Zwischencode
Basierend auf unserem Funktionssyntaxbaum könnte der Zwischencode in Drei-Adress-Code wie folgt aussehen:
func add:
param x
param y
t1 = x + y
ret t1
Compiler-Prozess: Codeoptimierung
- Definition: Verbesserung des Zwischencodes zur Steigerung der Effizienz.
- Ziel: Reduzierung der Laufzeit und des Speicherverbrauchs des Programms.
- Techniken: Dead Code Elimination, Loop Unrolling.
Compiler-Prozess: Codeerzeugung
- Definition: Umwandlung des optimierten Zwischencodes in Zielcode.
- Bedeutung: Der Zielcode ist spezifisch für die Maschine oder das Betriebssystem.
- Beispiel: Maschinencode für eine x86-CPU.
Compiler-Prozess: Zielcode
- Definition: Der finale, ausführbare Maschinencode.
- Bedeutung: Dies ist der Code, der von der CPU direkt ausgeführt wird.
- Wichtig: Der Zielcode muss effizient und fehlerfrei sein.
Zusammenfassung der semantischen Analyse
- Die semantische Analyse stellt sicher, dass der Code sinnvoll und korrekt ist, bevor er in maschinenlesbaren Code übersetzt wird.
- Fehler in dieser Phase führen in der Regel zu semantischen Fehlermeldungen, die auf logische Inkonsistenzen oder falsche Typen hinweisen.
15 Compiler
By Harald Haberstroh
15 Compiler
- 108