Grundlagen der Programmierung 3 A - Compiler A: Phasen Lexikalische Analyse; Scanner
←
→
Transkription von Seiteninhalten
Wenn Ihr Browser die Seite nicht korrekt rendert, bitte, lesen Sie den Inhalt der Seite unten
Grundlagen der Programmierung 3 A Compiler A: Phasen Lexikalische Analyse; Scanner Prof. Dr. Manfred Schmidt-Schauß Sommersemester 2018
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. (Semantik meist separat erklärt) 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 (Compiler-A) – 2/21 –
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 (Compiler-A) – 3/21 –
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 (Compiler-A) – 4/21 –
Phasen eines Compilers: Diagramm Lexikalische Quelltext (String) Analyse Tokenstrom Syntaktische Analyse Syntaxbaum Semantische Analyse Syntaxbaum (annotiert) Zwischencode- erzeugung Zwischencode Code- optimierung Zwischencode (optimiert) Code- generierung maschinennaher Code Grundlagen der Programmierung 2 (Compiler-A) – 5/21 –
Beispiele zu den Phasen Lexikalische Analyse Z.B. IF 13 == X1 THEN A “ wird zu: ” ’IF’, 13, ==, ’X1’, ’THEN’,’A’ Grundlagen der Programmierung 2 (Compiler-A) – 6/21 –
Beispiele zu den Phasen Lexikalische Analyse Z.B. IF 13 == X1 THEN A “ wird zu: ” ’IF’, 13, ==, ’X1’, ’THEN’,’A’ Syntaxanalyse (Parsing): Programmtext anhand Grammatik strukturieren. Erstellen Herleitungsbaum, dann Syntaxbaum: Z.B. (’IF’, 13, ==, ’X1’, ’THEN’, ’A’) wird zu: • IFTHEN % 0 IF0 u •{ 0 THEN0 *0 A0 x == & 0 0 A { # 0 } & 13 == X10 13 0 X10 Grundlagen der Programmierung 2 (Compiler-A) – 6/21 –
Beispiele zu den Phasen Lexikalische Analyse Z.B. IF 13 == X1 THEN A “ wird zu: ” ’IF’, 13, ==, ’X1’, ’THEN’,’A’ Syntaxanalyse (Parsing): Programmtext anhand Grammatik strukturieren. Erstellen Herleitungsbaum, dann Syntaxbaum: Z.B. (’IF’, 13, ==, ’X1’, ’THEN’, ’A’) wird zu: • IFTHEN % 0 IF0 u •{ 0 THEN0 *0 A0 x == & 0 0 A { # 0 } & 13 == X10 13 0 X10 Semantische Analyse Typüberprüfungen: Z.B. 1 + ’a’ wird zurückgewiesen. Scopeprüfungen: Sind Variablen/Funktionen/. . . definiert? Sind sie an der richtigen Stelle deklariert? Grundlagen der Programmierung 2 (Compiler-A) – 6/21 –
Weitere Komponenten Symboltabelle speichert wesentliche Attribute (Typ, Gültigkeitsbereich, etc.) der Bezeichner des Quellprogramms. Fehlerbehandlung Analyse der Fehler, Lokalisierung im Quellprogramm, Meldung an den Benutzer. Evtl. automatische Behebung des Fehlers und Fortsetzung der Übersetzung Grundlagen der Programmierung 2 (Compiler-A) – 7/21 –
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 (Compiler-A) – 8/21 –
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 (Compiler-A) – 9/21 –
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 Σ: ist definiert als eine Teilmenge von Σ∗ Beispiele Σ = {a, b, c}, ac, cbaaaabcc sind Strings über Σ. Grundlagen der Programmierung 2 (Compiler-A) – 10/21 –
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. die Zeichenfolge: main = map quadrat [] Grundlagen der Programmierung 2 (Compiler-A) – 11/21 –
Reguläre formale Sprachen: Formalismen Beschreibungsmöglichkeiten für reguläre formale Sprachen: • reguläre Ausdrücke • (rechts- bzw. links-)reguläre Grammatik (nur ein Nichtterminal am rechten bzw. linken Ende von rechten Seiten von Produktionen erlaubt) • DEA (deterministischer endlicher Automat): akzeptierte Sprache • NEA (nicht-deterministischer endlicher Automat): akzeptierte Sprache Diese sind alle äquivalent in folgendem Sinn: die gleichen formalen Sprachen sind damit definierbar Grundlagen der Programmierung 2 (Compiler-A) – 12/21 –
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) Grundlagen der Programmierung 2 (Compiler-A) – 13/21 –
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 Grundlagen der Programmierung 2 (Compiler-A) – 13/21 –
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 Nicht regulär: alle geklammerten Ausdrücke wie (1 + ((2 + 3) + 9) Grundlagen der Programmierung 2 (Compiler-A) – 13/21 –
Lexikalische Analyse (Tokenizer, Scanner) Aufgaben-Aufteilung des Compilers: Zuerst Scanner Zeichen und Symbol-Analyse für reguläre formale Sprache Dann Parser Syntax-Analyse für kontextfreie formale Sprache Aufgaben des Scanners: • Erkennung von: Zahlen, Bezeichner, Namen, Schlüsselworte, Strings • 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 (Compiler-A) – 14/21 –
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: Art des Tokens (Schlüsselwort, Name, Zahl,. . . ) Attribute: wie String, Zahlwert, Position im Eingabefile. Üblich: Eigenes Ende-Token für Eingabestrom. Grundlagen der Programmierung 2 (Compiler-A) – 15/21 –
Beispiel für Tokens Eingabe Tokenmarkierung Attribut Position in der Eingabe if Keyword if 113 123 Num 123 500 x1 Id ’x1’ 1001 ∗ Mult Code(∗) 2000
Grammatik-Zerlegung: Zahl ::= Ziffer | Ziffer Zahl Ziffer ::= 0|1|2|3|4|5|6|7|8|9 Scanner Bezeichner ::= Char | Char RestBez ::= Alphanum RestBez | Alphanum RestBez ::= Ziffer | Char Alphanum E ::= E+T | T Parser T ::= T∗F | F ::= (E) | Zahl | Bezeichner F Parseralphabet = {Tokenmarkierungen} Parseralphabet im Beispiel: {+,*,Zahl, Bezeichner,),( } Der Parser behandelt alle Bezeichner als ein einziges Terminal Grundlagen der Programmierung 2 (Compiler-A) – 17/21 –
Compiler-Phasen: Struktur und Typen Typen der Funktionen, in Haskell-Notation: Scanner: String -> [Token] deterministischer [Token] -> ([Token], Syntaxbaum) Teil-Parser: Tokenliste -> (Rest der Tokenliste, Syntaxbaum der konsumierten Tokenliste) Teil-Parser [Token] -> [([Token], Syntaxbaum)] mit Backtracking Tokenliste -> Liste der Möglichkeiten Gesamt-Parser: [Token] -> Syntaxbaum Grundlagen der Programmierung 2 (Compiler-A) – 18/21 –
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 (Compiler-A) – 19/21 –
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 (Compiler-A) – 20/21 –
Scannen von Kommentaren: Zeilen- Anfangssymbol kommentare z.B. * oder ; oder % oder −− oder //. geklammerte Kommentar-Begrenzer: Kommentare 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 (Compiler-A) – 21/21 –
Sie können auch lesen