regulären Ausdrücken

Werbung
Kapitel 7
Sprachen
 Wir haben Information als grundlegenden Begriff der Informatik
eingeführt und über Codes und Datenstruktur abstrahiert.
Andererseits haben wir den Umgang mit Information, zunächst am
einfachen Beispiel (Rechnen mit Zahlen in beliebigen Zahlensystemen),
dann abstrakter und mächtiger mit Hilfe von Algorithmen kennengelernt.
Dieses Kapitel führt nun in die wesentliche Anwendung dieser
grundlegenden Überlegungen ein. Es beschreibt die fundamentalen
theoretischen Grundlagen von Sprachen im Allgemeinen und
Programmiersprachen im Besonderen.
 Inhalt
1.
2.
3.
4.
5.
Programmiersprachen
Syntax und Semantik
Reguläre Ausdrücke
Endliche Automaten
Grammatiken
7.1
Programmiersprache
 Programmiersprachen sind das Mittel der Informatik, Algorithmen
problemorientiert zu formulieren.
Die wichtigen Begriffe „Programmiersprache“ und „Programm“ sind
dabei - zumindest was den zweiten betrifft - nicht ganz klar und sollen
zunächst kurz eingeführt werden. Danach soll ein sehr kurzer Überblick
über die Entwicklung von Programmiersprachen gegeben werden, der
mit einem Resumée abschließt.
 Inhalt
1.
2.
3.
4.
Was ist eine Programmiersprache
Was ist ein Programm
Generationen von Programmiersprachen
Programmiersprachen heute
7.1.1 Was ist eine Programmiersprache
 Eine Programmiersprache ist eine künstliche Sprache zur präzisen
Formulierung von Algorithmen.
Sie ist:
 vollständig (zumindest in einigen Aspekten)
 eindeutig
 maschinell verarbeitbar
 Die Programmiersprache spielt eine Mittlerrolle zwischen Mensch und
Maschine
 Beschreibung soll für den Menschen generierbar und verständlich sein
 Beschreibung soll für die Maschine verständlich und exakt, wiederholbar
ausführbar sein.
 Programmiersprachen sind abhängig von
 Anwendungsgebiet
 Art der Rechnersysteme
 Mentalität der Programmierer
7.1.2 Was ist ein Programm
 Ein Programm durchläuft einige Repräsentationsformen
 Quelltext
 Der Quelltext ist die dem Menschen geläufigste Repräsentationsform eines
Programms.
 Der Quelltext wird meist vom Menschen mit einem Editor erstellt und
formalisiert einen Algorithmus
Teilweise wird Quelltext auch von anderen Programmen generiert
 Der Quelltext eines Programms befindet sich oft in genau einer Datei,
in größeren Programmen meist in mehreren Dateien
 Objektmodul
 Wird ein Programm z.B. mit Hilfe eines Compilers übersetzt, so ist es bereits in
binärer Repräsentation, allerdings so noch nicht lauffähig auf einem Prozessor
 Ausführbares Programm
 Das Programm ist binär repräsentiert und mit allen Teilen „zusammengebunden“, die notwendig für die Ausführung auf einem Prozessor sind.
 Prozess
 Ein Prozess ist ein Programm, welches gerade auf dem Prozessor (auf den
Prozessoren) ausgeführt wird.
7.1.3 Generationen von Programmiersprachen
1. Generation: Maschinensprachen
2. Generation: Assemblersprachen
Wie ?
Speicherstelle
3. Generation: Prozedurale Sprachen
Relation
4. Generation: Datenbankabfragesprachen
Objekt
5. Generation: Deklarative Sprachen
Was ?
7.1.3 Generation: 1. Maschinensprachen
 Prozessorspezifische Sprache, meist in Binärform :
0100 0111 0110 0110
0011 0111 0111 0000
1100 1000 0010 1000
0111 0100 0000 0000
...
 Direkte Ausführung durch Prozessor möglich
 Extrem aufwendige Programmierung
 Einsatz heute nur noch
 in (manchen) Mikroprozessorsteuerungen
 im ROM-BIOS, Bootblock
 zur Wartung alter Steuergeräte, wenn deren Einsatz bzgl. des
Programmieraufwandes noch lohnend ist
7.1.3 Generation: 2. Assemblersprachen
 Verwendung mnemonischer Bezeichner, strukturäquivalent zu
Binärcode (seit ca. 1950)
00401571
00401579
00401584
00401586
00401588
0040158E
00401592
0040159B
0040159D
0040159F
004015A7
...
mov
test
jne
push
call
pop
ret
mov
push
call
ret
ecx,dword ptr [esp+4]
ecx,ecx
0040158E
0FDh
dword ptr ds:[4010B8h]
ecx
8
esi,ecx
0
0040156C
 Übersetzung des Codes durch Assembler
 Einsatz heute noch bei systemnaher Programmierung
