Fachseminar Compilerbau

Ähnliche Dokumente
2.6 Deterministisches Top-Down-Parsen

Kapitel 5: Syntax-Analyse

Einführung - Parser. Was ist ein Parser?

Konstruieren der SLR Parsing Tabelle

Von der Grammatik zum AST

LL(k)-Analyse. (y) folgt α = β. (x) = start k. (=l> ist ein Linksableitungsschritt)

Kellerautomat (1/4) Kellerautomat (2/4) Kellerautomat (3/4) Kellerautomat (4/4)

Theoretische Grundlagen der Informatik

Theoretische Grundlagen der Informatik. Vorlesung am 8. Januar INSTITUT FÜR THEORETISCHE INFORMATIK

Grundlagen der Programmierung 2 (Comp-D)

Operationen auf Grammatiken

Klammersprache Definiere

VU Software Paradigmen / SS 2014

Inhalt Kapitel 11: Formale Syntax und Semantik

9.4 Grundlagen des Compilerbaus

Alphabet, formale Sprache

Was bisher geschah Chomsky-Hierarchie für Sprachen: L 0 Menge aller durch (beliebige) Grammatiken beschriebenen Sprachen L 1 Menge aller monotonen

Syntaxanalyse Ausgangspunkt und Ziel

Shift Reduce Parser (Bottom up Parser) Historie Grundbegriffe Tabellengesteuerter LR(1) Parser Konstruktion der Elementmengen Tabellenkonstruktion

Shift Reduce Parser (Bottom up Parser) Historie Grundbegriffe Tabellengesteuerter LR(1) Parser Konstruktion der Elementmengen Tabellenkonstruktion

Grammatiken. Grammatiken sind regelbasierte Kalküle zur Konstruktion von Systemen und Sprachen Überprüfung von Systemen und Sprachen

Ein Fragment von Pascal

LR-Parsing. Präsentation vom 19. Dez Adriana Kosior, Sandra Pyka & Michael Weidauer. Automatische Syntaxanalyse (Parsing) Wintersemester 12/13

I.5. Kontextfreie Sprachen

Bottom-Up Analyse. im Einzelnen

Syntax von Programmiersprachen

Kontextfreie Sprachen. Automaten und Formale Sprachen alias Theoretische Informatik. Sommersemester Kontextfreie Sprachen

Compilerbau. Bachelor-Programm. im SoSe Prof. Dr. Joachim Fischer Dr. Klaus Ahrens Dr. Andreas Kunert Dipl.-Inf.

6 Kontextfreie Grammatiken

Definition von LR(k)-Grammatiken

Compilerbau Syntaxanalyse 68. LR(1)-Syntaxanalyse

Automaten und formale Sprachen Klausurvorbereitung

Compiler: Parser. Prof. Dr. Oliver Braun. Fakultät für Informatik und Mathematik Hochschule München. Letzte Änderung:

Übungsblatt 6. Vorlesung Theoretische Grundlagen der Informatik im WS 18/19

Compilerbau. Bachelor-Programm. im SoSe Prof. Dr. Joachim Fischer Dr. Klaus Ahrens Dipl.-Inf. Ingmar Eveslage.

Kapitel: Die Chomsky Hierarchie. Die Chomsky Hierarchie 1 / 14

Formale Sprachen und Automaten

Deterministischer Kellerautomat (DPDA)

Sprachanalyse. Fachseminar WS 08/09 Dozent: Prof. Dr. Helmut Weber Referentin: Nadia Douiri

Umformung NTM DTM. Charakterisierung rek. aufz. Spr. Chomsky-3-Grammatiken (T5.3) Chomsky-0-Grammatik Rek. Aufz.

Automatentheorie und formale Sprachen

CompB Zusammenfassung

2.2 Syntax, Semantik und Simulation

LL(1)-Parsing. Ullrich Buschmann, Linda Schaffarczyk, Maurice Schleussinger. Automatische Syntaxanalyse (Parsing)

Theoretische Grundlagen der Informatik

Ogden s Lemma: Der Beweis (1/5)

LR-Parser, Shift-Reduce-Verfahren

Grammatiken. Eine Grammatik G mit Alphabet Σ besteht aus: Variablen V. Startsymbol S V. Kurzschreibweise G = (V, Σ, P, S)

Vorlesung Programmieren

Das Postsche Korrespondenzproblem

