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

Ähnliche Dokumente
Grundlagen der Programmierung

Grundlagen der Programmierung 2 (Comp-C)

Grundlagen der Programmierung 2 (Comp-C)

Syntaktische Analyse (Parsen)

LR-Parser, Shift-Reduce-Verfahren

Operationen auf Grammatiken

So schreiben Sie ein Parser-Programm

Compiler: Grundlagen

Phasen eines Compilers. Grundlagen der Programmierung 2. Programm für heute. LR-Parser, Shift-Reduce-Verfahren. Schiebe-Reduziere-Parser

Compiler: Grundlagen

Grundlagen der Programmierung 2 (Comp-D)

Compilerbau. Martin Plümicke WS 2018/19

Ein Compiler ist ein Programm, das ein Programm einer Programmiersprache

Grundlagen der Programmierung 2, SoSe 19

Grundlagen der Programmierung 2, SoSe 18

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

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

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

Einführung in die funktionale Programmierung

Einführung - Parser. Was ist ein Parser?

5. Die syntaktische Analyse

Definition von LR(k)-Grammatiken

Theoretische Grundlagen der Informatik

Fachseminar Compilerbau

Syntaxanalyse Ausgangspunkt und Ziel

Funktionale Programmierung und Typtheorie

Compilerbau Syntaxanalyse 68. LR(1)-Syntaxanalyse

Grundlagen der Programmierung 3 A

Proseminar: Fortgeschrittene Programmierung 1

Grundlagen der Programmierung 2. Operationale Semantik

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

2.6 Deterministisches Top-Down-Parsen

Haskell, Typen, und Typberechnung. Grundlagen der Programmierung 3 A. Überladung und Konversion in Haskell. Typisierung in Haskell

6 Kontextfreie Grammatiken

Kapitel 5: Syntax-Analyse

Theoretische Grundlagen der Informatik

Rekursive Abstiegsparser sind durch gegenseitig rekursive Funktionen - eine für jedes Nichtterminalsymbol der Grammatik - definiert.

Funktionale Programmierung. Das Funktionale Quiz. Das Funktionale Quiz. Das Funktionale Quiz

Konstruieren der SLR Parsing Tabelle

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

Lexikalische Analyse, Tokenizer, Scanner

Grundlagen der Programmierung 3 A

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

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

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

9.4 Grundlagen des Compilerbaus

Klammersprache Definiere

Praktikum Funktionale Programmierung Teil 1: Lexen und Parsen

Von der Grammatik zum AST

Ströme als unendliche Listen in Haskell

Grundlagen der Programmierung 3 A

Grundlegende Datentypen

Grundlagen der Programmierung 3 A

Grundlegende Datentypen

Hallo Welt für Fortgeschrittene

Deterministische PDAs

Inhalt Kapitel 11: Formale Syntax und Semantik

Grundlagen der Programmierung 2 (Comp-A)

Syntax von Programmiersprachen

Haskell, Typen, und Typberechnung. Grundlagen der Programmierung 3 A. Einige andere Programmiersprachen. Typisierung in Haskell

Vorlesung Programmieren

WS 2011/2012. RobertGiegerich. November 12, 2013

WS 2011/2012. RobertGiegerich. November 12, 2013

Der CKY-Parser. Vorlesung Computerlinguistische Techniken Alexander Koller. 27. Oktober 2015

Grundlegende Datentypen

Compiler; Übersetzungsprogramme. Grundlagen der Programmierung 3 A. Compiler für Programmiersprachen. Phasen eines Compilers

Funktionale Programmierung Grundlegende Datentypen

VU Software Paradigmen / SS 2014

Funktionale Programmierung ALP I. Funktionen höherer Ordnung SS Prof. Dr. Margarita Esponda. Prof. Dr. Margarita Esponda

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

LR Parsing. Prinzip: Entwicklung des Parsebaums bottom up, von links nach rechts (Abb. 2.52)

Anwendung von Kontextfreien Grammatiken

WS 2012/2013. Robert Giegerich. 21. November 2012

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

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

Lexikalische Programmanalyse der Scanner

Praktische Informatik 3: Funktionale Programmierung Vorlesung 11 vom : Monaden als Berechnungsmuster

