Grammatiken

Alphabet:  Eine endliche, nicht-leere Menge \(\Sigma \) heißt Alphabet.
Die Elemente von \(\Sigma \) heißen Buchstaben, Zeichen oder Terminalsymbole.

Menge \(\Sigma ^\ast \) aller Wörter:  Sei \(\Sigma \) ein Alphabet. Dann ist \(\Sigma ^\ast \) die Menge aller (endlichen) Wörter, die über \(\Sigma \) gebildet werden können (dabei ist auch das leere Wort \(\varepsilon \) zugelassen).
Ein Wort ist dabei eine endliche Folge von Buchstaben aus \(\Sigma \). Außerdem sei \(\Sigma ^+ := \Sigma ^\ast \setminus \{\varepsilon \}\).

Bemerkung: Es gibt \(1\) Wort der Länge \(0\), \(|\Sigma |\) Wörter der Länge \(1\), \(|\Sigma |^2\) Wörter der Länge \(2\) usw., d. h. \(|\Sigma |^k\) Wörter der Länge \(k\). \(\Sigma ^\ast \) ist somit immer abzählbar unendlich.
Ein Monoid ist eine Menge mit einer Verknüpfung \(\circ \), sodass \(\forall _{a, b, c}\; (a \circ b) \circ c = a \circ (b \circ c)\) (Assoziativität) und \(\exists _{e} \forall _{a}\; e \circ a = a \circ e = a\) (neutrales Element). Man kann daher \(\Sigma ^\ast \) auch freies Monoid über \(\Sigma \) nennen, wobei die Grundmenge \(\Sigma \) und die Verknüpfung die Konkatenation ist (\(e\) ist das leere Wort). „Frei“ deshalb, weil sich jedes Wort aus \(\Sigma ^\ast \) auf eindeutige Weise als Verknüpfung von Buchstaben aus \(\Sigma \) darstellen lässt.

formale Sprache:  Sei \(\Sigma \) ein Alphabet. Eine Teilmenge von \(\Sigma ^\ast \) heißt formale Sprache.

Bemerkung: Aufgrund \(|\Sigma ^\ast | = \aleph _0\) ist die Kardinalität der Menge aller formalen Sprachen gleich \(\aleph _1\). Da formale Sprachen selbst auch meistens unendlich sind, benötigt man für sie endliche Beschreibungsmöglichkeiten. Dafür dienen die Grammatiken und die Automaten.

Grammatik:  Eine Grammatik ist ein \(4\)-Tupel \(G = (V, \Sigma , P, S)\), wobei

  • \(V\) eine endliche, nicht-leere Menge (die Menge der Variablen),

  • \(\Sigma \) eine endliche, nicht-leere Menge mit \(V \cap \Sigma = \emptyset \) (das Terminalalphabet),

  • \(P\) eine endliche Teilmenge von \((V \cup \Sigma )^+ \times (V \cup \Sigma )^\ast \)
    (die Menge der Regeln oder Produktionen) und

  • \(S \in V\) (die Startvariable) ist.

Für \((u, v) \in P\) schreibt man auch \(u \rightarrow v\).

Satzform:  Ein Wort \(w \in (V \cup \Sigma )^\ast \) heißt Satzform.

Übergangsrelation:  Seien \(G = (V, \Sigma , P, S)\) eine Grammatik und \(u, v \in (V \cup \Sigma )^\ast \).
Dann sei \(u \Rightarrow _G v\), falls \(u = w_1 u_1 w_2\), \(v = w_1 u_2 w_2\) mit \(w_1, w_2 \in (V \cup \Sigma )^\ast \) und \((u_1, u_2) \in P\).
Dies definiert eine Relation \(\Rightarrow _G\) auf \((V \cup \Sigma )^\ast \), sie heißt Übergangsrelation.
\(\Rightarrow _G^\ast \) ist die reflexive und transitive Hülle von \(\Rightarrow _G\) (d. h. \(u \Rightarrow _G^\ast v\) gilt genau dann, wenn es Wörter \(w_1, \dotsc , w_k \in (V \cup \Sigma )^\ast \), \(k \in \natural _0\) gibt mit \(u \Rightarrow _G w_1 \Rightarrow _G \dotsb \Rightarrow _G w_k \Rightarrow _G v\) oder wenn \(u = v\)).

