6. Sprachübersetzer (Compiler) - pi4.informatik.uni...
Transcript of 6. Sprachübersetzer (Compiler) - pi4.informatik.uni...
Praktische Informatik II 6. Sprachübersetzer 6a - 1© Wolfgang Effelsberg
6. Sprachübersetzer (Compiler)
6.1 Syntaxdefinition einer Sprache
6.2 Aufbau und Wirkungsweise eines Compilers
• Lexikalische Analyse
• Syntaxanalyse
• Code-Erzeugung
6.3 Automatisches Generieren von Parsern
6.4 Interpretation vs. Kompilierung vs. Java-Bytecode
Praktische Informatik II 6. Sprachübersetzer 6a - 2© Wolfgang Effelsberg
6.1 Syntaxdefinition einer Sprache
Definitionen
• Alphabet: eine gegebene Zeichenmenge
• Wort: Zeichenfolge aus einem Alphabet
• Sprache: Menge von Wörtern
• Grammatik: Formalismus, um die Syntax einer Sprache festzulegen
Eine Grammatik G ist ein Tupel G = (VT, VN, R, S) mit
• VT: Menge der Terminalzeichen (Token)
• VN: Menge der Nonterminalzeichen
• R: Menge der Produktionsregeln
• S: einem Startzeichen.
Die Sprache zu einer Grammatik ist die Menge der herleitbaren Wörter. Die Produktions-regeln (oder kurz: Produktionen) sind die Bildungsregeln für gültige Worte.
Praktische Informatik II 6. Sprachübersetzer 6a - 3© Wolfgang Effelsberg
Produktionsregeln
Zur formalen Definition von Produktionsregeln sind zwei Verfahren gebräuchlich:
a) die Backus/Naur-Form
b) Syntax-Diagramme (z. B. im Pascal-Report)
Beispiel
a) Backus/Naur-Form (BNF): Personenname → Nachname Vorname {Vorname} Anmerkung: „{ ... }“ bedeutet null, eine oder mehrere Wiederholungen
b) Syntax-Diagramm:
Personenname
Nachname Vorname
Praktische Informatik II 6. Sprachübersetzer 6a - 4© Wolfgang Effelsberg
Beispiel 1: BNF für einfache arithmetische Ausdrücke
start → expr
expr → expr + term
expr → expr - term
expr → term
term → 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9
“|“ bedeutet dabei "oder"
Praktische Informatik II 6. Sprachübersetzer 6a - 5© Wolfgang Effelsberg
Beispiel 2: Ausschnitt aus der BNF-Definition einerProgrammiersprache
Anweisung → Abfrage | Schleife | Zuweisung
Abfrage → Falls Bedingung dann Anweisung"Abfrage" steht hier für "bedingte Anweisung"
Schleife → Solange Bedingung führe aus Anweisung
Zuweisung → Übertrage Ausdruck nach Name
Ausdruck → Name Operator Name
Operator → + | -
Bedingung → Name Relation Nummer
Relation → = | ≠
...
...
Praktische Informatik II 6. Sprachübersetzer 6a - 6© Wolfgang Effelsberg
Beispiel 3 für eine BNF-Definition
Syntax eines Telefonbucheintrags
Eintrag → Personenname Adresse Nummer
Personenname → Nachname Vorname {Vorname}
Nachname → Name
Vorname → Eigentlicher-Vorname | Initiale
Eigentlicher-Vorname → Name
Initiale → Buchstabe .Adresse → Straßenname Nummer
Straßenname → Name
Buchstabe → A | B | C | ... | Z
Praktische Informatik II 6. Sprachübersetzer 6a - 7© Wolfgang Effelsberg
6.2 Aufbau und Wirkungsweise eines Compilers
Bei der Kompilation gibt es drei Hauptphasen:
Literaturempfehlung
Aho, Alfred V., Sethi, Ravi, Ullman, Jeffrey D.: Compilerbau. 2 Bände, Oldenbourg-Verlag, 1997
Quellprogrammin höhererProgrammier-sprache
Objektpro-gramm inMaschinen-sprache
LEXIKALISCHE ANALYSE
SYNTAX-ANALYSE
CODE-ERZEUGUNG
Fehlerbericht Fehlerbericht Fehlerbericht
Praktische Informatik II 6. Sprachübersetzer 6a - 8© Wolfgang Effelsberg
Die Phasen im Detail (1)
1 Lexikalische AnalyseEingabe ist das Quellprogramm in Form einer Zeichenkette.Das Quellprogramm wird in eine Folge von Tokens aufgebrochen.Überflüssige Zwischenräume und Kommentare werden entfernt.
2 Syntax-AnalyseErkennen der Strukturbausteine des Programms.Die syntaktische Struktur wird mittels der Produktionsregel der Sprache bestimmt, d.h. es werden entsprechende syntaktische Einheiten gebildet (z.B. "Faktor", "Term", "Ausdruck", "Anweisung")
2a Semantische AnalysePrüfung auf semantische Fehler, insbesondere Typprüfungen, gegebenenfalls Typ-wandlung.
Praktische Informatik II 6. Sprachübersetzer 6a - 9© Wolfgang Effelsberg
Die Phasen im Detail (2)
3 CodegenerierungFür jede syntaktische Einheit des Programms werden entsprechende Maschinenanwei-sungen generiert. Das Resultat ist das Objektprogramm.
3a Optimierung (in manchen Compilern)Entfernen redundanter Anweisungen, Verwenden von Registern statt Speicherstellen für Zwischenvariable, Verkleinern von Schleifen durch Herausziehen invarianter Anweisun-gen usw.
Praktische Informatik II 6. Sprachübersetzer 6a - 10© Wolfgang Effelsberg
Die Lexikalische Analyse (1)
Der Programmtext wird Zeichen für Zeichen bearbeitet, und der Analysator
• bestimmt, ob ein Zeichen ein terminales Symbol (z.B. + oder - ) ist oder ob es mit seinem Nachbarn zur Bildung eines "größeren" terminalen Symbols wie einem Namen, einer Zahl oder einem reservierten Wort zusammengefasst werden kann,
• entfernt syntaktisch überflüssige Zeichen, wie mehrfache Zwischenräume (Leerzeichen) und eingeschobene Kommentare für den menschlichen Leser,
• weist unzulässige Zeichen im Quellprogramm als Fehler zurück.
Praktische Informatik II 6. Sprachübersetzer 6a - 11© Wolfgang Effelsberg
Lexikalische Analyse (2)
Terminale Symbole werden vom lexikalischen Analysator an den Syntaxanalysator in Form von Tokens weitergegeben. Jedes Token repräsentiert ein besonderes terminales Symbol.
Ein Token hat zwei Komponenten:
1) seinen Typ, der angibt welche Art eines terminalen Symbols das Token enthält (z.B. falls, +, Zahl),
2) seinen Wert, der angibt, welchen Wert das terminale Symbol erhält. So ist der Wert eines Zahlen-Tokens einfach die Zahl, die es enthält, und der Wert eines Namens-Token ist der Name, den es enthält. (Tatsächlich sind die Namens- und Zahlen-token die einzigen hier erwähnten Arten, die sinnvollerweise einen Wert annehmen. Die Werte der anderen Tokens können als undefiniert betrachtet werden.)
Praktische Informatik II 6. Sprachübersetzer 6a - 12© Wolfgang Effelsberg
Die Syntaxanalyse
Eine Folge von korrekten Wörtern ergibt nicht immer einen korrekten Satz.
Eine Folge von korrekten Tokens ergibt nicht immer eine korrekte Anweisung.
Beispiele
x = y + 3; ist syntaktisch korrekt
x = y + 3 *; ist syntaktisch falsch
z = $300.-; wird schon bei der lexikalischen Analyse als inkorrekt erkannt
Praktische Informatik II 6. Sprachübersetzer 6a - 13© Wolfgang Effelsberg
Zerlegungsbaum (Parse-Tree)
Ein Zerlegungsbaum veranschaulicht den Herleitungsvorgang für ein Wort in einer gege-benen Grammatik. Ein Zerlegungsbaum ist ein Baum mit den folgenden Eigenschaften:
(1) Die Wurzel ist das Startzeichen.
(2) Jedes Blatt ist ein Token.
(3) Jeder innere Knoten ist ein Nonterminalzeichen.
(4) Sei A ein innerer Knoten und seien x1,x2,...,xN die Nachfolger von A in der Reihen-folge von links nach rechts. Es existiert dann eine Produktionsregel A → x1x2...xNin der Grammatik.
Praktische Informatik II 6. Sprachübersetzer 6a - 14© Wolfgang Effelsberg
Zerlegungsbaum, Beispiel 1: arithmetischer Ausdruck
9 - 5 + 2 start
expr
expr +
expr term-
term
9
5
2
term
Praktische Informatik II 6. Sprachübersetzer 6a - 15© Wolfgang Effelsberg
Zerlegungsbaum, Beispiel 2: Anweisung
Anweisung
Abfrage
Anweisung
ZuweisungBedingung
Ausdruck
OperatorRelation
Falls Name Nummer dann Übertrage Name + Name Nach Name(x) (1) (x) (y) (y)
Praktische Informatik II 6. Sprachübersetzer 6a - 16© Wolfgang Effelsberg
Zerlegungsbaum, Beispiel 3: Telefonbucheintrag
EINTRAG
PERSONENNAME ADRESSE
NACHNAME VORNAME VORNAME
EIGENTLICHER INITIALEVORNAME
STRASSEN-NAME
BUCHSTABE
NAME NAME B NAME NUMMER NUMME(Schmidt) (Johann) (Hauptstraße) (97) (51374)
Praktische Informatik II 6. Sprachübersetzer 6a - 17© Wolfgang Effelsberg
Aufbau des Zerlegungsbaums
Zerlegung (parsing)
Unter Zerlegung (parsing) versteht man den Prozess der Entscheidung, ob eine Folge von Tokens durch die gegebene Grammatik erzeugt werden kann. Dazu wird ein Zerlegungs-baum (parse tree) aufgebaut.
Es gibt verschiedene Techniken zum Aufbau eines Zerlegungsbaums für eine gegebene Folge von Tokens. Man unterscheidet die Top-Down- und die Bottom-Up-Zerlegung:
• Die Top-Down-Zerlegung beginnt mit dem Startsymbol.
• Die Bottom-Up-Zerlegung mit dem zu analysierenden Wort der Eingabesprache.
Praktische Informatik II 6. Sprachübersetzer 6a - 18© Wolfgang Effelsberg
Beispiel: Bottom-Up-Zerlegung
Grammatik: S → aABe Eingabe: abbcde Ziel: SA → Abc | bB → d
Es existiert eine große Klasse von Bottom-Up berechenbaren Grammatiken sowie Software-Werkzeuge für die automatische Zerlegerkonstruktion.
abbcde
aAbcde
aAde
aABe
aAAcde
aAAcBe
S
A -> b
A -> b
B -> dB -> d
S -> aABe
A -> Abc
Backtracking!
Praktische Informatik II 6. Sprachübersetzer 6a - 19© Wolfgang Effelsberg
Top-Down-Zerlegung
• Beginn mit dem Startsymbol
• Prüfen der Eingabe von links nach rechts, um die passenden Produktionen zu erkennen
• Um auf der Ebene i Produktionen korrekt zuzuordnen, muss die nächsttiefere Ebene untersucht werden usw., rekursiv, bis zu den Blättern des Baumes.
• Dabei Aufzeichnen des bisherigen Pfades durch den Syntax-Baum
• Falls eine "Sackgasse" erkannt wird, ist "Back-tracking" erforderlich
Vorteil
Der Algorithmus für die Top-Down-Zerlegung oft sehr einfach aus den Produktionsregeln herleitbar.
Praktische Informatik II 6. Sprachübersetzer 6a - 20© Wolfgang Effelsberg
Beispiel: Top-Down-Zerlegung (1)
Produktion: Anweisung → Abfrage | Schleife | Zuweisung
Algorithmus
Modul Parsing-Anweisung
{Skizze eines Moduls für das Parsing einer Anweisung in unserer einfachen Programmiersprache.}
Parsing-Abfrage
Falls nicht erfolgreich
dann Parsing-Schleife
Falls nicht erfolgreich
dann Parsing-Zuweisung
Falls nicht erfolgreich
dann Fehlermeldung
Praktische Informatik II 6. Sprachübersetzer 6a - 21© Wolfgang Effelsberg
Beispiel: Top-Down-Zerlegung (2)
Modul Parsing-Zuweisung{Skizze eines Moduls für das Parsing einer Zuweisung. Fehler in einem der Schritte führen zur Fehlermeldung beim rufenden Modul.}
Prüfe, ob der Typ des nächsten Token “Übertrage" istParsing-AusdruckPrüfe, ob der Typ des nächsten Tokens "nach" istPrüfe, ob der Typ des nächsten Tokens "Name" ist(die übrigen Module analog dazu)
Anmerkung 1Die Struktur der Module entspricht der Struktur der Produktionen!
Anmerkung 2Rekursion ist zulässig und sehr häufig!
Anweisung → SchleifeSchleife → Solange Bedingung
führe aus AnweisungDies führt zu rekursiven Modulaufrufen für “Anweisung“.
Praktische Informatik II 6. Sprachübersetzer 6a - 22© Wolfgang Effelsberg
Zerlegung (Parsing) und Sprachentwurf
Beim Entwurf von Programmiersprachen wird bereits darauf geachtet, dass eine einfache Zerlegung durch den Compiler möglich ist. Insbesondere sollte Backtracking beim Parsingmöglichst selten vorkommen.
Im einfachsten Fall entscheidet das jeweils nächste Symbol (Token) im Eingabestrom des Parsers, wie die weitere Zerlegung zu erfolgen hat (lookahead = 1). Dann gibt es überhaupt kein Backtracking. Eine so aufgebaute Grammatik führt zu besonders effizienten Parsern.
Beispiel: die Sprache Basic: Es gibt ein eindeutiges Schlüsselwort zu Beginn jeder Anwei-sung.
Eine weitere Überlegung zielt auf die Verbesserung der Fehlererkennung ab. So können z. B. Trennzeichen (als terminale Symbole) eingeführt werden, die das Lokalisieren von Syntax-fehlern erleichtern.
Praktische Informatik II 6. Sprachübersetzer 6a - 23© Wolfgang Effelsberg
Beispiel für einen Syntaxfehler
Statt Jordan Maria Hochweg 42 67431
wird Jordan Maria Hochweg 67431
eingegeben.
Zerlegungsbaum:
"Hochweg" wird zunächst als zweiter Vorname interpretiert, der Fehler erst beim dann fehlenden Straßennamen entdeckt.
Abhilfe:Komma als Trennzeichen vorschreiben: Jordan Maria, Hochweg, 67431Dies erleichtert die frühzeitige Erkennung von Syntaxfehlern.
EINTRAG
PERSONENNAME ADRESSE
ZUNAME VORNAME VORNAME STRASSEN- NAME
EIGENTLICHER VORNAME
EIGENTLICHER VORNAME
NAME(Jordan)
NAME(Maria)
NAME(Hochweg)
NAME ??
NUMME(67421)
Praktische Informatik II 6. Sprachübersetzer 6a - 24© Wolfgang Effelsberg
Code-Erzeugung
Die Phase der Code-Erzeugung umfasst
• die Zuweisung von Hauptspeicherplatz zu den statischen Datenelementen des Pro-gramms,
• die Generierung der Maschineninstruktionen für die ausführbaren Anweisungen,
• die Generierung von externen Referenzen zur Verwendung durch den Binder (“linkageeditor“)
• zu den Laufzeitroutinen für Standard-Unterprogramme (Prozeduren und Funktio-nen bzw. Methoden (zum Beispiel "read", "sin")
• zu separat übersetzten Unterprogrammen.
Der Zerlegungsbaum (Parse Tree) wird als Parameter an den Code-Generator über-geben.
Für jeden Anweisungstyp gibt es Routinen zur Code-Erzeugung. Sie werden auch als "se-mantische Routinen" oder "semantische Aktionen" bezeichnet, da sie eine bedeutungs-gerechte Umsetzung der programmiersprachlichen Anweisungen in Maschineninstruktionen gewährleisten.
Praktische Informatik II 6. Sprachübersetzer 6a - 25© Wolfgang Effelsberg
Symboltabelle
Die Symboltabelle enthält die Zuordnung von Namen zu Speicheradressen. Sie
• wird während der lexikalischen Analyse aufgebaut und steht allen Phasen des Compilers zur Verfügung.
• enthält Namen, Datentyp und Hauptspeicheradresse jedes im Programm vorkommenden Datenelements.
• dient bei der Erzeugung des Maschinencodes zur Umsetzung von Namen in Adressen.
• wird nach Abschluss der Übersetzung überflüssig. Ausnahmen:• symbolische Debugger brauchen sie zur Laufzeit,• externe Referenzen, die erst beim Binden aufgelöst werden, müssen Speicher-
adressen in den „fremden“ Modulen zugeordnet werden können.
Praktische Informatik II 6. Sprachübersetzer 6a - 26© Wolfgang Effelsberg
Syntaxgesteuerte Übersetzung (1)
Von einer Eingabesprache wird in eine Ausgabesprache übersetzt, zum Beispiel in einen Zwischencode (z. B. Java Bytecode), Assembler oder direkt in binäre Maschineninstruk-tionen.
Die syntaktische Struktur der Eingabesprache wird mittels einer Grammatik spezifiziert.
Den Produktionsregeln werden semantische Aktionen zugewiesen. Die semantischen Akti-onen berechnen Werte, meist vom Typ String, für die Nonterminale.
Die Übersetzung ist dann eine Abbildung wie folgt:
• Sei I eine Eingabe:
• Erzeuge den Zerlegungsbaum für I.
• Sei x das Nonterminalzeichen eines Knotens des Zerlegungsbaums. Berechne den Wert von x gemäß der semantischen Aktion, die dem Knoten zugewiesen ist.
• Der Wert des Startzeichens ist die Ausgabe, d.h. die Übersetzung von I (dies ist in der Regel noch nicht das endgültige Maschinenprogramm.)
Praktische Informatik II 6. Sprachübersetzer 6a - 27© Wolfgang Effelsberg
Syntaxgesteuerte Übersetzung (2)
• Ein Zerlegungsbaum heißt annotiert, wenn den Knoten die entsprechenden semanti-schen Aktionen zur Erzeugung der Ausgabe zugewiesen sind.
• Gleichzeitig mit der Zerlegung wird oft bereits die Übersetzung gemäß den semantischen Aktionen ausgeführt.
Praktische Informatik II 6. Sprachübersetzer 6a - 28© Wolfgang Effelsberg
Beispiel: einfacher arithmetischer Ausdruck (1)
Infixform Postfixform
Produktionsregeln: Semantische Regeln:
start → expr start = expr
expr → expr + term expr = expr term '+'
expr → expr - term expr = expr term '-'
expr → term expr = term
term → 0 term = '0'
.
. .
term → 9 term = 9
Praktische Informatik II 6. Sprachübersetzer 6a - 29© Wolfgang Effelsberg
Beispiel: einfacher arithmetischer Ausdruck (2)
9 - 5 + 2start
expr
expr
expr
+ term
- term 2 {printf(”2”)}
{print (”+”)}
{printf(”-”)}
{printf(”5”)}term 5
9 {printf(”9”)}f
Praktische Informatik II 6. Sprachübersetzer 6a - 30© Wolfgang Effelsberg
Berechnung der Werte der Nonterminale
Häufig ist depth-first-Verarbeitung möglich, d.h. der Zerlegungsbaum wird im "Postorder Traversal" durchlaufen:
PROCEDURE evaluate (X: node);
BEGIN
FOR each child Y of X, from leftto right DO evaluate (Y)
compute the value of X
END;
Beim Erreichen eines Knotens im Zerlegungsbaum wird die zugehörige semantische Aktion "compute the value of X" ausgeführt.
Praktische Informatik II 6. Sprachübersetzer 6a - 31© Wolfgang Effelsberg
Übersetzungsschema
Ein Übersetzungsschema ist eine alternative Darstellung zum annotierten Zerlegungsbaum.
Ein Übersetzungsschema ist eine Grammatik, in die die semantischen Aktionen zur Erzeu-gung der Ausgabe integriert sind.
Beispiel:
start → exprexpr → expr + term {printf ('+')}expr → expr - term {printf ('-')}expr → termterm → 0 {printf ('0')}
.
.term → 9 {printf ('9')}
Die Produktionsregeln wurden um die semantischen Aktionen erweitert.
Bemerkung: Semantische Aktionen können an beliebiger Stelle in den rechten Seiten auf-treten, zum Beispiel rest → + term {printf('+')} rest
Praktische Informatik II 6. Sprachübersetzer 6a - 32© Wolfgang Effelsberg
Beispiel für die Code-Erzeugung (1)
In unserer einfachen Beispielsprache sei ein arithmetischer Ausdruck (expression) wie folgt definiert:
ausdruck → NAME operator NAME
operator → + | -
Wir betrachten die Code-Erzeugung für eine einfache Einadress-Akkumulatormaschine. Der generierte Code hat die Form:
LOAD A1ADD A2
oderLOAD A1SUB A2
Das Resultat steht im Akkumulator.Die Adressen zu den Namen A1 und A2 werden in der Symboltabelle gefunden.Wir wählen die Technik der Top-Down-Zerlegung. Zu den Nonterminalen definieren wir die semantischen Aktionen in Form von Modulen in Pseudocode.
Praktische Informatik II 6. Sprachübersetzer 6a - 33© Wolfgang Effelsberg
Beispiel für die Code-Erzeugung (2)
Semantische Aktion für die Code-Erzeugung für Ausdrücke:Modul Erzeuge:Ausdruck(B)
{Erzeugt den Objektcode für einen Ausdruck in unserer einfachen Sprache. Der Parameter B ist der Zerlegungsbaum des betreffenden Ausdrucks.}
Übertrage die Adresse des Datenelementes, das im linken Teilbaum von B steht, nach A1
Erzeuge „LOAD A1”
Übertrage die Adresse des Datenelementes, das im rechten Teilbaum von B steht, nach A2
Falls der Operator in B ein „+” ist
dann erzeuge „ADD A2”
sonst erzeuge „SUB A2”
Praktische Informatik II 6. Sprachübersetzer 6a - 34© Wolfgang Effelsberg
Beispiel für die Code-Erzeugung (3)
Semantische Aktion für die Code-Erzeugung für Zuweisungen:
("Übertrage Ausdruck nach Name")
Modul Erzeuge:Zuweisung(B)
{Erzeugt den Objektcode für eine Zuweisung in unserer einfachen Sprache. Der Parameter B ist der Zerlegungsbaum der betreffenden Zuweisung.}
Übertrage die Adresse des Datenelementes, das im „Name“-Teilbaum von B steht, nach A
Erzeuge:Ausdruck(„Ausdruck“-Teilbaum von B)
Erzeuge „STORE A”
Praktische Informatik II 6. Sprachübersetzer 6a - 35© Wolfgang Effelsberg
Beispiel für die Code-Erzeugung (4)
Semantische Aktion für die Code-Erzeugung für Anweisungen:
Modul Erzeuge:Anweisung(B)
{Erzeugt den Objektcode für eine Anweisung in unserer einfachen Sprache. Der Parameter B ist der Zerlegungsbaum der betreffenden Anweisung.}
Falls die Anweisung eine Abfrage ist
dann Erzeuge:Abfrage(Teilbaum von B)
sonst Falls die Anweisung eine Schleife ist
dann Erzeuge:Schleife(Teilbaum von B)
sonst Erzeuge:Zuweisung(Teilbaum von B)
Analog gibt es auch ein Modul für Erzeuge:Bedingung usw.
Praktische Informatik II 6. Sprachübersetzer 6a - 36© Wolfgang Effelsberg
Beispiel eines gesamten Übersetzungsablaufs (1)
Eingabe: Falls X ≠ 1 dann Übertrage X + Y nach Y
1. Lexikalische Analyse (Folge von Tokens)
Falls Name ≠ Zahl dann Übertrage Name(X) (1) (X)
+ Name nach Name(Y) (Y)
Symboltabelle:
Adresse Name Datenelement
100 DC 1
1456 X DS Ganzzahl
1457 Y DS Ganzzahl
Praktische Informatik II 6. Sprachübersetzer 6a - 37© Wolfgang Effelsberg
Beispiel eines gesamten Übersetzungsablaufs (2)
2. Syntaktische Analyse (erstellt Zerlegungsbaum)
Anweisung
Abfrage
Anweisung
ZuweisungBedingung
Ausdruck
OperatorRelation
Falls Name Nummer dann Übertrage Name + Name Nach Name(x) (1) (x) (y) (y)
Praktische Informatik II 6. Sprachübersetzer 6a - 38© Wolfgang Effelsberg
Beispiel eines gesamten Übersetzungsablaufs (3)
3. Code-Erzeugung
Der Zerlegungsbaum wird depth-first (in postorder traversal) abgearbeitet. An jedem Knoten wird die zugeordnete semantische Aktion durch Aufruf des entsprechenden Moduls ausge-führt.
Modul Erzeuge:Bedingung generiert die Maschineninstruktionen
1000 LOAD 1456 ;lade X
1001 SUB 100 ;subtrahiere 1
1002 JZ WEITER ;jump bei Accumulator = 0
Modul Erzeuge:Ausdruck generiert die Maschineninstruktionen
1003 LOAD 1456 ;lade X
1004 ADD 1457 ;addiere Y
Praktische Informatik II 6. Sprachübersetzer 6a - 39© Wolfgang Effelsberg
Beispiel eines gesamten Übersetzungsablaufs (4)
Modul Erzeuge:Zuweisung generiert die Maschineninstruktion
1005 STORE 1457 ;speichere Y
Bisher enthält die Ausgabe also die Instruktionen
1000 LOAD 1456 ;lade X
1001 SUB 100 ;subtrahiere 1
1002 JZ 1006 ;jump bei Accumulator = 0
1003 LOAD 1456 ;lade
1004 ADD 1457 ;addiere Y
1005 STORE 1457 ;speichere Y
1006 ...
Praktische Informatik II 6. Sprachübersetzer 6a - 40© Wolfgang Effelsberg
Beispiel 2: Übersetzung einer Wertzuweisung
Deklaration: float alpha, beta, gamma;
Praktische Informatik II 6. Sprachübersetzer 6a - 41© Wolfgang Effelsberg
Zwischencodegenerierung
Praktische Informatik II 6. Sprachübersetzer 6a - 42© Wolfgang Effelsberg
Optimierung
Optimierende Compiler haben eine weitere Phase, in der sie versuchen, den Objektcode zu optimieren. Dazu ist ein tiefergehendes Verständnis der Programmsemantik erforderlich. Optimierungsmöglichkeiten sind
• Verwendung von Registern statt Hauptspeicherstellen für Zwischenergebnisse
• “Herausziehen“ von invarianten Ausdrücken aus Schleifen.
Java-Beispiel: for (r=1; r<=n; r++){
pi = 3.141592; /* kann herausgezogen werdenf = pi * r * r;
…}
•Vermeidung der Mehrfachauswertung von Ausdrücken
Java-Beispiel: u = 2.0 * pi * r;
f = pi * r * r;
Hier kann das Zwischenergebnis für “pi * r“ zweimal verwendet werden.
Praktische Informatik II 6. Sprachübersetzer 6a - 43© Wolfgang Effelsberg
Automatisches Generieren von Parsern
Beobachtung• Eine Programmiersprache ist durch ihre Produktionen vollständig und formal beschrieben.
• Die Funktionalität eines Parsers (lexikalische und syntaktische Analyse) ist daher im Prinzip durch die Sprachdefinition eindeutig festgelegt.
Konsequenz
• Ein Parser lässt sich allein aus der Sprachdefinition komplett herleiten. Für einfache Sprachen kann dieser Prozess automatisiert werden
Praktische Informatik II 6. Sprachübersetzer 6a - 44© Wolfgang Effelsberg
Parser
Praktische Informatik II 6. Sprachübersetzer 6a - 45© Wolfgang Effelsberg
Compiler-Compiler
• Wenn ergänzend zur Syntax der Sprache auch noch die Zielsprache (Maschinenspra-che) des Compilers angegeben wird, kann man in manchen Fällen auch die Codeerzeu-gungsphase des Compilers automatisch generieren. Einen solchen Generator bezeichnet man als Compiler-Compiler. Schwierig ist allerdings die korrekte semantische Zuordnung von Folgen von Maschineninstruktionen zu den Knoten des Zerlegungsbaumes.
• Werkzeuge zum automatischen Generieren von Compilern werden zum Beispiel mit dem Betriebssystems Unix ausgeliefert (lex, yacc).
• Einsatzgebiete sind das Prototyping von neuen Sprachen, Datenbanksprachen, Kom-mando-Interpreter, Datenstruktur-Beschreibungssprachen usw.
Praktische Informatik II 6. Sprachübersetzer 6b - 1© Wolfgang Effelsberg
Optimierung
Optimierende Compiler haben eine weitere Phase, in der sie versuchen, den Objektcode zu optimieren. Dazu ist ein tiefergehendes Verständnis der Programmsemantik erforderlich. Optimierungsmöglichkeiten sind
• Verwendung von Registern statt Hauptspeicherstellen für Zwischenergebnisse
• “Herausziehen“ von invarianten Ausdrücken aus Schleifen.
Java-Beispiel: for (r=1; r<=n; r++){
pi = 3.141592; /* kann herausgezogen werdenf = pi * r * r;
…}
•Vermeidung der Mehrfachauswertung von Ausdrücken
Java-Beispiel: u = 2.0 * pi * r;
f = pi * r * r;
Hier kann das Zwischenergebnis für “pi * r“ zweimal verwendet werden.
Praktische Informatik II 6. Sprachübersetzer 6b - 2© Wolfgang Effelsberg
Beispiel 1 zur Optimierung (in Java) (1)
void loop(int n){
int s;for (int i=1; i<=n; ++i)s = i + i;
}a) Kompilierung ohne Optimierung
•Parameter n wird in R15 übergeben.•Es werden drei Worte auf dem Stack reserviert und R4 als Zeiger auf die lokalen Variablen verwendet.•@R4 enthält Parameter n•2(R4): Variable s•4(R4): Variable i
Praktische Informatik II 6. Sprachübersetzer 6b - 3© Wolfgang Effelsberg
Beispiel 1 zur Optimierung (in Java) (2)
Ausschnitt aus der kompilierten, nicht optimierten Version...push r4 ; Retten von R4sub #6, r1 ; Anlegen von 3 Worten: n, s, imov r1, r4 ; Verwenden von R4 als "local variables"
mov r15, @r4 ; Speichern von nmov #1, 4(r4) ; Initialisieren von i mit 1
.L2: ; Beginn der for-Schleifecmp 4(r4), @r4 ; Vergleiche i mit njge .L5 ; Falls n >= i, springe in Schleifenrumpfjmp .L1 ; Ansonsten springe zu Ende
.L5:mov 4(r4), r15 ; Lade i in R15add 4(r4), r15 ; Addiere i zu R15mov r15, 2(r4) ; Speichere R15 in sadd #1, 4(r4) ; Erhöhe Zähler i um einsjmp .L2 ; Fahre mit nächstem Schleifendurchlauf fort
.L1:add #6, r1 ; Löschen der lokalen Variablen vom Stackpop r4 ; Wiederherstellen von R4ret ; Rücksprung ...
Praktische Informatik II 6. Sprachübersetzer 6b - 4© Wolfgang Effelsberg
Beispiel 1 zur Optimierung (in Java) (3)
b) Kompilierung mit Optimierung :•Da die Schleife nichts Sinnvolles berechnet oder zurückgibt, wird ihr Rumpf komplett wegoptimiert.•Die Schleife läuft trotzdem n mal durch, wobei n als Zähler verwendet wird und rückwärts zählt.
Ausschnitt aus der kompilierten, optimierten Version:
…cmp #1, r15 ; Vergleiche n mit 1jl .L8 ; Falls n<1, springe zu Ende.L6:add #-1, r15 ; Ziehe eins von n abjne .L6 ; Falls jetzt n!=0, wiederhole.L8:ret …
Praktische Informatik II 6. Sprachübersetzer 6b - 5© Wolfgang Effelsberg
Beispiel 2 zur Optimierung (in Java) (1)
int add(int a, int b){
int c = 2 + a + b;int d = 4 + a + b;return c+d;
}
a) Kompilierung ohne Optimierung•Parameter werden hier in R14 und R15 übergeben.•Es werden vier Worte im Speicher angelegt, und R4 wird als Zeiger auf lokale Variablen verwendet.•@R4: Parameter a•2(R4): Parameter b•4(R4): Variable c•6(R4): Variable d
Praktische Informatik II 6. Sprachübersetzer 6b - 6© Wolfgang Effelsberg
Beispiel 2 zur Optimierung (in Java) (2)
Ausschnitt aus der kompilierten, nicht optimierten Version:
...push r4 ; Retten von R4Sub #8, r1 ; Anlegen von 4 Worten: a, b, c, dmov r1, r4 ; Verwendung von R4 als "local variables„
mov r15, @r4 ; Speichern von erstem Parameter in amov r14, 2(r4) ; Speichern von zweitem Parameter in bmov @r4, r15 ; Lade a in R15add 2(r4), r15 ; Addiere badd #2, r15 ; Addiere 2mov r15, 4(r4) ; Speichere Ergebnis in cmov @r4, r15 ; Lade a in R15add 2(r4), r15 ; Addiere badd #4, r15 ; Addiere 4mov r15, 6(r4) ; Speichere Ergebnis in dmov 4(r4), r15 ; Lade cadd 6(r4), r15 ; Addiere d
add #8, r1 ; Löschen der 4 Wortepop r4 ; Register wieder herstellenRet ; Rücksprung…
Praktische Informatik II 6. Sprachübersetzer 6b - 7© Wolfgang Effelsberg
Beispiel 2 zur Optimierung (in Java) (3)
b) Kompilierung mit Optimierung: Der Ausdruck wird vereinfacht zu (a+b)*2+6
Ausschnitt aus der kompilierten, optimierten Version:...add r14, r15 ; Addiere a und brla r15 ; Multipliziere Ergebnis mit 2add #6, r15 ; Addiere 6 zum Ergebnisret ; Rücksprung...
Praktische Informatik II 6. Sprachübersetzer 6b - 8© Wolfgang Effelsberg
Beispiel 3 zur Optimierung (in Java) (1)
int sum(int n){
int result = 0;for (int i=0; i<=n; ++i)
result += i;return result;
}
a) Kompilierung ohne Optimierung•Übergabe des Parameters n geschieht in R15.•Es werden drei Worte Speicher für lokale Variablen auf dem Stack reserviert.•R4 zeigt auf den Anfang des Bereiches für Variablen.•@R4: Parameter n•2(R4): Variable result•4(R4): Variable i
Praktische Informatik II 6. Sprachübersetzer 6b - 9© Wolfgang Effelsberg
Beispiel 3 zur Optimierung (in Java) (2)
Ausschnitt aus der kompilierten, nicht optimierten Version:
…push r4 ; Retten des Registers R4sub #6, r1 ; Anlegen von 3 Worten: n, result, imov r1,r4 ; Verwendung von R4 als "local variables"
mov r15, @r4 ; Speichern von nmov #0, 2(r4) ; Initialisieren von result mit 0mov #0, 4(r4) ; Initialisieren von i mit 0.L2: ; Beginn der for-Schleifecmp 4(r4), @r4 ; Vergleiche i mit njge .L5 ; Ist n >= i, springe in die Schleifejmp .L3 ; Ansonsten springe ans Ende.L5:add 4(r4), 2(r4) ; Addiere i zu resultadd #1, 4(r4) ; Erhöhen des Zählers ijmp .L2 ; Sprung zum nächsten Schleifendurchlauf.L3:mov 2(r4), r15 ; Übergeben den Wert von result in R15
add #6, r1 ; Löschen der lokalen Variablenpop r4 ; Wiederherstellen von R4ret ; Rücksprung…
Praktische Informatik II 6. Sprachübersetzer 6b - 10© Wolfgang Effelsberg
Beispiel 3 zur Optimierung (in Java) (3)
b) Kompilierung mit Optimierung :•Die Schleife wurde ersetzt durch eine do-while-Schleife mit anfänglicher Abfrage, ob n kleiner als null ist.•Alle Werte werden diesmal in Registern gehalten:•R15: enthält zu Beginn Parameter n, wird später für result verwendet.•R14: Variable i•R13: Kopie des Parameters n
Ausschnitt aus der kompilierten, optimierten Version:...mov r15, r13 ; Verschiebe Parameter n nach R13mov #0, r15 ; Initialisiere result mit 0mov #0, r14 ; Initialisiere i mit 0cmp #0, r13 ; Vergleiche n mit 0jl .L8 ; Falls n<0, springe sofort zum Ende.L6:add r14, r15 ; Addiere i zu resultadd #1, r14 ; Erhöhe Zähler i um einscmp r14, r13 ; Vergleiche i mit njge .L6 ; Falls n<=i, erneuter Schleifendurchlauf.L8:ret...
Praktische Informatik II 6. Sprachübersetzer 6b - 11© Wolfgang Effelsberg
6.3 Automatisches Generieren von Parsern
Beobachtung
• Eine Programmiersprache ist durch Ihre Produktionen vollständig und formal beschrieben.
• Die Funktionalität eines Parsers (lexikalische und syntaktische Analyse) ist daher im Prinzip durch die Sprachdefinition eindeutig festgelegt.
Konsequenz
• Ein Parser lässt sich allein aus der Sprachdefinition komplett herleiten. Für einfache Sprachen kann dieser Prozess automatisiert werden
Praktische Informatik II 6. Sprachübersetzer 6b - 12© Wolfgang Effelsberg
Parser
Sprachdefinitionfür L in BNF
Parsergenerator
Parser für L
Praktische Informatik II 6. Sprachübersetzer 6b - 13© Wolfgang Effelsberg
Compiler-Compiler
• Wenn ergänzend zur Syntax der Sprache auch noch die Zielsprache (Maschinensprache) des Compilers angegeben wird, kann man in manchen Fällen auch die Codeerzeugungsphase des Compilers automatisch generieren. Einen solchen Generator bezeichnet man als Compiler-Compiler. Schwierig ist allerdings die korrekte semantische Zuordnung von Folgen von Maschineninstruktionen zu den Knoten des Zerlegungsbaumes.
• Werkzeuge zum Compiler-Generieren werden zum Beispiel mit dem Betriebssystems Unix ausgeliefert (lex, yacc).
• Einsatzgebiete sind das Prototyping für neue Sprachen, Datenbanksprachen, Kommando-Interpreter, DatenstrukturBeschreibungssprachen usw.
Praktische Informatik II 6. Sprachübersetzer 6b - 14© Wolfgang Effelsberg
6.4 Interpretation vs. Kompilierung vs. Java Bytecode
Interpretation
• Verarbeitung des Programms durch einen Interpreter
• schrittweise Ausführung des Programms, jede Anweisung ist ein Schritt
• syntaktische Analyse der Anweisung; falls korrekt, Verzweigung zu einem Inter-pretationsmodul. Keine Erzeugung von Maschinencode für die Anweisungen!
• Wegen des hohen Aufwands während der Verarbeitung nur zweckmäßig für einfache Sprachen (z. B. BASIC: erstes Wort jeder Anweisung ist Schlüsselwort, verzweigt zum Interpretationsmodul)
• Vorteil: schnelleres Testen, schnelle Ausführung von einfachen, kurzen Programmen, Fehleranalyse auf Quellcode-Ebene leicht
• Nachteile: Bei wiederholter Ausführung einer Anweisung, z.B. in Schleifen oder Unter-programmen, muss die Syntaxanalyse jeweils neu durchgeführt werden. "Code-Opti-mierung" nicht möglich, da Interpretationsmodule allgemeingültig sein müssen.
Praktische Informatik II 6. Sprachübersetzer 6b - 15© Wolfgang Effelsberg
Algorithmus für die Interpretation
Beginne mit dem Anfang des Programms
Wiederhole
Analysiere die Syntax der nächsten Anweisung, um ihren Typ und ihre Operanden zu bestimmen
Falls kein Syntaxfehler vorliegt
dann rufe den Modul für diesen Anweisungstyp mit den Operanden als Parameter auf
sonst melde einen Fehler
Bis das Ende des Programms erreicht ist oder ein Syntaxfehler auftritt
Praktische Informatik II 6. Sprachübersetzer 6b - 16© Wolfgang Effelsberg
Kompilierung
• Übersetzung aus einer höheren Programmiersprache in Maschinensprache durch einen Compiler
• Das Programm wird als Ganzes vor der Ausführung übersetzt, die Maschinenbefehle als "Objektprogramm" (Objektcode) abgespeichert.
• Auch bei wiederholter Ausführung von Anweisungen (Schleifen) erfolgt die Übersetzung nur einmal (vorab). Das gesamte Objektprogramm kann immer wieder geladen und gestartet werden, ohne neu übersetzt zu werden.
Vorteile
• Effizienter, schneller Programmcode (optimiert)• Separate Übersetzung von Modulen möglich• Komplexe Programmiersprachen möglich
Nachteile
• Mehr Aufwand für einfache, kurze Programme.• Der Compiler selbst ist ein großes, komplexes Stück Software• Fehleranalysen für Laufzeitfehler erschwert, da Quellprogramm zur Laufzeit nicht
verfügbar.
Praktische Informatik II 6. Sprachübersetzer 6b - 17© Wolfgang Effelsberg
Ablauf der Kompilierung
Praktische Informatik II 6. Sprachübersetzer 6b - 18© Wolfgang Effelsberg
Java Bytecode
Java Bytecode ist ein maschinenunabhängiger Zwischencode. Aus der Sprache Java wird in Bytecode kompiliert. Der Bytecode wird dann auf der Zielmaschine interpretiert.
Praktische Informatik II 6. Sprachübersetzer 6b - 19© Wolfgang Effelsberg
Zusammenfassung
• Die Kompilation besteht aus den Schritten Lexikalische Analyse, Semantische Analyse und Codeerzeugung.
• In der Codeerzeugung lässt sich der Grad der Codeoptimierung wählen.
• Neben der Kompilation gibt es für einfache Sprachen noch die Interpretation, bei der der Code der höheren Programmiersprache direkt interpretiert wird. Es werden keine Maschi-nenbefehle erzeugt.
• Aus der Sprache Java wird in der Regel Bytecode kompiliert, der dann auf der Zielma-schine interpretiert wird.