Vorlesung Compilertechnik Sommersemester 2009 Syntaktische Analyse M. Schölzel.

Post on 05-Apr-2015

107 views 0 download

Transcript of Vorlesung Compilertechnik Sommersemester 2009 Syntaktische Analyse M. Schölzel.

Vorlesung CompilertechnikSommersemester 2009

Syntaktische Analyse

M. Schölzel

2

Einbettung des Parsers in den Compiler

Kontext-prüfungParser

Token

Syntax-baum

Syntax-baum

Parser

Symbol-tabelle

Symbol-tabelle

Zwischencode-/Zielcodeerzeugung

3

Aufgabe des Parsers

Verarbeitung der Tokenfolge, die vom Scanner geliefert wird und Überführung in eine strukturierte Darstellung, z.B. einen Syntaxbaum, der die syntaktische Struktur des Quellprogramms repräsentiert:

Deklarationen Anweisungsfolgen Anweisungen Ausdrücke

Die syntaktische Struktur ist im Allgemeinen durch eine kfG spezifiziert.

Der Parser muss eine Ableitung aus dem Startsymbol der kfG für die vom Scanner gelieferte Morphemfolge finden oder

syntaktische Fehler erkennen und diese lokalisieren.

4

Einteilung Analyseverfahren

Top-Down-Verfahren Bottom-Up-Verfahren

Univ

ers

elle

Verf

ahre

nA

uto

mate

nbasi

ert

Berechnung desAbleitungsbaums

Cocke-Younger-KasamiAlgorithmus

nicht-deterministisch

deterministischBacktracking-

frei

nicht-deterministisch

deterministischmit

Backtracking

deterministischBacktracking-

frei

deterministischmit

Backtracking

5

Der PDA mit Ausgabe als theoretisches Modell

Push-Down-Automat P = (Z, , z, f, , M) – ein endliches Eingabealphabet Z – ein endliches Kelleralphabet mit Z M – Metasymbole einer Grammatik (für die Ausgabe) z Z* – Die Startbelegung des Kellers f Z* – Kellerinhalt für akzeptierenden Zustand : Z* * ((Z* (M )) (Z*)) – Überführungsrelation

Unterschiede zu klassischen PDAs: P hat nur einen Zustand P darf eine endliche Anzahl Kellersymbole in einem Takt lesen P darf eine endliche Anzahl (mehr als eins) Symbole der Eingabe sehen P erzeugt eine Ausgabe (linearisierter abstrakter Syntaxbaum)

Situation (, , ) – Kellerinhalt (Kellerspitze rechts) – Eingabe – linearisierter abstrakter Syntaxbaum

Takt (Situationsübergang) (, , ) (, , (A,i)), falls Z* und * und (,(A,i)) (, ) (, a, ) (, , ), falls Z* und a * und (,a)

Akzeptierte Sprache eines PDA P: L(P) := { | (z, , ) * (, , ) und = f }

6

Top-Down-Verfahren

Gegeben sind eine kfG G und ein Wort w. Ausgehend vom Startsymbol S in G wird eine

Linksableitung nach w gesucht. In jedem Ableitungsschritt wird das am weitesten

links stehende Metasymbol durch eine seiner Alternativen ersetzt:

Wahl der Alternative nichtdeterministisch oder Nach einem festen Schema; bei falscher Wahl endet die

Ableitung in einer Sackgasse:

S *

* w

* ' * w

Beginn der Sackgasse, falls * w

Erkennung der Sackgasse, falls * und ' und w

7

Grammatik in Top-Down PDA transformieren

Es sei G = (, M, S, R) eine kfG Der PDA P = (V, , z, f, , M) mit L(P) = L(G) ist

definiert als: z := S f := V := M (a,a) : a ([A,i],(A,i)) (A,) : (A,[A,i]) R

Der nichtdeterministische PDA P hat damit folgendes Verhalten:

Startsituation (S, , ) (A, , ) ([A,i], , (A,i)) (a, a, ) (, , ) (, , ) akzeptierende Endsituation

8

Zusammenhang Analysesituation des Top-Down-PDA und Ableitung in Grammatik

Eine Analysesituation (,,) eines PDA P zur Grammatik G = (, M, S, R) mit (S,,) * (,,) entspricht der Linkssatzform in der Linksableitung S * * .

D.h. aus S wurde ein Präfix der Eingabe abgeleitet und es wird erwartet, dass aus die terminale Zeichenkette abgeleitet werden kann.

ist die Folge der angewendeten Ableitungen bei S *

ist für (S,,) * (,,) die Präfixlinearisierung des abstrakten Syntaxbaums zu .

9

Bottom-Up-Analyse

Gegeben sind eine Grammatik G und ein Wort w. Ausgehend von w wird versucht eine Reduktion

(Rückwärtsableitung) nach S zu finden. In jedem Rückwärtsableitungsschritt wird in der aktuellen

Satzform ein Teilwort gesucht, das einer Alternative eines Metasymbols entspricht und durch das entsprechende Metasymbol ersetzt. (Reduktionsschritt):

Wahl der Alternative und der Ersetzungsposition nichtdeterministisch oder

Nach einem festen Schema; bei falscher Wahl endet die Rückwärtsableitung in einer Sackgasse:

w *

* S

* ' S

Beginn der Sackgasse, falls * S

Erkennung der Sackgasse, falls z.B. kein Teilwort von ' eine Alternative eines Metasymbols ist

10

Grammatik in Bottom-Up PDA transformieren

Es sei G = (, M, S, R) eine kfG Der PDA P = (V, , z, f, , M) mit L(P) = L(G) ist

definiert als: z := f := S V := M (A, (A,i)) ([A,i], ) : (A,[A,i]) R a (,a) : a

Der nichtdeterministische PDA P hat damit folgendes Verhalten:

Startsituation (, , ) ([A,i], , ) (A, , (A,i)) (, a, ) (a, , ) (S, , ) akzeptierende Endsituation

11

Zusammenhang Analysesituation und Ableitung bei Bottom-Up

Eine Analysesituation (,,) eines PDA P zur Grammatik G = (, M, S, R) mit (,,) * (,,) entspricht der Rechtssatzform in der Rechtsableitung S * * (bzw. * * S).

D.h. wurde bereits eingestapelt (gesehen) und zu reduziert und es wird erwartet, dass zu S reduziert werden kann.

ist die Folge der angewendeten Ableitungen bei * .

ist für (,,) * (S,,) die Postfixlinearisierung des abstrakten Syntaxbaums zu .

12

Top-Down-Analyse mit Backtracking durch Berechnung des Ableitungsbaums

-Regeln eliminieren Regeln der Form A B eliminieren Dadurch: Ableitungsschritt verlängert das

Wort oder überführt Meta- in Terminalsymbol(e)

Abbruchkriterium: Wenn abgeleitetes Nicht-terminales Wort länger als das gegebene Wort w

Untersuchung bis zu einer maximalen Anzahl von 2|w| - 1 Ableitungsschritten

13

Ableitungsbaum

Ableitungsbaum zu einer Grammatik G = (, M, S, R) ist ein geordneter Baum B mit der Beschriftung : B V* für den gilt:

B darf unendlich sein () = S hat genau so viele Söhne, wie das in () am weitesten

links stehende Metasymbol Alternativen hat Für .i B gilt: * V*: (.i) = [A,i+1] und

() = A und (A,[A,i+1]) R In dem Ableitungsbaum werden Linksableitungen

betrachtet. Um festzustellen, ob w L(G) genügt es in B die

Beschriftungen von Adressen zu untersuchen, für die gilt: || < 2|w|

14

Cocke-Younger-Kasami-Algorithmus

Eingabe: kfG (, M, S, R) in Chomsyk-Normalform und Wort w = w1…wn (wk ).

Konstruktion der Ableitung durch Berechnung der Mengen ti,j, wobei ti,j die Metasymbole enthält, aus denen die Zeichenkette wi…wi+j-1 abgeleitet werden kann. Berechnung durch: ti,1 := {A | (A,wi) R} Berechnung von ti,j+1 := {A | k: 1 k < j+1 und

(A,BC) R und B ti,k und C ti+k,j+1-k} ist möglich, weil k < j+1 und j+1-k < j+1.

Falls S t1,n, dann ist w aus S ableitbar. Rekonstruktion der Ableitung beginnend bei t1,n.