7.1.3 Generation: 3. Prozedurale Sprachen
 Prozedurale Sprachen setzen Algorithmen und Datenstrukturen, wie sie
in den vorangegangenen Kapiteln beschrieben wurden sehr natürlich
um.
 Rechnerunabhängige Sprachen in einer meist anwendungsabhängigen
Schreibweise (Syntax) und Bedeutung (Semantik).
Fortran
Cobol
Algol
PL/1
Basic
Pascal
Chill
Pearl
C
Ada
Modula
C++
1954
1957
1957
1960
1963
1968
1968
1970
1970
1975
1975
1980
wissenschaftlich/technisch
kommerziell
wissenschaftlich/technisch
universell
einfach wissenschaftlich/technisch
universell, Lehre
Systemprogrammierung (T-Kom)
Regelung
universell, Systemprogrammierung
universell, Militär
Lehre
universell, Systemimplementierung
7.1.3 Generation: 4. Datenbankabfragesprachen
 Einfacher Umgang mit Datenbanken (4GL, seit 1965):
 Definition des Datenbank-Layouts
 Operationen auf Datenbanken
 Generierung von graphischen Benutzeroberflächen
 Beispiele:
 SQL (Standard Query Language)
 NATURAL
 Heute oft
 in Kombination mit prozeduralen Sprachen
 unterstützt durch graphische Sprachelemente
 Extreme Beschränkungen durch Fokus auf Datenbanken, daher nicht
(mehr) weit verbreitet.
7.1.3 Generation: 5. Deklarative Sprachen
 Beschreibung des Problems in einem (meist) mathematischen
Formalismus.
Lösung, basierend auf der Beschreibung, durch das System.
 Logische Sprachen, z.B. Prolog
 funktionale Sprachen, z.B. ML
 logisch-funktionale Sprachen
 (Zur Zeit) Keine wesentlichen Einsatzgebiete
7.1.4 Programmiersprachen heute
 Seit den Zeiten der Maschinensprachen hat eine Entwicklung zu mehr
Abstraktion stattgefunden
 Abstraktion des Algorithmus von einfachsten Maschinenbefehlen
(bestehend aus Zuweisungen, Vergleichen und Sprüngen) über
prozedurale Sprachen (mit ihren Mitteln der strukturierten Programmierung)
bis hin zu deklarativen Sprachen (die Probleme vollständig beschreiben,
deren Lösung dann generisch stattfinden kann)
 Abstraktion der Datenstrukturen von der „blanken“ Speicherstelle und
Register bis hin zu relationalen Strukturen (4GL-Sprachen) und
objektorientierten Strukturen (mit Vererbungsbeziehungen und
Datenkapselung)
 Stand heute:
 Tatsächlich sind die Sprachen der 3. Generation, insbesondere die mit
objektorientierter Erweiterung, die zur Zeit wesentlichen.
Grund für das „Scheitern“ der 4GL-Sprachen ist der eingeschränkte Fokus
auf relationale Strukturen - Grund des „Scheiterns“ deklarativer Sprachen
ist die unangepasste Abstraktionsfähigkeit der Datenstrukturen und
insbesondere der Aufwand generischer Algorithmen.
7.2
Syntax und Semantik
 Im vorangegangenen Unterkapitel haben wir Programmiersprachen als
präzise, vollständig und eindeutig beschrieben. Diese Beschreibung trifft
tatsächlich aber nicht auf alle Aspekte der Sprache zu.
In diesem Unterkapitel sollen nun die Aspekte einer Programmiersprache beschrieben werden, sowie die Möglichkeiten, diese Aspekte
zu beschreiben.
 Inhalt
1. Syntax vs. Semantik
2. Syntax
3. Semantik
7.2.1 Syntax vs. Semantik
 Bei der Beschreibung einer Programmiersprache unterscheidet man
zwischen Syntax und Semantik der Sprache:
 Unter der Syntax einer Programmiersprache bezeichnet man den formalen
Aufbau, also die Struktur, einer Sprache
 Unter der Semantik einer Programmiersprache bezeichnet man, die
Bedeutung der Konstrukte einer Programmiersprache
 Beispiel:
 Syntax für die Zuweisung: linke_seite = rechte_seite;
 Semantik der Zuweisung: Der Wert der rechten Seite wird bestimmt
und wird an die Speicherstelle abgelegt, die
durch die linke Seite bezeichnet ist.
 Beachte
 Die Syntax kann (meist) vollständig formal angegeben werden
 Die Semantik wird meist nur informell beschrieben
 Fehler, die auf fehlerhaftes Verständnis der Semantik beruhen sind oft
