44
Martin Schneider, 25.05.2000 Folien von Prof. H.-P. Gumm Parsen

Martin Schneider, 25.05.2000 Folien von Prof. H.-P. Gumm Martin Schneider, 25.05.2000 Folien von Prof. H.-P. Gumm Parsen

Embed Size (px)

Citation preview

Page 1: Martin Schneider, 25.05.2000 Folien von Prof. H.-P. Gumm Martin Schneider, 25.05.2000 Folien von Prof. H.-P. Gumm Parsen

Martin Schneider, 25.05.2000

Folien von Prof. H.-P. Gumm

Martin Schneider, 25.05.2000

Folien von Prof. H.-P. Gumm

Parsen

Page 2: Martin Schneider, 25.05.2000 Folien von Prof. H.-P. Gumm Martin Schneider, 25.05.2000 Folien von Prof. H.-P. Gumm Parsen

Der zweistufige Aufbau von Sprachen

Richtige Sprachen ( Deutsch, Englisch, Prolog, Pascal, ... ) lassen sich in zwei Stufen beschreiben :

Richtige Sprachen ( Deutsch, Englisch, Prolog, Pascal, ... ) lassen sich in zwei Stufen beschreiben :

Aus einem Zeichenalphabet (etwa = {a, b, ... ,z, A,B, ...,Z}

wird der lexikalische Anteil der Sprache aufgebaut,

die Menge aller Wörter der Sprache. Dies ist eine

Teilmenge von *, also eine Sprache.

1. Stufe1. Stufe

Worte, die eine gleichartige Rolle spielen, wie etwa Bezeichner oder Integerzahlen werden zu Klassen zusammengefaßt. Jede solche Klasse wird durch einen Stellvertreter (token) repräsentiert.

PASCAL-token sind z.B. : Identifier, IntegerConstant, RealConstant, BoolConstant, ... IF, THEN, :=, ...

Die Klasse jedes Tokens wird durch einen regulären Ausdruck beschrieben, ist also selber eine reguläre Sprache

Die Klasse jedes Tokens wird durch einen regulären Ausdruck beschrieben, ist also selber eine reguläre Sprache

Page 3: Martin Schneider, 25.05.2000 Folien von Prof. H.-P. Gumm Martin Schneider, 25.05.2000 Folien von Prof. H.-P. Gumm Parsen

Der zweistufige Aufbau von Sprachen

Aus einem Alphabet der Token (etwa

= {id, intConst, addop, assign, IF, THEN, VAR, INTEGER, ...}

wird der syntaktische Anteil der Sprache aufgebaut,

die Menge aller möglichen Sätze der Sprache.

Dies ist eine Teilmenge von *, also eine Sprache über .

2. Stufe2. Stufe

Letztere Sprache ist i.A. nicht mehr regulär, man benutzt zu ihrer Beschreibung entweder “kontextfreie Grammatiken”, BNF (Backus-Naur-Form) oder Syntaxdiagramme.

Beispiel in Pascal : VarDecl ::= VAR id (, id)* colon TypeType ::= INTEGER, BOOLEAN, RECORD Fields ENDFields ::= . . .

Page 4: Martin Schneider, 25.05.2000 Folien von Prof. H.-P. Gumm Martin Schneider, 25.05.2000 Folien von Prof. H.-P. Gumm Parsen

Sprachen und Grammatiken

Eine Grammatik ist ein mathematisches Hilfsmittel, um Sprachen zu beschreiben. Eine Grammatik ist eine Menge von Regeln mit deren Hilfe Worte (Sätze) der Sprache konstruiert werden können.

Eine Grammatik ist ein mathematisches Hilfsmittel, um Sprachen zu beschreiben. Eine Grammatik ist eine Menge von Regeln mit deren Hilfe Worte (Sätze) der Sprache konstruiert werden können.

Mit Hilfe der Grammatik kann man Sätze einer Sprache erzeugen. Die zu der Grammatik G gehörende Sprache, L(G) , besteht aus allen Sätzen die man mit der Grammatik erzeugen kann.

Erzeugen

Ist ein Satz gegeben, so möchte man evtl. feststellen , ob dieser Satz zu der Sprache L(G) gehört oder nicht. Diese Aufgabe nennt man “Parsen”. Dabei wird auch die Struktur des Satzes erkannt.

Parsen

Page 5: Martin Schneider, 25.05.2000 Folien von Prof. H.-P. Gumm Martin Schneider, 25.05.2000 Folien von Prof. H.-P. Gumm Parsen

Syntax - Semantik

Eine Grammatik beschreibt die Struktur ( Syntax ) von Sätzen, nicht aber deren Bedeutung (Semantik). Obwohl die Bedeutung eines Satzes auch von seiner Struktur abhängt, kann man leicht syntaktisch richtige Sätze bilden, die bedeutungslos sind.

Eine Grammatik beschreibt die Struktur ( Syntax ) von Sätzen, nicht aber deren Bedeutung (Semantik). Obwohl die Bedeutung eines Satzes auch von seiner Struktur abhängt, kann man leicht syntaktisch richtige Sätze bilden, die bedeutungslos sind.

gestohlen der hat Gans Fuchs die.

Syntaktisch falscher Satz

der Gans hat die Fuchs gestohlen.

Syntaktisch richtiger, semantisch falscher Satz

der Fuchs hat die Gans gestohlen.

Syntaktisch und semantisch richtiger Satz

Page 6: Martin Schneider, 25.05.2000 Folien von Prof. H.-P. Gumm Martin Schneider, 25.05.2000 Folien von Prof. H.-P. Gumm Parsen

Syntax - Semantik in PASCAL

Test Program. i for 1 do n ; to n+1 := n

Syntaktisch falsches Programm

Program Test ; Var i : Real; Begin For i := 1 To n Do i := n+1 End .

Syntaktisch richtiges, semantisch falsches Programm

Syntaktisch und semantisch richtiges Programm

Program Test ; Var i,n : Integer; Begin For i := 1 To n Do n := i+1 End .

Page 7: Martin Schneider, 25.05.2000 Folien von Prof. H.-P. Gumm Martin Schneider, 25.05.2000 Folien von Prof. H.-P. Gumm Parsen

ParsenParsen ist der Prozeß, ein gegebenes Wort w L(G) in seine grammatikalischen Bestandteile zu zerlegen und einen Herleitungsbaum zu finden.

Parsen ist der Prozeß, ein gegebenes Wort w L(G) in seine grammatikalischen Bestandteile zu zerlegen und einen Herleitungsbaum zu finden.

if x < 0 then y := y+1 else Inc(z)

if thenx 0< := +y y 1 else ( )Inc z

if Bexprthen

ExprRelOp

Expr

id num

Stmt elseStmt

id :=Expr

id

+num

id( )Expr

idx 0

<y

y 1

Inc

z

Stmt

Beispiel:

Page 8: Martin Schneider, 25.05.2000 Folien von Prof. H.-P. Gumm Martin Schneider, 25.05.2000 Folien von Prof. H.-P. Gumm Parsen

Bottom Up Parsing

Beim Parsen wird der zu analysierende Satz von links nach rechts gelesen und gleichzeitig analysiert. Dabei wird der Syntaxbaum von den Blättern zur Wurzel (bottom-up) aufgebaut.

Beim Parsen wird der zu analysierende Satz von links nach rechts gelesen und gleichzeitig analysiert. Dabei wird der Syntaxbaum von den Blättern zur Wurzel (bottom-up) aufgebaut.

if then else Inc ( z )x > 0

Bexpr

y + 1y :=

Expr

Stmt

Expr

id

Expr

numidid num

Lesepointer

Page 9: Martin Schneider, 25.05.2000 Folien von Prof. H.-P. Gumm Martin Schneider, 25.05.2000 Folien von Prof. H.-P. Gumm Parsen

Top Down Parsing

Beim top down parsing beginnt man an der Wurzel des Baumes und baut ihn von oben nach unten auf. Dabei gibt das jeweils am weitesten rechts stehende Blatt an, was man im Input zu sehen wünscht.

Beim top down parsing beginnt man an der Wurzel des Baumes und baut ihn von oben nach unten auf. Dabei gibt das jeweils am weitesten rechts stehende Blatt an, was man im Input zu sehen wünscht.

if then y := y + 1 else Inc ( z )

Stmt

Bexpr

Expr

x > 0

num

Expr

id

Stmt

Page 10: Martin Schneider, 25.05.2000 Folien von Prof. H.-P. Gumm Martin Schneider, 25.05.2000 Folien von Prof. H.-P. Gumm Parsen

Shift-Reduce Parser

Shift-Reduce Parser sind "bottom-up parser", d.h. sie bauen den Syntaxbaum von den Blättern her auf. Die Aktionen sind :

Shift-Reduce Parser sind "bottom-up parser", d.h. sie bauen den Syntaxbaum von den Blättern her auf. Die Aktionen sind :

Lese ein weiteres Zeichen aus dem Input

Die rechte Seite einer Produktion wurde im Input erkannt. Ersetze sie durch die linke Seite.

Der Input kann dabei eine beliebige Satzform sein.Der Input kann dabei eine beliebige Satzform sein.

ShiftShift

ReduceReduce

Page 11: Martin Schneider, 25.05.2000 Folien von Prof. H.-P. Gumm Martin Schneider, 25.05.2000 Folien von Prof. H.-P. Gumm Parsen

Shift-Reduce - Parsing

Der gegenwärtige String besteht aus der Satzform

if Bexpr then id := Expr

das nächste Token (lookahead) ist ein else.

Der gegenwärtige String besteht aus der Satzform

if Bexpr then id := Expr

das nächste Token (lookahead) ist ein else.

if then else ??? ? ? ?x > 0

Bexpr

y + 1y :=

ExprExpr

id

Expr

numidid

num

Lesepointer

Lese nächstesToken

ShiftShift

Ersetze id := Expr durch Stmt.

ReduceReduce

Wegen des Lookahead Tokens “else” ist ein Reduce angebracht.Wäre das lookahead ein `+` , so müßte geshiftet werden.

Page 12: Martin Schneider, 25.05.2000 Folien von Prof. H.-P. Gumm Martin Schneider, 25.05.2000 Folien von Prof. H.-P. Gumm Parsen

Shift-Reduce - Parsing

if then else ??? ? ? ?x > 0

Bexpr

y + 1y :=

ExprExpr

id

Expr

numidid num

Lesepointer

Lese nächstesToken

ShiftShift

Ersetze if Bexpr then Stmtdurch Stmt .

ReduceReduce

Wegen des Lookahead Tokens “else” ist ein Shift angebracht.Wäre das lookahead ein `;` oder ein `end` , so müßte geshiftet werden.

Der gegenwärtige String besteht aus der Satzform

if Bexpr then Stmt

das nächste Token (lookahead) ist ein else.

Der gegenwärtige String besteht aus der Satzform

if Bexpr then Stmt

das nächste Token (lookahead) ist ein else.

Stmt

Page 13: Martin Schneider, 25.05.2000 Folien von Prof. H.-P. Gumm Martin Schneider, 25.05.2000 Folien von Prof. H.-P. Gumm Parsen

Shift/Reduce Parsing

S Stmt eofStmt if Expr then Stmt | if Expr then Stmt else Stmt | id := Expr

Expr Expr + Term | Term

Term num | id | ( Expr )

S Stmt eofStmt if Expr then Stmt | if Expr then Stmt else Stmt | id := Expr

Expr Expr + Term | Term

Term num | id | ( Expr )

0123

45

678

Wir parsen den Input

“IF id THEN id := num + id eof“

Erkannt : lookahead AktionIF id shiftIF id THEN red(6)IF Term THEN red(5)IF Expr THEN shiftIF Expr THEN id shiftIF Expr THEN id := shiftIF Expr THEN id := num shiftIF Expr THEN id := num + red(6)IF Expr THEN id := Term + red(5)IF Expr THEN id := Expr + shiftIF Expr THEN id := Expr + id shiftIF Expr THEN id := Expr + id eof red(7)IF Expr THEN id := Expr + Term eof red(4)IF Expr THEN id := Expr eof red(3)IF Expr THEN Stmt eof red(1)Stmt eof red(0)S eof accept

Erkannt : lookahead AktionIF id shiftIF id THEN red(6)IF Term THEN red(5)IF Expr THEN shiftIF Expr THEN id shiftIF Expr THEN id := shiftIF Expr THEN id := num shiftIF Expr THEN id := num + red(6)IF Expr THEN id := Term + red(5)IF Expr THEN id := Expr + shiftIF Expr THEN id := Expr + id shiftIF Expr THEN id := Expr + id eof red(7)IF Expr THEN id := Expr + Term eof red(4)IF Expr THEN id := Expr eof red(3)IF Expr THEN Stmt eof red(1)Stmt eof red(0)S eof accept

Grammatik

Page 14: Martin Schneider, 25.05.2000 Folien von Prof. H.-P. Gumm Martin Schneider, 25.05.2000 Folien von Prof. H.-P. Gumm Parsen

ZuständeEin Shift/Reduce Parser kann i.a. nicht allein anhand des lookahead tokens entscheiden, was als nächstes zu tun ist.

Zu diesem Zweck muß man noch Zustände einführen, die besagen, was der Parser gerade zu erkennen versucht.

Ein Shift/Reduce Parser kann i.a. nicht allein anhand des lookahead tokens entscheiden, was als nächstes zu tun ist.

Zu diesem Zweck muß man noch Zustände einführen, die besagen, was der Parser gerade zu erkennen versucht.

Erkannt : lookahead Aktion... THEN id := Term + red(5) . . .... THEN id := Expr + Term + red(4)

Erkannt : lookahead Aktion... THEN id := Term + red(5) . . .... THEN id := Expr + Term + red(4)

Expr Expr + Term | Term

Expr Expr + Term | Term

45

45

Anhand dieser Grammatik

und allein aus der Tatsache, daß zuletzt ein Term gesehen wurde und das lookahead “+” ist, ist nicht ersichtlich, ob gemäß der Regel 4 oder derRegel 5 reduziert werden muß :

Eine Reduktion mit Regel 5 ( red(5) ) würde im zweiten Falle in eine Sackgasse führen !!!

Page 15: Martin Schneider, 25.05.2000 Folien von Prof. H.-P. Gumm Martin Schneider, 25.05.2000 Folien von Prof. H.-P. Gumm Parsen

LR(0)- items *)

Zur korrekten Entscheidungsfindung des Parsers sind Zustände notwendig. Ein Zustand drückt aus,

- Welches Nonterminal der Parser gerade erkennen will

- Mit welcher Regel er dies versucht und

- Wieviel von der rechten Seite der Regel er bereits erkannt hat.

Zur korrekten Entscheidungsfindung des Parsers sind Zustände notwendig. Ein Zustand drückt aus,

- Welches Nonterminal der Parser gerade erkennen will

- Mit welcher Regel er dies versucht und

- Wieviel von der rechten Seite der Regel er bereits erkannt hat.

Sei A eine Produktion. Ein item ist eine Produktion zusammen mit einer “Position” in . Diese Position wird durch einen Punkt markiert.

Beispiel: Aus der Produktion Expr Expr + Term gewinnt man folgende items mit den Bedeutungen :

Bei dem Versuch ein Expr zu finden ...

... erwarte ein Expr + Term ... Expr gesehen, erwarte + Term ... Expr + gesehen, erwarte Term ... Expr + Term gesehen

Expr Expr + TermExpr Expr + TermExpr Expr + TermExpr Expr + Term

Expr Expr + TermExpr Expr + TermExpr Expr + TermExpr Expr + Term

*) LR - steht für Left-Right, gemeint ist die Abarbeitung des Inputs von Links nach rechts

