Formale Sprachen und Automaten

Formale Sprachen und Automaten Kapitel 6: Syntaxanalyse Vorlesung an der DHBW Karlsruhe Thomas Worsch Karlsruher Institut für Technologie, Fakultät ...
Author: Johanna Feld
0 downloads 0 Views 889KB Size
Formale Sprachen und Automaten Kapitel 6: Syntaxanalyse

Vorlesung an der DHBW Karlsruhe

Thomas Worsch Karlsruher Institut für Technologie, Fakultät für Informatik

Wintersemester 2012

Kapitel 6

Syntaxanalyse

Einleitung Lexikalische Analyse Lexer-Generatoren Syntaktische Analyse Der Algorithmus von Cocke, Younger und Kasami Der Algorithmus von Earley LR Parsing Nach der Syntaxanalyse

Inhalt

2/112

Einleitung Lexikalische Analyse Lexer-Generatoren Syntaktische Analyse Der Algorithmus von Cocke, Younger und Kasami Der Algorithmus von Earley LR Parsing Nach der Syntaxanalyse

Einleitung

3/112

6.3 Phasen eines Übersetzers I

Analyseteil I I I I

I

Vorverarbeitung (Makroersetzung) lexikalische Analyse syntaktische Analyse semantische Analyse

Syntheseteil I I I I

Erzeugung des Zwischencodes Optimierung des Zwischencodes Erzeugung des Maschinencodes Optimierung des Maschinencodes

quer dazu I

Verwaltung der Symboltabelle

I

Fehlerbehandlung

Einleitung

4/112

T-Diagramme I

Übersetzer C von Quellsprache S nach Zielsprache T implementiert in Programmiersprache I : S C T I

I

Beispielaufgabe: P I

Gegeben: zwei Übersetzer

C1 M2 P C2 M1 M1 P und M2

P I

Gesucht: ein Übersetzer

Einleitung

M2

5/112

T-Diagramme (2) M2

P I

Gesucht: ein Übersetzer

M2

:

P C3 M2 1. Schritt: P C1 M2 P P C2 M1 M1 M1 2. Schritt:

I

P C1 M2 P C4 M2 P C1 M2 P P C3 M2 M2 P P C2 M1 M1 M1

Falls M1 = M2 = M:

P C1 M P C4 M P C1 M P P C3 M M P P C2 M M M

Einleitung

6/112

Einleitung Lexikalische Analyse Lexer-Generatoren Syntaktische Analyse Der Algorithmus von Cocke, Younger und Kasami Der Algorithmus von Earley LR Parsing Nach der Syntaxanalyse

Lexikalische Analyse

7/112

6.5 I

Ein Programm(teil), das die lexikalische Analyse durchführt, heißt auch Lexer oder Scanner.

I

Im Deutschen manchmal: Symbolentschlüssler.

Lexikalische Analyse

8/112

6.6 Aufgaben des Lexers I

Eingabetext an passenden Stellen in eine Folge von Lexemen aufteilen.

I

Jedes Lexem passt zu einem Muster, oft durch reguläre Ausdrücke beschrieben.

I

Daraus kann automatisch eine effizient arbeitende Implementierung des Lexers erzeugt werden.

Lexikalische Analyse

9/112

6.6 Aufgaben des Lexers (2) Schnittstelle des Lexers: I I

Prozeduren zur Initialisierung und Beendigung nextToken: Aufruf führt dazu, dass I I

I

I

Token besteht aus Typ und unter Umständen noch weiteren Attributen. Beispiel: Token für Variablennamen I I

I

der Lexer im Eingabestrom nach dem nächsten Lexem sucht, für das er ein Token zurückliefert.

Typ: id Attribute: Name der Variable oder Verweis auf den Eintrag des Namens in der Symboltabelle

Die Token sind die Terminalsymbole der Grammatik, die den „kontextfreien Anteil“ der Programmiersprachensyntax beschreibt.

Lexikalische Analyse

10/112

6.7 Kommentare I

Einen Kommentar kann man als Lexem auffassen, für das kein Token erzeugt wird.

I

Leerzeichen kann der Lexer auch überlesen, sofern sie für das Programm bedeutungslos sind.

I

Es gibt allerdings Programmiersprachen (Fortran, Occam, Python, . . . ), bei denen die Position eines Lexems im Quellprogramm von Bedeutung sein kann. In einem solchen Fall ist der Lexer entsprechend aufwendiger zu implementieren.

Lexikalische Analyse

11/112

6.8 Beispiel I

I

Eingabe: erna = hugo + 1; aufeinanderfolgende Aufrufe von nextToken liefern: I I I I I I

I

(id,erna) (assignOp,) (id,hugo) (plusOp,) (num,1) (semi,)

Die syntaktische Analyse muss überprüfen, ob die Zeichenfolge id assignOp id plusOp num semi in der Grammatik ableitbar ist.

Lexikalische Analyse

12/112

Einleitung Lexikalische Analyse Lexer-Generatoren Syntaktische Analyse Der Algorithmus von Cocke, Younger und Kasami Der Algorithmus von Earley LR Parsing Nach der Syntaxanalyse

Lexer-Generatoren

13/112

6.10 Lexer-Generatoren I

Lexer-Generator: Programm zur automatischen Erzeugung von Lexern Eingabe: Datei, in der insbesondere mit Hilfe regulärer Ausdrücke beschrieben ist, Lexeme welcher Struktur zu suchen sind, und welche Token daraus zu erzeugen sind. Ausgabe: Quelle eines Programmes, das als Lexer auf die gewünschte Art arbeitet.

I

Beispiele: lex, flex und JLex, Werkzeuge in Übersetzergenerator-Suiten wie cocktail und Eli

Lexer-Generatoren

14/112

6.11 Reguläre Definitionen I

zur besseren Lesbarkeit regulärer Ausdrücke

I

Teilausdrücke können mit Namen bezeichnet werden Beispiel:

I

I I I I

I