schwer zu finden.
7.2.2 Syntax
 Wie wir gesehen haben, beschreibt die Syntax einer Programmiersprache deren Struktur.
Dabei ist lässt sich die Struktur von Programmiersprachen noch in zwei
Abstraktionsebenen einteilen:
 Lexikalische Struktur:
Hierunter versteht man die Struktur der „atomaren“ Elemente oder
Grundsymbole der Programmiersprache
 Syntaktische Struktur:
Hierunter versteht man die Zusammensetzung von Grundsymbolen der
Sprache zu komplexeren Strukturen
lexikalische Struktur
syntaktische Struktur
 Notation:
Die Syntax einer Sprache lässt sich (meist) formal beschreiben. Dazu
gibt es verschiedene Beschreibungsmittel bzw. Notationen.
7.2.2 Syntax: Lexikalische Struktur
 Die lexikalische Struktur: beschreibt also den Aufbau der Grundsymbole
aus einzelnen Zeichen
 Diese Zeichen sind Buchstaben, Ziffern und Sonderzeichen
 Die resultierenden Grundsymbole nennt man auch Token
 Beispiel:
 Zahlen bestehen aus beliebig vielen, aber mindestens einer Ziffer
 IEEE 754 Zahlen bestehen aus Ziffern und Kommas in Mantisse und
Exponent mit einem (kleinen oder großen) E dazwischen. Dabei sind einige
Teile optional
 Bezeichner beginnen mit einem Buchstaben, gefolgt von Ziffern,
Buchstaben und dem Sonderzeichen „_“
 Die lexikalische Struktur lässt sich mit
 regulären Ausdrücken beschreiben
 endlichen Automaten untersuchen
 Ein Programm zur Umsetzung lexikalischer Strukturen in Token nennt
man Scanner.
7.2.2 Syntax: Syntaktische Struktur
 Die syntaktische Struktur beschreibt also die Zusammensetzung von
Grundsymbolen der Sprache zu Ausdrücken, Anweisungen und letztlich
zu einem vollständigen Programm.
 Beispiel:
 Ein Ausdruck besteht aus:
 einem Ausdruck gefolgt von einem Operator, gefolgt von einem Ausdruck
z.B.: 5 * 7
 oder aber einer einzelnen Zahl
z.B. 5
 Eine abweisende Schleife besteht aus:
 dem Schlüsselwort „while“, einem bool‘schen Ausdruck (in Klammern) und
einer Anweisung.
z.B. while (a>5) i=i+1;
 Die syntaktische Struktur lässt sich durch Grammatiken beschreiben
 Ein Programm zur Analyse der syntaktischen Struktur (und zu dessen
weiteren Verarbeitung) nennt man Parser.
7.2.3 Semantik
 Die Semantik beschreibt also die Bedeutung der Konstrukte einer
Programmiersprache.
 Beispiel:
 if (a>5) then a=5 else a=0
Die Bedeutung dieser Alternative lässt sich wie folgt beschreiben:
Die bool‘sche Bedingung der Alternative wird ausgewertet:
Ist das Ergebnis „wahr“ so werden die Anweisungen des then-Zweiges
ausgeführt,
Ansonsten werden die Anweisungen des else-Zweiges ausgeführt.
 Die Beschreibungsformen - also die Notation der Beschreibung - sind
typischerweise nicht vorgeschrieben bzw. formalisiert.
 Da es für die meisten Programmiersprachen keine formalen Notationen
der Semantik gibt, gibt es auch meist kein Programm, welches für eine
Programmiersprache automatisch einen Übersetzer für Programme
dieser Sprache generiert.
 Scanner und Parser können automatisch generiert werden
 Compiler meist nicht
7.3
Reguläre Ausdrücke
 Im vorangegangenen Unterkapitel haben wir gesehen, dass die
lexikalische Analyse auf einer formalen Beschreibung der lexikalischen
Strukturen einer Programmiersprache aufbaut. Diese formale
Beschreibung ist die der „regulären Ausdrücke“.
Dieses Unterkapitel führt nun in die regulären Ausdrücke ein.
 Inhalt
1.
2.
3.
4.
5.
Beispiel
Definition: reguläre Ausdrücke
Definition: reguläre Sprachen
UNIX-Notation
Beispiele
7.3.1 Beispiel
 Beispiel Ziffer = [‘0‘-‘9‘]
Zahl = {Ziffer}+
Buchstabe = [‘A‘-‘Z‘‘a‘-‘z‘]
Bezeichner = {Buchstabe}({Ziffer}|{Buchstaben})*
 Notation (Auszug aus der UNIX-Notation)
 Einfache Notationsregeln:
 Hintereinander schreiben