Software Entwicklung 1. Fallstudie: Arithmetische Ausdrücke. Rekursive Klassen. Überblick. Annette Bieniusa / Arnd Poetzsch-Heffter

Grundbegriffe. Grammatiken

Syntax von Programmiersprachen

Syntaktische Analyse (Parsen) Grundlagen der Programmierung. Syntaktische Analyse eines Programms. Syntaktische Analyse bzgl einer CFG

Übersicht. (A) Kontextfreie Sprachen (B) Syntaxanalyse (Parsing) (C) Grenzen der Kontextfreiheit

7. Syntax: Grammatiken, EBNF

Kontextsensitive Sprachen

Interdisziplinäre fachdidaktische Übung: Modelle für Sprachen in der Informatik. SS 2016: Grossmann, Jenko

Einführung in die Computerlinguistik

Vorlesung Automaten und Formale Sprachen Sommersemester Beispielsprachen. Sprachen

Nachklausur zur Vorlesung Informatik 3 mit einigen Anmerkungen zu Lösungen

Kontextfreie Grammatiken. Kontextfreie Grammatiken 1 / 45

Theoretische Informatik I

Das Halteproblem für Turingmaschinen

Programmiersprachen und Übersetzer

5. Syntaxanalyse und der Parser-Generator yacc. 5.5 Aufsteigende Analyse. Absteigende Analyse versus aufsteigende Analyse. 5.5 Aufsteigende Analyse

Definition 4 (Operationen auf Sprachen) Beispiel 5. Seien A, B Σ zwei (formale) Sprachen. Konkatenation: AB = {uv ; u A, v B} A + = n 1 An

Informatik III - WS07/08

Theoretische Grundlagen der Informatik

...imbeispiel: Die Konkatenation der Blätter des Ableitungsbaums t bezeichnen wir auch mit yield(t). liefert die Konkatenation: name int + int.

Syntax von Programmiersprachen

Übung zu Grundlagen des Übersetzerbaus

1 Grammar Engineering. 2 Abstrakte Syntax als abstrakte Algebra. 3 LL(1)-Parser. 4 LR Parser. 5 Fehlerbehandlung. 6 Earley Parser

Lemma Für jede monotone Grammatik G gibt es eine kontextsensitive

Klausur zur Vorlesung Formale Sprachen und Automaten TIT03G2 mit Lösungsvorschlägen

Transkript:

Fachseminar Compilerbau WS 08/09 Matthias Schiller

Syntaktische Analyse 1. Prinzip der Top-Down-Analyse 2. LL(1)-Grammatiken

Modell-Vorstellung Der Scanner liefert als Ergebnis der lexikalischen Analyse, dem Parser als Eingabe zur syntaktischen Analyse, eine Folge von Symbolen. Der Parser prüft, ob diese Symbolfolge von der Grammatik der Sprache erzeugt werden kann.

Parser Grammatiken geben an, wie die Sätze einer Sprache gebildet werden können. Jetzt haben wir den umgekehrten Fall. Ein Satz ist gegeben und es muss nun geprüft werden, ob dieser Satz mit einer Grammatik erzeugt werden kann. Mit Hilfe eines Prüfalgorithmus wird getestet, ob ein bestimmter Satz mit der Grammatik generiert werden kann. Dieses Verfahren wird von einem bestimmten Programm durchgeführt, dem Parser. Ein Parser zerlegt einen Satz in seine syntaktischen Bestandteile, dabei wird die Eingabe in einen Syntaxbaum umgewandelt. Falls ein Satz nicht mit der Grammatik erzeugt werden kann, kommt es zu einem Syntaxfehler.

Parser Kategorien Universelle Parse-Methoden: funktionieren für alle Grammatiken In der Praxis ineffizient, stattdessen in Compilern Anwendung von: Top-Down-Methoden: Konstruktion des Parse-Baums beginnt mit der Wurzel (Top) und setzt seine Arbeit in Richtung der Blätter fort. Bottom-Up-Methoden: Parser arbeitet sich von den Blättern (Bottom) zur Wurzel hoch. Beide Parser-Typen arbeiten die Eingabe Schritt für Schritt von links nach rechts ab.

