Grundlagen der Programmiersprachen und Compiler
|
|
- Willi Förstner
- vor 7 Jahren
- Abrufe
Transkript
1 Grundlagen der Programmiersprachen und Compiler Version: WS 08/09 Prof. Erhard Plödereder E. Ploedereder, Seite 1
2 1. Compiler im Überblick Aufgaben eines Compilers Quellprogramm COMPILER Zielprogramm Meldungen Quellprogramm Zielprogramm in einer höheren Programmiersprache (Pascal, Modula, Ada,... ) in einer maschinenorientierten Programmiersprache (Assembler, verschiebbarer Maschinencode,... ) Meldungen Hinweise auf Fehler Hinweise auf Probleme (unnötige Zuweisungen, fehlende Zuweisungen,... ) Tabellen (Querverweise zwischen Vereinbarungen und Benutzungen) E. Ploedereder, Seite 2
3 Laufzeitsystem Routinen, die vom übersetzten Code aufgerufen werden, ohne vom Übersetzer generiert zu sein größtenteils vom BS bereitgestellt Umfang ist stark sprachabhängig. übersetzter Code Laufzeitsystem Betriebssystem E. Ploedereder, Seite 3
4 Grobmodell eines Compilers Quellprogramm ANALYSE Das Zwischenprogramm beschreibt die Struktur des Quellprogramms. Zwischenprogramm Die Synthese orientiert sich an dieser Struktur. SYNTHESE Zielprogramm E. Ploedereder, Seite 4
5 Feinmodell eines Compilers I Quellprogramm lexikalische Analyse syntaktische Analyse semantische Analyse A N A L Y S E Tabellenverwaltung Fehlerbehandlung Zwischencodeerzeugung Codeoptimierung Codeerzeugung S Y N T H E S E E. Ploedereder, Seite 5
6 ... einige Begriffe Phase: logische Gruppierung der Algorithmen, die auf dem Eingabeprogramm ablaufen Pass: Einmaliger Durchlauf und Bearbeitung des Eingabe- bzw. Zwischenprogramms sowie Erzeugung eines Ausgabe- bzw. Zwischenprogramms Ein-Pass Compiler: Übersetzungsvorgang in einem Pass. Ausgabecode wird quasi-synchron mit dem Einlesen des Quellprogramms erzeugt. gesamter Übersetzer, aber nur kleine Teile des Eingabeprogramms im Speicher Probleme: Vorwärtsbezüge im Eingabeprogramm, nur minimale Code- Optimierung E. Ploedereder, Seite 6
7 Mehr-Pass Compiler: durchläuft das Programm in Zwischenformen evtl. nur der Einzelpass im Speicher gesamtes Eingabeprogramm in Zwischenform im Hauptspeicher oder alternativ in Dateien abgespeichert bessere Möglichkeiten der Programmanalyse und Code-Optimierung Routine-at-a-time Ansatz: Speicheraufwändige Optimierungsphasen laufen pro Routine des Eingabeprogramms besseres Speicherverhalten Probleme: Mehr Verwaltungsaufwand, keine inter-prozeduralen Optimierung, verschachtelte Unterprogramme schwierig E. Ploedereder, Seite 7
8 Feinmodell eines Compilers II Abhängigkeiten Zielsprache Quellprogramm lexikalische Analyse syntaktische Analyse semantische Analyse Exemplarische Pass-Struktur A N A L Y S E Quellsprache Zwischencode- Erzeugung Codeoptimierung Codeerzeugung S Y N T H E S E + E. Ploedereder, Seite 8
9 Symboltabelle enthält Informationen über Bezeichner, die vom Programmierer eingeführt werden Format, z.b. BEZEICHNER.... Beispiel: 1) var ZAHL : REAL; Attribute von ZAHL: Typ relative Adresse innerhalb des umschließenden Unterprogramms Gültigkeitsbereich usw. ATTRIBUTE... 2) var MATRIX : array [1..10, 1..20] of CHAR Attribute von MATRIX: Typ array... Dimensionen Anz. der Zeilen, Typ des Zeilenindex, Anz. der Spalten, Typ des Spaltenindex Typ der Komponenten siehe links, usw. E. Ploedereder, Seite 9
10 Fehlerbehandlung Ziel der Fehlerbehandlung ist die Entdeckung aller Fehler Beschreibung der Fehler Behebung der Fehler für eine Fortsetzung der Übersetzung E. Ploedereder, Seite 10
11 Lexikalische Analyse Eingabe: Folge von einzelnen Zeichen Buchstaben Ziffern Leerzeichen Zeilenende Operatorsymbole Interpunktionszeichen Ausgabe: Gruppe zusammenhöriger Zeichen (lexikalische Elemente, Lexeme, Token) Bezeichner Schlüsselworte Operatoren : = <= <>... Zahlen Zeichenketten ein_string Zusätzliche Aktionen: Aufbau der Token-Tabelle Entfernen von Kommentaren Entfernen überflüssiger Leerzeichen Fehlerbehandlung E. Ploedereder, Seite 11
12 Syntaktische Analyse Eingabe: Folgen lexikalischer Elemente Ausgabe: Hierarchische Darstellung ( Baum ) von Folgen lexikalischer Elemente Beispiel:... MITTE := (LINKS + RECHTS) div 2... Strukturbaum Zuweisung oder komprimiert zum Syntaxbaum := Bezeichner := Ausdruck Bezeichner div Ausdruck div Zahl + Zahl Bezeichner + Bezeichner Bezeichner Bezeichner Zusätzliche Aktion: Fehlerbehandlung E. Ploedereder, Seite 12
13 Semantische Analyse Eingabe: Hierarchische Darstellung des Quellprogramms Aufgabe: Aufbau der Symboltabelle Überprüfung der Typverträglichkeit Überprüfung von Aspekten, die nicht von der syntaktischen Analyse erfassbar sind, z.b. Deklaration / Verwendung von Bezeichnern Übereinstimmung zwischen formalen und aktuellen Parametern Übereinstimmung zwischen Dimension eines Feldes und Indizierung eines Zugriffs... Ausgabe: Geänderter (attributierter) Struktur- (oder Syntax-) Baum Symboltabelle E. Ploedereder, Seite 13
14 Zwischencode-Erzeugung Eingabe: Strukturbaum (Syntaxbaum) := Ausgabe: Zwischencode Bezeichner: MITTE div Beispiel: 3-Adress-Code HILF_1 := LINKS + RECHTS HILF_2 := HILF_1 div 2 MITTE := HILF_2 Bezeichner: LINKS + Bezeichner: RECHTS Zahl Codeoptimierung Eingabe: Zwischencode (nicht optimiert) Ausgabe: Zwischencode (optimiert) Beispiel: HILF_1 := LINKS + RECHTS MITTE := HILF_1 div 2 E. Ploedereder, Seite 14
15 Codeerzeugung Eingabe: Zwischencode HILF_1 := LINKS + RECHTS MITTE := HILF_1 div 2 Ausgabe: Zielcode Beispiel: 1-Registermaschine Akkumulator LOAD LINKS ADD RECHTS DIV 2 STORE MITTE E. Ploedereder, Seite 15
16 ... noch mehr Begriffe Front-End (FE) : Analyse- und frühe Synthesephasen, die weitgehend abhängig von der Quellsprache und unabhängig von der Zielsprache/Zielmaschine sind. Back-End (BE) : Middle-End (ME) : native Compiler: Cross-Compiler: Rehosting/Portierung: Retargeting: Synthesephasen, die weitgehend von der Zielsprache/Zielmaschine abhängig sind. Der Teil zwischen Front-End und Back-End. Ein Compiler, der auf System X (Architektur und Betriebssystem) läuft und Code für System X erzeugt. Ein Compiler, der auf System X läuft und Code für System Y ( X) erzeugt. Einen Compiler, der auf System X läuft, auf System Y lauffähig machen. Einen Compiler, der Code für System X erzeugt, so umbauen, dass er Code für System Y erzeugt. Ferner die Erzeugung des Laufzeitsystems für System Y. E. Ploedereder, Seite 16
17 Feinmodell eines Compilers III Quellprogramm lexikalische Analyse syntaktische Analyse semantische Analyse A N A L Y S E Front-End (FE) Zwischencode- Erzeugung Codeoptimierung Codeerzeugung Zwischencode S Y N T H E S E Middle-End (ME) Back-End (BE) E. Ploedereder, Seite 17
18 Eine Übersetzer-Familie Front-Ends für verschiedene Programmiersprachen FE FE FE FE ME = Middle- End, nach Sprache und Ziel parametrisiert ME ME ME Zwischensprache ME OPT = Optimierungen, nach Sprache und Ziel parametrisiert OPT CG CG CG CG Codegeneratoren für verschiedene Zielarchitekturen E. Ploedereder, Seite 18
19 Retargeting, Rehosting, Bootstrapping T-Diagramme als Darstellung von Compilern Quellsprache Zielsprache Impl.- sprache Übersetzung des Compilers selbst: Compiler für Q im Quellcode Q I Z I MC Q MC Z Compiler für I im Maschinencode, d.h. ausführbar MC ausführbarer Compiler für Q Z = MC native Compiler Z = MC X MC Cross-Compiler I = Q self-hosted Compiler E. Ploedereder, Seite 19
20 Rehosting Q Z Q Z I I MC B MC B MC X auf System B ausführbarer Q Z Compiler Retargeting Q I Z Q Y I I MC B Q MC B Y MC X Ersetzung der zielabhängigen Komponenten im Compiler auf System B ausführbarer Q Y Compiler E. Ploedereder, Seite 20
21 Retargeting II Im Folgenden wird der Übersetzungsvorgang dargestellt durch Compiler im Quellcode Q B Q I I MC Y MC X MC Y B erzeugter, ausführbarer Compiler Komplikation: Q benötigt ein Laufzeitsystem. Dieses Laufzeitsystem muss zusätzlich zum Compiler in binärer Form für System B verfügbar gemacht werden, damit Q-Programme auf B ausgeführt werden können. Komplikation: I benötigt ein Laufzeitsystem (d.h., der erzeugte Compiler ist erst nach Anbinden des Laufzeitsystems für I ausführbar). Normalerweise wird das Laufzeitsystem (in binärer Form) zusammen mit dem zur Übersetzung des Compilers verwendeten Compilers bereitgestellt. Beachte aber den Fall I=Q im Folgenden... E. Ploedereder, Seite 21
22 Bootstrap Das Übersetzen des Compilers mit sich selbst. Beispiel: Portieren (Rehost + Retarget) eines self-hosted Compilers Q M 2 Q M 2 Q M 1 Q M 2 Q Q M 2 M 2 Q Q Q M 1 M 1 Woher kommt dieser Compiler? M X Produkte Denn (*) Q M 1 Q M 1 ist zur Erzeugung nicht möglich. Q Q M 1 M 1 M 1 Ein benötigtes Laufzeitsystem für Q muss mit dem Cross-Compiler übersetzt werden, da der native Compiler ohne das Laufzeitsystem nicht zur Ausführung gebracht werden kann. E. Ploedereder, Seite 22
23 Herkunft des ursprünglichen Compiler : Q Sub M 1 1) für M i, i > 1, aus einer Portierung wie oben 2) Compiler eines anderen Herstellers (wird wegen (*) nur für die erste Entwicklung benötigt) 3) statt Q M 1 wird zuerst Q M 1 und Q Q Sub I entwickelt für Q Sub Q. Dann Q M 1 Q M 1 Q M 1 Q Q M 1 M 1 Q Sub M 1 Q Sub Q Sub M 1 M 1 I I M 1 M X * Fremdprodukt Die mit * bezeichneten Compiler werden nicht mehr benötigt. M 1 * * E. Ploedereder, Seite 23
24 Automatische Generierung von Compilerkomponenten lexikalische Analyse: Spezifikation der lexikalischen Elemente durch reguläre Ausdrücke Prinzip der Erkennung: endlicher Automat Lexergenerator, der aus der Spezifikation Code und Tabellen für die lexikalische Analyse erzeugt. syntaktische Analyse: Spezifikation der syntaktischen Elemente: BNF bzw. EBNF Prinzip der Erkennung: Kellerautomat Parsergenerator, der aus der Spezifikation Code und Tabellen für die syntaktische Analyse erzeugt. Lexer- und Parsergeneratoren sind Standardwerkzeuge des Informatikers, speziell im praktischen Compilerbau, aber auch für sonstige Anwendungen die formale Texte bearbeiten. weitere Compiler-spezifische Werkzeuge: Attributgrammatiksysteme ( semantische Analyse), Mustererkennungssysteme in der Optimierung und Codegenerierung, wiederverwendbare Komponenten E. Ploedereder, Seite 24
25 2. Lexikalische Analyse (LEXER) Aufgaben Erkennung von lexikalischen Elementen Erstellung einer internen Darstellung für lexikalische Elemente Einordnung Lexer ist i.allg. Unterprogramm des Parsers Liefere nächstes lex. Quellprogramm Element LEXER PARSER Strukturbaum Nächstes lex. Element Eintragen Tokentabelle Referenzen Erweiterungen Implementierung von Makro-Techniken Fehlerbehandlung (Einfügen, Löschen, Vertauschen von Zeichen) z.b.: -7.6F+8-7.6E+8 E. Ploedereder, Seite 25
26 Struktur der lexikalische Analyse Eingabe-Behandlung Zeichen Scanner Abgrenzung lexikalischer Elemente Sifter Token Parser E. Ploedereder, Seite 26
27 Pufferung der Eingabe für Scanner Scanner liest Quellprogramm zeichenweise Zeichenweise Eingabe kostet Zeit Scanner muss von Zeit zu Zeit ein bereits gelesenes Zeichen zurückgeben Das Quellprogramm wird blockweise (z.b Zeichen) in den Arbeitsspeicher geladen Quellprogramm Puffer Scanner arbeitet hier Lexikalische Elemente werden im Puffer mit Hilfe zweier Zeiger eingegrenzt. Beispiel:..... R E P E A T R E A D vorher: ANFANG ENDE..... R E P E A T R E A D später: ANFANG ENDE..... R E P E A T R E A D Rücksetzen: ANFANG ENDE REPEAT ist nächstes lexikalisches Element E. Ploedereder, Seite 27
28 (Fortsetzung)..... R E P E A T R E A D ANFANG ENDE Problem: Ende des Puffers erreicht..... I N F O R M A T ANFANG ENDE Laden des Puffers würde momentanen Inhalt löschen. Lösung: 2 Puffer verwenden PUFFER_1 PUFFER_2 I N F O R M A T I K ANFANG ENDE PUFFER_2 laden, falls Ende von PUFFER_1 erreicht PUFFER_1 laden, falls Ende von PUFFER_2 erreicht ENDE ANFANG E. Ploedereder, Seite 28
29 Methoden für die Implementierung von Scannern 1) Programmierung von Hand, z.b. C, Assembler, PASCAL 2) Scanner-Generator, z.b. LEX, welcher als Eingabe eine Spezifikation lexikalischer Elemente erwartet und daraus einen Scanner generiert. Wichtig! Der Scanner muss schnell sein Formale Beschreibung lexikalischer Elemente Lexikalische Elemente sind Worte über einem Alphabet bzw. Zeichensatz Beispiel: PASCAL Bezeichner sind Worte über dem Alphabet {a,..., z, A,..., Z, 0,..., 9} evtl. noch { _ }, die mit einem Buchstaben beginnen. gültige Bezeichner: anna BERTA jane_tarzan diana_1 E. Ploedereder, Seite 29
30 Begriffe Sprache: Menge von Worten Konkatenation: Zusammenfügen von Worten Leere Zeichenkette: Wort, bestehend aus 0 Zeichen. Darstellung i.allg.: ε Vereinigung von Sprachen: L 1 L 2 = { u u L 1 u L 2 } Konkatenation von Sprachen: L 1 L 2 = { uv u L 1, v L 2 } HAUS BOOT...Wort... ε HAUSBOOT...Wort... Kleene-Abschluss von Sprachen: L * = { u i... u j u i,..., u j L, i j } { ε } Positiver Abschluss von Sprachen: L + = { u i... u j u i,..., u j L, i j } E. Ploedereder, Seite 30
31 Beispiele: 1) L = { a, b,....., z } L* = {ε,... Worte bestehend aus Kleinbuchstaben... } 2) L 1 = { A,..., Z} L 2 = { 0,..., 9 } L 2 + = natürliche Zahlen (mit führenden Nullen) L 1 L 2 + = Worte der Form X n Großbuchstabe natürliche Zahl E. Ploedereder, Seite 31
32 Reguläre Ausdrücke Formalismus für die Beschreibung von Sprachen gutes Hilfsmittel für die Beschreibung lexikalischer Elemente in Programmiersprachen (1) ε ist ein reguärer Ausdruck, bezeichnet die Sprache { ε } (2) Jedes Symbol s des gegebenen Alphabets ist ein regulärer Ausdruck, bezeichnet die Sprache { s } (3) Angenommen r und s sind reguläre Ausdrücke, welche die Sprachen L r und L s bezeichnen. Dann lassen sich folgende reguläre Ausdrücke bilden (r) (s) bezeichnet L r L s (r)(s) bezeichnet L r L s (r)* bezeichnet L r * Abkürzungen: (Entfernen von Klammern) * hat höchste Priorität, linksassoziativ Konkatenation hat zweithöchste Priorität, linksassoziativ hat geringste Priorität, linksassoziativ E. Ploedereder, Seite 32
33 Vereinfachung: (Benennung von regulären Ausdrücken) Eine reguläre Definition hat die Form Name d 1 r 1 regulärer Ausdruck über einem Alphabet A Name d 2 r 2 regulärer Ausdruck über einem Alphabet A und d 1 Name d 3 r 3 regulärer Ausdruck über einem Alphabet A, d 1, d 2 usw. Beispiele: 1) Bezeichner: letter a b..... z A B..... Z digit identifier letter ( letter digit )* 2) Zahlen: digit digits digit digit* opt_fraction ε. digits opt_exponent ε E ( + - ε ) digits number ( + - ε ) digits opt_fraction opt_exponent E. Ploedereder, Seite 33
34 Zustandsdiagramme Für jede Kategorie lexikalischer Elemente, z.b. Bezeichner Schlüsselworte Zahlen Operatoren (relational, additiv, multiplikativ,... ) Trennzeichen Beispiel: relationale Operatoren: < = > Endzustände Bezeichner: letter sonst * = sonst * letter digit > = sonst * Rücksetzung im Puffer E. Ploedereder, Seite 34
35 Codierung von Zustandsdiagrammen I 1) Direkte Umsetzung mittels einer Variablen ZUSTAND 1 letter 2 sonst 3 4 < 5... andere Diagramme letter digit = > 6 7 Zur Erinnerung: (Naive) Idee: Der Lexer soll bei jedem Aufruf das nächste lexikalische Element ermitteln und eine Repräsentation (... Kategorie...,... Index... ) zurückgeben. Der Scanner versucht die Diagramme in der Reihenfolge ihrer Auflistung zu durchlaufen. Falls ein Fehler auftritt, erfolgt ein Übergang zur Start des nächsten Diagramms. Dabei ist Zeiger ENDE im Puffer auf ANFANG zurückzusetzen. E. Ploedereder, Seite 35
36 START: ZUSTAND: Bezeichnet Eingang des gerade durchlaufenen Diagramms. Bezeichnet den gerade besuchten Zustand. FEHLER: Eine Prozedur, die ENDE auf Position ANFANG zurücksetzt und auf den Startzustand des nächsten zu betrachtenden Diagramms umschaltet. Beispiel: Fehlerprozedur case START of 1: START := 4 4: START := Eingang des nächsten Diagramms nach dem für relationale Operatoren... usw.... end LIES_ZEICHEN: Eine Prozedur, die das nächste Zeichen im Puffer liest, ENDE weiterrückt und falls notwendig Puffer (Teil 1 oder Teil 2) neu füllt. E. Ploedereder, Seite 36
37 Skizze eines (naiven) Lexers initialisiere START, ZUSTAND while kein markierter Endzustand erreicht do case ZUSTAND of 1: LIES_ZEICHEN (ZEICHEN) if ZEICHEN ist Buchstabe then ZUSTAND := 2 else FEHLER 2: LIES_ZEICHEN (ZEICHEN) if ZEICHEN ist Buchstabe oder Ziffer then ZUSTAND := 2 else ZUSTAND := 3 3: setze ENDE eine Position zurück teste in Tokentabelle, ob Schlüsselwort / Bezeichner gefunden wurde, falls noch nicht vorhanden, trage es ein gib entsprechendes lexikalisches Element zurück markiere Endzustand 4: LIES_ZEICHEN (ZEICHEN) if ZEICHEN = '<' then ZUSTAND := 5 else if ZEICHEN = '=' then ZUSTAND := 7 usw.... end end E. Ploedereder, Seite 37
38 Codierung von Zustandsdiagrammen II 2) Zusammenfassung aller Zustandsdiagramme in ein Diagramm Nachteil der ersten Alternative war das sequentielle Durchprobieren aller Diagramme. Eine optimale Anordnung der Diagramme ist schwer (oder gar nicht!) zu finden. Idee: 1 a b c DIAGRAMM_1 i x y z DIAGRAMM_2... usw.... Kombination zu einem Diagramm Voraussetzung: Eingänge a, b, c,... und x, y, z,... sind voneinander verschieden 1 a b c x y z DIAGRAMM_1 DIAGRAMM_2... usw.... E. Ploedereder, Seite 38
39 FEHLER: setzt stets zum Zustand 1 zurück Weitere Verbesserung: FEHLER wird nur dann benötigt, wenn die Programmiersprache verschiedene Kategorien lexikalischer Elemente besitzt, die gemeinsame Anfangsstücke haben können, d.h. 1 2 aus Kategorie A 1 3 aus Kategorie B Bei Vorkommen eines Elements bisher so 1 3 arbeitet Scanner 1) 1 3 ANFANG in Diagramm für ENDE Kategorie A FEHLER 2) 1 3 Routine ANFANG ENDE 3) 1 3 ANFANG in Diagramm für ENDE Kategorie B 4) 1 3 ANFANG ENDE E. Ploedereder, Seite 39
40 Besser wäre ein Vermeiden von Phase 3-4 und ein direktes Umschalten vom Diagramm für Kategorie A in die Mitte des Diagramms für Kategorie B. Schwierig! Aber die meisten Programmiersprachen haben dieses Problem nicht (außer wenn bereits der Scanner Schlüsselworte von Bezeichnern unterscheidet, denn Schlüsselworte und Bezeichner fangen gleich an). Auf FEHLER Routine kann ganz verzichtet werden.... usw. usw. türmt sich Überlegung auf Cleverness, eingepackt in komplexen Code, mit Sonderbehandlungen und wehe, wenn die Sprache erweitert wird... Mit Werkzeugen (Lexergeneratoren) können diese Probleme aber ganz einfach überwunden werden... E. Ploedereder, Seite 40
41 Endliche Automaten Ein endlicher Automat wird durch ein 5-Tupel beschrieben: Endlicher Automat M = (Z, A, ü, s, E) mit Z A ü s Z E Z endliche Menge von Zuständen endliche Menge von Symbolen, Alphabet Übergangsfunktion Z A P(Z) nicht-deterministischer endlicher Automat (NEA) Z A Z deterministischer endlicher Automat (DEA) Startzustand Menge der Endzustände E. Ploedereder, Seite 41
42 Vom RA zum NEA (Verfahren von Thompson) Bemerkung: NEA mit zusätzlichen ε-übergängen wird verwendet. 1) Beginne mit RA Startzustand Endzustand 2) Verfeinere durch Zerlegung von RA, bis alle Übergänge mit a A oder ε attributiert sind, unter Verwendung der folgenden Transformationen: p r1 r2 q p r1 q r2 p r1 r2 q r1 r2 p q p (r) q p r q p r* q p ε r ε ε ε q oder p ε r ε q E. Ploedereder, Seite 42
43 Beispiel: Regulärer Ausdruck über A = {a, 0} a ( a 0 )* Konstruktionsschritte: a ( a 0 )* 0 1 a a a ( a 0 )* ε ε ε ε 1 ( a 0 ) a 2 ε ε 1 ε ε E. Ploedereder, Seite 43
44 Vom NEA zum DEA (Untermengenbildung, Verfahren von Rabin-Scott) NEA M = ( Z, A, ü, s, E ) DEA M = ( Z, A, ü, s, E ) Idee: Bilde zu jedem w A* die Menge der Zustände, die mittels w erreichbar sind. Teilmengen von Z bilden Zustände von Z. Endzustände: E = { q q Z und e E : e q } (Iterative) Methode: s = Ü(s, ε*); Z := {s }; s unmarkiert while T Z : T unmarkiert do markiere T ; für alle a A do q = {q Z p T, q Ü( p, aε*)} if q Z then Z := Z { q }; q unmarkiert end if; ü (T, a) := q ; od; od; siehe nächste Folie E. Ploedereder, Seite 44
45 Vom NEA zum DEA Fortsetzung E := {} für alle q Z do if e E : e q then E := E { q }; end if; od; Beispiel: nicht-deterministischer Automat 0 a a 2 ε 3 b 4 ε 1 ε ε Ergebnis der Untermengenkonstruktion A = {0}, B = {2, 3, 1}, C = {4, 3, 1} A a b a M B b a b C a b E. Ploedereder, Seite 45
46 Lexergenerierung Der DEA für die Erkennung regulärer Ausdrücke lässt sich durch die Übergangsmatrix und Vektoren der Zustandscharakterisierung sowie ein einfaches Ausführungsgerüst implementieren. In der folgenden Skizze eines Lexers repräsentiert Transition( alter Zustand, Eingabezeichen) neuer Zustand die Übergänge des DEA. Im DEA fehlende Transitionen aus dem alten Zustand führen hier zum ausgezeichneten Fehlerzustand. Endzustand(Zustand) {true, false} unterscheidet die Endzustände von anderen Zuständen im DEA. Fehlerzustand(Zustand) {true, false} unterscheidet den Fehlerzustand von anderen Zuständen im DEA. Kategorie(Endzustand) Tokenkategorie ergibt die Kategorie des in diesem Zustand erkannten RAs (z.b. Identifier, Zahl, usw.). E. Ploedereder, Seite 46
47 Skizze eines Lexers II Anfang, Ende : Natural; Next_char : Character; Start_Zustand : constant Zustand := 1; -- zum Beispiel Current_Position : Quell_Position; function Get_Next_Char return Character; -- verwaltet Puffer, Current_Position -- inkrementiert Ende -- gibt nächsten Character im Puffer zurück procedure Get_Token ( Result : out Token, Pos : out Position ) is Alt_Zustand, Neu_Zustand : Zustand; begin Alt_Zustand := Start_Zustand; *** Schleife siehe nächste Folie *** end Get_Token; E. Ploedereder, Seite 47
48 Skizze eines Lexers II Die Schleife loop Neu_Zustand := Transition (Alt_Zustand, Next_char); if Fehlerzustand (Neu_Zustand) then if Endzustand(Alt_Zustand) then Result := Sift_Token (Anfang, Ende, Kategorie(Alt_Zustand)); Pos := Current_Position; Anfang := Ende; return; else *** Fehlermeldung/ -behandlung end if; else Alt_Zustand := Neu_Zustand; Next_char := Get_Next_Char(); end if; end loop; E. Ploedereder, Seite 48
49 Anmerkung zum Lexer-Algorithmus Als Implementierung eines allgemeinen DEA für reguläre Ausdrücke stimmt der gegebene Algorithmus nicht. Er würde nämlich eine Eingabe, die im folgenden Ausschnitt eines DEA dem Endzustand A entspricht, aber dann unmittelbar von ab gefolgt wird, nicht erkennen. Dafür wäre ein Rücksetzen der Eingabe ab nach Erreichen des Fehlerzustands notwendig. a A a B a C a b b b M Praktisch alle Programmiersprachen vermeiden die Situation beliebig langen Rücksetzens durch ihre Definition der lexikalischen Elemente, die sich immer am längsten Match orientiert. Für diese Sprachen funktioniert der Algorithmus. E. Ploedereder, Seite 49
50 Darstellung lexikalischer Elemente durch Token 1) Nachfolgende Operationen im Compiler, insbesondere Vergleich, sollten sehr effizient sein. Token(X) = Token(Y) X Y nach lexikalischen Sprachregeln 2) Äquivalente lexikalische Elemente sollten nicht mehrfach abgespeichert werden. 3) Erkennung von Bezeichnern als Schlüsselworte kann hier durchgeführt werden (eklatante Reduktion der Zustände im Lexer). Sifter = Zuordnung der vom Scanner erkannten Bezeichner zu lexikalischen Kategorien Scanner Abgrenzung lexikalischer Elemente Sifter Token Parser E. Ploedereder, Seite 50
51 Interne Darstellung lexikalischer Elemente Paar: (... Kategorie...,... Index... ) Typische Kategorien: Bezeichner Zahlen Zeichenketten Schlüsselworte Operatoren Trennzeichen id num string key multop, addop, relop delimiter Index kennzeichnet ein lexikalisches Element innerhalb seiner Kategorie. Beispiel: (relop, 1) < (relop, 2) <= (relop, 3) = (relop, 4) <> (relop, 5) >= (relop, 6) > (multop, 1) * (multop, 2) / (multop, 3) div (multop, 4) mod E. Ploedereder, Seite 51
52 Beispiel: Eingabe: Mitte := Mitte + (Links+Rechts)/2 + f( ursprung ); erzeugte Paare: << id, ptr >> << op, 5 >> -- := << id, ptr >> << op, 9 >> -- + << op, 4 >> -- ( << id, ptr >>... usw.... << int_literal, 2>>... usw.... << string_literal, ptr >>... usw.... Tokentabelle Mitte Links Rechts ursprung Die Tokentabelle ist typischerweise eine Aneinanderreihung der zu speichernden Zeichenketten. Effiziente Suche, ob eine gegebene Zeichenreihe bereits in der Tokentabelle eingetragen ist, kann z.b. durch eine Hash-Tabelle erfolgen, deren Einträge auf die Einträge in der Tokentabelle verweisen. Für Sprachen mit einfachen Sichtbarkeitsregeln kann eventuell auch die Symboltabelle als Suchstruktur über der Tokentabelle verwendet werden. E. Ploedereder, Seite 52
53 Symboltabelle Die Symboltabelle (ST) speichert die bekannten (i. Allg. deklarierten) Bezeichner. Die ST assoziiert semantische Attribute mit den Token für die Bezeichner. Die ST dient zum Auffinden dieser Attribute. Tokenwert ptr ptr... Art id id... Attribute integer, 32bit, usw. real, 64bit, usw.... EP: Die Verwaltung der ST ist i. Allg. Aufgabe der semantischen Analyse und nachfolgender Phasen. Bei Verschränkung der lexikalischen, syntaktischen und semantischen Phasen (z.b. Ein-Pass-Compiler) können Lexer und Parser bei der Konstruktion und Nutzung der ST involviert sein. E. Ploedereder, Seite 53
54 Lexikalische Analyse Zusammenfassung 1) Die lexikalischen Elemente der meisten Sprachen sind durch reguläre Ausdrücke beschreibbar. 2) Reguläre Ausdrücke NEA DEA minimaler DEA Zustandsübergangsmatrix immer möglich und automatisierbar. Scanner sind automatisch generierbar. ( Scanner und entsprechende Methoden haben Anwendungen außerhalb des Compilerbaus.) 3) DEA ermöglichen sehr schnelle Analyse durch einfache Zustandsfortschreibung (keine Kellerung). 4) Lexer übersetzen das Eingabeprogramm in eine entsprechende Sequenz lexikalischer Elemente, dargestellt in Token-Form (= Terminale für die syntaktische Analyse). 5) Lexer müssen schnell sein (minimale Kosten per Eingabezeichen!). 6) Reguläre Ausdrücke entsprechen einseitig (rechts- oder links-) linearen Grammatiken. Abtrennung des Lexers vom Parser ist eine Software Engineering Entscheidung (effizientere Analyse; separation of concerns ). E. Ploedereder, Seite 54
55 3. Syntaktische Analyse Einbettung in den Compiler Quellprogramm LEXER Anforderung Lieferung eines lex. Elements PARSER Strukturbaum Tokentabelle Aufgaben des Parsers 1. Überprüfung des Programms hinsichtlich seines strukturellen Aufbaus 2. Fehlererkennung / Fehlerbeseitigung 3. Unterstützung der semantischen Analyse E. Ploedereder, Seite 55
56 Beispiel zu 1) PASCAL erfordert für Fallunterscheidungen den Aufbau case... Ausdruck... of Fall_1:... Anweisung_1...; Fall_2:... Anweisung_2...;... usw. end Das Programmstück case MANNSCHAFT of VFB_STUTTGART: STATUS:=AUSGESCHIEDEN; BAYERN_MÜNCHEN: STATUS:=WEITER; WERDER_BREMEN: STATUS:=WEITER; end besitzt die geforderte Struktur MANNSCHAFT AUSDRUCK, VFB_STUTTGART Fall_1, STATUS:=AUSGESCHIEDEN Anweisung_1, usw. case...of...end Gerüst. Beispiel zu 2) Der Parser erkennt einen Fehler in while... loop... und korrigiert ihn zu while... do... Beispiel zu 3) siehe später unter syntaxgesteuerte Übersetzung E. Ploedereder, Seite 56
57 Syntaktische Beschreibung von Programmiersprachen Eingabe PARSER Ausgabe Syntaktische Beschreibung kontextfreie Grammatik BNF Kontextfreie Grammatik Idee: Beschreibe die Festlegung 1) Zum Nichtterminalsymbol Anweisung gehören while-anweisungen 2) Eine while-anweisung besteht aus einem Ausdruck und einer Anweisung durch eine Regel statement while expression do statement E. Ploedereder, Seite 57
58 Kontextfreie Grammatik kfg = ( N, T, P, S ) N = Nichtterminalsymbole T = Terminalsymbole P = Produktionen S = Startsymbol Notationen: Großbuchstaben für N Kleinbuchstaben für T Griechisches Alphabet für Worte aus (N T)* k:w die ersten k Terminalsymbole in w, w T* S aus S mittels der Produktionen ableitbar E. Ploedereder, Seite 58
59 Bestandteile einer kontextfreien Grammatik Nichtterminalsymbole: Kategorien von Sprachelementen, z.b. expression statement variable type Festlegung: Nichtterminalsymbole beginnen mit einem Großbuchstaben. Terminalsymbole: Elementare Bestandteile der Programmiersprache lexikalische Elemente Produktionsregeln: A Nichtterminalsymbol Folgen von syntaktischen Nichtterminalen / Terminalen Startsymbol: Allgemeinste Kategorie von Sprachelementen, i.allg. Programm. E. Ploedereder, Seite 59
60 Beispiel: Grammatikproduktionen: Expression Expression + Expression Expression Expression * Expression Expression ( Expression ) Expression - Expression Expression id Eine Grammatik: Nichtterminal : Expression Terminale : + * ( ) id Startsymbol : Expression Regeln : s.o. Abkürzung für alternative Produktionen: Expression Expression + Expression Expression * Expression usw.... oder E. Ploedereder, Seite 60
61 Ableitungen in kontextfreien Grammatiken Durch Substitutionen (beginnend mit dem Startsymbol) rechte Seite einer Produktion ersetzt linke Seite lassen sich Folgen von lexikalischen Elementen ( Programme ) bilden. Prinzip 1 A 2 Nichtterminale / Terminale Nichtterminale / Terminale Substitution mit Regel für Nichtterminal A A Zwei Entscheidungen bei Substitutionen 1) welches Nichtterminal soll ersetzt werden 2) wodurch soll das Nichtterminal ersetzt werden Beispiel: (s.o. E für Expression) E E + E E + ( E ) E + ( E * E ) id + ( E * E ) id + ( id * E ) id + ( id * id ) E. Ploedereder, Seite 61
62 Strukturbäume Ableitungen lassen sich durch Bäume darstellen. Schritt 0: E Schritt 3: E Restliche Schritte : E Schritt 1: E E + E E + E E + E ( E ) id ( E ) E * E E * E Schritt 2: E id id E + E ( E ) E. Ploedereder, Seite 62
63 Beziehung Ableitung Strukturbaum Zu jeder Ableitung gibt es genau einen Baum. Zu einem Baum gibt es i.allg. mehr als eine Ableitung. Die Reihenfolge der Produktionen ist nicht eindeutig. Beispiel: (siehe Baum Seite 60 unten) Ableitung 1: E E + E id + E Linksableitung id + ( E ) id + ( E * E ) id + ( id * E ) id + ( id * id ) Ableitung 2: E E + E E + ( E ) Rechtsableitung E + ( E * E ) E + ( E * id ) E + ( id * id ) id + ( id * id )... weitere Ableitungen... Abmachung: Ein Strukturbaum wird durch seine Linksableitung oder Rechtsableitung charakterisiert. E. Ploedereder, Seite 63
64 Mehrdeutige Grammatiken Zu manchen Worten gibt es mehr als einen Strukturbaum (bzw. Linksableitung / Rechtsableitung). Beispiel: (Grammatik Seite 59) Wort: id + id * id Strukturbaum 1: E Strukturbaum 2: E E * E E + E id + id id id id * id Für Programmiersprachen sind mehrdeutige Grammatiken nicht erwünscht. semantische Mehrdeutigkeiten Aber: Mache Konstruktionen in Programmiersprachen verursachen Mehrdeutigkeiten. E. Ploedereder, Seite 64
65 Beispiel: Statement if Expression then Statement if Expression then Statement else Statement if... then... if... then... else Statement if Expression then Statement else Statement if Expression then Statement Statement oder if Expression then Statement if Expression then Statement else Statement E. Ploedereder, Seite 65
66 Lösung 1: Änderung der Sprache Statement if Expression then Statement end if if Expression then Statement else Statement end if Lösung 2: Änderung der Grammatik Statement Matched_statement Unmatched_statement Matched_statement if Expression then Matched_Statement else Matched_statement... Unmatched_statement if Expression then Matched_Statement else Unmatched_statement if Expression then Statement Problem: Satz aus der Theorie formaler Sprachen besagt: Mehrdeutigkeit für beliebige kfg ist nicht entscheidbar., aber es ist eine hinreichende Bedingung für die Nicht-Mehrdeutigkeit einer kfg bekannt, siehe LL(k)- und LR(k)-Grammatiken. Die LL(k)- oder LR(k)-Eigenschaft einer kfg impliziert, dass die kfg nicht mehrdeutig ist. E. Ploedereder, Seite 66
67 Ausgangspunkt für das weitere Vorgehen Kontextfreie Grammatik ohne Mehrdeutigkeiten Beispiel: E T F E + T T T * F F ( E ) id Aufgabe für Parser: Finde den Strukturbaum von id + id * id Vorgabe: Das Wort id + id * id ist von links nach rechts zu lesen. E. Ploedereder, Seite 67
68 Top-down Ansatz Baue Baum von oben nach unten d.h. E id + id * id Schritt 0 : E Schritt 1 : E + T Schritt 2 : T Schritt 5 : T * F Schritt 3 : F Schritt 6 : F Schritt 4 : id Schritt 7 : id Schritt 8 : id E. Ploedereder, Seite 68
69 Allgemeine Strategie beim top-down Ansatz bereits fertig Startsymbol am weitesten links vorkommende Variable 1 Schritt A bereits verarbeitete Eingabe noch nicht verarbeitete Eingabe fehlt noch neu Ziel: Finde eine Linksableitung E. Ploedereder, Seite 69
70 Bottom-up Ansatz Baue Baum von unten nach oben d.h. E id + id * id Schritt 12: E Schritt 3 : E Schritt 11: T Schritt 2 : T Schritt 7 : T Schritt 1 : F Schritt 6 : F Schritt 10: F Schritt 0 : id Schritt 4 : + Schritt 5 : id * Schritt 8 : Schritt 9 : id E. Ploedereder, Seite 70
71 Allgemeine Strategie beim bottom-up Ansatz bereits fertig Startsymbol 1 Schritt A fehlt noch neu Ziel: Finde eine umgekehrte Rechtsableitung E. Ploedereder, Seite 71
72 Kategorien von top-down Parsern deterministisch nicht-deterministisch mit Rücksetzungen ohne Rücksetzungen rekursiver Abstieg Tabellenmethode E. Ploedereder, Seite 72
73 Prinzip des rekursiven Abstiegs Schreibe für jedes Nichtterminalzeichen N eine Prozedur, welche testet, ob die nächsten lexikalischen Elemente ein aus N ableitbares Wort bilden: procedure N... Vergleich mit nächstem lexikalischem Element ( match )... end N Beispiel: N M + J procedure N call M; match ( + ); call J; end N E. Ploedereder, Seite 73
74 Komplikationen 1) Linksrekursion Expression Expression + Term Term Direkte Umsetzung führt zu unendlicher Rekursion: procedure EXPRESSION call EXPRESSION Lösung: Links- in Rechtsrekursion umwandeln Expression Term E E + Term E ε E. Ploedereder, Seite 74
75 Eliminierung direkter Linksrekursion allgemein gültige Regel: β i beginnt nicht mit A, 1 i n Beispiel: E E + T T E T E E + T E ε A A α 1 A α 2... A α m β 1 β 2... β n A β 1 A β 2 A... β n A A α 1 A α 2 A... α m A ε Für indirekte Linksrekursion siehe Aho, Kapitel 4.3, Algorithmus 4.1. Beispiel: S A a b A A c S d ε ist direkt linksrekursiv in A und indirekt linksrekursiv in S. E. Ploedereder, Seite 75
76 Zum Thema Grammatik-Transformationen L(G) = { w w T G * : S * w } ist die von der Grammatik erzeugte Sprache. Def.: Zwei kontextfreie Grammatiken G 1 und G 2 sind äquivalent L(G 1 ) = L(G 2 ) Für Grammatik-Transformationen zur Anpassung an Parsing-Verfahren sollten die Ausgangs- und Zielgrammatiken äquivalent sein. Pragmatisch: Es muss gelten, dass L(Ausgangsgrammatik) L(Zielgrammatik) Wenn die Zielgrammatik eine größere Sprache generiert, dann können die Überschüsse später (d.h. in der semantischen Analysephase) durch explizite Prüfungen ausgefiltert werden. E. Ploedereder, Seite 76
77 Komplikationen 1) Linksrekursion Expression Expression + Term Term Direkte Umsetzung führt zu unendlicher Rekursion: procedure EXPRESSION call EXPRESSION Lösung: Links- in Rechtsrekursion umwandeln Expression Term E E + Term E ε 2) Alternativen N α β Welche Alternative soll verfolgt werden? Lösung: Berechne FIRST(α), FIRST(β) Falls nächstes Token in FIRST(α), verfolgen N α, sonst... Voraussetzung: Alle FIRST-Mengen sind disjunkt! E. Ploedereder, Seite 77
78 FIRST- und FOLLOW-Mengen FIRST k (α) = { k:w α * w, w T* } d.h. die Menge aller Terminalsequenzen der Länge k, die ein von α ableitbares Wort der Grammatik einleiten, bzw. w, falls w weniger als k Elemente besitzt. FOLLOW k (A) = { k:w S * αaβ, w FIRST k (β) } d.h. die Menge aller Terminalsequenzen mit maximal der Länge k, die in einer Ableitung von S unmittelbar auf A folgen können, bzw. w, falls w weniger als k Elemente besitzt. Wir sind primär an k=1 interessiert. E. Ploedereder, Seite 78
79 Veranschaulichung der FIRST k - und FOLLOW k -Mengen FIRST k A FOLLOW k D A αb ω 1 B α ω 1 C σ D ω 3 C σ B βc ω 2 β C ω 2 B ρ C ω 2 B ρ C γd ω 3 γ D ω 3 A δ B ω 1 A δ FIRST k (A) = FIRST k (αβγ... ω 3 ω 2 ω 1 ) lokale k-folgemenge von A = FIRST k (δρσ... ) FOLLOW k (A) = Vereinigung der lokalen k-folgemengen von A für alle Vorkommen von A in Ableitungen S * lar E. Ploedereder, Seite 79
80 Berechnung der FIRST 1 -Mengen Regeln: 1) t T: FIRST(t) = { t } 2) p P: X ε: ε FIRST(X) 3) p P: X Y 1 Y 2...Y n, Y i T N: 3.1) wenn a FIRST(Y i ), a T, und j, j < i: ε FIRST(Y j ), dann a FIRST(X) 3.2) wenn i, i = 1... n : ε FIRST(Y i ), dann ε FIRST(X) Algorithmus: (Berechnung durch iterative Erweiterung der FIRST-Mengen nach obigen Regeln) 1. wende Regeln 1 und 2 an 2. repeat für alle p P, wende Regel 3 an until die innere Schleife bewirkt keine Veränderung der FIRST- Mengen mehr und schließlich wird FIRST(α), α = Y 1 Y 2...Y n, nach 3.1 und 3.2 entsprechend bestimmt. E. Ploedereder, Seite 80
81 Berechnung der FOLLOW 1 -Mengen Regeln: 1) für Startsymbol S G : $ FOLLOW(S G ) $ = Ende der Eingabe -Token 2) p: X αbγ : (FIRST(γ) {ε}) FOLLOW(B) 3) p: X αb oder αbγ und ε FIRST(γ): Algorithmus: FOLLOW(X) FOLLOW(B) (Iterative Berechnung nach obigen Regeln) 1. wende Regeln 1 und 2 an 2. repeat für alle p P, wende Regel 3 an until die innere Schleife bewirkt keine Veränderung der FOLLOW- Mengen mehr E. Ploedereder, Seite 81
82 Beispiel: Ausgangspunkt: E E + T T T T * F F F ( E ) id Elimination von Linksrekursion liefert: E T E E + T E ε T F T T * F T ε F ( E ) id FIRST(E) = FIRST(T) = FIRST(F) = { id, ( } FIRST(E ) = { +, ε } FIRST(T ) = { *, ε } FOLLOW(E) = { $, ) } FOLLOW(E ) = { $, ) } FOLLOW(T) = { $, ), + } FOLLOW(T ) = { $, ), + } FOLLOW(F) = { $, ), +, * } E. Ploedereder, Seite 82
83 Komplikationen II 3) Gemeinsame Vorsilben N αβ αγ FIRST(αβ) nicht disjunkt von FIRST(αγ) Beispiel: Statement if Expression then Statement fi if Expression then Statement else Statement fi Lösung: Linksfaktorisierung N α M M β γ 4) ε-produktionen N α... ε Lösung: Alternative N εverfolgen, falls nächstes lexikalisches Element in keinem der FIRST(α),... enthalten ist. (Eigentlich nur dann, wenn nächstes lexikalisches Element FOLLOW(N). Vereinfachung ist aber harmlos, solange Grammatik LL(1) ist.) E. Ploedereder, Seite 83
84 LL(1) und Simple LL(1) Grammatiken Eine Grammatik G = (N, T, P, S) ist LL(1), wenn: für alle A N und für alle p 1, p 2 P, p 1 : A α, p 2 : A β, gilt: 1. falls α * t 1 γ, β * t 2 δ, t 1, t 2 T, dann t 1 t 2, d.h. FIRST 1 (α) FIRST 1 (β) = { } 2. es gilt höchstens eine der Ableitungen α * ε, β * ε 3. falls β * ε, α * t 1 γ, dann t 1 FOLLOW 1 (A) Eine Grammatik G ist Simple LL(1), wenn sie: 1. keine ε-produktionen hat, und 2. für alle A N und für alle p 1, p 2 P, p 1 : A α, p 2 : A β, gilt: α = t 1 γ, β = t 2 δ, t 1 t 2, t 1, t 2 T Anm: Die Abkürzung SLL bedeutet Strong LL, nicht Simple LL. Für k=1 gilt: SLL(1) = LL(1), sodass wir SLL nicht gesondert behandeln.
85 S * lar lαr * lx = w L(G) lm lm Problem: Auswahl der richtigen Produktion für A S schon bekannt l T* A r r (N T)* l w i Laut Definition der LL(1)-Grammatik sind die FIRST-Mengen von Alternativproduktionen disjunkt. Daher ist die Auswahl einer Produktion für A eindeutig. Das nächste Eingabesymbol w i legt fest, welche Produktion anzuwenden ist: Falls w i FIRST(α), wende A αan, sonst falls w i FOLLOW(A), wende A εan, sonst w L(G). α w w = w 1 w 2... w n E. Ploedereder, Seite 85
86 Satz: Eine mehrdeutige Grammatik ist nicht LL(k) für ein beliebiges k. Beweis: aus der Definition der Mehrdeutigkeit: A N: A α, A β P, α β, S * lar * lαr * lx S * lar * lβr * lx k : x = k : x Also ist k : x FIRST k (α) FIRST k (β) Widerspruch zur LL(k)-Definition E. Ploedereder, Seite 86
87 Steuertabelle für die top-down Analyse Keller-Top nächste Eingabe Steuertabelle M: (N T) T Aktion Derartige Steuertabellen werden bei automatisch generierten top-down Parsern, die als Kellerautomat operieren, benötigt. Sie sind aber auch bei einer manuellen Implementierung des rekursiven Abstiegs als Entwurfshilfe sehr nützlich. Bedeutung von M [A, u], A N: Aktion: M [A, u] = A α 1... α n - i-te Produktion Wenn zu Beginn der Analyse für ein A das nächste Eingabesymbol u ist, dann analysier entsprechend der eingetragenen i-ten Produktion. Aktion: M [A, u] = error Diagnostiziere an der Eingabeposition einen Syntaxfehler. E. Ploedereder, Seite 87
88 Erstellung einer Steuertabelle für LL(1) Grammatik Steuertabelle M: N T P p G: p: A α a FIRST(α), trage A αin M [A, a] ein falls ε FIRST(α), trage A αin M [A, b] ein für alle b FOLLOW(A) freibleibende Tabellenpositionen sind error -Einträge Prüfung der LL(1)-Eigenschaft verfahre wie oben G ist LL(1) Grammatik keine Mehrfacheinträge in den Positionen der Steuermatrix E. Ploedereder, Seite 88
89 Beispiel: Steuertabelle für die Ausdrucksgrammatik (Nach Eliminierung der Linksrekursion) Nichtterminale E E T T F id E T E T F T F id + E + T E T ε Eingabesymbole * ( E T E T F T T * F T F ( E ) ) E ε T ε $ E ε T ε E. Ploedereder, Seite 89
90 Realisierung durch Kellerautomaten Z = ( noch zu analysierende Satzform, Keller noch zu analysierendes Teilwort, Eingabeband bisherige Linksableitung Ausgabeband ) M [A, u], A N, Aktion: M[A, u] = A α 1... α n - i-te Produktion dann pop; push(α n );... ; push(α 1 ); output(i); Aktion: M[A, u] = error dann error M[t, u], t T, t $, M[$, $] Aktion: wenn u = t dann sonst Aktion: M[$, t], t $ Aktion: pop; lies nächstes Zeichen error accept error E. Ploedereder, Seite 90
91 Rekursiver Abstieg Nach der Erstellung der FIRST- und FOLLOW-Mengen wird die deterministische top-down Analyse durch ein System sich rekursiv aufrufender Prozeduren implementiert. Prinzip: Schreibe für jedes Nichtterminalzeichen eine Prozedur, die andere Prozeduren aufruft. Beispiel: Grammatik, FIRST-, FOLLOW-Mengen von Seite 92 und Seite 81. program TOP_DOWN_ANALYSE (INPUT, OUTPUT); var SYMBOL : TOKEN; {enthält nächstes Zeichen der Eingabefolge} procedure E; if (SYMBOL = ( ) or (SYMBOL = id ) then call T; call E else WRITE( FEHLERHAFTE EINGABE ); end {E}; E. Ploedereder, Seite 91
92 procedure E ; if SYMBOL = + then READ(SYMBOL); call T; call E ; else if (SYMBOL <> ) ) and (SYMBOL <> $ ) then WRITE( FEHLERHAFTE EINGABE ); end {E }; call ist kein PASCAL- Schlüsselwort procedure T; if (SYMBOL = ( ) or (SYMBOL = id ) then call F; call T else WRITE( FEHLERHAFTE EINGABE ) end {T}; E. Ploedereder, Seite 92
93 procedure T ; if (SYMBOL = * ) then READ(SYMBOL); call F; call T else if (SYMBOL <> + ) and (SYMBOL <> ) ) and (SYMBOL <> $ ) then WRITE( FEHLERHAFTE EINGABE ) end {T }; procedure F; if SYMBOL = id then READ(SYMBOL) else if SYMBOL = ( then READ(SYMBOL); call E; if (SYMBOL = ) ) then READ(SYMBOL) else WRITE( FEHLERHAFTE EINGABE ) else WRITE( FEHLERHAFTE EINGABE ) end {F}; begin {TOP_DOWN_ANALYSE} READ(SYMBOL); call E; end {TOP_DOWN_ANALYSE} E. Ploedereder, Seite 93
94 Organisation von top-down Parsern Übergangsdiagramme helfen, eine bessere Organisation eines top-down Parsers nach der Methode des rekursiven Abstiegs zu finden. Konstruktion: Baue für jedes Nichtterminalzeichen N ein Diagramm Diagramm besitzt 1 Startzustand 1 Endzustand Diagramm erhält für jede Produktion N X 1 X 2... einen mit X 1, X 2,... markierten Weg vom Start- zum Endzustand. E. Ploedereder, Seite 94
95 Beispiel: Übergangsdiagramm dafür sind S : -3 E $ -2-1 T E E : T E E : e Diagramme lassen sich vereinfachen Schritt 1: Rückkopplung bei E ε + T E : ε T T : T : F : F T * 11 F 12 T 13 e 14 ( 15 E 16 ) 17 id E : ε E. Ploedereder, Seite 95
96 Schritt 2: Einsetzung des Diagramms E in Diagramm E T E : 0 T ε Verschmelzung von Zustand 0 und 4 + E : 0 T ε 3 6 Analog lässt sich ein Diagramm für T ableiten: F T : 7 8 * ε 13 aber Vorsicht: obige Strategien führen zwar zu syntaktisch äquivalenten Analyseverfahren, komplizieren aber unter Umständen die semantische Analyse (insbesondere ist die Assoziativität von Operatoren zu beachten!) E. Ploedereder, Seite 96
97 Rechts-/Links-Assoziativität G 1 :S $ E $ E T + E T T F T F F id id + id + id + id id + id G 1 hat rechts-assoziative Operatoren. G 2 :S $ E $ E E + T T T T F F F id id + id + id id + + id id G 2 hat links-assoziative Operatoren. (2 + 3) + 4 = 9 = 2 + (3 + 4) aber (2 3) 4 = = 2 (3 4) Obwohl L(G 1 ) = L(G 2 ); Assoziativitäts-Eigenschaften für die Semantik wichtig! E. Ploedereder, Seite 97
98 EBNF Die erweiterte Backus Naur Form (EBNF) ist eine Erweiterung der BNF um Notationen für häufig auftretende Strukturen: 1) Alternative in EBNF: "+" "-" ) Option in EBNF: [id] 14 id ε 15 3) Iteration in EBNF: {"+" T} T ε Option und Iteration haben höchste Priorität; Verkettung hat zweithöchste Priorität und Alternative niedrigste Priorität Klammerung mit runden Klammern ist möglich E. Ploedereder, Seite 98
99 Top-down Syntaxanalyse - Zusammenfassung Baut Strukturbaum vom Startsymbol ausgehend... durch Ersetzung von Nichtterminalen im Baum durch die entsprechende rechte Seite einer Produktion. Bei Ersetzung des jeweils am meisten links gelegenen Nichtterminals ergibt sicht die Linksableitung aus der Reihenfolge der angewandten Produktionen. Grammatik darf nicht links-rekursiv sein. Disjunkte FIRST-Mengen erlauben die geeignete Auswahl unter alternativen Produktionen für ein Nichtterminal. FOLLOW 1 -Mengen verhindern Anwendung von ε-produktionen im Fall von Fehlern in der Eingabe. Realisierung durch rekursiven, prozeduralen Abstieg Keller-Automat und Steuertabelle bzw. Zustandsübergangsmatrix E. Ploedereder, Seite 99
100 4. Syntaxgesteuerte Übersetzung Idee Syntaxanalyse steuert semantische und andere Aktionen wie Codeerzeugung und Typüberprüfung Beispiel: (Codeerzeugung für Stackmaschine) Grammatik: E E + T Produktion T T T * F F id F T T * F F (E) id E E + T Aktion wenn RHS erkannt erzeuge Befehl LOAD id erzeuge Befehl MULT erzeuge Befehl ADD Für die Eingabe (id + id) * id entsteht folgender Code für eine Stackmaschine: LOAD id LOAD id ADD LOAD id MULT E. Ploedereder, Seite 100
101 Beispiel: (Typinferenz) Gleiche Grammatik wie im letzten Beispiel, Bezeichner seien vom Typ INTEGER oder REAL. Produktion F F T T E id (E) F T 1 * F T E E 1 + T Aktion wenn RHS erkannt bestimme id.typ aus der Symboltabelle F.TYP id.typ F.TYP E.TYP T.TYP F.TYP if (T 1.TYP = INTEGER) and (F.TYP = INTEGER) then T.TYP INTEGER else T.TYP REAL E.TYP T.TYP if (E 1.TYP = INTEGER) and (T.TYP = INTEGER) then E.TYP INTEGER else E.TYP REAL Für A, B vom Typ INTEGER und C vom Typ REAL ergibt sich bei (A + B) * C als Resultatstyp REAL. E. Ploedereder, Seite 101
102 Die Implementierung der Idee zur Verzahnung von Syntaxanalyse und sonstigen Aktionen erfolgt über Attribute ( Wertemengen ) z.b. Typ, numerische Werte, Einträge in die Symboltabelle, generierter Code Operationen (Funktionen, Prozeduren) für die Auswertung von Attributen Es gibt zwei Arten von Attributen: zusammengesetzte Attribute ererbte Attribute Zusammengesetzte Attribute ( synthesized ) hängen von Attributen der Söhne ab. zusammengesetztes Attribut kann von eigenen oder von Attributen bei b, c, d und e abhängen a Beispiel: Die Bestimmung des Typs auf der vorhergehenden Seite erfolgt mittels des zusammengesetzten Attributs TYP. b c d e E. Ploedereder, Seite 102
103 Ererbte Attribute können von Attributen des Vaterknotens und der Geschwisterknoten abhängen. Ererbte Attribute erlauben die Erfassung von kontextsensitiven Konstruktionen in Programmiersprachen. Beispiel: a b c d ererbtes Attribut, kann von Attributen bei a, b und d abhängen Produktion Dec Type Var_list Type Var_list INTEGER REAL Var_list 1, Var Var Aktionen Var_list.TYP Type.TYP Type.TYP Var_list 1.TYP Var_list.TYP trage in Symboltabelle bei Var als Typ den Wert von Var_list.TYP ein trage... ein Type.TYP INTEGER REAL E. Ploedereder, Seite 103
104 Für die Deklarationen Dec INTEGER max, moritz, felix entsteht der nebenstehende Type Strukturbaum mit Attributierung. INTEGER Var_list Var_list Var Var_list Var Typ INTEGER Var Typ INTEGER Ausrechnung der Attribute kann Typ INTEGER durch Konstruktion des Syntaxbaumes und anschließende Ausrechnung oder auch parallel zur syntaktischen Analyse, wobei dies nicht immer möglich ist, erfolgen. E. Ploedereder, Seite 104
105 S-Attributierung Idee: Ausschließlich zusammengesetzte Attribute verwenden Zusammengesetztes Attribut L-Attributierung Idee: Attribute von oben nach unten und von links nach rechts auswerten, d.h. für jede Produktion soll Nebenstehendes gelten zusammengesetzte Attribute von oben nach unten von links nach rechts Konvention: zusammengesetzte Attribute rechts von Knoten, ererbte Attribute links vom Knoten E. Ploedereder, Seite 105
106 Implementierung der L-Attributierung Prinzip A B C D Abmachungen Ausrechnungsreihenfolg e ererbte Attr. von A ererbte Attr. von B Attr. des Teilbaums mit Wurzel B zusammengesetzte Attr. von B ererbte Attr. von C Attr. des Teilbaums mit Wurzel C zusammengesetzte Attr. von C ererbte Attr. von D Attr. des Teilbaums mit Wurzel D zusammengesetzte Attr. von D zusammengesetzte Attr. von A Ererbte Attribute eines Nichtterminalzeichens N sollen vor der Erkennung der aus N ableitbaren Strukturen errechnet werden. Zusammengesetzte Attribute eines Nichtterminalzeichens N sollen nach der Erkennung der aus N ableitbaren Strukturen errechnet werden. E. Ploedereder, Seite 106
107 Mit diesen Abmachungen lassen sich semantische Aktionen wie folgt in die Produktion A B C D einbauen. A {ererbte Attribute von B ausrechnen} B {ererbte Attribute von C ausrechnen} C {ererbte Attribute von D ausrechnen} D {zusammengesetzte Attribute von A ausrechnen} Falls die Produktionen für B, C und D analog aufgebaut werden, stehen hinter B, C und D jeweils deren gesamte Attribute zur Verfügung. E. Ploedereder, Seite 107
108 Definition der S-Attributierung (auch Z-Attributierung genannt) Eine S-Attributierung besteht nur aus zusammengesetzten Attributen. Definition der L-Attributierung Eine L-Attributierung besteht aus zusammengesetzten und ererbten Attributen, wobei die folgende Einschränkung gilt: In allen Produktionen A B 1 B 2... B m kann ein ererbtes Attribut bei B i für 1 i m nur abhängen von ererbten Attributen bei A (beliebigen) Attributen bei B j für j < i L-Attributierung reicht für die Beschreibung der meisten Konstruktionen in Programmiersprachen aus.... aber auch: ein Konstrukt, für das eine L-Attributierung nicht ausreicht, genügt, um das Design eines Ein-Pass Front-Ends zu vernichten. E. Ploedereder, Seite 108
109 Übersetzungsschemata bestehen aus kontextfreier Grammatik ( Basis ) Attributen für die Grammatiksymbole Produktionen mit in die rechte Seite eingebetteten semantischen Aktionen {...} : A...{...}...{...}... In......X {... AKTION... } Y... wird die semantische Aktion... AKTION... nach der Behandlung von X und vor der Behandlung von Y durchgeführt. Anmerkungen zur Schreibweise: Wir könnten Attributgleichungen als semantischen Aktionen auch nach der BNF der Syntaxproduktion schreiben, weil sich aus den Attributgleichungen ergibt, an welcher Stelle die Gleichungen in einem Übersetzungsschema der obigen Form stehen sollte. Dies gilt aber nicht für semantische Aktionen, die Seiteneffekte beinhalten. E. Ploedereder, Seite 109
110 Top-down Übersetzungsschemata Ausgangspunkt sei das Schema E E1 + T { E.WERT := E1.WERT + T.WERT } T { E.WERT := T.WERT } T T * F { T.WERT := T.WERT * F.WERT } F { T.WERT := F.WERT } F (E) { F.WERT := E.WERT } num { F.WERT := num.wert } Problem: Grammatik wegen Linksrekursion nicht für top-down Analyse geeignet. Elimination der Linksrekursion gibt: E T R {...} R + T R1 {...} ε {...} T F X {...} X * F X1 {...} ε {...} F ( E ) {...} num {...} E. Ploedereder, Seite 110
111 Attributfluss bei Elimination der Linksrekursion Heuristik für die Attributierung T T.WERT F F.WERT X.UEBER X X.WERT * F F.WERT X.UEBER X.WERT X 1 ε F ( E ) { F.WERT := E.WERT } num { F.WERT := num.wert } T F { X.UEBER := F.WERT } X X ε { T.WERT := X.WERT } { X.WERT := X.UEBER} X * F { X 1.UEBER := X.UEBER * F.WERT } X 1 { X.WERT := X 1.WERT} Analoge Attributierung für E T R R + T R 1 ε E. Ploedereder, Seite 111
112 Beispiel: Analyse und Attributauswertung für 7 * E E.WERT := 30 T T.WERT := 28 R.UEBER := 28 R R.WERT := 30 F F.WERT := 7 X.UEBER:= 7 X X.WERT := 28 + T T.WERT := 2 R.UEBER := 30 R R.WERT := 30 * F F.WERT := 4 X.UEBER := 7 * 4 X X.WERT := 28 F F.WERT := 2 X.UEBER:= 2 X ε X.WERT := ε 2 ε E. Ploedereder, Seite 112
113 LL(1) Übersetzungsschemata kontextfreie Basisgrammatik ist LL(1) Idee für die Implementierung: Erweitere die Methode des rekursiven Abstiegs um semantische Aktionen für die Auswertung von Attributen LL(1) Steuertabelle gibt in jeder Situation A: Nichtterminalzeichen a: nächstes lexikalisches Element an, welche Produktion A εanzuwenden ist Auswertung des Attribute erfolgt wie rechts skizziert A ererbte Attribute von A zusammengesetzte Attribute von A β habe die Form {... } α 1 {... } α 2... α n {... } semantische Aktionen β Attribute der Symbole aus β E. Ploedereder, Seite 113
114 Erstelle für jedes Nichtterminalzeichen A eine Funktion nach dem Muster function A (... Parameter für die ererbten Attribute von A... ) : zusammengesetzte Attribute von A... lokale Variable für jedes Attribut, das einem Symbol auf der rechten Seite β einer Produktion A βzugeordnet ist... begin Bestimme, welche Produktion A βanzuwenden ist. Diese habe die Form A β 1 β 2... β n, wobei für jedes β i, 1 i n, drei Fälle möglich sind (1) β i ist Terminalzeichen (2) β i ist Nichtterminalzeichen (3) β i ist semantische Aktion {..... } Setze Rumpf nach folgendem Muster zusammen end Behandlung von β 1 ; Behandlung von β 2 ; Behandlung von β n ; E. Ploedereder, Seite 114
115 Behandlung von Terminalzeichen besitzen nur zusammengesetzte Attribute, die vom Scanner geliefert werden Aktionen if Terminalzeichen t mit momentan dem betrachteten lexikalischen Element ( look-ahead ) übereinstimmt then speichere die Attribute von t mit den dafür vorgesehenen lokalen Variablen der Funktion für A; beschaffe nächstes lexikalisches Element else beende die Funktion mit einer Fehlermeldung Behandlung von semantischen Aktionen Aktionen kopiere die Aktionen ein, wobei jedes Auftreten eines Attributs durch den Namen der entsprechenden lokalen Variablen ersetzt wird E. Ploedereder, Seite 115
116 Behandlung von Nichtterminalzeichen N Aktionen Berechene die zusammengesetzten Attribute z von N durch eine Zuweisung auf Grundlage der ererbten Attribute, welche wegen der L-Attributierung zur Verfügung stehen. z := N (... ererbte Attribute von N... ) Beispiel: X * F X ε (s.o.) function X (UEBERTRAG_X : E_ATT) : Z_ATT; var WERT_F : Z_ATT; UEBER_X : E_ATT; WERT_X : Z_ATT; begin if LEX_ELEMENT = * then call Lexer;{ beschafft nächstes lexikalisches Element } WERT_F := call F(); UEBER_X := WERT_F * UEBERTRAG_X; WERT_X := call X(UEBER_X); X := WERT_X; else X := UEBERTRAG_X; end; E. Ploedereder, Seite 116
117 Bemerkungen zu Attributgleichungen 1. Gleichung kann / darf nur Attribute der Symbole der Produktion referenzieren. 2. Gleichung für Attribut A der linken Seite der Produktion X... { X.A =... } X.A ist synthetisiertes Attribut 3. Gleichung für Attribut B eines Nichtterminals der rechten Seite der Produktion X... Y... { Y.B =... } Y.B ist ererbtes Attribut 4. Kein Attribut darf mehrfach durch Gleichungen definiert sein. 5. zusammengesetzt / ererbt ist Eigenschaft eines Attributs pro Nichtterminal, d.h. für E R sind untenstehende Aussagen zulässig E.A R.A zusammengesetzt ererbt 6. Mit jeder Produktion müssen Gleichungen für alle zusammengesetzten Attribute des Nichtterminals der linken Seite und für alle ererbten Attribute aller Nichtterminale der rechten Seite assoziiert sein. E. Ploedereder, Seite 117
118 5. Bottom-up Analyse Ziel: Rekonstruktion der Rechtsableitung eines Programms, wobei die Grammatik als eindeutig vorausgesetzt ist Beispiel: Rechtsableitung von ( id + id ) * id E T T * F T * id F * id ( E ) * id ( E + T ) * id ( E + F ) * id ( E + id ) * id ( T + id ) * id ( F + id ) * id ( id + id ) * id Grammatik E E + T T T T * F F F (E) id Rechtsableitung lässt sich in umgekehrter Reihenfolge rekonstruieren, wobei die vorliegende Zeichenkette von links nach rechts gelesen wird. E. Ploedereder, Seite 118
119 Umgekehrte Rechtsableitung E T T F E usw.... Schritt 6 Schritt 3 E Schritt 2 T T Schritt 5 Schritt 1 F F Schritt 4 F Schritt 0: ( id + id ) * id E. Ploedereder, Seite 119
120 Prinzip In jeder Situation wird die rechte Seite einer Produktion durch ihre linke Seite ersetzt, z.b. ( E + T ) * id ( E ) * id Achtung: Jede Satzform kann mehr als eine rechte Seite einer Produktion enthalten, z.b. enthält ( E + T ) * id die rechten Seiten E + T (wegen E E + T) T (wegen E T) id (wegen F id) Wegen der Eindeutigkeit der Grammatik (Voraussetzung!) kommt für die Rekonstruktion der Rechtsableitung genau eine rechte Seite in Frage. Dieser Teil der vorliegenden Satzform heißt Henkel (engl.: handle). Algorithmus: while noch keine vollständige Rechtsableitung rekonstruiert do bestimme Henkel ersetze Henkel end while E. Ploedereder, Seite 120
121 Implementierung des Algorithmus Idee: Verwende einen Stapel zur Zwischenspeicherung. Möglichkeit 1: Spitze des Stapels ist der Henkel reduce Reduziere den Henkel auf Stapel Möglichkeit 2: Spitze des Stapels ist nicht der Henkel shift repeat bereits gelesen lege nächstes lexikalisches Element auf den Stapel until Henkel erscheint an der Spitze des Stapels Wegen dieser Implementierung heißen deterministische Bottom-up-Parser auch shift-reduce Parser. E. Ploedereder, Seite 121
122 Beispiel: S E$ E E + T T T F T * F F (E) id Stapel Eingabe Aktion $ ( id + id ) * id $ shift $ ( id + id ) * id $ shift $ ( id + id ) * id $ reduce $ ( F + id ) * id $ reduce reduce $ ( E + id ) * id $ shift shift Stapel Eingabe Aktion $ ( E + id ) * id $ reduce reduce $ ( E + T ) * id $ reduce shift $ ( E ) * id $ reduce $ F * id $ reduce $ T * id $ shift shift $ T * id $ reduce reduce $ T $ reduce $ E $ accept E. Ploedereder, Seite 122
123 LR(k)-Methoden L R k Untersuchung der Eingabe von links Konstruktion einer Rechtsableitung (in umgekehrter Reihenfolge) Umfang des Lookaheads 3 Varianten von LR Methoden simple LR (SLR) lookahead LR (LALR) canonical LR (LR) Mächtigkeit LALR SLR LR G ist eine LR(k) Grammatik, wenn aus S * αxw αβw rm S * γyx αβy rm k : w = k : y folgt, dass α = γ und X = Y und x = y. Erstellungskosten E. Ploedereder, Seite 123
124 Architektur eines LR-Parsers Alle Varianten (SLR, LALR, LR) verwenden die gleiche Kontrolle, unterscheiden sich lediglich in ihren Steuertabellen. bereits gelesen Zm Xm Zm-1 Xm-1 Zustand Grammatik- Symbol nächstes Token t KONTROLLE Zustände charakterisieren den Inhalt des Stapels unter ihnen: Z beschreibt den Abschnitt Zm-2... Zo Steuer- Tabelle ACTION GOTO Eine Speicherung der Grammatiksymbole ist nicht notwendig! Sie werden der Übersichtlichkeit halber weiterhin angegeben. E. Ploedereder, Seite 124
125 Aufbau der Steuertabelle lex. Elemente.... S. ACTION GOTO t A.... Nichtterminalzeichen Zustände Aktion Zustand ACTION (s, t) = accept: error: reduce i: shift z: Analyse erfolgreich abgeschlossen Analyse ohne Erfolg abbrechen reduziere mit Produktion der Nummer i lege lexikalisches Element t und darüber Zustand z auf den Stapel E. Ploedereder, Seite 125
126 LR-Analyse Situationen der Analyse haben die Form (s 0 X 1 s 1 X 2... X m s m, a i a i+1... a n $) Fall 1 : Fall 2 : Fall 3 : ACTION (s m, a i ) = shift z Neue Situation: (s 0 X 1 s 1 X 2... X m s m a i z, a i+1... a n $) ACTION (s m, a i ) = reduce j Produktion j habe die Form A β mit β = r Zwischensituation: (s 0 X 1 s 1 X 2... X m-r s m-r, a i a i+1... a n $) Neue Situation: (s 0 X 1 s 1 X 2... X m-r s m-r As, a i a i+1... a n $) where s = GOTO(s m-r, A) ACTION (s m, a i ) = accept Analyse erfolgreich abschließen Fall 4 : ACTION (s m, a i ) = error Analyse mit Fehlermeldung abbrechen (besser: Fehlerbehandlung) E. Ploedereder, Seite 126
127 LR(1)-Algorithmus s := Anfangszustand; ERFOLG:= FALSE; FEHLER:= FALSE; repeat sei t nächstes lexikalisches Element; s Zustand an der Spitze des Stapels; case ACTION (s, t) of accept : ERFOLG := TRUE; error : FEHLER := TRUE; shift z : lege t auf Stapel; lege z auf Stapel; beschaffe nächstes lexikalisches Element; reduce i: /*(i): A β*/ entferne 2 x β Elemente vom Stapel; /* s sei jetzt an der Spitze des Stapels */ lege A auf Stapel; lege GOTO (s, A) auf Stapel; end case until ERFOLG or FEHLER E. Ploedereder, Seite 127
128 Beispiel: Grammatik für Ausdrücke (0) S E$ Steuertabelle: (1) E E + T (2) E T (3) T T * F (4) T F (5) F (E) (6) F id id s5 s5 + s6 r2 r4 * s7 r4 ( s4 s4 ) r2 r4 $ acc r2 r4 E 1 8 T 2 2 F r6 r6 r6 r6 6 s5 s s5 s4 10 sj ri shift in den neuen Zustand Nr. j reduce mit Produktion (i) sonst error s6 r1 r3 r5 s7 r3 r5 s11 r1 r3 r5 r1 r3 r5 E. Ploedereder, Seite 128
129 Beispiel: Fortsetzung, Analyse von id * id + id $ (0, id * id + id $) (0 id 5, * id + id $) (0 F 3, * id + id $) (0 T 2, * id + id $) (0 T 2 * 7, id + id $) (0 T 2 * 7 id 5, + id $) (0 T 2 * 7 F 10, + id $) (0 T 2, + id $) (0 E 1, + id $) (0 E 1 + 6, id $) (0 E id 5, $) (0 E F 3, $) (0 E T 9, $) (0 E 1, $) id s5 s5 s5 s5 + s6 r2 r4 r6 s6 r1 r3 r5 * s7 r4 r6 s7 r3 r5 ( s4 s4 s4 s4 ) r2 r4 r6 s1 1 r1 r3 r5 $ acc r2 r4 r6 r1 r3 r5 E 1 8 T F (0 E acc, $) E. Ploedereder, Seite 129
130 Die Syntax-Maschinen... S : E 1 2 E: E + T T 7 T: 8 T 9 * 10 F 11 F 12 F: ( E ) id 17 E. Ploedereder, Seite 130
131 ... und ihre Verknüpfung S : T: F: ε 1 E: ε 2 ε 2 ε 3 ε 3 [S.E] E 1 2 E + T T T * F F r 1 ( E ) id [S E.] 7 r ε 4 r 2 ε 2 ε 3 r 1 + weitere r -Übergänge, wobei jedem ε Übergang entsprechende spontane r - Übergänge ( Rückkehr ) vom Ende alternativer Syntaxmaschinen zugeordnet sind. Um ein E zu erkennen, springen wir zur Maschine für E. Wenn diese ein E erkannt hat, meldet sie Erfolg zurück und wir nehmen den Übergang für E. (Prinzip des rekursiven Abstiegs) Beobachtung: Dieser Automat (ohne r - Übergänge) erkennt genau alle möglichen Stapelinhalte eines LR-Parsers. Aber nach Determinierung überlagern sich oft die Endzustände (Reduktionszustände) mit anderen Zuständen. Die Entscheidung zwischen Reduktionen und Shifts erfolgt auf der Basis des nächsten Eingabezeichens. E. Ploedereder, Seite 131
132 Zugehöriges Zustandsdiagramm des DEA: I 0 E I 1 + I 6 T F I 9 * nach I 3 nach I 7 T I 2 * I 7 ( id F ( id nach I 4 nach I 5 I 10 nach I 4 nach I 5 F I 3 ( Dieser Automat heißt Präfixautomat ( id I 4 E I 8 ) + T nach I 2 id F nach I 3 I 5 I 11 nach I 6 E. Ploedereder, Seite 132
133 Konfliktauflösung: Falls ein Zustand des Präfixautomaten mehrere Endzustände der Syntaxmaschinen repräsentiert (und damit verschiedene Reduktionen möglich sind) oder falls ein solcher Zustand sowohl einen Endzustand als auch einen Nicht- Endzustand der Syntaxmaschinen repräsentiert (und damit sowohl eine Reduktion als auch ein Shift möglich ist)... muss dieser Konflikt durch Vorausschaumengen gelöst werden. Verschiedene Verfahren je nach Differenziertheit der Berechnung der Vorausschaumengen. SLR LALR LR Anmerkung: Die tatsächlichen Algorithmen zur Bestimmung des Präfixautomaten verwenden effizientere und z.t. differenziertere Methoden als hier mit den Syntaxmaschinen zur Erläuterung dargestellt ist. E. Ploedereder, Seite 133
134 Definition Sei G = ( N, T, P, S ) eine kfg. Dann ist A β 1 β 2 ein LR(0)-Item für G, falls A β 1 β 2 P. A β 1 β 2 ist gültig für ein zuverlässiges Präfix αβ 1, falls eine Ableitung S * αaw αβ 1 β 2 w rm existiert. Sei I eine Menge von LR(0)-Items zu G. Dann ist Abschluss(I) die kleinste Menge, so dass gilt: (i) I Abschluss(I) (ii) X α.yβ Abschluss(I) Y γ P Y. γ Abschluss(I) Sei I eine Menge von LR(0)-Items, sei Y N T. Dann ist Nachf(I, Y) = { X αy. β X α.y β I } Beispiel: S aac A Abb b Abschluss({ S a.ac }) = { S a.ac, A.Abb, A.b } = I 1 Nachf(I 1, A) = { S aa.c, A A.bb } Nachf(I 1, b) = { A b. } E. Ploedereder, Seite 134
135 Konstruktion des Präfixautomaten Eingabe: kfg G = ( N, T, P, S), sei G = ( N S, T, P { S S $ }, S ). Ziel: Präfixautomat LR(0) DEA = ( Q, N T, δ, q 0, E) Algorithmus q 0 := Abschluss({ S.S$}) Q := {q 0 } δ := {} foreach q Q and X N T do q := Abschluss( Nachf(q, X)) if q {} then Q := Q {q } δ := δ {(q, X, q )} -- q q fi od E := Q Definition: Die durch Nachf(q, X) berechnete Menge von LR-Items wird als Kern des durch sie charakterisierten Zustands bezeichnet (denn identische Kerne haben auch identische Abschlüsse). E. Ploedereder, Seite 135
136 Beispiel: Grammatik für Ausdrücke E I 0 : S.E E.E + T E.T T.T * F T.F F.(E) F.id ( id I 4 : F (.E) E.E + T E.T T.T * F T.F F.(E) F.id I 7 : T T *.F F.(E) F.id I 8 : F (E.) E E. + T T I 1 : S E. E E. + T I 5 : F id. I 9 : E E + T. T T. * F F + I 10 :T T * F. I 2 : E T. T T. * F I 3 : T F. I 6 : E E +.T T.T * F T.F F.(E) F.id I 11 :F ( E ). Verlauf der übrigen Pfeile analog E. Ploedereder, Seite 136
137 LR(0)-Parser Grundlegender Aufbau: Gegeben: Stack q i q i-1 kfg G = ( N, T, P, S) zugehöriger LR(0)-DEA = ( Q, N T, δ, q 0, E ) Aktions-Tabelle Aktion(q) :=... q 0 a Aktions- Tabelle Eingabe Steuerung Goto- Tabelle shift falls (A α.aβ) q, a T reduce A α falls (A α.) q, A S accept falls (S S.) q error sonst Goto-Tabelle Goto(q, X) = q gdw. ( q, X, q ) C δ; q Q, X N T E. Ploedereder, Seite 137
138 Definition: Ein LR(0)-Zustand, der geteilte Produktionen sowohl von der Form X α. -- reduce als auch von der Form Y α.β, β = β 1... β n ε, β 1 T -- shift oder Y β., X Y oder α β -- reduce enthält, heißt unzureichender Zustand. Wir sprechen dann von einem shift-reduce, bzw. einem reduce-reduce Konflikt. Beispiel: In unserer Ausdrucksgrammatik sind I 1, I 2 und I 9 unzureichend. Grammatik nicht LR(0) I 2 : E T. -- reduce E T. * F -- lies * (shift) Lösung: Auflösung des Konflikts durch Vorausschaumengen. Verschiedene Verfahren je nach Differenziertheit der Berechnung der Vorausschaumengen. SLR LALR LR E. Ploedereder, Seite 138
139 SLR(1)-Analyse Für SLR(1)-Verfahren werden (globale) FOLLOW-Mengen verwendet. FOLLOW(S) = { $ } FOLLOW(E) = { +, ), $ } FOLLOW(T) = { *, +, ), $ } FOLLOW(F) = { *, +, ), $ } Also im Zustand I 2 : E T. -- reduziere, wenn nächste Eingabe FOLLOW(E) E T. * F -- für * shift, sonst Fehler Voraussetzung: Menge der Zeichen für shift Menge der Zeichen für reduce = { } in jedem Zustand. (sowie die analoge Bedingung für mehrere reduce-aktionen in einem Zustand) Konstruktives Kriterium für SLR(1)-Eigenschaft der Grammatik E. Ploedereder, Seite 139
140 Konstruktion der Steuertabelle für SLR(1) aus dem Präfixautomaten, LR(0)-Mengen, FOLLOW(X) X NT 1. Für alle LR(0)-Mengen I i : wenn I t T i I j : action(i, t) = shift j wenn I X ΝT i Ij : goto(i, X) = j wenn [ A α.] I i : t FOLLOW(A) : A Startsymbol action(i, t) = reduce A α wenn [ S α.] I i : action(i, $) = accept S = Startsymbol Versuch der Doppelbelegung: Grammatik ist nicht SLR(1)! 2. alle unbesetzten Felder: error als Aktion 3. Startzustand ist j : [ S.α ] I j S = Startzustand E. Ploedereder, Seite 140
141 kanonisches LR-Verfahren Determinierung der reduce -Aktionen durch lokale Folgemengen. Beobachtungen: Die globale Folgemenge eines Nichtterminals ist die Vereinigung der lokalen Folgemengen für alle Vorkommnisse des Nichtterminals in der Grammatik. Die bereits auf dem Stapel liegenden Teile von Henkeln, die noch auf ihre Vervollständigung warten, schränken aber die Menge der wirklich als nächstes Eingabezeichen eines syntaktisch korrekten Programms erlaubten Terminale deutlich ein. Diese Menge (die lokale Folgemenge) ist berechenbar. Methode: In der Konstruktion des Präfixautomaten werden Zustände (im Gegensatz zur SLR und LALR Analyse) auch unterschieden, wenn die lokalen Folgemengen unterschiedlich sind. E. Ploedereder, Seite 141
142 Konstruktion des LR(1)-Automaten LR(1)-Verfahren wie LR(0)-Verfahren mit folgenden Änderungen: 1. Erweiterungen der Parse-Items durch lokale Folgemengen, d.h. statt X α.β nun [ X α.β, {...} ], wobei {...} die lokale Folgemenge ist. Der Kern des Startzustandes erhält {$} als Folgemenge. 2. Abschluss-Funktion bestimmt Folgemengen, d.h. Abschluss(I): [ X α.yβ, F ] I Y γ P [ Y. γ, F ] I FIRST(β), wenn ε FIRST(β) F = FIRST(β) - ε F, sonst 3. Nachfolge-Funktion propagiert Folgemengen, d.h. Nachf(I, Y) : [ X α.yβ, F ] [ X αy.β, F ] 4. LR(1)-Automat wird wie LR(0)-Automat konstruiert, wobei aber Zustandsgleichheit die Folgemengen der Parse-Items mit einbezieht. E. Ploedereder, Seite 142
143 Beispiel I: NT = { S, A, B, X, Y } T = { a, b, c, d, e } P = { S ax by cae X Bd Ab Y Bc Aa A d B d } q1: S.aX, {$} S.bY, {$} S.cAe, {$} q2: S a.x {$} q3: S b.y {$} q4: S c.ae {$} X.Bd {$} Y.Bc {$} A.d {e} X.Ab {$} Y.Aa {$} A.d {b} A.d {a} B.d {d} A B.d {c} d q14: S ca.e {$} d a d b C q7: A d. {e} A q5: A d. {b} q6: A d. {a} B B d. {d} B d. {c} A B q8: X B.d {$} q10: Y B.c {$} X q9: X A.b {$} Y q11: Y A.a {$} q12: S ax. {$} q13: S by. {$} E. Ploedereder, Seite 143
144 LR(1) Tabelle a b c d e $ A B X Y 1 s2 s3 s4 2 s s s r8 r9 6 r8 r9 7 r8 8 s15 9 s16 10 s17 11 s18 12 r1 13 r2 14 s19 15 r4 16 r5 17 r6 18 r7 19 r3 E. Ploedereder, Seite 144
145 LALR-Verfahren Eine Mischform von SLR und kanonischem LR-Verfahren: Es werden in der Konstruktion des Präfixautomaten Zustände mit unterschiedlichen lokalen Folgemengen nicht unterschieden. Es werden aber in der Bestimmung der Zustände die jeweils entstehenden lokalen Folgemengen zur Vorausschaumenge vereinigt. 1. Erweiterung der Parse-Items, Nachfolge- und Anschluss-Funktion wie im LR(1)-Verfahren. 2. Konstruktion des LR(0)-Automaten, wobei Zustandsgleichheit (weiterhin) nur auf den geteilten Produktionen basiert und die Folgemenge vereinigt werden, wenn ein Zustand auf verschiedenen Pfaden des Automaten erreicht wird. E. Ploedereder, Seite 145
146 Beispiel I (LALR): NT = { S, A, B, X, Y } T = { a, b, c, d, e } P = { S ax by cae X Bd Ab Y Bc Aa A d B d } q1: S.aX, {$} S.bY, {$} S.cAe, {$} q2: S a.x {$} q3: S b.y {$} q4: S c.ae {$} X.Bd {$} Y.Bc {$} A.d {e} X.Ab {$} Y.Aa {$} A.d {b} A.d {a} B.d {d} A B.d {c} d q14: S ca.e {$} d a d b C q7: A d. {e} A q5: A d. {b} q6: q5/6: A d A {b, d. a} {a} B d. {d} B d. {c} B B d {d, c} A B q8: X B.d {$} q10: Y B.c {$} X q9: X A.b {$} Y q11: Y A.a {$} q12: S ax. {$} q13: S by. {$} E. Ploedereder, Seite 146
147 Unterschiede in den Tabellen LR(1)-Tabelle (Ausschnitt): a b c d e $ A B X Y 1 s2 s3 s4 2 s s s r8 r9 6 r8 r9 7 r8 SLR(1)-Tabelle (Ausschnitt): a b c d e $ A B X Y 1 s2 s3 s4 2 s s s7 14 5/6 r8 r8 r9 r9 r8 7 r8 r8 r8 LALR(1)-Tabelle (Ausschnitt): a b c d e $ A B X Y 1 s2 s3 s4 2 s s s7 14 5/6 r8 r8 r9 r9 7 r8 E. Ploedereder, Seite 147
148 Parser-Verhalten im Fehlerfall Eingabe: ade bdd LR(1): q 1 ade s2 q 1 bdd s3 q 1 q 2 de s5 q 1 q 3 dd s6 q 1 q 2 q 5 e error q 1 q 3 q 6 d error SLR(1): q 1 ade s2 q 1 bdd s3 q 1 q 2 de s5 q 1 q 3 dd s5 q 1 q 2 q 5 e r8! q 1 q 3 q 5 d r9! q 1 q 2 q 9 e error q 1 q 3 q 10 d error LALR(1): q 1 ade s2 q 1 bdd s3 q 1 q 2 de s5 q 1 q 3 dd s5 q 1 q 2 q 5 e error q 1 q 3 q 5 d r9! q 1 q 3 q 10 d error E. Ploedereder, Seite 148
149 Das obenstehende Beispiel demonstriert das unterschiedliche Verhalten der verschiedenen Verfahren für eine SLR(1) Grammatik. Für den Compilerbauer deutlich unangenehmer als die obigen irrigen Reduktionen bei fehlerhaftem Eingabetext ist die Situation, in der das Verfahren SLR oder LALR vorgegeben ist und entstehende (reduce-reduce) Konflikte nur wegen der ungenaueren Vorausschaumenge nicht auflösbar sind. Standardprobleme in der Entwicklung von bottom-up Parsern Die gegebene Grammatik ist nicht LALR(1) führt zu shift / reduce und reduce / reduce Konflikten (siehe nächste Folien). E. Ploedereder, Seite 149
150 Beispiel II (nicht SLR(1), aber LALR(1) und LR(1)): NT = { S, A, B, X, Y } T = { a, b, c, d, e } P = { S ax by cae X Be Ab Y Bc Aa A d B d } q1: S.aX, {$} S.bY, {$} S.cAe, {$} q2: S a.x {$} q3: S b.y {$} q4: S c.ae {$} X.Be {$} Y.Bc {$} A.d {e} X.Ab {$} Y.Aa {$} A.d {b} A.d {a} B.d {e} A B.d {c} d q14: S ca.e {$} d a d b C q7: A d. {e} A q5: A d. {b} q6: A d. {a} B B d. {e} B d. {c} A B q8: X B.d {$} q10: Y B.c {$} X q9: X A.b {$} Y q11: Y A.a {$} q12: S ax. {$} q13: S by. {$} E. Ploedereder, Seite 150
151 Beispiel III (nicht SLR(1) und LALR(1), aber LR(1)): NT = { S, A, B, X, Y } T = { a, b, c, d, e } P = { S ax by cae X Ba Ab Y Bc Aa A d B d } q1: S.aX, {$} S.bY, {$} S.cAe, {$} q2: S a.x {$} q3: S b.y {$} q4: S c.ae {$} X.Ba {$} Y.Bc {$} A.d {e} X.Ab {$} Y.Aa {$} A.d {b} A.d {a} B.d {a} A B.d {c} d q14: S ca.e {$} d a d b C q7: A d. {e} A q5: A d. {b} q6: A d. {a} B B d. {a} B d. {c} A B q8: X B.d {$} q10: Y B.c {$} X q9: X A.b {$} Y q11: Y A.a {$} q12: S ax. {$} q13: S by. {$} E. Ploedereder, Seite 151
152 1. shift / reduce Konflikt Der Parser kann nicht entscheiden, ob shift- oder reduce-operation durchzuführen ist. Beispiel: Id_list Id_list, id id Declaration Id_list : Type_mark ; id : Type_mark renames Name; In Situation... Stapel Eingabe..... id : kann entweder : gelesen werden (shift), oder id zu Id_list reduziert werden (reduce). Lösung: Grammatik erweitern zu:... Id_list : Type_mark renames Name; und (später) prüfen, ob Id_list nur ein Element hat. Beachte: Auch längerer Lookahead fester Länge kann solche Konflikte nicht auflösen, wenn der Diskriminator (im Beispiel renames und ; ) vom Entscheidungspunkt durch beliebig viele Token getrennt ist. E. Ploedereder, Seite 152
153 2. reduce / reduce Konflikt Stmt id ( Par_list ) Var := Expr Par_list Par_list, Par Par Par id Expr id ( Expr_list ) id Expr_list Expr_list, Expr Expr Var id id ( Expr_list ) In Situation Stapel Eingabe..... id ( id, id Was tun??? Reduktion id Par Prozeduraufruf Reduktion id Expr indizierte Variable E. Ploedereder, Seite 153
154 Lösung des reduce / reduce Konflikts Drachenbuch: Unterscheidung während der lexikalischen Analyse procid für Prozedurnamen arrid für Feldname Dies ist jedoch nur bei einer Ein-Pass-Analyse möglich, die die Symboltabelle gleichzeitig zur lexikalischen Analyse aufbaut. Außerdem werden dabei architektonische Gesichtspunkte verletzt. Besser: (... Umformung der Grammatik...) Stmt Expr Name Expr_list Name Name := Expr Name id ( Expr_list ) id Expr_list, Expr Expr und (spätere) Prüfung, dass die aktuellen Parameter von Prozeduraufrufen nur ids sein dürfen. E. Ploedereder, Seite 154
155 Implementierung der Steuertabelle Beobachtungen: Zeilen der ACTION-Tabelle sind dünn besetzt manche Zeilen sind identisch Spalten der GOTO-Tabelle sind dünn besetzt Fehlereinträge in ACTION-Tabelle können durch Reduktionen ersetzt werden ( Fehler werden evtl. später erkannt, jedoch mehr Gleichförmigkeit in Zeilen) Fehlereinträge in GOTO-Tabelle werden nicht benötigt ( Ersetzung durch häufigsten Spalteneintrag) Ermöglicht komprimierte Darstellung E. Ploedereder, Seite 155
156 Sätze 1. LL(1) LR(1), aber 2. SLL(1) SLR(1), LL(1) LALR(1) Beweis (für 2.) S aa bb A Ca Db offensichtlich LL(1) und SLL(1) B Cc Da C E D E (alternative Produktionen unterscheidbar) E ε Ausschnitt des Präfixautomaten S a.a S b.b LALR A.Ca B.Cc A.Db B.Da C.E{a} C.E{c} D.E{b} D.E{a} E. E. nicht LALR(1) E E D E.{a, b} C E.{a, c} E D E. C E. E SLR FOLLOW(C) = {a, c} FOLLOW(D) = {a, b} nicht SLR(1) E. Ploedereder, Seite 156
157 Zusammenfassung: LR, SLR, LALR Kanonisches LR Verfahren: arbeitet mit lokalen Folgemengen und unterscheidet Zustände mit unterschiedlichen Folgemengen viele Zustände ( >> SLR oder LALR Zustände) SLR: bestimmt den Lookahead für jeden unzureichenden Zustand durch die globalen FOLLOW-Mengen der Grammatik viele Grammatiken nicht SLR, aber LR oder LALR; irrige Reduktionen bei fehlerhafter Eingabe LALR: bestimmt den Lookahead für jeden unzureichenden Zustand aus dem Zustandsdiagramm Menge der Zustände ) SLR Zustände i.allg. lokalerer Lookahead als SLR, aber dennoch noch irrige Reduktionen bei fehlerhafter Eingabe möglich E. Ploedereder, Seite 157
158 Bottom-up Verfahren Zusammenfassung baut Strukturbaum von der Eingabe ausgehend... durch Ersetzung der rechten Seiten von Produktionen, die auf die gestapelte, bisher analysierte und teil-reduzierte Eingabe passt. (Begriff der handle auf dem Stapel) In jedem Analyseschritt entweder lesen, reduzieren, Fehler oder Gesamterfolg. Benennung shift/reduce Verfahren Erkennung der handle durch LR Verfahren (SLR, LALR, kanonisches LR) LR Verfahren unterscheiden sich nur in der Ausprägung der Steuertabelle. Im Fehlerfall erkennt das kanonische Verfahren den Fehler sofort; SLR und LALR führen eventuell noch Reduktionen, bei Lookahead der Länge 1 aber kein weiteres Lesen, durch. E. Ploedereder, Seite 158
159 Bottom-up Parsing für nicht-lr Grammatiken insbesondere für mehrdeutige Grammatiken ergibt immer Konflikte Parsergeneratoren bieten u.u. Möglichkeiten an, die Konflikte durch Präferenz-Angaben oder Defaults aufzulösen. Z.B. YACC, Bison erlaubt die Angabe von Präzedenz und Assoziativität für Terminale zur Auflösung von shift/reduce Konflikten zieht shift dem reduce vor zieht bei reduce/reduce Konflikten die jeweils erstgenannte Produktion vor. Vorsicht: Bei eindeutigen Grammatiken führen solche Eingriffe immer dazu, dass L(G) vom Parser erkannte Sprache, d.h. der Parser ist garantiert fehlerhaft! Bei mehrdeutigen Grammatiken darf die gewählte Präferenz nur dazu führen, dass die abgelehnte Alternative allenfalls eine zweite Rechtsableitung der Eingabe geliefert hätte. Diese Bedingung ist oft schwierig zu verifizieren. E. Ploedereder, Seite 159
160 Error-Produktionen Viele Parsergeneratoren bieten spezifische Methoden an, bei illegaler Syntax der Eingabe eine wohldefiniertes Wiederaufsetzen der Analyse zur weiteren Bearbeitung der Eingabe durchzuführen. (siehe spätere Ausführungen) typische Realisierung: X β<error> α wird von LR-Analyse wie eine normale Produktion behandelt (Achtung: Konflikte möglich) es gibt keine Produktionen mit <error> als linke Seite. <error> hat im Fehlerfall folgende Semantik für den Parser: suche Zustand im Stapel, der mit <error> einen Übergang besitzt; reduziere alle darüber liegenden Einträge auf <error> (über-)lese Eingabe, bis α erreicht und erkannt ist führe die obige Reduktion durch, gib eine Fehlermeldung aus (typisch: ill-formed X ) und annotiere die so reduzierte Eingabe damit + Heuristiken, wenn mehrere Error-Produktionen in Frage kommen Vorsicht: wenn in der Eingabe kein α mehr kommt, wird die gesamte Resteingabe überlesen! E. Ploedereder, Seite 160
161 6. Bottom-up syntax-gesteuerte Übersetzung S-Attributierung und bottom-up Analyse Idee: Speichere Attribute von Grammatiksymbolen auf einem Stapel, der parallel zum Parser-Stapel organisiert ist... Zustand über Symbol A. Z A Attribute von A Parser- Stapel Attribut- Stapel Bei einer Reduktion werden die Werte der beteiligten Attribute berechnet. E. Ploedereder, Seite 161
162 ...I D...I C...I B D.Attribute C.Attribute B.Attribute Reduktion mit A B C D A.Attribute = f (B.Attribute, C.Attribute, D.Attribute)...I A A.Attribute Berechnung der Attribute von A kann unmittelbar vor der Reduktion erfolgen. E. Ploedereder, Seite 162
163 Implementierung der L-Attributierung Prinzip A B C D Abmachungen Ausrechnungsreihenfolg e ererbte Attr. von A ererbte Attr. von B Attr. des Teilbaums mit Wurzel B zusammengesetzte Attr. von B ererbte Attr. von C Attr. des Teilbaums mit Wurzel C zusammengesetzte Attr. von C ererbte Attr. von D Attr. des Teilbaums mit Wurzel D zusammengesetzte Attr. von D zusammengesetzte Attr. von A Ererbte Attribute eines Nichtterminalzeichens N sollen vor der Erkennung der aus N ableitbaren Strukturen errechnet werden. Zusammengesetzte Attribute eines Nichtterminalzeichens N sollen nach der Erkennung der aus N ableitbaren Strukturen errechnet werden. E. Ploedereder, Seite 163
164 Bottom-up Implementierung von L-attributierten Übersetzungschemata semantische Aktionen erfolgen stets im Zusammenhang mit Reduktionen d.h. Auslösung der semantischen Aktionen A 1, A 2,... in L... { A 1 }... { A 2 }... muss an Reduktionen gekoppelt sein. Idee: Einführung von Markierungsvariablen für semantische Aktionen z.b. L α 1 { SA 1 } α 2 { SA 2 } α 3 { SA 3 } L α 1 M 1 α 2 M 2 α 3 { SA 3 } M 1 ε{ SA 1 } M 2 ε{ SA 2 } Beispiel: E TR R + T { print( + ) } R R ε T num { print(num.val) } E TR R + T M R R ε T num { print(num.val) } M ε { print( + ) } geht über in E. Ploedereder, Seite 164
165 Ererbte Attributierung und bottom-up Parsing Bei L-attributierten Grammatiken können folgende Vererbungsformen auftreten A B C D E F ererbtes Attribut Attribute, die von Geschwistern ererbt werden, sind bekannt, wenn E an der Spitze des Parser-Stapels auftaucht E E.Attribute D D.Attribute D D.Attribute C C.Attribute C C.Attribute B B.Attribute B B.Attribute... später... E. Ploedereder, Seite 165
166 Beispiel: Übersetzungsschema Declaration Type { Id_list.TYP := Type.TYP } Id_list Type integer { Type.TYP := integer } real { Type.TYP := real } Id_list { Id_list 1.TYP := Id_list.TYP } Id_list 1, id { EINTRAG_ST(id.POS, Id_list.TYP) } id { EINTRAG_ST(id.POS, Id_list.TYP) } Für Deklarationen Declaration integer ANNA, BERTA, CARLA ergibt sich folgender Strukturbaum (mit Attributierung) Type Id_list integer Id_list, id Id_list, id CARLA id BERTA ANNA E. Ploedereder, Seite 166
167 Aktionen eines Bottom-up Parsers wären Stapel integer Type ANNA Type Id_list Type Id_list, Type Id_list, BERTA Type Id_list Type Id_list, Type Id_list, CARLA Type Id_list Declaration Eingabe integer ANNA, BERTA, CARLA ANNA, BERTA, CARLA, BERTA, CARLA, BERTA, CARLA BERTA, CARLA, CARLA, CARLA CARLA Reduktion Type integer Id_list id Id_list Id_list, id Id_list Id_list, id Declaration Type Id_list E. Ploedereder, Seite 167
168 Beachte, dass bei Reduktionen mit Id_list id Id_list Id_list, id der Eintrag Type.TYP jeweils direkt unterhalb der rechten Seite der benutzten Produktion liegt. Declaration Type Id_list Type integer { ATT[ top ] := integer } real { ATT[ top ] := real } Id_list Id_list 1, id { EINTRAG_ST( ATT[ top ], ATT[ top-3 ]) } id {EINTRAG_ST( ATT[ top ], ATT[ top-1 ]) } Dabei bezeichnet ATT den Stapel für Attribute d.h. id, Id_list Type Parser top top-1 top-2 top-3 id.pos integer ATT und id Type Parser top top-1 id.pos integer ATT E. Ploedereder, Seite 168
169 Achtung Position von Attributen muss bekannt sein! ererbt zusammengesetzt A B C { C.e := B.z } D B E C { C.e := B.z } C c { C.z := f (... C.e... ) } irgendeine Funktion 2 Situationen sind bei Reduktion C c möglich 1. Benötigte Attribute liegen bei Position top-1 2. Benötigte Attribute liegen bei Position top-2 C C.z =? B B.Attribute top C C.z =? top E E.Attribute B B.Attribute E. Ploedereder, Seite 169
170 Problem kann mit Hilfe von Markierungsvariablen gelöst werden: A B C { C.e := B.z } D B E M C { M.e := B.z, C.e := M.z } C c { C.z := f (... C.e... ) } M ε { M.z := M.e } Das für die Berechnung von C.z benötigte Attribut C.e liegt jetzt direkt unterhalb der Position von C. Realisierung für Bottom-up Analyse: A B C D B E M C C c { C.z := f (... ATT[ top-1 ]... ) } M ε { M.z := M.e } E. Ploedereder, Seite 170
171 Problematisch im Zusammenhang mit der Bottom-up Analyse sind die vom Vaterknoten ererbten Attribute. Ererbte Attribute von A liegen direkt unterhalb A von B (Evtl. wurde eine Markierung X... M A... und M ε vorgenommen!) Wie oben gezeigt, können auch die anderen B... Attribute auf feste Positionen gerückt werden. C Fall 1: Die ererbten Attribute C.e müssen lediglich von anderen Attributen links von C kopiert werden. Zugriffe über die bekannten Positionen dieser Attribute leicht möglich. Fall 2: Ererbte Attribute C.e müssen erst errechnet werden, d.h. C.e = f (... B.a... ), wobei sich B links von C befindet (s.o.). Lösung Markierung setzen semantische Aktionen A... M C M.e... := B.a, C.e := M.z M ε M.z := f (... M.e... ), wobei M.e an fester Stelle steht E. Ploedereder, Seite 171
172 Zusammenfassung Bei der Implementierung von L-attributierten Übersetzungsschemata durch Bottom-up Verfahren spielen Markierungen A... M... M ε eine wichtige Rolle. Sie erlauben die folgenden Normierungen: Semantische Aktionen werden nur im Zusammenhang mit Reduktionen ausgeführt. Die Position von Attributen kann relativ zur Spitze des Attributstapels vorab bestimmt werden. Ererbt Attribute können rechtzeitig berechnet werden. Aber die Einführung von Markierungen zerstört eventuell die LR(1) Eigenschaft! E. Ploedereder, Seite 172
173 Satz: G ist LL(1); G = G, um Markierungssymbole erweitert G ist LL(1) [ und LR(1) ] aber leider G ist LR(1); G = G, um Markierungssymbole erweitert G ist LR(1) Beispiel: G: S L { L.count = 0 } L L 1 1{ L 1.count = L.count + 1 } ε { print( L.count ) } G ist LR(1) G : S M 1 L L M 2 L 1 1 ε M 1 ε M 2 ε G ist nicht LR(1) L M 2.L 1 { $ } L. M 2 L 1 { $ } L. { 1 } M 2. { 1 } reduce/reduce Konflikt M 2 S.M 1 L { $ } M 1. { $, 1 } M 1 S M 1.L { $ } L. M 2 L 1 { $ } L. { $ } M 2. { 1 } E. Ploedereder, Seite 173
174 7. Semantische Analyse Prüfung der kontext-sensitiven Korrektheitsbedingungen (z.b. Typprüfungen) Aufbau der Symboltabelle Attributierung des Syntaxbaums, bzw. Generierung des Zwischencodes durch syntax-gesteuerte Übersetzung Also... syntakt. semant.. Analyse Analyse... Syntaxbaum oder syntaxgesteuerte semantische Analyse Zwischencode E. Ploedereder, Seite 174
175 Grundsätzliche Methoden der semantischen Phase 1. Übersetzungsschemata beschränkt auf (S- und) L-Attributierung geeignet für einfache, compiler-ähnliche Problemlösungen (sowie Syntaxbaum-Aufbau) 2. Aufbau des Syntaxbaums mit nachfolgender Traversierung für die semantische Analyse (und Synthese) in Compilern für komplexere Programmiersprachen explizite Codierung der semantischen Regeln Flexibilität in der Traversierungsstrategie 3. Attributgrammatiken Verallgemeinerung (und Formalisierung) der Übersetzungsschemata Spezifikation der Berechnung der Attribute evtl. automatische Generierung von 2. E. Ploedereder, Seite 175
176 Aufbau eines Syntaxbaums durch synthetisierte Attribute Beispiel: E E 1 + T { E.node := make_node( +, E 1.node, T.node) } T { E.node := T.node } T T 1 * F { T.node := make_node( *, T 1.node, F.node) } F { T.node := F.node } F ( E ) { F.node := E.node } num { F.node := make_leaf( num.value) } Anmerkung: Komprimierung des Strukturbaums zum Syntaxbaum z.b. durch das Durchreichen der Knoten wie bei E T E. Ploedereder, Seite 176
177 Beispiel: MITTE := (LINKS + RECHTS) div 2 := statt Assignment MITTE div Variable := Expression + 2 id Term LINKS RECHTS Term div Factor Factor constant ( Expression ) Expression... + Term... E. Ploedereder, Seite 177
178 Traversierung des Syntaxbaums allgemeines Schema: procedure besuche (X: Knoten) is begin behandle X; -- N für alle UX : Unterknoten(X) loop -- LR oder RL besuche (UX); end loop; behandle (X); -- N end; Traversierungsstrategien: NLRN (Node-Left-to-Right-Node) = L-Attributierung LRN = S-Attributierung aber auch z.b. RLN NRLRN usw. möglich. Aber Vorsicht vor zyklischen Abhängigkeiten! E. Ploedereder, Seite 178
179 Zyklische Abhängigkeiten p : A B B.a = B.b p r : C... C.b = C.a q : B CD C.a= B.a D.a= C.b B.b = D.b q B.a B.b r C.a C.b s D.a D.b s : D... D.b = D.a globale zyklische Abhängigkeit aber mit s : D... D.b = 7 s D.a D.b obige zyklische Abhängigkeit nicht existent globale Zyklen sind Eigenschaften des speziellen Strukturbaums Anmerkung: r : C C.b = if P then C.a else 7... Zyklenfreiheit ist für nicht-strikte Funktionen unentscheidbar s: D D.b = if not P then D.a else 7 E. Ploedereder, Seite 179
180 Ein Notationsschema zur Spezifizierung statischer Semantik (in Anlehnung an den Formalismus von Attributgrammatiken) bestehend aus: 1. der (E)BNF der Syntaxproduktionen, ergänzt mit: 2. Attributdeklarationen 3. Attributgleichungen ( rules ) 4. Gültigkeitsbedingungen ( conditions ), die für legale Programme erfüllt sein müssen. Beispiel 1: Klammernde Syntax < procedure > ::= procedure id 1... begin : : end id 2 ; attribute id.token condition id 1.Token = id 2.Token -- muss derselbe id sein E. Ploedereder, Seite 180
181 Beispiel 2: Namensbindung und Typprüfung < assignment>::= < name > := < expr > attribute name.typ, expr.typ, name.st rule name.st = assignment.st condition name.typ = expr.typ < name > ::= id attribute name.def rule name.def = lookup(id.token, name.st) name.typ = name.def.typ < expr 1 > ::= < term> and < expr 2 > rule expr 1.Typ = boolean condition term.typ = boolean expr 2.Typ = boolean... E. Ploedereder, Seite 181
182 Beispiel 2a: Namensbindung und Typprüfung < assignment>::= < name > := < expr > attribute name.typ, expr.erwtyp, name.st, name.def rule name.st = assignment.st expr.erwtyp = name.typ < name > ::= id rule name.def = lookup(id.token, name.st) name.typ = name.def.typ < expr 1 > ::= < term> and < expr 2 > rule expr 2.ErwTyp = boolean term.erwtyp = boolean condition expr 1.ErwTyp = boolean Anmerkung: Oft (theoretisch immer) können ererbte Attribute durch abgeleitete Attribute (und umgekehrt) ersetzt werden, wobei Konsistenzprüfungen im Baum nach oben (unten) verschoben werden. E. Ploedereder, Seite 182
183 Begiffe: Namensbindung Bestimmung der gültigen Definition (i. Allg. Deklaration) für jeden im Programm auftretenden Bezeichner (Namen). statische Namensbindung Die gültige Definition wird aus dem Programmtext statisch abgeleitet. dynamische Namensbindung Die bei der Ausführung (ohne bereits abgeschlossene Unterprogrammaufrufe) zuletzt durchlaufene Definition des Bezeichners ist die gültige Definition. Gültigkeitsbereich einer Deklaration Bereich des Programmtextes, in dem die Deklaration prinzipiell benennbar ist. Sichtbarkeit (Benennbarkeit) einer Deklaration nur im Gültigkeitsbereich, aber möglicherweise weiter eingeschränkt (z.b. Verdeckung) E. Ploedereder, Seite 183
184 Signatur eine Deklaration Besteht aus dem deklarierten Bezeichner und ggf. (nach Sprachregeln) Anzahl und Typ von Parametern und Resultat im Fall von Unterprogrammen und ähnlichen Konstrukten. Homographe nach Sprachregeln identischer Bezeichner, bzw. Bezeichner und Signatur. Verdeckungsprinzip: lokale Deklarationen verdecken weniger lokale Homographen. X : Integer; -- X 1 procedure P (A: Integer); -- P 1... declare X : Float; -- verdeckt X 1 procedure P (A: Float); begin P(X); -- verdeckt P 1 oder nicht abhängig von -- Sprachregeln E. Ploedereder, Seite 184
185 Statische oder Dynamische Namensbindung A : Integer := 5; -- A 1 procedure X is begin... print(a); A X end X; procedure Y is A : Float := 1.0; -- A 2 begin... X; X Y end Y; X; -- X 1 Y; statische Bindung: A X A 1 Ergebnis: 5 5 dynamische Bindung: im Aufruf X 1 : A X A 1 Ergebnis: im Aufruf X y : A X A 2 E. Ploedereder, Seite 185
186 Symboltabellen Einträge beschreiben Attribute von Namen NAME ATTRIBUTE Attribute können sein Typ Index- und Komponententypen bei Feldern Zahl und Typ der Parameter von Unterprogrammen Blocknummer ( Schachtelungstiefe ) relative Adresse... Operationen auf Symboltabellen Einfügen Suchen Verschieben (zur Darstellung von Blockstrukturen) Löschen E. Ploedereder, Seite 186
187 Zeitpunkt der Eintragungen i.allg. bereitet der LEXER die Eintragungen von Namen vor z.b. bei Deklarationen var MAX, MORITZ 1. durch die Generierung von Token (und Tokentabelle) 2. für Sprachen ohne lokale Deklarationen, Eintrag in die Symboltabelle PARSER stößt durch semantische Aktionen Ergänzungen an z.b. Typ, Blocknummer, Verbindung von Parametern zu Prozeduren statisch semantische Analyse macht den Rest Proc_declaration id Par_list Angaben zu den Adressen für globale Variablen und Konstanten können relative Adressen bezüglich des statischen Datenbereichs vergeben werden. für lokale Variablen können relative Adressen bezüglich Aktivierungsblöcken zugeteilt werden. Par..... E. Ploedereder, Seite 187
188 Organisation von Symboltabellen prinzipiell alle Datenstrukturen, welche Einfügungen, Suchoperationen und variable Größe unterstützen, z.b. (verkettete) Listen, Baumstrukturen besonders beliebt: Hash-Tabellen 0 hash("id") Darstellung von Gültigkeitsbereichen Alternative 1: Symboltabellen trennen in aktiven und passiven Abschnitt Namen beim Verlassen von Gültigkeitsbereichen in passiven Abschnitt schieben ( stapelartige Organisation ) Alternative 2: Jeder Gültigkeitsbereich (Block, Prozedur, Record, Recordkomponenten, formale Parameter...) erhält seine eigene Symboltabelle E. Ploedereder, Seite 188
189 Zur Alternative 1 (Stapelorganisation) Problem: in einer Situation wie procedure EINS var X :... procedure ZWEI var X :... begin... X... end {ZWEI} begin..... X..... end {EINS} muss beim Zugriff auf X in Zusammenhang mit ihrer Verwendung in ZWEI der richtige Eintrag für X in der Symboltabelle genommen werden Lösung: Hash-Listen stapelartig organisieren, Einfügen / Löschen am Anfang bei Betreten / Verlassen eines Gültigkeitsbereichs : :.. X..... X... : : Spitze des Stapels E. Ploedereder, Seite 189
190 Zur Alternative 2 (separate Symboltabellen) 1 X : Real; Scope-Tree 2 procedure Demo 3 (Y : Real) is X : Integer; 4 5 3a type Rec is record X : Real; end record; R : Rec; begin X := 7; declare X : Boolean; begin X := true; R.X := Y; end; declare X : Integer; begin X := 5; end; X := X + 2; end Demo; Demo (Y => X); 5 X X Demo Y X Rec R X 3a X E. Ploedereder, Seite 190
191 Verwendung der Symboltabelle Eintrag eines Namens und erster Attribute (z.b. Typ) bei semantischer Analyse Bei weiterem Auftreten des Namens im Programm Suche nach dem zutreffenden Eintrag in der Symboltabelle (Namensbindung, lookup ) Entsprechende Bezüge auf Symboltabellen-Einträge werden als semantische Attribute im Struktur-, bzw. Syntaxbaum verwendet. Beobachtung Die in der Symboltabelle gespeicherten Attribute eines Namens sind i.allg. auch über den Syntaxbaum-Knoten seiner Deklaration erreichbar. Idee: Verwende Deklarations-Knoten als ST-Eintrag. Verwende Knoten-Bezüge als semantische Attribute. Symboltabelle wird überflüssig!! Realisiere Namensbindung über der Menge der Deklarations- Knoten. E. Ploedereder, Seite 191
192 Klassifizierung der Attribute im Syntaxbaum syntaktische Attribute: spannen den Baum gemäß der syntaktischen Struktur des Eingabeprogramms auf. (kontext-freie Information) semantische Attribute beinhalten (meist kontext-abhängige) Information über die semantische Bedeutung des syntaktischen Konstrukts. erweitern Syntaxbaum zu einem semantischen Netz. E. Ploedereder, Seite 192
193 Beschreibung des (attributierten) Syntaxbaums z.b. in IDL (Interface Description Language, Lamb, Nestor, ~ 1980) (Vereinfachte) BNF von IDL: Class_definition ::= Class_id ::= Class_id { Class_id}* ε Node_definition ::= Class_id Attribute_decl {, Attribute_decl}* Attribute_decl ::= Attribute_id : Attribute_type Attribute_type ::= Class_id Token predef_type seq of Attribute_type Class_id ::= id Attribute_id ::= id... E. Ploedereder, Seite 193
194 IDL Semantik : 1) A ::= B C D B, C und D sind Unterklassen von A. A ist Oberklasse von B, C und D. 2) A X: X_Typ, Y: Y_Typ X (vom Typ X_Typ) und Y (vom Typ Y_Typ) sind Attribute der Klasse A. 3) Jeder Knoten (des Syntaxbaums) gehört zu einer Klasse und besitzt die Attribute dieser Klasse und ihrer (transitiven) Oberklassen. Anmerkungen: 1. Eine Klasse kann mehrere direkte Oberklassen besitzen. 2. Es ist erlaubt, mehrere Node_definitions für eine Klasse zu spezifizieren. Die Attribute der Klasse werden entsprechend akkumuliert. ( ermöglicht Phasen-/Pass-bezogene Beschreibung) E. Ploedereder, Seite 194
195 BNF Typ_deklaration ::= type id is new Name ; Objekt_deklaration ::= id : [constant] Name [:= Ausdruck] ;; Deklaration ::= Typ_deklaration Objekt_deklaration; Name ::= id; IDL Typ_deklaration id : Token, Vater_typ : Name; Typ_deklaration sem_typ : Typ_deklaration; Objekt_deklaration id : Token, Konst : Boolean, Typ : Name, Init : Ausdruck; Objekt_deklaration sem_typ : Typ_deklaration; << wie BNF >> Name id : Token; Name def : Deklaration; E. Ploedereder, Seite 195
196 BNF IDL Ausdruck ::=Binärer Ausdruck Unärer_Ausdruck Name Literal; << wie BNF >> Ausdruck sem_res_typ : Typ_Deklaration; Anweisung ::= Zuweisung Block; << wie BNF >> Zuweisung ::= Name := Ausdruck;; Zuweisung LHS : Name, RHS : Ausdruck; Block ::= declare {Deklaration} + begin {Anweisung} + end; Block Dekls : seq of Deklaration, Anws : seq of Anweisung; E. Ploedereder, Seite 196
197 Effiziente Realisierung des Lookup Beobachtung: Der Syntax- oder Strukturbaum enthält für jedes Vorkommen eines Bezeichners einen Verweis auf das jeweilige Token. Nach der Strategie der Tokengenerierung existiert für einen deklarierten Bezeichner (ggf. auch unabhängig von Groß- und Kleinschreibung) nur ein Token. Idee: Erweitere die Einträge der Tokentabelle um Platz für einen Rückverweis auf einen ST-Eintrag, bzw. Deklarationsknoten. Erweitere letztere Datenstruktur ebenso. Realisiere die unter Alternative 1 gezeigte Verkettung der ST-Einträge über die so geschaffenen Einträge; das Token dient als Anker der jeweiligen Liste. Lookup (einer statischen Namensbindung) ist damit (für einfach Sprachen) durch Verfolgen von zwei Zeigern in konstanter Zeit durchführbar. E. Ploedereder, Seite 197
198 8. Fehlerbehandlung Forderungen: 0. kein Übersetzerabsturz bei Fehlern im Eingabeprogramm 1. alle Fehler müssen erkannt werden 2. gute Fehlermeldungen, die die Fehler möglichst genau lokalisieren und benutzer-verständlich diagnostizieren. 3. Korrekturversuch so, dass die Analyse zur Erkennung weiterer Fehler fortgesetzt werden kann. (Recovery, Wiederaufsetzen) 4. Korrektur soll keine Folgefehler verursachen (und keine tatsächlichen Fehler verdecken). Vier Aspekte: Erkennung Lokalisierung (Diagnostik) Wiederaufsetzen E. Ploedereder, Seite 198
199 Eigenschaft des gültigen Präfix die bereits eingelesenen Terminale können zu einem syntaktisch korrekten Programm ergänzt werden. m.a.w. In Unkenntnis der noch einzulesenden Terminale ist das Programm bislang korrekt. Die bislang behandelten Verfahren (RA-EA Scanner, LL-, LR-, SLR-, LALR- Parser) besitzen diese Eigenschaft. Sie liefern exakte Fehlererkennung und Fehlerlokalisierung. Anmerkungen: Die irrigen Reduktionen in SLR- und LALR-Parsern ändern nichts an dieser Eigenschaft; sie machen nur das Wiederaufsetzen zur Fortführung der Analyse schwieriger oder eingschränkter. Unter Miteinbeziehung des Lookaheads für k > 1 wird diese Charakterisierung differenzierter. E. Ploedereder, Seite 199
200 Klassifizierung von Recovery-Verfahren phrase-level lokale Korrektur, z.b. Token/Zeichen Ersetzung Einfügung panic mode Überlesen der Eingabe bis zum nächsten Synchronisationspunkt eingeplante Fehler Syntax erlaubt den Fehler; spätere Prüfung automatisiert blind hand-geschrieben heuristisch kontextabhängig mit Hinweisen Grundregel: ohne Hinweise Informiere Benutzer über vorgenommene Korrektur! E. Ploedereder, Seite 200
201 Fehlerbehandlung im Scanner Erkennung: Diagnostik: Recovery: Probleme: kein Übergang im EA für nächstes Eingabezeichen einfach ( unzulässiges Zeichen ) Empfehlung: panic mode oder allenfalls Einzelzeichen löschen für Rückgabe an Parser: entweder ein Fehlertoken (Hoffen auf Ersetzen) oder nächstes Token (Hoffen auf Einfügen) nicht abgeschlossene Strings EOL als Separator nicht abgeschlossene Kommentare Sprachentwurfsprobleme E. Ploedereder, Seite 201
202 Fehlerbehandlung im Parser Erkennung: kein Eintrag in Steuermatrix, bzw. Mismatch der Terminalerwartungen im rekursiven Abstieg Lokalisierung: am nächsten Eingabezeichen Diagnostik: meistens impliziert in der Beschreibung der Recovery-Aktionen Recovery: lokale Korrekturen: bei Top-down Verfahren i.allg. auf weitere Eingabe beschränkt; bei Bottom-up ggf. auch Veränderungen auf dem Stapel (aber dann Probleme mit Rücknahme semantischer Aktionen) jede Änderung sollte in kleinem Kontext (z.b. nächsten N Token) als hilfreich verifiziert werden bei Einfügungen gegen unendliche Schleifen schützen Einfügungen von öffnenden Schlüsselwörtern (z.b. BEGIN ) mit Vorsicht! Steuertabelle enthält gute Hinweise auf hilfreiche Token E. Ploedereder, Seite 202
203 Recovery im Parser panic mode top-down: Überlesen der weiteren Eingabe bis zu einem synchronisierenden Terminal Kandidaten für die Synchronisierung in der Analyse von A: FOLLOW(A) - offensichtlich FIRST(A) - falls A in Listen Firewalls - Schlüsselworte die größere Konstrukte umklammern (BEGIN, END, usw.) bottom-up: wie oben, aber mit Rücksetzen des Stapels typische Realisierung: Error-Produktionen, z.b. X β<error> α wird von LR-Analyse wie eine normale Produktion behandelt (Achtung: Konflikte möglich) es gibt keine Produktionen mit <error> als linke Seite. <error> hat im Fehlerfall folgende Semantik für den Parser: suche Zustand im Stapel, der mit <error> einen Übergang besitzt; reduziere alle darüber liegenden Einträge mit <error>. (über-)lese Eingabe, bis α erreicht und erkannt ist. + Heuristiken, wenn mehrere Error-Produktionen in Frage kommen E. Ploedereder, Seite 203
204 Recovery im Parser hand-geschriebene Recovery: Routinen werden in der Steuermatrix verankert und im Fehlerfall aufgerufen. aufwändig, aber... sehr flexibel und i.allg. benutzerfreundlich eingeplante Fehler : bewusste Grammatikerweiterung, die fehlerhafte Programme syntaktisch zulässt. Spätere Prüfung auf diese Fehler. Wozu?... In Situationen, in denen die automatische Recovery nicht dazu zu bewegen ist, benutzerfreundlich zu sein (z.b. großflächiger panic mode ). E. Ploedereder, Seite 204
205 Fehlerbehandlung in der semantischen Analyse Erkennung: durch explizite Prüfung einer Gültigkeitsbedingung Lokalisierung: durch Quellpositionsangabe im Syntaxbaum, bzw. Information durch den Parser Diagnostik: Beschreibung der verletzten Bedingung Recovery: Auch im Fehlerfall müssen die für die weitere Verarbeitung notwendigen semantischen Attribute gesetzt werden, bzw. ein ausgezeichnetes Fehler - Attribut gesetzt werden, das nachfolgenden Zugriff auf nicht gesetzte Attribute verhindert. (Letzteres ist eine häufige Ursache für Compilerabstürze!) Durch geeignete Ersatzwerte oder spezielle Maßnahmen kann erreicht werden, dass Folgefehler nicht zu weiteren Fehlermeldungen führen. Beispiele: Eine Typangabe benennt einen unbekannten Typ füge einen Fehlertyp ein, der mit allen anderen Typen kompatibel ist. Ein Bezeichner wird verwendet, ist aber nicht deklariert füge die Deklaration nachträglich in die Symboltabelle ein. Planung der semantischen Fehlerbehandlung ist wichtige Aufgabe im Design eines Compilers! (Nachträgliche Erweiterung ist fehleranfällig und teuer.) E. Ploedereder, Seite 205
206 9. Programmiersprachen 9.1 Globale Betrachtungen Programmiersprachen sind Hilfsmittel zur Beschreibung einer Vorschrift für einen Prozessor und bilden eine Schnittstelle zwischen Mensch und Computer. PROGRAMM beschrieben in PS PROZESSOR..... z.b. Daten..... Resultate E. Ploedereder, Seite 206
207 9.2 Forderungen an Programmiersprache Gewünschte Eigenschaften Anpassung der Sprache an die menschliche Denkweise und an die Struktur des zu lösenden Problems Unterstützung des Software Engineering effiziente Übersetzbarkeit in Maschinencode (Bezug zum Compilerbau, [ASU88, Tremblay85]) Dies sind im Einzelnen: 1. problem-angepasste Ausdrucksform (writability) 2. gute Lesbarkeit (readability) 3. leichte Änderbarkeit (modifyability) 4. Verlässlichkeit (reliability) 5. Portabilität (portability) 6. Effizienz (efficiency) 7. Wartbarkeit (bedingt 2. und 3.) (maintainability) Pragmatische Forderungen 8. (leichte) Lernbarkeit der PS 9. Kostengünstige Werkzeuge für PS 10. [Passend zum existierenden Umfeld] E. Ploedereder, Seite 207
208 Typische SW-Kostenverteilung Entwurf Codierung Test Wartung Erstentwicklung 40 % 20 % 40 % - Gesamtkosten 12 % 6 % 12 % 70 % nach Kosten hat Wartbarkeit höchste Priorität! E. Ploedereder, Seite 208
209 Ein Wartungsbeispiel... (Ein C-Programm, das von einer Firma als Weihnachtsgruß verschickt wurde...) >original.out E. Ploedereder, Seite 209
210 Ein Wartungsbeispiel... >original.out On the first day of Christmas my true love gave to me a partridge in a per tree. On the second day of Christmas my true love gave to me two turtle doves and a partridge in a per tree. On the third day of Christmas my true love gave to me three french hens, two turtle doves and a partridge in a per tree.... E. Ploedereder, Seite 210
211 Das Programm... #include <stdio.h> main(t,_,a) char *a; { return!0<t?t<3?main(-79,-13,a+main(-87,1-_,main(-86,0,a+1)+a)): 1,t<_?main(t+1,_,a):3,main(-94,-27+t,a)&&t==2?_<13? main(2,_+1,"%s %d %d\n"):9:16:t<0?t<-72?main(_,t, "@n'+,#'/*{}w+/w#cdnr/+,{}r/*de}+,/*{*+,/w{%+,/w#q#n+,/#{l+,/n{n+,/+#n+,/#\ ;#q#n+,/+k#;*+,/'r :'d*'3,}{w+k w'k:'+}e#';dq#'l \ q#'+d'k#!/+k#;q#'r}ekk#}w'r}ekk{nl]'/#;#q#n'){)#}w'){){nl]'/+#n';d}rw' i;# \ ){nl]!/n{n#'; r{#w'r nc{nl]'/#{l,+'k {rw' ik{;[{nl]'/w#q#n'wk nw' \ iwk{kk{nl]!/w{%'l##w#' i; :{nl]'/*{q#'ld;r'}{nlwb!/*de}'c \ ;;{nl'-{}rw]'/+,}##'*}#nc,',#nw]'/+kd'+e}+;#'rdq#w! nr'/ ') }+}{rl#'{n' ')#\ }'+}##(!!/") :t<-50?_==*a?putchar(31[a]):main(-65,_,a+1):main((*a=='/')+t,_,a+1) :0<t?main(2,2,"%s"):*a=='/' main(0,main(-61,*a, "!ek;dc i@bk'(q)-[w]*%n+r3#l,{}:\nuwloca-o;m.vpbks,fxntdceghiry"),a+1); } E. Ploedereder, Seite 211
212 Die "Korrektur"... > original.out sed -e s/per/pear/ On the first day of Christmas my true love gave to me a partridge in a pear tree. On the second day of Christmas my true love gave to me two turtle doves and a partridge in a pear tree. On the third day of Christmas my true love gave to me three french hens, two turtle doves and a partridge in a pear tree. E. Ploedereder, Seite 212
213 Der "Test" (diff)... > original.out sed -e s/per/pear/ On the first day of Christmas my true love gave to me a partridge in a per peartree. On the second day of Christmas my true love gave to me two turtle doves and a partridge in a pear per tree. On the third day of Christmas my true love gave to me three french hens, two turtle doves and a partridge in a pear per tree.... E. Ploedereder, Seite 213
214 "Release" und die Folgen... vorher: On the eleventh day of Christmas my true love gave to me eleven pipers piping, ten lords a-leaping,... and a partridge in a per tree. nachher: On the eleventh day of Christmas my true love gave to me eleven pipears piping, ten lords a-leaping, nine ladies dancing, eight maids a-milking, seven swans a-swimming, six geese a-laying, five gold rings; four calling birds, three french hens, two turtle doves and a partridge in a pear tree. E. Ploedereder, Seite 214
215 Verständliche SW-Struktur E. Ploedereder, Seite 215
216 Verständliche SW-Struktur?? E. Ploedereder, Seite 216
Fachseminar Compilerbau
Fachseminar Compilerbau WS 08/09 Matthias Schiller Syntaktische Analyse 1. Prinzip der Top-Down-Analyse 2. LL(1)-Grammatiken Modell-Vorstellung Der Scanner liefert als Ergebnis der lexikalischen Analyse,
MehrLexikalische Programmanalyse der Scanner
Der Scanner führt die lexikalische Analyse des Programms durch Er sammelt (scanned) Zeichen für Zeichen und baut logisch zusammengehörige Zeichenketten (Tokens) aus diesen Zeichen Zur formalen Beschreibung
MehrÜbungs- und Praktikumsaufgaben zur Systemprogrammierung Dipl.-Ing. H. Büchter (Lehrbeauftragter) FH-Dortmund WS 2001/2002 / SS 2002
1. Stellen Sie die schrittweise Verbesserung eines Compilers durch das Bootstrap- Verfahren mit Hilfe von T-Diagrammen dar. Gegeben ist ein auf der Maschine M lauffähiger Compiler C 1, der in S geschrieben
MehrSyntax von Programmiersprachen
Syntax von Programmiersprachen SEP 209 Programmiersprachen Sprache = Menge von Wörtern, typischerweise unendlich Programmiersprache: Wörter repräsentieren Programme Programm kann auf einem Computer evtl.
MehrEinführung - Parser. Was ist ein Parser?
Gliederung 1. Einleitung 1.1 Was ist ein Parser? 1.2 Was ist ein tabellengesteuerter TD-Parser? 1. Tabellengesteuerter TD-Parser 2.1 Funktionsweise 2.2 Darstellung als Pseudocode 2.3 Konstruktion von prädiktiven
MehrKapitel 5: Syntax-Analyse
Kapitel 5: Syntax-Analyse Aufgabe Die Token-Folge wird strukturiert in Anweisungen, Ausdrücke etc., um die Semantische Analyse und Code-Erzeugung zu ermöglichen Themen Kontextfreie Grammatik Äquivalente
MehrAutomaten und formale Sprachen Klausurvorbereitung
Automaten und formale Sprachen Klausurvorbereitung Rami Swailem Mathematik Naturwissenschaften und Informatik FH-Gießen-Friedberg Inhaltsverzeichnis 1 Definitionen 2 2 Altklausur Jäger 2006 8 1 1 Definitionen
MehrKonstruieren der SLR Parsing Tabelle
Konstruieren der SLR Parsing Tabelle Kontextfreie Grammatik (CFG) Notation 1. Diese Symbole sind Terminals: (a) Kleinbuchstaben vom Anfang des Alphabets wie a, b, c. (b) Operator Symbole wie +,, usw. (c)
MehrCompiler; Übersetzungsprogramme. Grundlagen der Programmierung 3 A. Compiler für Programmiersprachen. Phasen eines Compilers
ompiler; Übersetzungsprogramme Grundlagen der Programmierung 3 A ompiler A: Phasen; Scanner Prof. Dr. Manfred Schmidt-Schauß Sommersemester 2017 Ein Übersetzer (ompiler) ist ein Programm, das ein Wort
MehrInhalt Kapitel 11: Formale Syntax und Semantik
Inhalt Kapitel 11: Formale Syntax und Semantik 1 Abstrakte und konkrete Syntax 2 Lexikalische Analyse 3 Formale Sprachen, Grammatiken, BNF 4 Syntaxanalyse konkret 266 Abstrakte und konkrete Syntax Abstrakte
MehrSyntaxanalyse Ausgangspunkt und Ziel
Syntaxanalyse Ausgangspunkt und Ziel Ausgangspunkt: Kontextfreie Grammatik Im Normalfall BNF, manchmal EBNF BNF = Backus-Naur-Form = Produktionsregeln EBNF = erweiterte BNF (+ reguläre Ausdrücke) Prüfung
MehrDefinition von LR(k)-Grammatiken
Definition von LR(k)-Grammatiken Ziel: Ein Lookahead von k soll ausreichen um entscheiden zu können, welche Regel angewendet werden muss. Definition: FIRST k (w 1 w n ):= w 1 w k, falls n k, w 1 w n, sonst.
MehrOperationen auf Grammatiken
Operationen auf Grammatiken Ziel: Normalisierungen, Vereinfachungen, Elimination bestimmter Konstrukte Erzeugen eines Parsers Transformation G 1 G 2 mit L(G 1 ) = L(G 2 ) I.a. Parsebaum 1 (w) Parsebaum
MehrDefinition Compiler. Bekannte Compiler
Compiler Inhalt: Definition Compiler / bekannte Compiler Klassifikationen von Compilern Analyse-Synthese-Modell der Kompilierung Analyse des Quellprogramms Synthesephase Die Phasen eines Compilers Symboltabellenverwaltung
MehrLexikalische Analyse, Tokenizer, Scanner
Lexikalische Analyse, Tokenizer, Scanner Frühe Phase des Übersetzers Aufgabenteilung: Scanner (lokale) Zeichen (Symbol-)Analyse Parser Syntax-Analyse Aufgabe des Scanners: Erkennung von: Zahlen, Bezeichner,
MehrShift Reduce Parser (Bottom up Parser) Historie Grundbegriffe Tabellengesteuerter LR(1) Parser Konstruktion der Elementmengen Tabellenkonstruktion
Shift Reduce Parser (Bottom up Parser) Historie Grundbegriffe Tabellengesteuerter LR(1) Parser Konstruktion der Elementmengen Tabellenkonstruktion Historie Die ersten Compiler entstanden in den 50ern.
Mehr9.4 Grundlagen des Compilerbaus
Kap09.fm Seite 717 Dienstag, 7. September 2010 2:06 14 9.4 Grundlagen des Compilerbaus 717 so dass die Benutzung dieser Regeln zum Aufbau eines + -Knotens bzw. eines Negations- Knotens im abstrakten Syntaxbaum
MehrAnwenundg regulärer Sprachen und endlicher Automaten
Proseminar Theoretische Informatik Dozent: Prof. Helmut Alt Anwenundg regulärer Sprachen und endlicher Automaten Madlen Thaleiser 30. Oktober 2012 Reguläre Sprachen Regulärer Ausdruck definiert über einem
MehrDeterministischer Kellerautomat (DPDA)
Deterministische Kellerautomaten Deterministischer Kellerautomat (DPDA) Definition Ein Septupel M = (Σ,Γ, Z,δ, z 0,#, F) heißt deterministischer Kellerautomat (kurz DPDA), falls gilt: 1 M = (Σ,Γ, Z,δ,
MehrCompilerbau Syntaxanalyse 68. LR(1)-Syntaxanalyse
Compilerbau Syntaxanalyse 68 LR(1)-Syntaxanalyse Bei der LL(1)-Syntaxanalyse wird allein aufgrund des nächsten Tokens die zu verwendende Produktion ermittelt. Bei der LR(1)-Syntaxanalyse braucht diese
Mehr7. Syntax: Grammatiken, EBNF
7. Syntax: Grammatiken, EBNF Teil 1 Sehr schönes Beispiel für Notwendigkeit der Theoretischen Informatik für Belange der Praktischen Informatik Vertiefung in: Einführung in die Theoretische Informatik
MehrKapitel 5: Syntaxdiagramme und Grammatikregeln
5. Syntaxdiagramme und Grammatikregeln 5-1 Objektorientierte Programmierung (Winter 2010/2011) Kapitel 5: Syntaxdiagramme und Grammatikregeln Syntaxdiagramme Grammatikregeln (kontextfrei) Beispiele: Lexikalische
MehrLemma Für jede monotone Grammatik G gibt es eine kontextsensitive
Lemma Für jede monotone Grammatik G gibt es eine kontextsensitive Grammatik G mit L(G) = L(G ). Beweis im Beispiel (2.): G = (V,Σ, P, S) : P = {S asbc, S abc, CB BC, ab ab, bb bb, bc bc, cc cc}. (i) G
MehrAutomaten und Formale Sprachen alias Theoretische Informatik. Sommersemester 2011
Automaten und Formale Sprachen alias Theoretische Informatik Sommersemester 2011 Dr. Sander Bruggink Übungsleitung: Jan Stückrath Sander Bruggink Automaten und Formale Sprachen 1 Wir beschäftigen uns ab
Mehr1. Der Begriff Informatik 2. Syntax und Semantik von Programmiersprachen - 1 -
1. Der Begriff Informatik 2. Syntax und Semantik von Programmiersprachen I.2. I.2. Grundlagen von von Programmiersprachen. - 1 - 1. Der Begriff Informatik "Informatik" = Kunstwort aus Information und Mathematik
Mehr1. Der Begriff Informatik 2. Syntax und Semantik von Programmiersprachen - 1 -
1. Der Begriff Informatik 2. Syntax und Semantik von Programmiersprachen I.2. I.2. Grundlagen von von Programmiersprachen. - 1 - 1. Der Begriff Informatik "Informatik" = Kunstwort aus Information und Mathematik
MehrAlgorithmen auf Sequenzen
Algorithmen auf Sequenzen Vorlesung von Prof. Dr. Sven Rahmann im Sommersemester 2008 Kapitel 4 Reguläre Ausdrücke Webseite zur Vorlesung http://ls11-www.cs.tu-dortmund.de/people/rahmann/teaching/ss2008/algorithmenaufsequenzen
MehrVon der Grammatik zum AST
Von der Grammatik zum AST Welche Eigenschaften soll ein Parser haben? Wann ist eine Grammatik eindeutig? Wie sollte eine Grammatik aussehen? Theoretischer Hin tergrund: FIRST, FOLLOW Einschränkungen von
MehrProgrammiersprachen und Übersetzer
Programmiersprachen und Übersetzer Sommersemester 2009 5. April 2009 Vorteile bei der Verwendung höherer Programmiersprachen Vorteile bei der Verwendung höherer Programmiersprachen 1. Einfache Notation
MehrGrundlagen der Programmierung 2 (Comp-D)
Grundlagen der Programmierung 2 (Comp-D) Prof. Dr. Manfred Schmidt-Schauÿ Künstliche Intelligenz und Softwaretechnologie 31. Mai 2007 Operationen auf Grammatiken Ziel: Normalisierungen, Vereinfachungen
MehrVU Software Paradigmen / SS 2012
VU Software Paradigmen 716.060 / SS 2012 Sandra Fruhmann sandra.fruhmann@student.tugraz.at Inhalt Grammatiken Chomsky Sprachhierarchie Parse Trees Recursive Descent Parser First-, Follow-Mengen 2 Compiler
MehrSprachen sind durch folgenden Aufbau gekennzeichnet:
BNF UND SYNTAXDIAGRAMME 1. Allgemeines 1.1 Aufbau von Sprachen BNF und Syntaxdiagramme werden verwendet, um die Syntax einer Sprache darzustellen und graphisch zu veranschaulichen. Mit ihnen können entweder
MehrKapitel 2. Methoden zur Beschreibung von Syntax
1 Kapitel 2 Methoden zur Beschreibung von Syntax Grammatik, die sogar Könige zu kontrollieren weiß... aus Molière, Les Femmes Savantes (1672), 2. Akt 2 Ziele Zwei Standards zur Definition der Syntax von
MehrAlphabet, formale Sprache
n Alphabet Alphabet, formale Sprache l nichtleere endliche Menge von Zeichen ( Buchstaben, Symbole) n Wort über einem Alphabet l endliche Folge von Buchstaben, die auch leer sein kann ( ε leere Wort) l
MehrSyntax von Programmiersprachen
"Grammatik, die sogar Könige zu kontrollieren weiß... aus Molière, Les Femmes Savantes (1672), 2. Akt Syntax von Programmiersprachen Prof. Dr. Christian Böhm in Zusammenarbeit mit Gefei Zhang WS 07/08
Mehr2.1 Allgemeines. Was ist eine Sprache? Beispiele:
Was ist eine Sprache? Beispiele: (a) Deutsch, Japanisch, Latein, Esperanto,...: Natürliche Sprachen (b) Pascal, C, Java, Aussagenlogik,...: Formale Sprachen Wie beschreibt man eine Sprache? (i) Syntax
MehrLR-Parser, Shift-Reduce-Verfahren
LR-Parser, Shift-Reduce-Verfahren Bottom-Up-Syntaxanalyse LR-Parser L: Eingabe von links nach rechts; R: Rechtsherleitung Shift-Reduce-Verfahren Beachte: Kein Backtracking nicht auf jede Grammatik anwendbar
Mehr6 Kontextfreie Grammatiken
6 Kontextfreie Grammatiken Reguläre Grammatiken und damit auch reguläre Ausdrücke bzw. endliche Automaten haben bezüglich ihres Sprachumfangs Grenzen. Diese Grenzen resultieren aus den inschränkungen,
Mehr3 Syntax von Programmiersprachen
3 Syntax von Programmiersprachen Syntax ( Lehre vom Satzbau ) formale Beschreibung des Aufbaus der Worte und Sätze, die zu einer Sprache gehören; im Falle einer Programmiersprache Festlegung, wie Programme
MehrDeterministische PDAs
Deterministische PDAs Erinnerung: Ein PDA ist deterministisch, wenn q Q, a Σ, Z Γ: δ(q,a,z) + δ(q,ε,z) 1. Definition: Eine Sprache heißt deterministisch kontextfrei, wenn es für sie einen DPDA gibt. Ziel:
MehrWas bisher geschah Chomsky-Hierarchie für Sprachen: L 0 Menge aller durch (beliebige) Grammatiken beschriebenen Sprachen L 1 Menge aller monotonen
Was bisher geschah Chomsky-Hierarchie für Sprachen: L 0 Menge aller durch (beliebige) Grammatiken beschriebenen Sprachen L 1 Menge aller monotonen (Kontextsensitive) Sprachen L 2 Menge aller kontextfreien
MehrVU Software Paradigmen / SS 2014
VU Software Paradigmen 716.060 / SS 2014 Ralph Ankele ralph.ankele@tugraz.at Termine Ausgabe: 19. März (heute) Fragestunde: 24. März Abgabe: 09. April(bis 16:00 Uhr) Einsichtsnahme: xx. April (16:00 Uhr)
MehrFormale Sprachen. Inhaltsverzeichnis. M. Jakob. 10. Dezember Allgemeine Einführung. Aufbau formaler Sprachen
M. Jakob Gymnasium Pegnitz 10. Dezember 2014 Inhaltsverzeichnis Allgemeine Einführung Aufbau formaler Sprachen Notationsformen formaler Sprachen Backus-Naur-Formen Erkennen formaler Sprachen Implementierung
MehrGrammatiken. Grammatiken sind regelbasierte Kalküle zur Konstruktion von Systemen und Sprachen Überprüfung von Systemen und Sprachen
Grammatiken Grammatiken sind regelbasierte Kalküle zur Konstruktion von Systemen und Sprachen Überprüfung von Systemen und Sprachen Grammatiken eignen sich besonders zur Modellierung beliebig tief geschachtelter,
MehrTheoretische Grundlagen der Informatik
Theoretische Grundlagen der Informatik Vorlesung am 15.01.2015 INSTITUT FÜR THEORETISCHE 0 KIT 15.01.2015 Universität des Dorothea Landes Baden-Württemberg Wagner - Theoretische und Grundlagen der Informatik
MehrKlammersprache Definiere
Klammersprache w=w 1...w n {(,)}* heißt korrekt geklammert, falls die Anzahl ( ist gleich der Anzahl ). in jedem Anfangsstück w 1,...,w i (i n) ist die Anzahl ( nicht kleiner als die Anzahl ). Definiere
MehrKontextfreie Grammatiken. Kontextfreie Grammatiken 1 / 45
Kontextfreie Grammatiken Kontextfreie Grammatiken 1 / 45 Was kann man mit kontextfreien Grammatiken anfangen? Kontextfreie Grammatiken, kurz: werden zur Modellierung von KFGs beliebig tief geschachtelten
Mehr4. Die lexikalische Analyse
zerlegt Folge von Zeichen in Eingabedatei in Folge von Symbolen (Token) Scanner-Sieber-Modul Token: Typ und Inhalt übliche Token-Typen: reservierte Wörter (if, while, for, ) Bezeichner (x, dauer, name,..)
MehrDefinition 4 (Operationen auf Sprachen) Beispiel 5. Seien A, B Σ zwei (formale) Sprachen. Konkatenation: AB = {uv ; u A, v B} A + = n 1 An
Definition 4 (Operationen auf Sprachen) Seien A, B Σ zwei (formale) Sprachen. Konkatenation: AB = {uv ; u A, v B} A 0 = {ɛ}, A n+1 = AA n A = n 0 An A + = n 1 An Beispiel 5 {ab, b}{a, bb} = {aba, abbb,
MehrLL(k)-Analyse. (y) folgt α = β. (x) = start k. (=l> ist ein Linksableitungsschritt)
LL(k)-Analyse Eine KFG G = (N,T,P,S) heisst LL(k)-Grammatik, wenn für alle w,x,y T*, α,β,σ (N U T)* und A N mit 1. S =l>* waσ =l> wασ =l>* wx, 2. S =l>* waσ = > wβσ =l>* wy, 3. start k (x) = start k (y)
MehrMehrdeutige Grammatiken
Mehrdeutige Grammatiken Wir haben gesehen, dass es auch mehr als eine Linksableitung, d.h. mehr als einen Syntaxbaum geben kann, um das selbe Terminalwort zu erzeugen. Eine Grammatik, die für mindestens
MehrThomas Behr. 17. November 2011
in in Fakultät für Mathematik und Informatik Datenbanksysteme für neue Anwendungen FernUniversität in Hagen 17. November 2011 c 2011 FernUniversität in Hagen Outline in 1 2 3 4 5 6 - Was ist das? in über
Mehr2.6 Deterministisches Top-Down-Parsen
48 2.6 Deterministisches Top-Down-Parsen Als nächstes wollen wir uns mit Methoden zur syntaktischen Analyse befassen. Der lexikale canner eines Compilers liest die Eingabe Zeichen für Zeichen und erzeugt
Mehrkontextfreie Grammatiken Theoretische Informatik kontextfreie Grammatiken kontextfreie Grammatiken Rainer Schrader 14. Juli 2009 Gliederung
Theoretische Informatik Rainer Schrader Zentrum für Angewandte Informatik Köln 14. Juli 2009 1 / 40 2 / 40 Beispiele: Aus den bisher gemachten Überlegungen ergibt sich: aus der Chomsky-Hierarchie bleiben
MehrObjektorientierte Programmierung. Kapitel 3: Syntaxdiagramme
Stefan Brass: OOP (Java), 3. 1/31 Objektorientierte Programmierung Kapitel 3: Stefan Brass Martin-Luther-Universität Halle-Wittenberg Wintersemester 2014/15 http://www.informatik.uni-halle.de/ brass/oop14/
MehrPumping-Lemma 2 Pumping-Lemma Sei L eine von einem endlichen Automaten erkannte Sprache. Dann existiert eine natürliche Zahl p N derart, dass jedes Wo
1 Endliche Automaten Modellierungskonzept mit vielen brauchbaren Eigenschaften schnelle Spracherkennung graphisch-visuelle Beschreibung automatische Korrektheitsbeweise gute Kompositionalitätseigenschaften
MehrKellerautomat (1/4) Kellerautomat (2/4) Kellerautomat (3/4) Kellerautomat (4/4)
Kellerautomat (1/4) Kellerautomat (2/4) Kontextfreie Grammatiken können von Kellerautomaten (Push Down Automata, PDA) erkannt werden PDAs sind eine Erweiterung der endlichen Automaten um ein einfaches
MehrSoftware Entwicklung 2. Übersetzerbau 1
Software Entwicklung 2 Übersetzerbau 1 Übersetzerbau I Inhalt Aufgaben von Übersetzern Weitere Anwendungen von Übersetzern Arten von Übersetzern Grundlagen: Sprachdefinition Grammatik: BNF, EBNF, Syntaxdiagramme
MehrSei Σ ein endliches Alphabet. Eine Sprache L Σ ist genau dann regulär, wenn sie von einem regulären Ausdruck beschrieben werden kann.
Der Satz von Kleene Wir haben somit Folgendes bewiesen: Der Satz von Kleene Sei Σ ein endliches Alphabet. Eine Sprache L Σ ist genau dann regulär, wenn sie von einem regulären Ausdruck beschrieben werden
MehrEinführung IMP-Syntax Reduktionssemantik Maschinen-Semantik. Teil IV. Semantik imperativer Sprachen
Teil IV Semantik imperativer Sprachen 201 1. Einführung Alternativen zur Beschreibung der Semantik: natürliche Sprache (bisher, unpräzise) operational Reduktionssemantik (vgl. Haskell-Semantik in Kap.
MehrEinführung in die Programmiertechnik
Einführung in die Programmiertechnik Formale Beschreibung von Programmiersprachen Lexikalische Regeln Definition von Wörtern (Lexem, Token) Gruppierung von Zeichen Lexikalische Kategorien: Klassen ähnlicher
MehrFachseminar WS 2008/09
Fachseminar WS 2008/09 Fachgebiet: Compilerbau Thema: Lexikalische Analyse (Scanner) Referent: Ali Sediq Betreuer: Prof. Dr. Helmut Weber 1 Inhaltsverzeichnis Lexikalische Analyse 1.0 Grundprobleme der
MehrFORMALE SYSTEME. 3. Vorlesung: Endliche Automaten. TU Dresden, 17. Oktober Markus Krötzsch
FORMALE SYSTEME 3. Vorlesung: Endliche Automaten Markus Krötzsch TU Dresden, 17. Oktober 2016 Rückblick Markus Krötzsch, 17. Oktober 2016 Formale Systeme Folie 2 von 31 Wiederholung Mit Grammatiken können
MehrCompiler: Parser. Prof. Dr. Oliver Braun. Fakultät für Informatik und Mathematik Hochschule München. Letzte Änderung:
Fakultät für Informatik und Mathematik Hochschule München Letzte Änderung: 17.05.2017 11:06 Inhaltsverzeichnis Parsing....................................... 2 Syntax........................................
MehrProgrammiersprachen und Übersetzer
Programmiersprachen und Übersetzer Sommersemester 2010 19. April 2010 Theoretische Grundlagen Problem Wie kann man eine unendliche Menge von (syntaktisch) korrekten Programmen definieren? Lösung Wie auch
MehrInformatik II SS Der Kompilationsprozess (-phasen) Schreiben des Programms. Die Organisation eines typischen Compilers
Der Kompilationsprozess (-phasen) Informatik II SS 2004 Teil 6: Sprachen, Compiler und Theorie 7 Prof. Dr. Dieter Hogrefe Dipl.-Inform. Michael Ebner Lehrstuhl für Telematik Institut für Informatik Scanner
MehrReguläre Sprachen. R. Stiebe: Theoretische Informatik für ING-IF und Lehrer,
Reguläre Sprachen Reguläre Sprachen (Typ-3-Sprachen) haben große Bedeutung in Textverarbeitung und Programmierung (z.b. lexikalische Analyse) besitzen für viele Entscheidungsprobleme effiziente Algorithmen
MehrAlgorithmen mit konstantem Platzbedarf: Die Klasse REG
Algorithmen mit konstantem Platzbedarf: Die Klasse REG Sommerakademie Rot an der Rot AG 1 Wieviel Platz brauchen Algorithmen wirklich? Daniel Alm Institut für Numerische Simulation Universität Bonn August
MehrCompilerbau. Bachelor-Programm. im SoSe Prof. Dr. Joachim Fischer Dr. Klaus Ahrens Dipl.-Inf. Ingmar Eveslage.
Bachelor-Programm Compilerbau im SoSe 2014 Prof. Dr. Joachim Fischer Dr. Klaus Ahrens Dipl.-Inf. Ingmar Eveslage fischer@informatik.hu-berlin.de J.Fischer 8.1 Position Kapitel 1 Compilationsprozess Teil
MehrKapitel 2. Methoden zur Beschreibung von Syntax
1 Kapitel 2 Methoden zur Beschreibung von Syntax Grammatik, die sogar Könige zu kontrollieren weiß... aus Molière, Les Femmes Savantes (1672), 2. Akt 2 Ziele Zwei Standards zur Definition der Syntax von
MehrFormale Sprachen, reguläre und kontextfreie Grammatiken
Formale Sprachen, reguläre und kontextfreie Grammatiken Alphabet A: endliche Menge von Zeichen Wort über A: endliche Folge von Zeichen aus A A : volle Sprache über A: Menge der A-Worte formale Sprache
MehrTheoretische Grundlagen der Informatik
Theoretische Grundlagen der Informatik Vorlesung am 18. Januar 2018 INSTITUT FÜR THEORETISCHE 0 18.01.2018 Dorothea Wagner - Theoretische Grundlagen der Informatik INSTITUT FÜR THEORETISCHE KIT Die Forschungsuniversität
Mehr1 Formale Sprachen, reguläre und kontextfreie Grammatiken
Praktische Informatik 1, WS 2001/02, reguläre Ausdrücke und kontextfreie Grammatiken 1 1 Formale Sprachen, reguläre und kontextfreie Grammatiken Ein Alphabet A ist eine endliche Menge von Zeichen. Die
MehrInterpreter - Gliederung
Institut für Informatik Ludwig-Maximilian Universität Interpreter - Gliederung Programmiersprache Syntax Konkrete Syntax Abstrakter Syntax Baum (Abstrakte Syntax) Parser Syntaktische Struktur einer Sprache
MehrZwischencode-Erzeugung. 2. Juni 2009
Zwischencode-Erzeugung im Rahmen des Seminars "Übersetzung von künstlichen Sprachen" Sebastian Hanneken 2. Juni 2009 1 / 32 1 Einleitung Einordnung Funktion von Zwischencode 3-Adresscode (3AC) 2 Erzeugung
MehrKapitel 4: Syntaxdiagramme und Grammatikregeln
4. Syntaxdiagramme und Grammatikregeln 4-1 Objektorientierte Programmierung (Winter 2006/2007) Kapitel 4: Syntaxdiagramme und Grammatikregeln Syntaxdiagramme Grammatikregeln (kontextfrei) Beispiele: Lexikalische
MehrWas ist ein Compiler?
Was ist ein Compiler? Was ist ein Compiler und worum geht es? Wie ist ein Compiler aufgebaut? Warum beschäftigen wir uns mit Compilerbau? Wie ist die Veranstaltung organisiert? Was interessiert Sie besonders?
MehrInduktive Definition
Rechenregeln A B = B A A (B C) = (A B) C A (B C) = (A B) C A (B C) = A B A C (B C) A = B A C A {ε} A = A A {ε} = A (A {ε}) = A (A ) = A A A = A + A A = A + A + {ε} = A Beispiel. Real-Zahlen = {0,..., 9}
MehrEinführung in die Informatik. Programming Languages
Einführung in die Informatik Programming Languages Beschreibung von Programmiersprachen Wolfram Burgard Motivation und Einleitung Wir haben in den vorangehenden Kapiteln meistens vollständige Java- Programme
MehrKapitel: Die Chomsky Hierarchie. Die Chomsky Hierarchie 1 / 14
Kapitel: Die Chomsky Hierarchie Die Chomsky Hierarchie 1 / 14 Allgemeine Grammatiken Definition Eine Grammatik G = (Σ, V, S, P) besteht aus: einem endlichen Alphabet Σ, einer endlichen Menge V von Variablen
MehrLiteratur Reguläre Ausdrücke
Literatur Reguläre Ausdrücke [2-1] https://de.wikipedia.org/wiki/regul%c3%a4rer_ausdruck [2-2] http://openbook.rheinwerk-verlag.de/linux/linux_kap08_001.html http://openbook.rheinwerk-verlag.de/shell_programmierung/shell_013_0
MehrReguläre Sprachen und endliche Automaten
Reguläre Sprachen und endliche Automaten 1 Motivation: Syntaxüberprüfung Definition: Fließkommazahlen in Java A floating-point literal has the following parts: a whole-number part, a decimal point (represented
MehrEinführung in die Informatik. Programming Languages
Einführung in die Informatik Programming Languages Beschreibung von Programmiersprachen Wolfram Burgard Cyrill Stachniss 1/15 Motivation und Einleitung Wir haben in den vorangehenden Kapiteln meistens
MehrInhalt Kapitel 5: Syntax
Inhalt Kapitel 5: Syntax 1 Syntax und Semantik 2 Formale Sprachen 3 Backus-Naur Form 4 Chomsky Grammatik 5 Reguläre Ausdrücke 6 Endliche Automaten 180 Syntax und Semantik Syntax Syntax: Festlegung des
MehrTheoretische Informatik I
Theoretische nformatik inheit 3 Kontextfreie Sprachen 1. Kontextfreie Grammatiken 2. Pushdown Automaten 3. igenschaften kontextfreier Sprachen Verarbeitung von Programmiersprachen Was ist das einfachste
MehrGrundlagen der Theoretischen Informatik
Grundlagen der Theoretischen Informatik 4. Kellerautomaten und kontextfreie Sprachen (III) 17.06.2015 Viorica Sofronie-Stokkermans e-mail: sofronie@uni-koblenz.de 1 Übersicht 1. Motivation 2. Terminologie
MehrÜbersetzergenerierung mit lex und yacc
Übersetzergenerierung mit lex und yacc 0. Überblick und Organisatorisches Jan Bredereke WiSe 2006/07, Universität Bremen otivation Übersetzer: Grundlegende Werkzeuge welche Fehler kann er finden? Konstrukt
MehrWS06/07 Referentin: Katharina Blinova. Formale Sprachen. Hauptseminar Intelligente Systeme Dozent: Prof. Dr. J. Rolshoven
WS06/07 Referentin: Katharina Blinova Formale Sprachen Hauptseminar Intelligente Systeme Dozent: Prof. Dr. J. Rolshoven 1. Allgemeines 2. Formale Sprachen 3. Formale Grammatiken 4. Chomsky-Hierarchie 5.
MehrKontextfreie Sprachen
Kontextfreie Sprachen Bedeutung: Programmiersprachen (Compilerbau) Syntaxbäume Chomsky-Normalform effiziente Lösung des Wortproblems (CYK-Algorithmus) Grenzen kontextfreier Sprachen (Pumping Lemma) Charakterisierung
MehrKapitel IV Formale Sprachen und Grammatiken
Kapitel IV Formale Sprachen und Grammatiken 1. Begriffe und Notationen Sei Σ ein (endliches) Alphabet. Dann Definition 42 1 ist Σ das Monoid über Σ, d.h. die Menge aller endlichen Wörter über Σ; 2 ist
MehrEinführung in die Informatik I (autip)
Einführung in die Informatik I (autip) Dr. Stefan Lewandowski Fakultät 5: Informatik, Elektrotechnik und Informationstechnik Abteilung Formale Konzepte Universität Stuttgart 24. Oktober 2007 Was Sie bis
Mehrzu große Programme (Bildschirmseite!) zerlegen in (weitgehend) unabhängige Einheiten: Unterprogramme
Bisher Datentypen: einfach Zahlen, Wahrheitswerte, Zeichenketten zusammengesetzt Arrays (Felder) zur Verwaltung mehrerer zusammengehörender Daten desselben Datentypes eindimensional, mehrdimensional, Array-Grenzen
MehrGrundlagen der Theoretischen Informatik
Grundlagen der Theoretischen Informatik 4. Kellerautomaten und kontextfreie Sprachen (II) 11.06.2015 Viorica Sofronie-Stokkermans e-mail: sofronie@uni-koblenz.de 1 Übersicht 1. Motivation 2. Terminologie
MehrKontextfreie Sprachen
Kontextfreie Sprachen besitzen große Bedeutung im Compilerbau Chomsky-Normalform effiziente Lösung des Wortproblems (CYK-Algorithmus) Grenzen kontextfreier Sprachen (Pumping Lemma) Charakterisierung durch
Mehr8. Turingmaschinen und kontextsensitive Sprachen
8. Turingmaschinen und kontextsensitive Sprachen Turingmaschinen (TM) von A. Turing vorgeschlagen, um den Begriff der Berechenbarkeit formal zu präzisieren. Intuitiv: statt des Stacks bei Kellerautomaten
Mehr(Prüfungs-)Aufgaben zu formale Sprachen
(Prüfungs-)Aufgaben zu formale Sprachen (siehe auch bei den Aufgaben zu endlichen Automaten) 1) Eine Grammatik G sei gegeben durch: N = {S, A}, T = {a, b, c, d}, P = { (S, Sa), (S, ba), (A, ba), (A, c),
Mehr