5.5 LR-Grammatiken

Motivation und Intuition

Die LL-Parser, die wir im letzten Teilkapitel gesehen haben, versuchen, eine Linksableitung Schritt für Schritt zu konstruieren. Eine Hauptschwäche ist, dass Sie von einer Wortform die nächste Ableitungsregel bestimmen müssen, ohne das "Endergebnis" von überhaupt vollständig gelesen zu haben. Sie müssen also erkennen, dass der rote / mit "?" markierte Pfeil in der Ableitung die richtige Entscheidung ist, obwohl sie von dem aus abgeleiteten Wort nur die ersten Buchstaben sehen. Wäre es nicht besser, der Maxime zu folgen:

Maxime: Wende eine Regel nur dann an, wenn Du im Eingabewort ein vollständiges Teilwort erkannt hast, das aus ableitbar ist.

Ich will diese Maxime an einem Beispiel demonstrieren. Betrachten wir die Grammatik

Die von der Grammatik erzeugte Sprache ist also: mindestens ein und mindestens so viele s wie s. Die Sprache ist nicht LL: Die Ableitungen unterscheiden sich bereits in der ersten angewandten Produktion; trotzdem stimmen die Endergebnisse in den ersten (ja sogar ) Zeichen überein. Ich will nun aber zeigen, wie man eine Ableitung findet, wenn man die obige Maxime befolgt. Hierfür arbeiten wir uns von hinte nach vorne durch: statt mit anzufangen und zu hinzuarbeiten, fangen wir mit an und versuchen, und zu hin zurückzuarbeiten. In der folgenden Animation ist der Teil des Wortes, den unser Parser gelesen hat, schwarz markiert; der noch nicht gelesene Teil ist grau.

Wir erhalten eine Rechtsableitung, allerdings in verkehrter Reihenfolge, vom Zielwort hin zum Startsymbol. Daher schreiben wir in der obigen Animation auch statt geschrieben. Wenn Sie die Animation in verkehrter Reihenfolge anschauen (also immer auf klicken), dann sehen Sie die Rechtsableitung in der "üblichen" Reihenfolge.

Notation

Da aus der Sicht unseres neuen Parsers die Rückwärtsanwendung einer Produktion , also der Schritt einen Fortschritt darstellt und zeitlich auch nach vorne geht, verwende ich ab jetzt ein neues Pfeilsymbol, das von links nach rechts geht und daher dem in Europa üblichen Gefühlt, dass die Zeit von links nach rechts verläuft, mehr entgegen kommt: .

Definition 5.5.1 (Reduktion und Linksreduktion) Seien Wortformen und eine Produktion. Dann schreiben wir und sagen reduziert zu . Man nennt einen solchen Schritt auch einen Reduktionsschritt.

Wir nennen den Schritt einen Linksreduktionsschritt, wenn rechts von nur Terminale stehen. Wenn also und somit

Eine Folge von Linksreduktionsschritten nennen wir eine Linksreduktion. Links, weil wir uns von links nach rechts in den terminalen Teil hineinarbeiten. Wir werden im folgenden nur Linksreduktionsschritte betrachten und sagen daher manchmal auch einfach nur Reduktionsschritt.
Beobachtung: Wenn wir einen Linksreduktionsschritt betrachten, also so sehen wir, dass das am weitesten rechts stehende Nichtterminal ersetzt worden ist; Linksreduktionen entsprechen also einer Rechtsableitung.

Vorsicht. Verfallen Sie nicht in verfrühte Euphorie. Das Prinzip der Linksreduktion ist immer noch nichtdeterministisch. Die folgende Abbildung illustriert, dass manchmal mehrere Reduktionen anwendbar sind:

Uns als Betrachter ist klar, dass nur die obere Alternative zum Erfolg führt; wie das der Parser aber im Allgemeinen entscheiden soll, ist uns zu diesem Zeitpunkt noch nicht klar und wird uns für den Rest dieses Kapitels beschäftigen.

Definition 5.5.2 (Gültige Wortform, korrekter Reduktionsschritt) Eine Wortform heißt gültig, wenn es eine Rechtsableitung gibt. Ein Linksreduktionsschritt heißt korrekt, wenn gültig ist.

Beobachtung: Das impliziert unmittelbar, dass die Wortform auch gültig ist.

