Vorlesung Compilertechnik Sommersemester 2009 Syntaktische Analyse M. Schölzel.
-
Upload
albrecht-zaunbrecher -
Category
Documents
-
view
107 -
download
0
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