bedeutet: Konkatenation
 |
bedeutet: Alternative
 Die Klammerung ist möglich (und äquivalent der math. Bedeutung)




[‘0‘-‘9‘] ist die Kurzform von ‘0‘ | ‘1‘ | ... | ‘9‘
{x} wird ersetzt durch die Definition von x
+ bedeutet: beliebig oft, aber mindestens einmal
* bedeutet: beliebig oft, auch kein mal
7.3.2 Definition: reguläre Ausdrücke
 Definition
 Reguläre Ausdrücke sind Formeln, mit denen (bestimmte) Sprachen definiert
werden können
 Sei  ein endlichen Alphabet bestehend aus Zeichen.
Reguläre Ausdrücke sind wie folgt induktiv definiert
1. e ist ein regulärer Ausdruck (d.h. auch leere Ausdrücke möglich)
2. Jedes x  ist a ein regulärer Ausdruck
3. wenn a und b reguläre Ausdrücke sind, dann auch:
a) ab
a konkateniert mit b
b) (a|b)
a oder b
c) (a)*
a beliebig oft, einschließlich 0-mal
wobei die Wiederholung Vorrang vor der Konkatenation und diese wiederum Vorrang vor
der Alternative hat. Um Präzedenzen explizit darzustellen ist es möglich, beliebig zu
klammern.
 Beispiel: Sei ={a,b,c,d,e}: (ab|cd*)* ist ein regulärer Ausdruck denn,
a und b ist regulärer Ausdruck wg. 2., ab ist regulärer Ausdruck wg. 3.a
c und d ist regulärer Ausdruck wg. 2.,
d* ist regulärer Ausdruck wg. 3. c), cd* ist regulärer Ausdruck wg .3.a)
(ab|cd*) ist regulärer Ausdruck wg. 3.b)
(ab|cd*)* ist regulärer Ausdruck wg. 3.c)
7.3.3 Definition: reguläre Sprachen
 Definition
 Alle durch einen regulären Ausdruck r definierten Folgen von Zeichen
nennt man Wörter der Sprache L(r)
 Die Menge der durch reguläre Ausdrücke beschreibbaren Sprachen ist
genau der Menge der regulären Sprachen
 Beispiele:
  ={a,b}
 = {a,b,c}
r=e
r = a | bc | ccc
L(r) = { }
L(r) = {a,bc,ccc}
  = {a,b,c}
r = (abc|bc)a*(bc|ab)*
L(r) = {abcabc, bcabc, abcaab, bcaab, abcaabc, ....}
 Es gibt Sprachen, die sich nicht durch reguläre Ausdrücke definieren
lassen:
 L(r) = {ab,aabb,aaa,bbb, ....., anbn} (keine Obergrenze für Wiederholungen)
 Alle Programmiersprachen
7.3.3 UNIX-Notation - die klassischen Elemente
 Für Token einer real existierenden Programmiersprache ist die einfache
Notation regulärer Ausdrücke teilweise sehr umständlich.
 Beispiel:
Bezeichner: (a|b|c|....|z|A|B|...|Z|_)(a|b|c|....|z|A|B|...|Z|_|0|1|....|9)*
 Daher hat sich in der Informatik eine zwar komplexere aber dafür
flexiblere Notation eingebürgert:
Die UNIX-Notation
Die UNIX Notation übernimmt die folgenden Konstruktionsregeln (mit
den zugehörigen Präzedenzregeln) aus der „klassischen“ Notation für
reguläre Ausdrücke
1.
2.
3.
4.
5.
Konkatenation zweier reg. Ausdrücke erfolgt ohne expliziten Operator
Alternativen werden mittels | gebildet
ein normales Zeichen steht für sich selbst
ein nachgestellter * steht für beliebige Wiederholung
Klammerung erfolgt durch ( und )
7.3.3 UNIX-Notation - Die Sonderzeichen
 Zusätzlich unterscheidet man in der UNIX Notation zwischen weiteren
Sonderzeichen und normale Zeichen.
 Weitere Sonderzeichen sind z.B. : * + [ ] ? ^ ( ) . $
 Falls ein Sonderzeichen nicht als solches interpretiert werden soll, ist ein
‘\‘ voranzustellen z.B. ‘\*‘
 Die Bedeutung der Sonderzeichen ist wie folgt
6. . steht für ein beliebiges Zeichen außer '\n' ‚ (Zeilenumbruch)
7. Auch in Apostrophe eingeschlossene Strings werden verbatim interpretiert.
8. ein nachgestelltes + steht für nichtleere Wiederholung
9. ein nachgestelltes ? bezeichnet einen optionalen Anteil
10. ^ am Anfang eines regulären Ausdrucks steht für Zeilenanfang
11. $ am Ende eines regulären Ausdrucks steht für Zeilenende
Verbatim := ver·ba·tim adv, adj wörtlich; Wort für Wort
7.3.3 UNIX-Notation - Die Zeichenklasse
 eine Zeichenklasse steht für genau ein Zeichen.
