Automaten und formale Sprachen Notizen zu den Folien 14 Abschlusseigenschaften und Algorithmen kontextfreier Sprachen Kellerautomaten mit Endzuständen (Folie 272) Satz. Eine Sprache wird genau dann von einem Kellerautomaten mit Endzuständen akzeptiert, wenn sie von einem Kellerautomaten (ohne Endzustände) akzeptiert wird. Hinweise warum dieser Satz gilt: ⇒ Wir wandeln einen Kellerautomaten mit Endzuständen in einen gewöhnlichen Kellerautomaten um, indem wir einen neuen Akzeptanzzustand hinzufügen, in dem den Keller abgebaut wird. Der Akzeptanzzustand kann über -Übergänge aus allen ursprünglichen Endzuständen erreicht werden. ⇐ Wir wandeln einen Kellerautomaten in einen Kellerautomaten mit Endzuständen um, indem wir einen Endzustand zE hinzufügen und für jeden Übergang in dem das Kellerbodenzeichen entfernt wird, stattdessen in den neuen Endzustand führen. (Dies funktioniert nur, wenn das Kellerbodenzeichen immer als letztes entfernt wird und niemals auf den Keller gepusht wird. Wir können immer einen solchen Kellerautomaten konstruieren.) Abschluss unter Vereinigung (Folie 275) Die kontextfreien Sprachen sind abgeschlossen unter Vereinigung. Warum ist das der Fall? Seien zwei Grammatiken G1 = (V1 , Σ, P1 , S1 ) und G2 = (V2 , Σ, P2 , S2 ) über demselben Alphabet gegeben. Wir können davon ausgehen, dass die Variablen der zwei Grammatiken disjunkt sind (das heißt, es gibt keine Variable, die sowohl Element von V1 als auch Element von V2 ist), denn wenn dies nicht der Fall wäre, könnten wir die Variablen einer Grammatik einfach umbenennen. Wir können nun eine Grammatik G mit L(G) = L(G1 ) ∪ L(G2 ) konstruieren, indem wir eine neue Startvariable S (die nicht in einer der Grammatiken G1 oder G2 vorkommt) hinzufügen und die folgenden zwei Produktionen aufnehmen: S → S1 S → S2 Wenn wir ein Wort aus S ableiten wollen, können wir S durch S1 oder S2 ersetzen. Je nachdem welche Wahl wir getroffen haben, können wir das Wort nun in G1 oder G2 ableiten. Es ist also klar, dass L(G) = L(G1 ) ∪ L(G2 ). Abschluss unter Produkt (Folie 276) Die kontextfreien Sprachen sind abgeschlossen unter Produkt. Warum is das der Fall? Seien zwei Grammatiken G1 = (V1 , Σ, P1 , S1 ) und G2 = (V2 , Σ, P2 , S2 ) über demselben Alphabet gegeben. Wir gehen wieder davon aus, dass die zwei Grammatiken keine Variablen gemein haben. Wir können nun eine Grammatik G mit L(G) = L(G1 )L(G2 ) konstruieren, indem wir eine neue Startvariable S (die nicht in einer der Grammatiken G1 oder G2 vorkommt) hinzufügen und die folgende Produktion aufnehmen: S → S1 S2 1 Wenn wir versuchen ein Wort aus S abzuleiten, müssen wir im ersten Schritt S1 S2 ableiten. Aus S1 wird ein Wort w1 ∈ L(G1 ) abgeleitet und aus S2 ein Wort w2 ∈ L(G2 ). Die Sprache L(G) besteht also aus der Konkatenation von Wörtern aus S1 und S2 . Abschluss unter der Stern-Operation (Folie 277) Die kontextfreien Sprachen sind abgeschlossen unter der Stern-Operation. Warum is das der Fall? Sei eine Grammatik G1 = (V1 , Σ, P1 , S1 ) gegeben. Wir können nun eine Grammatik G mit L(G) = L(G1 )∗ konstruieren, indem wir eine neue Startvariable S (die nicht in der der Grammatik G1 vorkommt) hinzufügen und die folgenden Produktionen aufnehmen: S → S1 S | Mit den neuen Produktionen können wir eine Folge von S1 belieber Länge ableiten. Jedes abgeleitete S1 kann jetzt durch einem Wort der Sprache L(G1 ) ersetzt werden. Es ist also klar, dass L(G) = L(G1 )∗ . Beispiel Kreuzproduktkonstruktion NFA/Kellerautomat (Folie 279,280) Sei Σ = {<, >}. Sei M der folgende Kellerautomat (mit Endzuständen): M = ({z0 , zE }, Σ, {A, #}, δM , z0 , {zE }), wobei δM gegeben ist durch: δM (z0 , <, #) 3 (z0 , A#) δM (z0 , <, A) 3 (z0 , AA) δM (z0 , >, A) 3 (z0 , ) δM (z0 , , #) 3 (zE , #) M akzeptiert die Sprache korrekt geklammerter Ausdrücke. Sei A der folgende (nichtdeterministische) endliche Automat: A = ({z1 , z2 }, Σ, δA , z1 , {z2 }), wobei δA gegeben ist durch: δ(z1 , <) = {z1 } δ(z1 , >) = {z2 } δ(z2 , <) = ∅ δ(z2 , >) = {z2 } A kann folgendermaßen grafisch dargestellt werden: > z1 < z2 > A akzeptiert die Sprache L((<)∗ (>)∗ ). Der folgende Kellerautomat akzeptiert die Sprache N (M ) ∩ T (A): M 0 = (Z, Σ, Γ, δ, (z0 , z1 ), #, E) wobei 2 • Z = {z0 , zE } × {z1 , z2 } = {(z0 , z1 ), (z0 , z2 ), (zE , z1 ), (zE , z2 )} • E = {zE } × {z1 , z2 } = {(zE , z1 ), (zE , z2 )} und δ wie folgt definiert ist: δ((z0 , z1 ), <, #) 3 ((z0 , z1 ), A#) δ((z0 , z1 ), <, A) 3 ((z0 , z1 ), AA) δ((z0 , z1 ), >, A) 3 ((z0 , z2 ), ) δ((z0 , z1 ), , #) 3 ((zE , z1 ), #) δ((z0 , z2 ), , #) 3 ((zE , z2 ), #) Durch scharfes hingucken erkennen wir, dass M 0 die Sprache N (M 0 ) = {<n >n | n ≥ 0} = N (M ) ∩ T (A) akzeptiert. Zu Folie 275 Ein Problem ist entscheidbar, falls es einen Algorithmus gibt, der es löst. Wie bei den Algorithmen regulärer Sprachen, nehmen wir in allen Fällen an, dass (kontextfreie) Sprachen in der Form einer Grammatik oder eines Kellerautomaten angegeben werden. 15 Erzeugen eines Parsers mit JavaCC Zu Folien 299–300 In der Praxis wird einen anderen Syntax verwendet (Nichtterminale werden in Spitzenklammern eingeschlossen, Terminale in Anführungszeichen und statt →“ wird ::=“ oder =“ geschrieben); ” ” ” wir benutzen heute aber den Syntax, den wir im Rest der Vorlesung schon benutzt haben. Zu Folie 301 Beispiele von Ausdrücke, die von der EBNF-Grammatik akzeptiert werden: (2+5)/2 ((2+5)/2)*34 Zu Folie 302 JavaCC erzeugt eine Klasse, die den Parser implementiert. Name ist der Name der zu erzeugenden Klasse. 3 JavaCC anrufen In UNIX-ähnliche Betriebssysteme (Linux, Mac OS X, . . . ): $javacc Arith.jj Java Compiler Compiler Version 5.0 (Parser Generator) (type "javacc" with no arguments for help) Reading from file Arith.jj . . . File "TokenMgrError.java" does not exist. Will create one. File "ParseException.java" does not exist. Will create one. File "Token.java" does not exist. Will create one. File "SimpleCharStream.java" does not exist. Will create one. Parser generated successfully. $javac Arith.java $java Arith (2+5)/2 (Eingabe-Ende wird angegeben mit Strg-D.) In der Windows Kommandozeile: F:\Parser\>javacc Arith.jj Java Compiler Compiler Version 5.0 (Parser Generator) (type "javacc" with no arguments for help) Reading from file Arith.jj . . . File "TokenMgrError.java" does not exist. Will create one. File "ParseException.java" does not exist. Will create one. File "Token.java" does not exist. Will create one. File "SimpleCharStream.java" does not exist. Will create one. Parser generated successfully. F:\Parser\>javac Arith.java F:\Parser\>java Arith (2+5)/2 (Eingabe-Ende wird angegeben mit Strg-Z.) 4