30.01.2014 Aufrufe

LEXIKALISCHE ANALYSE

LEXIKALISCHE ANALYSE

LEXIKALISCHE ANALYSE

MEHR ANZEIGEN
WENIGER ANZEIGEN

Erfolgreiche ePaper selbst erstellen

Machen Sie aus Ihren PDF Publikationen ein blätterbares Flipbook mit unserer einzigartigen Google optimierten e-Paper Software.

2. Kapitel<br />

<strong>LEXIKALISCHE</strong> <strong>ANALYSE</strong> <br />

Compilerbau <br />

Prof. Dr. Wolfgang Schramm


Lexikalische Analyse (Scanner) 1/2 <br />

1 <br />

Aufgabe <br />

– Erkennen von lexikalischen Elementen. <br />

– Erstellung einer internen Darstellung für lexikalische Elemente. <br />

Einordnung <br />

¤<br />

Der Scanner ist i.allg. Unterprogramm des Parsers. <br />

Erweiterungen: <br />

¤<br />

¤<br />

¤<br />

Kopie des Quellprogramms (evtl.) mit Meldungen erzeugen. <br />

ImplemenNerung von Makrotechniken. <br />

Fehlerbehandlung (Einfügen, Löschen, Vertauschen von Zeichen). <br />

Bsp: -­‐7.6F+8 è -­‐7.6E+8


Lexikalische Analyse (Scanner) 2/2 <br />

2 <br />

liefere nächstes lex.<br />

Element<br />

Quell-<br />

Scanner<br />

Parser<br />

pro-<br />

gramm<br />

Strukturbaum<br />

Eintragen<br />

Nächstes lexikalisches<br />

Element<br />

Symboltabelle<br />

Eintragen/<br />

Nachschlagen


TheoreNscher Hintergrund der lexikalischen Analyse 1/2 <br />

3 <br />

1. Die Struktur lexikalischer Symbole kann durch reguläre Ausdrücke beschrieben <br />

werden. Das heißt, die Menge der Zeichenke^en, die auf ein Token abgebildet <br />

werden, ist die zum Ausdruck gehörige reguläre Sprache. <br />

2. Reguläre Sprachen werden durch rechtslineare oder durch linkslineare <br />

GrammaNken erzeugt. <br />

3. Reguläre Sprachen werden von nichtdeterminisNschen endlichen Automaten <br />

(NEA) erkannt. <br />

NFA<br />

4. Zu jedem nichtdeterminisNschen endlichen Automaten kann man auch einen <br />

determinisNschen endlichen Automaten (DEA) konstruieren, der die gleiche <br />

Sprache erkennt. <br />

DFA


TheoreNscher Hintergrund der lexikalischen Analyse 2/2 <br />

4 <br />

Kurz gesagt:<br />

Man braucht einen Formalismus zur Spezifikation der Token.<br />

Reguläre Ausdrücke<br />

Man braucht einen Mechanismus zur Erkennung der Token.<br />

DFA,<br />

Zustandsdiagramme


DefiniNonen für Beschreibung formaler Sprachen 1/3 <br />

5 <br />

Terminalsymbole <br />

¤<br />

Die Terminalsymbole (Lexeme, Token) sind Elemente einer Menge T von <br />

GrammaNksymbolen. Terminalsymbole sind die kleinsten syntakNschen <br />

Grundeinheiten einer formalen Sprache mit selbstständiger Bedeutung. Ein <br />

Satz ist die Aneinanderreihung von Terminalsymbolen. <br />

Nonterminalsymbole <br />

¤<br />

Die Nonterminalsymbole sind Elemente einer Menge N von GrammaNk-­symbolen,<br />

die zur Darstellung von Zwischenzuständen des Spracher-­zeugungsprozesses<br />

dienen. <br />

ProdukKonen <br />

¤<br />

Die ProdukNonen sind Vorschrifen für das Ersetzen einer Symbolke^e α durch <br />

eine andere Symbolke^e β (α → β ).


DefiniNonen für Beschreibung formaler Sprachen 2/3 <br />

6 <br />

Eine (Chomsky-) Grammatik zur Beschreibung von Syntax ist durch ein 4-Tupel:<br />

G = (T, N, P, S)<br />

definiert.<br />

T: Menge der Terminalsymbole.<br />

N: Menge der Nonterminalsymbole.<br />

Lies: Element<br />

von<br />

Epsilon<br />

(leeres Wort)<br />

P: Menge von Produktionen α → β mit α, β ∈ (N ∪ T) * und N ∩ T = ∅, α ≠ ε <br />

S: Ein ausgezeichnetes Nonterminalsymbol - das Startsymbol mit S ∈ N.<br />

