Monadick é parsery

16
Monadické parsery Graham Hutton, Erik Mejer: Monadic Parser Combinators http:// www.cs.nott.ac.uk/Department/Staff/gmh/monparsing.ps Graham Hutton, Erik Mejer: Functional Pearls: Monadic Parsing in Haskell http://www.cs.nott.ac.uk/~gmh/bib.html#pearl Daan Leijen: Parsec, a fast combinator parser http://legacy.cs.uu.nl/daan/download/parsec/parsec.html Cieľom tejto prednášky je ukázať na probléme syntaktickej analýzy, ktorý sme začali minule, ideu fungovania monád a monadického štýlu programovania. Mnohé analyzátory, ktoré skonštruujeme sa historicky nazývajú inými menami, ale ich analógie nájdete aj v predošlej prednáške. Rôzne typy monád budeme študovať na budúcej prednáške.

description

Monadick é parsery. Graham Hutton, Erik Mejer: Monadic Parser Combinators http://www.cs.nott.ac.uk/Department/Staff/gmh/monparsing.ps Graham Hutton, Erik Mejer: Functional Pearls : Monadic Parsing in Haskell http://www.cs.nott.ac.uk/~gmh/bib.html#pearl - PowerPoint PPT Presentation

Transcript of Monadick é parsery

Page 1: Monadick é parsery

Monadické parseryGraham Hutton, Erik Mejer: Monadic Parser Combinatorshttp://www.cs.nott.ac.uk/Department/Staff/gmh/monparsing.ps

Graham Hutton, Erik Mejer: Functional Pearls: Monadic Parsing in Haskell

http://www.cs.nott.ac.uk/~gmh/bib.html#pearl

Daan Leijen: Parsec, a fast combinator parserhttp://legacy.cs.uu.nl/daan/download/parsec/parsec.html

Cieľom tejto prednášky je ukázať na probléme syntaktickej analýzy, ktorý sme začali minule, ideu fungovania monád a monadického štýlu programovania.

Mnohé analyzátory, ktoré skonštruujeme sa historicky nazývajú inými menami, ale ich analógie nájdete aj v predošlej prednáške.

Rôzne typy monád budeme študovať na budúcej prednáške.

Page 2: Monadick é parsery

Syntaktický analyzátorMinule sme definovali analyzáror ako nasledujúci typ:

type Parser symbol result = [symbol] -> [([symbol],result)]dnes to bude (zámena poradia – rešpektujúc použité zdroje):

type Parser result = String -> [(result, String)]resp.:

type Parser result = Parser(String -> [(result, String)])Primitívne parsery:return :: a->Parser a -- tento sa volal succeedreturn v = \xs -> [(v,xs)] -- nečíta zo vstupu, dá výsledok vreturn v xs = [(v,xs)]zero :: Parser a -- tento sa volal failzero = \xs -> [] -- neakceptuje ničzero xs = []item :: Parser Char -- akceptuje ľubovoľný znakitem = \xs -> case xs of -- tento znak dá do výsledku

[] -> [] -- prázdny vstup, neakceptuje(v:vs) -> [(v,vs)] -- akceptuje znak v

Page 3: Monadick é parsery

Kombinátory parserovseq :: Parser a -> Parser b -> Parser (a,b) -- zreťazenie

<*>p `seq` q = \xs -> [ ((v,w),xs'') |

(v,xs') <- p xs, (w,xs'') <- q xs']

tento kombinátor zbytočne vyrába vnorené výrazy typu \(a,(b,(c,d)))->…

v tejto časti seq (<*>) upadne do zabudnutia, nahradí ho bind (>>=),

nový kombinátor bind (>>=) kombinuje analyzáror p::Parser a s funkciou qf:: a -> Parser b, ktorá ako argument dostane výsledok analýzy analyzátora p a vráti analyzátor q:

bind :: Parser a -> (a -> Parser b) -> Parser bp `bind` qf= \xs -> concat [ (qf v) xs' | (v,xs')<-p xs])

v je výsledok analýzy p, qf v je nový analyzátor parametrizovaný výsledkom v.

Page 4: Monadick é parsery

Ako sa bind používabind :: Parser a -> (a -> Parser b) -> Parser bp `bind` qf= \xs -> concat [ (qf v) xs' | (v,xs')<-p xs])

spôsob použitia: preprogramujeme seq:p1 `bind` \x1 -> p `seq` q =p2 `bind` \x2 -> p `bind` (\x ->… q `bind` (\y ->pn `bind` \xn -> return (x,y) ) )return (f x1 x2 . …..xn)

spôsob čítania:najprv pustíme analyzátor p1, ktorého výsledok je v premennej x1,potom pustíme analyzátor p2, ktorého výsledok je v premennej x2,...nakoniec pustíme analyzátor pn, ktorého výsledok je v premennej

xn,výsledok celej analýzy dostaneme ako funkciu f x1 x2 . …..xn.