Top-Down-Analyse Eine der Strategien nach denen ein Parser vorgeht ist die Top-Down-Analyse. Der Parser arbeitet im allgemeinen die Eingabe von links nach rechts symbolweise ab. Die Konstruktion des Parse-Baums beginnt mit der Wurzel und schreitet in Richtung der Blätter fort. Ausgehend vom Startsymbol S der Grammatik wird versucht, einen Ableitungsbaum zu erzeugen, der in seiner Basis dem zu analysierenden Satz entspricht. Diese Strategie geht primär von der Grammatik aus.

Konstruktion des Ableitungsbaum - Konstruktion beginnt an der Wurzel -Sequentielle Abarbeitung des Eingabestroms (meistens von links nach rechts) - Aktuelles zu untersuchendes Eingabesymbol heißt: Look-ahead-Symbol

Vorgehensweise 0. Die Wurzel wird mit dem Startsymbol markiert. Anschließend sind die beiden folgenden Schritte wiederholt auszuführen: 1. Ist der Knoten n mit dem Nichtterminal A markiert, so wähle in Abhängigkeit vom Look-ahead-Symbol eine der Produktionen für A und erzeuge für jedes Grammatiksymbol auf der rechten Seite der Produktion je einen Nachfolger des Knoten n. Ist der gerade behandelte Knoten n mit einem Terminalsymbol markiert und stimmt dieses mit dem Look-ahead-Symbol überein, so gehe sowohl im Parse-Baum, als auch in der Eingabe einen Schritt weiter. Wenn die beiden Terminalsymbole nicht übereinstimmen, so ist ein Fehler gefunden. 2. Suche den nächsten zu behandelnden Knoten n. Sind die Nachfolger eines Knoten erzeugt, so behandeln wir als nächstes diese Nachfolger von links nach rechts.

Beispiel i Grammatik erzeugt eine Untermenge der Pascal-Datentypen. Grammatik G = (N, T, S, P) mit N={ type, simple } T={ ^id, array, [, ], of, integer, char, num, dotdot } S={ type } P={ type -> simple ^id array [ simple ] oftype simple -> integer char num dotdot num } Zu analysierender Satz: array [ num dotdot num ] of integer

Probleme bei der Top-Down-Analyse 1. Sackgassen 2. Links-Faktorisierung 3. Links-Rekursion

Sackgassen (1) Um herauszufinden, ob durch die Auswahl der Produktionen nach und nach ein Satz gebildet werden kann, wendet man ausgehend vom Startsymbol nacheinander die Regeln an. Eine Regel ist immer auf das am weitesten links stehende Nichtterminalsymbol anzuwenden, solange bis der Satzanfang oder ein falscher Satzanfang, erzeugt wurde. Hat man einen falschen Satzanfang abgeleitet, so wurde zuvor eine falsche Alternative einer Produktion gewählt, man befindet sich in einer Sackgasse.

Sackgassen(2) Jetzt geht man in der Ableitung zurück bis zu der letzten Regelanwendung, g an der eine andere Alternative hätte gewählt werden können, dies bezeichnet man als backtracking. So entsteht schließlich eine Ableitung des Satzes. Hat man alle Alternativen ausprobiert, ohne den Satz ableiten zu können, dann gehört dieser Satz nicht zur Sprache der Grammatik. Forderung: Grammatik muss eine sackgassenfreie Top-Down-Analyse ermöglichen.

Beispiel i Grammatik G = (N, T, S, P) mit N={ S, A } T={ a,b,c,d} S={ S } P={S -> cad A -> ab a } Zu untersuchendes Wort ω = cad

Links-Faktorisierung Manchmal kann man nicht gleich entscheiden, welche von mindestens zwei alternativen Produktionen für ein Nichtterminal A auszuwählen ist. Der Grund dafür sind Regeln die mehrere Alternativen mit gleichem Anfang haben: A αβ αγ Hier haben beide Produktionen einen gemeinsamen Faktor α. Man verlagert nun die Entscheidung auf einen späteren Zeitpunkt durch die sog. Links-Faktorisierung, die Aufspaltung in zwei Produktionen: A αb B β γ

Links-Rekursionen Für Produktionen, die in einer Alternative als erstes Symbol wieder das Nichtterminalsymbol stehen haben, das auch auf der linken Seite steht, funktioniert die Strategie, immer zuerst das am weitesten links stehende Nichtterminalsymbol abzuleiten, nicht. Linksrekursive Produktionen können so zu einer Endlos-Schleife führen. In einfachen Fällen kann eine Grammatik-Transformation die Links-Rekursionen k i entfernen: A Aα β A β B B α B ε aus wird und