digit → [0-9] letter → [a-zA-Z] id → letter (letter|digit)* /*[-+]?digit+)? num → digit+ (/.digit+)? (O

Wichtig: I

I

Auf der rechten Seite dürfen nur Namen regulärer Ausdrücke benutzen darf, die vorher definiert wurden. Direkte oder indirekte Rekursionen sind verboten.

Lexer-Generatoren

15/112

6.12 Eingabedatei für Lexergenerator I

reguläre Definitionen

I

Regeln, die einigen regulären Ausdrücken Aktionen zuordnen.

I

Programmstücke, die in den erzeugten Lexer mit eingebaut werden.

I

Aktion gibt an, was der Lexer tun soll, wenn nextToken aufgerufen wird und das zum nächsten Token gehörende Lexem zum jeweiligen regulären Ausdruck gehört. Zum Beispiel könnte für den regulären Ausdruck id als Aktion spezifiziert sein, dass

I

I

I

der gefundene Name in der Symboltabelle zu suchen und gegebenenfalls einzutragen ist und der Rückgabewert das Token id und als Attribut der Verweis auf den Symboltabelleneintrag zu liefern ist.

Lexer-Generatoren

16/112

6.13 Wichtig: I

I

Lexer versucht, zu einem regulären Ausdruck eine möglichst lange passende Folge von Eingabezeichen zu finden. Beispiel: aus Eingabe 123.45E67 wird vom Lexer I I

I

I

ein Token num mit Attribut 123.45E67 gemacht und nicht z. B. zwei num-Token mit Attributen 123.4 und 5E67.

Wenn das gleiche maximal lange Präfix der Eingabe auf die regulären Ausdrücke mehrerer Lexerregeln passt, wird bei vielen Lexergeneratoren ein Lexer erzeugt, der immer die erste passende Regel benutzt. Beispiel: I

I

reguläre Definition keyword → for|do|if|then mit Aktion für keyword vor der Aktion für id (wie oben) Dann wird der Lexer für die Zeichenfolge if in der Eingabe ein Token keyword zurückliefern und nicht ein Token id. Lexer-Generatoren

17/112

6.15 Beispiel: Lexerspezifikation für JLex Grobstruktur: hBenutzer-Codei %% hreguläre Definitioneni %% hRegeln mit Aktioneni

Lexer-Generatoren

18/112

6.15 Beispiel: Lexerspezifikation für JLex (2) Beispiel: I hBenutzer-Codei: import java.lang.System; class Simple { public static void main(String argv[]) .... { Yylex yy = new Yylex(System.in); Yytoken t; while ((t = yy.yylex()) != null) System.out.println(t); } } class Yytoken { Yytoken(int index, String text) { .... } private static final String tokName[] = { "UNKNOWN", "FLOAT", "INT", "ID" }; public int m_index; public String m_text; public String toString() {return .... } } I

%% Lexer-Generatoren

19/112

6.15 Beispiel: Lexerspezifikation für JLex (3) I

hreguläre Definitioneni: ALPHA = [A-Za-z] DIGIT = [0-9] ID = {ALPHA}({ALPHA}|{DIGIT}|_)* INT = {DIGIT}+ FLOAT = {INT}\.{INT} WHITE_SPACE_CHAR = [\n\ \t\b\012]

I

%%

Lexer-Generatoren

20/112

6.15 Beispiel: Lexerspezifikation für JLex (4) I

hRegeln mit Aktioneni: {WHITE_SPACE_CHAR}+ { } {FLOAT} { return (new Yytoken(1,yytext())); } {INT} { return (new Yytoken(2,yytext())); } {ID} { return (new Yytoken(3,yytext())); } . { return (new Yytoken(0,yytext())); } Lexer-Generatoren

21/112

6.16 Arbeiten mit JLex Voraussetzungen: I obige JLex-Spezifikation in Datei simple.lex I JLex irgendwo im CLASSPATH verfügbar Dann: I java JLex.Main simple.lex I I

erzeugt Datei simple.lex.java: Java-Quelle eines Lexers Ausgaben von JLex: Processing first section -- user code. Processing second section -- JLex declarations. Processing third section -- lexical rules. Creating NFA ..... Creating DFA ..... Minimizing DFA .....

I

I

javac simple.lex.java erzeugt den lauffähigen Lexer java Simple startet den Lexer. Lexer-Generatoren

22/112

6.19 I

Viele (aber nicht alle) Lexergeneratoren erzeugen tabellengesteuerte Lexer.

I

Sie bestehen im wesentlichen immer aus dem gleichen Code, der die endlichen Automaten simuliert,

I

die in Form einer Tabelle gespeichert sind.

Lexer-Generatoren

23/112

6.20 Kommentare in Programmiersprachen I

Angenommen, die lexikalische Analyse soll: I I

I

alle Kommentare aus dem Programm entfernen durch endlichen Automaten erledigt werden

Programmiersprachen, bei denen Kommentare durch ausgezeichnete Zeichenketten am Anfang und am Ende gekennzeichnet sind (z. B. /* und */ in C) I

geschachtelte Kommentare verboten

I

Man müsste die Eingabe auf korrekte Klammerstruktur überprüfen, und das kann ein endlicher Automat nicht.

I

andererseits: ein einfacher Zähler für die Schachtelungstiefe würde genügen

I

Manche Lexergeneratoren unterstützen den Anwender bei solchen Problemen.

Lexer-Generatoren

24/112

Einleitung Lexikalische Analyse Lexer-Generatoren Syntaktische Analyse Der Algorithmus von Cocke, Younger und Kasami Der Algorithmus von Earley LR Parsing Nach der Syntaxanalyse

Syntaktische Analyse

25/112

6.21 Wie kommt man von den nichtdeterministischen Kellerautomaten weg? I

Benutze nicht Kellerautomaten, sondern „andere Algorithmen“.

I

Einschränkung auf eine echte Teilklasse aller kontextfreien Grammatiken, damit deterministischen Kellerautomaten oder wenigstens eine „einfache“ Erweiterung davon reichen.

Syntaktische Analyse

26/112

Einleitung Lexikalische Analyse Lexer-Generatoren Syntaktische Analyse Der Algorithmus von Cocke, Younger und Kasami Der Algorithmus von Earley LR Parsing Nach der Syntaxanalyse

Der Algorithmus von Cocke, Younger und Kasami

27/112

6.22 Definition Grammatik in Chomsky-Normalform: I

Jede Produktion X → w mit X 6= S hat als rechte Seite I I

I

entweder ein Wort w ∈ N 2 (genau zwei Nichtterminalsymbole) oder ein Wort w ∈ T (genau ein Terminalsymbol).

Wenn es die Produktion S → ε gibt, dann kommt S bei keiner Produktion auf der rechten Seite vor.

Der Algorithmus von Cocke, Younger und Kasami

28/112

6.23 Satz Zu jeder kontextfreien Grammatik G gibt es eine kontextfreie Grammatik G 0 in Chomsky-Normalform, die zu G äquivalent ist (also L(G ) = L(G 0 )).

Der Algorithmus von Cocke, Younger und Kasami

29/112

6.24 Lemma Für jede kontextfreie Grammatik G kann man die Menge EPS(G ) = {X ∈ N | X ⇒∗ ε} aller Nichtterminalsymbole berechnen, aus denen das leere Wort ableitbar ist.

Der Algorithmus von Cocke, Younger und Kasami

30/112

6.25 Beweisskizze Algorithmus: i ←0 M0 ← {X | X → ε ∈ P} do i ←i +1 ∗ Mi ← Mi−1 ∪ {X | ∃w ∈ Mi−1 : X → w ∈ P} until Mi = Mi−1 return Mi

Der Algorithmus von Cocke, Younger und Kasami

31/112

6.26 Beispiel Grammatik G = ({X , Y , Z }, {a}, X , P) mit Produktionenmenge P = {X → YZ , Y → ZZ , Z → a|ε} Der obige Algorithmus berechnet nacheinander: M0 = {Z } M1 = {Z , Y }

wegen Y → ZZ ∈ P

M2 = {Z , Y , X }