Sie kann durch Zeichen-Aufzählung x1, x2,... xn und Bereichsangaben
x1-xn gebildet werden:
 [x1-xn] steht für ein Zeichen aus dem Bereich und entsprich der klassischen
Notation x1|x2|... |xn
Beispiel: [0-9]
 [x1x2...xn] steht für genau ein Element aus der Menge der angegebenen
Zeichen
Beispiel: [abc_]
 Beide Schreibweisen (Bereich und Aufzählung) können beliebig kombiniert
werden.
Beispiel: [a-zA-Z0-9_]
 Ein ^-Zeichen am Anfang der Zeichnklasse ( [^....] ) spezifiziert die
komplementäre Zeichenmenge.
Beispiel: [^0-9] steht für ein beliebiges Zeichen außer einer Ziffer
7.3.4 Beispiele
1. Alle mit kleinem „a“ beginnenden Zeichenketten: a.*
2. Alle nichtleeren Dezimalziffernfolgen: [0-9]+
3. Alle Wörter, die aus genau 3 Zeichen bestehen und nicht mit einer Ziffer
enden: ..[^0-9]
4. Pascal-Bezeichner = [A-Za-z][A-Za-z0-9]*
5. C-Float-Literale = -?[0-9]+ ((\.[0-9]+)|((\.[0-9]+)?[eE]-?[0-9]+))
C-Float-Literale bestehen aus




einem Vorkomma-Anteil\ (ggf. mit Minuszeichen) (Syntax: -?[0-9]+)
einem optionalen Nachkomma-Anteil (Syntax: \.[0-9]+)
und / oder einem optionalen Exponenten-Anteil (Syntax: [eE]-?[0-9]+)
Dabei ist zu beachten, dass entweder der Nachkomma-Anteil oder der
Exponent vorhanden sein muss. Wenn beides fehlt, liegt eine IntegerKonstante vor. Daher ist die folgende Spezifikation nicht korrekt:
-?[0-9]+(\.[0-9]+)?([eE]-?[0-9]+)?
 Viel komplizierter wird es in der Praxis nur selten !
7.4
Endliche Automaten
 Wie wir gesehen haben, lassen sich die Grundelemente einer
Programmiersprache, die Token, durch reguläre Ausdrücke formal
beschreiben. Es gibt nun eine zweite äquivalente Beschreibungsart, die
zudem der Ausgangspunkt für eine maschinelle Überprüfung von
Wörtern bezüglich gegebener regulärer Ausdrücke ist:
Endliche Automaten.
Diese sind Gegenstand dieses Unterkapitels
 Inhalt





Definition
Graphische Darstellung
Funktionsweise
Beispiel: NEA, DEA
Automaten und reguläre Ausdrücke
7.4.1 Definition
 Definitionen
 Ein endlicher Automat M ist ein 5-Tupel
M = (Z, , d, z0, E) mit:
 Z ist eine endliche Menge der Zustände
  ist das Eingabealphabet
mit Z   = (d.h. kein Zustand ist Element des Alphabets und umgekehrt)
 d : Z x   Z ist die Überführungsfunktion
 z0 ist der Startzustand
 E  Z ist die Menge der Endzustände (E ist echte Teilmenge)
 Ist in d die Wertemenge Z eindeutig für alle Zustände und Zeichen und ist
e   (e ist das “leere” Zeichen), so nennt man den Automaten
deterministisch ansonsten Indeterministisch.
 Die von M akzeptierte Sprache L(M) ist definiert als:
L(M) = {x  * | z0 geht mit x in keinem oder min. einem Zustand aus Z in E über}
7.4.2 Graphische Darstellung
 Ein endlicher Automat kann als gerichteter Graph dargestellt werden:




Die Zustände werden als Knoten repräsentiert
Die Überführungsfunktion wird als Kanten dargestellt
Der Anfangszustand wird mit einem eingehenden Pfeil markiert
Die Endzustände werden mit einem zweiten Kreis umringt
a
z3
M=
{
b a
b
z0
z2
b
a
z1
Z = z0, z1, z2, z3,
S = a,b,
d = (z0,b,z3), (z0,a,z1), (z1,b,z0), (z1,a,z2), (z2,b,z1), (z2,a,z3),
z0,
E = z3,
}
7.4.3 Funktionsweise
 Der endliche Automat versucht, ein Eingabewort, also eine Sequenz