Page 16: Martin Schneider, 25.05.2000 Folien von Prof. H.-P. Gumm Martin Schneider, 25.05.2000 Folien von Prof. H.-P. Gumm Parsen

Der Automat einer Grammatik

Ein item drückt den jeweiligen Zustand des Parsers beim Erkennen eines Nonterminals aus.

Mit dem Lesen des nächsten Tokens geht der Parser in den nächsten Zustand über. Es gibt zwei mögliche Übergänge :

Ein item drückt den jeweiligen Zustand des Parsers beim Erkennen eines Nonterminals aus.

Mit dem Lesen des nächsten Tokens geht der Parser in den nächsten Zustand über. Es gibt zwei mögliche Übergänge :

Expr Expr + Term Expr Expr + Term+ im Input

Expr Expr + Term

Term ( Expr )

Term id

Term num

Dies definiert einen nichtdeterministischen Automaten: N(G).

Page 17: Martin Schneider, 25.05.2000 Folien von Prof. H.-P. Gumm Martin Schneider, 25.05.2000 Folien von Prof. H.-P. Gumm Parsen

N(G) : Automat zu einer Grammatik G

Gegeben eine Grammatik G=(V,T,P,S), mit Startsymbol S, dann definieren wir einen NFA mit den items als Zustandsmenge und den Terminalen und Nonterminalen der Grammatik als Alphabet :