wegen X → YZ ∈ P

M3 = {Z , Y , X }

(keine Änderung mehr)

Also ist EPS(G ) = {X , Y , Z }.

Der Algorithmus von Cocke, Younger und Kasami

32/112

6.27 Lemma Zu jeder kontextfreien Grammatik G = (N, T , S, P) kann man eine kontextfreie Grammatik G 0 = (N, T , S, P 0 ) berechnen, für die gilt: I

L(G 0 ) = L(G ) r {ε} und

I

keine Produktion von G 0 hat ε als rechte Seite.

Der Algorithmus von Cocke, Younger und Kasami

33/112

6.28 Beweisskizze In P 0 nimmt man alle Produktionen X → w 0 auf, für die gilt: I

w 0 6= ε und

I

es gibt eine Produktion X → w ∈ P, so dass w 0 aus w entsteht, indem man einige Vorkommen von Symbolen aus EPS(G ) entfernt.

Der Algorithmus von Cocke, Younger und Kasami

34/112

6.29 Lemma Zu jeder kontextfreien Grammatik G = (N, T , S, P) kann man eine ¯ = (N, T , S, ¯ P) ¯ berechnen, äquivalente kontextfreie Grammatik G für die gilt: ¯ keine Produktion ε als rechte I Wenn ε ∈ / L(G ) ist, dann hat G Seite. ¯ → ε die einzige Produktion von I Wenn ε ∈ L(G ) ist, dann ist S ¯ G mit ε als rechter Seite und S¯ kommt bei keiner Produktion auf der rechten Seite vor.

Der Algorithmus von Cocke, Younger und Kasami

35/112

6.30 Beweisskizze I

I I

Konstruiere wie in Lemma 6.27 eine Grammatik G 0 mit L(G 0 ) = L(G ) r {ε}. ¯ = G 0 setzen und ist fertig. Wenn ε ∈ / L(G ) ist, kann man G Ist ε ∈ L(G ), denn erweitert man G 0 um ein neues ¯ das Startsymbol von G ¯ wird und setzt Nichtterminalsymbol S, 0 0 ¯ = P ∪ {S¯ → ε, S¯ → S }. P

Der Algorithmus von Cocke, Younger und Kasami

36/112

6.23 Satz Zu jeder kontextfreien Grammatik G gibt es eine kontextfreie Grammatik G 0 in Chomsky-Normalform, die zu G äquivalent ist (also L(G ) = L(G 0 )).

Der Algorithmus von Cocke, Younger und Kasami

37/112

6.31 Beweis (von Satz 6.23) Es sei G0 = (N0 , T , S, P0 ) eine beliebige kontextfreie Grammatik. Konstruiere äquivalente kontextfreie Grammatik G4 in Chomsky-Normalform in vier Schritten: 1. Konstruiere aus G eine äquivalente Grammatik G1 , bei der Terminalsymbole a ∈ T nur in Produktionen der Form X → a vorkommen. 2. Konstruiere aus G1 eine äquivalente Grammatik G2 wie in Lemma 6.29 beschrieben. 3. Konstruiere aus G2 eine äquivalente Grammatik G3 , die keine Produktionen der Form X → Y enthält. 4. Konstruiere aus G3 eine äquivalente Grammatik G4 , die keine Produktionen der Form X → Y1 Y2 · · · Yk mit k ≥ 3 enthält.

Der Algorithmus von Cocke, Younger und Kasami

38/112

6.31 Beweis (2) 1. Konstruiere aus G eine äquivalente Grammatik G1 , bei der Terminalsymbole a ∈ T nur in Produktionen der Form X → a vorkommen:

Der Algorithmus von Cocke, Younger und Kasami

39/112

6.31 Beweis (2) 1. Konstruiere aus G eine äquivalente Grammatik G1 , bei der Terminalsymbole a ∈ T nur in Produktionen der Form X → a vorkommen: I

I

Führe für jedes Terminalsymbol a ∈ T ein neues Nichtterminalsymbol Za ein: N1 = N0 ∪ {Za | a ∈ T }. Produktionenmenge P1 enthält I I

alle Produktionen der Form Za → a und alle Produktionen, die man erhält, indem man in jeder Produktion aus P0 jedes Vorkommen eines Terminalsymboles a durch Za ersetzt.

Der Algorithmus von Cocke, Younger und Kasami

39/112

6.31 Beweis (3) 2. Konstruiere aus G1 eine äquivalente Grammatik G2 wie in Lemma 6.29 beschrieben:

Der Algorithmus von Cocke, Younger und Kasami

40/112

6.31 Beweis (3) 2. Konstruiere aus G1 eine äquivalente Grammatik G2 wie in Lemma 6.29 beschrieben: Nun sind alle Produktionen I I

von der Form X → a oder von der Form X → Y1 · · · Yk mit k ≥ 1 (evtl. Ausnahme S → ε)

Der Algorithmus von Cocke, Younger und Kasami

40/112

6.31 Beweis (4) 3. Konstruiere aus G2 eine äquivalente Grammatik G3 , die keine Produktionen der Form X → Y enthält:

Der Algorithmus von Cocke, Younger und Kasami

41/112

6.31 Beweis (4) 3. Konstruiere aus G2 eine äquivalente Grammatik G3 , die keine Produktionen der Form X → Y enthält: I

I

I

I

Für jedes X ∈ N2 berechnet man die Menge M(X ) = {X 0 | X ⇒∗ X 0 } der aus X ableitbaren Nichtterminalsymbole. i ←0 M0 (X ) ← {X } do i ←i +1 Mi (X ) ← Mi−1 (X ) ∪ {X 0 | ∃Y ∈ Mi−1 (X ) : Y → X 0 ∈ P} until Mi (X ) = Mi−1 (X ) return Mi (X ) Aus P2 streicht man zunächst alle Produktionen der Form X → X 0. Für verbliebene Produktion X 0 → Y1 · · · Yk nimmt man alle X → Y10 · · · Yk0 hinzu, sofern X 0 ∈ M(X ) und für alle i, 1 ≤ i ≤ k, auch Yi0 ∈ M(Yi ) ist. Das ergibt P3 .

Der Algorithmus von Cocke, Younger und Kasami

41/112

6.31 Beweis (5) 4. Konstruiere aus G3 eine äquivalente Grammatik G4 , die keine Produktionen der Form X → Y1 Y2 · · · Yk mit k ≥ 3 enthält.

Der Algorithmus von Cocke, Younger und Kasami

42/112

6.31 Beweis (5) 4. Konstruiere aus G3 eine äquivalente Grammatik G4 , die keine Produktionen der Form X → Y1 Y2 · · · Yk mit k ≥ 3 enthält. P4 erhält man aus P3 , indem man I

I

I

jede Produktion der Form X → Y1 · · · Yk mit k ≥ 3 entfernt und ersetzt durch die k − 1 Produktionen X → Y1 V1 , V1 → Y2 V2 , . . . , Vk−1 → Yk−1 Yk . Dabei seien die Vi jeweils neue Nichtterminalsymbole (die natürlich alle zu N4 hinzugenommen werden).

