k grammatik

Werbung
3. Kapitel (Teil 1) SYNTAXANALYSE – TOP-­‐DOWN-­‐ANALYSE Compilerbau Prof. Dr. Wolfgang Schramm Syntak'sche Analyse (Parser) 1/2 1 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. Syntak'sche Analyse (Parser) 2/2 2 StrukturQuellprogramm
Eintragen
Scanner
Anforderung
Token
baum
Parser
Symbol Attribut
Symboltabelle
Eintragen/
Nachschlagen
Parser – Beispiele zu den Aufgaben 1/2 3 (1)  if (a <= 10) max = a;
stmt
cond_stmt
if
numexpr
id
(
boolexpr
cop
numexpr
const
)
stmt
;
assignment
id
=
expr
numexpr
id
Parser – Beispiele zu den Aufgaben 2/2 4 (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.
Beschreibung der Syntax von Programmiersprachen 5 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.
Gramma'k (kontexPrei) 1/2 6 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. Gramma'k (kontexPrei) 2/2 7 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. Defini'on Ableitung 8 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. Beispiel folgt!
Die Folge von Worten, für die ja gilt ϕ1 ⇒ ϕ2 ⇒ . . . ⇒ ϕn heißt eine Ableitung von ψ aus ϕ in G. Backus-­‐Naur-­‐Form (BNF) 9 o 
o 
Einfacher Formalismus für die Syntaxbeschreibung von Kunstsprachen (= Programmiersprachen). 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 Backus-­‐Naur-­‐Form (BNF) -­‐ Beispiel 10 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>. Rekursion
<Bezeichner> ::= <Buchstabe> | <Buchstabe> <Zeichenke[e>. Das lässt sich
einfacher mit
regulären
Ausdrücken
beschreiben.
Erweiterte Backus-­‐Naur-­‐Form (EBNF) 11 o 
o 
o 
Erweitert die BNF um einige Metasymbole, um die Syntax bequemer bzw. leichter verständlich zu beschreiben. Es gibt viele verschiedene EBNFs – ob gibt es für die Beschreibung der Syntax einer Programmiersprache eine eigene EBNF 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 Erweiterte Backus-­‐Naur-­‐Form (EBNF) – Beispiel 12 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 Einfache arithmetische Ausdrücke:
Startsymbol
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 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 Einfache arithmetische Ausdrücke (erweitert um Vorzeichen):
expr →
term | expr add_op term. term
→
factor | term mul_op factor. factor
→
[sign] ( number | id | "(" expr ")" ). sign
→
add_op
→
"+" | "-­‐". mul_op
→
"*" | "/". "+" | "-­‐". 15 Erweiterte Backus-­‐Naur-­‐Form (EBNF) – komplizierteres Beispiel: Umformung 3/3 Einfache arithmetische Ausdrücke (umgeformt):
expr
→
term {add_op term}. term
→
factor {mul_op factor}. factor
→
[sign] ( number | id | "(" expr ")" ). sign
→
add_op
→
"+" | "-­‐". mul_op
→
"*" | "/". "+" | "-­‐". 16 Erweiterte Backus-­‐Naur-­‐Form (EBNF) –größere Erweiterung Einfache arithmetische Ausdrücke - erweitert um relationale Operatoren und
boolesche Operatoren:
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
"<" | "<=" | "=" | "!=" | ">=" | ">". →
Ableitung aus dem Startsymbol 17 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.
expr
expr add_op term
term
Ableitungsbaum
factor
id
+
term
factor
id
mul_op
*
factor
id
Defini'on Ableitungsbaum 18 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. Defini'on: Links-­‐ und Rechtsableitung 19 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). Mehrdeu'ge Gramma'ken 1/4 20 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 Mehrdeu'ge Gramma'ken 2/4 21 Beispiel: stmt
à if expr then stmt |
if expr then stmt else stmt.
if . . . then . . . if . . . then . . . else . . .
stmt
if
expr
if
then
expr
stmt
then
stmt
else
stmt
Mehrdeu'ge Gramma'ken 3/4 22 Beispiel:
stmt
à if expr then stmt |
if expr then stmt else stmt.
if . . . then . . . if . . . then . . . else . . .
stmt
if
expr
then
if
expr
stmt
then
else
stmt
stmt
Mehrdeu'ge Gramma'ken 4/4 23 Auflösung der Mehrdeu'gkeit è Änderung der Sprache è Änderung der Gramma'k stmt
à
if expr then stmt endif |
if expr then stmt else stmt endif.
Sprache
Grammatik
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.
Top-­‐Down-­‐Analyse – Allgemeine Strategie 1/2 24 Ziel: Finde eine Linksableitung
Startsymbol bereits fertig
fehlt noch
noch nicht verarbeitete Eingabe
bereits verarbeitete
Eingabe
Am weitesten links vorkommendes
NTS
A
neu
Top-­‐Down-­‐Analyse – Allgemeine Strategie 2/2 25 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 
n 
war eine vorher ausgewählte Produk'on falsch und muss rückgängig gemacht werden. oder die Eingabefolge ist syntak'sch falsch. Kategorien von Top-­‐Down-­‐Parsern 26 determinis'sch mit Rücksetzungen
rekursiver Abstieg
nicht-deterministisch
ohne Rücksetzungen
Tabellenmethode
Beispielgramma'k 27 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)
Top-­‐Down-­‐Analyse mit Backtracking – Beispiel 1/3 28 stmt
Startsymbol
Eingabefolge (von
Scanner)
if id cop const then id := const fi
stmt
assignment
stmt
assignment
if id cop const then id := const fi
id
BACKTRACKING
:=
expr
if id cop const then id := const fi
Top-­‐Down-­‐Analyse mit Backtracking – Beispiel 2/3 29 stmt
cond
if boolexpr then
stmt
stmt
fi
cond
if id cop const then id := const fi
if boolexpr then
numexpr cop numexpr
stmt
id
cond
if boolexpr then
numexpr cop numexpr
id
const
stmt
stmt
fi
assignment
id
:=
if id cop const then id := const fi
expr
if id cop const then id := const fi
fi
Top-­‐Down-­‐Analyse mit Backtracking – Beispiel 3/3 30 stmt
cond
if boolexpr then
numexpr cop numexpr
id
const
stmt
fi
assignment
id
:=
expr
boolexpr
numexpr cop numexpr
const
BACKTRACKING
if id cop const then id := const fi
Top-­‐Down-­‐Analyse -­‐ Probleme 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.
Linksrekursion 32 • 
Direkte Linksrekursion:
A → Aα
• 
Indirekte Linksrekursion:
A ⇒* Aα
Eliminierung direkte
Linksrekursion
Beispiel: A → Aα | β.
A
A
A
A
A
α
α
α
β
A‘
β
A
→ βA‘.
A‘ → αA‘ | ε.
A‘
α
A‘
α
α
A‘
ε
linksrekursive Produktionen
rechtsrekursive Produktionen
Algorithmus zur Besei'gung der Linksrekursion 33 Eingabe: Gramma'k G – ohne Zyklen und ε-­‐Produk'onen. o 
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. Beispielgramma'k (geändert) 34 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)
Predic've Parsing – vorausschauende Syntaxanalyse 35 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. LL(k)-­‐Gramma'ken 36 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
LL(k)-­‐Gramma'ken -­‐ Defini'on 37 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)
S
folgt α = β.
A
Lesen der Eingabe von links nach rechts und
berechnen einer Linksableitung unter
Vorausschau auf die nächsten k-Zeichen.
σ
α
w
x
startk(x)
Starke LL(k)-­‐Gramma'ken -­‐ Defini'on 38 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. FIRST-­‐ Menge 39 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. FOLLOW-­‐ Menge 40 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. Steuermenge 41 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. LL(1)-­‐Gramma'k 42 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: D1(A→ αi) :=
FIRST1(αi), falls ε ∉FIRST1(αi) FIRST1(αi) – {ε } ∪ FOLLOW1(A) sonst Linksfaktorisierung 43 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)
Berechnung der FIRST-­‐Mengen 44 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.
FIRST-­‐Mengen Berechnungsreihenfolge 45 In welcher Reihenfolge berechnet man denn die FIRST-­‐Mengen? 1. 
2. 
Bes'mme Menge Nε von NTS, aus denen ε abgeleitet werden kann: Nε := {X ∈ N | X ⇒* ε}. 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). FIRST-­‐Mengen für Beispielgramma'k 1/3 46 Nε = { term‘, numexpr‘ }
stmt
assignment
cond
loop
cond-rest
term‘
expr
numexpr‘
boolexpr
numexpr
term
factor
FIRST-­‐Mengen für Beispielgramma'k 2/3 47 FIRST-Mengen
assignment →
id := expr.
{ id }
cond
→
if boolexpr then stmt cond-rest.
{ if }
loop
→
while boolexpr do stmt od.
stmt
→
assignment | cond | loop.
{ id, if, while }
factor
→
id | const | (numexpr) .
{ id, const, ( }
term
→
factor term‘.
{ id, const, ( }
numexpr
→
term numexpr‘.
{ id, const, ( }
boolexpr
→
numexpr cop numexpr .
{ id, const, ( }
expr
→
boolexpr | numexpr.
{ id, const, ( }
cond-rest
→
fi | else stmt fi .
term‘
→
* factor term‘ | ε.
{ *, ε }
numexpr‘
→
+ term numexpr‘ | ε.
{ +, ε }
{ while }
{ fi, else }
FIRST-­‐Mengen für Beispielgramma'k 3/3 48 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
→
boolexpr | numexpr.
(5)
expr
→
numexpr bool-rest.
{ id, const, ( }
(5a)
bool-rest
→
cop numexpr | ε .
{ cop, e }
(5b)
Beispielgramma'k (geändert) 49 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)
→
Berechnung der FOLLOW-­‐Mengen 50 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. 
3. 
Für A → αBβ ∈ P mit B ∈ NTS und α, β ∈ NTS ∪ TS mit β ≠ ε :
FOLLOW (B) := FOLLOW (B) ∪ FIRST (β) \ { ε }, β ≠ ε.
Für A → αBβ oder A → αB, wobei gilt ε ∈ FIRST(β): FOLLOW (B) := FOLLOW (B) ∪ FOLLOW (A ). FOLLOW-­‐Mengen Berechnungsreihenfolge 51 In welcher Reihenfolge berechnet man denn die FOLLOW-­‐Mengen? 1. 
2. 
Zeichne Graph: jedes NTS = ein Knoten. Die Knoten werden markiert (mit TS und $). Markiere Startsymbol S mit $. Betrachte alle Produk'onen aus P. Für jede Produk'on betrachte die NTS auf deren rechter Seite. i. 
ii. 
3. 
4. 
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). A → αB ∈ P, B ∈ NTS, füge Kante A → B ein (analog ε ∈ FIRST (β)) Berechne alle starken Komponenten des Graphen und behandle jede Komponente wie einen einzigen Knoten K; Markierung (K) := ∪ Markierungen all seiner Knoten. FOLLOW (B) := Markierung(B) ∪ Markierungen seiner Vorgänger. FOLLOW-­‐Mengen für Beispielgramma'k 1/2 52 Hinweis: Es sind nicht alle FOLLOWMengen vollständig angegeben.
stmt
assignment
cond
loop
Abhängigkeiten nach 2i
cond-rest
Abhängigkeiten nach 2ii
Direkte Markierungen (2i)
expr
bool-rest
numexpr
then, do
od, fi, else, $
cop, )
numexpr‘
term
boolexpr
* factor
Propagierte Markierungen (4)
cop, do, then, ),
od, fi, else, $
+
term‘
cop, do, then, ), od,
fi, else, +, $
FOLLOW-­‐Mengen für Beispielgramma'k 2/2 53 FIRST- und FOLLOW-Mengen
bool-rest
→
{ od, fi, else, $ }
ε.
term‘
→
→
{*}
* factor term‘ |
{ cop, do, then, ), od, fi, else, +, $ }
ε.
numexpr‘
{ cop }
cop numexpr |
{+}
+ term numexpr‘ |
{ cop, do, then, ), od, fi, else, $ }
ε.
Steuermengen
FIRST- und FOLLOW-Mengen bzw. die Steuermengen für die Alternativen sind
disjunkt à Grammatik hat LL(1)-Eigenschaft.
Zusammenfassendes Beispiel 54 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. Top-­‐Down-­‐Parser mit Analysetabelle 55 id := id + const $
X
Predictive Parser
Y
Z
$
Analysetabelle
(M)
Ausgabe: Folge von
Produktionen, die eine
Linksableitung darstellen
Parser-­‐Konfigura'on 56 Aktueller Zustand der Analyse:
($αX, xw$)
Stackinhalt
Eingabe(rest)
Top of Stack
Startsymbol
Aktuelles Eingabesymbol
Programm
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
Arbeitsweise des Parsers 57 1. 
2. 
3. 
4. 
5. 
($αX, xw$) mit X ∈T und X = x
→ ($α, w$)
($αX, xw$) mit X ∈T und X ≠ x
→ error
($αX, xw$) mit X ∈ N und M(X, x) = i, Pi = X → X1 ... Xm
→ i ($α Xm ... X1, xw$)
($αX, xw$) mit X ∈ N und M(X, x) = error
→ Abbruch mit Fehlermeldung
($, $) Stack und Eingabe sind abgearbeitet
→ Ende mit Erfolgsmeldung „accept“
Konstruk'on der Analysetabelle 58 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
M[A, b] =
ij
falls ∃ j ∈ {1, ..., n}: b ∈ Dj
error
sonst
für alle A ∈ N und b ∈ (T ∪ $)
Beispielgramma'k – Nummerierung der Produk'onen und Steuermengen 59 (1) 
(2) 
(3) 
(4) 
(5) 
(6) 
(7) 
(8) 
(9) 
(10) 
(11) 
(12) 
(13) 
(14) 
(15) 
(16) 
(17) 
(18) 
(19) 
(20) 
(21) 
stmt
assignment
cond
cond-rest
loop
expr
bool-rest
boolexpr
numexpr
numexpr‘
term
term‘
factor
→
→
→
→
→
→
→
→
→
→
→
→
→
assignment |
cond |
loop.
id := expr.
if boolexpr then stmt cond-rest.
fi |
else stmt fi.
while boolexpr do stmt od.
numexpr bool-rest.
cop numexpr |
ε.
numexpr cop numexpr .
term numexpr‘.
+ term numexpr‘ |
ε.
factor term‘.
* factor term‘ |
ε.
id |
const |
(numexpr) .
{id}
{if}
{while}
{id}
{if}
{fi}
{else}
{while}
{id, const, ( }
{cop}
{od, fi, else, $}
{id, const, ( }
{id, const, ( }
{+}
{cop, do, then, ), od, fi, else, $}
{id, const , ( }
{*}
{cop, do, then, ), od, fi, else, +, $}
{id}
{const}
{(}
Analysetabelle für Beispielgramma'k 60 stmt
assignment
cond
cond-rest
loop
expr
bool-rest
boolexpr
numexpr
numexpr'
term
term'
factor
id
1
4
:=
if then else
2
fi
while do
3
od
cop
+
*
const
(
9
9
)
$
5
7
6
8
9
11
11
11
10
11
12
13
12
13
15
15
15
15
15
15
14
16
16
18
19
18
18
18
18
18
12
13
18
15
18
18
16
17
20
15
21
Kein Eintrag è error
Beispiel für Ablauf Top-­‐Down-­‐Analyse 1/2 61 Eingabe
Ausgabe
$ stmt
id:=c*c+c$
1
$ assignment
id:=c*c+c$
4
$ expr := id
id:=c*c+c$
Stack
$ expr :=
:=c*c+c$
$ expr
c*c+c$
9
$ bool-rest numexpr
c*c+c$
13
$ bool-rest numexpr‘ term
c*c+c$
16
$ bool-rest numexpr‘ term‘ factor
c*c+c$
20
$ bool-rest numexpr‘ term‘ c
c*c+c$
$ bool-rest numexpr‘ term‘
*c+c$
$ bool-rest numexpr‘ term‘ factor *
*c+c$
$ bool-rest numexpr‘ term‘ factor
c+c$
$ bool-rest numexpr‘ term‘ c
c+c$
$ bool-rest numexpr‘ term‘
+c$
17
20
18
Top-­‐Down-­‐Parser mit Analysetabelle 62 Eingabe
Ausgabe
$ bool-rest numexpr‘
+c$
14
$ bool-rest numexpr‘ term +
+c$
Stack
$ bool-rest numexpr‘ term
c$
16
$ bool-rest numexpr‘ term‘ factor
c$
20
$ bool-rest numexpr‘ term‘ c
c$
$ bool-rest numexpr‘ term‘
$
18
$ bool-rest numexpr‘
$
15
$ bool-rest
$
11
$
$
accept
Prinzip des rekursiven Abs'egs (recursive descent) 63 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;
}
Rekursiver Abs'eg am Beispiel 64 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;
}
Baum der wechselseitigen
Prozeduraufrufe spiegelt die Struktur
des Ableitungsbaums wider.
Scanner-Aufruf
procedure match (symboltype t) {
if symbol = t then nextsymbol; else error; fi;
}
Fehlerbehandlung -­‐ Ausgangssitua'on 65 o 
AusgangssituaMon ¤ 
Das nächste Token entspricht nicht dem erwarteten Token: n 
n 
TS an Top of Stack ≠ Token. NTS A ist Top of Stack und a das nächste Token und Eintrag M[A, a] = error. Fehlerbehandlung – Strategien 1/2 66 o 
Panic mode ¤ 
o 
Überspringen/Verwerfen von Symbolen bei der Eingabe. Wiederherstellung auf Satzebene (phrase-­‐level recovery) ¤ 
¤ 
¤ 
¤ 
¤ 
¤ 
Ändern von Symbolen in der Eingabe (lokale Korrektur: Ersetzung eines Präfix der verbleibenden Eingabe durch einen String, der das Fortsetzen der Verarbeitung erlaubt). EnPernen von Symbolen auf dem Stack. Typische Korrekturen: Ersetzen eines Kommas durch ein Semikolon, Löschen eines zusätzlichen oder Einfügen eines Einfügen eines fehlenden Semikolons. Problem: Ersetzungen könnten zu Endlosschleifen führen, wenn man grundsätzlich vor dem aktuellen Symbol etwas einfügt. Vorteil: Kann jeden beliebigen Eingabestring berücksich'gen. Nachteil: Situa'onen, in denen der eigentliche Fehler vor dem Zeitpunkt der Aufdeckung liegt. Fehlerbehandlung – Strategien 2/2 67 o 
Fehlerproduk'onen ¤ 
¤ 
o 
Durch Vorhersehen häufig vorkommender Fehler à Gramma'k wird um Produk'onen erweitert, welche die fehlerhaben Konstrukte erzeugen. Kommt der Parser in eine Fehlerproduk'on à Fehler kann exakt beschrieben und vernünbig behoben werden. Globale Korrektur ¤ 
¤ 
¤ 
¤ 
¤ 
Compiler soll bei Erkennen eines nicht korrekten Eingabestrings möglichst wenige Veränderungen vornehmen. Einsatz von Algorithmen zur Auswahl einer möglichst geringen Folge von Änderungen, um die global kostengüns'gste Korrektur zu erreichen. Beispiel: Für fehlerhabe Eingabe x wird ein Parse-­‐Tree für ähnlichen String < gefunden, bei dem zur Umwandlung von x nach y eine möglichst geringe Anzahl von Einfügungen, Löschungen und Ersetzungen erforderlich ist Nachteil: Aufwand (Speicher, Laufzeit) sehr (= unrealis'sch) hoch. Einsatz: Bewertung von Fehlerbehebungstechniken, Finden op'maler Ersetzungsstrings für die Fehlerkorrektur auf Satzebene. Fehlerbehandlung – Panic Mode 68 o 
Idee ¤ 
¤ 
¤ 
o 
Ziel ¤ 
¤ 
o 
Überspringe Symbole bei der Eingabe, bis ein Token aus einer ausgewählten Menge von Synchronisierungstoken erscheint. Synchronisierungstoken werden vom Designer des Compilers festgelegt. Synchronisierungstoken sind normalerweise Begrenzer (;, }), die eine eindeu'ge Bedeutung haben. Einfaches, determiniertes Verfahren. Der Parser soll sich nach Erkennen eines Fehlers schnell wieder erholen. Probleme/Schwierigkeit ¤ 
¤ 
Erhebliche Teile des Code werden ohne weitere Fehlerprüfung übersprungen. Effizienz des Verfahrens hängt von der Auswahl der Menge der Synchronisierungstoken ab. n 
n 
Auswahl der Token der Synchronisierungsmenge entsprechend der Fehlerwahrscheinlichkeit. Benutzung von Heuris'ken. Panic Mode – Fehlersitua'on 69 o 
o 
Oben auf dem Stack liegt das NTS A, nächstes Eingabesymbol = a und M[A, a] = error. Oben auf dem Stack liegt das TS a ≠ b (Token der Eingabe), d.h. a wurde erwartet und b vorgefunden. Panic Mode – Heuris'ken 70 Voraussetzung: Oben auf dem Stack liegt das NTS A. Es ist bei der Erstellung des Ableitungsbaums für A ein Syntaxfehler aufgetreten. 1.  sync (A) = FOLLWOW (A) ¤ 
¤ 
¤ 
¤ 
Es werden solange Token übersprungen, bis man auf ein Element von aus FOLLOW (A) triŠ. Dann nimmt man A von Stack herunter und setzt die Analyse fort. Problem: In Sprachen wie C/Java werden Anweisungen mit Semikolon abgeschlossen. Schlüsselwörter, mit denen Anweisungen beginnen, erscheinen möglicher Weise nicht in der FOLLOW-­‐Menge des NTS, das für einen Ausdruck steht. Beispiel: a = b + c (Semikolon wurde vergessen). In der FOLLOW-­‐Menge von Ausdruck steht dann zwar das Semikolon, nicht aber das Schlüsselwort der nächsten Anweisung (z.B. if). Man würde dann alle Token bis zum nächsten Semikolon überspringen. Das ist zu viel, weil man mit dem nächsten Schlüsselwort forPahren könnte. Beobachtung: Häufig gibt es bei Sprachkonstrukten eine hierarchische Struktur; Ausdrücke erscheinen innerhalb von Anweisungen, Anweisungen innerhalb von Blöcken etc. Panic Mode – Heuris'ken 71 2.  sync (A) wird erweitert um Symbole, mit denen Konstrukte der nächst höheren Ebene beginnen. Beispiel: sync (assign) = sync (assing) ∪ FIRST (stmt) 3.  Es ist möglich, dass Symbole aus FIRST (A) in synch (A) enthalten sind à es ist möglich, die Syntaxanalyse mit A wiederaufzunehmen, wenn ein Token aus FIRST (A) in der Eingabe aubaucht. 4.  Gibt es eine Alterna've A → ε dann nimmt man die ε-­‐Produk'on als Standardfall, enPernt also einfach das NTS A vom Stack, ohne Token aus der Eingabe zu überlesen. Die Entdeckung eines Fehlers kann dadurch verschoben, nicht aber verhindert werden. ⇒ Reduziert die Anzahl der NTS, die bei der Fehlerbehandlung berücksich'gt werden müssen. 5.  Oben auf dem Stack liegt das TS a ≠ b (Token der Eingabe), d.h. a wurde erwartet und b vorgefunden. à Fehlermeldung (Token wurde eingefügt) und EnPernen des Token vom Stack. Man verhält sich so, als enthielte die Synchronisa'onsmenge eines Token alle anderen Token. Beispielgramma'k – Nummerierung der Produk'onen und Steuermengen 72 (1) 
(2) 
(3) 
(4) 
(5) 
(6) 
(7) 
(8) 
(9) 
(10) 
(11) 
(12) 
(13) 
(14) 
(15) 
(16) 
(17) 
(18) 
(19) 
(20) 
(21) 
stmt
assignment
cond
cond-rest
loop
expr
bool-rest
boolexpr
numexpr
numexpr‘
term
term‘
factor
→
→
→
→
→
→
→
→
→
→
→
→
→
assignment |
cond |
loop.
id := expr.
if boolexpr then stmt cond-rest.
fi |
else stmt fi.
while boolexpr do stmt od.
numexpr bool-rest.
cop numexpr |
ε.
numexpr cop numexpr .
term numexpr‘.
+ term numexpr‘ |
ε.
factor term‘.
* factor term‘ |
ε.
id |
const |
(numexpr) .
{id}
{if}
{while}
{id}
{if}
{fi}
{else}
{while}
{id, const, ( }
{cop}
{od, fi, else, $}
{id, const, ( }
{id, const, ( }
{+}
{cop, do, then, ), od, fi, else, $}
{id, const , ( }
{*}
{cop, do, then, ), od, fi, else, +, $}
{id}
{const}
{(}
73 Analysetabelle für Beispielgramma'k – nach Hinzufügen der Synchronisierungstoken stmt
assignment
cond
cond-rest
loop
expr
bool-rest
boolexpr
numexpr
numexpr'
term
term'
factor
id
1
4
9
11
12
13
15
16
18
19
:=
if
2
then else
sync
sync
5
sync
7
sync
sync
11 11 11
11
sync sync
sync sync
15 15 15
15
sync sync
18 18 18
18
sync sync
fi while do od cop +
* const
sync 3
sync
sync
sync
sync
sync
6
sync
sync 8
sync
sync
sync
9
11
11
11
11
10 11 11
11
sync
sync sync sync
12
sync
sync sync sync
13
15
15
15 15 15 14 15
15
sync
sync sync sync sync
16
18
18
18 18 18 18 17
18
sync
sync sync sync sync sync 20
(
)
9
11
12
13
15
16
18
21
11
sync
sync
15
sync
18
sync
Kein Eintrag è error
Rot: Token aus der
Synchronisationsmenge
$
sync
sync
sync
sync
sync
sync
11
sync
sync
15
sync
18
sync
74 o 
Beispiel mit fehlerhaben Eingabe, um Error-­‐Handling zu zeigen. Wiederherstellung auf Satzebene 75 o 
o 
Einträge der Parsetabelle werden durch Referenzen auf Fehlerrou'nen ersetzt. Fehlerrou'nen: ¤ 
¤ 
ändern, fügen ein, löschen, geben Fehlermeldungen aus, enPernen Symbole vom Stack. 
Herunterladen