Pumping-Lemma 2 Pumping-Lemma Sei L eine von einem endlichen Automaten erkannte Sprache. Dann existiert eine natürliche Zahl p N derart, dass jedes Wo

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

Software Entwicklung 1

Funktionale Programmierung mit Haskell

Paradigmen der Programmierung

Funktionale Programmierung mit Haskell

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

Kontextfreie Sprachen

Automaten und Formale Sprachen alias Theoretische Informatik. Sommersemester 2011

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

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

Earley Parser. Flutura Mestani

Programmieren in Haskell

Höhere Funktionale Programmierung: Parser-Kombinatoren

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

Automaten und Formale Sprachen alias Theoretische Informatik. Sommersemester 2012

Kontextfreie Sprachen

ProInformatik: Funktionale Programmierung. Punkte

Die Definition eines Typen kann rekursiv sein, d.h. Typ-Konstruktoren dürfen Elemente des zu definierenden Typ erhalten.

Transkript:

Syntaktische Analyse (Parsen) Grundlagen der Programmierung Compiler: Parser (5C) Gegeben: eine kontextfreie Grammatik G und ein String w. Prof. Dr. Manfred Schmidt-Schauß Sommersemester 2015 Fragen: Vorgehen: (1) gehört w zu L(G)? (2) Welchen Syntaxbaum hat w? (3) Welche Bedeutung hat w? Konstruiere Herleitungsbaum zu w Grundlagen der Programmierung 2 Parser 2/57 Syntaktische Analyse eines Programms Syntaktische Analyse bzgl einer CFG Gegeben: Fragen: Aufgabe: Syntax einer Programmiersprache und der Quelltext eines Programms. Ist das Programm syntaktisch korrekt? Was soll dieses Programm bewirken? Ermittle Bedeutung des Programms, Konstruktionsverfahren für Herleitungsbäume (bzw. Syntaxbäume) Für jede CFG gibt es einen Parse-Algorithmus mit worst case Laufzeit O(n 3 ) (n : Anzahl der Eingabesymbole) CYK: Cocke, Younger, Kasami, falls Grammatik in Chomsky-Normalform (Alle Regeln von der Form N W mit W 2 oder Earley-Algorithmus CYK benutzt dynamisches Programmieren. erzeugt eine Tabelle: pro Paar (N, w) von Nichtterminal N und Subwort w der Eingabe ein Eintrag True wenn N G w, sonst False Grundlagen der Programmierung 2 Parser 3/57 Grundlagen der Programmierung 2 Parser 4/57

Syntaktische Analyse bzgl einer CFG Parse-Methoden und Beschränkungen Praxis: Für jede Programmiersprache gibt es einen Parser, der effizient arbeitet, d.h. in O(n), oder in O(n log(n)) Beschränkung in dieser Vorlesung auf einfach implementierbare oder effiziente Parser Nur für eingeschränkte CFGs Verarbeitung des Zeichenstroms bzw. des Eingabewortes von links nach rechts evtl. auch mit Vorausschau um einige Zeichen. Grundlagen der Programmierung 2 Parser 5/57 Grundlagen der Programmierung 2 Parser 6/57 Parse-Methoden: Vorgehensweisen: Parse-Methoden: Vorgehensweisen: Top-Down: Es wird versucht eine Herleitung vorwärts, vom Startsymbol der Grammatik aus, zu bilden ( forward-chaining ) Bottom-Up: Es wird versucht eine Herleitung rückwärts, vom Wort aus, zu bilden; bis das Startsymbol der Grammatik erreicht ist. ( backward-chaining ). Weiteres Unterscheidungsmerkmal: R : Konstruktion einer Rechtsherleitung L : Konstruktion einer Linksherleitung Gängige Kombinationsmöglichkeiten: Top-Down-Verfahren zur Konstruktion einer Linksherleitung Bottom-Up-Verfahren zur Konstruktion einer Rechtsherleitung Grundlagen der Programmierung 2 Parser 7/57 Grundlagen der Programmierung 2 Parser 8/57

Beispiel 09-Beispiel: Top-down: S ::= AB A ::= 0 1 B ::= 8 9 Frage: Kann 09 aus dieser Grammatik hergeleitet werden? Start mit Startsymbol S Rate die Produktionen; Nutze den zu parsenden String zur Steuerung Bilde Restproblem Ziel: Eingabestring bis zum Ende verarbeiten. Grundlagen der Programmierung 2 Parser 9/57 Grundlagen der Programmierung 2 Parser 10/57 Beispiel 09-Beispiel: Bottom-up: Vorgehen: Regeln rückwärts auf den gegebenen String anwenden das Startsymbol der Grammatik ist zu erreichen S ::= AB A ::= 0 1 B ::= 8 9 Eingabe 09 09 9 ε (N + T ) -Wort S AB B Herleitung S AB 0B 09 Das ergibt eine Linksherleitung. Beachte 09 wird von links nach rechts bearbeitet Jedes Eingabezeichen bestimmt eindeutig die Produktion 09 A9 AB S anders geschrieben: S AB A9 0 Eine Rechtsherleitung wurde konstruiert Beachte: Manchmal sind mehrere Regeln anwendbar zudem muss man i.a. den Teilstring raten, auf den eine Produktion (rückwärts) anzuwenden ist Im Beispiel: Gleicher Herleitungsbaum S A B Grundlagen der Programmierung 2 Parser 11/57 1 2 Grundlagen der Programmierung 2 Parser 12/57

Beispiel: Suche nach der Herleitung Beispiel: Bemerkungen S ::= A B A ::= 0A 1 B ::= 0B 2 Kann 002 hergeleitet werden? Ziel 002 002 02 2 NT-Wort S A A A Herleitung S A 0A 00A? 002 kann nur aus B hergeleitet werden: Ziel 002 002 02 2 NT-Wort S B B B Herleitung S B 0B 00B 002 S ::= A B A ::= 0A 1 B ::= 0B 2 Ein deterministischer Top-Down-Parser muss beim ersten Zeichen von 002 entscheiden, ob A, oder B. Diese Wahl kann falsch sein. Misslingt eine Herleitung, so muss der Parser zurücksetzen: Backtracking Grundlagen der Programmierung 2 Parser 13/57 Grundlagen der Programmierung 2 Parser 14/57 Parsemethoden Rekursiv absteigende Parser Wir betrachten im folgenden: rekursive absteigende Parser: Allgemeine optimierte: rekursive-prädiktive Parser (LL-Parser) Bottom-Up-Parser (LR-Parser) Rekursiv absteigender Parser / Syntaxanalyse ist an der Form der Regeln der Grammatik orientiert. Methode: Top-Down-Prüfung der Anwendbarkeit der Regeln Grundlagen der Programmierung 2 Parser 15/57 Grundlagen der Programmierung 2 Parser 16/57

Struktur eines rekursiv absteigenden Parsers Struktur eines rekursiv absteigenden Parsers Top-Down bzgl. der Grammatik. Eingabewort von links nach rechts Backtracking, falls Sackgasse Konstruktion einer Linksherleitung Pro Nichtterminal N wird ein Parser P N programmiert. Eingabe: String (bzw. Tokenstrom) Ausgabe: Syntaxbaum zum Prefix der Eingabe; und Reststring N w 1... w n (das sind alle Regeln zu N) P N probiert alle w i aus Prüfung, ob ein w i passt: w i = w i1 w i2... w im von links nach rechts durchgehen Jeweils Parser P wij aufrufen und Reststring weitergeben I.a. rekursiver Aufruf, falls w ij Nichtterminal. Grundlagen der Programmierung 2 Parser 17/57 Grundlagen der Programmierung 2 Parser 18/57 Eigenschaften: rekursiv-absteigender Parser Rekursiv-absteigende Parser Liefert alle Linksherleitungen für alle Präfixe des Tokenstroms (wenn der Parser terminiert) Leicht implementierbar Leicht erweiterbar auf weitere Einschränkungen I.a. exponentiell oder sogar: Terminiert nicht für bestimmte (linksrekursive) Grammatiken, obwohl eine Herleitung existiert: Beispiel A ::= A+A A-A 1... 9 Eingabe: 1+1 : Aber: nur die erste Regel wird (jeweils rekursiv) versucht: (A,1+1) (A+A,1+1) ((A+A)+A, 1+1)... Programme von Programmiersprachen kann man i.a. in O(n) oder O(n log(n)) parsen, Effiziente rekursiv-absteigende Parser benötigen i.a.: Erweiterungen wie Vorausschau Umbau der Grammatik (Optimierung der Grammatik) Grundlagen der Programmierung 2 Parser 19/57 Grundlagen der Programmierung 2 Parser 20/57

Funktionale Kombinator-Parser Funktionale Kombinator-Parser Programmierung Implementierung von rekursiv-absteigenden Parsern in Haskell Vorteile relativ leicht verständliche Programmierung 1-1-Übersetzung der Regeln in Programmcode Nach Erweiterung und Optimierung kann der Parser Fehler gut erkennen und deterministisch werden. Pro Nichtterminal N eine Funktion parsern:: String -> [(String, Syntaxbaum)] bzw. parsern:: [Token] -> [([Token], Syntaxbaum)] Präfix der Eingabe (Rest der Eingabe, Resultat (z.b. Syntaxbaum) )... Liste aller Möglichkeiten Grundlagen der Programmierung 2 Parser 21/57 Grundlagen der Programmierung 2 Parser 22/57 Funktionale Kombinator-Parser Haskell-Implementierung der Parser-Kombinatoren Kombinator (kombiniert Parser) Z.B. Alternative, Sequenz, Resultat-Umbau Um Backtracking zu implementieren: Liste von erfolgreichen Ergebnissen verzögerte Auswertung ergibt richtige Reihenfolge der Abarbeitung. module CombParser where --- bzw. CombParserWithError import Char infixr 6 <*>, <*, *> infixr 4 < >, <!> infixl 5 <@ type Parser a b = [a] -> [([a],b)] erkennt ein Zeichen: symbol :: Eq s => s -> Parser s s symbol a [] = [] symbol a (x:xs) a ==x = [(xs,x)] otherwise = [] Grundlagen der Programmierung 2 Parser 23/57 Grundlagen der Programmierung 2 Parser 24/57

Haskell: Parser-Kombinatoren (2) Haskell: Parser-Kombinatoren (3) erkennt einen String: token :: Eq s => [s] -> Parser s [s] -- token :: Eq s => [s] -> Parser s [s] token k xs k == (take n xs) = [(drop n xs, k)] otherwise = [] where n = length k testet ein Zeichen der Eingabe: satisfy :: (s -> Bool) -> Parser s s satisfy p [] = [] satisfy p (x:xs) = [(xs,x) p x] immer erfolgreich: succeed :: r -> Parser s r succeed v xs = [(xs,v)] immer fehlschlagend: pfail :: Parser s r pfail xs = [] epsilon :: Parser s () epsilon xs = [(xs,())] Grundlagen der Programmierung 2 Parser 25/57 Grundlagen der Programmierung 2 Parser 26/57 Haskell: Parser-Kombinatoren (4) Haskell: Parser-Kombinatoren (4b) Sequenzkombinator : (<*>) :: Parser s a -> Parser s b -> Parser s (a,b) (p1 <*> p2) xs = [(xs2, (v1,v2)) (xs1,v1) <- p1 xs, (xs2,v2) <- p2 xs1] xs: xs2 p1 p2 } {{ } p1 <*> p2 p1 parst den Anfang der Eingabe; gibt den Reststring xs1 weiter an p2 p2 parst danach den Anfang des Reststrings gibt den Reststring zurück Gesamtresultat = Tupel aus den zwei Resultaten Grundlagen der Programmierung 2 Parser 27/57 Alternativkombinator : (< >) :: Parser s a -> Parser s a -> Parser s a (p1 < > p2) xs = p1 xs ++ p2 xs Es werden beide Parser p1 und p2 auf die gleiche Eingabe angewendet Alternativkombinator-2: nur das erste Ergebnis: (<!>) :: Parser s a -> Parser s a -> Parser s a (p1 <!> p2) xs = take 1 (p1 xs ++ p2 xs) Grundlagen der Programmierung 2 Parser 28/57

Haskell: Parser-Kombinatoren (6) Haskell: Parser-Kombinatoren (6) Operation auf dem Ergebnis des Parse : (<@) :: Parser s a -> (a->b) -> Parser s b (p <@ f) xs = [(ys, f v) (ys,v) <- p xs] Kombinatoren, die die Nachverarbeitung miterledigen: ignoriert rechtes Ergebnis: (<*) :: Parser s a -> Parser s b -> Parser s a p <* q = p <*> q <@ fst f ist der Modifikator des Ergebnisses v des Parsers p. Typischer Fall: p < > q kann ungetypt sein, wenn p, q verschiedene Ergebnis-Typen liefern. Dann z.b. (p <@ f) < > (q <@ g) benutzen. ignoriert linkes Ergebnis: (*>) :: Parser s a -> Parser s b -> Parser s b p *> q = p <*> q <@ snd Grundlagen der Programmierung 2 Parser 29/57 Grundlagen der Programmierung 2 Parser 30/57 Funktionale Kombinator-Parser Haskell: Parser-Kombinatoren (7) erkennt Folge. d.h. entspricht *: Aufgaben der Kombinatoren 1 Lesen und Prüfen, 2 Kombinatoren zum Aufbau des Syntaxbaums Beispiel p <* q = p <*> q <@ fst <*> ist ein Kombinator zum Lesen und Prüfen. <@ bewirkt die Nachbearbeitung. many :: Parser s a -> Parser s [a] many p = p <*> many p <@ list < > succeed [] many1 p = p <*> many p <@ list digit :: Parser Char Int digit = satisfy isdigit <@ f where f c = ord c - ord 0 erkennt Zahl: natural :: Parser Char Int natural = many1 digit <@ foldl f 0 where f a b = a*10 + b Grundlagen der Programmierung 2 Parser 31/57 Grundlagen der Programmierung 2 Parser 32/57

Haskell: Parser-Kombinatoren (8) Nimmt nur die erste (maximale) Alternative des many: nur erlaubt, wenn der Parser die weggelassenen Alternativen nicht benötigt manyex :: Parser s a -> Parser s [a] manyex p = p <*> many p <@ list <!> succeed [] many1ex p = p <*> manyex p <@ list option p = p <@ (\x->[x]) <!> epsilon <@ (\x-> []) Nimmt nur die erste (maximale) Alternative bei Zahlen: Haskell: Parser-Kombinatoren (9) Erkennt Klammerung; Klammern kommen nicht in den Syntaxbaum: pack:: Parser s a -> Parser s b -> Parser s c -> Parser s b pack s1 p s2 = s1 *> p <* s2 Erkennt Infix-Folge wie z.b. (1+2+3+4+5): Liste der Argumente: opseqinf psymb parg = (parg <*> many (psymb *> parg)) <@ list naturalex :: Parser Char Int naturalex = many1ex digit <@ foldl f 0 where f a b = a*10 + b Grundlagen der Programmierung 2 Parser 33/57 Grundlagen der Programmierung 2 Parser 34/57 Einfaches Beispiel Leicht komplexeres Beispiel Grammatik-Regeln: S AB A a B b Programm: parse S = parse A <*> parse B parse A = (symbol a ) parse B = (symbol b ) Grammatik-Regeln: S AB A aa a B bb b Programm: parses = parsea <*> parseb parsea = (symbol a ) <*> parsea < > (symbol a ) (parseb = (symbol b ) <*> parseb) < > (symbol b ) Grundlagen der Programmierung 2 Parser 35/57 Grundlagen der Programmierung 2 Parser 36/57

Leicht komplexeres Beispiel Leicht komplexeres Beispiel Grammatik-Regeln: S AB A aa a B bb b Typgerecht programmieren mit Modifikatoren und Syntaxbaum-Erzeugung: parses = (parsea <*> parseb) <@ (\(x,y)-> [x,y]) parsea = ((symbol a ) <*> parsea) <@ (list) < > (symbol a ) <@ (\x -> (x:[])) parseb = (((symbol b ) <*> parseb) <@ (list)) < > (symbol b ) <@ (\x -> (x:[])) list (x,xs) = x:xs Zusätzliche Verwendung des Kombinators many um neue Parser zu generieren: parse S = parsea <*> parseb <@ (\(x,y) -> [x,y]) parsea = many (symbol a ) parseb = many (symbol b ) oder, noch besser: parse S = parsea <*> parseb <@ (\(x,y) -> [x,y]) parsea = manyex (symbol a ) parseb = manyex (symbol b ) Grundlagen der Programmierung 2 Parser 37/57 Grundlagen der Programmierung 2 Parser 38/57 Beispiel: Polymorphe Typ-Ausdrücke Beispiel: Polymorphe Typ-Ausdrücke Grammatik AT ::= AT -> AT (AT) [AT] Var TCA TCA ::= TC (TC AT... AT) (AT 1,...,AT n ), n > 1 Grammatik ist linksrekursiv! umgebaute Grammatik; nicht linksrekursiv und optimiert für den Parser AT ::= NOAR { NOARNX ε } NOARNX ::= -> AT NOAR ::= Var TCT KLRUND KLECK TCT ::= TC NOAR... NOAR KLRUND ::= (AT,...,AT) Mindestens 2-Tupel KLECK ::= [AT] Grundlagen der Programmierung 2 Parser 39/57 Grundlagen der Programmierung 2 Parser 40/57

Kombinatorparser mit Fehlerbehandlung Kombinatorparser; Beispiele Erweiterte Bibliothek mit neuen Kombinatoren ((p1 <*>!) errstr) p2 ((p1 *>!) errstr) p2 ((p1 *<!) errstr) p2 Ergibt Fehler mit Text errstr Wenn p2 fehlschlägt Wie <*>! aber nur Ergebnis von p2 Wie <*>! aber nur Ergebnis von p1 AT ::= NOAR { NOARNX ε } NOARNX ::= -> AT NOAR ::= Var TCT KLRUND KLECK TCT ::= TC NOAR... NOAR KLRUND ::= (AT,...,AT) Mindestens 2-Tupel KLECK ::= [AT] parseklrund = (parsesymbol ( *> (parseinklrund <*! ") erwartet") (parsesymbol ) )) <@ id parseinklrund = (parseat <*> (manyex (((parsesymbol, ) *>! "Typ nach, erwartet") parseat))) <@@ (\(t1,t2) er -> if null t2 then t1 else (Fn ("Tup"++(show ((length t2) +1))) (t1:t2) er)) Grundlagen der Programmierung 2 Parser 41/57 Grundlagen der Programmierung 2 Parser 42/57 Kombinatorparser mit Fehlerbehandlung Kombinatorparser mit Fehlerbehandlung Programme und Vorführung typeuniferr combparserwitherror parseequation parseat. prelex printunif " (a,a) = (b,[b]) Programme und Vorführung html-parser.hs main prelex (linposnumbering "<DD> xxx </DD>\n<br> text </br>") Grundlagen der Programmierung 2 Parser 43/57 Grundlagen der Programmierung 2 Parser 44/57

Fehler-Meldungen: Bemerkungen Rekursiv-prädiktive Parser Die Fehlererkennung und -meldung sollte spezifisch sein und möglichst genau die Ursache und Stelle melden. Schlecht: Keine Alternativen mehr gefunden in Zeile... Gut Fehler in Zeile... Spalte... Möglicher Grund:... Bei deterministischen Parsern (und Kombinatorparser mit Fehlerbehandlung) Die Fehlerstelle ist klar; die Fehlerursache ist auch meist spezifisch genug Bei Parsern mit Backtracking und ohne Fehlerbehandlung Der richtige Fehlerstelle ist meist unklar Der Backtracking-Parser kann meist nur melden: keine Alternativen mehr Optimierte rekursiv absteigende Parser für eingeschränkte Grammatiken ( LL(1) ). Eigenschaften: Die anzuwendende Produktion ist immer eindeutig festgelegt abhängig vom aktuellen Nichtterminal und dem nächsten Symbol (Lookahead-Symbol) der Resteingabe kein Zurücksetzen notwendig, deterministische Abarbeitung der Eingabe von links nach rechts Aber: man kann nicht für jede eindeutige kontextfreie Grammatik einen rekursiv-prädiktiven Parser konstruieren. Grundlagen der Programmierung 2 Parser 45/57 Grundlagen der Programmierung 2 Parser 46/57 Rekursiv-prädiktive Parser Rekursiv-prädiktive Parser Zweidimensionale Tabelle: (Lookahead-Symbol, Nichtterminal) Regel oder Fehlereintrag Eindeutigkeitsbedingung: Wenn A w 1... w n alle Regeln zu A sind: Falls Parser im Zustand A Für jedes erste Symbol a der Eingabe: nur eine Regel A w i darf anwendbar sein! Tabellengesteuerter rekursiv-prädiktiver Parser: Beispiel: A bcd aef cg H H dabc... Grundlagen der Programmierung 2 Parser 47/57 Grundlagen der Programmierung 2 Parser 48/57

Rekursiv-prädiktive Parser Rekursiv-prädiktive Parser, ε-fall Sonderfall: Es gibt eine Regel A w i mit w i ε: Diese wird ausgewählt, wenn: keine passende rechte Seite für das Lookahead-Symbol und das Lookahead-Symbol kann auf A folgen und es gibt nur eine solche Regel für A Beispiel: S AB AC A bcd aef cg H H ε... B da C ea Im Zustand A und bei Eingabesymbol d: A H wird ausgewählt. Grundlagen der Programmierung 2 Parser 49/57 Grundlagen der Programmierung 2 Parser 50/57 FIRST- und FOLLOW-Mengen Beispiel für first Wenn Grammatik G gegeben ist: first(a) := Terminal-Symbole die am Anfang eines erkannten A-Wortes stehen können. (auch ε) follow(a) := Terminal-Symbole die auf ein erkanntes A-Wort folgen können. Diese Mengen kann man in allen rekursiv-absteigenden Parsern zur Eindämmung, evtl. zur Vermeidung, von Backtracking verwenden. Ex ::= Plus Plus ::= SigZ Plusrest PlusRest ::= + SigZ PlusRest ε SigZ ::= B - B B ::= Z ( Ex ) Z ::= 0... 9 Man erhält als first-mengen: Ex Plus Plus SigZ B Z Rest 0,...,9, (,- 0,...,9, (,- +, ε 0,...,9, (,- 0,...,9, ( 0,...,9 Grundlagen der Programmierung 2 Parser 51/57 Grundlagen der Programmierung 2 Parser 52/57

Beispiel für follow : Vorgehen des LL(1)-Parsers Ex ::= Plus Plus ::= SigZ Plusrest PlusRest ::= + SigZ PlusRest ε SigZ ::= B - B B ::= Z ( Ex ) Z ::= 0... 9 Man erhält als follow- Mengen: Ex Plus PlusRest SigZ B Z ) ) ) +,) +,) +,) Grammatik ist LL(1) parsebar, da: First-Mengen zu Regelalternativen passen und first(plusrest) follow(plusrest) = Bei Symbol a, und aktuellem Nichtterminal A: Ist a first(w i ) für eine Regel A ::= w i, dann nehme diese Regel. (ε first(w i ) für alle i muss gelten. ) Ist a first(w i ) für alle Regeln A ::= w i, dann gibt es maximal eine Regel A ::= w mit first(w) = Falls a follow(a), dann diese Regel. Wenn auch dann keine passende Alternative existiert, wird mit Fehler abgebrochen. Vorteil: genaue und frühe Fehlererkennung Grundlagen der Programmierung 2 Parser 53/57 Grundlagen der Programmierung 2 Parser 54/57 Beispiel: vereinfachte Grammatik für Ausdrücke Beispielparser zur Grammatik Expr ::= Term Rest Rest ::= + Term Rest Term Rest ε Term ::= 0... 9 first(term Rest) = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9} first(+ Term Rest) = {+}, first( Term Rest) = { } first(expr ) = first(term ) = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9} first(rest) = {+,, ε} follow(expr) =. follow(rest) =. follow(term) = {+, }. Diese Grammatik hat somit die LL(1)-Eigenschaft. Parsebaum: PExp 1 PRest + 2 PRest Syntaxbaum: 3 PLeer + 1 2 3 Der Parsebaum entspricht der Grammatik, aber noch nicht der gewünschten Struktur des arithmetischen Ausdrucks. Man braucht eine Nachbearbeitung des Parsebaumes. Grundlagen der Programmierung 2 Parser 55/57 Grundlagen der Programmierung 2 Parser 56/57

Prädiktiv vs. Kombinatoren Meistens kann man für Grammatiken die geeignet sind für rekursiv-prädiktive Parser (LL(1)) auch einen deterministischen Kombinator-Parser schreiben. (Nach etwas Analyse und Nachdenken) Dabei ist im Parserprogramm überall der Parserkombinator < > durch <!> ersetzt. und man kann teilweise die um Fehlermeldungen erweiterten Kombinatoren verwenden D.h der Parser ist frei von Backtracking. Grundlagen der Programmierung 2 Parser 57/57