Der Algorithmus von Cocke, Younger und Kasami

42/112

6.32 Beispiel Grammatik mit Startsymbol E und den Produktionen: E

→ E +F | F

F

→ (E ) | a

1. Schritt: Behandlung der Terminalsymbole ergibt: E → E Z+ F | F

Z+ → +

Za → a

F → Z( E Z) | Za

Z( → (

Z) → )

Für das Terminalsymbol a ist das Vorgehen natürlich überflüssig. 2. Schritt: keine ε-Produktionen vorhanden, also ändert sich nichts. Der Algorithmus von Cocke, Younger und Kasami

43/112

6.32 Beispiel (2) 3. Schritt: I

I

I

Berechne zunächst X M0 (X ) E {E } F {F }

die Mengen Mi (X ): M1 (X ) M2 (X ) {E , F } {E , F , Za } {F , Za } {F , Za }

M3 (X ) {E , F , Za } {F , Za }

Die Produktionen E → F und F → Za werden entfernt und dafür neue aufgenommen. Aus E → E Z+ F entstehen: E

→ E Z+ F | F Z+ F | Za Z+ F | E Z+ Za | F Z+ Za | Za Z+ Za

I

Aus F → Z( E Z) ergeben sich F

→ Z( E Z) | Z( F Z) | Z( Za Z)

E

→ Z( E Z) | Z( F Z) | Z( Za Z)

Der Algorithmus von Cocke, Younger und Kasami

44/112

6.32 Beispiel (3) 4. Schritt: Jede Produktion mit drei Nichtterminalzeichen auf der rechten Seite wird ersetzt durch zwei Produktionen mit nur zwei Nichtterminalzeichen auf der rechten Seite. I

I

I

Zum Beispiel: E → E Z+ F ersetzt durch E → E V1 und V1 → Z+ F . Analog in allen anderen Fällen vor, wobei jedes Mal neue zusätzliche Nichtterminalsymbole V2 , V3 , . . . eingeführt werden. Jedenfalls verlangt das der Algorithmus so. Scharfes Hinsehen ergibt hier, dass man sparsamer sein kann: E → E V1 | F V1 | Za V1

V1 → Z+ F

E → E V2 | F V2 | Za V2

V2 → Z+ Za