von Eingabezeichen, zu „akzeptieren“.
 Der Automat befindet sich anfänglich im Eingangszustand zo
 Jetzt werden das Wort, Buchstabe für Buchstabe im Automat verarbeitet.
Dabei ergibt ein aktueller Zustand und der gerade gelesenen Buchstabe
über die Überführungsfunktion einen Folgezustand, der dann der aktuelle
Zustand für den Folgebuchstaben wird.
 Das Wort ist akzeptiert, wenn sich der Automat nach der Verarbeitung des
letzten Buchstabens in einem Endzustand befindet
 Das Wort ist nicht akzeptiert, wenn
 sich der Automat nach der Verarbeitung des letzten Buchstabens nicht in einem
Endzustand befindet, oder
 die Übergangsfunktion bei der Abarbeitung keinen Wert für einen aktuellen
Zustand und das zugehörige aktuell gelesene Zeichen findet.
 Beispiel:
 Der Automat des vorangegangen Beispiels akzeptiert z.B. b,aaa,abaa, ...
 Er akzeptiert nicht: aa (kein Endzustand), ba ( b in Z3 nicht definiert)
7.4.3 Beispiel: NEA, DEA
 Beispiel eines nichtdeterministischen endlichen Automaten (NEA)
z2
z3
a
e
a
z0
b
a
a
z1
 Beispiel eines (äquivalenten) deterministischen endlichen Automaten
(DEA)
a
b
b
z0
z2
z1
z3
a
a
a
 Jeder indeterministische endliche Automat lässt sich in einen
deterministischen endlichen Automaten (automatisch) umformen, der
die gleiche Sprache akzeptiert (also L(NEA) = L(DEA) )
7.4.3 Automaten und reguläre Ausdrücke
 Satz:
Die Menge der regulären Sprachen ist identisch mit der Menge, der von
endlichen Automaten akzeptierten Sprachen.
 Beweisidee:
Für jeden endlichen Automaten gibt es einen regulären Ausdruck, der
die vom Automaten akzeptierte Sprache beschreibt.
Umgekehrt gibt es für jeden regulären Ausdruck einen endlichen
Automaten, der die durch den regulären Ausdruck beschriebenen
Sprache akzeptiert.
Beweis durch Konstruktion eines Automaten aus einem regulären
Ausdruck und umgekehrt ( Compilerbau, 4. Semester)
 Beispiel: (a|bc)*
e
a
z2
e
z0
b
z2
c
z2
z2
e
7.5
Grammatiken
 Die Grundsymbole einer Programmiersprache, die deren lexikalische
Struktur definieren, lassen sich mit Hilfe regulärer Ausdrücke
beschreiben und mit endlichen Automaten überprüfen. Reguläre
Sprachen bzw. endl. Automaten sind aber nicht mächtig genug,
komplexere Strukturen wie z.B. syntaktische Strukturen einer
Programmiersprache zu beschreiben.
Dieses Unterkapitel stellt nun eine Beschreibungsform vor, die auch
(bestimmte) komplexere Strukturen, wie sie typisch für
Programmiersprachen sind, zu beschreiben vermag: Grammatiken.
 Inhalt
1.
2.
3.
4.
Beispiel aus der deutschen Sprache
Definitionen
Notationen
Chomsky Hierarchie
7.5.1 Beispiel: Deutsche Sprache
 Beispiel:
 Ein Satz ist eine Folge von Subjekt Prädikat und möglicherweise Objekt,
abgeschlossen mit einem Punkt (".")
 Ein Subjekt ist ein Eigenname oder ein Artikel, gefolgt von einem
Substantiv
 Ein Prädikat ist ein Verb
 Ein Objekt ist ein Artikel, gefolgt von einem Substantiv
 Eigenname: Adam, Eva
 Verb: ißt, beißt
 Artikel: der, die
 Substantiv: Apfel, Schlange
 Grammatik:
 Nichtterminalsymbole und Terminalsymbole bilden die Elemente
 Regeln zu deren Verwendung
 Ein Startsymbol (Satz)
7.5.1 Beispiel: Notation
 Beispiel
 Ein Satz ist eine Folge von Subjekt Prädikat und möglicherweise Objekt,
abgeschlossen mit einem Punkt (".")
<Satz> := <Subjekt> <Prädikat> [<Objekt>] .
 Ein Subjekt ist ein Eigenname oder ein Artikel, gefolgt von einem
Substantiv
<Subjekt> :=(<Eigenname> | <Artikel>) <Substantiv>
 Ein Prädikat ist ein Verb
<Prädikat> := <Verb>
 Ein Objekt ist ein Artikel, gefolgt von einem Substantiv
<Objekt> := <Artikel><Substantiv>
 Eigenname: Adam, Eva