Überlegen wir nun, welche Situationen schlecht wären für unser Vorhaben, einen Parser ohne Sackgassen zu implementieren.

  1. Schlechter Fall 1: Es gibt für eine Wortform zwei korrekte Linksreduktionsschritte: und . In diesem Falle gäbe es zwei verschiedene Rechtsableitungen (wir nehmen an, dass man aus jedem Nichtterminal mindestens ein Wort ableiten kann; andernfalls kann man solche nutzlosen Nichtterminale eliminieren). Das Wort hat also zwei verschiedene Ableitungsbäume. Die Grammatik ist somit mehrdeutig. Mit uneindeutigen Grammatiken beschäftigen wir zunächst gar nicht; sie sind von einem praktischen Standpunkt aus auch unattraktiv: wenn z.B. eine Programmiersprache eine mehrdeutige Grammatik hätte, dann wäre ja für gewisse Programme nicht eindeutig festzustellen, was damit gemeint ist. Wir nehmen also an, dass die Grammatik eindeutig ist und somit dieser Fall nicht eintreten kann.
  2. Schlechter Fall 2: Der Linksreduktionsschritt ist korrekt, aber der Schritt ist nicht korrekt, obwohl eine gültige Wortform ist. Dieser Fall ist schlecht, weil der Parser zum Zeitpunkt , wenn er also gelesen hat, aber noch nicht , sich nicht sicher sein kann, dass der Reduktionsschritt korrekt ist. Korrektheit hängt davon ab, was weiter rechts davon kommt.