Kleene Stern


DefiniNonen für Beschreibung formaler Sprachen 3/3 <br />

7 <br />

Die Sprache L(G) einer GrammaKk ist die Menge der Terminalsymbolke^en, die <br />

über das Startsymbol S nach den Regeln von P hergeleitet werden kann. <br />

* <br />

L (G) = { s | S → s mit s ∈ T * }


Die Chomsky GrammaNk-­‐Typen <br />

8 <br />

Eine GrammaNk G = (T, N, P, S) heißt <br />

• vom Typ 0 oder unbeschränkt (rekursiv aufzählbar), <br />

wenn sie nach den Regeln von P hergeleitet werden kann. <br />

• vom Typ 1 oder kontextsensiKv, <br />

wenn in jeder Regel α → β die linke Seite nicht länger als die rechte ist: |α|<br />

≤ |β|; die Regel A → ε mit A ∈ N ist zulässig, wenn A auf keiner rechten <br />

Seite vorkommt. <br />

• vom Typ 2 oder kontexWrei, <br />

wenn jede Regel die Form A → α hat, mit A ∈ N und α ∈ (N ∪ T) * . <br />

• vom Typ 3 oder regulär, <br />

wenn jede Regel eine der Formen A → α, A → αB oder A → Bα hat, mit A, <br />

B ∈ N und α ∈ T * .


Die Chomsky Sprachhierarchie <br />

9 <br />

o<br />

Wenn L i die Menge aller Sprachen ist, die von den GrammaNken des Chomsky-­‐Typs i <br />

erzeugt werden kann, dann ist <br />

Rekursiv aufzählbare Sprachen<br />

Kontextsensitive Sprachen<br />

Kontextfreie Sprachen<br />

Reguläre Sprachen<br />

L 0 ⊃ L 1 ⊃ L 2 ⊃ L 3


Alphabet und Sprache in der InformaNk 1/3 <br />

10 <br />

o Im Sinne der MathemaNk und InformaNk ist eine Sprache eine <br />

Menge von Zeichenreihen. Die Sprache wird dabei auf Lexik und <br />

Syntax reduziert. Für Lexik wird der Begriff Alphabet verwendet. <br />

o Ein Alphabet ist eine nicht leere, endliche Menge A von Zeichen a <br />

∈ A. <br />

A1 = {A, ..., Z} <br />

A2= {a} <br />

A3 = {A, ..., Z, a, ..., z, 0, ..., 9}<br />

A4= {#, 0}


Alphabet und Sprache in der InformaNk 2/3 <br />

11 <br />

o Die Menge aller Zeichenreihen A* über einem Alphabet ist <br />

definiert durch: <br />

¤<br />

¤<br />

¤<br />

ε ∈ A* die leere Zeichenreihe ist eine Zeichenreihe. <br />

x ∈A*, a ∈ A* ⇒ x ° a ∈ A* („° “ ist der Verke^ungsoperator). <br />

Andere Zeichenreihen gibt es nicht. <br />

Konkatenation<br />

ATA, ATTTNBNM ∈ A1* <br />

a, aa, aaa .... ∈ A2* <br />

Paul, Omega, x12 ∈ A3* #, #0#000 ∈ A4* <br />

o Eine Sprache L (L, wie engl. language) ist eine Teilmenge aller <br />

Zeichenreihen über einem Alphabet A: L ⊂ A* <br />

Deutsche Sprache ⊂ {A, ..., Z, a, ..., z, ß, 0, ..., 9, ?, !, ...}*


Alphabet und Sprache in der InformaNk 3/3 <br />

12 <br />

Wie werden die Zeichenreihen einer besNmmten Sprache <br />

beschrieben? <br />

a) Aufzählung aller Zeichenreihen oder Wörter, die zur Sprache gehören (das ist nur bei <br />

endlichen Sprachen möglich!). <br />

L 1 = {a, aaa, aaaaa} <br />

b) MathemaKsche Charakterisierung der zur Sprache gehörenden Wörter als Menge <br />

(sog. formale Sprachen). <br />

L 2 = {a n | n ≥ 3} oder L 3 = {a n b n c n | n ≥ 1} <br />

c) GrammaKken: sind eine endliche konstrukNve Beschreibung einer im allgemeinen <br />

unendlichen Menge. <br />

Damit werden Programmiersprachen beschrieben.


Kontexzreie GrammaNk 1/2 <br />

13 <br />

Eine GrammaKk zur Beschreibung von Syntax ist ein 4-­‐Tupel: <br />

G = (T, N, P, S) <br />

T: Menge von Token, sog. Terminalsymbole (TS). <br />