Beispiel für Linksrekursion Grammatik mit linksrekursiver Regel: EXPR -> EXPR+zahl zahl Versuch den Satz: zahl+zahl mit einem linksrekursiv absteigenden Parser abzuleiten, führt zu einer Endlos-Schleife. EXPR -> EXPR+zahl -> EXPR+zahl+zahl -> EXPR+zahl+zahl+zahl

Transformation Wir betrachten die erste Produktion der Standardgrammatik d tik für arithmetische Ausdrücke: EXPR -> TERM EXPR + TERM EXPR TERM Transformation in: 1. eine rechtsrekursive Produktion: EXPR -> TERM TERM + EXPR TERM EXPR Dies kann jedoch zu Problemen führen wie Eintausch der Links- Dies kann jedoch zu Problemen führen, wie Eintausch der Links Assoziativität eines Operators gegen die Rechts-Assoziativität!

Transformation in: 2. zwei Produktionen der Form: 3. eine Iteration: EXPR -> TERM E E -> +TERM E -TERM E ε EXPR -> TERM { +TERM -TERM } Dies ist eine äquivalente kürzere Darstellung von 2.

Implementationstechniken für Top-Down-Parser 1. Recursive Descent Parser 2. Tabellengesteuerter Top-Down-Parser

LL(1) - Grammatiken Ziel ist es, eine Klasse kontextfreier Grammatiken zu charakterisieren, für die eine sackgassenfreie Top-Down-Analyse möglich ist. Gibt es eine Grammatik der Form X -> σ 1 σ 2 σ k muss allein durch Betrachtung des Look-ahead-Symbols erkennbar sein, welche der Alternativen σ 1, σ 2,,σ k in Frage kommt. Bleibt die einzige Alternative erfolglos, kann man mit Sicherheit davon ausgehen, dass der vorgegebene String keine korrekte Syntax besitzt. Parser, die ohne backtracking auskommen heißen prädiktive Parser und entsprechend nennt man die Analyse, prädiktive Syntaxanalyse.

FIRST- und FOLLOW-Mengen In Verbindung mit einer Grammatik werden die beiden Funktionen FIRST und FOLLOW definiert, die zur Konstruktion eines prädiktiven Parsers dienen. Es soll also für eine Grammatik festgestellt werden, ob sie eine sackgassenfreie Top-Down-Analyse ermöglicht.

FIRST - Definition iti Sei G = (N, T, S, P), σ V*. Dann ist: FIRST(σ):={ t T σ * t } { ε }, falls σ *ε FIRST(σ):={ t T σ * t }, andernfalls. Sei σ eine beliebige Folge von Grammatiksymbolen. Dann ist FIRST(σ) als die Menge aller Terminalsymbole definiert, mit denen ein aus σ hergeleiteter String beginnen kann. Gilt σ ->ε, dann ist auch ε in FIRST(σ).

FOLLOW - Definition iti Sei A N. Dann ist FOLLOW(A):={ t T S * αatβ, α, β beliebig } Für ein Nichtterminal A wird FOLLOW(A) als die Menge aller Terminalsymbole t definiert, die in einer Satzform direkt rechts neben A stehen können.

Berechnung von FIRST - Mengen Für alle Grammatiksymbole X wird FIRST(X) berechnet, indem die folgenden Regeln solange angewendet werden, bis zu keiner FIRST-Menge mehr ein neues Terminalsymbol oder ε hinzukommt: 1. Wenn X Terminalsymbol ist, dann ist FIRST(X) = { X }. 2. Wenn X->ε eine Produktion ist, so füge ε zu FIRST(X) hinzu. 3. Wenn X Nichtterminal i und X->Y 1 Y 2 Y k eine Produktion ist, dann füge wie folgt Symbole zu FIRST(X) hinzu: - falls a FIRST(Y i ) und für alle 1 j < i gilt ε FIRST(Y j ) dann füge a zu FIRST(X) hinzu. - wenn für alle 1 i k gilt ε FIRST(Y i ) dann füge ε zu FIRST(X) hinzu. Dieser Algorithmus spielt quasi abstrakt die möglichen Ableitungen der Grammatik durch.

