• Keine Ergebnisse gefunden

Generator Scanner

N/A
N/A
Protected

Academic year: 2022

Aktie "Generator Scanner "

Copied!
46
0
0

Wird geladen.... (Jetzt Volltext ansehen)

Volltext

(1)

Diskussion:

• Scanner und Sieber werden i.a. in einer Komponente zusammen gefasst, indem man dem Scanner nach Erkennen eines Tokens gestattet, eine Aktion auszuführen :-)

• Scanner werden i.a. nicht von Hand programmiert, sondern aus einer Spezifikation generiert:

Generator Scanner

Spezifikation

(2)

Vorteile:

Produktivität:

Die Komponente lässt sich schneller herstellen :-) Korrektheit:

Die Komponente realisiert (beweisbar :-) die Spezifikation.

Effizienz:

Der Generator kann die erzeugte Programmkomponente mit den effizientesten Algorithmen ausstatten.

Einschränkungen:

→ Spezifizieren ist auch Programmieren — nur eventuell einfacher :-)

→ Generierung statt Implementierung lohnt sich nur für Routine-Aufgaben ... und ist nur für Probleme möglich, die sehr gut verstanden sind :-(

(3)

Vorteile:

Produktivität:

Die Komponente lässt sich schneller herstellen :-) Korrektheit:

Die Komponente realisiert (beweisbar :-) die Spezifikation.

Effizienz:

Der Generator kann die erzeugte Programmkomponente mit den effizientesten Algorithmen ausstatten.

Einschränkungen:

→ Spezifizieren ist auch Programmieren — nur eventuell einfacher :-)

→ Generierung statt Implementierung lohnt sich nur für Routine-Aufgaben ... und ist nur für Probleme möglich, die sehr gut verstanden sind :-(

(4)

... in unserem Fall:

Generator Scanner

Spezifikation

Spezifikation von Token-Klassen: Reguläre Ausdrücke;

Generierte Implementierung: Endliche Automaten + X :-)

(5)

... in unserem Fall:

Generator

[0−9]

[1−9]

0

0 | [1-9][0-9]*

Spezifikation von Token-Klassen: Reguläre Ausdrücke;

Generierte Implementierung: Endliche Automaten + X :-)

(6)

1.1 Grundlagen: Reguläre Ausdrücke

• Programmtext benutzt ein endliches Alphabet Σ von Eingabe-Zeichen, z.B. ASCII :-)

• Die Menge der Textabschnitte einer Token-Klasse ist i.a. regulär.

• Reguläre Sprachen kann man mithilfe regulärer Ausdrückespezifizieren.

Die Menge EΣ der (nicht-leeren) regulären Ausdrücke ist die kleinste Menge E mit:

• ∈ E ( neues Symbol nicht aus Σ);

a ∈ E für alle aΣ;

• (e1 | e2),(e1 ·e2), e1 ∈ E sofern e1, e2 ∈ E.

(7)

1.1 Grundlagen: Reguläre Ausdrücke

• Programmtext benutzt ein endliches Alphabet Σ von Eingabe-Zeichen, z.B. ASCII :-)

• Die Menge der Textabschnitte einer Token-Klasse ist i.a. regulär.

• Reguläre Sprachen kann man mithilfe regulärer Ausdrückespezifizieren.

Die Menge EΣ der (nicht-leeren) regulären Ausdrücke ist die kleinste Menge E mit:

• ∈ E ( neues Symbol nicht aus Σ);

a ∈ E für alle aΣ;

• (e1 | e2),(e1 ·e2), e1 ∈ E sofern e1, e2 ∈ E.

(8)

Stephen Kleene, Madison Wisconsin, 1909-1994

(9)

Beispiele:

((a·ba) (a | b)

((a·b)·(a·b))

Achtung:

• Wir unterscheiden zwischen Zeichen a, 0,|,... und Meta-Zeichen (,|, ),...

• Um (hässliche) Klammern zu sparen, benutzen wir Operator-Präzedenzen:

> · >|

und lassen “·” weg :-)

• Reale Spezifikations-Sprachen bieten zusätzliche Konstrukte wie:

e? ≡ ( | e) e+ ≡ (e ·e) und verzichten auf “” :-)

(10)

Beispiele:

((a·ba) (a | b)

((a·b)·(a·b))

Achtung:

• Wir unterscheiden zwischen Zeichen a, 0, |,... und Meta-Zeichen (,|,),...

• Um (hässliche) Klammern zu sparen, benutzen wir Operator-Präzedenzen:

> · > |

und lassen “·” weg :-)

• Reale Spezifikations-Sprachen bieten zusätzliche Konstrukte wie:

e? ≡ ( | e) e+ ≡ (e ·e) und verzichten auf “” :-)

(11)

Beispiele:

((a·ba) (a | b)

((a·b)·(a·b))

Achtung:

• Wir unterscheiden zwischen Zeichen a, 0, |,... und Meta-Zeichen (,|,),...

• Um (hässliche) Klammern zu sparen, benutzen wir Operator-Präzedenzen:

> · > |

und lassen “·” weg :-)

• Reale Spezifikations-Sprachen bieten zusätzliche Konstrukte wie:

e? ≡ ( | e) e+ ≡ (e ·e) und verzichten auf “ :-)

(12)

Spezifikationen benötigen eine Semantik :-) Im Beispiel:

Spezifikation Semantik aba {abna | n ≥ 0} a | b {a,b}

abab {abab}

Für e ∈ EΣ definieren wir die spezifizierte Sprache [[e]] ⊆ Σ induktiv durch:

[[]] = {} [[a]] = {a} [[e]] = ([[e]])

[[e1|e2]] = [[e1]]∪ [[e2]]

[[e1·e2]] = [[e1]]· [[e2]]

(13)

Beachte:

• Die Operatoren (_),∪, · sind die entsprechenden Operationen auf Wort-Mengen:

(L) = {w1 . . .wk | k ≥ 0, wiL} L1 ·L2 = {w1w2 | w1L1, w2L2}

(14)

Beachte:

• Die Operatoren (_),∪, · sind die entsprechenden Operationen auf Wort-Mengen:

(L) = {w1 . . .wk | k ≥ 0, wiL} L1 ·L2 = {w1w2 | w1L1, w2L2}

• Reguläre Ausdrücke stellen wir intern als markierte geordnete Bäume dar:

.

|

*

b

a (ab|)

Innere Knoten: Operator-Anwendungen;

Blätter: einzelne Zeichen oder .

(15)

Finger-Übung:

Zu jedem regulären Ausdruck e können wir einen Ausdruck e0 (evt. mit

“?”) konstruieren so dass:

• [[e]] = [[e0]];

• Falls [[e]] = {}, dann ist e0;

• Falls [[e]] 6= {}, dann enthält e0 kein “”.

Konstruktion:

Wir definieren eine Transformation T von regulären Ausdrücken durch:

(16)

Finger-Übung:

Zu jedem regulären Ausdruck e können wir einen Ausdruck e0 (evt. mit

“?”) konstruieren so dass:

• [[e]] = [[e0]];

• Falls [[e]] = {}, dann ist e0;

• Falls [[e]] 6= {}, dann enthält e0 kein “”.

Konstruktion:

Wir definieren eine Transformation T von regulären Ausdrücken durch:

(17)

T [] = T [a] = a

T [e1|e2] = case (T [e1], T [e2]) of (,) :

| (e01,) : e01?

| (, e0

2) : e02?

| (e01,e02): (e01 | e02) T [e1·e2] = case (T [e1], T [e2]) of (,) :

| (e01,) : e01

| (, e0

2) : e02

| (e01,e02): (e01 ·e02) T [e] = case T [e] of :

| e1: e1

T [e?] = case T [e] of :

| e1: e1?

(18)

Unsere Anwendung:

Identifier in Java:

le = [a-zA-Z_\$]

di = [0-9]

Id = {le} ({le} | {di})*

Bemerkungen:

• “le” und “di” sind Zeichenklassen.

• Definierte Namen werden in “{”, “}” eingeschlossen.

• Zeichen werden von Meta-Zeichen durch “\” unterschieden.