<Eigenname> := "Adam" | "Eva"
 ....
7.5.1 Beispiel: Ein „Wort“ der Sprache
 Beispiel








<Satz>
<Subjekt>
<Artikel>
<Attribut>
<Adjektiv>
<Substantiv>
<Prädikat>
<Objekt>
:=
:=
:=
:=
:=
:=
:=
:=
<Subjekt> <Prädikat> <Objekt>
<Artikel> <Attribut> <Substantiv>
e | der | die | das
e | <Adjektiv> | <Adjektiv> <Attribut>
kleine | bissige | große
Hund | Katze
jagt | sieht
<Artikel> <Attribut> <Substantiv>
 Ableitungsbaum:
<Satz>
<Subjekt>
<Artikel>
<Attribut>
<Prädikat>
<Substantiv>
<Artikel>
<Adjektiv> <Attribut>
<Objekt>
<Attribut>
<Substantiv>
<Adjektiv>
<Adjektiv>
der kleine bissige Hund jagt
die
große
Katze
7.5.2 Definitionen
 Eine Grammatik G ist ein 4-Tupel G = (N, T, P, S) mit:
 N ist eine endliche Menge, die Menge der Nichtterminalen
 T ist eine endliche Menge, das Terminalalphabet
mit NT=
 P ist eine endliche Menge von Regeln oder Projektionen mit
P ist eine Teilmenge von (NT)+  (N T)*
die Grammatik heißt kontextfrei, wenn gilt:
P ist eine Teilmenge von N  (N T)*
 S  N ist das Startsymbol
 ein Wort v ist direkt ableitbar aus u (notiert: u 1 v) , wenn gilt:
u = xyz, v = xy’z (x,y Worte); y := y’ ist Regel in P
 ein Wort w ist ableitbar aus u (notiert: u  w), wenn gilt:
1
1
1
1
u  u1  u2  …  w (u1,u2,… Worte)
 Die von G dargestellte Sprache L(G) ist definiert als:
L(G) = {w  T* | S  w }
(S : Startsymbol)
7.5.2 Definition: Beispiel
 Eine Grammatik G ist ein 4-Tupel G = (<N>, T, P, S) mit:
 N ist eine endliche Menge, die Menge der Nichtterminalen
 T ist eine endliche Menge, das Terminalalphabet
mit NT=
 P ist eine endliche Menge von Regeln oder Projektionen mit
+
P ist eine Teilmenge von (NT)  (N T)*
die Grammatik heißt kontextfrei, wenn gilt:
P ist eine Teilmenge von N  (N T)*
 S  N ist das Startsymbol
 Aus unserem Beispiel








<Satz>
<Subjekt>
<Artikel>
<Attribut>
<Adjektiv>
<Substantiv>
<Prädikat>
<Objekt>
:=
:=
:=
:=
:=
:=
:=
:=
<Subjekt> <Prädikat> <Objekt>
<Artikel> <Attribut> <Substantiv>
e | der | die | das
e | <Adjektiv> | <Adjektiv> <Attribut>
kleine | bissige | große
Hund | Katze
jagt | sieht
<Artikel> <Attribut> <Substantiv>
7.5.2 Definition: Beispiel
G=(
N= { <Satz>, <Subjekt>, <Prädikat>, <Objekt>, <Artikel>, <Attribut>,
<Substantiv>, <Adjektiv>}
T= { kleine, bissige, große, Hund, Katze, jagt, sieht}
P= {
<Satz>
:=
<Subjekt>
:=
<Artikel>
:=
<Attribut>
:=
<Adjektiv> :=
<Substantiv> :=
<Prädikat> :=
<Objekt>
:=
}
S = <Satz>
)
<Subjekt> <Prädikat> <Objekt>
<Artikel> <Attribut> <Substantiv>
e | der | die | das
e | <Adjektiv> | <Adjektiv> <Attribut>
kleine | bissige | große
Hund | Katze
jagt | sieht
<Artikel> <Attribut> <Substantiv>
7.5.2 Definition: Beispiele
 G = { N,T,P,S }, N = { A,B,C,S }, T = { a,b,c },
P = { S:=ABC, A:=aAa, A:=BC, B:=bbB, B:=BC, B:=e, C:=Cc, C:=c }
 a2b2c3 a2  L(G)
 a4 b2 c b2 c a4  L(G)
 G = { N,T,P,S }, N = { A,B,C,S }, T = { a,b,c },
P = { S:=ABC, A:=ABA, C:=CBC, A:=a, B:=b, C:=c }
 {wT* | w= (ab)nac(bc)n, n>=1} = L(G)
 b2a2c3  L(G)
 G = { N,T,P,S }, N = { A,B,C,S }, T = { a,b,c },