15

Benötigte Operationen auf Wort(meng)en für Backtrackingfreie Analyse

prek : * * ist definiert als:

. k . : (*) (*) ist definiert als:A k B := {prek() | A und B}

, falls | |( ) :

, sonst, wobei und | |k

kpre

k

a aa

b a bg b

ì <ïïï= íï = =ïïî

16

Backtrackingfreie Analyse

Ziel: In jeder Situation Wahl des richtigen Takts und der richtigen Alternative im Ableitungs-/Reduktionstakt, so dass Sackgassen vermieden werden.

Entscheidung basiert auf: einer endlichen Vorausschau in der Eingabe um eine feste

Anzahl von k vielen Morphemen (Look-Ahead), evtl. dem Wissen über die bisher gesehene Eingabe.

Eingabe wird um k Morpheme der Art verlängert, um auch am Ende des zu analysierenden Wortes immer eine Vorausschau von k Morphemen zu haben.

Dafür Transformation der ursprünglichen Grammatik G in Grammatik G' mit der Regel S' S k zum neuen Startsymbol S' und dem alten Startsymbol S.

Vereinbarung: Im Folgenden wird in den Situationen des PDA der linearisierte abstrakte Syntaxbaum weggelassen.

17

Backtrackingfreie Top-Down-Analyse

Analysesituation (A,) bedeutet, dass erwartet wird, die Resteingabe aus A ableiten zu können: A * .

Das ist offensichtlich nur dann möglich, wenn durch Wahl der richtigen Alternative i von A gilt: [A,i] * .

Bei Verwendung einer Vorausschau von k Zeichen muss sich aus [A,i] eine Zeichenkette ableiten lassen, die in den ersten k Terminalzeichen mit übereinstimmt.

Im folgenden Betrachtung verschiedener Klassen von Grammatiken, für die diese Entscheidung eindeutig getroffen werden kann.

18

Einfache LL(k)-Grammatiken

Eine Grammatik ist eine einfache LL(k)-Grammatik, wenn für jedes Metasymbol A gilt: |[A]| = 1 oder 1 i |[A]|: prek([A,i]) k und 1 i j |[A]|: prek([A,i]) prek([A,j])

Offensichtlich ist dann in der Analysesituation (A,) die Auswahl einer Alternative eindeutig möglich durch: (A,) ([A,i],) : prek([A,i]) = prek().

19

Beispiel: Einfache LL(1)-Grammatik

S'::= S

S ::= if Id then S else S fi |

while Id do S od |

begin S end |

Id := Id E

E ::= + Id Q|

* Id Q

Q ::= Id := Id E |

;

20

Syntaxgebundene Implementierung

Für jedes Metasymbol A der Grammatik wird eine Funktion int A() definiert mit Rückgabe 0 für Fehler in der Eingabe oder Rückgabe 1 bei erfolgreicher Ableitung eines Präfixes der Eingabe aus A

Globale Variablen für Aktuelle Vorausschau Bisher erzeugten Syntaxbaum

Jede Funktion int A() trifft auf Grund der aktuellen Vorausschau die Auswahl einer Alternativ von A.

Abarbeitung der Symbole der Alternative von links nach rechts durch:

Terminalsymbol: Prüfen, ob aktuelles Symbol in der Eingabe mit dem Terminalsymbol in der Alternative übereinstimmt:

Ja: Eingabe konsumieren Nein: Fehler in der Eingabe

Metasymbol: Rekursiver Aufruf der Funktion zum Metasymbol in der Alternative und Rückgabe des Fehlerwertes

21

Beispiel für syntaxgebundene Implementierung

int E() { switch(nextMor()) { case PLUS: printf("(E_1)"); if(nextMor() == ID && Q()) return 1; return 0; case MAL: printf("(E_2)"); if(nextMor() == ID && Q()) return 1; return 0; } return 0;}

S'::= SS ::= if id then S else S fi | while id do S od | begin S end | id := id E

E ::= + id Q| * id QQ ::= id := id E | ;

int Q(){ switch(nextMor()) { case ID: printf("(Q_1)"); if(nextMor() == ASSIGN && nextMor() == ID && E()) return 1; return 0; case SEMIKOLON: printf("(Q_2)"); return 1; } return 0;}

Quelltextausschnitt:

Grammatik

Vollständiger Quelltext (h, c, l) Ausführbares Programm

22

Starke LL(k)-Grammatiken

Einfache LL(k)-Grammatiken sind für viele praktisch benötigte Programmstrukturen nicht ausreichend.

Aufhebung der Forderung, dass die ersten k Symbole jeder Alternative Terminalsymbole sein müssen.

Weiterhin muss aber anhand der Vorausschau von k Symbolen eine Alternative für das am weitesten links stehende Metasymbol in einer Linkssatzform eindeutig bestimmbar sein.

Es sei G = (, M, R, S) eine Grammatik und k . Dann ist G eine starke LL(k)-Grammatik, wenn gilt: Existieren zwei Linksableitungen

mit prek() = prek('), dann folgt, dass = ', wobei ,,',' *, S,A M und ,,',' V*

Folgerungen: Jede starke LL(k)-Grammatik ist eindeutig Jede einfache LL(k)-Grammatik ist eine starke LL(k)-Grammatik

S * *

A *

'A' ''' * ''

23

Prüfen der starken LL(k)-Eigenschaft

Es sei G = (, M, R, S) eine Grammatik und k . Dann ist G eine starke LL(k)-Grammatik, wenn gilt: Existieren zwei Linksableitungen

mit prek() = prek('), dann folgt, dass = ', wobei ,,',' *, S,A M und ,,',' V*

Beispielgrammatik, die für kein k die starke LL(k)-Eigenschaft besitzt Die Entscheidung zwischen zwei verschiedenen Alternative und ' von

A kann eindeutig getroffen werden, wenn alle aus ableitbaren terminalen Zeichenketten sich in den ersten k Symbolen von allen aus '' ableitbaren terminalen Zeichenketten unterscheiden:

{prek() | * und *}) {prek(') | * und '' *'} =

Zur Überprüfung dieser Eigenschaft werden die Firstk- und Followk-Mengen eingeführt.

S * *

A *

'A' ''' * ''

24

Beispielgrammatik

Grammatik 1 für reguläre Ausdrücke:REG ALT ALT KON | KON "|" ALTKON SYM | SYM "." KONSYM a | … | Z | $ | "(" ALT ")" | "(" ALT ")" "*"

Grammatik 1 hat für kein k die starke LL(k)-Eigenschaft Links-Faktorisierte Grammatik 2:

REG ALT ALT KON A A | "|" ALT KON SYM KK | "." KON SYM a | … | Z | $ | "(" ALT ")" SS | "*"

Links-Faktorisierte Grammatik hat die starke LL(k)-Eigenschaft für k = 1

Zurück

25

Firstk-Menge

Firstk() = {prek() | * und *} für V*

Berechnung durch Lösung eines Mengengleichungssystems, das sich aus den folgenden Regeln ergibt:

Firstk(a1…an) = Firstk(a1) k … k Firstk(an) für ai V mit 1 i n

Firstk(a) = {a} für a Firstk(A) = Firstk([A,1]) … Firstk([A,|[A]|]) für A M Firstk() = {}

26

Beispiel: Ergebnis der Berechnung der First1-Menge für Grammatik 2

It First1(REG) First1(ALT) First1(A) First1(KON) First1(K) First1(SYM) First1(S)

0

1 {} {} {a,…,Z,$} {, *}

2 {} {a,…,Z,$} {} {a,…,Z,$} {, *}

3 {a,…,Z,$} {} {a,…,Z,$} {, . } {a,…,Z,$} {, *}

4 {a,…,Z,$} {a,…,Z,$} {, | } {a,…,Z,$} {, . } {a,…,Z,$,(} {, *}

5 {a,…,Z,$} {a,…,Z,$} {, | } {a,…,Z,$,(} {, . } {a,…,Z,$,(} {, *}

6 {a,…,Z,$} {a,…,Z,$,(} {, | } {a,…,Z,$,(} {, . } {a,…,Z,$,(} {, *}

7 {a,…,Z,$,(} {a,…,Z,$,(} {, | } {a,…,Z,$,(} {, . } {a,…,Z,$,(} {, *}

8 {a,…,Z,$,(} {a,…,Z,$,(} {, | } {a,…,Z,$,(} {, . } {a,…,Z,$,(} {, *}

Beispielgrammatik 2

27

Followk-Menge

Es interessieren alle terminalen Zeichenketten der Länge k, die in irgendeiner Satzform hinter einem Metasymbol A auftreten können: S' * A und *

Followk(A) = {Firstk() | S' * A } Berechnung durch:

Followk(A) = {Firstk() k Followk(B) | B A} Auch das ursprüngliche Startsymbol S der Grammatik

hat in seiner Followk-Menge nur Zeichenketten der Länge k, weil die Grammatik um die Regel S' S k mit dem neuen Startsymbol S' ergänzt wurde.

Followk(S') = {}

28

Beispiel: Ergebnis der Berechnung der Follow1-Menge für Grammatik 2

Beispielgrammatik 2

It Follow1(REG) Follow1(ALT) Follow1(A) Follow1(KON) Follow1(K) Follow1(SYM) Follow1(S)

0

1 {}

2 {} {}

3 {} {} {} {, | }

4 {} {} {} {, | } {, | } {, | , . }

5 {} {, )} {} {, | } {, | } {, | , . } {, | , . }

6 {} {, )} {, )} {, ), | } {, | } {, | , . } {, | , . }

7 {} {, )} {, )} {, ), | } {, ), | } {, | , ), . } {, | , . }

8 {} {, )} {, )} {, ), | } {, ), | } {, | , ), . } {, | , ), . }

9 {} {, )} {, )} {, ), | } {, ), | } {, | , ), . } {, | , ), . }