Ableitung:  Eine Folge von Wörtern \((S, w_1, \dotsc , w_k)\) mit \(w_k \in (V \cup \Sigma )^\ast \), \(k \in \natural \) und
\(S \Rightarrow _G w_1 \Rightarrow _G \dotsb \Rightarrow _G w_k\) heißt Ableitung von \(w_k\).

Linksableitung:  Eine Linksableitung ist eine Ableitung, bei der immer die am weitesten links stehende Variable ersetzt wird.
(Dies ergibt nur für kontextfreie Grammatiken Sinn, d. h. falls \(P \subset V \times (V \cup \Sigma )^+\).)

erzeugte Sprache:  Die von einer Grammatik \(G = (V, \Sigma , P, S)\) erzeugte Sprache ist
\(L(G) := \{w \in \Sigma ^\ast \;|\; S \Rightarrow _G^\ast w\}\).

Beispiel: \(G = (\{S\}, \{a, b\}, P, S)\) mit \(P = \{(S, ab), (S, aSb)\} = \{S \rightarrow ab \;|\; aSb\}\) (BNF) ist eine Grammatik mit \(L(G) = \{a^n b^n \;|\; n \ge 1\}\) (dies ist eine Kurzschreibweise).

Beispiel: Für ein beliebiges Alphabet \(\Sigma \) erhält man mit \(G = (\{S\}, \Sigma , P, S\}\) und
\(P = \{S \rightarrow a \;|\; a \in \Sigma \} \cup \{S \rightarrow \varepsilon ,\; S \rightarrow SS\}\) eine Grammatik mit \(L(G) = \Sigma ^\ast \).
\(L(G) = \emptyset \) erhält man mit \(P = \emptyset \).

