Was bisher geschah Funktionale Programmierung (Haskell): I Algebraische Datentypen I Typklassen und Instanzen Standard (z.B. Num, Eq, Ord, Read, Show) oder selbstdefiniert I strukturelle Induktion I Rekursionsschemata I Funktionen höherer Ordnung I Lazy evaluation I Monaden 130 Wiederholung Monaden abstrakter Datentyp mit Grundoperationen class Monad m where return :: a -> m a ( >>= ) :: m a -> (a -> m b) -> m b Axiome (Monaden-Gesetze) (return x) >>= f == f x m >>= return x == m ( m >>= f) >>= g == m >>= (\ x -> f x >>= g) 131 Beispiele I I I Maybe (kein oder ein Ergebnis) data Maybe a = Nothing | Just a instance Monad Maybe where return = \ x -> Just x m >>= f = case m of Nothing -> Nothing Just x -> f x Listen (Nichtdeterminismus, mehrere Ergebnisse) instance Monad [] where return = \ x -> [x] m >>= f = concat ( map f m ) IO data World = ... data IO = World -> World data IO a = IO { World -> (a, World) } Anwendung, z.B. do cs <- readFile "foo.bar" ; putStrLn cs 132 Übersetzer für Programme Höhere Programmiersprachen (z.B. Java, Haskell, C) erfordern Übersetzung von Quell- in Maschinen- oder Byte-Code Beispiel: Übersetzung von Java-Programmen Quellcode ↓ Zwischendarstellung (attributierter Syntaxbaum) ↓ Java-Bytecode Übersetzung in zwei Phasen (oft miteinander verschränkt): 1. Analyse-Phase (Front-End): Transformation des Quellcodes in eine Zwischendarstellung 2. Synthese-Phase (Back-End): Transformation der Zwischendarstellung in Maschinen- oder Bytecode 133 Analyse-Phase Quellcode Scanner Parser −→ Folge von Token −→ Syntaxbaum lexikalische Analyse (Scanner) lineare Analyse des Quelltextes, Aufteilung in Einheiten (Token) z.B. Schlüsselwörter, Bezeichner, Zahlen reguläre Sprachen, endliche Automaten syntaktische Analyse (Parser) hierarchische Struktur des Quelltextes z.B. Ausdrücke, Verzweigungen, Schleifen kontextfreie Sprachen, Kellerautomaten semantische Analyse Annotationen im Syntaxbaum, z.B. Typprüfungen 134 Anwendung ähnlicher Methoden I Übersetzung von Daten zwischen verschiedenen Formaten I automatische Code-Generierung I Verarbeitung von Domain-spezifischen Sprachen I Textformatierung I kontextabhängige Hilfe in Entwicklungsumgebungen I statische Analyse zur Fehlersuche in Programmen I Interpreter I graphische Editoren (z.B. für UML-Diagramme) mit Programmerzeugung 135 Parser zum Beispiel für I arithmetische und logische Ausdrücke I HTML-Code im Web-Browser I Haskell-Code in GHC / GHCI type Parser = String -> Tree parse :: Parser i.A. nur teilweise Verarbeitung der Eingabe type Parser = String -> (Tree, String) Ergebnis: 1. Syntaxbaum (o.Ä.) des verarbeiteten Teiles der Eingabe 2. unverarbeiteter Teil der Eingabe 136 Nichtdeterminismus Behandlung (temporär) uneindeutiger Ableitungen mehrdeutige Grammatiken, z.B. verschiedene Ableitungen für 5−3−2 E ::= n | E − E mit n ∈ N Nichtdeterministische Ausgabe (Ergebnisliste) type Parser = String -> [(Tree, String)] Ergebnis: Liste möglicher Syntaxbäume I []: Fehler I [ x ]: eindeutig interpretierbar (Erfolg) I [ x, y, .. ]: mögliche Interpretationen 137 Typabstraktion Idee: verschiedene Typen von I „Eingabesymbolen“ c und I und „Syntaxbäumen“ a als Datentyp: data Parser c a = Parser ( [c] -> [(a, [c])] ) parse :: Parser c a -> [c] -> [(a, [c])] parse ( Parser f ) s = f s (oft Char für c) A Parser for Things is a functions from Strings to Lists of Pairs of Things and Strings! 138 Parser Konstruktion komplexer Parser durch: I I Elementare Parser Operationen zur Kombination von Parsern: I I I sequentielle Kombination parallele Kombination (Auswahl) Iteration Man bemerke die Analogie zu regulären Ausdrücken E ::= ∅ | ε | a | EE | E + E | E ∗ mit a ∈ A und E ∈ RegExp(A) 139 Elementare Parser data Parser c a = Parser ( [c] -> [(a, [c])] ) I return (immer erfolgreich und eindeutig) return :: a -> Parser c a return v = Parser $ \x -> [(v, x)] I reject (nie erfolgreich) reject :: Parser c a reject = Parser $ \_ -> [] I item (vearbeitet das erste Symbol der Eingabe) item :: Parser c c item = Parser $ \x -> case x of [] -> [] (x : xs) -> [( x, xs )] I eof (nur bei leerer Eingabe erfolgreich) 140 Anwendung data Parser c a = Parser ( [c] -> [(a, [c])] ) parse :: Parser c a -> [c] -> [(a, [c])] parse p eingabe = p eingabe Beispiele: I parse (return 1) "abc" I parse reject "abc" I parse item "abc" I parse item "" 141 Test des ersten Symbols I satisfy (akzeptiert das erste Symbol, falls es die Bedingung pred erfüllt) satisfy :: (c -> Bool) -> Parser c c satisfy pred = do x <- item if pred x then return x else reject I expect (akzeptiert das erste Symbol, falls es genau das erwartete ist) expect :: Eq c => c -> Parser c c expect c = satisfy ( == c ) Beispiele: I parse (expect ’a’) "abc" I parse (expect ’a’) "" I parse (expect ’b’) "abc" I parse (satisfy isDigit) "1a4" 142 Sequentielle Verknüpfung Verkettung von Sprachen: L ◦ L0 = {uv | u ∈ L ∧ v ∈ L0 } seq :: Parser c a -> ( a -> Parser c b) -> Parser c b kennen wir schon als bind-Operation für Monaden >>= :: Parser c a -> ( a -> Parser c b) -> Parser c b p >>= f = Parser $ \ s -> do ( v, t ) <- parse p s parse (f v) t Anwendung: p1 >>= ( \v1 -> p2 >>= \v2 -> ... (\vn -> return (f v1 v2 ... vn))...) oder in do-Notation: do v1 <- p1 v2 <- p2 ... vn <- pn return (f v1 v2 ... vn) 143 Beispiele p :: Parser Char (Char, Char) p = do x <- item item y <- item return (x,y) parse p "abcde" parse p "a" parens :: Parser Char a -> Parser Char a parens p = do x <- item expect (’(’) y <- p expect (’)’) return y parse (parens item) "(r)" parse (parens p) "(abcd)" parse (parens p) "(abc)" 144 Parallele Verknüpfung (nichtdeterministisch) Vereinigung von Sprachen: L ∪ L0 (+++) :: Parser c a -> Parser c a -> Parser c a p +++ q = Parser $ \ s -> ( parse p s ) ++ ( parse q s ) Beispiel: s :: Parser Char () s = do { expect ’a’ ; s ; expect ’b’ ; s } +++ return () parse (do s ; eof) "abab" 145 Iteration bekannt aus LV Theoretische Informatik: L∗ = {ε} ∪ L+ L+ = L ◦ L∗ many’ :: Parser c a -> Parser c [a] many’ p = many1’ p +++ return [] many1’ :: Parser c a -> Parser c [a] many1’ p = do x <- p xs <- many’ p return (x : xs) Beispiel: nat’ :: Parser Char Integer nat’ = do xs <- many1’ (satisfy isDigit) return (read xs) 146 Akzeptanz formaler Sprachen s :: Parser Char () akzeptiert alle von der Grammatik mit den Regeln S → aSbS | ε erzeugten Wörter s = do { expect ’a’ ; s ; expect ’b’ ; s } +++ return () Beispiel: parse (do s ; eof) "abaabb" 147 Alternative Verknüpfung (deterministisch) (<|>) :: Parser c a -> Parser c a -> Parser c a p <|> q = Parser $ \ s -> case ( parse p s ) of [] -> ( parse q s ) x : xs -> return x Determinismus meist gewünscht, deshalb übliche Definitionen von many und many1: many :: Parser c a -> Parser c [a] many p = many1 p <|> return [] many1 :: Parser c a -> Parser c [a] many1 p = do x <- p xs <- many p return (x : xs) 148 Beispiel: Arithmetische Ausdrücke Grammatik (mit Operator-Präferenzen): E ::= P(+P)∗ P F ::= F (∗F )∗ ::= (E) | nat mit Parsec-Bibliothek ( import Text.Parsec) expr :: Parsec String () Integer expr = do xs <- sepBy1 produkt ( satisfy ( == ’+’ )) return $ sum xs produkt = do xs <- sepBy1 factor ( satisfy ( == ’*’ )) return $ product xs factor = parens expr <|> nat Anwendung: parse expr "egal" "3+4*(5+2)" 149 Beispiel: Prolog-Terme Beispiele für Prolog-Terme: vater(X), foo, Foo, foo(bar) Haskell-Datentyp: data Term = Var String | App String [ Term ] pterm :: Parsec String () Term pterm = do v <- var ; return $ Var v <|> do f <- fun ( do args <- parens $ sepBy pterm ( string "," return $ App f args ) <|> ( return $ App f [] ) fun :: Parsec String () String fun = withFirst isLower var :: Parsec String () String var = withFirst isUpper 150 Beispiel: Prolog-Atome Beispiele für Prolog-Atome: liest(vater(X), krimis), foo, foo(bar) Haskell-Datentyp: data Atom = Atom String [ Term ] patom :: Parsec String () Atom patom = do f <- fun ( do args <- parens $ sepBy pterm ( string "," ) return $ Atom f args ) <|> ( return $ Atom f [] ) 151 Beispiel: Prolog-Regeln Prolog-Regel und -Fakten: mag(tom, X) :- frau(X), liest (X, krimis). liest(mimi, krimis). Haskell-Datentyp: data Rule = Fact Atom | Rule Atom [ Atom ] prule :: Parsec String () Rule prule = do head <- patom ( do string ":-" body <- sepBy patom (string ",") string "." return $ Rule head body ) <|> ( do string "."; return $ Fact head ) 152