Programmieren Stefan Janssen ProgrammiereninHaskell · Programmieren in Haskell Stefan Janssen...

Post on 24-Jul-2018

264 views 0 download

Transcript of Programmieren Stefan Janssen ProgrammiereninHaskell · Programmieren in Haskell Stefan Janssen...

Programmierenin Haskell

StefanJanssen

Grammatikenund Parser

DSL

Programmieren in HaskellKombinator-Sprachen

Stefan Janssen

Universität BielefeldAG Praktische Informatik

20. Januar 2015

Programmierenin Haskell

StefanJanssen

Grammatikenund Parser

DSL

Vorschau

Das heutige Thema hat zwei Teilekontextfreie Grammatiken, Sprachen und ihre “Parser”Domain Specific Languages (DSL, anwendungsspezifischeSprachen) am Beispiel des Parsensals Beispiel einer Spracherweiterung in Haskell

Gemeinsamer Ausgangspunkt: In vielen Anwendungenbesteht die Eingabe aus einem Text, der in seiner Strukturerfasst werden muss, ehe die eigentliche Verarbeitungbeginntkann diese Struktur durch ein Grammatik beschriebenwerden

Programmierenin Haskell

StefanJanssen

Grammatikenund Parser

DSL

Vorschau

Das heutige Thema hat zwei Teilekontextfreie Grammatiken, Sprachen und ihre “Parser”Domain Specific Languages (DSL, anwendungsspezifischeSprachen) am Beispiel des Parsensals Beispiel einer Spracherweiterung in Haskell

Gemeinsamer Ausgangspunkt: In vielen Anwendungenbesteht die Eingabe aus einem Text, der in seiner Strukturerfasst werden muss, ehe die eigentliche Verarbeitungbeginntkann diese Struktur durch ein Grammatik beschriebenwerden

Programmierenin Haskell

StefanJanssen

Grammatikenund Parser

DSL

Beispiel: Übersetzung von Programmen

Wichtigstes Beispiel: CompilerEingabe: Programmtext als ASCII StringZerlegung 1: Folge von Namen, Zahlen, OperatorenZerlegung 2: Erkennen von Deklarationen, Definitionen,Ausdrücken, Import-Listen, ...Resultat der “syntaktischen Analyse”: BaumartigeDarstellung des Programms als rekursiver DatentypWeitere Verarbeitung des Programmbaums: SemantischePrüfung, Transformation, Optimierung, Codeerzeugung, ...

Programmierenin Haskell

StefanJanssen

Grammatikenund Parser

DSL

Beispiele von DSLs (1)

Textbeschreibung in LATEX:

\section{Grammatiken und Parser}\begin{frame}{Vorschau}Das n\"achste Thema hat zwei Teile\begin{itemize}\item ‘‘Parser’’ f\"ur kontextfreie Sprachen\item Domain Specific Languages (DSL, anwendungsspezifische Sprachen) \emph{am Beispiel} des Parsens\item \emph{als Beispiel} einer Spracherweiterung in Haskell\end{itemize}

\pause...

Programmierenin Haskell

StefanJanssen

Grammatikenund Parser

DSL

Beispiele von DSLs (2)

Literatureintrag in BibTeX:

Example1 @book{UBHD1745069 ,2 autho r={Bird , R i cha rd } ,3 t i t l e ={ I n t r o d u c t i o n to f u n c t i o n a l programming u s i n g H a s k e l l } ,4 pages ={433} ,5 p u b l i s h e r ={P r e n t i c e H a l l } ,6 yea r ={1998} ,7 i s b n ={978−0−13−484346−9},8 e d i t i o n ={2nd}9 }

\cite{UBHD 1745069} -- Referenz im Text

Programmierenin Haskell

StefanJanssen

Grammatikenund Parser

DSL

Beispiele von DSLs (2)

Literatureintrag in BibTeX:

Example1 @book{UBHD1745069 ,2 autho r={Bird , R i cha rd } ,3 t i t l e ={ I n t r o d u c t i o n to f u n c t i o n a l programming u s i n g H a s k e l l } ,4 pages ={433} ,5 p u b l i s h e r ={P r e n t i c e H a l l } ,6 yea r ={1998} ,7 i s b n ={978−0−13−484346−9},8 e d i t i o n ={2nd}9 }

\cite{UBHD 1745069} -- Referenz im Text

Programmierenin Haskell

StefanJanssen

Grammatikenund Parser

DSL

Textzerlegung mit groupBy

Eine ausgesprochen nützliche Funktion:groupBy aus Data.List