N: Menge von Nonterminalsymbolen (NTS). <br />

P: Menge von ProdukKonen (oder ProdukNonsregeln), wobei jede <br />

ProdukNon aus einem Nonterminalsymbol (linke Seite der <br />

ProdukNon) einem Pfeil (→) und einer Folge von <br />

Terminalsymbolen und/oder Nonterminalsymbolen (rechte <br />

Seite der ProdukNon) besteht. <br />

S: Ein ausgezeichnetes Nonterminalsymbol -­‐ das Startsymbol.


Kontexzreie GrammaNk 2/2 <br />

14 <br />

Die Sprache L(G) einer GrammaKk besteht aus allen aus dem Startsymbol S <br />

abgeleiteten Zeichenke^en (Wörtern), die nur Terminalsymbole enthalten. <br />

Ein Wort ist eine Folge von Terminalsymbolen, die durch wiederholtes <br />

Anwenden von Regeln erzeugt werden kann, wobei das Startsymbol S der <br />

Ausgangspunkt der Erzeugung ist. <br />

GrammaNken werden in zweierlei Hinsicht genutzt: <br />

¤<br />

Um Worte einer Sprache zu erzeugen. <br />

Ableiten<br />

¤<br />

Um festzustellen, ob ein gegebenes Wort zur Sprache gehört. <br />

Analysieren<br />

Es gibt verschiedene Formalismen zur Beschreibung von GrammaNken: <br />

¤<br />

¤<br />

¤<br />

¤<br />

Reguläre Ausdrücke (einfacher Mechanismus, nicht für alles geeignet) <br />

Backus Naur Form (BNF). <br />

Erweiterte Bauckus Naur Form (EBNF). <br />

Syntaxdiagramme.


Reguläre Sprachen -­‐ DefiniNon <br />

15 <br />

Die regulären Sprachen über dem Alphabet A werden durch folgende Regeln <br />

indukNv definiert: <br />

i. ∅ und {ε} sind reguläre Sprachen. <br />

ii.<br />

iii.<br />

iv.<br />

Für jedes a ∈ A ist {a} eine reguläre Sprache. <br />

Seien B und C reguläre Sprachen, dann sind auch <br />

B ∪ C, BC und B * reguläre Sprachen. <br />

Nichts sonst ist eine reguläre Sprache über A. <br />

Ein regulärer Ausdruck r beschreibt eine reguläre Sprache L(r).


Reguläre Ausdrücke -­‐ DefiniNon <br />

16 <br />

Reguläre Ausdrücke über dem Alphabet A werden durch folgende Regeln indukNv <br />

definiert: <br />

i. ∅ ist ein regulärer Ausdruck, der die reguläre Sprache ∅ beschreibt. <br />

ε ist ein regulärer Ausdruck, der die reguläre Sprache { ε } beschreibt <br />

ii.<br />

iii.<br />

iv.<br />

Für jedes a ∈ A ist a ein regulärer Ausdruck; er beschreibt die Sprache {a}. <br />

Wenn a und b reguläre Ausdrücke sind, die die Sprachen A und B <br />

beschreiben, so ist auch <br />

-­‐<br />

-­‐<br />

-­‐<br />

(a | b ) ein regulärer Ausdruck, der A ∪ B beschreibt, <br />

ab ein regulärer Ausdruck, der AB beschreibt, <br />

a * ein regulärer Ausdruck, der A * beschreibt. <br />

Nichts sonst ist eine regulärer Ausdruck


Reguläre Ausdrücke – vereinfachende NotaNonen 1/2 <br />

17 <br />

Benennung von regulären Ausdrücken <br />

Eine reguläre DefiniNon hat die Form <br />

d 1 → r 1 <br />

d 2 → r 2 <br />

Name<br />

Name<br />

Regulärer Ausdruck über<br />

einem Alphabet A<br />

Regulärer Ausdruck über A<br />

und d 1<br />

. . <br />

d 3 → r 3 <br />

Name<br />

. . . . . <br />

etc. <br />

Regulärer Ausdruck über A<br />

und d 1 , d 2<br />

In r i dürfen nur die Namen d 1 , d , . . ., d i-­‐1 vorkommen.


Reguläre Ausdrücke – vereinfachende NotaNonen 2/2 <br />

18 <br />

o Zur Unterscheidung von Namen und Symbolen, werden Namen fe^ <br />

geschrieben. <br />

Priorität<br />

o Die Auswahl ermöglicht die Wahl zwischen 2 AlternaNven: <br />

a | b oder auch a + b (eher ungewöhnlich). <br />