P = { S:=ABC, A:=Aa, A:=a, B:=bBc, B:=b, C:=c }
 {wT* | w= anbncn, n>1}  L(G)
 a3b2c2  L(G)
 a2b2c3  L(G)
 …
7.5.3 Notationen: BNF / EBNF
 Die BNF (John Backus, Peter Naur, 1960) ist die Grundform und
entspricht der Definition
 eventuell erweitert um den alternativ-Operator (“|”)  siehe Beispiele
 Die EBNF erweitert die BNF um 3 (2) Operatoren
 den Alternativ Operator: |
 den Wiederholungs-Operator: { … }
Notiert auf der rechten Seite einer Regel, drückt er aus, dass der
geklammerter Ausdruck beliebig oft wiederholt werden kann - einschließlich
kein mal.
Teilweise wird die minimal und maximal mögliche Anzahl von
max
Wiederholungen zusätzlich notiert: { … } min
 der Optional-Operator: [ … ]
Notiert auf der rechten Seite einer Regel, drückt er aus, dass der
geklammerter Ausdruck ein oder keinmal wiederholt werden kann, also
optional ist.
 Es lässt sich zeigen, dass beide Notationen gleich mächtig sind, dass
sich also BNF notierte Grammatiken auch in EBNF notieren lassen
(trivial) und umgekehrt (wie ?)
7.5.3 Notationen: Syntaxdiagramme
 Syntaxdiagramme sind eine einfache graphische Repräsentation der
syntaktischen Struktur:
 Konkatenationen werden durch Sequenzen, Alternativen durch
Verzweigungen, Wiederholungen durch Rückkopplung dargestellt.
 Elemente sind Terminale und Nichtterminale
 Unser Beispiel (vereinfacht) im Syntaxdiagramm





<Satz> = <Subjekt> <Prädikat> [<Objekt>] .
<Subjekt> = <Eigenname> | <Artikel> <Substantiv>
<Prädikat> = <Verb>
<Objekt> = <Artikel><Substantiv>
<Eigenname> = "Adam" | "Eva"
Satz:
Eigename
Artikel Substantiv
Eigenname:
Adam
Eva
Verb
Artikel
Substantiv
.
7.5.4 Chomsky Hierarchie
 Grammatiken - und damit die von ihnen definierten Sprachen - bilden eine
Hierarchie von “Mächtigkeiten”




Typ 0
Typ 1
Typ 2
Typ 3
rekursiv aufzählbar Keine Einschränkung
kontextsensitiv Linke Seite aller Regeln kürzer/gleich als rechte
kontextfrei
zusätzlich: linke Seite aller Regeln ein Nichtterminal
regulär
zusätzlich: rechte Seite beginnt mit Terminal
 Eine Sprache L heißt vom Typ x (x=0,1,2,3), falls es eine Typ x Grammatik
G gibt mit L(G) = L
 Reguläre Ausdrücke beschreiben Typ 3-Sprachen, kontextfreie
Grammatiken beschreiben Typ 1-Sprachen
Typ 0-Sprachen
Typ 1-Sprachen
Typ 2-Sprachen
Typ 3-Sprachen
nicht entscheidbar
entscheidbar, aber zu komplex
entscheidbar mit endl. Automat mit Stack
entscheidbar mit endl. Automat
7.5.4 Chomsky Hierarchie: Beispiel
 Aus den Teilmengenbeziehungen der Chomsky-Hierarchie lässt sich
also insbesondere ableiten
 dass alle Sprachen, die sich mit regulären Ausdrücken beschreiben lassen
auch durch kontextfreie Grammatiken darstellen lassen
 dass sich nicht notwendigerweise alle durch Grammatiken darstellbaren
Sprachen auch durch reguläre Ausdrücke beschreiben lassen
Endlicher
Automat
a
z3
z2
b a
b
z0
Grammatik
b
a
z1
<start> := <ab>b|a<ba>a<ba>a
<ab> := e | ab | ab<ab>
<ba> := e | ba | ba<ba>
7.6
Zusammenfassung des Kapitels
 Programmiersprache
 Was ist eine Programmiersprache / ein Programm
 Generationen von Programmiersprachen / Programmiersprachen heute
 Syntax und Semantik
 Syntax vs. Semantik
 Syntax / Semantik
 Reguläre Ausdrücke
 Definitionen: reguläre Ausdrücke und reguläre Sprachen
 UNIX-Notation
 Endliche Automaten
 Definition, Graphische Darstellung und Funktionsweise
 Automaten und reguläre Ausdrücke

Beispiel aus der deutschen Sprache
1. Definition und Notationen
2. Chomsky Hierarchie
Zugehörige Unterlagen
Herunterladen