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: Die deterministisch kontextfreien Sprachen sind eine echte Teilmenge der kontextfreien Sprachen. 685
Akzeptanzmodi Bemerkung: Die Akzeptanzmodi leerer Stack und akzeptierende Zustände sind für DPDAs nicht äquivalent. Beweisidee: Für die Sprache 0* gibt es einen DPDA mit akzeptierenden Zuständen, nicht aber mit leerem Stack. Also: Der Begriff deterministisch kontextfrei kann (nur) über DPDAs mit akzeptierenden Zuständen definiert werden. 686
Abschluss gegen Komplement Ein DPDA kann verwerfen, indem er vor dem Ende der Eingabe alle Zeichen vom Stack entfernt. Neues unterstes Symbol im Stack Ein DPDA akzeptiert auch, wenn er bei den ε-bewegungen nach Lesen der Eingabe einmal einen akzeptierenden Zustand erreicht, diesen aber wieder verlässt. Im Zustand merken, ob dies eingetreten ist. 687
Abschluss gegen Komplement Sei A=(Q,Σ,Γ,q 0,Z 0,δ,F) ein DPDA. Konstruktion eines DPDAs B: Q ={q 0,q akz,q rej } (Q {1,2,3}), Startzustand q 0, Σ =Σ, Γ =Γ {Z*}, Z 0 =Z 0 F ={q akz } (Q {3}). Definiere für q Q: 688
Idee Leerer Stack in A vor Eingabeende Z* in B gelesen Gehe in q akz und bleibe dort bis zum Erreichen des Wortendes. (q,1): Seit dem Lesen des letzten Zeichens (bzw. Beginn der Rechnung beim ersten Zeichen) wurde Zustand aus F erreicht. (q,2): wurde kein Zustand aus F erreicht. Vor dem Lesen eines Zeichen wird von (q,2) in (q,3) übergegangen B akzeptiert. 689
Übergangsfunktion Zu Beginn Z* auf Stack legen: δ (q 0,ε,Z 0 ) = ((q 0,f(q 0 )),Z 0 Z*). Leerer Stack bei A entspricht Z* bei B: δ ((q,1),ε,z*)=(q rej,z*) δ (q rej,b,z*)=(q akz,z*) für alle b Σ δ ((q,x),b,z*)=(q akz,z*) für alle x {2,3}, b Σ δ (q akz,b,z*)=(q akz,z*) für alle b Σ 690
Übergangsfunktion ε-übergänge von A simulieren: Falls δ(q,ε,z)=(q,α) mit Z Z*: δ ((q,1),ε,z)=((q,1),α), δ ((q,2),ε,z)=((q,f(q )),α). a-übergänge von A simulieren (a Σ): Falls δ(q,a,z)=(q,α) mit Z Z*: δ ((q,1),a,z)=((q,f(q )),α), δ ((q,3),a,z)=((q,f(q )),α), δ ((q,2),ε,z)=((q,3),z). 691
Eine nicht det.kontextfreie Sprache Satz: Sei L={a n b n c n n 1}. Dann ist L kontextfrei, aber nicht deterministisch kontextfrei. Beweis: L ist kontextfrei Übungen Falls L deterministisch kontextfrei wäre, wäre auch L (deterministisch) kontextfrei, was nicht der Fall ist. 692
LR(k)-Parser Ziele: Effizienter (und deterministischer) Test, ob ein gegebenes Wort w in der Sprache L(G) enthalten ist. Falls ja: Konstruktion des Syntaxbaums Falls nein: Hinweise zum Fehler CYK-Algorithmus ist zu langsam. 693
LR(k)-Parser Bedeutung der Abkürzung: L: left-to-right scan R: rightmost derivation Berechnung einer Rechtsableitung Der Syntaxbaum wird bottom-up aufgebaut. k: Lookahead von k Zeichen Der Algorithmus muss anhand der nächsten k Zeichen der Eingabe entscheiden, welche Ableitungsregel anzuwenden ist. 694
Notation r α β, falls β aus α mit einer Rechtsableitung in einem Schritt herleitbar ist. α* r β, falls β aus α mit einer endlichen Folge von Rechtsableitungen herleitbar ist. 695
Beispiel: L={a i b i c j d j e k f k i,j,k 0} X S Y Z a X b c Y d e Z f a X ε 3 5 7 2 1 b 4 ε 8 6 ε Links-Rechts-Scan erzeugt Num. der Regeln in bottom-up- Ordnung (Postorder) Anwendung der Regeln gemäß der umgedrehten Nummerierung ist Rechtsableitung. Lookahead nötig. 696
Bemerkungen Alternative Sichtweise: Syntaxanalyse entspricht Reduzierung der Eingabe auf das Startsymbol durch umgekehrtes Anwenden der Regeln der Grammatik. Syntaxanalyse mit Hilfe von Lookahead ist nur für spezielle Grammatiken möglich siehe folgende Beispiele. 697
Beispiel T8.2.6 Grammatik G 1 für a*c+a*d: S C, S D, C ac, C c, D ad,d d S S C D a C a D a C c a D d Lookahead von 0 ausreichend 698
Beispiel T8.2.7 Grammatik G 2 für a*c+a*d: S Cc, S Dd, C Ca, C ε, D Da, D ε S S C D C C C c a a D D D d a a Syntaxanalyse mit endlichem Lookahead nicht möglich. ε ε 699
Beispiel T.8.2.8 Grammatik für {aabbb,aabba}: S CD, C a, D EF, D ag, E ab, F bb,g bba. S S C D C D a E F a b b b a a G b b a Lookahead von 2 nötig. 700
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. (Vereinfachung gegenüber T8.2.4) 701
Neues Startsymbol S Ziel: Wir wollen einfach das Ende einer erfolgreichen Herleitung erkennen. Dazu: Modifiziere die Grammatik: Neues Startsymbol S und Regel S S. Dann: Wenn diese Regel erreicht wird, liegt eine erfolgreiche bottom-up Herleitung vor. 702
Definition T8.2.5 Eine kontextfreie Grammatik G heißt LR(k)- Grammatik, falls für alle α,β,γ (V T )*, alle A,B V und alle w,x,y T * gilt: * r S αaw αβw S γbx αβy α=γ, A=B und y=x. FIRST k (w)=first k (y) r * r r 703
Shift-Reduce-Parser Spezieller DPDA, der zusätzlich eine Rechtsableitung berechnet. Aufbau: Eingabeband Lookahead von k S t a c k Steuerung mit Übergangstabelle Ausgabe 704
Übergangstabelle Enthält das Programm des Parsers. Operationen (in Abhängigkeit vom Lookahead und dem obersten Stacksymbol) Shift: Lies nächstes Zeichen der Eingabe ein und schiebe es auf den Stack. Zusätzlich speichere Zustand auf dem Stack. Reduce: Wende eine Regel A α an. Dazu entferne den zu α gehörenden Stackinhalt und lege A u. Zustand auf Stack. Error/Accept: Rechnung verwerfend/akzeptierend beenden. 705
Reduce-Operation Anwendung der Regel A β 1 β r Entferne die obersten 2r Symbole vom Stack (β 1,,β r, sowie die Zustände dazwischen) Weicht vom bisherigen Modell ab, da mehrere Zeichen vom Stack entfernt werden. Schreibe A auf Stack. Berechne anhand Tabelle neuen Zustand und lege ihn auf dem Stack ab. 706
Bsp: S S, S SaSb, S ε R0 Lookahead T 0 T 1 T 2 T 3 shift shift error error T 4 T 5 T 4 R2 R2 error error error T 5 a R2 shift R2 R1 error error R2 error R2 Acc error R1 error error error error T 6 shift shift error error T 4 T 7 T 7 R1 R1 error error error error Alter Zustand R1 Aktion b ε S T 1 T 3 T 6 Oberstes Stacksymbol R2 neuer Zustand a b error T 2 error error error error 707
Wie berechnet man die Tabelle? Algorithmus: siehe Kapitel T8.3 und T8.4 oder Aho, A., Sethi, R. und Ullman, J.D. Compilers, Principles, Techniques and Tools, Addison-Wesley, 1986. Für LR(0)-Parser: http://en.wikipedia.org/wiki/lr_parser Zu aufwändig für diese Vorlesung. 708
Wie berechnet man die Tabelle? Praxis: Verwende Parser-Generator. Eingabe: Kontextfreie Grammatik sowie zusätzliche Befehle, die beim Anwenden der Regeln auszuführen sind. Ausgabe: Parser. Beispiele (für sog. LALR(1)-Gram.): yacc (yet another compiler compiler) bison 709
Arbeitsweise e. Parser-Generators Grammatik & Befehle: name.y Parser als C-Programm name.tab.c Ausführb. Programm bison yacc cc 710
Aufbau der Datei name.y /* Teil 1: Deklarationen */ %{ #include <stdio.h> int nr; Deklarationen für eigene Prozeduren/Var. %} int yylex(void); void yyerror (char const *); Deklarationen der Parser-Proz. 711
Teil 2: Angabe der Grammatik %% s: 0 s 1 {nr++; $$=nr; printf( Knoten %d, Regel s->0s1 mit Nachfolger %d\n,nr,$2); } ; 0 1 {nr++; $$=nr; printf( Knoten %d, Regel s->01\n,nr); } Ende der Regeln für s /* ggf. weitere Regeln */ C-Code, der beim Erreichen der Regel auszuführen ist 712
Teil 3: Angabe von weiteren Proz. %% int yylex(void) { int c; c=getchar(); if ((c!= 0 ) && (c!= 1 )) return 0; return c; } int yyerror() /* weggelassen */ int main(void) { nr=0; return yyparse(); } Übergabe von einzelnen Zeichen an den Parser Ende der Eingabe Fehlerbehandlung Aufruf des Parsers 713