Page 5: Monadick é parsery

Príklady jednoduchých parserov

ilustrujme na niekoľkých príkladoch použitie bind:sat :: (Char->Bool) -> Parser Char -- ten sa volal

satisfysat pred = item `bind` \x-> -- akceptuje symbol, pre

ktorý if pred x then return x else zero -- platí predikát

pred

char :: Char -> Parser Char -- ten sa volal symbolchar x = sat (\y -> x==y) -- akceptuje znak x

digit :: Parser Char -- akceptuje cifrudigit = sat isDigit

Main> parse (sat isLower) "a123ad"[('a',"123ad")]

Main> parse (char 'a') "a123ad"[('a',"123ad")]

Main> parse (digit) "123ad"[('1',"23ad")]

Main> parse (item) "abcd"[('a',"bcd")]

Page 6: Monadick é parsery

Zjednotenie zjednotenie/disjunkciu analyzátorov poznáme ako <|>:

plus :: Parser a -> Parser a -> Parser ap `plus` q = \xs -> (p xs ++ q xs)p `plus` q xs = (p xs ++ q xs)

definujme analyzátor word, ktorý akceptuje postupnosť písmen, {letter}*:

gramatická idea: word -> letter word | εword :: Parser Stringword = nonEmptyWord `plus` return "" where

nonEmptyWord = letter `bind` \x -> word `bind` \xs -> return (x:xs)Main> parse word "abcd"

[("abcd",""),("abc","d"),("ab","cd"),("a","bcd"),("","abcd")]

Main> parse (letter `plus` digit) "123abc"[('1',"23abc")]

Problém je, že nechceme dostať toľko riešení, chceme greedy verziu...

Page 7: Monadick é parsery

Deterministické plus predefinujeme zjednotenie analyzátorov:

(+++) :: Parser a -> Parser a -> Parser ap +++ q = \xs -> case p `plus` q xs of [] -> [] (x:xs) -> [x] -- z výsledku zober prvé

riešenie word' :: Parser Stringword' = nonEmptyWord +++ return "" where

nonEmptyWord = letter `bind` \x -> word' `bind` \xs -> return (x:xs)Main> parse word'

"abcd12"[("abcd","12")]

Page 8: Monadick é parsery

MonádyMonáda je analógia algebraickej štruktúry nazývanej monoid s doménou M abinárnou asociatívnou operáciou MxM->M s (ľavo a pravo)-neutrálnym

prvkom.

definícia v Haskelli (zapnite si Options/Allow Hugs-Ghc Extensions):class Monad m wherereturn :: a -> m a -- neutrálny prvok vzhľadom na >>=>>= :: m a -> (a -> m b) -> m b -- asociatívna operácia, nič iné ako

bind

chceli by sme vytvoriť Monad Parser t, ale musíme predefinovať typ Parser:

data Parser result = Parser(String -> [(result,String)]) to nás stojí trochu syntaktických zmien:parse :: Parser a -> String -> [(a,String)]parse (Parser p) = p -- inak: parse (Parser p) xs = p xs

instance Monad Parser where return v = Parser(\xs -> [(v,xs)]) p >>= f = Parser( -- bind

\xs -> concat [ parse (f v) xs' | (v,xs')<-parse p xs])

Page 9: Monadick é parsery

Zákony monád vlastnosti return a bind (>>=):return a >>= f = f a -- return ako identita zľavap >>= return = p -- retrun ako identita spravap >>= (\a -> (f a >>= g))= (p >>= (\a -> f a)) >>= g --

“asociativita”

vlastnosti zero a `plus`:zero `plus` p = p -- zero ako identita zľavap `plus` zero = p -- zero ako identita spravap `plus` (q `plus` r) = (p plus q) plus r -- asociativita

vlastnosti zero `plus` a >>= :zero >>= f = zero -- zero ako identita zľavap >>= const zero = zero -- zero ako identita sprava(p `plus` q) >>= f = (p >>= f) `plus` (q >>= f) -- distribut.p>>=(\a->f a `plus` g a) = (p >>= f) `plus` (q >>= f) -- asociat.

bind je >>=

Page 10: Monadick é parsery

Monad comprehensionp1 >>= \x1 -> [ f x1 x2 . …..xn | do {x1 <- p1,p2 >>= \x2 -> x1 <- p1 x2 <- p2,… x2 <- p2 ….

pn >>= \xn -> … xn <- pn,return (f x1 x2 . …..xn) xn <- pn] return (f x1 x2 . …..xn) }

string :: String -> Parser String -- volal sa tokenstring "" = return ""string (x:xs) = char x >>= \_ -> -- výsledok char x zahoď

string xs >>= \_ -> -- výsledok string xs zahoď return (x:xs) -- výsledok je celé slovo x:xs

string :: String -> Parser String -- prepíšeme do novejstring "" = return "“ -- syntaxistring (c:cs) = do { _ <- char c; _ <- string cs; return (c:cs)} -- explicitné...string (c:cs) = do {char c; string cs; return (c:cs)} -- čitateľnejšie...

