Funktionale Programmierung

Werbung
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
Herunterladen