Beispiel 5.5.3 Erinnern Sie sich: unsere obige Grammatik ist eindeutig (das ist leicht zu sehen), allerdings tritt Schlechte Fall 2 ein: der Reduktionsschritt ist korrekt (hier also , aber leider ist nicht korrekt (hier , da gültig ist, aber nicht. Die Entscheidung für eine Reduktionsregel kann also nicht ohne Wissen über das, was dahinter kommt, getroffen werden.

Definition 5.5.4 (-Grammatiken) Eine kontextfreie Grammatik heißt -Grammatik, wenn keiner der obigen schlechten Fälle eintritt. Formal, wenn (1) die Grammatik eindeutig ist und (2) wenn ein korrekter Linksreduktionsschritt bedeutet, dass auch alle anderen Linksreduktionsschritte korrekt sind, sofern selbst überhaupt gültig ist.

In Worten/Graustufen: wenn wir guten gewissens anwenden dürfen, ohne auch nur ein Zeichen von gelesen zu haben.

Übungsaufgabe 5.5.1 Zeigen Sie, dass die folgende Grammatik ist: Als ersten Schritt sollten Sie sich überlegen, wie gültige Wortformen überhaupt aussehen können.
Übungsaufgabe 5.5.2 Zeigen Sie, dass die folgende Grammatik ist: Als ersten Schritt sollten Sie sich überlegen, wie gültige Wortformen überhaupt aussehen können.
Übungsaufgabe 5.5.3 Wir ändern die Grammatik etwas ab: Die erzeugte Sprache ist . Zeigen Sie, dass die Grammatik nicht ist.
Es stellen sich unmittelbar zwei zentrale Fragen:
  1. Wie können wir verifizieren, ob eine gegebene Grammatik die -Bedingung erfüllt?
  2. Falls sie es tut, wie können wir für eine gegebene Wortform den korrekten Reduktionsschritt finden? Insbesondere die Zerlegung in ?
Beide Aufgaben können mit Hilfe eines endlichen Automaten, des DK-Automaten erledigt werden (DK steht als Abkürzung für Donald Knuth, der als erstes diese Idee hatte).
Hinweis. Was nun folgt, ist mathematisch recht herausfordernd. Lesen Sie daher gerne auch das Kapitel 2.4 (Deterministic context free languages) im Lehrbuch Introduction to the Theory of Computing von Michael Sipser. Meine Darstellung des doch recht schwierigen Materials fußt auf diesem Kapitel, weicht aber doch stark genug von Sipser ab, so dass es womöglich hilfreich ist, beides zu lesen: dieses Vorlesungsskript und das Kapitel in Sipers Buch.

Gültige Wortformen und deren Ableitungsbäume

Zu jeder Ableitung können wir eindeutig einen Ableitungsbaum zeichnen. Wenn die Grammatik eindeutig ist, so hängt auch der Baum nur vom Wort ab und nicht von der Ableitung . Allerdings können wir für Zwischenformen auch einen Ableitungsbaum zeichnen, und der unterscheidet sich stark, abhängig davon, ob eine Rechtsableitung, Linksableitung oder sonst was ist. Ich zeige Ihnen jetzt ein Beispiel für eine Grammatik und eine Handvoll Ableitungen samt Ableitungsbaum. Es ist zu diesem Zeitpunkt irrelevant, ob eindeutig oder sogar ist. Ich interessiere mich gerade nur für Ableitungsbäume von Wortformen.

Fällt Ihnen etwas auf? Schauen Sie sich bitte noch ein weiteres Beispiel an für den Ableitungsbaum einer in einer gültigen Wortform, also von einer, die in einer Rechtsableitung vorkommen kann:

Warten Sie! Scrollen Sie erst weiter, wenn Sie den Baum oben lang genug angeschaut haben! Versuchen Sie selbst, die spezielle Form dieses Baumes möglichst formal zu beschreiben!
Auflösung. Hier sehen Sie noch einmal den gleichen Baum, nun aber gewisse Teile verschieden umrandet / eingefärbt.

Sie sehen: links vom Stamm gibt es nur Blätter. Rechts vom Stamm ist jedes Blatt ein Terminalsymbol. Wir erkennen auch, was der letzte Ableitungsschritt war, der zu diesem Baum geführt hat: die Blüte ist hinzugekommen, in diesem Fall also . Wir definieren nun eingeführten Begriffe formal:

Definition / Beobachtung 5.5.5 (Stamm, linker Rand, Blüte, rechter Rest) Sei eine Rechtsableitung, also eine gültige Wortform, und der Ableitungsbaum von . Stamm ist der Pfad von der Wurzel zu jenem inneren Knoten , der von allen inneren Knoten, deren Kinder allesamt Blätter sind, am weistesten links steht. Die Kinder von , per Definition alles Blätter, sind die Blüte. Die Menge der Knoten, die einen Stammknoten als rechtes Geschwister haben, heißt der linke Rand. Jeder Knoten im linken Rand muss ein Blatt sein, ansonsten stünde er ja weiter links als ; die Menge der rechten Geschwisterkinder von Stammknoten sowie deren Nachkommen heißt der rechte Rand. Im rechten Rest ist jedes Blatt ein Terminalsymbol, ansonsten wäre es keine Rechtsableitung.

Die Beschriftung der Knoten im linken Rand ergibt eine Wortform ; die Blüte ergibt . Die Blätter im rechten Rand sind ausschließlich mit Terminalen beschriftet und ergeben ein Wort . Der ganze Baum stellt also eine Rechtsableitung

dar. Wir sagen auch, dass eine Blüte von ist, ohne über den Ableitungsbaum selbst zu reden. Hierbei ist zu beachten, dass in einer mehrdeutigen Grammatik eine gültige Wortform mehrere Ableitungsbäume und somit mehrere Blüten haben kann, die Unterteilung also nicht eindeutig ist. Für eindeutige Grammatiken ist die Unterteilung aber eindeutig.

Sei weiterhin die Beschriftung des Elternknoten der Blüte (notwenigerweise ein Nichtterminal; Terminale haben keine Kinder). Dann ist eine Produktion in der Grammatik und eine gültige Wortform; wir erhalten den Ableitungsbaum von , indem wir die Blüte von entfernen. Wir schließen, dass ein korrekter Linksreduktionsschritt ist.

Wir können also, ausgehend von der Wortform , eine Linksreduktion finden, indem wir den Ableitungsbaum von zeichnen und immer wieder die Blüte abschneiden:

Um für eine Wortform den korrekten Reduktionsschritt zu finden, reicht es also aus, linken Rand und Blüte zu bestimmen, also und , so dass und korrekt ist ( steht hier für das Nichtterminal, mit dem der Elternknoten der Blüte beschriftet ist). Linken Rand und Blüte zu finden scheint keine leichte Aufgabe zu sein: schließlich müssen wir dafür doch den Ableitungsbaum von bilden, was selbst wieder eine Parsing-Aufgabe ist???

An dieser Stelle zeigt sich die Genialität des DK-Ansatzes: der Ableitungsbaum von kann beliebig verschachtelt sein, aber Stamm, linker Rand und Blüte haben zusammen eine einfache, beinahe linear anmutende Struktur. Schematisch:

Die Aussage "Stamm, linker Rand und Blüte haben eine einfache Struktur" können wir formalisieren:

Lemma 5.5.6 Für eine kontextfreie Grammatik definieren wir die Sprache : also die Menge aller Wortformen, die als Beschriftung des linken Randes und der Blüte in einem Ableitungsbaum einer gültigen Wortform vorkommen können.

Die Sprache SLRB ist regulär.

Insbesondere gibt es eine reguläre Grammatik für SLRB, so dass die Blüte genau die im letzen Ableitungsschritt erzeugten Terminalsymbole sind.

Hier ist etwas Mentalgymnastik vonnöten: aus Sicht der Sprache SLRB sind Terminalsymbole. Sie können ja schließlich in den Wörtern der Sprache auftauchen. Die Grammatik hat also die Terminalsymbole . Darüberhinaus hat sie die Nichtterminale , also für jedes Nichtterminal von ein Meta-Nichtterminal . Das entspricht dem in den obigen Bäumen, wo also als Blatt vorkommt; das entspricht dem , also wo als innerer Knoten vorkommt. Bevor ich formal definiere, zeige ich den obigen Ableitungsbaum (ohne rechten Rand, weil der ja bei SLRB eh fehlt) und annotiere jeden Knoten auf dem Stamm mit der entsprechenden -Produktion.

Definition 5.5.7 Sei eine kontextfreie Grammatik. Die SLRB-Grammatik oder DK-Grammatik von ist die reguläre Grammatik mit , wobei für jede -Produktion mit die Produktionen besitzt.

Strenggenommen ist eine erweitert reguläre Grammatik, weil manche Regeln mehrere Terminale hintereinander haben.

Beobachtung 5.5.8 erzeugt die Sprache .
Beispiel 5.5.9 Für unsere Grammatik oben ergeben sich folgende Produktionen in :

Nochmals: Produktionen wie sind regulär (strenggenommen: erweitert regulär), weil aus Sicht von beides Terminalsymbole sind. Wir können nun unseren -Parser beschreiben:

Algorithmus 5.5.10 - Der -Parser. Starte mit einem leerem Stack. Sei der Inhalt des Stacks zu einem Zeitpunkt.
  1. Wenn , dann betrachte die letzte angewandte -Produktion und schreibe . Wende die -Produktion rückwärts an, reduziere also Konkret: lösche vom Stack und ersetze es durch .
  2. Falls , lies das nächste Eingabezeichen und lege es auf den Stack.
Der Parser endet, wenn weder Schritt 1 noch Schritt 2 möglich sind; wenn zu diesem Zeitpunkt nur noch auf dem Stack liegt, akzeptiert er, andernfalls lehnt er das Eingabewort ab.
Theorem 5.5.11 Wenn der -Parser akzeptiert, dann hat er eine Linksreduktion und somit eine Rechtsableitung konstruiert; es gilt also .

Wenn umgekehrt gilt und die -Bedingung erfüllt, dann findet der Parser die Rechtsableitung ,

Beweis. Da eindeutig ist, hat jede gültige Wortform eine eindeutige Rechtsableitung und einen dazugehörigen Ableitungsbaum mit einem linken Rand , einer Blüte und einem rechten Rest. Wir nennen die Wortform den aktiven Teil von . Beachten Sie, dass rechts vom aktiven Teil nur Terminalsymbole folgen. Da eindeutig ist, nennen wir auch den linken Rand von anstatt den linken Rand von . Betrachten wir einen Zeitpunkt während des Parsing-Prozesses. Sei der Stackinhalt und der ungelesene Teil des Eingabewortes. Wir werden beweisen, dass zu jedem Zeitpunkt folgende Invariante gilt:

Behauptung. (i) ist eine gültige Wortform. (ii) ist ein Präfix des aktiven Teils .

Beweis. Die Behauptung gilt offensichtlich am Anfang, da und somit eine gültige Wortform ist. Des weiteren ist der Stack leer, also , und daher sicherlich ein Präfix von . Wir zeigen nun, dass, wenn die Invariante in Schritt gilt, sie auch im nächsten Schritt gilt. Es gibt nun zwei Möglichkeiten.

  1. Der Parser wendet Schritt 1 an, also . Das heißt nach Definition von SLRB, dass es ein gibt, so dass eine gültige Wortform ist und der aktive Teil, also , und ist die (eindeutige) Blüte von . Sei die letzte angewandte -Produktion. Dann ist ein korrekter Reduktionsschritt. Da nach Annahme eine -Grammatik ist, ist nach Definition auch korrekt. Der Stackinhalt im nächsten Schritt ist somit ; der ungelesene Teil ist nach wie vor , und somit gilt Teil (i) der Invariante. Um zu sehen, dass (ii) gilt, beachten Sie, dass nun auf dem Stack oben ein Nichtterminal liegt: ; da rechts vom aktiven Teil nur Terminale stehen, muss im aktiven Teil von liegen; somit ist Worten: der Stackinhalt ein Präfix des aktiven Teils.
  2. Der Parser wendet Schritt 2 an, also , er liest und legt es auf den Stack. Im nächsten Schritt ist der Stackinhalt und das ungelesene Wort ist . Teil (i) der Behauptung gilt offensichtlich, da und somit immer noch eine gültige Wortform ist. Um zu sehen, dass Teil (ii) gilt, beachten Sie, dass ist. Das heißt also, dass der aktive Teil von länger ist als (kürzer kann er laut Invariante nicht sein). Daher ist nun auch ein Präfix des aktiven Teils.
Wenn das Eingabewort gelesen ist, ist nun und Stackinhalt ist selbst eine gültige Wortform, die allerdings nicht weiter reduziert werden kann. Also muss gelten und der Parser akzeptiert.