E → Z( V3 | Z( V4 | Z( V5

V3 → E Z )

F → Z( V3 | Z( V4 | Z( V5

V4 → F Z ) V5 → Za Z)

Der Algorithmus von Cocke, Younger und Kasami

45/112

6.33 I

Der Algorithmus von Cocke, Younger und Kasami (kurz CYK) kann für jede Grammatik G = (N, T , S, P) in Chomsky-Normalform und jedes Wort w ∈ T ∗ auf deterministische Weise feststellen, ob w ∈ L(G ) ist oder nicht.

I

Eingabe w = x1 · · · xn ∈ T n ist mit n + 1 „Trennstellen“ versehen, die neben den einzelnen Symbolen liegen. |a|b|b|a|b| 0

1

2

3

4

5

I

Es sei w (i, j] das Teilwort zwischen den Trennstellen | und |,

I

also w (i, j] = xi+1 · · · xj (für 0 ≤ i < j ≤ n). Beispiele:

i

I I

j

w (0, n] = w w (i, i] = ε

Der Algorithmus von Cocke, Younger und Kasami

46/112

6.34 Algorithmus (Cocke, Younger, Kasami) I

CYK berechnet eine Pyramiden-Datenstruktur. D[0, 5] D[0, 4] D[0, 3] D[0, 2] D[0, 1] | 0

x1

D[1, 4]

D[1, 3]

D[1, 2] | 1

D[1, 5]

x2

D[2, 5]

D[2, 4]

D[2, 3] | 2

x3

D[3, 5]

D[3, 4] | 3

x4

D[4, 5] | 4

x5

| 5

I

Die einzelnen Komponenten D[i, j] sind (möglicherweise leere) Teilmengen von Nichtterminalsymbolen.

I

Ziel: D[i, j] = {X | X ⇒∗ w (i, j] }

Der Algorithmus von Cocke, Younger und Kasami

47/112

6.34 Algorithmus CYK (2) for i ← 0 to n − 1 do D[i, i + 1] = {X | X → xi+1 ∈ P} od for k ← 2 to n do for i ← 0 to n − k do D[i, i + k] ← ∅ for m ← i + 1 to i + k − 1 do if X → YZ ∈ P ∧ Y ∈ D[i, m] ∧ Z ∈ D[m, i + k] then D[i, i + k] ← D[i, i + k] ∪ {X } fi od od od

Der Algorithmus von Cocke, Younger und Kasami

48/112

6.34 Algorithmus CYK (3) I

Drei ineinander geschachelte Schleifen, I I I

für die ersten n/4 Durchläufe der äußersten und die letzten n/4 Durchläufe der mittleren Schleife wird die innerste jeweils mindestens n/4 mal durchlaufen.

I

Der Test für die if-Anweisung benötigt konstante Zeit.

I

Also ist die Laufzeit des Algorithmus in Θ(n3 ).

I

Es ist auch nicht schwer, einzusehen, dass am Ende in der Tat D[i, j] = {X | X ⇒∗ xi+1 · · · xj } ist.

I

Also ist x1 · · · xn genau dann aus L(G ), wenn S ∈ D[0, n] ist.

Der Algorithmus von Cocke, Younger und Kasami

49/112

6.35 Beispiel für Grammatik aus Beispiel 6.32: {E, F } {V3 } {E} {V2 } {Z( } | 0

(

{Za } | 1

a

{Z+ } | 2

+

{V5 } {Za }

| 3

a

{Z) } | 4

)

| 5

leere Kästchen: D[i, j] = ∅.

Der Algorithmus von Cocke, Younger und Kasami

50/112

Zur Vorbereitung auf den Algorithmus von Earley: einfache Umformung von CYK Der Algorithmus von Earley ist nicht relevant für die Klausur. Deswegen springen wir jetzt zum Abschnitt über LR Parsing

Der Algorithmus von Cocke, Younger und Kasami

51/112

6.36 Algorithmus Andere Darstellung der Pyramide: D[0, 5]

D[0, 1] | 0

x1

| 1

D[0, 4]

D[1, 5]

D[0, 3]

D[1, 4]

D[2, 5]

D[0, 2]

D[1, 3]

D[2, 4]

D[3, 5]

D[1, 2]

D[2, 3]

D[3, 4]

D[4, 5]

x2

| 2

x3

| 3

x4

| 4

x5

| 5

neue Berechnungsreihenfolge: I

spaltenweise von links nach rechts und

I

innerhalb jeder Spalte von unten nach oben.

Der Algorithmus von Cocke, Younger und Kasami

52/112

6.36 Algorithmus (2) for j ← 1 to n do D[j − 1, j] ← {X | X → xj ∈ P} for i ← j − 1 downto 0 do D[i, j] ← ∅ for m ← i + 1 to j do if X → YZ ∈ P ∧ Y ∈ D[i, m] ∧ Z ∈ D[m, j] then D[i, j] ← D[i, j] ∪ {X } fi od od od

Der Algorithmus von Cocke, Younger und Kasami

53/112

6.37 Weitere Transformation von CYK

S0

S1

S2

S3

S4

S5 D[4, 5]

| 0

x1

D[3, 4]

D[3, 5]

D[2, 3]

D[2, 4]

D[2, 5]

D[1, 2]

D[1, 3]

D[1, 4]

D[1, 5]

D[0, 1]

D[0, 2]

D[0, 3]

D[0, 4]

D[0, 5]

|

|

|

|

1

x2

2

Der Algorithmus von Cocke, Younger und Kasami

x3

3

x4

4

x5

| 5

54/112

6.38 Beispiel Für obige Grammatik ergibt sich mit zusätzlicher Angabe der Produktionen und relevanten Trennstellen:

S0

S1

S2

S3

S4

S5 E → | Z( V3 | 0

5

V3 → | EZ) | 1

5

E → | Za V2 | 1

Z( → | ( | 0

| 0

(

| 1

Za → | a |

1

1

a

| 2

Der Algorithmus von Cocke, Younger und Kasami

Z+ → | + |

2

2

+

| 3

4

V2 → | Z+ Za |

V5 → | Za Z) |

2

3

4

Z) → | ) |

Za → | a |

3

3

a

| 4

5

4

4

)

5

| 5

55/112

6.38 Beispiel (2) I

Angabe der rechten Trennstelle redundant

I

stattdessen dicker Punkt: •

S0

S1

S2

S3

S4

S5 E → | Z( V3 • 0

V3 → | EZ) • 1

E → | Za V2 • 1

V2 → | Z+ Za •

V5 → | Za Z) •

2

Z( → | ( •

Za → | a •

0

| 0

(

| 1

Z+ → | + •

1

a

| 2

Der Algorithmus von Cocke, Younger und Kasami

Za → | a •

2

+

| 3

3

Z) → | ) •

3

a

| 4

4

)

| 5

56/112

Einleitung Lexikalische Analyse Lexer-Generatoren Syntaktische Analyse Der Algorithmus von Cocke, Younger und Kasami Der Algorithmus von Earley LR Parsing Nach der Syntaxanalyse

Der Algorithmus von Earley

57/112

Achtung Dieser Abschnitt ist nicht relevant für die Klausur.

Der Algorithmus von Earley

58/112

6.39 In der vorangegangenen Abbildung gibt es I I

für eine Eingabe der Länge n Mengen S0 , S1 ,. . . , Sn , die sogenannte Items enthalten: I I

Produktionen mit einem Markierungspunkt •, bei denen an der gleiche Stelle oder links davon noch eine Trennstelle angegeben ist.

Der Algorithmus von Earley

59/112

6.40 Bei CYK gilt für die Items: I

Hat die Trennstelle Nummer i und ist das Item in Sj , so ist sicher, dass man aus dem, was auf der rechten Seite der Produktion zwischen Trennstelle und • steht, das Teilwort w (i, j] der Eingabe ableiten kann.

I

Eine Produktion X → | YZ • wird in Sj aufgenommen, wenn i

es ein m gibt, so dass ein Item Y → | v • in Sm ist und ein Item Z → | v 0 • in Sj .

i

m

Beim Algorithmus von Earley ändern sich I

die Form der Items und

I

die Regeln zur Berechung der Sj .

Der Algorithmus von Earley

60/112

6.41 Definition I

Ein Item hat die Form X → | v1 • v2 . i

I

Dabei ist X → v1 v2 eine ursprünglichen Produktion.

I

Präfix v1 und Suffix v2 sind ansonsten nicht eingeschränkt.

I

Z. B. erlaubt: Items der Form X → | • w i

Ein Item X → | v1 • v2 wird immer nur zu Mengen Sj mit i ≤ j i

gehören.

Der Algorithmus von Earley

61/112

6.42 Bedeutung der Items Für ein Item X → | v1 • v2 , das in Sj ist, gilt: i I

• markiert die Stelle j, „bis zu der man schon gekommen ist“,

I

und aus dem, was zwischen Trennstelle | und • steht, kann i

man das Teilwort w (i, j] ableiten. I

Für das Suffix hinter dem •, wird noch offen sein, ob aus ihm ein weiteres Stück der Eingabe ableitbar ist.

Der Algorithmus von Earley

62/112

6.43 Algorithmus von Earley Wie kommt ein Item X → | v1 • v2 in eine Menge Sj ? i

1. Lexer-Regel 2. Completer-Regel 3. Predictor-Regel

Der Algorithmus von Earley

63/112

6.43 Algorithmus von Earley (2) Wann kann man • an einem Terminalsymbol „vorbeischieben“? I Prüfe, ob das nächste Eingabesymbol gerade a ist. Falls I

ja, dann füge X → | v1 a • v2 zu Sj+1 hinzu.

I

nein, dann lässt man das.

i

1. Lexer-Regel: Wenn X → | v1 • av2 in Sj und a = w (j, j + 1], dann kommt i

X → | v1 a • v2 zu Sj+1 hinzu. i

Der Algorithmus von Earley

64/112

6.43 Algorithmus von Earley (3) Wann • an Y in X → | v1 • Yv2 aus Sm „vorbeischieben“? i I

Sinnvoll, wenn man aus Y ein Teilwort der Eingabe ableiten kann, das an Trennstelle m beginnt.

I

Das soll man aber gerade daran ablesen können, dass ein Item Y → | v • in einem Sj ist für ein j > m. m

2. Completer-Regel: Wenn X → | v1 • Yv2 in Sm ist und Y → | v • in Sj , dann m

i

kommt X → | v1 Y • v2 zu Sj hinzu. i

Der Algorithmus von Earley

65/112

6.43 Algorithmus von Earley (4) Woher kommen Items der Form Y → | • v2 mit dem • am Anfang? i I

Plausibel: Item Y → | • v2 dann zu einem Sj hinzuzunehmen, i

wenn man „das Y brauchen kann“. 3. Predictor-Regel: Wenn X → | v1 • Yv2 in Sj ist, dann kommt Y → | • v zu Sj i

j

hinzu für jede ProduktionY → v .

Der Algorithmus von Earley

66/112

6.43 Algorithmus von Earley (5) Der Algorithmus von Earley (genauer gesagt die Variante, die ohne sogenannten Look-Ahead in der Eingabe arbeitet) für eine Grammatik G = (N, T , S, P) ergibt sich nun einfach wie folgt: I

Man beginnt mit der Menge S0 , die genau alle Items der Form S → | • w für jede Produktion der Form S → w ∈ P (S ist 0

das Startsymbol der Grammatik). I

Auf eine Menge Sj werden solange die Lexer-, Completer- und Predictor-Regeln angewendet, bis sich nichts mehr ändert.

I

Dann fährt man mit Sj+1 fort.

Der Algorithmus von Earley

67/112

6.43 Algorithmus von Earley (6) Ende des Algorithmus: I

Ist einmal ein Sj = ∅, dann ist die Eingabe nicht ableitbar.

I

Andernfalls muss man am Ende überprüfen, ob Sn ein Item der Form S → | w • enthält.

I

Wenn das der Fall ist (und nur dann), ist die Eingabe in der Grammatik ableitbar.

0

Der Nachweis, dass dieser Algorithmus wirklich das Richtige tut, wurde von Jay Earley geführt.

Der Algorithmus von Earley

68/112

6.44 Beispiel Betrachte sehr einfache Grammatik G = (N, T , S, P) mit I N = {S, X , A, B, C }, T = {a, b, c}, Startsymbol S I P = {S → AX , X → BC , A → a, B → b, C → c}. I Eingabe abc: S0

S1

S → | • AX

S → | A•X

S → | AX •

0

0

0

A → | •a

S2

X → | • BC

X → | B•C

X → | BC •

1

1

1

B → | •b

0

C → | •c

1

2

A → | a•

B → | b•

0

| 0

a

S3

| 1

Der Algorithmus von Earley

C → | c•

1

b

| 2

2

c

| 3

69/112

6.44 Beispiel (2) I

Lexer-Regel: durchgezogene Linien

I

Predictor-Regel: gestrichelte Linien

I

Completer-Regel: gepunktete Linien. S0

S1

S2

S → | • AX

S → | A•X

S → | AX •

0

0

0

X → | • BC

X → | B•C

X → | BC •

1

1

1

B → | •b

A → | •a 0

C → | •c

1

2

A → | a•

B → | b•

0

| 0

a

S3

| 1

Der Algorithmus von Earley

C → | c•

1

b

| 2

2

c

| 3

70/112

Einleitung Lexikalische Analyse Lexer-Generatoren Syntaktische Analyse Der Algorithmus von Cocke, Younger und Kasami Der Algorithmus von Earley LR Parsing Nach der Syntaxanalyse

LR Parsing

71/112

Achtung Dieser Abschnitt und die folgenden sind wieder relevant für die Klausur.

LR Parsing

72/112

6.45 Beispielgrammatik

E

→ E +T | T

T

→ T *F | F

F

→ (E ) | a

LR Parsing

73/112

6.46 Vereinbarung Stets Eingabebegrenzungen vorhanden: E0 → a E ` E

→ E +T | T

T

→ T *F | F

F

→ (E ) | a

LR Parsing

74/112

6.47 Definition I

Eine Grammatik hat die Eigenschaft LR(k) für ein k ∈ N0 , wenn für zwei beliebige Rechtsableitungen r

r

r

r

r

r

S ⇒∗ ξ1 A1 ωη1 ⇒ ξ1 γ1 ωη1 ⇒∗ αωη1 ∈ T ∗ S ⇒∗ ξ2 A2 τ2 ⇒ ξ1 γ1 ωη2 ⇒∗ αωη2 ∈ T ∗ mit |ω| = k oder η1 = η2 = ε immer gilt: ξ2 A2 τ2 = ξ1 A1 ωη2 . I

M.a.W.: Wenn τ2 ∈ T ∗ ist, wenn also A2 das zuletzt durch eine Reduktion entstandene Zeichen ist, dann ist A1 = A2 , ξ1 = ξ2 und τ2 = ωη2 .

I

D. h. durch den Kellerinhalt (ξ1 γ1 ) und die nächsten k Eingabezeichen (ω) ist der nächste Reduktionsschritt bei der Bottom-Up-Syntaxanalyse eindeutig bestimmt.

LR Parsing

75/112

6.49 Prinzip der Syntaxanalyse für LR(k)-Grammatiken I

Erweiterung der Kellerautomaten um „Zustandskeller“

I

„Zustand“ z(κ): Äquivalenzklasse von Kellerinhalten

I

Aktionen: shift- und reduce-Schritte außerdem accept und error   shift    reduce A → γ act(z(κ), ω) =  accept    error

I

Funktion goto zur einfachen Berechnung neuer Zustände: goto(z(κ), X ) = z(κX )

I

zunächst Annahme: Funktionen act und goto liegen schon vor

LR Parsing

76/112

6.49 Prinzip der Syntaxanalyse für LR(k)-Grammatiken (2) Automatenmodell: Zustandskeller

z

Symbolkeller

Eingabe

z = z(κ)

κ

α

LR Parsing

ω

η

77/112

6.49 Prinzip der Syntaxanalyse für LR(k)-Grammatiken (3) Shift-Aktion: vorher Zustandskeller

z

Symbolkeller

Eingabe

z = z(κ)

κ

α

LR Parsing

ω

η

78/112

6.49 Prinzip der Syntaxanalyse für LR(k)-Grammatiken (3) Shift-Aktion: nachher Zustandskeller

z

Symbolkeller

Eingabe

κ

α

LR Parsing

z0

z 0 = goto(z, a)

a

a

ω0

η0

79/112

6.49 Prinzip der Syntaxanalyse für LR(k)-Grammatiken (4) Reduce-Aktion: vorher z 00

Zustandskeller

Symbolkeller

Eingabe

ξ

z

γ

α

LR Parsing

ω

η

80/112

6.49 Prinzip der Syntaxanalyse für LR(k)-Grammatiken (4) Reduce-Aktion: nachher z 00 z 0

Zustandskeller

Symbolkeller

Eingabe

ξ

α

LR Parsing

z 0 = goto(z 00 , A)

A

ω

η

81/112

6.49 Prinzip der Syntaxanalyse für LR(k)-Grammatiken (5) I

Beginn der Analyse: I I

I

in den Symbolkeller wird unten ein a gelegt und in den Zustandskeller entsprechend z(a)

Ende der Analyse: I I I

im Symbolkeller ist nur noch a S ` das kann zu S 0 reduziert werden Der Kellerautomat kann schon akzeptieren, wenn im Symbolkeller a S steht und als letztes Eingabesymbol ` ansteht.

LR Parsing

82/112

6.50 Beispiel

E0 → a E ` E

→ E +T | T

1 | 2

T

→ T *F | F

3 | 4

F

→ (E ) | a

5 | 6

LR Parsing

83/112

6.50 Beispiel (2) I

Die folgenden Tabellen kann man algorithmisch finden

I

wie, verraten wir gleich

I

Es ist hier |ω| = 1.

z 0 1 2 3 4 5 6 7 8 9 10 11

κ a aE · · · (E · · · E+ · · · E +T · · · (T ···T* · · · T *F · · · (F ···( · · · (E ) ···a

a S

+

act(z, ω) * ( S

S S

)

`

a 11

A S

R1 R2

S S

R3 R4

R3 R4

R5 R6

R5 R6

S

11 R1 R2

R1 R2

R3 R4

R3 R4

R5 R6

R5 R6

S

S

S

LR Parsing

*

3 3

S

S

+

goto(z, X ) ( ) E 9 1 10 9

T 5

F 8

4

8

6 6 11

9

11

9

7

2

5

8

84/112

6.50 Beispiel (3)

0 a

0 a 0 a 0 a 0 a 9 (

0 a 9 ( 9 ( 9 ( 9 ( 2 E

κ 0 a 9 ( 11 a 8 F 5 T 2 E 3 +

ω

η

···

(

a

+

a

)

*

a

a

+

a

)

*

a

`

+

a

)

*

a

`

+

a

)

*

a

`

+

a

)

*

a

`

+

a

)

*

a

`

a

)

*

a

`

LR Parsing

`

85/112

6.50 Beispiel (4)

0 a 0 a 0 a

0 a 9 ( 9 ( 9 (

0 a

9 ( 2 E 2 E 2 E 0 a 9 (

2 E 3 + 3 + 3 + 9 ( 2 E 0 a

3 + 11 a 8 F 4 T 2 E 10 ) 8 F

a

)

*

a

)

*

a

`

)

*

a

`

)

*

a

`

)

*

a

`

*

a

`

*

a

`

LR Parsing

`

86/112

6.50 Beispiel (5)

0 a 0 a

0 a 5 T 5 T

0 a 0 a 5 T 6 * 6 * 0 a 0 a

8 F 5 T 6 * 11 a 7 F 5 T 1 E

*

a

`

*

a

`

a

`

` ` ` `

LR Parsing

87/112

6.51 Definition I

Zu jeder Produktion A → X1 X2 · · · Xk (mit Xi ∈ N ∪ T ) gehören die k + 1 markierten Produktionen (Items) I I I I I

I

A → • X1 X2 · · · Xk A → X1 • X2 · · · Xk .. . A → X1 X2 · · · • Xk A → X1 X2 · · · Xk •

Zu A → ε gehört A → •

LR Parsing

88/112

6.52 Konstruktion der Zustände: I

Jeder Zustand ist eine Menge markierter Produktionen.

I

Beginne mit „Zustandskern“ M0 = {S 0 →a • S `}. Vervollständigungsregel:

I

I

I

I

Mi wird erweitert zu zi = kompl (Mi ) ⊇ Mi gemäß folgender „Predictor-Regel“: Wenn A → µ • Bν ∈ kompl (Mi ) und B → γ ∈ P, dann auch B → • γ ∈ kompl (Mi )

Fortschreibungsregel: erzeuge Kerne für neue M-Mengen gemäß: I

wenn A → µ • X ν ∈ z(κ), dann A → µX • ν ∈ z(κX )

anschließend Vervollständigung

LR Parsing

89/112

6.52 (2) Wenn man das so macht, dann gilt: z(κ) enthält genau diejenigen A → µ • ν, für die gilt: I

A → µν ∈ P,

I

∃ξ ∈ V ∗ : κ = ξµ und

I

∃η ∈ T ∗ : S 0 ⇒∗ ξAη

r

LR Parsing

90/112

6.53 großes Beispiel an der Tafel

LR Parsing

91/112

6.54 I

Also: z(κX ) = kompl ({A → µX • ν | A → µ • X ν ∈ z(κ)}).

I

Damit hat man auch schon goto(z(κ), X ) = z(κX ).

LR Parsing

92/112

6.55 Beispiel z0 z1 z2 z3 z4 z5 z6 z7 z8 z9 z10 z11 z12 z13

E z1 z6 z6 z6 z6 z10 z6 z6

T z2 z6 z6 z6 z6 z2 z6 z11

F z3 z6 z6 z6 z6 z3 z6 z3

a z4 z6 z6 z6 z6 z4 z6 z4

( z5 z6 z6 z6 z6 z5 z6 z5

) z6 z6 z6 z6 z6 z6 z6 z6

+ z6 z7 z6 z6 z6 z6 z6 z6

* z6 z6 z9 z6 z6 z6 z6 z6

` z6 z8 z6 z6 z6 z6 z6 z6

z6 z6 z6 z6 z6

z6 z6 z6 z6 z6

z12 z6 z6 z6 z6

z4 z6 z6 z6 z6

z5 z6 z6 z6 z6

z6 z13 z6 z6 z6

z6 z7 z6 z6 z6

z6 z6 z9 z6 z6

z6 z6 z6 z6 z6

LR Parsing

93/112

6.56 Wie findet man die Funktion act? I

Idee: I

I

I

Look-ahead-Mengen la(z, A → µ • ν) ⊂ T und la(z, shift) ⊂ T , so dass für jedes z alle diese Mengen disjunkt sind.

Dann definieren die folgenden Festlegungen act konsistent und sinnvoll:   reduce A → γ falls a ∈ la(z, A → µ •)      falls a ∈ la(z, shift) shift f (z, a) = accept falls z = z(a S ` •)    (oder falls a S • `∈ z und a =` )     error sonst

LR Parsing

94/112

6.57 Mitteilung I

Für verschieden „nette“ Grammatiken kann man auf mehr oder weniger einfache Weise geeignete Look-ahead-Mengen konstruieren.

I

Zum Beispiel ist eine Grammatik LALR(1), wenn die wie folgt definierten la-Mengen paarweise disjunkt sind: I I I

la(z, S 0 →a • S `) = ∅ a ∈ la(z, A → µ • Bν) ∧ b ∈ anf (νa) =⇒ b ∈ la(z, B → • γ) a ∈ la(z, A → µ • X ν) =⇒ a ∈ la(goto(z, X ), A → µX • ν)

LR Parsing

95/112

Einleitung Lexikalische Analyse Lexer-Generatoren Syntaktische Analyse Der Algorithmus von Cocke, Younger und Kasami Der Algorithmus von Earley LR Parsing Nach der Syntaxanalyse

Nach der Syntaxanalyse

96/112

6.59 Grammatiken mit semantischen Aktionen I

„Produktionen“ der Form A → {code 0 } X1 {code 1 } X2 · · · Xm {code m }

I

Codefragment am Ende wird nach der Reduktion auf dem Keller gemäß der kontextfreien Produktion ausgeführt.

I

Für andere Codestücke stelle man sich vor: A → M0 X1 M1 X2 · · · Xm {code m } M0 → ε {code 0 } M1 → ε {code 1 } .. . Mm−1 → ε {code m−1 }

Nach der Syntaxanalyse

97/112

6.60 Beispiel E E T T F F

→ → → → → →

E +T T T *F F (E ) a

{ { { { { {

printf("Reduktion printf("Reduktion printf("Reduktion printf("Reduktion printf("Reduktion printf("Reduktion

Nach der Syntaxanalyse

mit mit mit mit mit mit

E E T T F F

-> -> -> -> -> ->

E+T T T*F F (E) a

\n"); \n"); \n"); \n"); \n"); \n");

} } } } } }

98/112

6.61 Anwendung semantischer Aktionen: Berechnung von Werten für Attribute von Kellersymbolen

Nach der Syntaxanalyse

99/112

6.62 Beispiel (Auswertung arithmetischer Ausdrücke) I

Attribut val für Terminal- und Nichtterminalsymbole

I

Indizes zur Unterscheidung mehrerer Vorkommen des gleichen Symbols in einer Produktion E0 E0 T0 T0 F0 F0

→ → → → → →

E1 +T1 T1 T1 *F1 F1 (E1 ) num

{ { { { { {

E0 .val = E1 .val + T1 .val } E0 .val = T1 .val } T0 .val = T1 .val · F1 .val } T0 .val = F1 .val } F0 .val = E1 .val } F0 .val = num.val }

Nach der Syntaxanalyse

100/112

6.62 Beispiel (2) Sinnvoll: I Auswertung von unten nach oben E.val =?

E.val =?

T .val =?

+

T .val =?

F.val =?

F.val =?

num.val = 3

*

F.val =?

num.val = 4

num.val = 2 Nach der Syntaxanalyse

101/112

6.62 Beispiel (2) Sinnvoll: I Auswertung von unten nach oben E.val =?

E.val =?

T .val =?

+

T .val =?

F.val = 3

F.val = 2

num.val = 3

*

F.val = 4

num.val = 4

num.val = 2 Nach der Syntaxanalyse

101/112

6.62 Beispiel (2) Sinnvoll: I Auswertung von unten nach oben E.val =?

E.val =?

T .val = 12

+

T .val = 2

F.val = 3

F.val = 2

num.val = 3

*

F.val = 4

num.val = 4

num.val = 2 Nach der Syntaxanalyse

101/112

6.62 Beispiel (2) Sinnvoll: I Auswertung von unten nach oben E.val =?

E.val = 2

T .val = 12

+

T .val = 2

F.val = 3

F.val = 2

num.val = 3

*

F.val = 4

num.val = 4

num.val = 2 Nach der Syntaxanalyse

101/112

6.62 Beispiel (2) Sinnvoll: I Auswertung von unten nach oben E.val = 14

E.val = 2

T .val = 12

+

T .val = 2

F.val = 3

F.val = 2

num.val = 3

*

F.val = 4

num.val = 4

num.val = 2 Nach der Syntaxanalyse

101/112

6.63 Attributierte Grammatik I

I

Kontextfreie Grammatik G (N, T , S, P) mit endlicher Menge A von Attributen a ∈ A (mit je einem Wertebereich Va ). Zu jedem Symbol X ∈ N ∪ T disjunkte Attributmengen I

I

DX ⊂ A sogenannter synthetisierter Attribute (engl. synthesized oder derived) IX ⊂ A sogenannter vererbter (engl. inherited) Attribute

Nach der Syntaxanalyse

102/112

6.63 Attributierte Grammatik (2) I

Zu jedem p : X0 → X1 · · · Xm für jedes a ∈ DX0 eine Funktion, die in Abhängigkeit von allen Attributen aller auf der rechten Seite vorkommenden Symbole einen Wert für das Attribut a festlegt.

I

Zu p : X0 → X1 · · · Xm für jedes i = 1, . . . , n und a ∈ IXi eine Funktion, die in Abhängigkeit von allen Attributen aller vorkommenden Symbole einen Wert für das Attribut a festlegt.

Nach der Syntaxanalyse

103/112

6.64 S-attributierte Grammatik I

nur synthetisierte Attribute

I

Solche Grammtiken sind sehr nützlich und für Bottom-up-Syntaxanalyse gut geeignet.

I

Kellerautomat: Bei Reduktion gemäß Produktion A → w berechnet er aus den Attributwerten der Symbole in w die Attributwerte für A.

Nach der Syntaxanalyse

104/112

6.65 Beispiel Kellersymbole Attributwerte a

noch nicht gelesene Eingabe (

2

+

3

)

*

4 `

a

(

2

+

3

)

*

4

a

(

+

3

)

*

4

`

a

(

+

3

)

*

4

`

a

(

+

3

)

*

4

`

a

(

2 2 F 2 T 2 E 2

+

3

)

*

4

`

Nach der Syntaxanalyse

`

105/112

6.65 Beispiel (2) a

(

a

(

a

(

a

(

E 2 E 2 E 2 a

a

(

E 2 + + + ( E 5 a

+

3

)

*

4

3 3 F 3 T 3 E 5 )

)

*

4

`

)

*

4

`

)

*

4

`

)

*

4

`

*

4

`

F 5

*

4

`

Nach der Syntaxanalyse

`

106/112

6.65 Beispiel (3) a a a a

T 5 T 5

T 5 * * a a

Nach der Syntaxanalyse

T 5 *

*

4

4

`

4 4 F 4 T 20 E 20

`

`

` ` `

107/112

6.66 L-attributierte Grammatik I

nur synthetisierte Attribute und solche vererbte Attribute, für die gilt: In jeder Produktion A → X1 · · · Xm hängen die vererbten Attribute jedes Xj nur I I

I

von den vererbten Attributen von A und von den links von Xj stehenden Symbolen X1 , . . . , Xj−1 ab.

auch noch für Bottom-up-Syntaxanalyse gut geeignet

Nach der Syntaxanalyse

108/112

6.67 Beispiel I

Ableitungsbäume enthalten manchmal „überflüssige“ Knoten. Z. B. kompaktere Darstellung arithmetischer Ausdrücke:







I



So genannter abstrakter Syntaxbaum (AST); analog etwa bei Produktionen für Kontrollstrukturen

Nach der Syntaxanalyse

109/112

6.67 Beispiel (2) I

Die Kontruktion eines Syntaxbaumes aus Ableitungsbaum kann man mit Hilfe eines synthetisiserten Attributes ast (die englische Abkürzung für „abstract syntax tree“) durchführen.

I

Attributwert für ein Nichtterminalsymbol A ist dabei ein Zeiger auf die Wurzel des Syntax(teil)baumes, der A entspricht.

Nach der Syntaxanalyse

110/112

6.68 Beispiel I

I

Einfache (!) Codeerzeugung (nur die, aber immerhin) kann man auch mit attributierten Grammatiken machen. synthetisierte Attribute I I

I

code für den erzeugten Code temp für den Namen einer Hilfsvariable, z. B. der Form H4711

Beispiel-Produktion E0 → E1 +T1 I

I

neuer Name für Hilfsvariable: E0 .temp = H gefolgt von 1 + Maximum der Nummern von E1 .temp und T1 .temp alten Code übernehmen und zusätzlichen Code anhängen: E0 .code

= E1 .code; T1 .code; E0 .temp ← E1 .temp + T1 .temp

I

primitive Methode, sie demonstriert aber das Prinzip Nach der Syntaxanalyse

111/112

Parser-Generatoren I

I

lesen eine Eingabedatei, in der im wesentlichen eine kontextfreie Grammatik G beschrieben ist, und erzeugen als Ausgabedatei ein Programm, dass I I I

I

die Syntaxanalyse fur G macht, (implizit) den Ableitungsbaum erzeugt und evtl. Berechnung von Attributwerten durchführt

Beispiele: in der Shell

Nach der Syntaxanalyse

112/112