Compilerbau Prof. Dr. Wolfgang Schramm
SYNTAXANALYSE – TOP-‐DOWN-‐ANALYSE
3. Kapitel (Teil 1)
1
Syntak'sche Analyse (Parser) 1/2
Aufgaben des Parsers
(1) Überprüfung des Programms hinsichtlich seines strukturellen AuAaus und Erzeugung eines Strukturbaums.
(2) Bearbeitung (Neueinträge/Ergänzungen) der Symboltabelle.
(3) Fehlererkennung / Fehlerbesei'gung.
(4) Unterstützung der seman'schen Analyse.
2
Syntak'sche Analyse (Parser) 2/2
Symboltabelle
Scanner Parser
Quell- pro- gramm
Struktur- baum
Eintragen/
Nachschlagen Eintragen
Symbol Attribut Token
Anfor- derung
3
Parser – Beispiele zu den Aufgaben 1/2
(1) if (a <= 10) max = a;
stmt cond_stmt
if ( boolexpr ) stmt ;
numexpr cop numexpr assignment
id const id = expr numexpr
id
4
Parser – Beispiele zu den Aufgaben 2/2
(2) int a, b, c;
In der Deklaration erkennt der Scanner die lexikalischen Elemente int (Token:
keyword), a, b und c (Token jeweils: id). Er erkennt aber nicht den Zusammenhang:
int ist der Typ von a, b und c.
(3) if a <= 10 max = a;
Der Parser erkennt einen Fehler und korrigiert ihn zu:
if (a <= 10) max = a;
(4) if (a <= 10) max = a;
Der Parser weiß, dass der Ausdruck a <= 10 vom Typ boolean sein muss.
5
Beschreibung der Syntax von Programmiersprachen
Parser
Eingabe Ausgabe
Syntaxbeschreibung
à Kontextfreie Grammatik (in BNF)
Grammatik ist Basis für den Parser
... um festzustellen (= analysieren), ob ein gegebenes Wort (= Programm) zur Sprache gehört.
6
Gramma'k (kontexPrei) 1/2
Eine Gramma'k zur Beschreibung von Syntax ist ein 4-‐Tupel:
G = (T, N, P, S), mit
T: Menge von Token, sog. Terminalsymbole (TS).
N: Menge von Nonterminalsymbolen (NTS).
P: Menge von Produk'onen (oder Produk'onsregeln), wobei jede Produk'on aus einem Nonterminalsymbol (linke Seite der Produk'on) einem Pfeil (→) und einer Folge von Terminalsymbolen und/oder Nonterminalsymbolen (rechte Seite der Produk'on) besteht.
S: Ein ausgezeichnetes Nonterminalsymbol -‐ das Startsymbol.
7
Gramma'k
(kontexPrei) 2/2 Die Sprache L(G) einer GrammaMk besteht aus allen aus dem Startsymbol S abgeleiteten Zeichenke[en (Wörtern), die nur Terminalsymbole enthalten. Ein Wort ist eine Folge von Terminalsymbolen, die durch wiederholtes Anwenden von Regeln ( = Subs'tu'on „rechte Seite einer Produk'on ersetzt linke Seite“) erzeugt werden kann, wobei das Startsymbol S der Ausgangspunkt der Erzeugung ist.Es gibt verschiedene Formalismen zur Beschreibung von kontexPreien Gramma'ken:
¤ Backus Naur Form (BNF).
¤ Erweiterte Backus Naur Form (EBNF).
¤ Syntaxdiagramme.
8
Defini'on Ableitung
Produk'onen sind Ersetzungsregeln.
A → α besagt, dass man ein Aubreten des NTS A innerhalb eines Wortes ϕ durch die Folge von Symbolen α ersetzen darf. Damit verwandelt sich das Ausgangswort ϕ in ein Wort ψ.
Sei G = (T, N, P, S) eine kontexPreie Gramma'k. ψ ist aus ϕ direkt ableitbar (Nota'on: ϕ ⇒ ψ), wenn es Worte σ, τ gibt und eine Produk'on A → α , so dass gilt: ϕ = σΑτ und ψ = σατ. Man sagt ψ ist aus ϕ ableitbar (ϕ produziert ψ; Nota'on: ϕ ⇒* ψ), wenn es eine Folge von Worten ϕ1, ϕ2, ..., ϕn (n ≥ 1) gibt, so dass gilt ϕ = ϕ1, ψ = ϕn und ϕi ⇒ ϕi+1, für 1 ≤ i< n.
Die Folge von Worten, für die ja gilt ϕ1 ⇒ ϕ2 ⇒ . . . ⇒ ϕn heißt eine Ableitung von ψ aus ϕ in G.
Beispiel folgt!
9
Backus-‐Naur-‐Form (BNF)
o Einfacher Formalismus für die Syntaxbeschreibung von Kunstsprachen (= Programmiersprachen).
o Dieser Formalismus hat selbst wiederum eine Syntax – man spricht deshalb von der Metasyntax der BNF:
¤ Ersetzungsregeln in der Form: linke Seite ::= rechte Seite
¤ . Markiert das Regelende
¤ | Alterna've
¤ ( ) Klammerung zusammengehöriger Symbole
¤ < > schließen Nonterminalsymbole ein
¤ Terminalsymbole werden (wegen der besseren Kenntlichkeit) ob in “ “ eingeschlossen oder fe_ gedruckt
10
Backus-‐Naur-‐Form (BNF) -‐ Beispiel
o Bezeichner einer Programmiersprache müssen mit einem
Buchstaben beginnen, dürfen nach dem ersten Buchstaben aber auch Ziffern enthalten:
<Ziffer> ::= 1|2|3|4|5|6|7|8|9|0.
<Buchstabe> ::= a|b|c| ... |z.
<Zeichenke[e> ::= <Buchstabe> | <Ziffer> |
<Buchstabe> <Zeichenke[e> |
<Ziffer> <Zeichenke[e>.
<Bezeichner> ::= <Buchstabe> | <Buchstabe> <Zeichenke[e>.
Rekursion
Das lässt sich einfacher mit
regulären Ausdrücken beschreiben.
11
Erweiterte Backus-‐Naur-‐Form (EBNF)
o Erweitert die BNF um einige Metasymbole, um die Syntax bequemer bzw. leichter verständlich zu beschreiben.
o Es gibt viele verschiedene EBNFs – ob gibt es für die Beschreibung der Syntax einer Programmiersprache eine eigene EBNF
o Bekannteste Erweiterung der Metasyntax der EBNF:
¤ Ersetzungsregeln in der Form: linke Seite → rechte Seite
¤ [ ] op'onale Klammerung
¤ { } Wiederholungsklammerung (0 -‐ n mal)
¤ { }+ Wiederholungsklammerung (1 -‐ n mal)
-‐ . Markiert das Regelende
-‐ | Alterna've
-‐ ( ) Klammerung zusammengehöriger Symbole
-‐ < > schließen Nonterminalsymbole ein
-‐ Terminalsymbole werden (der besseren Kenntlichkeit) ob in
“ “ eingeschlossen oder fe[ gedruckt
12
Erweiterte Backus-‐Naur-‐Form (EBNF) – Beispiel
o Bezeichner einer Programmiersprache müssen mit einem
Buchstaben beginnen, dürfen nach dem ersten Buchstaben aber auch Ziffern enthalten:
<Ziffer> ::= 1|2|3|4|5|6|7|8|9|0.
<Buchstabe> ::= a|b|c| ... |z.
<Bezeichner> ::= <Buchstabe> {<Buchstabe>| <Ziffer> }.
13
Erweiterte Backus-‐Naur-‐Form (EBNF) – komplizierteres Beispiel 1/3
expr → term | expr add_op term.
term → factor | term mul_op factor.
factor → number | id | "(" expr ")".
add_op → "+" | "-‐".
mul_op → "*" | "/".
number und id seien Terminalsymbole
Einfache arithmetische Ausdrücke:
Start- symbol
expr ⇒ expr add_op term ⇒ term add_op term ⇒ factor add_op term ⇒ id add_op term ⇒ id + term ⇒ id + term mul_op factor ⇒
id + factor mul_op factor ⇒ id + id mul_op factor ⇒ id + id * factor ⇒ id + id * id.
14
Erweiterte Backus-‐Naur-‐Form (EBNF) – komplizierteres Beispiel: Erweiterung 2/3
expr → term | expr add_op term.
term → factor | term mul_op factor.
factor → [sign] ( number | id | "(" expr ")" ).
sign → "+" | "-‐".
add_op → "+" | "-‐".
mul_op → "*" | "/".
Einfache arithmetische Ausdrücke (erweitert um Vorzeichen):
15
Erweiterte Backus-‐Naur-‐Form (EBNF) – komplizierteres Beispiel: Umformung 3/3
expr → term {add_op term}.
term → factor {mul_op factor}.
factor → [sign] ( number | id | "(" expr ")" ).
sign → "+" | "-‐".
add_op → "+" | "-‐".
mul_op → "*" | "/".
Einfache arithmetische Ausdrücke (umgeformt):
16
Erweiterte Backus-‐Naur-‐Form (EBNF) –größere Erweiterung
expr → s_expr | s_expr rel_op s_expr . s_expr → term | s_expr add_op term.
term → factor | term mul_op factor.
factor → [sign] ( number | id | "(" expr ")" ) | "not" ( id | "(" expr ")" ) .
sign → "+" | "-‐".
add_op → "+" | "-‐" | "or".
mul_op → "*" | "/" | "and".
rel_op → "<" | "<=" | "=" | "!=" | ">=" | ">".
Einfache arithmetische Ausdrücke - erweitert um relationale Operatoren und boolesche Operatoren:
17
Ableitung aus dem Startsymbol
expr
expr add_op term
term + term mul_op factor factor factor * id
id id Ableitungsbaum
expr ⇒ expr add_op term ⇒ term add_op term ⇒ factor add_op term ⇒ id add_op term ⇒ id + term ⇒ id + term mul_op factor ⇒
id + factor mul_op factor ⇒ id + id mul_op factor ⇒ id + id * factor ⇒ id + id * id.
18
Defini'on Ableitungsbaum
Sei G = (T, N, P, S) eine kontexPreie Gramma'k. Sei B ein Baum,
dessen innere Knoten mit NTS und dessen Blä[er mit TS von G oder mit dem leeren Wort ε markiert sind. B heißt Ableitungsbaum (oder Syntaxbaum) für das Wort w ∈ T* und für X ∈ N, falls gilt:
1. Für jeden inneren Knoten p, der mit Y ∈ N markiert ist und dessen Söhne (von links nach rechts) q1 . . . qn mit Q1 . . . Qn ∈ (N ∪ T) markiert sind, gibt es eine Produk'on Y → Q1 . . . Qn in P. Falls p einen einzigen Sohn hat, der mit ε markiert ist, so exis'ert eine Produk'on Y → ε.
2. Die Wurzel des Baumes ist mit X markiert, und die Konkatena'on der Blä[er ergibt w.
19
Defini'on: Links-‐ und Rechtsableitung
Sei ϕ1, . . ., ϕn eine Ableitung mit S = ϕ1 , ϕ= ϕn . ϕ1, . . ., ϕn heißt
Linksableitung von ϕ, falls in jedem Schri[ von ϕι nach ϕι+1 in ϕι jeweils das am weitesten links stehende NTS ersetzt wird, also gilt ϕι = wAσ und ϕι+1 = wασ.
Analog heißt ϕ1, . . ., ϕn Rechtsableitung von ϕ, falls in jedem Schri[ von ϕι nach ϕι+1 in ϕι jeweils das am weitesten rechts stehende NTS ersetzt wird, das heißt ϕι = σAw und ϕι+1 = σαw.
Eine Satzform (das ist jeder Zwischenzustand eines Ableitungsbaums) innerhalb einer Linksableitung (Rechtsableitung) heißt Linkssatzform (Rechtssatzform).
20
Mehrdeu'ge Gramma'ken 1/4
Führen verschiedene Ableitungen zum selben Syntaxbaum, dann spielt das wegen derselben Struktur des Wortes w keine Rolle.
Ist es aber möglich zu einem Wort w verschiedene Ableitungsbäume anzugeben, dann nennt man die zugrunde liegende Gramma'k
mehrdeuMg.
Mehrdeu'ge Gramma'k è seman'schen Mehrdeu'gkeiten
21
Mehrdeu'ge Gramma'ken 2/4
Beispiel:
stmt à if expr then stmt |
if expr then stmt else stmt.
if . . . then . . . if . . . then . . . else . . .
stmt
if expr then stmt
if expr then stmt else stmt
22
Mehrdeu'ge Gramma'ken 3/4
stmt
if expr then stmt
if expr then stmt else stmt
Beispiel:
stmt à if expr then stmt |
if expr then stmt else stmt.
if . . . then . . . if . . . then . . . else . . .
23
Mehrdeu'ge Gramma'ken 4/4
Auflösung der Mehrdeu'gkeit
è Änderung der Sprache è Änderung der Gramma'k
stmt à matched_stmt | unmatched_stmt.
matched_stmt à if expr then matched_stmt else matched_stmt.
unmatched_stmt à if expr then matched_stmt else unmatched_stmt | if expr then stmt.
Grammatik
Sprache stmt à if expr then stmt endif |
if expr then stmt else stmt endif.
24
Top-‐Down-‐Analyse – Allgemeine Strategie 1/2
Startsymbol
bereits fertig
fehlt noch
bereits verarbeitete Eingabe
noch nicht verarbeitete Eingabe
A
neu
Am weitesten links vorkommendes NTS
Ziel: Finde eine Linksableitung
25
Top-‐Down-‐Analyse – Allgemeine Strategie 2/2
Man baut den Ableitungsbaum von der Wurzel aus auf.
Dabei wird der AuAau des Baums (irgendwie) durch Betrachtung der Eingabefolge kontrolliert.
Strategie:
¤ Vergleiche die Bla•olge des bisher erzeugten Ableitungsbaums mit der Eingabesymbolfolge, d.h. beide Symbolfolgen werden von links nach rechts gelesen.
¤ Solange beide Symbolfolgen Terminalsymbole enthalten, wird weiter gelesen.
¤ Enthält die Eingabefolge ein TS und das entsprechende Bla[ des Baums ein NTS, wird eine Produk'on der Gramma'k ausgewählt, die auf dieses NTS anwendbar ist, die Bla•olge des Baums wird dadurch lokal verändert.
¤ Werden 2 nicht übereins'mmende TS angetroffen, dann
n war eine vorher ausgewählte Produk'on falsch und muss rückgängig gemacht werden.
n oder die Eingabefolge ist syntak'sch falsch.
26
Kategorien von Top-‐Down-‐Parsern
determinis'sch nicht-deterministisch
mit Rücksetzungen ohne Rücksetzungen
rekursiver Abstieg Tabellenmethode
27
Beispielgramma'k
stmt → assignment | cond | loop. (1)
assignment → id := expr. (2)
cond → if boolexpr then stmt fi | (3)
if boolexpr then stmt else stmt fi.
loop → while boolexpr do stmt od. (4)
expr → boolexpr | numexpr. (5)
boolexpr → numexpr cop numexpr . (6) numexpr → id | const . (7)
numexpr → numexpr + term | term . (7)
term → term * factor | factor. (8)
factor → id | const | (numexpr) . (9)
28
Top-‐Down-‐Analyse mit Backtracking – Beispiel 1/3
Startsymbol
Eingabefolge (von Scanner)
if id cop const then id := const fi stmt
assignment
if id cop const then id := const fi stmt
assignment id := expr stmt
if id cop const then id := const fi
BACKTRACKING
29
Top-‐Down-‐Analyse mit Backtracking – Beispiel 2/3
if id cop const then id := const fi stmt
cond
if boolexpr then stmt fi
if id cop const then id := const fi stmt
cond
if boolexpr then stmt fi numexpr cop numexpr
id
if id cop const then id := const fi assignment stmt
cond
if boolexpr then stmt fi numexpr cop numexpr
id const id := expr
30
Top-‐Down-‐Analyse mit Backtracking – Beispiel 3/3
if id cop const then id := const fi assignment stmt
cond
if boolexpr then stmt fi numexpr cop numexpr
id const id := expr
numexpr cop numexpr const
boolexpr
BACKTRACKING
31
• Verlaufen in Sackgassen.
⇒ Ineffizienz
• Linksrekursive Produktionen (Regeln (7) und (8)).
⇒ Analyse ist nicht möglich!
⇒ Grammatiken so konstruieren bzw. modifizieren, dass beide Probleme nicht auftreten.
Top-‐Down-‐Analyse -‐ Probleme
32
Linksrekursion
• Direkte Linksrekursion: A → Aα
• Indirekte Linksrekursion: A ⇒* Aα Beispiel: A → Aα | β.
A
A α
A α
A α
β
A β A‘
α A‘
α A‘
ε α A‘
linksrekursive Produktionen rechtsrekursive Produktionen A → βA‘.
A‘ → αA‘ | ε.
Eliminierung direkte Linksrekursion
33
Algorithmus zur Besei'gung der Linksrekursion
o Eingabe: Gramma'k G – ohne Zyklen und ε-‐Produk'onen.
o Ausgabe: Äquivalente Gramma'k ohne Linksrekursion.
1. Ordne die NTS in der Reihenfolge A1, A2, . . ., An an.
2. for i := 1 to n do
for j := 1 to i-‐1 do {
Ersetze jede Produk'on der Form Ai → Ajγ durch die Produk'onen
Ai → δ1γ | δ2γ | . . . | δkγ, wobei Aj → δ1 | δ2| . . . | δk alle aktuellen Aj-‐
Produk'onen sind.
}
Eliminiere die direkten Linksrekursionen unter den Ai-‐Produk'onen.
34
Beispielgramma'k (geändert)
stmt → assignment | cond | loop. (1)
assignment → id := expr. (2)
cond → if boolexpr then stmt fi| (3)
if boolexpr then stmt else stmt fi.
loop → while boolexpr do stmt od. (4)
expr → boolexpr| numexpr. (5)
boolexpr → numexpr cop numexpr . (6)
numexpr → term numexpr‘. (7a)
numexpr‘ → + term numexpr‘ | ε . (7b)
term → factor term‘. (8a)
term‘ → * factor term‘ | ε. (8b)
factor → id | const | (numexpr) . (9)
35
Predic've Parsing – vorausschauende Syntaxanalyse
Ziel: Vermeiden von Sackgassen und damit von Backtracking.
Idee: Durch gemeinsames Betrachten des aktuell zu expandierenden Baumknoten und des aktuellen Zeichens der Eingabefolge,
kann man eindeu'g entscheiden welche Alterna've bei mehreren möglichen Produk'onen auszuwählen ist.
Voraussetzung: Die Gramma'k muss vom Typ LL(1) sein. Wenn sie das nicht ist, muss sie in LL(1)-‐Form gebracht werden.
36
LL(k)-‐Gramma'ken
Bei dieser Klasse von Grammatiken kann immer eine eindeutige Entscheidung durch Ansehen der nächsten k Terminalsymbole der Eingabefolge getroffen werden.
Definition: Worte, Anfangsstücke von Worten einer Sprache Sei L ⊆ T* eine beliebige Sprache und sei k > 0. Dann ist
startk(L) := {w| (w ∈ L und |w| < k) oder (es existiert wu ∈ L und |w| = k)}.
Für ein Wort v ∈ T* sei
v falls |v| < k startk(v) :=
u falls u, t existieren mit |u| = k, ut = v
37
LL(k)-‐Gramma'ken -‐ Defini'on
Definition: LL(k)-Grammatik
Eine kontextfreie Grammatik G = (N, T, P, S) heißt LL(k)-Grammatik, wenn gilt: Aus S ⇒* wAσ ⇒ wασ ⇒* wx,
S ⇒* wAσ ⇒ wβσ ⇒* wy, und startk(x) = startk(y) folgt α = β.
A S
w
α σ
x startk(x)
Lesen der Eingabe von links nach rechts und berechnen einer Linksableitung unter
Vorausschau auf die nächsten k-Zeichen.
38
Starke LL(k)-‐Gramma'ken -‐ Defini'on
Defini'on: Starke LL(k)-‐Gramma'k
Eine kontexPreie Gramma'k G = (N, T, P, S) heißt starke LL(k)-‐
Gramma'k, wenn gilt: Aus
S ⇒* w1Aσ1 ⇒ w1ασ1 ⇒* w1x, S ⇒* w2Aσ2 ⇒ w2βσ2 ⇒* w2y, und startk(x) = startk(y)
folgt α = β.
Hier spielt der Kontext des zu expandierenden NTS A keine Rolle.
39
FIRST-‐ Menge
Die FIRST-‐Menge einer Zeichenfolge α (α ∈ (N ∪ T)* ) besteht aus allen TS, mit denen Zeichenke[en beginnen können, welche von α abgeleitet werden.
Defini'on: FIRST-‐Menge
Sei G = (N, T, P, S) eine kontexPreie Gramma'k, α ∈ (N ∪ T)* und k >
0, dann ist FIRSTk(α) := startk ({w | α ⇒* w}).
Die Menge FIRSTk(α) beschreibt also gerade die Anfangsstücke bis zur Länge k von aus α ableitbaren Terminalworten.
40
FOLLOW-‐ Menge
Die FOLLOW-‐Menge eines NTS A enthält alle TS, die in einer (Links-‐) Satzform direkt rechts von A stehen können.
Defini'on: FOLLOW-‐Menge
Sei G = (N, T, P, S) eine kontexPreie Gramma'k, A ∈ N und k > 0, dann ist
FOLLOWk(A) := {w | S ⇒* uAv und w = FIRSTk(v) }.
Die Menge FOLLOWk(A) beschreibt also Terminalzeichenfolgen bis zur Länge k, die innerhalb von Ableitungen in G auf das NTS A folgen
können.
41
Steuermenge
Falls αi die rich'ge Entscheidung ist, dann muss die Folge der nächsten k Zeichen, auf die wir vorausschauen, in der Konkatena'on der Mengen FIRSTk(αi) und FOLLOWk(A) liegen.
Defini'on: Steuermenge
Sei G = (N, T, P, S) eine kontexPreie Gramma'k, A ∈ N, k > 0, und A→ α1 | α2 | . . . | αn die Menge der A-‐Produk'onen, dann ist für 1 ≤ i ≤ n die Steuermenge Dk(A→ αi) definiert als
Dk(A→ αi) := startk (FIRSTk(αi) . FOLLOWk(A)) .
Die Entscheidung unter den αi kann eindeu'g getroffen werden, wenn die Mengen Dk(A→ α1), ..., Dk(A→ αn) alle paarweise disjunkt sind.
42
LL(1)-‐Gramma'k
Eine Gramma'k ist genau dann eine LL(1)-‐GrammaMk, wenn für jedes NTS A mit A-‐Produk'onen A→ α1 | α2 | . . . | αn gilt:
Die Mengen FIRST1(α1), . . ., FIRST1(αn) sind paarweise disjunkt.
Genau eine der Mengen FIRST1(α1), . . ., FIRST1(αn) darf das leere Wort ε enthalten. Wenn ε ∈ FIRST1(αi), dann gilt: FOLLOW1(A) ist disjunkt von allen anderen Mengen FIRST1(αj), i ≠ j.
Für k = 1 reduziert sich die Defini'on der Steuermengen zu:
FIRST1(αi), falls ε ∉FIRST1(αi) D1(A→ αi) :=
FIRST1(αi) – {ε } ∪ FOLLOW1(A) sonst
43
Linksfaktorisierung
cond → if boolexpr then stmt fi| (3)
if boolexpr then stmt else stmt fi.
Mit k = 1, d.h. durch Ansehen des ersten Symbols if, ist die richtige Alternative nicht eindeutig zu bestimmen à Verletzung der LL(1)-Eigenschaft.
è Grammatik so umschreiben, dass LL(1)-Eigenschaft erfüllt wird.
Verschiedene Alternativen einer Produktion haben ein gemeinsames Präfix: A → αβ1 | αβ2 è Linksfaktorisierung
A → α A‘
A‘ → β1 | β2
cond → if boolexpr then stmt cond-rest. (3a)
cond-rest → fi | else stmt fi. (3b)
44
Berechnung der FIRST-‐Mengen
Berechnung von FIRST(A) für alle Gramma'ksymbole A ∈ NTS ∪ TS.
Ini'alisiere FIRST(A) := ∅.
Anwendung der folgenden Regeln, solange, bis keine weiteren TS oder ε der Menge hinzugefügt werden können.
Wenn A ∈ TS: FIRST (A) := { A }.
Wenn A ∈ NTS und A → ε ∈ P: FIRST (A) := FIRST (A) ∪ ε.
Wenn A ∈ NTS und A → X1 X2 . . . Xk ∈ P:
FIRST (A) := FIRST (A) ∪ FIRST (X1) \ { ε }.
∀ i, 2 ≤ i ≤ k, mit ε ⊆ FIRST(X1), ..., ε ⊆ FIRST(Xi-‐1):
FIRST (A) := FIRST (A) ∪ FIRST (Xi) \ { ε }.
Wenn ε ⊆ FIRST(Xj) für j = 1,2, . . ., k: FIRST (A) := FIRST (A) ∪ ε
Diese Berechnung erfolgt für jede Alterna've in A → α1|α2| ...| αn.
45
FIRST-‐Mengen Berechnungsreihenfolge
In welcher Reihenfolge berechnet man denn die FIRST-‐Mengen?
1. Bes'mme Menge Nε von NTS, aus denen ε abgeleitet werden kann:
Nε := {X ∈ N | X ⇒* ε}.
2. Zeichne Graph, dessen Knoten NTS sind. Für jede Produk'on A → X1 . . . Xm
i. Füge für NTS X1 eine gerichtete Kante (A → X1) ein.
ii. Falls X1 ∈ Nε und X2 ∈ N, füge eine gerichtete Kante (A → X2) ein.
iii. Weiter so, wenn aufeinander folgende Xi ∈ Nε.
Kante A → B bedeutet: berechne FIRST(B) vor FIRST(A).
46
FIRST-‐Mengen für Beispielgramma'k 1/3
Nε = { term‘, numexpr‘ }
stmt
assignment cond loop
expr
boolexpr numexpr term
factor
cond-rest term‘
numexpr‘
47
FIRST-‐Mengen für Beispielgramma'k 2/3
assignment → id := expr.
cond → if boolexpr then stmt cond-rest.
loop → while boolexpr do stmt od.
stmt → assignment | cond | loop.
factor → id | const | (numexpr) . term → factor term‘.
numexpr → term numexpr‘.
boolexpr → numexpr cop numexpr . expr → boolexpr | numexpr.
cond-rest → fi | else stmt fi . term‘ → * factor term‘ | ε.
numexpr‘ → + term numexpr‘ | ε.
FIRST-Mengen { id } { if } { while } { id, if, while } { id, const, ( } { id, const, ( } { id, const, ( } { id, const, ( } { id, const, ( } { fi, else } { *, ε } { +, ε }
48
FIRST-‐Mengen für Beispielgramma'k 3/3
Beobachtung: Die FIRST-Mengen für die Alternativen boolexpr und numexpr sind nicht disjunkt – LL(1)-Konflikt !!!
⇒ Bei Produktion expr → boolexpr | numexpr kann nicht eindeutig bestimmt werden, welche Alternative ausgewählt werden soll.
Ursache: Ein Ausdruck beginnt stets mit einem numerischen Ausdruck, ob er ein boolescher Ausdruck ist, kann erst beim Antreffen eines cop-Symbols entschieden werden.
Lösung: Linksfaktorisierung
expr → numexpr bool-rest. { id, const, ( } (5a) bool-rest → cop numexpr | ε . { cop, e } (5b)
expr → boolexpr | numexpr. (5)
49
Beispielgramma'k (geändert)
stmt → assignment | cond | loop. (1)
assignment → id := expr. (2)
cond → if boolexpr then stmt cond-rest. (3a) cond-rest → fi | else stmt fi. (3b) loop → while boolexpr do stmt od. (4) expr → numexpr bool-rest. (5a) bool-rest → cop numexpr | ε. (5b) boolexpr → numexpr cop numexpr . (6)
numexpr → term numexpr‘. (7a)
numexpr‘ → + term numexpr‘ | ε . (7b)
term → factor term‘. (8a)
term‘ → * factor term‘ | ε. (8b)
factor → id | const | (numexpr) . (9)
50
Berechnung der FOLLOW-‐Mengen
Berechnung der FOLLOW-‐Mengen FOLLOW (A) ∀ A ∈ NTS.
1. FOLLOW(S) := { $ }, S = Startsymbol, $ = Endemarkierung der Eingabe.
Anwendung der folgenden Regeln, solange, bis keine weiteren TS der Menge hinzugefügt werden können:
2. Für A → αBβ ∈ P mit B ∈ NTS und α, β ∈ NTS ∪ TS mit β ≠ ε : FOLLOW (B) := FOLLOW (B) ∪ FIRST (β) \ { ε }, β ≠ ε.
3. Für A → αBβ oder A → αB, wobei gilt ε ∈ FIRST(β):
FOLLOW (B) := FOLLOW (B) ∪ FOLLOW (A ).
51
FOLLOW-‐Mengen Berechnungsreihenfolge
In welcher Reihenfolge berechnet man denn die FOLLOW-‐Mengen?
1. Zeichne Graph: jedes NTS = ein Knoten. Die Knoten werden markiert (mit TS und $). Markiere Startsymbol S mit $.
2. Betrachte alle Produk'onen aus P. Für jede Produk'on betrachte die NTS auf deren rechter Seite.
i. A → αBβ ∈ P, B ∈ NTS, β ≠ ε :
-‐ markiere Knoten B mit allen Symbolen aus FIRST (β) außer ε
- ε ∈ FIRST (β): füge Kante A → B ein (falls noch nicht drin in Graphen).
ii. A → αB ∈ P, B ∈ NTS, füge Kante A → B ein (analog ε ∈ FIRST (β))
3. Berechne alle starken Komponenten des Graphen und behandle jede Komponente wie einen einzigen Knoten K;
Markierung (K) := ∪ Markierungen all seiner Knoten.
4. FOLLOW (B) := Markierung(B) ∪ Markierungen seiner Vorgänger.
52
stmt
assignment cond loop
expr
boolexpr term
factor
cond-rest
term‘
numexpr‘
bool-rest
numexpr cop, do, then, ),
od, fi, else, $
cop, do, then, ), od, fi, else, +, $ +
* then, do
cop, )
od, fi, else, $
Abhängigkeiten nach 2i Abhängigkeiten nach 2ii
Direkte Markierungen (2i) Propagierte Markierungen (4)
FOLLOW-‐Mengen für Beispielgramma'k 1/2
Hinweis: Es sind nicht alle FOLLOW- Mengen vollständig angegeben.
53
bool-rest → cop numexpr | ε .
term‘ → * factor term‘ | ε.
numexpr‘ → + term numexpr‘ | ε.
FIRST- und FOLLOW-Mengen { cop } { od, fi, else, $ } { * } { cop, do, then, ), od, fi, else, +, $ } { + } { cop, do, then, ), od, fi, else, $ }
FIRST- und FOLLOW-Mengen bzw. die Steuermengen für die Alternativen sind disjunkt à Grammatik hat LL(1)-Eigenschaft.
Steuermengen
FOLLOW-‐Mengen für Beispielgramma'k 2/2
54
Zusammenfassendes Beispiel
Gegeben ist folgende Gramma'k für arithme'sche Ausdrücke:
E → E + T | T.
T → T * F | F.
F → ( E ) | id.
Sorgen Sie dafür, dass die Gramma'k LL(1)-‐Eigenschab hat und berechnen Sie die FIRST-‐ und FOLLOW-‐Mengen.
55
id := id + const $
X Y Z
$
Predictive Parser
Analysetabelle (M)
Ausgabe: Folge von Produktionen, die eine Linksableitung darstellen
Top-‐Down-‐Parser mit Analysetabelle
56
Parser-‐Konfigura'on
Aktueller Zustand der Analyse:
($αX, xw$)
Stackinhalt Eingabe(rest)
Top of Stack Aktuelles Eingabesymbol
Startkonfiguration des Parsers: ($S, p$)
Arbeitsschritte des Parsers: Übergang von Konfiguration K nach K‘: K → K‘ oder K →i K‘ (wenn Übergang mit Produktion i).
Endkonfiguration des Parsers: ($, $) – im Erfolgsfall oder ($αX, xw$) – im Fehlerfall
Startsymbol Programm
57
Arbeitsweise des Parsers
1. ($αX, xw$) mit X ∈T und X = x → ($α, w$)
2. ($αX, xw$) mit X ∈T und X ≠ x → error
3. ($αX, xw$) mit X ∈ N und M(X, x) = i, Pi = X → X1 ... Xm → i ($α Xm ... X1, xw$)
4. ($αX, xw$) mit X ∈ N und M(X, x) = error → Abbruch mit Fehlermeldung
5. ($, $) Stack und Eingabe sind abgearbeitet → Ende mit Erfolgsmeldung „accept“
58
Konstruk'on der Analysetabelle
Ausgangspunkt: Produktionen
Für jedes NTS A sei die Menge der A-Produktionen
i1 A → α1 | D1
i2 A → α2 | D2
. . .
in-1 A → αn-1 | Dn-1
in A → αn Dn
ij falls ∃ j ∈ {1, ..., n}: b ∈ Dj M[A, b] =
error sonst für alle A ∈ N und b ∈ (T ∪ $)
59
Beispielgramma'k – Nummerierung der Produk'onen und Steuermengen
(1) stmt → assignment | {id}
(2) cond | {if}
(3) loop. {while}
(4) assignment → id := expr. {id}
(5) cond → if boolexpr then stmt cond-rest. {if}
(6) cond-rest → fi | {fi}
(7) else stmt fi. {else}
(8) loop → while boolexpr do stmt od. {while}
(9) expr → numexpr bool-rest. {id, const, ( }
(10) bool-rest → cop numexpr | {cop}
(11) ε. {od, fi, else, $}
(12) boolexpr → numexpr cop numexpr . {id, const, ( }
(13) numexpr → termnumexpr‘. {id, const, ( }
(14) numexpr‘ → + term numexpr‘ | { + }
(15) ε . {cop, do, then, ), od, fi, else, $}
(16) term → factor term‘. {id, const , ( }
(17) term‘ → * factor term‘ | { * }
(18) ε. {cop, do, then, ), od, fi, else, +, $}
(19) factor → id | {id}
(20) const | {const}
(21) (numexpr) . { ( }
60
Analysetabelle für Beispielgramma'k
id := if then else fi while do od cop + * const ( ) $
stmt 1 2 3
assignment 4
cond 5
cond-rest 7 6
loop 8
expr 9 9 9
bool-rest 11 11 11 10 11
boolexpr 12 12 12
numexpr 13 13 13
numexpr' 15 15 15 15 15 15 14 15 15
term 16 16 16
term' 18 18 18 18 18 18 18 17 18 18
factor 19 20 21
Kein Eintrag è error
61
Beispiel für Ablauf Top-‐Down-‐Analyse 1/2
Stack
$ stmt
$ assignment
$ expr := id
$ expr :=
$ expr
$ bool-rest numexpr
$ bool-rest numexpr‘ term
$ bool-rest numexpr‘ term‘ factor
$ bool-rest numexpr‘ term‘ c
$ bool-rest numexpr‘ term‘
$ bool-rest numexpr‘ term‘ factor *
$ bool-rest numexpr‘ term‘ factor
$ bool-rest numexpr‘ term‘ c
$ bool-rest numexpr‘ term‘
Eingabe id:=c*c+c$
id:=c*c+c$
id:=c*c+c$
:=c*c+c$
c*c+c$
c*c+c$
c*c+c$
c*c+c$
c*c+c$
*c+c$
*c+c$
c+c$
c+c$
+c$
Ausgabe 1 4
9 13 16 20 17 20 18
62
Stack
$ bool-rest numexpr‘
$ bool-rest numexpr‘ term +
$ bool-rest numexpr‘ term
$ bool-rest numexpr‘ term‘ factor
$ bool-rest numexpr‘ term‘ c
$ bool-rest numexpr‘ term‘
$ bool-rest numexpr‘
$ bool-rest
$
Eingabe +c$
+c$
c$
c$
c$
$
$
$
$
Ausgabe 14 16 20 18 15 11 accept
Top-‐Down-‐Parser mit Analysetabelle
63
Prinzip des rekursiven Abs'egs (recursive descent)
Für jedes NTS N gibt es eine Prozedur, welche testet, ob die nächsten lexikalischen Elemente ein aus N ableitbares Wort bilden.
Ausgangspunkt: Menge der A-Produktionen
i1 A → α1 | D1
i2 A → α2 | D2
. . .
in-1 A → αn-1 | Dn-1
in A → αn Dn
procedure A {
if symbol ∈ D1 then output(i1); bearbeite(α1);
elsif symbol ∈ D2 then output(i2); bearbeite(α2);
. . .
if symbol ∈ Dn then output(in); bearbeite(αn);
else error;
fi;
}
64
Rekursiver Abs'eg am Beispiel
Scanner-Aufruf procedure stmt {
if symbol = id then output(1); assignment;
elsif symbol = if then output(2); cond;
elsif symbol = while then output(3); loop;
else error;
fi;
}
procedure assignment { if symbol = id then
output(4); match(id); match(:=); expr;
else error;
fi;
}
procedure match (symboltype t) {
if symbol = t then nextsymbol; else error; fi;
}
Baum der wechselseitigen
Prozeduraufrufe spiegelt die Struktur des Ableitungsbaums wider.