Grundlagen der Programmierung 2 (Comp-A) Prof. Dr. Manfred Schmidt-Schauß Künstliche Intelligenz und Softwaretechnologie 16. Mai 2012 Compiler; Übersetzungsprogramme Ein Übersetzer (Compiler) ist ein Programm, das ein Wort einer formalen Sprache S1 (den Quelltext) in ein Wort einer anderen formalen Sprache S2 (den Zieltext) umwandelt, wobei die Semantik erhalten bleibt. Beispiele: Programme in Haskell, Java, Python, oder PASCAL, werden in Maschinenkode, C, Assembler, oder Bytecode übersetzt. LaTex-Eingaben werden in eine PDF-Datei (bzw. ps-Datei) übersetzt Grundlagen der Programmierung 2 (Comp-A) - 1 - Compiler für Programmiersprachen Ein Compiler ist ein Programm, das ein S1-Programm (das Quellprogramm) in ein S2-Programm (das Zielprogramm) umwandelt, wobei die (operationale) Semantik erhalten bleibt Grundlagen der Programmierung 2 (Comp-A) - 2 - T-Diagramme nach N. Wirth S1 → S2 S3 S1 → S2 S3 S3 Intp Übersetzung von S1 nach S2 Programm ist geschrieben in S3 Im rechten Diagramm: ein Interpreter für S3 ist vorhanden. ⇒ Compiler kann ausgeführt werden. Grundlagen der Programmierung 2 (Comp-A) - 3 - Zusammensetzen von Compilern: S1 → S2 → S2 S4 S5 S4 Intp S5 Intp S3 Hintereinanderausführung: übersetzt S1 → S3, falls S4 und S5-Interpreter vorhanden sind. Grundlagen der Programmierung 2 (Comp-A) - 4 - Zusammensetzen von Compilern: S1 → S2 S4 S4 → S6 S1 → S5 S5 S2 S5 Intp S6 Intp das rechteste (blaue) T-Diagramm steht für den resultierenden Compiler Konstruierbar: lauffähiger Übersetzer von S1 nach S2 der nur einen S5-Interpreter benötigt Grundlagen der Programmierung 2 (Comp-A) - 5 - T-Diagramme; Anwendung: Bootstrapping Konstruktion eines Compilers für Programmiersprache Pneu möglichst viel bereits in Pneu geschrieben. 1. 2. 3. PL Pneu ,Core -Compiler für einen kleinen Ausschnitt Pneu,L-Übersetzer nach Pneu ,Core , geschrieben in Pneu ,Core Pneu,L- Compiler zusammensetzen ; (Resultat: blau) → PCore PCore PCore → PL → PCore PCore → C C C C C Intp C Intp C Intp C PL → C C Intp Grundlagen der Programmierung 2 (Comp-A) - 6 - C Anwendung der T-Diagramme: Bootstrapping Darauf aufbauend, kann man weiter konstruieren, Resultat dunkelblau PXXL → PL PL PL PXXL → → C PL PL → C PXXL → C C C C Intp C Intp C Intp C C Intp Resultat: Ein Compiler, der die volle Sprache PXXL übersetzt. und bei dem ein großer Teil in PL geschrieben ist. Grundlagen der Programmierung 2 (Comp-A) - 7 - C Phasen eines Compilers Lexikalische Analyse (Scanning): Text des Programms wird in Tokenstrom umgewandelt Syntaxanalyse (Parsing): Tokenstrom wird entsprechend der Grammatik in einen Syntaxbaum transformiert Semantische Analyse: Typcheck, Scope von Variablen, . . . Zwischencode-Erzeugung: Generierung von Code für eine abstrakte Maschine Codeoptimierung: Verbesserung des Zwischencodes Code-Erzeugung: Erzeugung des Ausgabe-Programms (z.B. in C, Assembler) Grundlagen der Programmierung 2 (Comp-A) - 8 - Beispiele zu den Phasen Lexikalische Analyse Z.B. IF 13 == X1 THEN A “ wird zu: ” (’IF’, 13, ==, ’X1’, ’THEN’,’A’) Syntaxanalyse (Parsing): Test, ob das Programm der Grammatik entspricht. Umwandlung in Herleitungsbaum, dann in Syntaxbaum: Z.B. (’IF’, 13, ==, ’X1’, ’THEN’, ’A’) wird zu: ii iiitittt i i i iiii tttt iiii i i tt i tii ytt II uu II uu II u u II uu I$ u zu 0 IF0 • 13 == IFTHEN NNN ooo N • LULULULULUUUUU 0 X10 LLL UUUUUU UUUU L& UUUU UU* 0 THEN0 0 A0 oo ooo o o o w oo o OOO OOO OOO OOO O' NNN NNN NN& == ww 13 ww ww w {w w 0 A0 0 X10 Semantische Analyse Typüberprüfungen : Z.B. 1 + ’a’ wird zurückgewiesen. Sind Variablen / Funktionen / Klassen an der richtigen Stelle deklariert ? Grundlagen der Programmierung 2 (Comp-A) - 9 - Weitere Komponenten Symboltabelle speichert wesentliche Attribute (Typ, Gültigkeitsbereich, etc.) der Bezeichner des Quellprogramms Fehlerbehandlung Analyse der Fehler, Lokalisierung, Meldung an den Benutzer Evtl. automatische Behebung des Fehlers und Fortsetzung der Übersetzung Grundlagen der Programmierung 2 (Comp-A) - 10 - Weitere mögliche Phasen Einleseroutine; Zeichenbehandlung abhängig vom Prozessor bzw. Betriebssystem I.a. Standard-Routinen (Einlesen, Zeichenkodierung) Präprozessor Vorverarbeitung des Quellprogramms (i.a. Makro-Expansion) Assembler übersetzt das Assemblerprogramm in Objektcode. Binder/Lader Umsetzung in ausführbaren Maschinenkode plus Hinzunahme von anderem Objektcode z.B. aus Bibliothek Grundlagen der Programmierung 2 (Comp-A) - 11 - Front-End und Back-End Aufteilung des Compilers: Front-End: maschinenunabhängiger Teil (z.B. Syntaxanalyse, lexikalische Analyse, Typüberprüfung, Zwischencode-Erzeugung, Zwischencode-Optimierung). Back-End: maschinenspezifischer Teil (z. B. I/O, Codegenerierung) Grundlagen der Programmierung 2 (Comp-A) - 12 - Worte, Zeichenketten, formale Sprachen Gegeben: ein Alphabet Σ Definitionen: • Eine Zeichenkette (String) ist endliche Folge von Σ-Symbolen • ε bezeichnet den leeren String • Σ∗: die Menge aller Worte über Σ • formale Sprache über Σ: Teilmenge von Σ∗ Beispiele Σ = {a, b, c}, ac, aaaabcc sind Strings über Σ. Grundlagen der Programmierung 2 (Comp-A) - 13 - Beispiele • Σ = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}: Die Ziffernrepräsentationen aller natürlichen Zahlen ist eine formale Sprache über Σ. Z.B. 111, 7, 3210999, ... • Σ = {A, . . . , Z, a, . . . , z, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, ), (, . . .}. Alle möglichen Haskell-Programme (als Strings) sind eine formale Sprache über Σ. Z.B. main = map quadrat [] Grundlagen der Programmierung 2 (Comp-A) - 14 - Kontextfreie Grammatik Kontextfreie Grammatik: ein Formalismus zum Erzeugen von Mengen von Worten über einem Alphabet Bestandteile einer kontextfreien Grammatik: Nichtterminale N Terminale (bzw. Alphabet) T (Σ) Produktionen (Regeln) P Startsymbol S Regeln haben die Form N ::= w, w ist ein Wort aus Nichtterminalen und Terminalen. Die Sprache der Grammatik besteht aus allen vom Startsymbol S aus erreichbaren Worten, die nur aus T -Symbolen bestehen. Grundlagen der Programmierung 2 (Comp-A) - 15 - Grammatik Beispiele Grammatik zur Erzeugung aller Klammerausdrücke aus + und x: S ::= x | (S) | (S + S) erzeugte Worte; Beispiele: x“ ” (x)“ ” ((x))“ ” ... (x+x)“ ” (x+(x))“ ” (x+((x)))“ ” ((((x)))+(x))“ ” (((x+(x))+x)+((((x)))))“ ” ... Grundlagen der Programmierung 2 (Comp-A) - 16 - Reguläre formale Sprachen: Formalismen Beschreibungsmöglichkeiten für reguläre formale Sprachen: • reguläre Ausdrücke • (rechts- bzw. links-)reguläre Grammatik • DEA (deterministischer endlicher Automat): akzeptierte Sprache • NEA (nicht-deterministischer endlicher Automat): akzeptierte Sprache (sind alle äquivalent in folgendem Sinn: die gleichen formalen Sprachen sind damit definierbar) Grundlagen der Programmierung 2 (Comp-A) - 17 - Reguläre Ausdrücke Σ sei das Alphabet. R ::= a (R) ε RR R + R R∗ für a ∈ Σ Leeres Wort Konkatinierte Ausdrücke Auswahl bzw. Vereinigung beliebige Anzahl Konkatinierungen Grundlagen der Programmierung 2 (Comp-A) - 18 - Reguläre Ausdrücke: Beispiele 0+(1+2+3+4+5+6+7+8+9)(0+1+2+3+4+5+6+7+8+9)∗ positive ganze Zahlen (als Text) (A + ... + Z)(A + ... + Z + a + ... + z)∗ Worte, die mit Großbuchstaben beginnen (A + ... + Z)(A + ... + Z + a + ... + z)(A + ... + Z + a + ... + z) Worte aus drei Buchstaben, die mit Großbuchstaben beginnen Grundlagen der Programmierung 2 (Comp-A) - 19 - Lexikalische Analyse (Tokenizer, Scanner) Aufgabenteilung: Zuerst Dann Scanner Parser (lokale) Zeichen (Symbol-)Analyse Syntax-Analyse Aufgaben des Scanners: • • • Erkennung von: Zahlen, Bezeichner, Namen, Schlüsselworte, Strings d.h. reguläre Ausdrücke Aufbau der Symboltabelle Zeichenstrom → Tokenstrom und Weitergabe an den Parser Es gibt Scanner-Generatoren (Z.B. lex). Allerdings ist es oft einfacher, einen Scanner selbst zu schreiben. Z.B. unter Benutzung endlicher Automaten. Grundlagen der Programmierung 2 (Comp-A) - 20 - Scanner: Etwas genauer • • • • Einlesen des Programmtexts als lineare Zeichenkette Beseitigung von Kommentaren, Leer- und Tabulatorzeichen. Zusammenfassen von Teilstrings (Schlüsselworte, Bezeichner, Zahlenwerte, u ä) Ergebnis: Strom von Token Ein Token kann bestehen aus: Markierung: Attribute: Üblich: Art des Tokens wie String, Zahlwert, Position im Eingabefile. Eigenes Endetoken für Eingabestrom. Grundlagen der Programmierung 2 (Comp-A) - 21 - Beispiel für Tokens Eingabe if 123 x1 ∗ <= Tokenmarkierung Keyword Num Id Mult Relop Grundlagen der Programmierung 2 (Comp-A) Attribut if 123 ’x1’ Code(∗) Code(<=) Position in der Eingabe 113 500 1001 2000 (501,30) - 22 - Grammatik-Zerlegung: Tokenizer Zahl Ziffer Bezeichner Alphanum Parser E T F ::= ::= ::= ::= ::= ::= ::= Ziffer | ZifferZahl 0|1|2|3|4|5|6|7|8|9 Char | Char Alphanum Ziffer | Char E + T | T T ∗ F | F (E) | Zahl | Bezeichner Parseralphabet = {Tokenmarkierungen} Parseralphabet im Beispiel: {+,*,Zahl, Bezeichner,),( } Der Parser behandelt alle Bezeichner als ein einziges Terminal Grundlagen der Programmierung 2 (Comp-A) - 23 - Compiler-Phasen: Struktur und Typen Typen, in Haskell-Notation: Scanner: String -> [Token] det. Teil-Parser: [Token] -> ([Token], Syntaxbaum) Teil-Parser mit Backtracking [Token] -> [([Token], Syntaxbaum)] Gesamt-Parser: [Token] -> Syntaxbaum Grundlagen der Programmierung 2 (Comp-A) - 24 - Fehlererkennung des Scanners • • • • falsche Zahlen Z.B. 123.a falsche Escape-Folgen in Strings ein fehlendes String-Endezeichen, oder evtl. Längenüberschreitungen. Bezeichner, die nicht der Konvention entsprechen Ungültige Symbole in bestimmten Kontexten Nicht erkennbar für den Scanner: • Klammerfehler, • Klammerfehler bzgl. (tief) geschachtelter Kommentare • falsche Schlüsselworte. Grundlagen der Programmierung 2 (Comp-A) - 25 - Scannen von Kommentaren und Strings String im Eingabestrom: = Unterstring mit linkem und rechtem Begrenzungssymbol ”. Bekannte Problematik: String im String? Beachte: ” trennt, erzeugt aber keine Klammerstruktur sinnvolle Lösungsalternativen: • Escape-symbol \ als Kommando: \” im String → ” \\ im String → \ • Verdopplung: ”” im String → ” Nachteil: Exponentiell große Strings könnten in der Eingabe erforderlich sein abhängig von der Tiefe bzw. der Anzahl Scanner-Durchgänge. Der Scanner darf keine Leerzeichen in Strings entfernen Grundlagen der Programmierung 2 (Comp-A) - 26 - Scannen von Kommentaren: Zeilenkommentare Anfangssymbol z.B. * oder ; oder % oder −− oder //. geklammerte Kommentare Kommentar-Begrenzer: Z.B. Anfang: /∗ , Ende: ∗/. Aktuelle Programmiersprachen: nur eine Klammerebene; Echte Kommentar-Schachtelung muss vom Parser erkannt werden. Beachte Zeilenkommentar im String sind Stringzeichen. Grundlagen der Programmierung 2 (Comp-A) - 27 -