Parser in Haskell Funktionale Programmierung Parser D. Rösner Institut für Wissens- und Sprachverarbeitung Fakultät für Informatik Otto-von-Guericke Universität Magdeburg c Sommer 2013, 6. Juni 2013, 2011-13 D.Rösner D. Rösner FP 2013 . . . 1 Parser in Haskell Gliederung 1 Parser in Haskell . . . als Funktionen . . . als Monaden D. Rösner FP 2013 . . . 2 Parser in Haskell . . . als Funktionen . . . als Monaden Eine Bibliothek von Bausteinen für Parser Grundidee (vgl. [Thompson 17.5]): Parser als Funktionen Kombinatoren für Parser als Funktionen höherer Ordnung Polymorphismus D. Rösner FP 2013 . . . 4 Parser in Haskell . . . als Funktionen . . . als Monaden Eine Bibliothek von Bausteinen für Parser token . . . ein Parser, der in der Eingabeliste ein spezielles erstes Element – ein Token – erwartet und im Erfolgsfall dieses Token ’konsumiert’ >token :: Eq a => a -> Parse a a >token t (x:xs) > | t==x = [(t,xs)] > | otherwise = [] >token t [] = [] D. Rösner FP 2013 . . . 5 Parser in Haskell . . . als Funktionen . . . als Monaden Eine Bibliothek von Bausteinen für Parser spot . . . Verallgemeinerung von token; ein Parser, der ein erstes Element in der Eingabeliste mit einer speziellen Eigenschaft ’konsumiert’ >spot :: (a -> Bool) -> Parse a a >spot p (x:xs) > | p x = [(x,xs)] > | otherwise = [] >spot p [] = [] D. Rösner FP 2013 . . . 6 Parser in Haskell . . . als Funktionen . . . als Monaden Eine Bibliothek von Bausteinen für Parser die beiden elementaren Parser none bzw. succeed sind Sonderfälle, die beide nichts konsumieren none . . . schlägt dabei immer fehl >none :: Parse a b >none inp = [] succeed . . . ist immer erfolgreich mit einem als Parameter übergebenen Rückgabewert >succeed :: b -> Parse a b >succeed val inp = [(val,inp)] D. Rösner FP 2013 . . . 7 Parser in Haskell . . . als Funktionen . . . als Monaden Bibliothek für Parser: Kombinatoren Alternative Parser lassen sich mit alt zu einem Parser vereinen, der die Ergebnisse der Alternativen in einer Ergebnisliste vereint >alt :: Parse a b -> Parse a b -> Parse a b > >alt p1 p2 inp = p1 inp ++ p2 inp D. Rösner FP 2013 . . . 8 Parser in Haskell . . . als Funktionen . . . als Monaden Bibliothek für Parser: Kombinatoren Damit Parser so aneinandergefügt werden können, dass der zweite mit dem Teil der ursprünglichen Eingabe weiterarbeitet, der beim ersten Parser übrigblieb, definieren wir den Operator >*>: >infixr 5 >*> >(>*>) :: Parse a b -> Parse a c -> Parse a (b,c) >(>*>) p1 p2 inp > = [((y,z),rem2)|(y,rem1) <- p1 inp, (z,rem2) <- p2 rem1] D. Rösner FP 2013 . . . 9 Parser in Haskell . . . als Funktionen . . . als Monaden Bibliothek für Parser: Kombinatoren build erlaubt, einen Parser vom Typ Parse a c mit Ergebnistyp c dadurch zu ’bauen’, dass auf das Ergebnis (vom Typ b) eines Parser vom Typ Parse a b eine Funktion vom Typ b -> c angewendet wird >build :: Parse a b -> (b -> c) -> Parse a c >build p f inp = [(f x,rem)|(x,rem) <- p inp] D. Rösner FP 2013 . . . 10 Parser in Haskell . . . als Funktionen . . . als Monaden Bibliothek für Parser: Kombinatoren list erlaubt es, aus einem Parser für ein bestimmtes Objekt einen Parser zu kombinieren, der Listen solcher Objekte erkennen kann >list :: Parse a b -> Parse a [b] >list p = (succeed []) > ‘alt‘ > ((p >*> list p) ‘build‘ (uncurry (:))) auch der Spezialfall einer leeren Liste ist zugelassen. Diese wird vom Parser (succeed []) verarbeitet. D. Rösner FP 2013 . . . 11 Parser in Haskell . . . als Funktionen . . . als Monaden Monadische Parser Quelle: A HASKELL LIBRARY OF MONADIC PARSER COMBINATORS 17th April 1997 Graham Hutton University of Nottingham Erik Meijer University of Utrecht definiert wird eine Sammlung monadischer Kombinatoren für Parser genutzt für zeichenweise arbeitende Parser Verallgemeinerung möglich D. Rösner FP 2013 . . . 13 Parser in Haskell . . . als Funktionen . . . als Monaden Monadische Parser der Typ: newtype Parser a = Parser (String -> [(a,String)]) die Monade: instance Monad Parser where return a = Parser (\cs -> [(a,cs)]) p >>= f = Parser (\cs -> concat [parse (f a) cs’ | (a,cs’) <- parse p cs]) D. Rösner FP 2013 . . . 14 Parser in Haskell . . . als Funktionen . . . als Monaden Monadische Parser zusätzlich Parser als Instanz von MonadPlus: instance MonadPlus Parser where mzero = Parser (\cs -> []) p ‘mplus‘ q = Parser (\cs -> parse p cs ++ parse q cs) mzero . . . Parser, der immer fehlschlägt ‘mplus‘ . . . vereinige die Ergebnisse alternativer Parser D. Rösner FP 2013 . . . 15 Parser in Haskell . . . als Funktionen . . . als Monaden Monadische Parser der zugehörige Dekonstruktor parse :: Parser a -> String -> [(a,String)] parse (Parser p) = p D. Rösner FP 2013 . . . 16 Parser in Haskell . . . als Funktionen . . . als Monaden Monadische Parser item . . . akzeptiert das erste Zeichen eines nichtleeren String item :: Parser Char item = Parser (\cs -> case cs of "" -> [] (c:cs) -> [(c,cs)]) D. Rösner FP 2013 . . . 17 Parser in Haskell . . . als Funktionen . . . als Monaden Monadische Parser sat . . . akzeptiert das erste Zeichen eines nichtleeren String, wenn dieses einer Bedingung p genügt sat :: (Char -> Bool) -> Parser Char sat p = do c <- item if p c then return c else mzero D. Rösner FP 2013 . . . 18 Parser in Haskell . . . als Funktionen . . . als Monaden Monadische Parser: Beispiele char c . . . akzeptiert genau das Zeichen c char :: Char -> Parser Char char c = sat (c ==) digit . . . akzeptiert eine Ziffer und gibt ihren Wert als Integer zurück digit :: Parser Int digit = do {c <- sat isDigit; return (ord c - ord ’0’)} D. Rösner FP 2013 . . . 19 Parser in Haskell . . . als Funktionen . . . als Monaden Monadische Parser string . . . nimmt einen String und liefert einen Parser, der genau diesen akzeptiert string :: String -> Parser String string "" = return "" string (c:cs) = do {char c; string cs; return (c:cs)} D. Rösner FP 2013 . . . 20 Parser in Haskell . . . als Funktionen . . . als Monaden Monadische Parser: Kombinatoren (+++) . . . zwei Parser werden so kombiniert, dass die Kombination fehlschlägt, wenn beide fehlschlagen und andernfalls das erste Resultat zurückgegeben wird (+++) :: Parser a -> Parser a -> Parser a p +++ q = Parser (\cs -> case parse (p ‘mplus‘ q) cs of [] -> [] (x:xs) -> [x]) D. Rösner FP 2013 . . . 21 Parser in Haskell . . . als Funktionen . . . als Monaden Monadische Parser: Kombinatoren force . . . force :: Parser a -> Parser a force p = Parser (\cs -> let xs = parse p cs in (fst (head xs), snd (head xs)) : tail xs) D. Rösner FP 2013 . . . 22 Parser in Haskell . . . als Funktionen . . . als Monaden Monadische Parser: Kombinatoren many p . . . Parser, der null oder mehrere erfolgreiche Anwendungen des Parsers p akzeptiert; zurückgegeben wird der längste erfolgreiche Match many :: Parser a -> Parser [a] many p = force (many1 p +++ return []) many1 p . . . Parser, der ein oder mehrere erfolgreiche Anwendungen des Parsers p akzeptiert; zurückgegeben wird der längste erfolgreiche Match many1 :: Parser a -> Parser [a] many1 p = do {a <- p; as <- many p; return (a:as)} D. Rösner FP 2013 . . . 23 Parser in Haskell . . . als Funktionen . . . als Monaden Monadische Parser: Kombinatoren sepby p sep . . . kombiniert Parser p und Parser sep so, dass ein Parser entsteht, der null oder mehrere erfolgreiche Anwendungen des Parser p akzeptiert, zwischen denen jeweils (als Separator) eine erfolgreiche Anwendung des Parser sep liegt; Rückgabe ist die Liste der Ergebnisse der Anwendungen von p ... D. Rösner FP 2013 . . . 24 Parser in Haskell . . . als Funktionen . . . als Monaden Monadische Parser: Kombinatoren sepby p sep . . . sepby :: Parser a -> Parser b -> Parser [a] p ‘sepby‘ sep = (p ‘sepby1‘ sep) +++ return [] sepby1 :: Parser a -> Parser b -> Parser [a] p ‘sepby1‘ sep = do a <- p as <- many (do {sep; p}) return (a:as) D. Rösner FP 2013 . . . 25 Parser in Haskell . . . als Funktionen . . . als Monaden Monadische Parser: Kombinatoren chainl p op a . . . kombiniert Parser p und Parser op; dabei liefern Anwendungen von op Funktionen, die Resultate in linksassoziativer Weise bilden aus den Ergebnissen von p vor und nach der Anwendung von op ; a ist der Rückgabewert, falls Parser fehlschlägt ... D. Rösner FP 2013 . . . 26 Parser in Haskell . . . als Funktionen . . . als Monaden Monadische Parser: Kombinatoren chainl p op a . . . chainl :: Parser a -> Parser (a -> a -> a) -> a -> Parser a chainl p op a = (p ‘chainl1‘ op) +++ return a chainl1 :: Parser a -> Parser (a -> a -> a) -> Parser a p ‘chainl1‘ op = do {a <- p; rest a} where rest a = do {f <- op; b <- p; rest (f a b)} +++ return a D. Rösner FP 2013 . . . 27 Parser in Haskell . . . als Funktionen . . . als Monaden Monadische Parser: Beispiele Parser für klein- bzw. grossgeschriebene Zeichen, für Buchstaben bzw. alphanumerische Zeichen lower,upper :: Parser Char lower = sat isLower upper = sat isUpper letter :: Parser Char letter = sat isAlpha alphanum :: Parser Char alphanum = sat isAlphaNum D. Rösner FP 2013 . . . 28 Parser in Haskell . . . als Funktionen . . . als Monaden Monadische Parser: Beispiele Kombinatoren für die Behandlung von Leerraum space :: Parser String space = many (sat isSpace) token :: Parser a -> Parser a token p = do {a <- p; space; return a} apply :: Parser a -> String -> [(a,String)] apply p = parse (do {space; p}) D. Rösner FP 2013 . . . 29 Parser in Haskell . . . als Funktionen . . . als Monaden Monadische Parser: Beispiele Anwendung von ‘chainl1‘: akzeptiere Folge von Ziffern und erzeuge den Wert der zugehörigen Integer-Zahl natural :: Parser Int natural = digit ‘chainl1‘ return (\m n -> 10*m + n) nat :: Parser Int nat = token natural D. Rösner FP 2013 . . . 30 Parser in Haskell . . . als Funktionen . . . als Monaden 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. 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 2013 . . . 31