Berechnung von FOLLOW - Mengen FOLLOW(A) wird für alle Nichtterminale A berechnet, indem die folgenden Regeln solange angewendet werden, bis keine FOLLOW-Menge mehr vergrößert werden kann: 1. Wenn es eine Produktion A->αBβ gibt, wird jedes Element von FIRST(β) mit Ausnahme von ε auch in FOLLOW(B) aufgenommen. 2. Wenn es Produktionen A->αB oder A->αBβ gibt und FIRST(β) ε enthält (d.h. β->ε), dann gehört jedes Element von FOLLOW(A) auch zu FOLLOW(B).

Beispiel 1 Grammatik G = (N, T, S, P) mit N={ S, A, B } T={ a, b, c } S={ S } P={ S -> A B A -> ca a B -> cb b } L(G) = {c n a n 0} {c n b n 0 }

FIRST-Mengen FOLLOW-Mengen σ a b ca cb A B S FIRST(σ) {a} {b} {c} {c} {c, a} {c, b} {a, b, c} T A B S FOLLOW(T) Ø Ø Ø

Beispiel 2 Grammatik G = (N, T, S, P) mit N={ S } T={ t, + } S={ S } P={ S -> S+t ε } L(G) = { (+t) n n 0 } = { ε, +t, +t+t, }

FIRST- und FOLLOW-Mengen σ S +t ε FIRST(σ) {+, ε} {+} {ε} FOLLOW(σ) {+} undefiniert undefiniert

Charakterisierung von LL(1)-Grammatiken (1) Mit Hilfe der beiden Mengen First und Follow wird diese besondere Klasse kontextfreier Grammatiken definiert. Erklärung von LL(1): Das erste L bedeutet, t dass die Analyse, die Eingabe von links nach rechts verarbeitet. Das zweite L bedeutet dass eine Linksableitung erzeugt Das zweite L bedeutet, dass eine Linksableitung erzeugt wird, und die 1, dass in jedem Schritt ein Symbol vorausgeschaut wird, um zu entscheiden, welche Aktion durchzuführen ist.

Charakterisierung von LL(1)-Grammatiken (2) Definition: iti Eine kontextfreie tf Grammatik G=(N, T, S, P), die die Eigenschaften E1 und E2 besitzt, heißt LL(1)-Grammatik. E1: Falls es zu einem Nichtterminal zwei alternative Produktionen N->σ 1 und N->σ 2 gibt, so muss gelten: FIRST(σ 1 ) FIRST(σ 2 ) = Ø E2: Falls aus einem Nichtterminal N der Leerstring ε abgeleitet werden kann, muss gelten: FIRST(N) FOLLOW(N) = Ø Satz: Eine LL(1)-Grammatik erlaubt eine sackgassenfreie Top-Down-Analyse und besitzt keine Linksrekursivitäten.

Analyse 1 1. Bsp. von FIRST UND FOLLOW Besitzt nicht die Eigenschaft E1: FIRST(A) = { a, c } und FIRST(B) = { b, c } Wegen S -> A B müsste aber der Durchschnitt beider Mengen leer sein. Da es keine Produktion nach ε gibt, ist Eigenschaft E2 gegenstandslos. Grammatik ist nicht LL(1)!!!

Analyse 2 2. Bsp. von FIRST UND FOLLOW Besitzt nicht die Eigenschaft E2: FIRST(S) = { +, ε } und FOLLOW(S) = { + } Wegen S -> ε müsste aber gelten: FIRST(S) FOLLOW(S) = Ø Grammatik ist nicht LL(1)!!!

Beispiel 3 Grammatik für arithmetische Ausdrücke (linksrekursive Regeln): G=(N,T,S,P)mit N={ addexpr, multexpr, addop, multop } T={ zahl, +, -, *, /} S={ addexpr } P={ addexpr -> addexpr addop multexpr multexpr multexpr -> multexpr multop zahl zahl addop -> >+ - multop -> * / }

FIRST- und FOLLOW-Mengen σ FIRST(σ) FOLLOW(σ) addop multop { +, - } { *, / } { zahl } { zahl } addexpr multexpr { zahl } { zahl } { +, - } { *, / }

Analyse 3 Es gilt: addexpr -> addexpr addop multexpr addexpr -> multexpr Besitzt nicht die Eigenschaft E1: FIRST(addExpr addop multexpr) = { zahl } und FIRST(multExpr) = { zahl } Der Durchschnitt beider Mengen müsste aber leer sein, um die Eigenschaft zu erfüllen. Grammatik ist nicht LL(1)!!!

Fragen?