(19)

Unsere Anwendung:

Identifier in Java:

le = [a-zA-Z_\$]

di = [0-9]

Id = {le} ({le} | {di})*

Bemerkungen:

• “le” und “di” sind Zeichenklassen.

• Definierte Namen werden in “{”, “}” eingeschlossen.

• Zeichen werden von Meta-Zeichen durch “\” unterschieden.

(20)

Unsere Anwendung:

Identifier in Java:

le = [a-zA-Z_\$]

di = [0-9]

Id = {le} ({le}|{di})*

Gleitkommazahlen:

Float = {di}* (\.{di}|{di}\.) {di}*((e|E)(\+|\-)?{di}+)?

Bemerkungen:

• “le” und “di” sind Zeichenklassen.

• Definierte Namen werden in “{”, “}” eingeschlossen.

• Zeichen werden von Meta-Zeichen durch “\” unterschieden.

(21)

1.2 Grundlagen: Endliche Automaten Beispiel:

a b

Knoten: Zustände;

Kanten: Übergänge;

Beschriftungen: konsumierter Input :-)

(22)

1.2 Grundlagen: Endliche Automaten Beispiel:

a b

Knoten: Zustände;

Kanten: Übergänge;

Beschriftungen: konsumierter Input :-)

(23)

Michael O. Rabin, Stanford University

Dana S. Scott, Carnegy Mellon University, Pittsburgh

(24)

Formal ist ein nicht-deterministischer endlicher Automat mit-Übergängen (-NFA) ein Tupel A = (Q, Σ,δ, I, F) wobei:

Q eine endliche Menge von Zuständen;

Σ ein endliches Eingabe-Alphabet;

IQ die Menge der Anfangszustände;

FQ die Menge der Endzustände und

δ die Menge der Übergänge (die Übergangs-Relation) ist.

(25)

Formal ist ein nicht-deterministischer endlicher Automat mit-Übergängen (-NFA) ein Tupel A = (Q, Σ,δ, I, F) wobei:

Q eine endliche Menge von Zuständen;

Σ ein endliches Eingabe-Alphabet;

IQ die Menge der Anfangszustände;

FQ die Menge der Endzustände und

δ die Menge der Übergänge (die Übergangs-Relation) ist.

Für-NFAs ist:

δ ⊆ Q × (Σ∪ {})× Q

• Gibt es keine-Übergänge (p,,q), ist A ein NFA.

• Ist δ : Q× ΣQ eine Funktion und #I = 1, heißt A deterministisch (DFA).

(26)

Formal ist ein nicht-deterministischer endlicher Automat mit-Übergängen (-NFA) ein Tupel A = (Q, Σ,δ, I, F) wobei:

Q eine endliche Menge von Zuständen;

Σ ein endliches Eingabe-Alphabet;

IQ die Menge der Anfangszustände;

FQ die Menge der Endzustände und

δ die Menge der Übergänge (die Übergangs-Relation) ist.

Für-NFAs ist:

δ ⊆ Q × (Σ∪ {})× Q

• Gibt es keine-Übergänge (p,, q), ist A ein NFA.

• Ist δ : Q× ΣQ eine Funktion und #I = 1, heißt A deterministisch(DFA).

(27)

Akzeptierung

• Berechnungen sind Pfade im Graphen.

• akzeptierende Berechnungen führen von I nach F .

• Ein akzeptiertes Wort ist die Beschriftung eines akzeptierenden Pfades ...

a b

(28)

Akzeptierung

• Berechnungen sind Pfade im Graphen.

• akzeptierende Berechnungen führen von I nach F .

• Ein akzeptiertes Wort ist die Beschriftung eines akzeptierenden Pfades ...

a b

(29)

• Dazu definieren wir den transitiven Abschluss δ von δ als kleinste Menge δ0 mit:

(p,, p) ∈ δ0 und

(p,xw, q) ∈ δ0 sofern (p, x, p1) ∈ δ und (p1, w,q) ∈ δ0.

δ beschreibt für je zwei Zustände, mit welchen Wörtern man vom einen zum andern kommt :-)