o Die Sequenz (KonkatenaNon) beschreibt das hintereinander Schreiben: ab. <br />

o Die IteraKon ermöglicht das Wiederholen von Satzbausteinen: a* (0, 1 oder <br />

n-­‐mal) oder a + (1 oder n-­‐mal) ≡ aa* . <br />

o OpNonale Satzbausteine: a? Abkürzung für a | ε (eher ungewöhnlich). <br />

o Zusätzlich besteht die Möglichkeit der Klammerung zur Strukturierung. <br />

Ansonsten gilt „Punkt-­‐ vor Strichrechnung“. <br />

o Zeichenklassen: Sta^ char → a | b | c | etc. schreibt man als Abkürzung <br />

auch char → [a – z].


Reguläre Ausdrücke -­‐ Beispiele <br />

19 <br />

o Binärzahlen beginnen mit 1, danach kann eine beliebig lange Folge <br />

von 1 und 0 kommen: <br />

1 (1 + 0)* ⇒ 1, 10, 1010, 100000001111 <br />

o Will man auch noch die 0, als einzige mit diesem Symbol startende <br />

Zahl: <br />

0 + 1 (1 + 0)* ⇒ 0, 1, 10, 1010, 100000001111 <br />

o Bezeichner einer Programmiersprache müssen mit einem <br />

Buchstaben beginnen, dürfen nach dem ersten Buchstaben aber <br />

auch Ziffern enthalten: <br />

(a + b + ... + z) (a + b + ... + z + 0 + ... + 9)* ⇒ a, COI, ma07, u2 <br />

o Verwendung von Regulären Ausdrücken in der InformaNk: <br />

¤<br />

¤<br />

Festlegung von Datenformaten für Programmeingaben. <br />

Festlegen von Mustern zum Suchen in Texten.


Zustandsdiagramme <br />

20 <br />

Zustandsdiagramme (transiNon diagrams) sind <br />

• eine graphische NotaNon für determinisNsche endliche Automaten. <br />

• beschreiben die AkNon, die der Scanner bei der Anforderung des nächsten <br />

Token durch den Parser ausführt. <br />

• Die Elemente von Zustandsdiagrammen sind <br />

• Kreise, welche die Zustände bezeichnen. <br />

• Gerichtete Kanten, welche die Zustände miteinander verbinden. <br />

• Die von einem Zustand herausführenden Kanten haben eine Markierung (label). <br />

Das sind die Zeichen, mit denen man in den nächsten Zustand gelangt. <br />

• Es gibt einen ausgezeichneten Startzustand und mindestens einen <br />

ausgezeichneten Endzustand. <br />

Gerichteter,<br />

markierter<br />

Graph


Zustandsdiagramme – Beispiel 1 <br />

21 <br />

a<br />

start<br />

a<br />

0 1<br />

b<br />

b<br />

2


Zustandsdiagramme – Beispiel 2: relaNonale Operatoren <br />

22 <br />

start<br />

< =<br />

0 1<br />

2<br />

return (relop, LE)<br />

><br />

3<br />

return (relop, NE)<br />

=<br />

other<br />

4<br />

*<br />

return (relop, LT)<br />

><br />

5<br />

=<br />

other<br />

6<br />

return (relop, EQ)<br />

return (assop, ...)<br />

7<br />

= 8<br />

other<br />

9<br />

*<br />

return (relop, GE)<br />

return (relop, GT)


Endlicher Automat <br />

23 <br />

Eingabe<br />

aktueller Zustand<br />

Steuerung


Erkennung von Token 1/3 <br />

24 <br />

Der Scanner liest das Quellprogramm zeichenweise ein, um aus den einzelnen Zeichen<br />

die Token aufzubauen.<br />

Dazu wird das Quellprogramm blockweise (gepufferte E/A) von Datei in den<br />

Arbeitsspeicher geladen.<br />

Quellprogramm<br />

Puffer<br />

Scanner arbeitet hier


Erkennung von Token 2/3 <br />

25 <br />

Eingrenzen der Lexeme im Puffer mit Hilfe zweier Zeiger:<br />

vorher:<br />

