Grundlagen der Programmiersprachen und Compiler

Größe: px
Ab Seite anzeigen:

Download "Grundlagen der Programmiersprachen und Compiler"

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 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,

Mehr

Lexikalische Programmanalyse der Scanner

Lexikalische 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

Ü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

Mehr

Syntax von Programmiersprachen

Syntax 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.

Mehr

Einführung - Parser. Was ist ein Parser?

Einfü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

Mehr

Kapitel 5: Syntax-Analyse

Kapitel 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

Mehr

Automaten und formale Sprachen Klausurvorbereitung

Automaten 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

Mehr

Konstruieren der SLR Parsing Tabelle

Konstruieren 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)

Mehr

Compiler; Übersetzungsprogramme. Grundlagen der Programmierung 3 A. Compiler für Programmiersprachen. Phasen eines Compilers

Compiler; Ü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

Mehr

Inhalt Kapitel 11: Formale Syntax und Semantik

Inhalt 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

Mehr

Syntaxanalyse Ausgangspunkt und Ziel

Syntaxanalyse 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

Mehr

Definition von LR(k)-Grammatiken

Definition 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.

Mehr

Operationen auf Grammatiken

Operationen 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

Mehr

Definition Compiler. Bekannte Compiler

Definition 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

Mehr

Lexikalische Analyse, Tokenizer, Scanner

Lexikalische 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,

Mehr

Shift 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 Shift Reduce Parser (Bottom up Parser) Historie Grundbegriffe Tabellengesteuerter LR(1) Parser Konstruktion der Elementmengen Tabellenkonstruktion Historie Die ersten Compiler entstanden in den 50ern.

Mehr

9.4 Grundlagen des Compilerbaus

9.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

Mehr

Anwenundg regulärer Sprachen und endlicher Automaten

Anwenundg 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

Mehr

Deterministischer Kellerautomat (DPDA)

Deterministischer 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,δ,

Mehr

Compilerbau Syntaxanalyse 68. LR(1)-Syntaxanalyse

Compilerbau 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

Mehr

7. Syntax: Grammatiken, EBNF

7. 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

Mehr

Kapitel 5: Syntaxdiagramme und Grammatikregeln

Kapitel 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

Mehr

Lemma Für jede monotone Grammatik G gibt es eine kontextsensitive

Lemma 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

Mehr

Automaten und Formale Sprachen alias Theoretische Informatik. Sommersemester 2011

Automaten 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

Mehr

1. Der Begriff Informatik 2. Syntax und Semantik von Programmiersprachen - 1 -

1. 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

Mehr

1. Der Begriff Informatik 2. Syntax und Semantik von Programmiersprachen - 1 -

1. 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

Mehr

Algorithmen auf Sequenzen

Algorithmen 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

Mehr

Von der Grammatik zum AST

Von 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

Mehr

Programmiersprachen und Übersetzer

Programmiersprachen 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

Mehr

Grundlagen der Programmierung 2 (Comp-D)

Grundlagen 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

Mehr

VU Software Paradigmen / SS 2012

VU 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

Mehr

Sprachen sind durch folgenden Aufbau gekennzeichnet:

Sprachen 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

Mehr

Kapitel 2. Methoden zur Beschreibung von Syntax

Kapitel 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

Mehr

Alphabet, formale Sprache

Alphabet, 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

Mehr

Syntax von Programmiersprachen

Syntax 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

Mehr

2.1 Allgemeines. Was ist eine Sprache? Beispiele:

2.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

Mehr

LR-Parser, Shift-Reduce-Verfahren

LR-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

Mehr

6 Kontextfreie Grammatiken

6 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,

Mehr

3 Syntax von Programmiersprachen

3 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

Mehr

Deterministische PDAs

Deterministische 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:

Mehr

Was 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 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

Mehr

VU Software Paradigmen / SS 2014

VU 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)

Mehr

Formale Sprachen. Inhaltsverzeichnis. M. Jakob. 10. Dezember Allgemeine Einführung. Aufbau formaler Sprachen

Formale 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

Mehr

Grammatiken. 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 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,

Mehr

Theoretische Grundlagen der Informatik

Theoretische 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

Mehr

Klammersprache Definiere

Klammersprache 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

Mehr

Kontextfreie Grammatiken. Kontextfreie Grammatiken 1 / 45

Kontextfreie 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

Mehr

4. Die lexikalische Analyse

4. 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,..)

Mehr

Definition 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) 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,

Mehr

LL(k)-Analyse. (y) folgt α = β. (x) = start k. (=l> ist ein Linksableitungsschritt)

LL(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)

Mehr

Mehrdeutige Grammatiken

Mehrdeutige 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