S = Menge aller items

= V T

s0 =S T = { X X P }

Gegeben eine Grammatik G=(V,T,P,S), mit Startsymbol S, dann definieren wir einen NFA mit den items als Zustandsmenge und den Terminalen und Nonterminalen der Grammatik als Alphabet :

S = Menge aller items

= V T

s0 =S T = { X X P }

Start Term Term Term Factor | Factor Factor id | ( Term )

Start Term Term Term Factor | Factor Factor id | ( Term )

(YX, ) = {X | XP }

(Yu, u) = {Yu } für u V T

Für die Grammatik ergibt sich :

STT

TTF TTF TTF TTF F

TF

Fid

F( T ) F( T ) F( T) F( T )(

Fid

T )

id

Alle unbeschrifteten Pfeile sind -TransitionenTF

ST

F

T

Page 18: Martin Schneider, 25.05.2000 Folien von Prof. H.-P. Gumm Martin Schneider, 25.05.2000 Folien von Prof. H.-P. Gumm Parsen

Vom NFA zum DFA

STT

TTF TTF TTF TTF F

TF

Fid

F( T ) F(T ) F( T) F( T )(

Fid

T )

id

Alle unbeschrifteten Pfeile sind -Transitionen

E

H

J

K

A = { ST, TTF, TF, F( T ) , Fid }