• Die Menge aller akzeptierten Worte, d.h. die von A akzeptierte Sprache können wir kurz beschreiben als:

L(A) = {wΣ | ∃ iI, fF : (i,w, f) ∈ δ}

(30)

Satz:

Für jeden regulären Ausdruck e kann (in linearer Zeit :-) ein-NFA konstruiert werden, der die Sprache [[e]] akzeptiert.

Idee:

Der Automat verfolgt (konzepionell mithilfe einer Marke “•”), wohin man in e mit der Eingabe w gelangen kann.

(31)

Beispiel:

*

.

.

|

|

b a

b a a

( a | b )

a ( a | b )

(32)

Beispiel:

*

.

.

|

|

b a

b a a

w = bbaa :

(33)

Beispiel:

*

.

.

|

|

b a

b a a

w = bbaa :

(34)

Beispiel:

*

.

.

|

|

b a

b a a

w = bbaa :

(35)

Beispiel:

*

.

.

|

|

b a

b a a

w = bbaa :

(36)

Beispiel:

*

.

.

|

|

b a

b a a

w = bbaa :

(37)

Beispiel:

*

.

.

|

|

b a

b a a

w = bbaa :

(38)

Beispiel:

*

.

.

|

|

b a

b a a

w = bbaa :

(39)

Beispiel:

*

.

.

|

|

b a

b a a

w = bbaa :

(40)

Beispiel:

*

.

.

|

|

b a

b a a

w = bbaa :

(41)

Beachte:

• Gelesen wird nur an den Blättern.

• Die Navigation im Baum erfolgt ohne Lesen, d.h. mit-Übergängen.

• Für eine formale Konstruktion müssen wir die Knoten im Baum bezeichnen.

• Dazu benutzen wir (hier) einfach den dargestellten Teilausdruck :-)

• Leider gibt es eventuell mehrere gleiche Teilausdrücke :-(

==⇒ Wir numerieren die Blätter durch ...

(42)

... im Beispiel:

*

.

.

|

|

b a

b a

a

(43)

... im Beispiel:

*

.

.

|

|

0 1

2

3

b

4

a

b a

a

(44)

... im Beispiel:

*

.

.

|

|

0 1

2

3 4

a b a b

a

(45)

Die Konstruktion:

Zustände:r, rr Knoten von e;

Anfangszustand:e;

Endzustand: e•; Übergangsrelation:

Für Blätter ri x benötigen wir: (•r,x,r•). Die übrigen Übergänge sind:

(46)

r Übergänge r1 | r2 (•r,,r1)

(•r,,r2) (r1•,, r•) (r2•,, r•) r1 ·r2 (•r,,r1) (r1•,,r2) (r2•,, r•)

r Übergänge r1 (•r,, r•)

(•r,,r1) (r1•,,r1) (r1•,,r•) r1? (•r,, r•)

(•r,,r1) (r1•,,r•)

Referenzen

ÄHNLICHE DOKUMENTE

Man zeichnet alle Kanten ein, die benötigt werden, damit sich die von dem Automaten akzeptierte Sprache nicht ändert, wenn man die -Kante wegläÿt. Hierzu kann man sich überlegen,

Zeichne alle Kanten ein, die benötigt werden, damit sich die von dem Automaten akzeptierte Sprache nicht ändert, wenn man die -Kante wegläÿt... deterministische

Automaten akzeptiert wird, gibt es eine rechts- lineare Grammatik, die diese Sprache erzeugt und umgekehrt8. A=

Das sind Sprachen, die durch sogenannte deterministische endliche Automaten (deterministic finite automaton, DFA) erzeugt

[r]

(d) Erg¨ anze den DFA um einen weiteren Zustand und zus¨ atzliche ¨ Uberg¨ ange, so dass der neue DFA alle ¨ Uberg¨ ange definiert aber dieselbe Sprache akzeptiert.

Dezember 2006, vor der Vorlesung in die Briefkästen

Folgerungen aus dem Satz von Kleene Korollar 2.3.2 die Klasse der regul¨ aren Sprachen ist abgeschlossen unter allen Booleschen Operationen sowie Konkatenation und Stern