Die Parsec Library Funktionale Programmierung Die Parsec Library D. Rösner Institut für Wissens- und Sprachverarbeitung Fakultät für Informatik Otto-von-Guericke Universität Magdeburg c Sommer 2015, 19. Juni 2015, 2011-15 D.Rösner D. Rösner FP 2015 . . . 1 Die Parsec Library Gliederung 1 Die Parsec Library Intro Kombinatoren Erweiterungen Arithm. Ausdrücke Permutationsparser D. Rösner FP 2015 . . . 2 Die Parsec Library Intro Kombinatoren Erweiterungen Parsec – Eine Parser-Bibliothek Grundideen (vgl. [OGS09], Ch. 16): Parser sowohl für lexikalische Analyse wie für eigentliches Parsing Kombinatoren für Parser als Funktionen höherer Ordnung Polymorphismus s.a. [LM01], [Lei01] D. Rösner FP 2015 . . . 3 Die Parsec Library Intro Kombinatoren Erweiterungen Parsec – Eine Parser-Bibliothek einführendes Beispiel: Parser für CSV-Dateien diese bestehen aus endlich vielen Zeilen jede Zeile enthält Zellen, die durch Kommata getrennt zunächst: Implementierung mit Grundbausteinen dann: wesentlich kompakterer Code durch Nutzung geeigneter Kombinatoren [OGS09], Ch. 16 D. Rösner FP 2015 . . . 5 Die Parsec Library Intro Kombinatoren Erweiterungen CSV-Parser direkte Implementierung Definitionen csvFile, line ([OGS09], Ch. 16) import Text.ParserCombinators.Parsec csvFile :: GenParser Char st [[String]] csvFile = do result <- many line eof return result line :: GenParser Char st [String] line = do result <- cells eol return result ... D. Rösner FP 2015 . . . 6 Die Parsec Library Intro Kombinatoren Erweiterungen CSV-Parser direkte Implementierung Definitionen Zellen ... cells :: GenParser Char st [String] cells = do first <- cellContent next <- remainingCells return (first:next) remainingCells :: GenParser Char st [String] remainingCells = (char ’,’ >> cells) <|> (return []) ... [OGS09], Ch. 16 D. Rösner FP 2015 . . . 7 Die Parsec Library Intro Kombinatoren Erweiterungen CSV-Parser direkte Implementierung Definitionen Zellinhalt, Zeilenende ... cellContent :: GenParser Char st String cellContent = many (noneOf ",\n") eol :: GenParser Char st Char eol = char ’\n’ [OGS09], Ch. 16 D. Rösner FP 2015 . . . 8 Die Parsec Library Intro Kombinatoren Erweiterungen CSV-Parser direkte Implementierung Aufruf des Parsers via parse parseCSV :: String -> Either ParseError [[String]] parseCSV input = parse csvFile "(unknown)" input [OGS09], Ch. 16 D. Rösner FP 2015 . . . 9 Die Parsec Library Intro Kombinatoren Erweiterungen CSV-Parser direkte Implementierung verwendete Bausteine: monadisch: do, >> many char <|> noneOf [OGS09], Ch. 16; s.a. [LM01], [Lei01] D. Rösner FP 2015 . . . 10 Die Parsec Library Intro Kombinatoren Erweiterungen CSV-Parser direkte Implementierung Beispiele für Verwendung *Main> parseCSV "" Right [] *Main> parseCSV "one,line,with,cells\n" Right [["one","line","with","cells"]] *Main> parseCSV "one,line,with,cells\nand another, one\n" Right [["one","line","with","cells"],["and another"," one"]] *Main> parseCSV "one,line,with,cells,but,no eol" Left "(unknown)" (line 1, column 31): unexpected end of input expecting "," or "\n" [OGS09], Ch. 16 D. Rösner FP 2015 . . . 11 Die Parsec Library Intro Kombinatoren Erweiterungen Parsec – Eine Parser-Bibliothek mit zwei Kombinatoren wesentliche Vereinfachung des Code sepBy ... nimmt einen Parser für Inhalt und einen Parser für einen Separator, wendet diese – solange wie möglich, d.h. bis einem Inhalt kein Separator mehr folgt – nacheinander jeweils im Wechsel an und gibt eine Liste aller geparsten Inhalte als Gesamtergebnis zurück [OGS09], Ch. 16 D. Rösner FP 2015 . . . 13 Die Parsec Library Intro Kombinatoren Erweiterungen Parsec – Eine Parser-Bibliothek zweiter Kombinator arbeitet ähnlich endBy ... nimmt ebenfalls einen Parser für Inhalt und einen Parser für einen Separator, wendet diese – solange wie möglich, d.h. bis einem (abschließenden) Separator kein Inhalt mehr folgt – nacheinander jeweils im Wechsel an und gibt ebenfalls eine Liste aller geparsten Inhalte als Gesamtergebnis zurück [OGS09], Ch. 16 D. Rösner FP 2015 . . . 14 Die Parsec Library Intro Kombinatoren Erweiterungen Parsec – Eine Parser-Bibliothek Beispiele für Verwendung *Main> parse (sepBy (many (oneOf [’a’ .. ’z’])) (char ’;’)) "(test)" "first;sec;third" Right ["first","sec","third"] *Main> parse (endBy (many (oneOf [’a’ .. ’z’])) (char ’;’)) "(test)" "first;sec;third;last;" Right ["first","sec","third","last"] [OGS09], Ch. 16 D. Rösner FP 2015 . . . 15 Die Parsec Library Intro Kombinatoren Erweiterungen Parsec – Eine Parser-Bibliothek Beispiele für Verwendung *Main> parse (sepBy (many (oneOf [’a’ .. ’z’])) (char ’;’)) "(test)" "first;sec;third;last;" Right ["first","sec","third","last",""] *Main> parse (endBy (many (oneOf [’a’ .. ’z’])) (char ’;’)) "(test)" "first;sec;third;last" Left "(test)" (line 1, column 21): unexpected end of input expecting ";" [OGS09], Ch. 16 D. Rösner FP 2015 . . . 16 Die Parsec Library Intro Kombinatoren Erweiterungen Parsec – Eine Parser-Bibliothek Reimplementation des CSV-Parsers mit diesen Kombinatoren import Text.ParserCombinators.Parsec csvFile = endBy line eol line = sepBy cell (char ’,’) cell = many (noneOf ",\n") eol = char ’\n’ parseCSV :: String -> Either ParseError [[String]] parseCSV input = parse csvFile "(unknown)" input D. Rösner FP 2015 . . . 17 Die Parsec Library Intro Kombinatoren Erweiterungen Parsec – Eine Parser-Bibliothek vordefinierte Funktionen: Anwenden und Testen von Parsern runParser . . . parse . . . parsefromFile . . . parseTest . . . s.a. [LM01], [Lei01] D. Rösner FP 2015 . . . 18 Die Parsec Library Intro Kombinatoren Erweiterungen Parsec – Eine Parser-Bibliothek Kombinator für Alternative: <|> Semantik von (p <|> q): zunächst wird p angewendet im Erfolgsfall wird der von p gelieferte Wert zurückgegeben nur wenn p fehlschlägt ohne Eingabe konsumiert zu haben, wird der Parser q versucht die Vorausschau (look ahead) ist also 1 der Verzicht auf Backtracking erlaubt effiziente Implementation s.a. [LM01], [Lei01] D. Rösner FP 2015 . . . 19 Die Parsec Library Intro Kombinatoren Erweiterungen Parsec – Eine Parser-Bibliothek Kombinator für Alternative: <|> das spezielle Auswahlverhalten von <|> wird auch als prädiktiv bezeichnet monadische Betrachtung: <|> entspricht mplus aus MonadPlus s.a. [LM01], [Lei01] D. Rösner FP 2015 . . . 20 Die Parsec Library Intro Kombinatoren Erweiterungen Parsec – Eine Parser-Bibliothek Kombinator für Alternative: <|> Frage: Was ergibt *Main> parseTest (string "(a)" <|> string "(b)") "(a)" und was ergibt *Main> parseTest (string "(a)" <|> string "(b)") "(b)" s.a. [LM01], [Lei01] D. Rösner FP 2015 . . . 21 Die Parsec Library Intro Kombinatoren Erweiterungen Parsec – Eine Parser-Bibliothek Kombinator für Alternative: <|> vorgeschlagenes Vorgehen: Links-Faktorisierung (left-factoring) der Grammatik d.h. Verschmelzen gemeinsamer Präfixe Variante 1: *Main> parseTest (do{char ’(’; char ’a’ <|> char ’b’; char ’)’}) "(a)" ’)’ *Main> parseTest (do{char ’(’; char ’a’ <|> char ’b’; char ’)’}) "(b)" ’)’ s.a. [LM01], [Lei01] D. Rösner FP 2015 . . . 22 Die Parsec Library Intro Kombinatoren Erweiterungen Parsec – Eine Parser-Bibliothek Kombinator für Alternative: <|> Links-Faktorisierung (left-factoring) der Grammatik Variante 2: *Main> parseTest (do{char ’(’; ch <- char ’a’ <|> char ’b’ ;char ’)’; return ("(" ++ ch:")")}) "(b)" "(b)" *Main> parseTest (do{char ’(’; ch <- char ’a’ <|> char ’b’ ; char ’)’; return ("(" ++ ch:")")}) "(a)" "(a)" s.a. [LM01], [Lei01] D. Rösner FP 2015 . . . 23 Die Parsec Library Intro Kombinatoren Erweiterungen Parsec – Eine Parser-Bibliothek Beispiel: Einbringen von Semantik hier: Bestimmen der maximalen Einbettung korrespondierender Klammern import Text.ParserCombinators.Parsec nesting :: Parser Int nesting = do{ char ’(’ ; n <- nesting ; char ’)’ ; m <- nesting ; return (max (n+1) m) } <|> return 0 s.a. [LM01], [Lei01] D. Rösner FP 2015 . . . 24 Die Parsec Library Intro Kombinatoren Erweiterungen Parsec – Eine Parser-Bibliothek Beispiel: Einbringen von Semantik Verwendung von nesting *Main> parseTest nesting "(())()" 2 *Main> parseTest nesting "(())((()))" 3 *Main> parseTest nesting "(())((())" parse error at (line 1, column 10): unexpected end of input expecting "(" or ")" s.a. [LM01], [Lei01] D. Rösner FP 2015 . . . 25 Die Parsec Library Intro Kombinatoren Erweiterungen Parsec – Eine Parser-Bibliothek try . . . alternativer Kombinator für Alternativen (try p) verhält sich zunächst wie p bei Fehlschlag gibt (try p) vor, keine Eingabe konsumiert zu haben damit steht - in Kombination mit <|> - unendliche Vorausschau (infinite look-ahead) zur Verfügung D. Rösner FP 2015 . . . 26 Die Parsec Library Intro Kombinatoren Erweiterungen Parsec – Eine Parser-Bibliothek try . . . alternativer Kombinator für Alternativen Damit alternative Lösung: *Main> parseTest (try (string "(a)") <|> string "(b)") "(a)" "(a)" *Main> parseTest (try (string "(a)") <|> string "(b)") "(b)" "(b)" *Main> parseTest (try (string "(a)") <|> string "(b)") "(c)" parse error at (line 1, column 1): unexpected "c" expecting "(b)" Variante 2: do{try (string "(a"); char ’)’; return "(a)"} <|> string "(b)" Warum ist diese ’an even better version’ ? s.a. [LM01], [Lei01] D. Rösner FP 2015 . . . 27 Die Parsec Library Intro Kombinatoren Erweiterungen Parsec – Eine Parser-Bibliothek Kombinator <?> . . . für Fehlermeldungen Parser p <?> msg verhält sich wie Parser p wenn p fehlschlägt ohne Eingabe zu konsumieren, dann wird die Fehlermeldung aus p durch msg ersetzt ’. . . This is normally used at the end of a set alternatives where we want to return an error message in terms of a higher level construct rather than returning all possible characters.. . . ’ ([Lei01], p. 27) D. Rösner FP 2015 . . . 28 Die Parsec Library Intro Kombinatoren Erweiterungen Parsec – Eine Parser-Bibliothek Parsec wird mit einigen Erweiterungen angeboten Unterstützung für das Definieren von Parsern für arithmetische Ausdrücke Unterstützung für Parser für sog. Permutationsphrasen s.a. [Lei01] D. Rösner FP 2015 . . . 30 Die Parsec Library Intro Kombinatoren Erweiterungen Parsec – Eine Parser-Bibliothek Modul Text.ParserCombinators.Parsec.Expr Funktion buildExpressionParser verknüpft die Informationen über Operatoren und ihre Präzedenz und Assoziativität aus einer Tabelle mit einem Parser für Terme s.a. [Lei01] D. Rösner FP 2015 . . . 31 Die Parsec Library Intro Kombinatoren Erweiterungen Parsec – Eine Parser-Bibliothek Beispiel: neben arithmetischen Operatoren auch ein postfix zu verwendender Inkrementationsoperator ++ expr = buildExpressionParser table term <?> "expression" term = parens expr <|> natural <?> "simple expression" s.a. [Lei01] D. Rösner FP 2015 . . . 32 Die Parsec Library Intro Kombinatoren Erweiterungen Parsec – Eine Parser-Bibliothek Beispiel: neben arithmetischen Operatoren auch ein postfix zu verwendender Inkrementationsoperator ++ Operatorentabelle table :: OperatorTable Char () Integer table = [ , , , ] [prefix "-" negate, prefix "+" id ] [postfix "++" (+1)] [binary "*" (*) AssocLeft, binary "/" (div) AssocLeft ] [binary "+" (+) AssocLeft, binary "-" (-) AssocLeft ] binary name fun assoc = Infix (do{ reservedOp name; return fun }) assoc prefix name fun = Prefix (do{ reservedOp name; return fun }) postfix name fun = Postfix (do{ reservedOp name; return fun }) s.a. [Lei01] D. Rösner FP 2015 . . . 33 Die Parsec Library Intro Kombinatoren Erweiterungen Parsec – Eine Parser-Bibliothek Beispiele für Anwendung dieses Parsers: *Main> parseTest expr "1+2*3" 7 *Main> parseTest expr "-1+2*3" 5 *Main> parseTest expr "-(1+2*3)" -7 *Main> parseTest expr "+(1+2*3)++" 8 *Main> parseTest expr "-2++" -1 *Main> parseTest expr "-(2++)" -3 *Main> parseTest expr "-(2++" parse error at (line 1, column 6): unexpected end of input expecting end of "++", operator or ")" *Main> parseTest expr "10/4++" 2 *Main> parseTest expr "(10/4)++" 3 s.a. [Lei01] D. Rösner FP 2015 . . . 34 Die Parsec Library Intro Kombinatoren Erweiterungen Parsec – Eine Parser-Bibliothek Permutationsphrasen . . . A permutation phrase is sequence of elements of possibly different types in which each element occurs at most once and the order is irrelevant. ([Lei01], p.21) mögliche Anwendungen: Parser für XML-Attribute Parser für Records im Haskell-Stil s.a. [Lei01], [LM01] D. Rösner FP 2015 . . . 35 Die Parsec Library Intro Kombinatoren Erweiterungen Parsec – Eine Parser-Bibliothek Permutationsphrasen . . . Modul Text.ParserCombinators.Parsec.Perm Permutationsparser werden zunächst mit speziellen Operatoren – z.B. <$$>, <$?> <||>, <|?> – aufgebaut und dann mit der Funktion permute in normale Parser transformiert falls optionale Elemente verwendet werden, muß zusätzlich zum Parser des Elements ein Default-Wert angegeben werden, der in einem solchen Fall zurückgegeben wird s.a. [Lei01], pp. 46 D. Rösner FP 2015 . . . 36 Die Parsec Library Intro Kombinatoren Erweiterungen Parsec – Eine Parser-Bibliothek Beispiel: geparst werden soll eine Permutation von optionaler String aus as das Zeichen b und optionales Zeichen c Definition: test = permute (tuple <$?> ("",many1 (char ’a’)) <||> char ’b’ <|?> (’ ’,char ’c’)) where tuple a b c = (a,b,c) Rückgabe ist Tripel der geparsten bzw. Default-Werte s.a. [Lei01] D. Rösner FP 2015 . . . 37 Die Parsec Library Intro Kombinatoren Erweiterungen Parsec – Eine Parser-Bibliothek Beispiele für Anwendung dieses Parsers: *Main> parseTest test "aaabc" ("aaa",’b’,’c’) *Main> parseTest test "bc" ("",’b’,’c’) *Main> parseTest test "baaac" ("aaa",’b’,’c’) *Main> parseTest test "aaac" parse error at (line 1, column 5): unexpected end of input expecting "b" *Main> parseTest test "aaacb" ("aaa",’b’,’c’) *Main> parseTest test "aaab" ("aaa",’b’,’ ’) *Main> parseTest test "b" ("",’b’,’ ’) s.a. [Lei01] D. Rösner FP 2015 . . . 38 Die Parsec Library Intro Kombinatoren Erweiterungen Literatur: I Paul Hudak. The Haskell School of Expression – Learning Functional Programming through Multimedia. Cambridge University Press, Cambridge, UK, 2000. ISBN 0-521-64338-4. Daan Leijen. Parsec, a fast combinator parser. Technical report, Department of Computer Science, Universiteit Utrecht, 2001. D. Rösner FP 2015 . . . 39 Die Parsec Library Intro Kombinatoren Erweiterungen Literatur: II Daan Leijen and Erik Meijer. Parsec: Direct style monadic parser combinators for the real world. Technical Report UU-CS-2001-27, Department of Computer Science, Universiteit Utrecht, 2001. Bryan O’Sullivan, John Goerzen, and Don Stewart. Real World Haskell. O’Reilly Media, Sebastopol, CA 95472, 2009. ISBN 978-0-596-51498-3; online available at http://book.realworldhaskell.org/. D. Rösner FP 2015 . . . 40 Die Parsec Library Intro Kombinatoren Erweiterungen Literatur: III Simon Thompson. Haskell - The Craft of Functional Programming. Addison Wesley Longman Ltd., Essex, 1999. 2nd edition, ISBN 0-201-34275-8; Accompanying Web site: http://www.cs.ukc.ac.uk/people/staff/sjt/craft2e. D. Rösner FP 2015 . . . 41