Mehr

Thomas Behr. 17. November 2011

Thomas 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

Mehr

2.6 Deterministisches Top-Down-Parsen

2.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

Mehr

kontextfreie Grammatiken Theoretische Informatik kontextfreie Grammatiken kontextfreie Grammatiken Rainer Schrader 14. Juli 2009 Gliederung

kontextfreie 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

Mehr

Objektorientierte Programmierung. Kapitel 3: Syntaxdiagramme

Objektorientierte 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/

Mehr

Pumping-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

Pumping-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

Mehr

Kellerautomat (1/4) Kellerautomat (2/4) Kellerautomat (3/4) Kellerautomat (4/4)

Kellerautomat (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

Mehr

Software Entwicklung 2. Übersetzerbau 1

Software 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

Mehr

Sei Σ ein endliches Alphabet. Eine Sprache L Σ ist genau dann regulär, wenn sie von einem regulären Ausdruck beschrieben werden kann.

Sei Σ 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

Mehr

Einführung IMP-Syntax Reduktionssemantik Maschinen-Semantik. Teil IV. Semantik imperativer Sprachen

Einfü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.

Mehr

Einführung in die Programmiertechnik

Einfü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

Mehr

Fachseminar WS 2008/09

Fachseminar 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

Mehr

FORMALE SYSTEME. 3. Vorlesung: Endliche Automaten. TU Dresden, 17. Oktober Markus Krötzsch

FORMALE 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

Mehr

Compiler: Parser. Prof. Dr. Oliver Braun. Fakultät für Informatik und Mathematik Hochschule München. Letzte Änderung:

Compiler: 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........................................

Mehr

Programmiersprachen und Übersetzer

Programmiersprachen 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

Mehr

Informatik II SS Der Kompilationsprozess (-phasen) Schreiben des Programms. Die Organisation eines typischen Compilers

Informatik 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

Mehr

Reguläre Sprachen. R. Stiebe: Theoretische Informatik für ING-IF und Lehrer,

Regulä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

Mehr

Algorithmen mit konstantem Platzbedarf: Die Klasse REG

Algorithmen 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

Mehr

Compilerbau. Bachelor-Programm. im SoSe Prof. Dr. Joachim Fischer Dr. Klaus Ahrens Dipl.-Inf. Ingmar Eveslage.

Compilerbau. 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

Mehr

Kapitel 2. Methoden zur Beschreibung von Syntax

Kapitel 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

Mehr

Formale Sprachen, reguläre und kontextfreie Grammatiken

Formale 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

Mehr

Theoretische Grundlagen der Informatik

Theoretische 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

Mehr

1 Formale Sprachen, reguläre und kontextfreie Grammatiken

1 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

Mehr

Interpreter - Gliederung

Interpreter - 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

Mehr

Zwischencode-Erzeugung. 2. Juni 2009

Zwischencode-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

Mehr

Kapitel 4: Syntaxdiagramme und Grammatikregeln

Kapitel 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

Mehr

Was ist ein Compiler?

Was 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?

Mehr

Induktive Definition

Induktive 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}

Mehr

Einführung in die Informatik. Programming Languages

Einfü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

Mehr

Kapitel: Die Chomsky Hierarchie. Die Chomsky Hierarchie 1 / 14

Kapitel: 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

Mehr

Literatur Reguläre Ausdrücke

Literatur 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

Mehr

Reguläre Sprachen und endliche Automaten

Regulä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

Mehr

Einführung in die Informatik. Programming Languages

Einfü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

Mehr

Inhalt Kapitel 5: Syntax

Inhalt 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

Mehr

Theoretische Informatik I

Theoretische 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

Mehr

Grundlagen der Theoretischen Informatik

Grundlagen 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 Ü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

Mehr

WS06/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 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.

Mehr

Kontextfreie Sprachen

Kontextfreie Sprachen Kontextfreie Sprachen Bedeutung: Programmiersprachen (Compilerbau) Syntaxbäume Chomsky-Normalform effiziente Lösung des Wortproblems (CYK-Algorithmus) Grenzen kontextfreier Sprachen (Pumping Lemma) Charakterisierung

Mehr

Kapitel IV Formale Sprachen und Grammatiken

Kapitel 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

Mehr

Einführung in die Informatik I (autip)

Einfü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

Mehr

zu große Programme (Bildschirmseite!) zerlegen in (weitgehend) unabhängige Einheiten: Unterprogramme

zu 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

Mehr

Grundlagen der Theoretischen Informatik

Grundlagen 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

Mehr

Kontextfreie Sprachen

Kontextfreie 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

Mehr

8. Turingmaschinen und kontextsensitive Sprachen

8. 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 (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