1 Spezifikation formaler Sprachen 1.1 Formale Sprachen Ein Alphabet ist eine endliche, nicht-leere Menge von atomaren Symbolen, d.h. von Symbolen, die weder eine innere Struktur besitzen noch eine andere Eigenschaften haben als jene, voneinander unterscheidbar zu sein. Als mathematische Variablen für Alphabete verwenden wir Σ oder T . Um eindeutig zu kennzeichnen, dass ein Zeichen als Symbol und nicht als meta-sprachlicher Begriff zu verstehen ist, unterstreichen wir Symbole. 1.1 Beispiel Das Zeichen 0 ist ein Symbol, das wie die Ziffer 0 aussieht, tatsächlich aber von vorneherein keine Bedeutung besitzt (es sei denn, wir ordnen sie ihm explizit zu). Ist Σ ein Alphabet, dann entspricht der Ausdruck 0 ∈ Σ der Aussage Das Symbol 0 ” kommt im Alphabet Σ vor“. Die Aussage 0 ∈ Σ ergibt hingegen nur Sinn, wenn Σ kein Alphabet, sondern etwa eine Menge von Zahlen ist. Analog ist + ein Symbol, das wie ein Kreuz aussieht, während + in einem mathematischen Kontext die Addition, also eine zweistellige Funktion, bezeichnet. 2+3 ist eine Abfolge von 3 Symbolen, die nichts mit 5 zu tun hat, während 2 + 3 äquivalent zu 5 ist. Der Ausdruck 2 + 3 wird üblicherweise wenig Sinn haben, da die Addition nicht auf Zeichen definiert ist. Wichtig ist auch, zwischen Symbolen und mathematischen Variablen zu unterscheiden. Der Ausdruck a ∈ Σ entspricht der Aussage Das Symbol a kommt in Σ vor“, während ” a ∈ Σ der Typdefinition Sei a irgendein Symbol aus Σ“ entspricht. t u ” Ein Wort über einem Alphabet Σ ist eine Folge von Symbolen aus Σ. Das Wort, das aus gar keinen Zeichen besteht, wird Leerwort genannt. Um dieses unsichtbare“ Wort ” niederschreiben zu können, verwenden wir das metasprachliche Zeichen ε. Die Menge aller Wörter über Σ wird mit Σ+ oder, wenn auch das Leerwort ε in der Menge enthalten ist, mit Σ∗ bezeichnet: Σ+ = {s1 · · · sn | si ∈ Σ, 1 ≤ i ≤ n} Σ∗ = Σ+ ∪ {ε} Wörter können durch den Verkettungsoperator verknüpft werden: w1 · w2 = w1 w2 , d.h., das Wort w1 verkettet mit dem Wort w2 ergibt das Wort w1 w2 . Das Leerwort spielt dabei die Rolle eines neutralen Elementes, d.h., mit einem beliebigen Wort verkettet ergibt sich wieder dieses Wort: w · ε = ε · w = w. Algebraisch gesehen bildet hΣ∗ , ·, εi somit ein Monoid (=Halbgruppe mit Einselement), da Σ∗ abgeschlossen bezüglich des assoziativen Verkettungsoperators ist und ε als neutrales Element fungiert. 1 Zusätzlich definieren wir Potenzen von Worten: w0 = ε, wn+1 = w · wn für n ≥ 0 . Die Länge eines Wortes w, geschrieben als |w|, ist die Anzahl der Symbolvorkommnisse in w: |ε| = 0, |aw| = 1 + |w| für a ∈ Σ, w ∈ Σ∗ . 1.2 Beispiel Sei Σ das Alphabet {a, b}. Dann ist Σ∗ die Menge {ε, a, b, aa, ab, ba, bb, aaa, aab, aba, . . .} . Σ+ ist identisch mit Σ∗ bis auf den Umstand, dass Σ+ das Leerwort ε nicht enthält. Beispiele zur Verkettung: ab · ba = abba , ba · ab = baab , ab · ε = ε · ab = ab , (ab)0 = ε , (ab)1 = ab , (ab)2 = abab , (ab)3 = ababab Die ersten beiden Beispiele zeigen, dass die Verkettung nicht kommutativ ist. t u Unter einer formalen Sprache L über einem Alphabet Σ versteht man eine beliebige Menge von Wörtern über Σ, d.h., L ⊆ Σ∗ . Die Menge aller Sprachen ist die Menge aller Teilmengen von Σ∗ , also die Potenzmenge P(Σ∗ ) von Σ∗ . 1.3 Beispiel Beispiele für Sprachen: – Über den Buchstaben A–Z inklusive Umlauten und Satzzeichen: die Menge aller grammatikalisch richtigen deutschen Sätze. – Über dem Ascii-Zeichensatz: die Menge aller C-Programme. – Über einem beliebigen Alphabet: die Menge aller Palindrome (=Wörter, die von vorne nach hinten und hinten nach vorne gelesen gleich lauten, wie omo, anna, otto, reliefpfeiler, . . . ). Längere, aber nicht unbedingt sinnvollere Palindrome sind Vitaler Nebel mit Sinn ist im Leben relativ“ und Satan, oscillate my ” ” metallic sonatas“. – Über einem beliebigen Alphabet: die leere Menge; die Menge aller Wörter; die Menge aller Wörter der Länge 4. t u Der Verkettungsoperator kann von Wörtern auf Sprachen erweitert werden. Seien L1 und L2 zwei Sprachen über Σ, d.h. L1 , L2 ⊆ Σ∗ bzw. L1 , L2 ∈ P(Σ∗ ). Dann definieren wir die Verkettung von Sprachen als L1 · L2 = {w1 · w2 | w1 ∈ L1 , w2 ∈ L2 } . Bei dieser Operation übernimmt {ε} die Funktion des Einheitselementes. Somit bildet hP(Σ∗ ), ·, {ε}i wieder ein Monoid. 2 Rechengesetz A∪B = B∪A A ∪ (B ∪ C) = (A ∪ B) ∪ C A · (B · C) = (A · B) · C A · (B ∪ C) = A · B ∪ A · C (A ∪ B) · C = A · C ∪ B · C {ε} · A = A A · {ε} = A (A ∪ {ε})∗ = A∗ (A∗ )∗ = A∗ A · A∗ = A+ A∗ · A = A+ A+ ∪ {ε} = A∗ Kommentar Vereinigung ist kommutativ. Vereinigung ist assoziativ. Verkettung ist assoziativ. Verkettung distribuiert über Vereinigung {ε} ist das Einheitselement bzgl. Verkettung ∗ ist idempotent Tabelle 1.1: Algebraische Eigenschaften der Sprachoperatoren Auch die Potenzenbildung lässt sich auf Sprachen erweitern. Ist L eine Sprache, dann sind ihre Potenzen definiert durch L0 = {ε}, Ln+1 = L · Ln für n ≥ 0 . S Vereinigt man alle Potenzen, erhält man den Stern-Operator1 L∗ = n≥0 Ln , und analog S den Plus-Operator L+ = n≥1 Ln . Ist L ein Alphabet, d.h., bestehen alle Wörter in L aus nur einem Zeichen, dann ist Ln genau die Menge aller Wörter der Länge n, die mit den Symbolen in L gebildet werden können. L∗ ist dann die Vereinigung der Wörter aller Längen, somit die Menge aller Wörter über dem Alphabet L. Wegen L0 = {ε} enthält L∗ auch das Leerwort. Die Vereinigung in der Definition von L+ hingegen beginnt erst bei n = 1, somit ist das Leerwort nur dann in L+ , falls es bereits in L war. Tabelle 1.1 gibt einen Überblick über wichtige Rechenregeln für Sprachoperatoren. 1.4 Beispiel Sei Σ = {a, b}. Wir erhalten Σ0 = {ε}, Σ1 = Σ, Σ2 = {aa, ab, ba, bb} und Σ3 = {aaa, aab, aba, abb, baa, bab, . . .}. Die Menge Σ∗ ist die Vereinigung all dieser Mengen, somit die Menge aller Wörter über den Symbolen a und b. t u 1.2 Induktive Definitionen Die in der Informatik auftretenden Mengen enthalten zum Teil Elemente komplizierterer Bauart; man denke etwa an die Menge aller syntaktisch korrekten C-Programme. Eine übliche mathematische Methode zur Definition unendlicher Mengen ist die stufenweise Konstruktion: unter Verwendung einer gegebenen Grundmenge A0 sowie der 1 Nach seinem Erfinder auch Kleene-Stern genannt, gesprochen [’kli:ni]. 3 bisher konstruierten Mengen A1 , . . . , Ak baut man nach bestimmten Vorschriften die nächste Menge Ak+1 auf, die üblicherweise komplizierter als die Vorgängermengen ist. Die dadurch insgesamt definierte Menge ist dann die Vereinigung all dieser Mengen: S A = i∈ω Ai . 1.5 Beispiel Sei A0 die Menge der Ziffern, also A0 = {0, . . ., 9}. Additive Ausdrücke über dieser Menge lassen sich stufenweise konstruieren durch: A1 = A0 ∪ {u+v | u, v ∈ A0 } = {0, . . ., 9, 0+0, 0+1, . . ., 9+9} A2 = A1 ∪ {u+v | u, v ∈ A1 } = {0, . . ., 0+0, . . ., 0+0+0, 0+0+1, . . ., 0+0+0+0, . . ., 9+9+9+9} .. . Die Menge aller additiven Ausdrücke ergibt sich als S i≥0 Ai . t u Stufenweise konstruierte Mengen sind in folgendem Sinn abgeschlossen bezüglich der Konstruktionsvorschriften. 1.6 Definition Sei B eine Menge und f : B n → B eine Funktion. Eine Menge A ⊆ B heißt abgeschlossen unter f , wenn gilt: aus x1 , . . . , xn ∈ A folgt f (x1 , . . . , xn ) ∈ A. 1.7 Satz Sei B eine Menge, A0 ⊆ B und f : B n → B. Weiters sei Ak+1 = Ak ∪ {f (x1 , . . . , xn ) | x1 , . . . , xn ∈ Ak } und A = S k≥0 Ak . Dann gilt: (a) A ist abgeschlossen unter f . (b) Ist A0 irgendeine Teilmenge von B, die A0 enthält und abgeschlossen ist unter f , dann ist A Teilmenge von A0 (A ⊆ A0 ). Beweis (a) Laut Definition der Abgeschlossenheit müssen wir zeigen, dass f (x1 , . . . , xn ) für beliebige x1 , . . . , xn ∈ A in A liegt. Auf Grund der Konstruktion der Mengen Ai gilt Ai ⊆ Ai+1 für alle i ≥ 0. Daher muss es ein k geben, sodass x1 , . . . , xn allesamt in Ak liegen. Daraus folgt aber f (x1 , . . . , xn ) ∈ Ak+1 . Die Menge A ist die Vereinigung aller Ak , enthält also auch die Elemente von Ak+1 , insbesondere f (x1 , . . . , xn ). (b) Um zu zeigen, dass A Teilmenge von A0 ist, beweisen wir mittels vollständiger Induktion, dass Ak Teilmenge von A0 ist für alle k ≥ 0. Offenbar gilt dann auch A ⊆ A0 , da A die Vereinigung aller Ak ist. Induktionsanfang: A0 ist laut Voraussetzung des Satzes in A0 . Induktionshypothese: Ak ist Teilmenge von A0 . 4 Induktionsschritt: Wir müssen zeigen, dass auch Ak+1 ⊆ A0 gilt. Laut Definition gilt Ak+1 = Ak ∪ {f (x1 , . . . , xn ) | x1 , . . . , xn ∈ Ak }. Wegen der Induktionshypothese können wir Ak ⊆ A0 annehmen, es bleibt zu beweisen, dass {f (x1 , . . . , xn ) | x1 , . . . , xn ∈ Ak } ⊆ A0 gilt. Dies folgt aber aus der Induktionshypothese und der Abgeschlossenheit von A0 bezüglich f : da x1 , . . . , xn in Ak und damit in A0 liegen, muss auch f (x1 , . . . , xn ) in A0 liegen. t u In anderen Worten: A ist die kleinste Menge, die A0 enthält und abgeschlossen ist unter f . Um A kompakt und eindeutig zu definieren, kann daher folgendes Schema verwendet werden. Schema der induktiven Definition A ist die kleinste Menge, für die gilt: (a) A0 ⊆ A (b) Wenn x1 , . . . , xn ∈ A, dann f (x1 , . . . , xn ) ∈ A. Induktive Definitionen bestehen somit aus drei wesentlichen Komponenten: einer Grundmenge, einer Abschlusseigenschaft und einer Minimalitätsbedingung ( . . . ist kleinste ” Menge, für die gilt . . .“). 1.8 Beispiel Die in Beispiel 1.5 stufenweise konstruierte Menge lässt sich induktiv definieren als die kleinste Menge A, für die gilt: (a) {0, 1, . . ., 9} ⊆ A (b) Wenn x, y ∈ A, dann auch x+y ∈ A. t u 1.9 Beispiel Die geraden Zahlen können induktiv definiert werden als die kleinste Menge, für die gilt: (a) Grundelement: 0 ist eine gerade Zahl. (b) Abschlusseigenschaft: Ist n eine gerade Zahl, dann ist auch n + 2 eine solche. Jede der drei Bedingungen ist notwendig, um die geraden Zahlen eindeutig festzulegen. Ohne der Minimalitätsbedingung etwa käme auch die Menge aller natürlichen Zahlen in Frage, da 0 eine natürliche Zahl ist und die natürlichen Zahlen abgeschlossen unter der Funktion +2“ sind. Allerdings ist sie nicht die kleinste Menge mit diesen Eigenschaften, ” da die geraden Zahlen eine echte Teilmenge bilden. Die erste Bedingung hingegen scheidet die ungeraden Zahlen als Kandidat aus. Die induktive Definition kann gemäß Satz 1.7 auch benützt werden, um die geraden Zahlen stufenweise zu konstruieren: A0 = {0} A1 = A0 ∪ {n + 2 | n ∈ A0 } = {0} ∪ {2} = {0, 2} A2 = A1 ∪ {n + 2 | n ∈ A1 } = {0, 2} ∪ {2, 4} = {0, 2, 4} A3 = A2 ∪ {n + 2 | n ∈ A2 } = {0, 2, 4} ∪ {2, 4, 6} = {0, 2, 4, 6} .. . 5 t u 1.3 Reguläre Sprachen Eine der einfachsten Sprachklassen ist die Menge der regulären Sprachen. Reguläre Sprachen können auf mehrere gleichwertige Arten definiert werden: als reguläre Mengen, als die von endlichen Automaten akzeptierte Sprachfamilie, als die Menge der von regulären Grammatiken erzeugten Sprachen usw. Wegen ihrer Einfachheit und der damit verbundenen guten Eigenschaften treten reguläre Sprachen in der Informatik häufig auf. In der ersten Phase eines Compilerlaufes, der sogenannten lexikalischen Analyse, wird das Benutzerprogramm in eine Folge von Token umgewandelt; der entsprechende Programmteil des Compilers wird als Scanner oder Lexer bezeichnet. Der Lexer fasst alle Zeichen, die zu einem Schlüsselwort, einem Bezeichner, einem Operator oder einer Zahl gehören, zusammen und wandelt sie in eine interne Darstellung um. Die Menge aller Wörter, die jeweils ein Token bilden – alle Schlüsselwörter, Bezeichner, Zahlendarstellungen – bilden eine reguläre Sprache. Diese reguläre Sprache lässt sich mittels regulärer Ausdrücke (siehe unten) beschreiben. Es gibt Programme, sogenannte Scannergeneratoren, die aus dieser Beschreibung automatisch einen passenden Scanner erzeugen können, der dann als Teil eines Compilers genau diese reguläre Sprache in Token umwandelt; bekannte Scannergeneratoren unter Unix sind etwa lex und flex. Ein anderes Anwendungsgebiet sind Texteditoren. Fortgeschrittene Editoren erlauben nicht nur das Suchen fester Zeichenketten in einem Text, sondern ermöglichen die Angabe von Mustern, mit denen alle Zeichenketten gesucht werden können, die in der durch das Muster spezifizierten Sprache liegen. Beispiele für solche Editoren sind vi und emacs unter Unix oder auch der Norton Desktop-Editor. Etwa kann man im vi mit dem Muster [0-9][0-9]* alle ganzen Zahlen im Text auffinden. Weiters gibt es zahlreiche Programme, insbesondere unter Unix, die die Spezifikation von (Teilmengen von) regulären Sprachen zulassen. Dies beginnt bei Kommandointerpretern wie Dos oder Unix-Shells, die bei der Selektion von Dateien Ausdrücke mit Wildcards“ zulassen, und geht bis zu Programmen wie egrep oder awk, die beliebige ” reguläre Ausdrücke akzeptieren. Darüber hinaus bilden reguläre Ausdrücke ein wesentliches Element von Programmiersprachen wie Perl, die zur Analyse und Erzeugung von Textdateien (etwa von Html-Seiten) eingesetzt werden. Sucht man auf dem Server des World Wide Web Consortiums (W3C) nach den Spezifikationen der Websprachen, stößt man auf einen Formalismus, der ausgiebig von regulären Ausdrücken Gebrauch macht. In der Xml-Spezifikation treten reguläre Ausdrücke sogar auf zwei Ebenen auf: einmal objektsprachlich als Teil von Document Type Definitions (Dtds), und einmal metasprachlich zur Beschreibung der Syntax von Xml (und damit auch jener von Dtds). In den folgenden Abschnitten definieren wir die regulären Sprachen als reguläre Mengen und beschreiben verschiedene Formalismen und Notationen zur Festlegung regulärer Mengen. 6 1.3.1 Reguläre Mengen Reguläre Mengen sind nichts anderes als alle Sprachen, die aus einem Alphabet mittels der in Abschnitt 1.1 definierten Operationen Vereinigung, Verkettung und Kleene-Stern gebildet werden können. 1.10 Definition Die Menge Lreg (Σ) der regulären Sprachen über Σ ist die kleinste Menge, für die gilt: (a) {}, {ε} ∈ Lreg (Σ); {s} ∈ Lreg (Σ) für alle s ∈ Σ; (b) Wenn A, B ∈ Lreg (Σ), dann gilt auch A ∪ B, A · B, A∗ ∈ Lreg (Σ), d.h., Lreg (Σ) ist abgeschlossen gegenüber Vereinigung, Verkettung und Kleene-Stern. Offensichtlich ist Lreg auch gegenüber dem X + kann ja durch X · X ∗ ersetzt werden. + -Operator abgeschlossen: jeder Ausdruck 1.11 Beispiel Die Menge der Real-Zahlen in Modula ist eine reguläre Menge über dem Alphabet {0, . . ., 9, ., E, +, -}: – Laut erstem Punkt von Definition 1.10 sind die Mengen {ε}, {0}, . . . , {9}, {.}, {E}, {+} und {-} regulär. – Da die Vereinigung regulärer Mengen wieder regulär ist, ist auch digit = {0, . . ., 9} regulär. – Die Menge {E, E+, E-} ist regulär, da sie mittels Verkettung und Vereinigung aufgebaut werden kann: {E} ∪ {E} · {+} ∪ {E} · {-}. – Aus der Abgeschlossenheit bzgl. Plus- und Sternoperator folgt, dass auch digit + und digit ∗ regulär sind. Daraus lässt sich nun die Menge der Real-Zahlen zusammensetzen: real = digit + · {.} · digit ∗ · ({ε} ∪ {E, E+, E-} · digit + ) . Eine Realzahl beginnt demnach mit einer Folge von Ziffern (mindestens einer), gefolgt von einem Punkt und weiteren optionalen Ziffern. Danach kann ein Exponentialteil folgen, der aus dem Symbol E, einem optionalen Vorzeichen und mindestens einer Ziffer bestehen muss. t u 1.12 Beispiel Wir konstruieren einige der regulären Sprachen in Lreg ({a}) gemäß Satz 1.7. Im ersten Schritt erhalten wir alle Sprachen, die laut Punkt 1 der induktiven Definition von Lreg reguläre Mengen sind: A0 = {{}, {ε}, {a}} . 7 Im nächsten Schritt kommen alle Sprachen hinzu, die aus A0 mittels Vereinigung, Verkettung und Stern-Operator gebildet werden können: A1 = A0 ∪ {{ε, a}, {aa}, {an | n ≥ 0}} . Durch Vereinigung von Elementen aus A1 ergeben sich drei neue Mengen: {ε, aa}, {a, aa} und {ε, a, aa}. Durch Verkettung erhalten wir sechs neue Mengen, darunter etwa {ε, a} · {aa} = {aa, aaa} und {aa} · {an | n ≥ 0} = {an | n ≥ 2}. Der Stern-Operator liefert nur auf {aa} angewendet eine neue Sprache: {aa}∗ = {a2n | n ≥ 0}. Insgesamt ergibt sich: A2 = A1 ∪ { {ε, aa}, {a, aa}, {ε, a, aa}, {aa, a3 }, {a3 }, {a4 }, {an | n ≥ 1}, {an | n ≥ 2}, {a2n | n ≥ 0} } . In A3 alleine durch die Vereinigung 19 neue Sprachen hinzu. t u 1.3.2 Reguläre Ausdrücke Um reguläre Sprachen spezifizieren zu können, muss eine geeignete Notation festgelegt werden. Abhängig von den historischen Wurzeln und den Anforderungen – wie der Verständlichkeit für Nicht-Formalisten, der Ausdrückbarkeit im Ascii-Zeichensatz oder der einfachen algebraischen Handhabbarkeit – wurden verschiedene Varianten eingeführt, die sich direkt ineinander übersetzen lassen. Tabelle 1.2 gibt eine Übersicht, wobei A für die Bezeichnung einer Untersprache, s für ein Symbol und X bzw. Y für einen Teilausdruck im jeweiligen Formalismus steht. Algebraische Notation Aufgrund der Monoid-Eigenschaften der Sprachenverkettung orientiert sich die algebraische Notation an der Addition und verwendet + für die Mengenvereinigung und ∅ für das neutrale Elemente {}. Die Elementarsprachen {ε} und {s} (für s ∈ Σ) werden ohne Mengenklammern geschrieben, und Verkettung wird durch Nebeneinanderschreiben ausgedrückt. Um Klammern zu sparen, ordnet man ∗ die höchste und der Vereinigung die niedrigste Priorität zu. Formal lässt sich die Sprache L(e), die durch einen regulären Ausdruck e in algebraischer Notation spezifiziert wird, definieren als: L(0) = {} L(ε) = {ε} L(s) = {s} L(e1 e2 ) = L(e1 ) · L(e2 ) L(e1 + e2 ) = L(e1 ) ∪ L(e2 ) L(e∗ ) = L(e)∗ wobei s ∈ Σ ein Symbol und e, e1 , e2 reguläre Ausdrücke sind. 1.13 Beispiel Die Realzahlen aus Beispiel 1.11 lassen sich darstellen als digit digit ∗ . digit ∗ (ε + (E + E + + E -) digit digit ∗ ) wobei digit eine Abkürzung für den Ausdruck (0 + 1 + · · · + 9) ist. 8 t u reg. Menge Algebra Ebnf Syntaxdiagramm Kommentar A A A A Untersprache {} 0 {ε} ε {s} s "s" X ·Y XY XY Leersprache X ∪Y X +Y X |Y {ε} ∪ X ε+X [X ] X∗ X∗ {X } X+ XX ∗ X {X } (X) (X) (X ) s -X -Y - Leerwort-Sprache Terminalsymbole Aufeinanderfolge -X -Y Alternativen -X X Option Wiederholung ≥ 0 -X Wiederholung ≥ 1 Gruppierung Tabelle 1.2: Verschiedene Notationen für reguläre Ausdrücke EBNF Die Syntax der Programmiersprache Modula-2 im Anhang des Buches Programming in Modula-2 von N. Wirth wird durch Regeln in erweiterter Backus-Naur Form (EBNF) beschrieben. Eine EBNF ist nichts anderes als eine kontextfreie Grammatik (siehe später in der Vorlesung) erweitert um reguläre Ausdrücke. 1.14 Beispiel In dem erwähnten Buch wird die Syntax der Realzahlen spezifiziert durch real = digit {digit} "." {digit} [ScaleFactor] ScaleFactor = "E" ["+"|"-"] digit {digit} digit = "0"|"1"|"2"|"3"|"4"|"5"|"6"|"7"|"8"|"9" Diese Regeln lassen sich gemäß Tabelle 1.2 in reguläre Mengen übersetzen: real ScaleFactor digit = digit · digit ∗ · {.} · digit ∗ · ({ε} ∪ ScaleFactor ) = {E} · ({ε} ∪ {+} ∪ {-}) · digit · digit ∗ = {0} ∪ {1} ∪ · · · ∪ {9} Vereinfacht man die rechten Seiten, erhält man den gleichen Ausdruck für real wie in Beispiel 1.11. u t 9 real - digit digit - . - ScaleFactor ScaleFactor - + - E - digit - digit - 0 - 1 .. . .. . .. . - 9 Abbildung 1.3: Syntaxdiagramme für die Realzahlen in Modula Syntaxdiagramme Zur Beschreibung der Syntax von Pascal und Modula führte N. Wirth Syntaxdiagramme ein, die nichts anderes als eine graphische Darstellung regulärer Ausdrücke in EBNF sind. Die zu einem Syntaxdiagramm gehörende Sprache erhält man, indem man den Graphen in Richtung der Pfeile durchläuft und alle angetroffen Terminalsymbole aneinander reiht. Ein Rechteck mit dem Namen eines anderen Syntaxdiagrammes entspricht dabei einem Unterdiagrammaufruf“: an Stelle des Rechtecks wird das angegebene Syntaxdia” gramm durchlaufen und anschließend im ursprünglichen Diagramm nach dem Rechteck fortgesetzt. 1.15 Beispiel Die Syntaxdiagramme in Abb. 1.3 beschreiben (wieder einmal) die Syntax der Realzahlen in Modula. t u Reguläre Ausdrücke unter Unix Viele Standardprogramme in Unix erlauben als Eingabe reguläre Ausdrücke. Beispiele dafür sind Editoren wie emacs, vi, ex und sed, Kommandos wie awk oder egrep, und Scannergeneratoren wie lex. Die Schreibweise der regulären Ausdrücke ist bei all diesen Kommandos ähnlich (leider aber nicht identisch2 ). Stellvertretend betrachten wir die regulären Ausdrücke, wie sie von egrep akzeptiert werden. Das Kommando egrep ’hreg. Ausdruck i’ hTextdatei i sucht aus hTextdatei i alle Zeilen heraus, die eine Zeichenkette enthalten, welche in der durch hreg. Ausdruck i definierten regulären Sprache liegt. Tabelle 1.4 fasst die wichtigsten Ausdruckselemente zusammen. 2 Der Mathematiker und Informatiker D.E. Knuth – auch bekannt als Schöpfer des Textsystems TEX, das zum Setzen dieses Skriptums verwendet wurde – definiert Unix als 30 definitions of regular ” expressions living under one roof“. 10 Ausdruck s \s . ^ selektiert Einzelzeichen s, falls kein Spezialsymbol Einzelzeichen s (auch wenn Spezialsymbol) jedes beliebige Zeichen außer Zeilenende Zeilenanfang $ [s1 · · · sn ] Zeilenende alle Zeichen in {s1 , . . ., sn } [^s1 · · · sn ] alle Zeichen, die nicht in {s1 , . . ., sn } vorkommen null oder mehr Vorkommnisse von r r* r+ ein oder mehr Vorkommnisse von r r? ein oder kein Vorkommnis von r r{i} i Vorkommnisse von r r{i,} r{i,j} r1 r2 i oder mehr Vorkommnisse von r i bis j Vorkommnisse von r r1 gefolgt von r2 r1 |r2 r1 oder r2 (r) r Beispiel a selektiert alle Zeilen, die a enthalten a\.b selektiert alle Zeilen mit der Zeichenfolge a.b a.b selektiert alle Zeilen, die entweder aab oder abb oder acb oder . . . enthalten ^a selektiert alle Zeilen, die mit a beginnen a$ selektiert alle Zeilen, die mit a enden [ab] selektiert alle Zeilen, die a oder b enthalten [^ab] selektiert alle Zeilen, die nicht nur aus a’s und b’s bestehen a.*a selektiert alle Zeilen, die zwei durch beliebig viele (auch null) Zeichen getrennte a’s enthalten a.+a selektiert alle Zeilen, die zwei durch beliebig viele (aber mind. ein) Zeichen getrennte a’s enthalten a.?a selektiert alle Zeilen, die zwei durch null oder ein Zeichen getrennte a’s enthalten a.{4}a selektiert alle Zeilen, die zwei durch genau vier Zeichen getrennte a’s enthalten a{0,} ist gleichbedeutend mit a* und a{1,} mit a+ a{0,1} ist gleichbedeutend mit a? ab selektiert alle Zeilen, die ein a unmittelbar gefolgt von b enthalten a|b selektiert alle Zeilen, die a oder b enthalten (a|b) ist gleichbedeutend mit [ab] Tabelle 1.4: Reguläre Ausdrücke des Kommandos egrep 11 1.16 Beispiel Um alle Zeilen zu erhalten, die genau eine Realzahl in Modula-Syntax (und sonst nichts) enthalten, muss egrep mit dem regulären Ausdruck ^[0-9]+\.[0-9]*(E[+-]?[0-9]+)?$ t u aufgerufen werden. Reguläre Definitionen Hinter dem Begriff der regulären Definition verbirgt sich nichts anderes als die Verwendung von Abkürzungen für reguläre Teilausdrücke. In den vorangegangenen Beispielen wurde bereits stillschweigend davon Gebrauch gemacht. So verwenden die meisten regulären Ausdrücke für die Realzahlen (inklusive der Syntaxdiagramme) neben real auch noch ScaleFactor und digit. Reguläre Definitionen erhöhen nicht die Ausdruckskraft regulärer Ausdrücke: sie können ja durch Ersetzung der linken durch die rechten Seiten jederzeit eliminiert werden. Ihr Nutzen liegt in der besseren Strukturierung und damit der besseren Lesbarkeit der regulären Ausdrücke. Die einzige Einschränkung bei der Verwendung regulärer Definitionen besteht darin, dass keine (direkten oder indirekten) Rekursionen entstehen dürfen. eine Definition wie digits = digit · digits ∪ {ε} ist nicht zulässig, da digits in seiner eigenen Definition (der rechten Seite Der Gleichung) vorkommt. 12