Beispiel: Weitere (Anwendungs-)Beispiele für Grammatiken sind natürliche Sprachen (hier nicht), korrekte arithmetische Ausdrücke (z. B. \((a + a) \cdot a\), aber nicht \((((a((a((\) oder \((\cdot + a))(\)), Palindrome über dem Alphabet \(\{a, b\}\) (mittels \(S \rightarrow aSa \;|\; bSb \;|\; a \;|\; b \;|\; \varepsilon \)) und Wörter der Form \(a^n b^n c^n\) (komplizierter, kontextsensitiv).

Bemerkung: Bei Maschinen ist Nichtdeterminismus meist nur von „akademischem“ Belang, bei Grammatiken ist er jedoch essentiell (eine Satzform kann verschieden abgeleitet werden).
Der einfache Pfeil \(\rightarrow \) wird für Regeln, \(\Rightarrow _G\) wird für Ableitungsschritte verwendet.
\(a^n\) ist eine Kurzform für \(a \dotsb a\) (\(n\)-mal).
Variablen werden mit Groß- und Terminalzeichen werden mit Kleinbuchstaben bezeichnet.

Chomsky-Hierarchie

Chomsky-Hierarchie: 

  • Typ 0: Jede Grammatik ist vom Typ 0.

  • Typ 1: Eine Typ-0-Grammatik ist vom Typ 1 (kontextsensitiv),
    falls für alle Regeln \(w_1 \rightarrow w_2\) in \(P\) gilt, dass \(|w_1| \le |w_2|\).

  • Typ 2: Eine Typ-1-Grammatik ist vom Typ 2 (kontextfrei),
    falls für alle Regeln \(w_1 \rightarrow w_2\) in \(P\) gilt, dass \(w_1 \in V\).

  • Typ 3: Eine Typ-2-Grammatik ist vom Typ 3 (regulär),
    falls für alle Regeln \(w_1 \rightarrow w_2\) in \(P\) gilt, dass \(w_2 \in \Sigma \cup \Sigma V\).

Eine Sprache \(L \subset \Sigma ^\ast \) heißt vom Typ \(i\), falls es eine Typ-\(i\)-Grammatik \(G\) gibt mit \(L(G) = L\) (\(i = 0, \dotsc , 3\)).

Bemerkung: Die Ableitungen einer Typ-3-Grammatik haben die Form \(S \Rightarrow a_1 A_1 \Rightarrow a_1 a_2 A_2\)
\(\Rightarrow \dotsb \Rightarrow a_1 \dotsb a_n A_n\). Weiter geht es mit \(a_1 \dotsb a_n a_{n+1} A_{n+1}\), terminiert wird mit \(a_1 \dotsb a_n a_{n+1}\).
Bei Typ-0-Grammatiken kann die Satzform beim Ableiten länger und wieder kürzer werden, bei Typ-1-Grammatiken ist dagegen die Länge der Satzform monoton steigend, daher heißen Typ-1-Grammatiken auch nicht-verkürzend. Das leere Wort \(\varepsilon \) kann von diesen Grammatiken nie erzeugt werden (für alle \(w_1 \rightarrow w_2\) in \(P\) gilt, dass \(1 \le |w_1| \le |w_2|\)).

\(\varepsilon \)-Sonderregel:  Für Typ-1-Grammatiken kann \(S \rightarrow \varepsilon \) zugelassen werden.
In diesem Fall ist aber \(S\) auf allen rechten Seiten verboten.

Bemerkung: \(S\) ist auf allen rechten Seiten verboten, weil sonst die Definition der Typ-1-Grammatik sinnlos werden würde: Man könnte z. B. eine Regel der Form \(AB \rightarrow CSS\) basteln, die wegen \(S \rightarrow \varepsilon \) als \(AB \rightarrow C\) angewendet werden könnte.
Damit obige Definition sinnvoll ist, sollte man zeigen, dass es zu jeder Typ-\(i\)-Grammatik
\(G = (V, \Sigma , P, S)\) eine Typ-\(i\)-Grammatik \(G’\) mit \(\varepsilon \)-Sonderregel gibt, sodass \(L’ = L \cup \{\varepsilon \}\) mit \(L’ := L(G’)\) und \(L := L(G)\) (\(i = 1, 2, 3\)), d. h. man kann jede Grammatik bei zugelassener \(\varepsilon \)-Sonderregel so verändern, dass ihre Sprache nur um das leere Wort ergänzt wird.

Für Typ-1-Grammatiken verfährt man, indem man \(G’ = (V’, \Sigma , P’, S)\) setzt mit \(V’ = V \cup \{S’\}\) und \(S’ \notin V\). \(P’\) erhält man aus \(P\), indem man in allen Regeln \(S\) durch \(S’\) ersetzt und die Regeln \(S \rightarrow S’\), \(S \rightarrow \varepsilon \) hinzufügt.

Für Typ-2-Grammatiken kann man genauso verfahren, da die hinzugefügten Regeln (insbesondere \(S \rightarrow S’\)) Typ-2-konform sind.

Bei Typ-3-Grammatiken ergibt sich das Problem, dass \(S \rightarrow S’\) keine Typ-3-Regel ist. Man verfährt stattdessen folgendermaßen, um \(P’\) aus \(P\) zu erhalten:

  • ersetze in allen Regeln \(S\) durch \(S’\)

  • für jede Regel mit \(S’\) auf der linken Seite füge dieselbe Regel mit \(S\) auf der linken Seite hinzu

  • füge \(S \rightarrow \varepsilon \) hinzu

Es ergibt sich eine Grammatik \(G’\) gleichen Typs (bis auf \(\varepsilon \)-Sonderregel) mit \(L(G’) = L(G) \cup \{\varepsilon \}\).

Bemerkung: Eine Typ-1-Sprache, die durch eine Typ-1-Grammatik (die keine Typ-3-Grammatik ist) erzeugt wird, kann auch vom Typ 3 sein.

Bemerkung: Die Chomsky-Hierarchie hat ihren Namen von der Anordnung der Sprachenmengen. In der Menge aller Sprachen bilden die Typ-0-Sprachen eine echte Teilmenge, die die entscheidbaren Sprachen wieder als echte Teilmenge beinhalten. Diese enthalten in echter Inklusion die Typ-1-Sprachen (kontextsensitive Sprachen), die in echter Inklusion die Typ-2-Sprachen beinhalten (kontextfreie Sprachen), die in echter Inklusion die Typ-3-Sprachen beinhalten (reguläre Sprachen).

Wortproblem

Wortproblem:  Gegeben seien eine Grammatik \(G = (V, \Sigma , P, S)\) und ein Wort \(w \in \Sigma ^\ast \). Gesucht ist ein Algorithmus, der beliebige \(G\) und \(w\) als Eingabe hat sowie \(1\) ausgibt, falls \(w \in L(G)\), und \(0\), falls \(w \notin L(G)\) (d. h. der Algorithmus implementiert die charakteristische Funktion von \(L(G)\) auf \(\Sigma ^\ast \)). Dieses Problem heißt Wortproblem.

Satz (Wortproblem entscheidbar für Typ-1-Sprachen): Das Wortproblem ist für Sprachen vom Typ 1 entscheidbar, d. h. der gesuchte Algorithmus existiert (falls die Sprache durch eine Typ-1-Grammatik gegeben ist).

Bemerkung: Sprachen vom Typ 2 und Typ 3 sind auch vom Typ 1, d. h. das Wortproblem ist auch hier entscheidbar. Für Typ-0-Sprachen ist das Wortproblem i. A. nicht entscheidbar.

Beweis: Als Beweis wird ein Algorithmus angegeben.
Seien also \(G = (V, \Sigma , P, S)\) eine Typ-1-Grammatik und \(x \in \Sigma ^\ast \) mit \(n := |x| \ge 1\).

\begin{align*} &T := \{S\}\\ &\REPEAT T_1 := T;\; T := \Abl _n(T)\\ &\UNTIL (x \in T) \;\OR (T = T_1);\\ &\IF x \in T \;\THEN \text {output}(1) \;\ELSE \text {output}(0); \end{align*} Für eine Menge \(X \subset (V \cup \Sigma )^\ast \) ist dabei \(\Abl _n(X)\) definiert als
\(\Abl _n(X) := X \cup \{w \in (V \cup \Sigma )^\ast \;|\; (|w| \le n) \land (\exists _{w’ \in X}\; w’ \Rightarrow _G w)\}\).

Der Algorithmus terminiert stets (d. h. er bricht für jede Eingabe nach einer endlichen Anzahl von Schritten ab), denn: Aufgrund \(X \subset \Abl _n(X)\) gilt am Ende jedes Schleifendurchlaufs entweder \(T_1 = \Abl _n(T_1)\) (dann wird terminiert) oder es gilt \(T_1 \subsetneqq \Abl _n(T_1)\).
Der letzte Fall ist jedoch nur endlich oft möglich, da dabei \(|T_1| < |\Abl _n(T_1)|\) gilt, aber \(|\Abl _n(T_1)|\) nach oben durch \(\sum _{i=1}^n t^i\) mit \(t = |V \cup \Sigma |\) beschränkt ist. Somit terminiert der Algorithmus nach endlich vielen Schritten.

Der Algorithmus ist korrekt (d. h. er gibt \(1\) aus genau dann, wenn \(x \in L(G)\)), denn:
Ist \(x \in T\) im \(r\)-ten Schritt (das soll bedeuten, dass \(x \notin T\) für vorherige Schritte gilt), so gibt es ein \(w_1 \in (V \cup \Sigma )^+\) mit \(w_1 \in T\) im \((r - 1)\)-ten Schritt und \(w_1 \Rightarrow _G x\). Daraus folgt, dass es ein \(w_2 \in (V \cup \Sigma )^+\) gibt mit \(w_2 \in T\) im \((r - 2)\)-ten Schritt und \(w_2 \Rightarrow _G w_1\) usw.
Induktiv gibt es also ein \(w_{r-1} \in (V \cup \Sigma )^+\) mit \(w_{r-1} \in T\) im ersten Schritt und \(w_{r-1} \Rightarrow _G w_{r-2}\). Daraus folgt wieder, dass es ein \(w_r \in (V \cup \Sigma )^+\) mit \(w_r \in T\) im nullten Schritt und \(w_r \Rightarrow _G w_{r-1}\). Im nullten Schritt ist allerdings \(T = \{S\}\), d. h. es gilt \(w_r = S\).
Insgesamt gilt also \(S = w_r \Rightarrow _G w_{r-1} \Rightarrow _G \dotsb \Rightarrow _G w_2 \Rightarrow _G w_1 \Rightarrow _G x\), also \(x \in L(G)\). Wenn der Algorithmus \(1\) ausgibt, dann ist daher \(x \in L(G)\). Die andere Richtung überlegt man sich analog, es gilt also \(x \in T\) im \(r\)-ten Schritt genau dann, wenn \(S \Rightarrow _G^\ast x\) in \(r\) Schritten.   ƒ

Syntaxbäume

Syntaxbaum:  Sei eine Typ-2-Grammatik \(G = (V, \Sigma , P, S)\) gegeben. Jeder Ableitung eines Wortes \(x \in L(G)\) kann man einen Syntaxbaum oder Ableitungsbaum zuordnen:
Sei dazu \(S = x_0 \Rightarrow _G x_1 \Rightarrow _G \dotsb \Rightarrow _G x_n = x\) eine Ableitung des Wortes \(x \in L(G)\). Man ordnet der Wurzel des (zu konstruierenden) Syntaxbaums die Startvariable \(S\) zu. Für \(i = 1, \dotsc , n\) führe man folgendes durch: Falls im \(i\)-ten Ableitungsschritt \(x_{i-1} \Rightarrow _G x_i\) die Variable \(A\) mit der Regel \(A \rightarrow z \in P\) durch ein Wort \(z\) ersetzt wird, erstelle im Syntaxbaum \(|z|\) Söhne von \(A\) und beschrifte diese mit den einzelnen Zeichen von \(z\). Auf diese Weise entsteht ein Baum, dess Blätter gerade mit den Zeichen in \(x\) beschriftet sind.

Bemerkung: Syntaxbäume für Typ-3-Grammatiken sind immer entartet, d. h. jeder Knoten hat höchstens zwei Söhne (davon immer ein Terminalzeichen und evtl. eine Variable).
Syntaxbäume für Grammatiken, die nicht vom Typ 2 sind, sind nicht sinnvoll definiert.

Bemerkung: Verschiedenen Ableitungen eines Wortes \(x \in L(G)\) kann derselbe Syntaxbaum zugeordnet sein (beispielsweise indem man die Ableitungsreihenfolge variiert). Ersetzt man immer die erste vorkommende (am weitesten links stehende) Variable, so spricht man von einer Linksableitung. Weil man jedem Syntaxbaum eindeutig eine Linksableitung zuordnen kann, gibt es für jedes Wort \(x \in L(G)\) eine Linksableitung.

Bemerkung: Verschiedenen Syntaxbäumen eines Wortes können verschiedene Bedeutungen zugewiesen werden. Man denke dabei an die Sprache der arithmetischen Ausdrücke, in der es einen Sinn ergeben würde, implizit Klammern um den zuletzt abgeleiteten Term zu setzen. Man würde also den zuletzt abgeleiteten Term zuerst ausrechnen. Solche Interpetationen sind z. B. im Compilerbau sinnvoll. Hier wird dieser Aspekt nicht weiter verfolgt.

eindeutig/mehrdeutig:  Eine Typ-2-Grammatik \(G\) heißt mehrdeutig, falls es ein Wort
\(x \in L(G)\) gibt, dass mindestens zwei Ableitungen besitzt, deren Syntaxbäume verschieden sind. Sonst heißt die Grammatik eindeutig (jedes Wort \(x \in L(G)\) besitzt genau einen Syntaxbaum).

Bemerkung: Es kann sein, dass es für eine Sprache mehrere die Sprache erzeugende Grammatiken gibt, von denen eine mehrdeutig und eine eindeutig ist.

inhärent mehrdeutig:  Eine Typ-2-Sprache \(L\) heißt inhärent mehrdeutig, falls jede Typ-2-Grammatik \(G\) mit \(L(G) = L\) mehrdeutig ist.

Bemerkung: Im Allgemeinen ist es algorithmisch unmöglich festzustellen, ob eine Typ-2-Grammatik mehrdeutig (oder ob eine Typ-2-Sprache inhärent mehrdeutig) ist oder nicht.

Backus-Naur-Form

Backus-Naur-Form (BNF):  Backus und Naur führten einen Formalismus zum kompakten Aufschreiben von Typ-2-Grammatiken ein (Backus-Naur-Form (BNF)).
In Grammatiken \(G\) bedeutet die Regel \(A \rightarrow \beta _1 \;|\; \beta _2 \;|\; \dotsb \;|\; \beta _n\) den Satz von Regeln
\(A \rightarrow \beta _1\), \(A \rightarrow \beta _2\), …, \(A \rightarrow \beta _n\). (Man kann statt \(\rightarrow \) auch \(::=\) schreiben.)

erweiterte Backus-Naur-Form (EBNF):  Verwendet man zusätzliche Notationen, so spricht man von der erweiterten Backus-Naur-Form (EBNF).
In Grammatiken \(G\) steht die Regel \(A \rightarrow \alpha [\beta ] \gamma \) für die Regeln \(A \rightarrow \alpha \gamma \), \(A \rightarrow \alpha \beta \gamma \).
Die Regel \(A \rightarrow \alpha \{\beta \} \gamma \) steht für die Regeln \(A \rightarrow \alpha \gamma \), \(A \rightarrow \alpha B \gamma \), \(B \rightarrow \beta \), \(B \rightarrow \beta B\).

Bemerkung: Da (E)BNF und kontextfreie Grammatiken gleichwertig sind, können durch
(E)BNF genau die kontextfreien Sprachen dargestellt werden.