bind je >>=

Main> parse (string "begin") "beginend"[("begin","end")]

Page 11: Monadick é parsery

Iterátory {p}*, {p}+

• manyp -> many1p | εmany :: Parser a -> Parser [a]many p = many1 p +++ return []

• many1p -> p manyp

many1 :: Parser a -> Parser [a]many1 p = do {a <- p; as <- many p; return (a:as)}

opakovanie p s oddeľovačom sep: {p sep}*p | εsepby :: Parser a -> Parser b -> Parser [a]p `sepby` sep = (p `sepby1` sep) +++ return []

{p sep}*psepby1 :: Parser a -> Parser b -> Parser [a]p `sepby1` sep = do { a<-p; as<- many(do {sep;p}); return

(a:as) }

Main> parse (many digit) "123abc"[("123","abc")]

Main> parse ((many digit) `sepby` (char '+' `plus` char '*')) "1+2*3abc"[(["1","2","3"],"abc")]

Page 12: Monadick é parsery

Zátvorkyopen :: Parser Charopen = char '('close = char ')' verzia 1paren :: Parser () -- nezaujíma nás výstupná hodnotaparen = do { open; paren; close; paren; return () }

+++ return ()

verzia 2data Bin = Nil | Node Bin Bin -- vnútorná reprezentácia

deriving(Show, Read, Eq)

parenBin :: Parser Bin -- analyzátor zo String do BinparenBin = do {

open; x<-parenBin; close; y<-parenBin; return (Node x y) }

+++ return Nil

Main> parse parenBin "(())()"[(Node (Node Nil Nil) (Node Nil Nil),"")]Main> parse parenBin "())()"[(Node Nil Nil,")()")]

Analyzátor pre dobre uzátvorkované výrazy podľa gramatiky: P -> ( P ) P | ε

Main> parse parenBin "()"[(Node Nil Nil,"")]Main> parse parenBin "()()"[(Node Nil (Node Nil Nil),"")]

Page 13: Monadick é parsery

Zátvorky 2 najivneparenBr :: Parser ()parenBr =

do { (open `plus` openBr) ; parenBr; (close `plus` closeBr) ; parenBr; return () }

+++ return ()

reálneparenBr :: Parser ()parenBr = do { open; parenBr; close; parenBr; return () }

+++do { openBr ; parenBr; closeBr ; parenBr; return

() } +++ return ()

Main> parse parenBr "([[]])([])"[((),"")]Main> parse parenBr "([[(])])([])"[((),"([[(])])([])")]

P -> (P) P | [P] P | ε

Page 14: Monadick é parsery

expr najivneGramatika je ľavo-rekurzívna, preto sa to zacyklí:

expr = do {x <- expr; f <- addop; y<-factor; return (f x y)} `plus` factor

factor = nat `plus` do { open; x<-expr; close; return x}

addop = do { char '+'; return (+) } `plus` do { char '-'; return (-) }

riešenie je zlé, čo ale stojí za zmienku je typ

addop :: Parser (Int -> Int -> Int) parser, ktorého vnútorná reprezentácia toho, čo zanalyzuje je

funkcia, táto funkcia sa potom použije na kombináciu dvoch posebe-

idúcich výsledkov iného parsera (f x z)

<expr> ::= <expr> + <expr> | <expr> - <expr>

<factor> ::= nat | ( <expr> )

Page 15: Monadick é parsery

chainl Ak tú myšlienku zovšeobecníme dostaneme nasledujúci kód:chainl1 :: Parser a -> Parser (a -> a -> a) -> Parser ap `chainl1` op = do {a <- p; rest a} where rest a = do {f <- op; b <- p; rest (f a b)}

`plus` return a

Aritmetické výrazy:expr = term `chainl1` addopterm = factor `chainl1` mulopfactor = nat `plus` (do {open; n <- expr; close; return n})mulop = do { char '*'; return (*) } `plus` do { char '/'; return

(div) }

<expr> ::= <term> | <term> + <expr> | <term> - <expr> <term> ::= <factor> | <factor> * <term> | <factor> / <term><factor> ::= number | ( <expr> )

Main> parse expr "1+2*3+10"[(17,""),(8,"0"),(7,"+10"),(3,"*3+10"),(1,"+2*3+10")]

Page 16: Monadick é parsery

Cvičenia parser pre λ-calcul, t.j. Parser LExp:

parser binárnej/hexa konštanty

parser palindromov, t.j. Parser ()

parser morseovej abecedy, t.j. Parser String

Main> parse lambda "(\\x.(x x) \\x.(x x))"[(APL (LAMBDA "x" (APL (ID "x") (ID "x"))) (LAMBDA "x" (APL (ID "x") (ID "x"))),"")]

Main> parse binConst "1101"[13,"")]Main> parse hexaConst "FF"[255,"")]