29

Prüfen der starken LL(k)-Eigenschaft

Zur Erinnerung; gezeigt werden sollte für S * A und S * 'A' ''' :{prek() | * und * } {prek(') | * und '' * '} =

und ' waren verschiedene Alternativen des Metasymbols A an der Stapelspitze.

aus und ' lassen sich terminale Zeichenketten ableiten, deren Präfixe Elemente der Followk-Mengen des Metasymbols A sind

Die obige Eigenschaft kann somit umgeschrieben werden zu:Firstk([A,i]) k Followk(A) Firstk([A,j]) k Followk(A) = für alle 1 i j |[A]|

Eine Grammatik ist damit genau dann eine starke LL(k)-Grammtik, wenn letztere Eigenschaft gilt.

In der Parsersituation (A,) ist damit die Alternative i von A zu wählen, für die gilt: prek() Firstk([A,i]) k Followk(A)

30

Beispiel: Prüfen der starken LL(1)-Eigenschaft

Grammatik:REG ALT ALT KON A A | "|" ALT KON SYM KK | "." KON SYM "a" | … | "Z" | $ | "(" ALT ")" SS | "*"

First1-Mengen:

Follow1-Mengen:

Test für REG, ALT, KON nicht notwendig. Für A, K und S ergibt sich:First1([A,1]) 1 Follow1(A) First1([A,2]) 1 Follow1(A) = First1([K,1]) 1 Follow1(K) First1([K,2]) 1 Follow1(K) = First1([S,1]) 1 Follow1(S) First1([S,2]) 1 Follow1(S) =

Test für SYM auch klar.

First1(REG) First1(ALT) First1(A) First1(KON) First1(K) First1(SYM) First1(S)

{a,…,Z,$,(} {a,…,Z,$,(} {, | } {a,…,Z,$,(} {, . } {a,…,Z,$,(} {, *}

Follow1(REG) Follow1(ALT) Follow1(A) Follow1(KON) Follow1(K) Follow1(SYM) Follow1(S)

{} {, )} {, )} {, ), | } {, ), | } {, | , ), . } {, | , ), . }

31

Syntaxgesteuerter starker LL(k)-Parser

Berechnung einer Steuerfunktion T : M k durch:

Steuerung der Analyse mit dem PDA durch diese Funktion mittels:

(a,a) (,), falls a (A,) ([A,i],), falls A M und i = T(A,prek()) Fehler, in den Situationen:

(a,b), falls a b und a,b (A,), falls A M und T(A,prek()) = error

, falls ([ , ]) ( )( , ) :

, sonstk ki First A i Follow A

T Aerror

aa

ì Î ·ïïï= íïïïî

32

Beispiel: Steuerfunktion (-tabelle)

T a … Z $ ( ) | . *

REG 1 … 1 1 1

ALT 1 … 1 1 1

A 1 2 1

KON 1 … 1 1 1

K 1 1 2 1

SYM 1 … 26 27 28

S 1 1 1 2 1

Leere Felder markieren Fehlerzustände (Eintrag = error)

33

Beispiel syntaxgesteuerte Analyse

Stapel Resteingabe linearisierte Syntaxbaum

REG (a)*|bALT (a)*|b (REG,1)

A KON (a)*|b (ALT,1)

A K SYM (a)*|b (KON,1)

A K S ) ALT ( (a)*|b (SYM,28)

A K S ) ALT a)*|b A K S ) A KON a)*|b (ALT,1)

A K S ) A K SYM a)*|b (KON,1)

A K S ) A K a a)*|b (SYM,1)

A K S ) A K )*|b A K S ) A )*|b (K,1)

A K S ) )*|b (A,1)

A K S *|b A K * *|b (S,2)

A K |b A |b (K,1)

ALT | |b (A,2)

ALT b A KON b (ALT,1)

A K SYM b (KON,1)

A K b b (SYM,2)

A K A (K,1)

(A,1)

T a … Z $ ( ) | . *

REG 1 … 1 1 1

ALT 1 … 1 1 1

A 1 2 1

KON 1 … 1 1 1

K 1 1 2 1

SYM 1 … 26 27 28

S 1 1 1 2 1

REG ALT ALT KON A A | "|" ALT KON SYM KK | "." KON SYM "a" | … | "Z" | $ | "(" ALT ")" SS | "*"

Parser: Steuertabelle:

Grammatik:

34

Syntaxgebundener starker LL(k)-Parser

Berechnung der Steuerfunktion T wie im syntaxgesteuerten Fall.

Für jedes Metasymbol A Implementierung einer Funktion int A(): Funktion A wählt eine Alternative von A auf

Grund der k Look-Ahead Symbole und leitet einen Präfix der anliegenden Eingabe aus dieser Alternative ab, indem:

Für Metasymbole die zugehörige Funktion aufgerufen wird.

Für Terminalsymbole überprüft wird, ob diese in der Eingabe auftreten.

Rückgabewert gibt Auskunft über eine erfolgreiche Ableitung.

35

Beispiel: Syntaxgebundene Implementierung für SYM und S in Grammatik 2

int SYM(){ char buf[8]; if((currMor() >= 'a' && currMor() <= 'z') || currMor() == '$') { if(currMor() == '$') sprintf(buf,"SYM_27"); else sprintf(buf,"SYM_%d",currMor() - 'a' + 1); outSyn(buf); nextMor(); return 1; }

if(currMor() == OPENPAR) { nextMor(); outSyn("SYM_28"); if(!ALT()) return 0; if(currMor() != CLOSEPAR) return 0; nextMor(); return S(); } return 0;}

int S(){ if(nextMor() == '*') { nextMor(); outSyn("S_2"); return 1; } if(currMor() == ENDOF || currMor() == CLOSEPAR || currMor() == PIPE || currMor() == DOT) { outSyn("SYM_1"); return 1; } return 0;}

36

Beispiel: Syntaxgebundene Implementierung für Grammatik 1

Links-Faktorisierung direkt implementierbar Dadurch Erzeugung einer Postfixlinearisierung des

Syntaxbaums erforderlich.int ALT(){ int res; if(!KON()) return 0; if(currMor() == PIPE) { nextMor(); res = ALT(); outSyn("ALT_2"); return res; } if(currMor() == ENDOF || currMor() == CLOSEPAR) { outSyn("ALT_1"); return 1; } return 0;}

int KON(){ int res; if(!SYM()) return 0; if(currMor() == DOT) { nextMor(); res = KON(); outSyn("KON_2"); return res; } if(currMor() == ENDOF || currMor() == CLOSEPAR || currMor() == PIPE) { outSyn("KON_1"); return 1; } return 0;}

Vollständiger Quelltext mit Fehlerbehandlung Ausführbares Programm

37

Behandlung von Linksrekursivitäten

Generell können linksrekursive Grammatiken nicht mit deterministischen tabellengesteuerten Top-Down-Parsern behandelt werden.

GrammatikA A "+" M | A "–" M | MM M "*" T | M "/" T | TT "n" | "(" A ")"muss entweder transformiert werden (vgl. Folie 30 im Kapitel Grundlagen) oder

Lösung bei syntaxgebundener Implementierung: Behandlung der Linksrekursivität wie eine

Rechtsrekursivität Aufbau des Syntaxbaums als Postfixlinearisierung Dadurch entsteht der Syntaxbaum der linksrekursiven

GrammatikVollständiger Quelltext mit Fehlerbehandlung Ausführbare Datei

38

Transformation von Linksrekursivitäten bei syntaxgebundener Implementierung

Regel der Form A A1 | | An | 1 | | m transformieren in:

check() prüft, ob sich ein Präfix der Eingabe aus ableiten lässt.

int A(){ if (currMor() Firstk(1)kFollowk(A)) { check(1); outSyn((A,n+1)); do { if (currMor() Firstk(1)kFollowk(A)) { check(1); outSyn((A,1)); } else if (currMor() Firstk(2)kFollowk(A)) { check(2); outSyn((A,2)); } else if ... else return 1; }while(1); } else if (currMor() Firstk(2)kFollowk(A)) { ... } ... return 0;}

39

Motivation schwache LL(k)-Grammatiken

Es gibt Grammatiken, bei denen Faktorisierung nicht hilft:

S aSab | bSba | cAA a | ab | cA

Problem ist, dass für jedes k genügend lange und in Followk(A) existieren, so dass:Firstk([A,i])k{} Firstk([A,j])k{}

Aber: Bei der Entscheidung zwischen [A,i] und [A,j] folgen in der Analysesituation (A,) dem A nicht beliebige Zeichenketten oder aus Followk(A) sondern nur solche, die aus (Stapelinhalt) ableitbar sind.

Deshalb: Einbeziehung des Stapelinhalts in die Entscheidung für eine Alternative.

40

Definition schwache LL(k)-Grammatik

Es sei G = (, M, R, S) eine Grammatik und k . Dann ist G eine schwache LL(k)-Grammatik, wenn gilt: Existieren zwei Linksableitungen

und prek() = prek('), dann folgt, dass = ', wobei ,,' *, S,A M und ,,' V*

Unterschied zur Definition einer starken LL(k)-Grammatik: Existieren zwei Linksableitungen

und prek() = prek(') dann folgt, dass = ', wobei ,,',' *, S,A M und ,,',' V*

Folgerungen: Jede schwache LL(k)-Grammatik ist eindeutig Jede starke LL(k)-Grammatik ist eine schwache LL(k)-Grammatik

S * *

A *

'A' ''' * ''