i f ( a < b<br />

anfang<br />

ende<br />

nachher:<br />

i f ( a < b<br />

anfang<br />

ende<br />

Rück-setzen:<br />

i f ( a < b<br />

anfang ende Nächstes Token


Erkennung von Token 3/3 <br />

26 <br />

Fortsetzung:<br />

i f ( a < b<br />

anfang<br />

ende<br />

Problem: Wenn das Pufferende erreicht ist, ohne dass bis dahin ein Token erkannt<br />

wurde.<br />

Lösung: 2 Puffer, die abwechselnd gefüllt werden. Die beiden Zeiger laufen über<br />

beide Puffer im Kreis.


Token, Lexeme und Pa^ern <br />

27 <br />

Token<br />

interne Darstellung (für Parser) – Aufbau ist durch Regeln<br />

beschrieben.<br />

Pattern Menge von Strings, die durch eine Regel beschrieben werden und<br />

mit einem Token verknüpft sind.<br />

Lexem konkrete Ausprägung eines Pattern für einen Token.<br />

Merke: Verschiedene Strings (Pattern) können auf dasselbe Token<br />

abgebildet werden.<br />

Bsp.: const pi = 3.1416;<br />

Token = id<br />

Lexem = pi


Token, Lexeme und Pa^ern -­‐ Beispiele <br />

28 <br />

Token Ausprägung (Lexem) Pattern-Beschreibung<br />

const final final<br />

if if if<br />

rel_op = < oder oder >=<br />

id pi, count Buchstabe gefolgt von<br />

Buchstaben-Ziffern<br />

Kombination<br />

num 3.1416, 0, 6.0E23 numerische Konstante<br />

literal “any string“ Zeichen zwischen “<br />

und “ außer “


Interne Darstellung lexikalischer Elemente 1/3 <br />

29 <br />

Für jedes Lexem wird ein Paar ( Kategorie, Index) erzeugt.<br />

Typische Kategorien: Bezeichner id<br />

Zahlen num<br />

Zeichenketten string<br />

Schlüsselworte key<br />

Beispiel:<br />

if (max == 4711)<br />

name = “Madonna“;<br />

Operatoren mulop<br />

addop<br />

relop<br />

Trennzeichen delimiter


Interne Darstellung lexikalischer Elemente 2/3 <br />

30 <br />

Der Index kennzeichnet ein lexikalisches Element entweder innerhalb seiner<br />

Kategorie oder er ist eine Referenz in die Symboltabelle.<br />

Beispiel: (relop, 1) à <<br />

(relop, 2) à =<br />

(relop, 6) à ><br />

Kategorie:<br />

Vergleichsoperator<br />

à EQUAL -<br />

Operation<br />

(mulop, 1) à *<br />

(mulop, 2) à /


31 <br />

Interne Darstellung lexikalischer Elemente -­‐ <br />

Symboltabelle 3/3 <br />

Symbol Attribute<br />

Index<br />

3 if<br />

... ...<br />

14 max<br />

... ...<br />

40 4711<br />

... ...<br />

51 name<br />

... ...<br />

63 “Madonna“<br />

Beispiel:<br />

if (max == 4711)<br />

name = “Madonna“;<br />

Beispiel:<br />

(key, 3)<br />

(id, 14)<br />

(rel_op, 3)<br />

(num, 40)<br />

(id, 51)<br />

(string, 63)


ImplemenNerung eines Übergangsdiagramms 1/3 <br />

32 <br />

Direkte Umsetzung mittels einer Variablen state.<br />

Die Zustände werden ab 0 beginnend über alle Zustandsdiagramme<br />

durchnummeriert.<br />

letter | digit<br />

start<br />

letter<br />

0 1<br />

other<br />

2<br />

< =<br />

3 4<br />

5<br />

Idee: Scanner versucht Diagramme in der Reihenfolge<br />

andere<br />

ihrer Auflistung<br />

Diagramme<br />

zu<br />

durchlaufen.<br />

Falls ein Fehler auftritt, erfolgt der Übergang zum Start des nächsten Diagramms.<br />

Dabei ist der Zeiger end im Puffer auf start zurückzusetzen.


ImplemenNerung eines Übergangsdiagramms 2/3 <br />

33 <br />

iniNalisiere start, state <br />

while kein markierter Endzustand erreicht do <br />

case state of <br />

0: lies_zeichen (zeichen) <br />

if zeichen ist Buchstabe then state := 1 <br />

else fehler <br />

1: lies_zeichen (zeichen) <br />

if zeichen ist Buchstabe oder Ziffer then state := 1 <br />

else state := 2 <br />

2: setze ende eine PosiNon zurück // Endzustand erreicht <br />

teste in Symboltabelle, ob Schlüsselwort / Bezeichner gefunden, falls noch nicht <br />

vorhanden, trage es ein, gib entsprechendes Element zurück; markiere Endzustand.


34 <br />

end<br />

end<br />

ImplemenNerung eines Übergangsdiagramms 3/3 <br />

3: lies_zeichen (zeichen)<br />

if zeichen = ‘


NichtdeterminisNsche endliche Automaten <br />

35 <br />

o<br />

Ein nichtdeterminisNscher endlicher Automat (NEA oder NFA) ist ein <br />

o<br />

Tupel M = (A, MOVE, S, s 0 , F), wobei <br />

• A ein endliches Alphabet von Eingabezeichen ist, <br />

• S eine endliche Menge von Zuständen (state) ist, <br />

• s 0 ∈ S der Anfangszustand ist, <br />

• F ⊆ S die Menge der Endzustände (final states) ist, und <br />

• MOVE die ÜbergangsrelaKon, die (Zustands, Symbol)-­‐Paare mit einer Menge <br />

von Zuständen verbindet, ist. <br />

MOVE : (state, symbol) à {state} bzw. MOVE : S x (A ∪ {ε} )à {S}


NFA -­‐ Beispiel <br />

36 <br />

Hinweise:<br />

• Dasselbe Eingabesymbol kann mehrere Übergänge eines Zustands markieren.<br />

• Die Übergänge können sowohl mit ε als auch mit Eingabesymbolen markiert<br />

sein.<br />

NFA der die Sprache: (a | b)*abb erkennt.<br />

a<br />

start<br />

0<br />

a<br />

b<br />

b<br />

1 2 3<br />

b


NFA -­‐ Anmerkungen <br />

37 <br />

o<br />

o<br />

Ein NFA akzepNert einen Eingabestring x, genau dann wenn es einen Pfad vom <br />

Startzustand zu einem Endzustand im Zustandsdiagramm gibt, dessen <br />

Kantenmarkierungen zusammen gefasst x ergeben. <br />

Da die Zustandsübergänge nicht eindeuNg sind, gibt es immer mehrere <br />

mögliche AlternaNven bei der Auswahl eines Übergangs. Wenn man den <br />

„falschen“ Übergang auswählt, kann es passieren, dass ein eingeschlagener <br />

Pfad in die Irre führt (à MehrdeuNgkeit, d.h. Nicht-­‐determinismus). Der NFA <br />

muss „raten“, welchen Pfad er einschlagen soll. Hat der NFA „falsch geraten“, <br />

würde ein korrekter Eingabestring nicht akzepNert. Man muss dann sehr viele <br />

AlternaNven (à Backtracking) ausprobieren, um zu einer Entscheidung zu <br />

kommen.


DeterminisNsche endliche Automaten <br />

38 <br />

o<br />

Ein determinisNscher endlicher Automat (DEA oder DFA) ist ein spezieller Fall <br />

eines nichtdeterminisNschen endlichen Automaten, für den gilt: <br />

• Es gibt keinen Zustand mit einem ε-Übergang, d.h. keinen Übergang ohne ein <br />

Eingabesymbol zu verarbeiten. <br />

• Für jeden Zustand s und jedes Eingabesymbol a gibt es höchstens eine Kante <br />

von s ausgehend, die mit a markiert ist. <br />

Die ÜbergangsfunkKon MOVE verbindet die (Zustands, Symbol)-­‐Paare mit <br />

einem Zustand. <br />

MOVE : (state, symbol) à state bzw. MOVE : S x A à S.


DFA -­‐ Beispiel <br />

39 <br />

Hinweise:<br />

• Dasselbe Eingabesymbol kann nur einen Übergang eines Zustands markieren.<br />

• Die Übergänge können nicht mit e markiert sein.<br />

DFA, der die Sprache: (a | b)*abb erkennt.<br />

b<br />

b<br />

start<br />

0<br />

a<br />

b<br />

b<br />

1 2 3<br />

a<br />

a<br />

a


RA, NFA und DFA <br />

40 <br />

o<br />

Warum betrachtet man überhaupt die NFA? <br />

o<br />

Für die (direkte) ImplemenNerung eines Scanners benöNgt man einen DFA und <br />

den kann man immer angeben. <br />

o<br />

o<br />

Für Erzeugung (Generierung) man eines Scanners benöNgt man reguläre <br />

Ausdrücke (RA) als Metasprache. <br />

Dann kommen folgende Sätze zur Anwendung: <br />

1. Zu jedem regulären Ausdruck r gibt es einen NFA, der die von r beschriebene <br />

reguläre Menge akzepNert. <br />

2. Wird eine Sprache von einem NFA akzepNert, so gibt es einen DFA, der L <br />

akzepNert.


Generierung eines Scanners mit LEX <br />

41 <br />

lex.l<br />

Lex-<br />

Spezifikation<br />

Reguläre Ausdrücke für<br />

Sprache L<br />

Lex<br />

lex.yy.c<br />

Scanner in C-<br />

Code<br />

Scanner<br />

C-Compiler<br />

yyin<br />

Quellpro-gramm<br />

in L<br />

yylex<br />

Scanner<br />

Tokenfolge


Lex DeklaraNonsdatei -­‐ Struktur <br />

42 <br />

Deklarationen<br />

- in der Wirtssprache (globale Vereinbarungen)<br />

- Ersetzungsdeklarationen<br />

- Startzustände (Anfangsbedingungen)<br />

- Zeichensatzdeklarationen<br />

- Festlegungen für Lex-Tabellen<br />

%%<br />

Tokendefinitionen<br />

- Lex-Regeln und Aktionen (in der Wirtssprache)<br />

%%<br />

Hilfsprozeduren (zusätzlicher Programmcode in der Wirtssprache)


Lex -­‐ DeklaraNonsteil <br />

43 <br />

Deklarationen sind z.B. Konstanten als Tokendarstellungen<br />

%{<br />

#define IDENTIFIER 1000<br />

#define REL_OP 1100<br />

#define LT_OP 1101<br />

. . .<br />

%}<br />

und reguläre Definitionen<br />

sign [+-]<br />

digit [0-9]<br />

letter<br />

[A-Za-z]<br />

globale<br />

Deklarationen<br />

C-Makroprozessoranweisungen<br />

Lex Deklarationen<br />

Basiselemente


Lex – Regeln und AkNonen <br />

44 <br />

Paare (Reguläre Ausdrücke; auszuführende Aktion)<br />


Lex -­‐ Regelbearbeitung <br />

45 <br />

o Lex versucht mit seinem Regelsystem im (sequenNellen) Eingabestrom (Datei <br />

yyin) das nächste Lexem zu finden. <br />

o Bei einem Regeldurchlauf können auch mehrere oder gar kein Lexem gefunden <br />

werden. <br />

o Das Ergebnis kann sein: <br />

o Die Regel, die das längst mögliche Lexem beschreibt, wird als erste Wahl <br />

ermi^elt und der rechte Teil der Regel (AkNonen in C) wird ausgeführt. <br />

o Mehrere Regeln können gleich lange Lexeme erkennen. In diesem Fall wird die <br />

zuerst platzierte Regel erste Wahl und ausgeführt. <br />

o Mit dem nächsten Eingabezeichen kann das Regelsystem kein Lexem <br />

erkennen. Der Text wird dann solange zeichenweise in die Standardausgabe <br />

(Datei yyout) kopiert, bis das Regelsystem ein neues Lexem findet.


Lex -­‐ NotaNon der RA 1/4 <br />

46 <br />

Zeichen vs. Metazeichen<br />

Die Zeichen zur Beschreibung der regulären Ausdrücke nennt man Metazeichen bzw.<br />

Metasymbole.<br />

Metasymbole:<br />

. $ ^ [ ] - ? * + | ( ) / { } < > “ \<br />

Alle anderen Zeichen stehen für sich selbst. Will man ein Metasymbol als normales<br />

Zeichen benutzen à einschließen in “ “


Lex -­‐ NotaNon der RA 2/4 <br />

47 <br />

Metasymbol Bedeutung Beispiel<br />

[ ] Zeichenklassen [aby&]<br />

- Bereich [A-Z]<br />

^ Komplement [^0-9]<br />

? optional [-+]?<br />

| Alternative a|bc<br />

+ ein- oder mehrmals {digit}+<br />

* 0 oder mehrmals {letter}*<br />

() normale Klammerung (a|bc)*


Lex -­‐ NotaNon der RA 3/4 <br />

48 <br />

Metasymbol Bedeutung Beispiel<br />

. alle Zeichen außer<br />

newline (\n)<br />

^ $ Ausdruck am Zeilenan- ^hallo \t$<br />

fang oder –ende<br />

/ Vorausschau -/{digit}+<br />

{ } Kennzeichen eines {digit}<br />

regulären Symbols<br />

“ \ Escape-Zeichen “....“ \“<br />

Whitespaces müssen in Anführungszeichen gesetzt werden.<br />

Konkatenation: Hintereinanderschreiben<br />

Es gibt weitere Bedeutungen der Metasymbole!


Lex -­‐ NotaNon der RA 4/4 <br />

49 <br />

Metasymbol Bedeutung Beispiel<br />

{ } Anzahl der Wieder- [A-Z] {3}<br />

holungen eines<br />

regulären Ausdrucks A {2, 5}<br />

Pattern wird nur im Zustand<br />

betrachtet.<br />

A {1, }<br />

A {0, }<br />

erkennt end of file nur in flex


Prioritäten der Metasymbole <br />

50 <br />

|<br />

{ }<br />

Konkatenation<br />

* + ?<br />

( ) /<br />

<br />

“ \<br />

Die anderen Metasymbole werden nur alleine verwendet.


Einige FunkNonen der Lex-­‐Bibliothek <br />

51 <br />

yylex(): Aufruf von Lex.<br />

input(): Liefert das nächste Zeichen des Eingabestroms.<br />

unput(c):<br />

yymore():<br />

Schreibt das Zeichen c in den Eingabestrom zurück (d.h. in den<br />

sog. Rückstell-Stack)<br />

Dieses Zeichen wird beim nächsten Regeldurchlauf oder input<br />

()-Aufruf zurück geliefert.<br />

Funktion verhindert das Löschen der Variablen yytext bei<br />

Regelabschluss. Die Zeichenkette, die durch die nächste Regel<br />

erkannt wird, wird an das Ende der aktuellen yytext-<br />

Zeichenkette angehängt.<br />

yyless(anz): Rückstellen von Zeichen des Eingabestroms. Von den erkannten<br />

Zeichen sollen nur die Zeichen 1..anz erhalten bleiben, der Rest<br />

wird in den Rückstellstack zurück geschoben.


52 <br />

identifier<br />

integer<br />

Pascal-Kommentar<br />

Whitespace<br />

float<br />

Beispiele für Lex-­‐Regeln <br />

[A-Za-z][A-Za-z0-9_]*<br />

[-+]?[0-9]+<br />

\{[^\}]*\}<br />

[ \t\n]<br />

[-+]?[0-9]+(/.[0-9]+)?<br />

(E[-+]?[0-9]+)?


Lex-­‐Beispielprogramm 1/2 <br />

53 <br />

%{<br />

/* Zählen: Lexeme, Zeichen und Zeilen */<br />

int anz_zeichen = 0, anz_lexeme = 0, anz_zeilen = 0;<br />

%}<br />

%%<br />

[^ \t\n]+ {/* kein Blank, Tab, nl */<br />

++anz_lexeme;<br />

anz_zeichen += yyleng; }<br />

. { /* bel. Zeichen außer nl */<br />

++anz_zeichen; }<br />

\n { /* nl */<br />

++anz_zeichen; ++anz_zeilen;}<br />

%%


Lex-­‐Beispielprogramm 2/2 <br />

54 <br />

main () {<br />

yylex(); /* Aufruf von Lex */<br />

/* Lex erkennt o. spezifizierte Token bis<br />

eof der Eingabe */<br />

printf (“Anz. Zeichen =\t%d\n“<br />

“Anz. Zeilen =\t%d\n“<br />

“Anz. Lexeme =\t%d\n“,<br />

anz_zeichen, anz_zeilen, anz_lexeme);<br />

}


55 <br />

Lex -­‐ Auswertung der Kontextbeziehungen des <br />

Eingabestrings <br />

Linker<br />

Kontext<br />

Eingabestring<br />

Rechter<br />

Kontext<br />

Zugriff auf linken Kontext:<br />

• Merkvariablen auf der Aktionsseite (im C-Code).<br />

• Anfangsbedingungen (Startzustände- start states)<br />

Zugriff auf rechten Kontext:<br />

• Lookahead-Metasymbol / (in Lex-Regeln)<br />

• yyless(anz) – Funktionsausfruf (Aktionsseite – C-Code)<br />

• REJECT – Makroaufruf (Aktionsseite – C-Code)


Lex – REJECT-­‐Mechanismus <br />

56 <br />

• REJECT – Makroaufruf<br />

Weist die Regel und die erkannten Inputzeichen zurück.<br />

Danach wird die nächste passende Regel gesucht.<br />

Gibt es in mehreren Regeln REJECT-Aufrufe, werden diese jeweils zurückgewiesen und<br />

die nächsten passenden Regeln der Reihe nach gesucht.<br />

Terminierung: entweder eine Regel ohne REJECT-Aufruf oder „konsumieren“ des ersten<br />

Zeichens des Inputs per default-Regel.


Lex – REJECT Beispiel <br />

57 <br />

%%<br />

[a-z]* { /* Regel 1 */<br />

printf("Regel 1 mit %s\n", yytext);<br />

REJECT;<br />

}<br />

[a-z][a-z] { /* Regel 2 */<br />

printf("Regel 2 mit %s\n", yytext);<br />

REJECT;<br />

}<br />

[a-z] { /* Regel 3 */<br />

printf("Regel 3 mit %s\n", yytext);<br />

REJECT;<br />

}<br />

%%

Hurra! Ihre Datei wurde hochgeladen und ist bereit für die Veröffentlichung.

Erfolgreich gespeichert!

Leider ist etwas schief gelaufen!