groupBy :: (a -> a -> Bool) -> [a] -> [[a]]

zerlegt einen String in “Gruppen”, so dassconcat . groupBy f == id

für jedes f

gruppiert wird, solange die Bedingung f über dem erstenund dem (bisher) letzten Buchstaben der Gruppe erfüllt istder erste Buchstabe, der die Bedingung f verletzt, starteteine neue Gruppe

Programmierenin Haskell

StefanJanssen

Grammatikenund Parser

DSL

Beispiele zu GroupBy

1 Parse > groupBy (==) " aaggccccggt "2 ["aa","gg","cccc","gg","t"]3

4 Parse > groupBy (/=) " aaccgaccgaaa "5 ["a","accg","accg","a","a","a"]6

7 Parse > groupBy (<=) " abraham "8 [" abraham "]9

10 Parse > groupBy (<) " abraham "11 ["abr","ah","am"]12

13 Parse > groupBy (\a b -> a /= ’ ’ && b /= ’ ’)14 "wenn er aber kommt?"15 ["wenn"," ","er"," ","aber"," ","kommt?"]

Programmierenin Haskell

StefanJanssen

Grammatikenund Parser

DSL

Textstruktur allgemein

groupBy ist nützlich, aber beschränkt.

Nur ein Kriterium der Zerlegung,nur eine Form des Zusammenhangs der Teile (:)

Texte habe Zeichen, Wörter, Sätze, Abschnitte, Kapitel, relativflache StrukturenProgramme haben Operatoren, Variablen, Konstanten, Zahlen,Ausdrücke, Deklarationen, Definitionen, und beliebig tiefeStrukturenKomplexe formale Sprachen beschreibt man durch

Grammatiken, die Sprachen generieren, oderAutomaten, die Sprachen akzeptieren

Beide Methoden sind gleich mächtig

Programmierenin Haskell

StefanJanssen

Grammatikenund Parser

DSL

Klassen formaler Sprachen

Endliche Automaten sind aus A&D bekannt. Sie könnenSprachen wie {(ab)n} beschreiben, aber nicht {anbn}.

Kontextfreie Grammatiken sind etwas mächtiger.Sie können Sprachen wie {anbn} und {anbmcn} beschreiben,aber nicht {anbncn} oder {anbmcndm}.

Programmierenin Haskell

StefanJanssen

Grammatikenund Parser

DSL

Klassen formaler Sprachen

Endliche Automaten sind aus A&D bekannt. Sie könnenSprachen wie {(ab)n} beschreiben, aber nicht {anbn}.

Kontextfreie Grammatiken sind etwas mächtiger.Sie können Sprachen wie {anbn} und {anbmcn} beschreiben,aber nicht {anbncn} oder {anbmcndm}.

Programmierenin Haskell

StefanJanssen

Grammatikenund Parser

DSL

Kontextfreie Grammatiken

Hier eine kleine Grammatik für Ausdrücke:

Terminalsymbole: 0 1 2 a b c d ) ( + *Nichtterminalsymbole: A T F Z B CAxiom A

RegelnA -> T | T + AT -> F | F * TF -> Z | B | ( A )Z -> 0 | 1 | 2B -> C | C BC -> a | b | c | d

Ausdrücke sind also"a+1*(abba+2)", "2*a+2*b", "2*(a+b)"

Programmierenin Haskell

StefanJanssen

Grammatikenund Parser

DSL

Programmbäume

Darstellung der Ausdrücke"a+1*(abba+2)", "2*a+2*b", "2*(a+b)"nach ihrer Zerlegung

1 Plus (Var "a")2 (Mul (Numb "1")3 (Plus (var "abba") (Numb "2")))4

5 Plus (Mul (Numb "2") (Var "a"))6 (Mul (Numb "2") (Var "b"))7

8 Mul (Numb "2")9 (Plus (Var "a")

10 (Var "b"))

Programmierenin Haskell

StefanJanssen

Grammatikenund Parser

DSL

Vorüberlegungen zum Parser

Anforderungen: Der Parser sollwohlgeformte Ausdrücke in Programmbäume übersetzenwenn es mehrere Parses gibt, dann für alle denProgrammbaum liefern ...... und ggf. gar keinen

Wir schreiben einen rekursiven Top-Down Parser:Für jedes Nichtterminalsymbol X

gibt es eine Parserfunktion pXdie Ableitungen aus X konstruiertund die entsprechenden Programmbäume zusammensetzt

Programmierenin Haskell

StefanJanssen

Grammatikenund Parser

DSL

Arbeitsweise der Parser

Eine Parserfunktion pX arbeitet wie folgtEingabe ist ein (restlicher) String wpX leitet Präfix u von w = uv aus X abdazu ruft sie sich selbst und andere Parser für die NTs derrechten Seite aufgibt Programmbaum P(X -> u) und Resteingabe vzurück ...... und das für alle Paare P(X -> u) und vWird kein Parse gefunden, wird eine leere Listezurückgegeben

Programmierenin Haskell

StefanJanssen

Grammatikenund Parser

DSL

Parser-Code

1 > type Parser a b = [a] -> [(b, [a])]2

3 > data Ptree = Plus Ptree Ptree |4 > Mul Ptree Ptree |5 > Var String |6 > Numb String deriving Show7

8 > parse xs = [t | (t ,[]) <- pA xs]pA wird als erster Parser aufgerufen, weil A das Axiom derGrammatik istEin Parse ist nur erfolgreich, wenn er die ganze Eingabe“verbraucht”.

Programmierenin Haskell

StefanJanssen

Grammatikenund Parser

DSL

Parser-Code

1 > pA xs = pT xs ++2 > [( Plus l r, v) | (l, o:u) <- pT xs ,3 > o == ’+’,4 > (r, v) <- pA u]5 > pT xs = pF xs ++6 > [( Mul l r, v) | (l, ’*’:u) <- pF xs ,7 > (r, v) <- pT u]8 > pF xs = pZ xs ++ pB xs ++9 > [(t, v) | (’(’:u) <- [xs],

10 > (t,’)’:v) <- pA u]

Programmierenin Haskell

StefanJanssen

Grammatikenund Parser

DSL

Parser-Code

1 > pZ xs = case xs of ’0’:v -> [( Numb "0", v)]2 > ’1’:v -> [( Numb "1", v)]3 > ’2’:v -> [( Numb "2", v)]4 > otherwise -> []5 > pB xs = [( Var (c:""), v) | (c, v) <- pC xs] ++6 > [( Var (c:cs), v) | (c, u) <- pC xs ,7 > (Var cs ,v) <- pB u]8 > pC xs = case xs of ’a’:v -> [(’a’, v)]9 > ’b’:v -> [(’b’, v)]

10 > ’c’:v -> [(’c’, v)]11 > ’d’:v -> [(’d’, v)]12 > otherwise -> []

Programmierenin Haskell

StefanJanssen

Grammatikenund Parser

DSL

Systematik

Bei der Parser-Konstruktion wiederholen sichLesen bestimmter Zeichen oder ZeichenkombinationenAufruf anderer Parser in der Reihenfolge der Nonterminalsauf den rechten Seiten(++) für die Zusammenführung von Resultaten ausAlternativenWeiterreichen von Teilergebnissen und Resteingabe an dennächsten ParserAnwendung eines Programmbaum-Konstruktors aufTeilergebnisse

Wenn wir dafür spezielle Operationen kreieren, erhalten wireine DSL für die schnelle und fehlersichere Implementierungvon Parsern

Programmierenin Haskell

StefanJanssen

Grammatikenund Parser

DSL

Vorfreude

Der Parser für die Grammatik

A -> T | T + AT -> F | F * TF -> Z | B | ( A )Z -> 0 | 1 | 2B -> C | C BC -> a | b | c | d

wird dann etwa so aussehen:1 > pA = pT ||| Plus <<< pT ~~~ ’+’ ~~~ pA2 > pT = pF ||| Mul <<< pF ~~~ ’*’ ~~~ pT3 > pF = pZ ||| pB ||| ’(’ ~~~ pA ~~~ ’)’4 > pZ = Numb <<< ’0’ ||| ’1’ ||| ’2’5 > pB = (:[]) <<< pC ||| (:) <<< pC ~~~ pB6 > pC = ’a ’||| ’b|||’c ’||| ’d’Achtung: Das ist noch nicht der endgültige Code

Programmierenin Haskell

StefanJanssen

Grammatikenund Parser

DSL

Vorfreude

Der Parser für die Grammatik

A -> T | T + AT -> F | F * TF -> Z | B | ( A )Z -> 0 | 1 | 2B -> C | C BC -> a | b | c | d

wird dann etwa so aussehen:1 > pA = pT ||| Plus <<< pT ~~~ ’+’ ~~~ pA2 > pT = pF ||| Mul <<< pF ~~~ ’*’ ~~~ pT3 > pF = pZ ||| pB ||| ’(’ ~~~ pA ~~~ ’)’4 > pZ = Numb <<< ’0’ ||| ’1’ ||| ’2’5 > pB = (:[]) <<< pC ||| (:) <<< pC ~~~ pB6 > pC = ’a ’||| ’b|||’c ’||| ’d’Achtung: Das ist noch nicht der endgültige Code

Programmierenin Haskell

StefanJanssen

Grammatikenund Parser

DSL

Vorbereitung

Was wird gebraucht?Definition der Parser-KombinatorenDefinition der terminalen ParserDefinition der Funktionen zum Baumaufbau, soweit nichtdirekt die Konstruktoren verwendbar sind

Programmierenin Haskell

StefanJanssen

Grammatikenund Parser

DSL

Zur Erinnerung

Typ eines Parsers war1 > type Parser a b = [a] -> [(b, [a])]Ein Parser

verarbeitet Liste vom Elementtyp a

berechnet Liste von Ergebnissen vom Typ[(Programmbaum, Resteingabe)]

NB: Die Eingabe muss also kein String sein ....

Programmierenin Haskell

StefanJanssen

Grammatikenund Parser

DSL

Parser für einzelne Zeichen

1 > cchar :: (Eq a) => a -> Parser a a-- vergleiche mit Definition von char weiter unten

2 > cchar c [] = []3 > cchar c (x:xs)4 > | x == c = [ (c, xs) ]5 > | otherwise = []

Parser cchar ’x’ erkennt nur den Buchstaben ’x’ am Anfangder Eingabe.

Später lernen wir einen allgemeineren Parser für beliebigeEingabesymbole kennen.

Programmierenin Haskell

StefanJanssen

Grammatikenund Parser

DSL

Parser-Kombinatoren

Kombinatoren nennt man Funktionen, deren Argumente undErgebnisse Funktionen sind.

Unsere Kombinatoren kombinieren Parser mit Parsern zu neuenParsern

1 > infix 8 <<< -- Programmbaum - Aufbau2 > infixl 7 ~~~ -- Verkettung von Parsern3 > infixr 6 ||| -- Zusammenfassen von Alternativen

Die Wahl der Prioritäten und der Assoziierungsrichtung wirdspäter wichtig ...

Programmierenin Haskell

StefanJanssen

Grammatikenund Parser

DSL

Parser-Kombinatoren

Kombinatoren für Funktionsanwendung, Verkettung undAlternative

1

2 > (<<<) :: (b -> c) -> Parser a b -> Parser a c3 > (<<<) f p inp = [(f x,rest )| (x,rest) <- p inp]4

5 > (~~~) :: Parser a (b->c)-> Parser a b-> Parser a c6 > (~~~) p q inp = [(x y,r2) | (x,rest) <- p inp ,7 > (y,r2) <- q rest ]8 > (|||) :: Parser a b -> Parser a b -> Parser a b9 > (|||) p q inp = p inp ++ q inp

Programmierenin Haskell

StefanJanssen

Grammatikenund Parser

DSL

Parser-Kombinatoren

Jeder Parser kann zum Axiom der Grammatik erklärt werdendurch den Kombinator axiom

1 > axiom :: [a] -> Parser a b -> [b]2 > axiom inp p = [ x | (x, []) <- p inp ]Das Axiom muss die ganze Eingabe ableiten, nicht nur einenPräfix davon (wie alle anderen Parser)

Programmierenin Haskell

StefanJanssen

Grammatikenund Parser

DSL

Grammatik in Kombinator-Schreibweise

Jetzt können wir den Parser wie eine Kopie der Regeln in derGrammatik schreiben:

Aus A -> T | T + A wird1 > pA = pT ||| plus <<< pT ~~~ cchar ’+’ ~~~ pA

aus F -> Z | B | ( A ) wird1 > pF = Numb <<< pZ ||| Var <<< pB |||2 > mid <<< cchar ’(’ ~~~ pA ~~~ cchar ’)’

Wir brauchen aber noch ein paar Bausteine

Programmierenin Haskell

StefanJanssen

Grammatikenund Parser

DSL

Programmbaumaufbau

Die Konstruktoren Plus und Mul erwarten nur zweiArgumente, der Parser liefert drei. Daher:

1 > pC = cchar ’a’ ||| cchar ’b’ ||| cchar ’c’2 > ||| cchar ’d’3 > plus x _ z = Plus x z4 > mul x _ z = Mul x z

Programmierenin Haskell

StefanJanssen

Grammatikenund Parser

DSL

Der komplette Parser

1 > parse_C :: String -> [Ptree]2 > parse_C inp = axiom inp pA where3 > pA = pT ||| plus <<< pT ~~~ cchar ’+’ ~~~ pA4 > pT = pF ||| mul <<< pF ~~~ cchar ’*’ ~~~ pT5 > pF = Numb <<< pZ ||| Var <<< pB |||6 > mid <<< cchar ’(’ ~~~ pA ~~~ cchar ’)’7 > pZ = (:[]) <<< (cchar ’0’ ||| cchar ’1’8 > ||| cchar ’2’)9 > pB = (:[]) <<< pC ||| (:) <<< pC ~~~ pB

10 > pC = cchar ’a’ ||| cchar ’b’ ||| cchar ’c’11 > ||| cchar ’d’12 > plus x _ z = Plus x z13 > mul x _ z = Mul x z14 > mid _ y _ = y

Programmierenin Haskell

StefanJanssen

Grammatikenund Parser

DSL

Vereinfachungen

Reale Parser werden nach der gleichen Methode gebaut, sindaber komplizierter

Zahlen mit beliebig vielen ZiffernOperatoren aus mehreren Zeichen“Whitespace” in der Eingabe

Es muss eine Ebene der lexikalischen Analyse vorgschaltetwerden.Dazu gibt es ein ausgefeilteres Beispiel im nächsten Abschnitt

Programmierenin Haskell

StefanJanssen

Grammatikenund Parser

DSL

DSLs – Anwendungssprachen

Domain Specific Language (DSL)vs. General-Purpose-Language, Libraries, FrameworksZiele: Unterstützung komplexer Anwendungen mitwiederkehrenden Grund-KonstruktionenBeispiele: TEX, LATEX, Lex, Yacc, awk . . .

FortgeschrittenesPaul Hudak. Modular Domain Specific Languages and Tools.Proceedings of the Fifth International Conference on SoftwareReuse, IEEE Computer Society, 1998. http://haskell.cs.yale.edu/wp-content/uploads/2011/01/DSEL-Reuse.pdf

Programmierenin Haskell

StefanJanssen

Grammatikenund Parser

DSL

DSLs – Anwendungssprachen

Domain Specific Language (DSL)vs. General-Purpose-Language, Libraries, FrameworksZiele: Unterstützung komplexer Anwendungen mitwiederkehrenden Grund-KonstruktionenBeispiele: TEX, LATEX, Lex, Yacc, awk . . .

FortgeschrittenesPaul Hudak. Modular Domain Specific Languages and Tools.Proceedings of the Fifth International Conference on SoftwareReuse, IEEE Computer Society, 1998. http://haskell.cs.yale.edu/wp-content/uploads/2011/01/DSEL-Reuse.pdf

Programmierenin Haskell

StefanJanssen

Grammatikenund Parser

DSL

Autonome versus eingebettete DSL

Autonome DSL: Eigenständige SpracheBeispiel aus Bielefeld: Bellman’s GAPGebiet: biologische Sequenzanalyse

eigener Compiler mit umfangreicher Optimierung

Embedded DSL: Sprach-Subset, Erweiterung oder Bibliothek inHost Sprache

Beispiel aus Bielefeld: Haskell ADP, ADPfusionGebiet: biologische Sequenzanalyse

kein eigener Compiler, keine spezifische Optimierung, abereinfache Implementierung

Strategie für neue DSL: Erst “embedded” entwickeln underproben, dann “ex-bedding”

Programmierenin Haskell

StefanJanssen

Grammatikenund Parser

DSL

Autonome versus eingebettete DSL

Autonome DSL: Eigenständige SpracheBeispiel aus Bielefeld: Bellman’s GAPGebiet: biologische Sequenzanalyse

eigener Compiler mit umfangreicher Optimierung

Embedded DSL: Sprach-Subset, Erweiterung oder Bibliothek inHost Sprache

Beispiel aus Bielefeld: Haskell ADP, ADPfusionGebiet: biologische Sequenzanalyse

kein eigener Compiler, keine spezifische Optimierung, abereinfache Implementierung

Strategie für neue DSL: Erst “embedded” entwickeln underproben, dann “ex-bedding”

Programmierenin Haskell

StefanJanssen

Grammatikenund Parser

DSL

Autonome versus eingebettete DSL

Autonome DSL: Eigenständige SpracheBeispiel aus Bielefeld: Bellman’s GAPGebiet: biologische Sequenzanalyse

eigener Compiler mit umfangreicher Optimierung

Embedded DSL: Sprach-Subset, Erweiterung oder Bibliothek inHost Sprache

Beispiel aus Bielefeld: Haskell ADP, ADPfusionGebiet: biologische Sequenzanalyse

kein eigener Compiler, keine spezifische Optimierung, abereinfache Implementierung

Strategie für neue DSL: Erst “embedded” entwickeln underproben, dann “ex-bedding”

Programmierenin Haskell

StefanJanssen

Grammatikenund Parser

DSL

Unser eDSL-Beispiel

Eine eDSL zur Parser-Konstruktion, eingebettet in Haskellallgemein: Parser-Combinators (sind unabhängig vongegebener Grammatik)Haskell ist flexibel genug und erlaubt durch die Definitionder Kombinatoren als inf-x-Operatoren eine “eingebettete”Spracherweiterung

Dazu noch ein nicht ganz so einfaches Beispiel: BibTeX-Format

Example1 @book{UBHD1745069 ,2 autho r={Bird , R i cha rd } ,3 t i t l e ={ I n t r o d u c t i o n to f u n c t i o n a l programming u s i n g H a s k e l l } ,4 pages ={433} ,5 p u b l i s h e r ={P r e n t i c e H a l l } ,6 yea r ={1998} ,7 i s b n ={978−0−13−484346−9},8 e d i t i o n ={2nd}9 }

Programmierenin Haskell

StefanJanssen

Grammatikenund Parser

DSL

Unser eDSL-Beispiel

Eine eDSL zur Parser-Konstruktion, eingebettet in Haskellallgemein: Parser-Combinators (sind unabhängig vongegebener Grammatik)Haskell ist flexibel genug und erlaubt durch die Definitionder Kombinatoren als inf-x-Operatoren eine “eingebettete”Spracherweiterung

Dazu noch ein nicht ganz so einfaches Beispiel: BibTeX-FormatExample

1 @book{UBHD1745069 ,2 autho r={Bird , R i cha rd } ,3 t i t l e ={ I n t r o d u c t i o n to f u n c t i o n a l programming u s i n g H a s k e l l } ,4 pages ={433} ,5 p u b l i s h e r ={P r e n t i c e H a l l } ,6 yea r ={1998} ,7 i s b n ={978−0−13−484346−9},8 e d i t i o n ={2nd}9 }

Programmierenin Haskell

StefanJanssen

Grammatikenund Parser

DSL

Grammatik für BibTex-Eintraege

record -> recHead recBody recEndrecHead -> ’@’ entype ’{’ aword ’,’recBody -> entriesrecEnd -> ’}’entype -> "book" | "article"entries -> entry | entry ’,’ entriesentry -> aword ’=’ rhsrhs -> ’{’ content ’}’ |

’"’ content ’"’ |aword

aword: beliebige Zeichenreiheohne die Trennzeichen { } @ = ,

Programmierenin Haskell

StefanJanssen

Grammatikenund Parser

DSL

Weitere Festlegungen

Die “lexikalische Ebene”:“Wort” ist beliebige Zeichenfolge ohne Trennzeichen ...... und ohne die “whitespace” Zeichen <neue Zeile> und<Blank>vor dem eigenlichen Parsen wird der Text in “tokens”(Worte oder Trennzeichen) zerlegt ...... und der Whitespace herausgefiltert

Diese Vorverarbeitung nennt man lexikalische Analyse

Programmierenin Haskell

StefanJanssen

Grammatikenund Parser

DSL

Zur Erinnerung

Typ eines Parsers war1 > type Parser a b = [a] -> [(b, [a])]Ein Parser

verarbeitet Liste vom Elementtyp a

berechnet Liste von Ergebnissen vom Typ[(Programmbaum, Resteingabe)]

NB: Die Eingabe muss also kein String sein ....

Programmierenin Haskell

StefanJanssen

Grammatikenund Parser

DSL

Parser-Kombinatoren

Wie zuvor ...Kombinatoren nennt man Funktionen, deren Argumente undErgebnisse Funktionen sind.

Unsere Kombinatoren kombinieren Parser mit Parsern zu neuenParsern

1 > infix 8 <<< -- Programmbaum - Aufbau2 > infixl 7 ~~~ -- Verkettung von Parsern3 > infixr 6 ||| -- Zusammenfassen von Alternativen

Die Wahl der Prioritäten und der Assoziierungsrichtung wirdspäter wichtig ...

Programmierenin Haskell

StefanJanssen

Grammatikenund Parser

DSL

Parser-Kombinatoren

Wie zuvor ...1

2 > (<<<) :: (b -> c) -> Parser a b -> Parser a c3 > (<<<) f p inp = [(f x,rest )| (x,rest) <-

p inp]4

5 > (~~~) :: Parser a (b->c)-> Parser a b-> Parser a c6 > (~~~) p q inp = [(x y,r2) | (x,rest) <- p inp ,7 > (y,r2) <- q rest ]8 > (|||) :: Parser a b -> Parser a b -> Parser a b9 > (|||) p q inp = p inp ++ q inp

Programmierenin Haskell

StefanJanssen

Grammatikenund Parser

DSL

Parser-Kombinatoren

Wie zuvor...

Jeder Parser kann zum Axiom der Grammatik erklärt werdendurch den Kombinator axiom

1 > axiom :: [a] -> Parser a b -> [b]2 > axiom inp p = [ x | (x, []) <- p inp ]Das Axiom muss die ganze Eingabe ableiten, nicht nur einenPräfix davon (wie alle anderen Parser)

Programmierenin Haskell

StefanJanssen

Grammatikenund Parser

DSL

Grammatik in Kombinator-Schreibweise

Jetzt können wir den Parser wie eine Kopie der Regeln in derGrammatik schreiben:

Aus entries -> entry | entry ’,’ entries wird1 > entries = s1 <<< entry |||2 > s2 <<< entry ~~~ char ’,’ ~~~ entries

aus entry -> aword ’=’ rhs wird1 > entry = e1 <<< aword ~~~ char ’=’ ~~~ rhs

Wir brauchen aber noch ein paar Bausteine

Programmierenin Haskell

StefanJanssen

Grammatikenund Parser

DSL

Parser für die lexikalischen tokens

1 > char :: (Eq a) => a -> Parser [a] a2 > char c [] = []3 > char c (x:xs)4 > | x == [c] = [ (c, xs) ]5 > | otherwise = []

Parser char ’c’ erkennt nur den Buchstaben ’c’ als "c" amAnfang der Eingabe

Programmierenin Haskell

StefanJanssen

Grammatikenund Parser

DSL

Parser für die lexikalischen tokens

1

2 > word :: Eq a => a -> Parser a a3 > word w [] = []4 > word w (x:xs)5 > | x == w = [ (w, xs) ]6 > | otherwise = []7

8 > aword :: Parser String String9 > aword [] = []

10 > aword (x:xs)11 > | x /= "{" && x/= "}" = [ (x, xs) ]12 > | otherwise = []

Parser aword akzeptiert ein beliebiges Wort (außer denKlammern),Parser word w nur das Wort w

Programmierenin Haskell

StefanJanssen

Grammatikenund Parser

DSL

Aufbau der Bäume für die Bib-Entries

1 > data BibRecord = BR {2 > bibType :: String ,3 > bibId :: String ,4 > bibEntries :: [ BibEntry ]5 > } deriving (Show)6

7 > data BibEntry = BE {8 > entryKey :: String ,9 > entryVals :: [ String ]

10 > } deriving (Show)

Programmierenin Haskell

StefanJanssen

Grammatikenund Parser

DSL

Lexikalische Analyse

1 > lexer :: String -> [ String ]2 > lexer inp = filter isWS $ groupBy g inp where3 > g a b = and $ map (\c -> a/=c && b/=c) delims4 > delims = " {},@,="5 > isWS w = not $ and $6 > map (\c -> c==’ ’ || c==’\n’) w

Vorgruppieren mittels groupBy,Herausfiltern des Whitespace mit isWS

Programmierenin Haskell

StefanJanssen

Grammatikenund Parser

DSL

Parser-Code

Die Grammatik in Kombinator-Schreibweise

> parser inp = axiom inp record> where> record = a1 <<< recHead ~~~ recBody ~~~ recEnd> recHead = h1 <<< char ’@’ ~~~ entype ~~~ char ’{’> ~~~ aword ~~~ char ’,’> recBody = entries> recEnd = char ’}’> entype = word "book" |||> word "article"> entries = s1 <<< entry |||

s2 <<< entry ~~~ char ’,’ ~~~ entries

Programmierenin Haskell

StefanJanssen

Grammatikenund Parser

DSL

Parser-Code

Die Grammatik in Kombinator-Schreibweise

> entry = e1 <<< aword ~~~ char ’=’ ~~~ rhs> rhs = r1 <<< char ’{’ ~~~ content ~~~ char ’}’ |||

r1 <<< char ’"’ ~~~ content ~~~ char ’"’ |||r2 <<< aword

> content = c1 <<< char ’{’ ~~~ content ~~~ char ’}’ |||c2 <<< aword ~~~ content |||c3 <<< aword

Die Funktionen e1, r1, ... sind für den Baumaufbauzuständig

Programmierenin Haskell

StefanJanssen

Grammatikenund Parser

DSL

Baumaufbau

Der Baumaufbau benutzt die Konstruktoren BR und BE, aberlässt irrelvante tokens weg:

1 > a1 (typ , iden) ents _ = BR typ iden ents2 > h1 _ typ _ iden _ = (typ , iden)3 > s1 e = [e]4 > s2 e _ es = e:es5 > e1 k _ xs = BE k xs6 > r1 _ x _ = x7 > r2 x = [x]8 > c1 _ x _ = ("{":x) ++ ["}"]9 > c2 a b = a:b

10 > c3 a = [a]

Programmierenin Haskell

StefanJanssen

Grammatikenund Parser

DSL

Aufruf des Parsers

Lexer und Parser werden hintereinandergeschaltet im Aufrufparser $ lexer xs

Wir sind fertig!!Die Grammatik in Kombinator-Schreibweise IST der Parser ...!Siehe Anwendungsbeispiel im Code von parse.lhs

FortgeschrittenesGraham Hutton. Higher-order functions for parsing. Journal ofFunctional Programming, Volume 2 Issue 3, 323–343, 1992.http://www.cs.nott.ac.uk/~gmh/parsing.pdf

Programmierenin Haskell

StefanJanssen

Grammatikenund Parser

DSL

Aufruf des Parsers

Lexer und Parser werden hintereinandergeschaltet im Aufrufparser $ lexer xs

Wir sind fertig!!Die Grammatik in Kombinator-Schreibweise IST der Parser ...!Siehe Anwendungsbeispiel im Code von parse.lhs

FortgeschrittenesGraham Hutton. Higher-order functions for parsing. Journal ofFunctional Programming, Volume 2 Issue 3, 323–343, 1992.http://www.cs.nott.ac.uk/~gmh/parsing.pdf

Programmierenin Haskell

StefanJanssen

Grammatikenund Parser

DSL

Traum und Wahrheit

Wir lesen den Parser für

A → B C D | F ,

also

pA = f <<< pA ~~~ pB ~~~ pC ||| g <<< pF

als

pA = (f <<< (pA ~~~ pB ~~~ pC)) ||| (g <<< pF)

die Warhheit ist dank der Prioritäten aber

pA = ((((f <<< pA) ~~~ pB) ~~~ pC)) ||| (g <<< pF)

Programmierenin Haskell

StefanJanssen

Grammatikenund Parser

DSL

Nutzung der Parser-eDSL

Kombinatorparser sindeinfach zu entwickelnfehlersicher: die Grammatik IST der Parsereinfach zu anzupassen, wenn die Sprache sich ändert

Grenzen:nicht so effizient wie generierte Parser (z.B. durch Yacc,Bison)bei mehrdeutigen Sprachen müssen sie mit dynamischerProgrammierung verbunden werden zur Bewertung undAuswahl der Lösungen

Ende der Vorlesung “Programmieren in Haskell”

Programmierenin Haskell

StefanJanssen

Grammatikenund Parser

DSL

Nutzung der Parser-eDSL

Kombinatorparser sindeinfach zu entwickelnfehlersicher: die Grammatik IST der Parsereinfach zu anzupassen, wenn die Sprache sich ändert

Grenzen:nicht so effizient wie generierte Parser (z.B. durch Yacc,Bison)bei mehrdeutigen Sprachen müssen sie mit dynamischerProgrammierung verbunden werden zur Bewertung undAuswahl der Lösungen

Ende der Vorlesung “Programmieren in Haskell”

Programmierenin Haskell

StefanJanssen

Grammatikenund Parser

DSL

Wie geht es weiter

Programmiersprachen und Algorithmik⇒ Objektorientierte Programmierung in Java (2. Semester,

vll. OOP mit Hashing, Heaps, RB-Trees)⇒ Sequenzanalyse (3. Semester)⇒ Algorithmen der Informatik (4. Semester,

Graph-Algorithmen, . . . )Endliche Automaten, Registermaschine, Komplexität

⇒ Grundlagen Theoretischer Informatik (3. Semester,Formale Sprachen, Berechenbarkeit, Komplexität . . . )

von Neumann ArchitekturRechnerarchitektur (3. Semester)

Dynamic Programmingdiverse DP-Algorithmen in verschiedenen Veranstaltungen

⇒ Algebraic Dynamic Programming (im Master)

Programmierenin Haskell

StefanJanssen

Grammatikenund Parser

DSL

The EndViel Erfolg im Studium ...

... und auch etwas Spaß!

Letzte Sitzung: Fragestunde zur Prüfungsvorbereitung