S * A

*

' * '

Entscheidung in Analysesituation (A,)

Entscheidung in Analysesituation (A,')

Entscheidung in Analysesituation (A,)

Entscheidung in Analysesituation ('A,')

41

Prüfen der schwachen LL(k)-Eigenschaft

Es sei G = (, M, R, S) eine Grammatik und k . Dann ist G eine schwache LL(k)-Grammatik, wenn gilt: Existieren zwei Linksableitungen

und prek() = prek('), dann folgt, dass = ', wobei ,,' *, S,A M und ,,' V*

Die Entscheidung zwischen zwei verschiedenen Alternativen und ' kann damit eindeutig getroffen werden, wenn alle aus ableitbaren terminalen Zeichenketten sich in den ersten k Symbolen von allen aus ' ableitbaren terminalen Zeichenketten unterscheiden:

{prek() | *, * } {prek() | *, ' * '} =

Zur Überprüfung dieser Eigenschaft müssen die Präfixmengen der verschiedenen Rechts-Kontexte , die hinter A in einer Linksableitung auftreten können, separat berechnet werden.

Ziel: Berechnung einer Menge k(A) := {Firstk() | S * A ist Linksableitung}

S * A

*

' * '

42

Herleitung für Berechnung der k(A)-Mengen

Verschiedene Rechts-Kontexte entstehen durch die Wahl verschiedener Alternativen in vorangegangenen Ableitungsschritten:

Da die Wahl einer Alternative von A in Abhängigkeit vom konkreten Rechts-Kontext getroffen werden soll, muss unterschieden werden, ob A durch die Regel B' 'nA'n oder B nAn entstanden ist.

Es müssen für diese Regeln die wohl unterschiedenen Followk-Mengen Firstk() und Firstk(') gebildet werden.

Falls Firstk(n') und/oder Firstk(n) für eine Alternative von A nicht genügend lange Zeichenketten enthalten, muss diese Betrachtung für die Entstehung von B bzw. B' wiederholt werden.

Da B und B' sich selbst auch in verschiedenen Rechts-Kontexten {F1,…Fn} bzw. {F'1,…,F'n} befinden können, kann sich A in den Rechts-Kontexten Firstk(n) F1,…, Firstk(n) Fn, Firstk('n) F'1,…, Firstk('n) F'n befinden.

Wir erhalten deshalb allgemein:k(A) = {Firstk() k F | B A und F k(B)}

S * *

1'…n-2'C'n-2'…1' 1'…n-1'B'n-1'…1' 1'…n'An'…1' = 'A'

1…n-2Cn-2…1 1…n-1Bn-1…1 1…nAn…1 = A

43

Berechnung der k(A)-Mengen

Durch k(A) = {Firstk() k F | B A und F k(B)} wird für die Metasymbole einer Grammatik ein Mengengleichungssytem definiert, dessen kleinster Fixpunkt die k(A)-Mengen der Metasymbole A sind.

BeispielgrammatikS' S S aSab | bSba | cAA a | ab | cA

Mengengleichungssystem für k = 3:3(S') = {{}}3(S) = {First3(ab) 3 F, First3(ba) 3 F | F 3(S)}

{First3() 3 F | F 3(S')} 3(A) = {First3() 3 F | F 3(S)} {First3() 3 F | F 3(A)}

It 3(S') 3(S) 3(A)

0

1 {{}}

2 {{}} {{}}

3 {{}} {{ab},{ba},{}} {{}}

4 {{}} {{aba},{abb},{ab},{baa},{bab},{ba},{}}

{{ab},{ba},{}}

5 {{}} {{aba},{abb},{ab},{baa},{bab},{ba},{}}

{{aba},{abb},{ab},{baa},{bab},{ba},{}}

6 {{}} {{aba},{abb},{ab},{baa},{bab},{ba},{}}

{{aba},{abb},{ab},{baa},{bab},{ba},{}}

44

Prüfen der schwachen LL(k)-Eigenschaft

Eine Grammatik G hat genau dann die schwache LL(k)-Eigenschaft, wenn ein k existiert, so dass A M 1 i j |[A]| F k(A) gilt Firstk([A,i]) k F Firstk([A,j]) k F = .

BeispielgrammatikS' S S aSab | bSba | cAA a | ab | cA

3-Mengen:

Test für [A,1] und [A,2] und k = 3:

3(S') 3(S) 3(A)

{{}} {{aba},{abb},{ab},{baa},{bab},{ba},{}}

{{aba},{abb},{ab},{baa},{bab},{ba},{}}

3(A) {aba} {abb} {ab} {baa} {bab} {ba} {}

Firstk([A,1]) k F {aab} {aab} {aab} {aba} {aba} {aba} {a}

Firstk([A,2]) k F {aba} {aba} {aba} {abb} {abb} {abb} {ab}

45

Schwacher LL(k)-Automat

Falls die schwache LL(k)-Eigenschaft für eine Grammatik G abgesichert ist, kann der PDA wie folgt arbeiten:

Steuerung der Analyse durch: (a,a) (,), falls a (A,) ([A,i],), falls A M und prek() Firstk([A,i]) Fehler, in den Situationen:

(a,b), falls a b und a,b (A,), falls A M und für alle 1 i |[A]|

prek() Firstk([A,i])

Problem: bei jedem Takt der Art 2 muss die Firstk-Menge des Kellerinhalts berechnet werden.

Ziel: Berechnung einer Steuertabelle wie bei starken LL(k)-Grammatiken

46

Vereinfachung der Berechnung während der Analyse

Prinzip: Jedes Metasymbol A im Stapel wird mit der Firstk-Menge F des darunter befindlichen Stapelinhalts markiert: AF.

Einfache Berechnung der Markierung neu eingestapelter Metasymbole durch: AF aktueller Kellerinhalt mit Firstk() = F Jedes Metasymbol B in Alternative B von A wird

beim Ersetzen von A durch diese Alternative markiert mit Firstk() k Firstk() = Firstk() k F.

Startsituation des PDA: (S'{},). Firstk() kann auch im Voraus berechnet werden.

47

Vereinfachter schwacher LL(k)-Automat

Mit den markierten Metasymbolen im Stapel kann die Arbeitsweise des PDA vereinfacht werden:

Steuerung der Analyse durch: (a,a) (,), falls a (AF,) ([A,i],), falls A M und

prek() Firstk([A,i]) k F, wobei jedes Metasymbol in [A,i] beim Einstapeln wie auf voriger Folie beschrieben markiert wird.

Fehler, in den Situationen: (a,b), falls a b und a,b (AF,), falls A M und für alle 1 i |[A]|

prek() Firstk([A,i]) k F

48

Transformation einer schwachen LL(k)-Grammatik

Fakt 1: In der Situation (AF,) ändert sich bei Ersetzung von AF durch Alternative = B die Markierung der Metasymbole B in nach dem Einstapeln nicht mehr und hängt nur von der Markierung von A und ab.

Fakt 2: Markierungen eines Metasymbols A sind nur Elemente aus k(A).

Dadurch ist die Berechnung aller möglichen Markierungen in im Voraus möglich.

Man erhält zur schwachen LL(k)-Grammatik (, M, S',R) die transformierte Grammatik (, Mm, S'{},Rm) durch:

Mm := {AF | A M und F k(A)}

1 2

00 1 1 1 1

0 0 1 1 1 1

1 1 1

( , ) :

( , ) und ( ) und

( )

nF F F Fn n n m

n n n k

i k i i i n n n k

A A A A R

A A A A R F A

F First A A F

a a a a

a a a a

a a a a

- -

- -

+ - -

Î Û

Î Î

= ·

F

K

K

K

49

Beispiel für Transformation

F {} {aba} {abb} {ab} {baa} {bab} {ba} {}

Nummer

0 1 2 3 4 5 6 7

S' S S aSab | bSba | cAA a | ab | cA

1 2

00 1 1 1 1

0 0 1 1 1

1 1 1

( , ) :

( , ) und ( ) und

( )

nF F F Fn n n m

n n n k

i k i i i n n n k

A A A A R

A A A A R F A

F First A A F

a a a a

a a a a

a a a a

- -

-

+ + -

Î Û

Î Î

= ·

F

K

K

K

S'0 S7 S7 aS3ab | bS6ba | cA7

S6 aS2ab | bS5ba | cA6

S5 aS2ab | bS5ba | cA5

S3 aS1ab | bS4ba | cA3

S4 aS2ab | bS5ba | cA4

S2 aS1ab | bS4ba | cA2

S1 aS1ab | bS4ba | cA1

A7 a | ab | cA7

A6 a | ab | cA6

A5 a | ab | cA5

A4 a | ab | cA4

A3 a | ab | cA3

A2 a | ab | cA2

A1 a | ab | cA1

Transformierte Grammatik:

Ursprüngliche Grammatik: Transformationsregel:

Nummerierung der F 3-Mengen

50

PDA für transformierte schwache LL(k)-Grammatik

Da in jeder Regel bereits alle Metasymbole markiert sind, kann die Berechnung der Markierungen während der Analyse entfallen.

Steuerung der Analyse durch: (a,a) (,), falls a (AF,) ([AF,i],), falls AF Mm und

prek() Firstk([AF,i]) k F Fehler, in den Situationen:

(a,b), falls a b und a,b (AF,), falls AF Mm und für alle 1 i |[AF]|

prek() Firstk([AF,i]) k F Entscheidung für eine Alternative i von AF basiert

nur noch auf den Mengen Firstk([AF,i]) und F Konsequenz: Konstruktion einer Steuerfunktion

möglich

51

Steuerfunktion für transformierte schwache LL(k)-Grammatiken

Berechnung einer Steuerfunktion T : Mm k durch:

Steuerung der Analyse mit dem PDA durch diese Funktion mittels:

(a,a) (,), falls a (AF,) ([AF,i],), falls AF Mm und i = T(AF,prek()) Fehler, in den Situationen:

(a,b), falls a b und a,b (AF,), falls AF Mm und T(AF,prek()) = error

Dieser PDA ist ein PDA für eine starke LL(k)-Grammatik, weil für jedes AF Mm gilt: Followk(AF) = F.

, falls ([ , ])( , ) :

, sonst

Fk kF

i First A i FT A

error

aa

ìï Î ·ïï= íïïïî

52

Zusammenfassung LL(k)-Grammatiken

Jede schwache LL(k)-Grammatik kann in eine bedeutungsäquivalente starke LL(k)-Grammatik transformiert werden.

Jede Sprache, die mit einer schwachen LL(k)-Grammatik analysiert werden kann, kann auch mit einer starken LL(k)-Grammatik analysiert werden.

Es gibt Sprachen, die mit LL(k) aber nicht mit LL(k-1) (für 1 k) analysierbar sind.

Es gibt deterministisch analysierbare Sprachen, die nicht mit LL(k)-Verfahren analysierbar sind.

53

LR(k)-Grammatik

Es sei S * A eine Rechtsableitung zu einer kontextfreien Grammatik G = (, M, R, S).

Dann heißt Griff der Rechtssatzform . Bei eindeutigen Grammatiken ist der Griff das nächste Teilwort, das im Keller des Bottom-Up-PDA ersetzt wird.

Jeder Präfix von heißt zuverlässiger Präfix von G. Solche Präfix einer Rechtssatzform, erstrecken sich nicht über den Griff dieser Rechtssatzform hinaus. Sie sind somit Kellerinhalt des Bottom-Up-PDA.

Es sei G eine kfG und k . Dann besitzt G die LR(k)-Eigenschaft, falls gilt: Existieren zwei Rechtsableitungen

und prek() = prek(), dann folgt, dass A = A', = ' und = ', wobei S, A, A' M und , , ' * und , , ' V*.

Das bedeutet, dass für jedes w L(G) in jeder Rechtssatzform der Ableitung von w der längste zuverlässige Präfix und der Griff eindeutig durch die ersten k Symbole nach dem Griff (in ) bestimmt sind.

S * *

A

'A''

54

LR(k)-Item

[A ., ] ist genau dann ein gültiges LR(k)-Item für die Grammatik G, wenn S * A eine Rechtsableitung und = prek(), wobei *, V* und A R.

Zu einem zuverlässigen Präfix gehört somit die Menge der gültigen Items:Items() = {[A ., ] | S * A und = prek()}

Wichtig: Aus [A .B, ] Items() und B R folgt: Für alle ' (Firstk() gilt [B ., '] Items(). (Closure)

Ein Item [A ., ] Items() repräsentiert eine Analysesituation (, ) des Bottom-Up-PDA, in der

der PDA bereits einen Anfang * der Eingabe verarbeitet hat: (, ) * (, )

Die Analyse sich gerade in einem Teil des Wortes befindet, der aus abgeleitet wurde. D.h. es gibt u, v * mit * u und * v, wobei :

u bereits gelesen wurde ( = wu) und erwartet wird, dass mit v beginnt ( = vz)

Nachdem v gesehen und zu reduziert wurde, kann dann zu A reduziert werden

Das entspricht der zu [A ., ] Items() existierenden Rechtsableitung S * Az z * vz * , wobei A, S M und , , V* und v, , z *.

55

LR(k)-Analyse

Es gilt: G hat die LR(k)-Eigenschaft, gdw. für jeden zuverlässigen Präfix gilt: Wenn [A ., ] Items(), dann gilt für alle [B ., '] Items(), dass aus Firstk(') folgt: A = B, = , = und damit = '.

Dadurch ist in jeder Satzform einer Rechtsableitung der Griff eindeutig bestimmt und der Bottom-Up-PDA muss wie folgt arbeiten:

(, ) (A, ) gdw. [A .,prek()] Items() (Reduzieren) (, a) (a, ) gdw. = ' und

[A ., ] Items() und prek(a) Firstk() und V+ (Schieben)

56

LR(k)-Analyse mit dem PDA

Damit ist abgesichert, dass für jeden möglichen Kellerinhalt (zuverlässigen Präfix) eindeutig entschieden werden kann, ob und wenn ja nach welcher Regel reduziert werden muss.

Das heißt: Es gibt keine Konflikte beim Schieben und Reduzieren, weil für jeden zuverlässigen Präfix und die Resteingabe gilt:

[A .,prek()] Items() und [B .,prek()] Items(), dann A = B und = .

[A .,prek()] Items() und [B .,prek()] Items() und prek() Firstk( prek()) , dann A = B und = und = .

Problem: Für jeden Kellerinhalt muss während der Analyse Items() berechnet werden.

57

Mitführen des Stapelzustandes

Lösung: Zu jedem Symbol ai V im Keller mit Inhalt a1…an wird die zugehörige Menge Items(a1…ai) mitgeführt für 1 i n.

Aus diesen bekannten Item-Mengen soll beim Einstapeln (Schieben) eines neuen Symbols

a die Menge Items(a1…ana) berechnet werden, beim Reduzieren nach A ak…an die Menge

Items(a1…ak-1A) berechnet werden. Die Menge aller möglichen gültigen Items ist

endlich, damit auch die Menge aller ihrer Teilmengen.

Somit ist eine Vorausberechnung möglich.

58

Vorausberechnung der Items beim Einstapeln

Beziehung zwischen Items() und Items(a) beim Einstapeln von a:

Aus Einstapeln von a bei Stapelinhalt folgt: Es ex. eine Rechtssatzform S * A a, weil [A .a, prek()] Items().

Falls = b', dann gibt es eine Rechtsableitung S * A ab' und somit [A a., prek()] Items(a)

Falls = , dann gibt es eine Rechtsableitung S * A a und somit [A a., prek()] Items(a)

Falls = B', dann gibt es eine Rechtsableitung S * aB' * aB' und somit [A a.B', prek()] Items(a). Außerdem [B ., ] Items(a) für alle Firstk('), weil aB' a' eine Rechtsableitung ist (Closure auf Folie 51).

Konsequenz: Konstruktion von Items(a) ist mittels der Menge Items() und der Regeln der Grammatik möglich!

59

Konstruktion der erforderlichen Item-Mengen für Schiebe-Takte

Für die zu einem Kellerinhalt gehörenden Itemmenge I am obersten Kellersymbol kann für die Items [A .a, ] I, die ein Einstapeln des Symbols a verlangen, die zugehörige neue Itemmenge I' berechnet werden durch:

I' := {[A a., ] | [A .a, ] I} I' := closure(I'), wobei

closure(I) = I {[B ., '] | [A .B, ] I und B R und ' (Firstk()}

Ist also das Symbol an der Kellerspitze mit I beschriftet und a wird eingestapelt, dann wird a mit I' beschriftet.

Zu der Startsituation (,w) des Parsers, gehört die Itemmenge closure({[S' .Sk, ]}).

Es verbleibt die Vorausberechnung der Analysesituationen, die der Parser nach einem Reduktionstakt einnimmt.

60

Vorausberechnung der Items beim Reduzieren

Beziehung zwischen Items() und Items(A) beim Reduzieren nach A :

Aus Reduzieren nach A bei Stapelinhalt folgt: Es ex. S * A , weil [A ., prek()] Items().

Damit ex. auch S' * 'B' 'A'' * 'A = A. Damit ist [B A.', prek(')] Items(A), weil = '. Zur Berechnung von Items(A) wird die Menge Items()

verwendet, weil gilt: [B A.', prek(')] Items(A), gdw. [B .A', prek(')] Items().

Konsequenz: Konstruktion von Items(A) ist bei Stapelinhalt aus der Menge Items() möglich, die mit dem obersten Symbol in gespeichert wurde.

61

Konstruktion der erforderlichen Item-Mengen für Reduktions-Takte

Falls bei Kellerinhalt nach A reduziert wird, so ergibt sich die Itemmenge für das neue Symbol A an der Spitze des Stapelinhalts A durch:

Ausstapeln von und Lesen der Itemmenge I an der Spitze des Stapelinhalts ,

I' := {[B A.', ] | [B .A', ] Items()}, I' := closure(I').

Da durch Ausstapeln eines jede zuvor eingestapelte Itemmenge an der Stapelspitze freigelegt werden kann, die ein Item der Form [B .A', ] enthält, ergibt sich folgender Algorithmus zur Konstruktion aller Itemmengen:

62

Berechnung der Item-Mengen

Berechnung aller Itemmengen; ist eine Menge von Itemmengen

Berechnung der Übergänge von einer Itemmenge in eine andere

:= {closure([S'.S,k])} := do ' := for each z do (, ) := goto(z,(, ))while( ≠ ')

goto(z,(, ))for each x V do z' := for each [A .x,] z do z' := z' {[A x.,] } od if z' ≠ then := closure(z') := (z,x,z') fiodreturn (, );

closure(z)do z' := z; for each [A .B,] z do z := z {(B .,)|B R und Firstk(,)} odwhile(z ≠ z')return z; Beispiel

63

Eigenschaften des konstruierten Automaten

Die beschriebene Konstruktion erzeugt einen Automaten (,V,closure([S' .S,k]),,) für den gilt: Seine Zustandsmenge ist endlich Die Zustandsübergänge sind deterministisch Der Automat erkennt genau die zuverlässigen

Präfixe der Grammatik (Beweis durch Induktion über die Anzahl der Zustandsübergänge)

Dieser Automat wird als kanonischer DEA bezeichnet, kurz: (,).

64

Konstruktion der kanonischen Steuerfunktion

Es sei (,) der kanonische DEA. Die Steuerfunktion T : k ist dann definiert als:

Kellerelemente sind Zweitupel aus V. Startsituation ((closure([S' .S,k]),S'),). Verhalten des Automaten:

((I,x),a) ((I,x)(I',a),), falls T(I,prek(a)) = shift und (I,a,I') ((I0,x0)…(In,xn),a) ((I0,x0)(I,A),a), falls

T(In,prek(a)) = reduce(A ) und | | = n und (I0,A,I) ((I,S),k) mit (closure([S' .S,k]),S,I) ist akzeptierender

Endzustand Offensichtlich genügt es sogar nur die Items im Keller zu

speichern. Damit Vereinfachung der kanonischen Steuerfunktion.

( ), falls [ ., ]

( , ) : , falls [ . , ] und ( ) und 0 | |

, sonstk

reduce A A I

T I shift A I First

error

b b a

a b g c a gc g

ìï ® ® Îïïïï= ® Î Î <íïïïïïî

65

Vereinfachung der kanonischen Steuerfunktion

Es sei (,) der kanonische DEA. Die Steuerfunktion T : (k M) ist dann definiert als:

Kellerelemente sind Itemmengen. Verhalten des Automaten:

(I,a) (II',), falls T(I,prek(a)) = shift I' (I0I1…In,a) (I,a), falls

T(In,prek(a)) = reduce(A ) und | | = n und T(I0,A) = I (I,) ist akzeptierende Situation, falls T(I,prek()) = accept Error in allen anderen Situationen

, falls [ ., ] und

( ), falls [ ., ]

, falls [ . , ] und ( ) und 0 | |( , ) :

und ( , (0))

, falls und ( , , )

, sonst

k k

k

accept S' S I

reduce A A I

shift I ' A I FirstT I

I I '

I ' M I I'

error

a

b b a

b g c a gc ga

d a

a a d

ìï ® Î =ïïïï ® ® Îïïïï ® Î Î <ïïï= íï =ïïÎ Î

î

ïïïïïïïïï

66

Konflikte

Schiebe-Reduktions-Konflikt: Ein Schiebe-Reduktions-Konflikt ist in der Itemmenge I vorhanden, falls es Items [A ., ] I und [B .c, '] I mit , , V* und c und Firstk(c') gibt.

Reduktions-Reduktions-Konflikt: Ein Reduktions-Reduktions-Konflikt ist in der Itemmenge I vorhanden, falls es verschiedene Items [A ., ] I und [B ., '] I mit , V* und = ' gibt.

Konflikte treten genau dann auf, wenn die Grammatik nicht die LR(k)-Eigenschaft hat.

Konstruktion des kanonische DEA ist damit ein Test für die LR(k)-Eigenschaft.

Auflösen von Konflikten durch: Umschreiben der Grammatik. Definition der Steuerfunktion entsprechend eines Items. D.h.,

Auflösen des Konflikts bzgl. Schieben oder Reduzieren nach einer bestimmten Regel.

67

LALR (Lookahead-LR)

Der Kern kern(I) einer Itemmenge I ist definiert als {[A .] | : [A ., ] I}

Verkleinerung der Zustandsmenge eines kanonischen LR(1) DEA durch Zusammenfassen von Itemmengen mit dem gleichen Kern:

' := {[I] | I und [I] := {I' | kern(I) = kern(I')}} '([I],x) := [I'], falls (I,x) := I'

Konstruktion der LALR-Steuertabelle entspricht der Konstruktion der kanonischen Steuertabelle.

Verkleinerung der Zustandsmenge in praktischen Programmiersprachen dadurch in etwa um den Faktor 10.

68

Beispiel: kanonischer DEA

[S' .S,][S .CC,][C .cC,c][C .cC,d][C .d,c][C .d,d]

[S' S.,]

I0 I1

[S C.C,][C c.C,][C .d, ]

I2

[S CC.,]I5

[C c.C,][C .cC, ][C .d, ]

I6

[C d., ]I7

[C c.C,c][C c.C,d][C .cC,c][C .cC,d][C .d,c][C .d,d]

I3

[C d., c][C d., d]

I4

[C cC.,c][C cC.,d]

I8

[C cC., ]I9

S

C

c

d

d

c

C

d

c

d

C

C

Grammatik:S' SS C CC c C | d

c

69

Beispiel: LALR-DEA

[S' .S,][S .CC,][C .cC,c][C .cC,d][C .d,c][C .d,d]

[S' S.,]

I0 I1

[S C.C,][C c.C,][C .d, ]

I2

[S CC.,]I5

[C c.C,][C .cC, ][C .d, ][C c.C,c][C c.C,d][C .cC,c][C .cC,d][C .d,c][C .d,d]

I6

[C d., c][C d., d][C d., ]

I4/I7

[C cC., ][C cC.,c][C cC.,d]

I8/I9

S

C

c

d

c

d

c

d

C

C

Grammatik:S' SS C CC c C | d

70

Eigenschaften LALR-Steuertabelle

Eigenschaften dieser Transformation: Der entstehende Endliche Automat ist wieder

deterministisch. Hatten die Zustände des kanonischen DEA keine

Schiebe-Reduziere-Konflikte, dann haben auch die Zustände des LALR-DEA solche Konflikte nicht.

Es können Reduziere-Reduziere-Konflikte entstehen.

Konsequenz: Es gibt Grammatiken, für die eine kanonische Steuerfunktion aber keine LALR-Steuerfunktion existiert.

71

Beispiel

GrammatikS' SS a A d | b B d | a B e | b A eA cB c

besitzt eine kanonische LR(1) Steuertabelle aber keine LALR-Steuertabelle, weil

Items(ac) = {[A c., d], [B c., e]} Items(bc) = {[A c., e], [B c., d]} Damit entsteht im LALR-DEA eine

Reduktions-Reduktions-Konflikt.

72

SLR (Simple LR)

Verkleinerung der Menge durch Verzicht auf die Vorschau in den Itemmengen.

besteht damit nur noch aus den Kernen: ' = {kern(I) | I } '(kern(I),x) := kern(I'), falls (I,x) := I'

Konsequenz: Der SLR-DEA hat genauso viele Zustände wie der LALR-DEA. Die Berechnung des SLR-DEA vereinfacht sich jedoch.

73

Berechnung der Item-Mengen für SLR-DEA

Berechnung aller Itemmengen; ist eine Menge von Itemmengen

Berechnung der Übergänge von einer Itemmenge in eine andere

:= {closure([S'.S])} := do ' := for each z ∈ do (, ) := goto(z,(, ))while( ≠ ')

goto(z,(, ))for each x V do z' := for each [A .x] z do z' := z' {[A x.]} od if z ≠ then := closure(z') := (z,x,z') fiodreturn (, );

closure(z)do z' := z; for each [A .B] z do z := z {[B .]|B R} odwhile(z ≠ z')return z;

74

SLR Steuerfunktion

Da in den Items die Vorausschau fehlt, muss mit den Followk-Mengen gearbeitet werden:

, falls [ .] und

( , ), falls [ .] ( )

, falls [ . ] und ( ) ( ) ( , ) :

und 0 | | und ( , (0), )

, falls und ( , , )

, so

k

k

k k k

accept S' S I

reduce A I' A I Follow A

shift I ' A I First Follow AT I

I I '

I ' M I I'

error

a

b b a

b g a ga

g a d

a a d

® Î =

® ® Î Î

® Î Î ·=

< Î

Î Î

nst

ìïïïïïïïïïïïïíïïïïïïïïïïïïî

75

Beispiel: SLR(1)-Grammatik

Grammatik (vereinfacht) für arithmetische Ausdrücke ist SLR(1):

S' AA A + M | MM M * T | TT Number | ( A )

[S' .A][A .A+M][A .M][M .M*T][M .T][T .Number][T .( A )]

[S' A.][S' A.+M]

I0 I1

[A M.][M M.*T]

I2

A

M

[T Number.]

I5[T (.A)][A .A+M][A .M][M .M*T][M .T][T .Number][T .( A )]

I4

[M T.]

I3

[S' A+.M][M .M*T][M .T][T .Number][T .( A )]

I6

[M M*.T][T .Number][T .( A )]

I7

[A A.+M][T (A.)]

I8

[S' A+M.][M M.*T]

I9

[M M*T.]

I10

[T (A).]

I11

Number

T

(

+

*

Number

T

M

(A

+

Number

Number

((

T

M

*

T

)

76

Beispiel: Nicht SLR(1)-Grammatik

Grammatik, die nicht SLR(1) aber LALR(1) ist:S L = RL * R | IdR L

Follow1(R) enthält = (wegen der Ableitung S L=R *R = R)

[S' .S][S .L=R][S .R][L .*R][L .Id][R .L]

I0[S' S.]

I1

[S L.=R][R L.]

I2

[S R.]

I3[L *.R][R .L][L .*R][L .Id]

I4

[L Id.]

I5

[S L=.R][R .L][L .*R][L .Id]

I6

[L *R.]

I7

[R L.]

I8

[S L=R.]

I9S

L =

R

L

**

RR

Id

77

Bison

Zur Generierung des C-Quelltextes eines Parsers. Die vom Parser akzeptierte Sprache wird durch die

Regeln einer kfG spezifiziert. Jeder Alternative eines Metasymbols kann eine Aktion

in Form von C-Quelltext zugeordnet werden. Einfache Einbindung eines mit Flex generierten

Scanners.Lex

Quelltextname.l

Lex Quelltextname.l

LexAufruf:

lex name.l

Scanner als C-Datei:Lex.yy.c

Scanner als C-Datei:Lex.yy.c

C-CompilerAufruf:

gcc lex.yy.c name.tab.c

Ausführ-bare

Datei:a.exe

Ausführ-bare

Datei:a.exe

Bison Quelltextname.y

Bison Quelltextname.y

BisonAufruf:

bison name.y

Parser als C-Datei:

name.tab.c

Parser als C-Datei:

name.tab.c

Morphem-arten:

name.tab..h

Morphem-arten:

name.tab..h

-d

Parser-tabelle

Parser-tabelle

-v

78

Struktur eines Bison-Quelltextes

Deklarationsabschnitt: Token-Deklaration Vereinbarung von Typen für Grammatiksymbole C-Code-Fragmente

Regelabschnitt: Grammatikregeln in BNF Optional zu jeder Regel eine Aktion in Form von C-Code

Funktionsabschnitt C-Funktionen, die in die erzeugte C-Datei kopiert werden

Deklarationsabschnitt%%Regelabschnitt%%Funktionsabschnitt

79

Ablauf der Generierung des Parserquelltextes

…%%A1 : [A1,1] {Aktion 1.1} | [A1,2] {Aktion 1.2} | ……An : [An,1] {Aktion n.1} | [An,2] {Aktion n.2} | …%%…

…%%A1 : [A1,1] {Aktion 1.1} | [A1,2] {Aktion 1.2} | ……An : [An,1] {Aktion n.1} | [An,2] {Aktion n.2} | …%%…

Parser als C-Datei:y.tab.c

Parser als C-Datei:y.tab.c

LALR-DEA

Simulator für PDA

LALR-Steuer-tabelle

Look-AheadPars

ers

tapel

Wert

est

ap

el

Scanner

80

Deklarationsabschnitt

Deklaration von C-Code Eingeschlossen in %{ und %} Deklarieren von Bezeichnern/Typen, die im

Regel- oder Funktionsabschnitt benutzt werden Deklaration von Grammatiksymbolen

Deklaration von Morphemen durch %token Bezeichner

Bezeichner kann in der Regelsektion von bison und flex verwendet werden.

Deklaration von Präzedenzen %left Bezeichner, %right Bezeichner, %nonassoc Bezeichner

Deklaration von spezifischen Typen

81

Regelabschnitt

BFN-Regel A ::= 1 | … | n wird geschrieben als:A : 1 {Aktion1}

| 2 {Aktion2}…| n {Aktionn};

Aktioni ist C-Code, in dem verwendet werden kann: $$, $1,…,$k, wobei $$ der mit A und $i der mit n(i-1) assoziierte Wert ist.

{Aktioni} ist optional; Standardaktion: $$ = $1. Terminalsymbole definiert als Morphemart oder in ' '

eingeschlossen; dann ist ASCII-Code des ersten Zeichens die Morphemart.

-Regel durch Angabe einer leeren Alternative. Übrige Symbole sind Metasymbole.

82

Beispiel: Einfaches Bison-Programm

%{#include <stdlib.h>#include <ctype.h>

void yyerror(char*);int yylex();%}

%token DIGIT

%%line : expr '\n' {printf("%d\n",$1);} ;

expr : expr '+' term {$$ = $1 + $3;} | term ;

term : term '*' factor {$$ = $1 * $3;} | factor ;

factor: '(' expr ')' {$$ = $2;} | DIGIT ;%%

int yylex(){ int c; c = getchar(); if(isdigit(c)) { yylval = c - '0'; return DIGIT; } return c;}

void yyerror(char* err){ printf("%s",err);}

int main(int argc, char* argv[]){ return yyparse();}

Vollständiger Quelltext Ausführbares Programm

83

Beispiel: Mehrdeutige Grammatik

%{#include <ctype.h>%}

%token DIGIT

%%line : expr '\n' {printf("%d\n",$1);} ;

expr : expr '+' expr {$$ = $1 + $3;} | expr '-' expr {$$ = $1 - $3;} | expr '*' expr {$$ = $1 * $3;} | expr '/' expr {$$ = $1 / $3;} | DIGIT ;

%%int yylex(){ int c; c = getchar(); if(isdigit(c)) { yylval = c - '0'; return DIGIT; } return c;} Vollständiger Quelltext

Ausführbares ProgrammAusgabe nach Bison-Aufruf mit Option -v

84

LALR-DEA zum vereinfachten Beispiel

[S' .E,][E .E+E, |+|*][E .E*E, |+|*][E .z, |+|*]

[S' z., |+|*]

I0

I1

[S' E., ][E E.+E, |+|*][E E.*E, |+|*]

I2

z

Grammatik:S' EE E + EE E * EE z

[E E*.E, |+|*][E .E+E, |+|*][E .E*E, |+|*][E .z, |+|*]

I5

E *

[E E+.E, |+|*][E .E+E, |+|*][E .E*E, |+|*][E .z, |+|*]

I3

+

[E E+E., |+|*][E E.+E, |+|*][E E.*E, |+|*]

I4

E

[E E*E., |+|*][E E.+E, |+|*][E E.*E, |+|*]

I6

E*

+

+

*

z

z

85

Auflösen von Konflikten beim Erstellen der Steuertabelle

In der Steuertabelle werden die Konflikte entweder durch Schieben oder Reduzieren aufgelöst.

Durch Reduzieren wird dem letzten eingestapelten Operator eine höhere Priorität als dem nächsten Operator in der Eingabe gegeben. Falls beide Operatoren gleich sind entspricht das einer Linksassoziativität.

Entsprechend wird durch Schieben dem nächsten Operator in der Eingabe eine höhere Priorität als dem letzten eingestapelten Operator gegeben.

Steuertabelle zum LALR-DEA auf voriger Folie:Zustand z + * E

0 Shift 1 2

1 Reduce E z Reduce E z Reduce E z

2 Shift 3 Shift 5 Accept

3 Shift 1 4

4 Shift 3* Shift 5** Reduce E E + E

5 Shift 1 6

6 Reduce E E * E Reduce E E * E Reduce E E * E

* macht + rechtsassoziativ

** gibt * Vorrang vor +

86

Standardbehandlung von Konflikten in Bison

Konflikte in den Itemmengen werden von Bison angezeigt und beim Erstellen der Steuertabelle automatisch wie folgt aufgelöst: Reduktion-Reduktion-Konflikt: Es wird nach der

Regel reduziert, die zuerst in der Bison-Datei aufgeführt wurde.

Reduktion-Schiebe-Konflikt: Schieben wird bevorzugt.

Zum manuellen Beheben von Konflikten erzeugen des LALR-DEA in der Datei y.output mit Option –v.

87

Auflösen der Konflikte mit Bison

Zuordnung von Assoziativitäten zu Terminalsymbolen durch folgende Vereinbarungen im Deklarationsabschnitt:

%left Morphemartenfolge %right Morphemartenfolge %nonassoc Morphemartenfolge

und Prioritäten durch die Reihenfolge der obigen Deklarationen (erste Deklaration hat niedrigste Priorität).

Bei Konflikt zwischen zwei Items [A .,a] und [B .a,b] wird reduziert, falls

Priorität von höher ist, als Priorität von a oder Priorität von und a ist gleich und Assoziativität von ist links

In allen anderen Fällen wird geschoben Priorität und Assoziativität von entsprechen der des am weitesten

rechts stehenden Terminalsymbols in . Priorität und Assoziativität von kann explizit festgelegt werden

durch Anhängen von %prec Terminalsymbol an , wobei Priorität und Assoziativität von Terminalsymbol im Deklarationsteil festgelegt wurde (dieses Symbol muss sonst nirgends verwendet werden)

88

Beispiel: Konflikte aufgelöst

%{#include <ctype.h>%}

%token DIGIT%left '+' '-'%right '*' '/'

%%line : expr '\n' {printf("%d\n",$1);} ;

expr : expr '+' expr {$$ = $1 + $3;} | expr '-' expr {$$ = $1 - $3;} | expr '*' expr {$$ = $1 * $3;} | expr '/' expr {$$ = $1 / $3;} | DIGIT ;

%%int yylex(){ int c; c = getchar(); if(isdigit(c)) { yylval = c - '0'; return DIGIT; } return c;} Vollständiger Quelltext Ausführbares Programm

89

Gemeinsame Nutzung von Bison und Flex

Die Funktion yyparse() fordert Token durch Aufruf von yylex() an.

Flex kann abhängig von der Morphemart Werte unterschiedlicher Datentypen in yylval speichern.

Dafür Deklaration der Datentypen im Deklarationsabschnitt durch:%union { C-Datentyp Bezeichner; …};

Zuordnung der Datentypen zu Morphemarten und Metasymbolen der Grammatik im Deklarationsteil durch:%token <Bezeichner> Tokenbezeichner%type <Bezeichner> Metasymbol

Ausdrücke $$ bzw. $i haben dann den Typ, der dem zugehörigen Symbol zugeordnet wurde.

Vollständiges Beispiel (y, l, ast.c, ast.h, main.c, exe, Quelltext)

90

Zusammenfassung LR(k)-Grammatiken

Jede starke LL(k)-Grammatik ist auch LR(k)-Grammatik.

Jede mit einem deterministischen PDA analysierbare Sprache besitzt eine LR(k)-Grammatik.

Jede LR(k)-Grammatik kann in eine bedeutungsäquivalente LR(1)-Grammatik transformiert werden.

Ende der syntaktischen Analyse

Weiter zur Kontextprüfung