B = { ST, TTF }

C = { TF}

D = { F( T), TTF, TF, F(T), Fid }

E = { Fid }

F = { }

G = {TTF, F( T ), Fid }

H = { F( T), TTF }

J = { TTF }

K = { F( T) }

A B

C

F

GT F

F (id T

FD

(id

(

id

)

Aus dem NFA gewinnen wir die Zustände des DFA, den wir D(G) nennen wollen:

alle übrigen Pfeile auf F

Der DFA D(G) ist :

Der DFA D(G) ist :

TF

ST

F

T

Page 19: Martin Schneider, 25.05.2000 Folien von Prof. H.-P. Gumm Martin Schneider, 25.05.2000 Folien von Prof. H.-P. Gumm Parsen

Goto

In der Compilerliteratur nennt man die Tabelle des Automaten D(G) auch “Goto”-Tabelle.

In der Compilerliteratur nennt man die Tabelle des Automaten D(G) auch “Goto”-Tabelle.

E

H

J

K

A B

C

F

GT F

F (id T

FD

(id

(

id

)

alle übrigen Pfeile auf F

Goto-Tabelle : (Die leeren Positionen entsprechen Fehlerzuständen)

Goto id ( ) * T FA E D B CB GD E D H CG E D JH K G

Page 20: Martin Schneider, 25.05.2000 Folien von Prof. H.-P. Gumm Martin Schneider, 25.05.2000 Folien von Prof. H.-P. Gumm Parsen

Die Notwendigkeit eines Stacks

Angenommen der Parser ist in einem Zustand (mit dem item)

er muß nun also in einen Zustand springen, der das item

enthält. Nach Abarbeitung dieses items kommt er in den Zustand :

Nun muß er sich aber erinnern, daß er zurückspringen muß in den Zustand

Offensichtlich muß bei jedem Sprung der alte Zustand auf einem Stack aufbewahrt werden und bei der Beendigung des items zurückgesprungen werden, verbunden mit einem POP des Stacks.

Angenommen der Parser ist in einem Zustand (mit dem item)

er muß nun also in einen Zustand springen, der das item

enthält. Nach Abarbeitung dieses items kommt er in den Zustand :

Nun muß er sich aber erinnern, daß er zurückspringen muß in den Zustand

Offensichtlich muß bei jedem Sprung der alte Zustand auf einem Stack aufbewahrt werden und bei der Beendigung des items zurückgesprungen werden, verbunden mit einem POP des Stacks.

Expr Expr + Term

Term ( Expr )

Expr Expr + Term

Term ( Expr )

Page 21: Martin Schneider, 25.05.2000 Folien von Prof. H.-P. Gumm Martin Schneider, 25.05.2000 Folien von Prof. H.-P. Gumm Parsen

shift items / reduce items

Ein shift item hat die Form

A u , mit u T.

Ein shift item hat die Form

A u , mit u T.

Ein reduce item hat die Form

A .

Ein reduce item hat die Form

A .

Das shift item drückt aus, daß wir als nächstes ein u erwarten können.

Sind wir in einem Zustand Y = { ... , A u ... } und ist das lookahead

tatsächlich u , so wird es konsumiert und der nächste Zustand

Z = Goto(Y,u) = { ... , A u ... } auf den Stack gepusht.

Es drückt aus, daß wir gerade erfolgreich ein A erkannt haben.

Auf dem Stack sollte gefälligst ein Zustand Y = { ... , X A ... }

liegen , darüber | | weitere Zustände. Wir poppen also | | viele

Zustände und ersetzen sie durch Goto(Y,A) = { ... , X A ... }.

shift pushshift push

reduce pop reduce pop

Page 22: Martin Schneider, 25.05.2000 Folien von Prof. H.-P. Gumm Martin Schneider, 25.05.2000 Folien von Prof. H.-P. Gumm Parsen

Der Stack beim Parsen von ( id id )

( id id ) eof ( id id ) eof

A A

D

A

D

E

A

D

C

A

D

H

A

D

H

G

A

D

H

G

E

A

D

H

G

J

A

D

H

A = { ST, TTF, TF, F( T ) , Fid }B = { ST, TTF }C = { TF}D = { F( T), TTF, TF, F(T), Fid }E = { Fid } F = { } G = {TTF, F( T ), Fid } H = { F( T), TTF } J = { TTF }K = { F( T) }

A

D

H

K

A

C

A

(

id F T

idF

)

T

F T

Die Shift-Aktionen entsprechen einer Traversierung des DFA(G)

A

B

Page 23: Martin Schneider, 25.05.2000 Folien von Prof. H.-P. Gumm Martin Schneider, 25.05.2000 Folien von Prof. H.-P. Gumm Parsen

Shift-Zustände - Reduce Zustände

Ein reduce-Zustand ist ein Zustand, der ein reduce item enthält.

Ein shift-Zustand ist ein Zustand, der ein shift item enthält.

Ein accept-Zustand ist ein reduce-Zustand der Form Start

Ein reduce-Zustand ist ein Zustand, der ein reduce item enthält.

Ein shift-Zustand ist ein Zustand, der ein shift item enthält.

Ein accept-Zustand ist ein reduce-Zustand der Form Start

A = {ST, TTF, TF, F( T ) , Fid }B = { ST, TT F }C = { TF}D = { F( T), TTF, TF, F(T), Fid }E = { Fid } F = { } G = {TTF, F( T ), Fid } H = { F( T), TTF } J = { TTF }K = { F( T) }

Beispiel : Für die Zustände des vorigen Automaten hat man Shift Zustände : A, B, D, G, H Reduce Zustände : B, C, E, J, KAccept Zustand: B

Überlegen Sie sich : Falls L(X) für alle X V, dann enthält jeder nichtleere Zustand ein shift item oder ein reduce item.

Der Zustand, der der leeren Menge von items entspricht, kann immer als Fehlerzustand interpretiert werden.

Der Zustand, der der leeren Menge von items entspricht, kann immer als Fehlerzustand interpretiert werden.

Page 24: Martin Schneider, 25.05.2000 Folien von Prof. H.-P. Gumm Martin Schneider, 25.05.2000 Folien von Prof. H.-P. Gumm Parsen

Shift-Reduce Konflikte

Enthält ein Zustand Z sowohl ein shift item als auch ein reduce item, so muß evtl. das lookahead entscheiden, was zu tun ist :

Enthält ein Zustand Z sowohl ein shift item als auch ein reduce item, so muß evtl. das lookahead entscheiden, was zu tun ist :

Z = { ... , A u , ... ,B , ... }

lookahead = u shift, lookahead Follow(B) reduce

Falls u Follow(B) gibt es einen Konflikt !Man sagt dann: Z hat einen Shift-Reduce Konflikt für u.

Pragmatik:Meistens entscheidet man sich bei einem Shift-Reduce Konflikt

zugunsten eines shift.

Page 25: Martin Schneider, 25.05.2000 Folien von Prof. H.-P. Gumm Martin Schneider, 25.05.2000 Folien von Prof. H.-P. Gumm Parsen

Reduce-Reduce Konflikte

Ein Reduce Konflikt kommt zustande, wenn ein Zustand mehrere Reduce items mit nicht disjunkten Follow-Mengen enthält :

Ein Reduce Konflikt kommt zustande, wenn ein Zustand mehrere Reduce items mit nicht disjunkten Follow-Mengen enthält :

Z = { ... , A , ... ,B , ... }

lookahead Follow(A) reduce mit A lookahead Follow(B) reduce mit B

Falls u Follow(A) Follow(B) , gibt es einen Konflikt !Man sagt dann: Z hat einen Reduce-Reduce Konflikt für u.

Pragmatik:Reduce-Reduce Konflikte sind unangenehm. Notfalls muß man die

Grammatik ändern.

Page 26: Martin Schneider, 25.05.2000 Folien von Prof. H.-P. Gumm Martin Schneider, 25.05.2000 Folien von Prof. H.-P. Gumm Parsen

Syntaxfehler

Ein Syntaxfehler im Input ist beim LR-Parsen immer zum frühestmöglichen Zeitpunkt erkennbar :

Ein Syntaxfehler im Input ist beim LR-Parsen immer zum frühestmöglichen Zeitpunkt erkennbar :

Angenommen der Parser ist in Zustand Z und das lookahead ist a. Ein Syntaxfehler liegt vor, wenn Z kein shift item A a enthält und für alle reduce items B in Z gilt : aFollow(B)

Beispiel : A = { AT, TTF, TF, F( T ) , Fid } B = { AT, TT F }In Zustand A gibt es den möglichen Syntaxfehler : id or ‘(‘ expected In Zustand B gibt es den möglichen Syntaxfehler : ‘*’ or end-of-file expected

Page 27: Martin Schneider, 25.05.2000 Folien von Prof. H.-P. Gumm Martin Schneider, 25.05.2000 Folien von Prof. H.-P. Gumm Parsen

Präzedenz und Assoziativität

Sowohl Präzedenz, als auch durch Assoziativität bedingte Mehrdeutigkeit der Grammatik führt zu Shift-Reduce-Konflikten.

Sowohl Präzedenz, als auch durch Assoziativität bedingte Mehrdeutigkeit der Grammatik führt zu Shift-Reduce-Konflikten.

E E + E E E + EE E E

S E E E + E | E E | ( E ) | id

S E E E + E | E E | ( E ) | id

erzeugt u.a. die Zustände

E E E E E + EE E E

I7

I8

shift-reduce Konfliktfür + und für .

shift-reduce Konfliktfür + und für .

E E + E E E + EE E E

E E E E E + EE E E

für shift ( garantiert Präzedenz von * über ) für + reduce ( bewirkt Links-Klammerung von + )

für reduce (bewirkt Links-Klammerung von ) für + reduce (garantiert Präzedenz von * über )

I7

I8

Wie sind diese Konflikte zu lösen ?

Page 28: Martin Schneider, 25.05.2000 Folien von Prof. H.-P. Gumm Martin Schneider, 25.05.2000 Folien von Prof. H.-P. Gumm Parsen

ParsertabelleIn einer Tabelle kann die Aktion des Parsers festgehalten werden. Links steht immer der Zustand und oben ein Terminal oder ein Nonterminal

In einer Tabelle kann die Aktion des Parsers festgehalten werden. Links steht immer der Zustand und oben ein Terminal oder ein Nonterminal

a

Q PEin Eintrag

bedeutet: im Zustand Q bei lookahead a shift und gehe in Zustand P

a

Q r k

Ein Eintrag

bedeutet: im Zustand Q bei lookahead a reduce mit Grammatikregel k

Die Reduktion mit der Startregel, A T wird mit Accept bezeichnet.

Für ein Reduce mit der Regel A ist nur interessant : | | - weil soviele Stackelemente gepoppt werden A - weil anschließend mit Goto(top(stack) ,A) geshiftet wird.

Page 29: Martin Schneider, 25.05.2000 Folien von Prof. H.-P. Gumm Martin Schneider, 25.05.2000 Folien von Prof. H.-P. Gumm Parsen

Parsertabelle

Die komplette Information über die Grammatik steckt in der Parsertabelle:Die komplette Information über die Grammatik steckt in der Parsertabelle:

(0) A Term (1) Term Term Factor(2) | Factor (3) Factor id (4) | ( Term )

(0) A Term (1) Term Term Factor(2) | Factor (3) Factor id (4) | ( Term )

Action id ( ) * T F eofA E D B CB G AcceptC r 2 r 2 r 2D E D H CE r 3 r 3 r 3FG E D JH K GJ r 1 r 1 r 1K r 4 r 4 r 4

Grammatik

Parsertabelle

Page 30: Martin Schneider, 25.05.2000 Folien von Prof. H.-P. Gumm Martin Schneider, 25.05.2000 Folien von Prof. H.-P. Gumm Parsen

LR(0) Parsing

id ( ) * T F eofA E D B CB G AcceptC r 2 r 2 r 2D E D H CE r 3 r 3 r 3FG E D JH K GJ r 1 r 1 r 1K r 4 r 4 r 4

(0) A T (1) T T F(2) | F(3) F id (4) | ( T )

(0) A T (1) T T F(2) | F(3) F id (4) | ( T )

( id id ) eof ( id id ) eof

A A

D

A

D

E

A

D

C

A

D

H

A

D

H

G

A

D

H

G

E

A

D

H

G

J

A

D

H

A

D

H

K

A

C

A

(

id F T

idF

)

T

F T

A

B

Page 31: Martin Schneider, 25.05.2000 Folien von Prof. H.-P. Gumm Martin Schneider, 25.05.2000 Folien von Prof. H.-P. Gumm Parsen

yacc

Yacc ist ein Parser Generator. Aus einer kontextfreien Grammatik erzeugt er automatisch einen LALR(1) Parser (mächtiger als LR(0)).Der Parser ist ein ausführbares Programm und liegt als C-Funktion yyparse() vor. Typischerweise arbeitet yacc mit mit lex zusammen. lex erkennt die Token, während yacc für die darauf aufbauende Grammatik verantwortlich ist.

Yacc ist ein Parser Generator. Aus einer kontextfreien Grammatik erzeugt er automatisch einen LALR(1) Parser (mächtiger als LR(0)).Der Parser ist ein ausführbares Programm und liegt als C-Funktion yyparse() vor. Typischerweise arbeitet yacc mit mit lex zusammen. lex erkennt die Token, während yacc für die darauf aufbauende Grammatik verantwortlich ist.

# yacc Input File für Baby Deutsch

%start Satz

%Token ARTIKEL NAME HAUPTWORT AUX VERB PUNKT%%Satz : Subjekt Praedikat Objekt PUNKT

| Subjekt Praedikat PUNKT ;

Subjekt : ARTIKEL HAUPTWORT| NAME ;

Objekt : Subjekt ;

Praedikat: AUX VERB ;| VERB| error{“Verb erwartet} ;

# yacc Input File für Baby Deutsch

%start Satz

%Token ARTIKEL NAME HAUPTWORT AUX VERB PUNKT%%Satz : Subjekt Praedikat Objekt PUNKT

| Subjekt Praedikat PUNKT ;

Subjekt : ARTIKEL HAUPTWORT| NAME ;

Objekt : Subjekt ;

Praedikat: AUX VERB ;| VERB| error{“Verb erwartet} ;

Startsymbol Für diese Token ist lex verantwortlich

Aktionen werden wie Terminalsymbole behandelt

Page 32: Martin Schneider, 25.05.2000 Folien von Prof. H.-P. Gumm Martin Schneider, 25.05.2000 Folien von Prof. H.-P. Gumm Parsen

Das Duo : lex und yacc

yacc erzeugt die Funktion yyparse(), lex die Funktion yylex().

yylex() liefert die Nummer des Tokens. Der String aus dem dieses besteht ist immer in der (globalen) Variablen yytext vorhanden.

yacc erzeugt die Funktion yyparse(), lex die Funktion yylex().

yylex() liefert die Nummer des Tokens. Der String aus dem dieses besteht ist immer in der (globalen) Variablen yytext vorhanden.

Fig. nach: T.Mason, D. Brown: lex & yaccO’Reilly & Associates, Inc.

main()main()

yyparse()yyparse()

yylex()yylex() InputInputyytext

0 falls gültig1 sonst

Token Nummer, 0 falls eof

verlange nächstes token

lese nächstesZeichen

Page 33: Martin Schneider, 25.05.2000 Folien von Prof. H.-P. Gumm Martin Schneider, 25.05.2000 Folien von Prof. H.-P. Gumm Parsen

Beispiel: Ein Compiler für Expressions

Wir wollen einen Compiler bauen, der algebraische Ausdrücke in Postfix-Notation verwandelt. Wir gehen aus von der einfachen Grammatik

Wir wollen einen Compiler bauen, der algebraische Ausdrücke in Postfix-Notation verwandelt. Wir gehen aus von der einfachen Grammatik

Expr Expr + Expr | Expr - Expr | Expr * Expr | Expr / Expr | ( Expr ) | id | num

Expr Expr + Expr | Expr - Expr | Expr * Expr | Expr / Expr | ( Expr ) | id | num

Als erstes spezifizieren wir die Token durch ein lex file .

Eine Umwandlung, z.B. zur Beseitigung der Linksrekursion, oder zurErzwingung der Präzedenzen oder der Linksassoziativität ist nicht nötig.

Page 34: Martin Schneider, 25.05.2000 Folien von Prof. H.-P. Gumm Martin Schneider, 25.05.2000 Folien von Prof. H.-P. Gumm Parsen

Das lex file

Das lex-file infix_postfix.l .

Wir benutzen die Konvertierungsfunktion “sscanf” der Sprache C, um den (aus Ziffern bestehenden) String “yytext” in den entsprechenden Zahlenwert zu verwandeln. Diesen speichern wir in der globalen Variablen “yylval”.

Die Integerkonstanten PLUS,MINUS,TIMES,QUOT werden wir in dem zugehörigen yacc-file spezifizieren.

Das lex-file infix_postfix.l .

Wir benutzen die Konvertierungsfunktion “sscanf” der Sprache C, um den (aus Ziffern bestehenden) String “yytext” in den entsprechenden Zahlenwert zu verwandeln. Diesen speichern wir in der globalen Variablen “yylval”.

Die Integerkonstanten PLUS,MINUS,TIMES,QUOT werden wir in dem zugehörigen yacc-file spezifizieren.

letter [a-zA-Z]digit [0-9]%% [ \t]+ ;“+” { return(PLUS) }“-” { return(MINUS) }“*” { return(TIMES) }“/” { return(QUOT) }{digit}+ { sscanf(yytext,”%d”, &yylval);

return(NUM)}{letter}({letter}|{digit})* {return(ID)}%%

letter [a-zA-Z]digit [0-9]%% [ \t]+ ;“+” { return(PLUS) }“-” { return(MINUS) }“*” { return(TIMES) }“/” { return(QUOT) }{digit}+ { sscanf(yytext,”%d”, &yylval);

return(NUM)}{letter}({letter}|{digit})* {return(ID)}%%

Page 35: Martin Schneider, 25.05.2000 Folien von Prof. H.-P. Gumm Martin Schneider, 25.05.2000 Folien von Prof. H.-P. Gumm Parsen

Das yacc file

In dem yacc file werden zunächst die token ID, NUM und PLUS, MINUS, TIMES, QUOT deklariert. Die letzteren werden als links-assoziative Operatoren spezifiziert, was den Parser zur richtigen Auflösung der entstehenden shift-reduce Konflikte veranlaßt. Die Reihenfolge (zunächst PLUS, MINUS, danach TIMES, QUOT) bewirkt die gewünschte Präzedenz.

Über yylval und yytext kann auf Attribute der Token NUM und ID zugegriffen werden.

In dem yacc file werden zunächst die token ID, NUM und PLUS, MINUS, TIMES, QUOT deklariert. Die letzteren werden als links-assoziative Operatoren spezifiziert, was den Parser zur richtigen Auflösung der entstehenden shift-reduce Konflikte veranlaßt. Die Reihenfolge (zunächst PLUS, MINUS, danach TIMES, QUOT) bewirkt die gewünschte Präzedenz.

Über yylval und yytext kann auf Attribute der Token NUM und ID zugegriffen werden.

%token ID, NUM%left PLUS, MINUS%left TIMES, QUOT%start expr%% expr : expr PLUS expr { printf(“add “); }

| expr MINUS expr { printf(“sub “); }| expr TIMES expr { printf(“mult “); }| expr QUOT expr { printf(“div “); }| NUM { printf("%d ",yylval);}| ID { printf("%s ",yytext);};

%%#include "lex.yy.c"int main(){

printf(“Bitte geben Sie einen Ausdruck ein :\n”);yyparse(); }

%token ID, NUM%left PLUS, MINUS%left TIMES, QUOT%start expr%% expr : expr PLUS expr { printf(“add “); }

| expr MINUS expr { printf(“sub “); }| expr TIMES expr { printf(“mult “); }| expr QUOT expr { printf(“div “); }| NUM { printf("%d ",yylval);}| ID { printf("%s ",yytext);};

%%#include "lex.yy.c"int main(){

printf(“Bitte geben Sie einen Ausdruck ein :\n”);yyparse(); }

Das von lex erzeugte C-Programm wird hier eingefügt.

Page 36: Martin Schneider, 25.05.2000 Folien von Prof. H.-P. Gumm Martin Schneider, 25.05.2000 Folien von Prof. H.-P. Gumm Parsen

Das fertige C-Programm

Aus dem lex file infix_postfix.l erzeugt lex ein C-File lex.yy.c. Das Kommando lautet hier :

lex infix_postfix.l

Über zusätzliche Optionen (-v, ..) können auch diagnostische Informationen hinzugefügt werden.

Aus dem yacc file infix.y erzeugt yacc ein C-File y.tab.c.

Das Kommando hierzu ist :

yacc infix.y

Der C-Compiler erzeugt aus y.tab.c das ausführbare Programm. Hierzu werden beim Linken gewisse Bibliotheksroutinen für lex (-ll) und für yacc (-ly) benötigt. Wenn das fertige Programm in2post heißen soll, lautet das Kommando :

cc -o in2post y.tab.c -ll -ly

Aus dem lex file infix_postfix.l erzeugt lex ein C-File lex.yy.c. Das Kommando lautet hier :

lex infix_postfix.l

Über zusätzliche Optionen (-v, ..) können auch diagnostische Informationen hinzugefügt werden.

Aus dem yacc file infix.y erzeugt yacc ein C-File y.tab.c.

Das Kommando hierzu ist :

yacc infix.y

Der C-Compiler erzeugt aus y.tab.c das ausführbare Programm. Hierzu werden beim Linken gewisse Bibliotheksroutinen für lex (-ll) und für yacc (-ly) benötigt. Wenn das fertige Programm in2post heißen soll, lautet das Kommando :

cc -o in2post y.tab.c -ll -ly

Page 37: Martin Schneider, 25.05.2000 Folien von Prof. H.-P. Gumm Martin Schneider, 25.05.2000 Folien von Prof. H.-P. Gumm Parsen

Das Zusammenspiel von lex & yacc

lexlex yacc yacc

lexSpezifikation

lexSpezifikation

yaccSpezifikation

yaccSpezifikation

yylex( )yylex( ) yyparse( )yyparse( ) Zusätzl.C-Routinen

Zusätzl.C-Routinen

cc cc UNIXBibliotheken

UNIXBibliotheken

fertigesProgramm

Fig. nach: T.Mason, D. Brown: lex & yaccO’Reilly & Associates, Inc.

*.l *.y

lex.yy.c y.tab.c *.c

Page 38: Martin Schneider, 25.05.2000 Folien von Prof. H.-P. Gumm Martin Schneider, 25.05.2000 Folien von Prof. H.-P. Gumm Parsen

Der Aufbau eines Syntaxprüfers

Die Arbeitsweise eines Syntaxprüfers geschieht in 2 Phasen. Diese Phasen können in ihrem Ablauf zeitlich verschachtelt sein.

Die Arbeitsweise eines Syntaxprüfers geschieht in 2 Phasen. Diese Phasen können in ihrem Ablauf zeitlich verschachtelt sein.

Der Scanner zerlegt das inputfile anhand regulärer Definitionen in eine Reihe von Token.

ScannerScanner

b e t r a g : = b e t r a g * ( 1 + z i n s )

id assign intConst + id * ( id )

File of char:

File of token:

Phase 1:Scannen

Phase 1:Scannen

Page 39: Martin Schneider, 25.05.2000 Folien von Prof. H.-P. Gumm Martin Schneider, 25.05.2000 Folien von Prof. H.-P. Gumm Parsen

Der Aufbau eines Syntaxprüfers

DerParser versucht die Reihe der Token zu einem Herleitungsbaum zu gruppieren

ParserParser

id

assign

intConst

+

id

*

(

id

)

File of token:

id

assign

*

id

+

intConst id

( )

Parse Tree :

Phase 2:Parsen

Phase 2:Parsen

Page 40: Martin Schneider, 25.05.2000 Folien von Prof. H.-P. Gumm Martin Schneider, 25.05.2000 Folien von Prof. H.-P. Gumm Parsen

Symboltabelle

Ein Compiler besteht aus einem Syntaxprüfer und einer weiteren Stufe, dem Codeerzeuger. Um aus einem Syntaxbaum Code erzeugen zu können, muß aber die Identität einiger Token bekannt sein. Etwa, welche Bezeichner identisch sind, welchen Wert eine intConst hat, etc.

Der Scanner legt diese Information in einer Symboltabelle ab und reicht dem Parser nicht nur die Token, sondern auch Zeiger in diese Tabelle weiter.

Ein Compiler besteht aus einem Syntaxprüfer und einer weiteren Stufe, dem Codeerzeuger. Um aus einem Syntaxbaum Code erzeugen zu können, muß aber die Identität einiger Token bekannt sein. Etwa, welche Bezeichner identisch sind, welchen Wert eine intConst hat, etc.

Der Scanner legt diese Information in einer Symboltabelle ab und reicht dem Parser nicht nur die Token, sondern auch Zeiger in diese Tabelle weiter.

betrag zins

xtest

Real Real

Integer Boolean

Name Typ 17F4 17F8 201C C011

Sp.Platz

ScannerScanner

b e t r a g : =

b e t r a g * ( 1

+ z i n s )

nl

nl

id assign intConst + id * ( id ) 1

Page 41: Martin Schneider, 25.05.2000 Folien von Prof. H.-P. Gumm Martin Schneider, 25.05.2000 Folien von Prof. H.-P. Gumm Parsen

Symboltabelle

Gewisse Token im Syntaxbaum haben einen Link in die SymboltabelleGewisse Token im Syntaxbaum haben einen Link in die Symboltabelle

assign

*

+

intConst

( )

Parse Tree :

betrag zins

xtest

Real Real

Integer Boolean

Name Typ 17F4 17F8 201C C011

Sp.Platz

id

id

id 1

Page 42: Martin Schneider, 25.05.2000 Folien von Prof. H.-P. Gumm Martin Schneider, 25.05.2000 Folien von Prof. H.-P. Gumm Parsen

Stackprozessor

Aus einem Programmtext gilt es, Code für eine einfache Maschine (einen Prozessor) zu erzeugen. Ein einfaches Maschinenmodell ist ein Stackprozessor.

Aus einem Programmtext gilt es, Code für eine einfache Maschine (einen Prozessor) zu erzeugen. Ein einfaches Maschinenmodell ist ein Stackprozessor.

Wir denken uns eine Maschine, die im wesentlichen aus einem Stack besteht und damit folgende Operationen ausführen kann :

PUSH <num>

LOAD <hex>

STORE <hex>

ADD

MULT

PUSH <num>

LOAD <hex>

STORE <hex>

ADD

MULT

Lege den Zahlenwert <num> auf dem Stack abLege den Zahlenwert <num> auf dem Stack ab

Lege den Inhalt von Adresse <hex> auf dem Stack abLege den Inhalt von Adresse <hex> auf dem Stack ab

Speichere den Top des Stacks an Adresse <hex>.Der Stack wird dabei gepopped.

Speichere den Top des Stacks an Adresse <hex>.Der Stack wird dabei gepopped.

Ersetze das oberste Element des Stacks durch die Summe der beiden obersten

Ersetze das oberste Element des Stacks durch die Summe der beiden obersten

Ersetze das oberste Element des Stacks durch das Produkt der beiden obersten

Ersetze das oberste Element des Stacks durch das Produkt der beiden obersten

Page 43: Martin Schneider, 25.05.2000 Folien von Prof. H.-P. Gumm Martin Schneider, 25.05.2000 Folien von Prof. H.-P. Gumm Parsen

Code-ErzeugungAus dem Syntaxbaum kann leicht Code erzeugt werden, etwa für einen Stackprozessor :

Aus dem Syntaxbaum kann leicht Code erzeugt werden, etwa für einen Stackprozessor :

assign

*

+

intConst

( )

Parse Tree :

betrag zins

xtest

Real Real

Integer Boolean

Name Typ 17F4 17F8 201C C011

Sp.Platz

id

id

id 1

LOAD 17F4 PUSH 1LOAD 17f8ADDMULTSTORE 17F4

LOAD 17F4 PUSH 1LOAD 17f8ADDMULTSTORE 17F4

Code für einen Stackprozessor

Page 44: Martin Schneider, 25.05.2000 Folien von Prof. H.-P. Gumm Martin Schneider, 25.05.2000 Folien von Prof. H.-P. Gumm Parsen

Code Erzeugung aus dem Parser

Die Ausgabe des Codes kann als semantische Aktion vom Parser veranlaßt werden, die erweiterte Syntax würde etwa lauten :

Die Ausgabe des Codes kann als semantische Aktion vom Parser veranlaßt werden, die erweiterte Syntax würde etwa lauten :

%{ int* loc }%

expr : expr + expr { printf("ADD \n"); }| expr * expr { printf("MULT\n"); }| intConst { printf("PUSH "); printf("%d\n",yylval);}| ID { printf("LOAD "); printf(lookup(yytext));};

stmt : ID { loc=lookup(yytext);} ASSIGN expr { printf("STORE "); printf(&loc); } | ... etc. ...

%%#include lex.yy.c...

%{ int* loc }%

expr : expr + expr { printf("ADD \n"); }| expr * expr { printf("MULT\n"); }| intConst { printf("PUSH "); printf("%d\n",yylval);}| ID { printf("LOAD "); printf(lookup(yytext));};

stmt : ID { loc=lookup(yytext);} ASSIGN expr { printf("STORE "); printf(&loc); } | ... etc. ...

%%#include lex.yy.c...