No.12 Monad Parserweb.sfc.keio.ac.jp/~hagino/fp17/12.pdf · Functor Parser •Before making it a...
Transcript of No.12 Monad Parserweb.sfc.keio.ac.jp/~hagino/fp17/12.pdf · Functor Parser •Before making it a...
Monad Parser
• Make Parser a monad, and write a parser more monadic
way.
2
newtype Parser a = P (String -> Maybe (a, String))
• In order to make Parser a monad, it needs to take a type variable.
• P is the data constructor.
• A parser takes a string and it may return a value and a string. The latter string is the string not used.
• Maybe is used because a parser may fail.
input string
string left
value
parser
Maybe
Using Parser• Since a parser cannot be used directly, define a function to
parse a string for a given parser.
3
newtype Parser a = P (String -> Maybe (a, String))
parse::Parser a -> String -> Maybe (a, String)
parse (P p) cs = p cs
• parse is a just a function which applies the parser.
• For example, a parser which parse only one character may be written as follows:
parseOne::Parser Char
parseOne = P p
where p [] = Nothing
p (c:cs) = Just (c, cs)
• Try it out.
> parse ParseOne "123"
Just ('1',"23")
Functor Parser
• Before making it a monad, it need to be an instance of Functor.
• Functor f needs fmap class method:
• fmap::(a -> b) -> f a -> f b
4
import Control.Applicative
instance Functor Parser where
fmap f p = P (∖cs -> do (v, cs1) <- parse p cs
return (f v, cs1))
• fmap of Parser applies the function to the parsed result.
• parseChar::Parser Char
• fmap isDigit parseChar::Parser Bool
> parse (fmap isDigit ParseOne) "123"
Just (True,"23")
Applicative Parser
• Next, make it an instance of Applicative.
• Applicative f needs two class methods:• pure::a -> f a
• (<*>)::f (a -> b) -> f a -> f b
5
instance Applicative Parser where
pure v = P (∖cs -> return (v, cs))
p <*> q = P (∖cs -> do (f, cs1) <- parse p cs
(v, cs2) <- parse q cs1
return (f v, cs2))
• As a parser, pure does not parse anything.
• <*> applies two parsers and applies the result of the second parser to the result of the first one.
• pure and <*> of Applicative have to satisify the following rules:• pure id <*> v = v
• pure (.) <*> u <*> v <*> w = u <*> (v <*> w)
• pure f <*> pure x = pure (f x)
• u <*> pure y = pure ($ y) <*> u
Monad Parser
• Now make the parser an instance of Monad.
• Monad m needs two class methods:• return::a -> m a
• (>>=)::m a -> (a -> m b) -> m b
6
instance Monad Parser where
p >>= f = P (∖cs -> do (v, cs1) <- parse p cs
parse (f v) cs1)
return x = P (∖cs -> return (x, cs))
• return is the same as pure of Applicative.
• p >>= f is a parser where if p successfully parses, the result is applied to f and continue the next parse.
• If p fails (i.e. returns Nothing), f is not used.
• Using Monad do expression, codes may become more readable.• p >>= (∖x -> q)• do { x <- p; q }
Alternative Parser
• Furthermore, by making the parser an instance of Alternative, parser writing becomes much easier.
• Alternative f needs two class methods:• empty::f a
• (<|>)::f a -> f a -> f a
7
instance Alternative Parser where
empty = P (∖cs -> Nothing)
p <|> q = P (∖cs -> parse p cs <|> parse q cs)
• empty fails to parse.
• p <|> q is the alternative parser where if p succeeds, its result is used, otherwise, tries q.• Maybe is also an instance of Alternative.
• some and many are defined.• some::f a -> f [a]
• many::f a -> f [a]
• some and many tries to apply the parser repeatedly. some requires at least one successful parsing, but many does not.
• Alternative instance is a monoid by empty as an identity element and <|>as an associative binary operator.
Parser Parts (1)• Just get one character.
8
parseOne::Parser Char
parseOne = P p
where p [] = Nothing
p (c:cs) = Just (c, cs)
• parse parseOne "abc"
⇒ Just ('a', "bc")
• parse parseOne ""
⇒ Nothing
• Check the first character satisfies a condition or not.
parseSat::(Char -> Bool) -> Parser Char
parseSat f = do x <- parseOne
if f x then return x else empty
• parse (parseSat isDigit) "123abc"
⇒ Just ('1', "23abc")
• parse (parseSat isDigit) "abc"
⇒ Nothing
Parser Parts (2)• Parse one character.
• Check the first character is the specified one or not.
9
parseChar::Char -> Parser Char
parseChar x = parseSat (== x)
• parse (parseChar 'a') "abc"
⇒ Just ('a', "bc")
• parse (parseChar 'a') "123"
⇒ Nothing
• Parse a string by repeatedly applying parseChar.parseString::String -> Parser String
parseString [] = return []
parseString (x:xs) = do parseChar x
parseString xs
return (x:xs)
• parse (parseString "abc") "abcab"
⇒ Just ("abc", "ab")
• parse (parseString "abc") "ababc"
⇒ Nothing
Parser Parts (3)• Skip spaces.
10
parseSpace::Parser ()
parseSpace = do many (parseSat isSpace)
return ()
• parse parseSpace " 123"
⇒ Just ((), "123")
• parse parseSpace "123"
⇒ Just ((), "123")
• Parse a number.
parseNum::Parser Int
parseNum = do parseSpace
cs <- some (parseSat isDigit)
return (read cs)
• parse parseNat " 123 + 567"
⇒ Just (123, " + 567")
• Skip the head spaces.
Parser Parts (4)• Parse a symbol.
11
parseSymbol::String -> Parser String
parseSymbol xs = do parseSpace
parseString xs
• parse (parseSymbol "*") " * 123"
⇒ Just ("*", " 123")
• parse (parseSymbol "*") " + 123"
⇒ Nothing
• Using parseNum and parseSymbol, various parsing is possible.
do x <- parseNum
parseSymbol "*"
y <- parseNum
return (x * y)
parseSymbol "*" <|> parseSymbol "+"
Expression Parser (1)• For simplicity, let parsers do not create parse trees but directly calculate
the result.• parseExpr::Parser Int
• parseTerm::Parser Int
• parseFactor::Parser Int
• The syntax of factor is:• factor ::= number | "(" expr ")“
It may start with a number or a left parenthesis.
12
parseFactor::Parser Int
parseFactor = parseNum
<|>
do parseSymbol "("
x <- parseExpr
parseSymbol ")"
return x
Expression Parser (2)• The syntax of term is:
• term ::= factor (("*" | "/") factor)*
It first parses a factor, then check whether it is followed by *or / .
13
parseTerm::Parser Int
parseTerm = parseFactor >>= nextFactor
where nextFactor x = do parseSymbol "*"
y <- parseFactor
nextFactor (x * y)
<|>
do parseSymbol "/"
y <- parseFactor
nextFactor (x `div` y)
<|>
return x
Expression Parser (3)• The syntax of expr is:
• expr ::= term (("+" | "-") term)*
It first parses a term, then check whether it is followed by +or - .
14
parseExpr::Parser Int
parseExpr = parseTerm >>= nextTerm
where nextTerm x = do parseSymbol "+"
y <- parseTerm
nextTerm (x + y)
<|>
do parseSymbol "-"
y <- parseTerm
nextTerm (x - y)
<|>
return x
Output the Result• Separate the input to lines, parse each line and output the
result.
15
showResult::Maybe (Int, String) -> String
showResult (Just (x, [])) = show x
showResult _ = "error"
main::IO ()
main = do cs <- getContents
putStr $
unlines $
map (showResult . parse parseExpr) $
lines cs
Parser (1)
16
import Control.Applicative
import Data.Char
newtype Parser a = P (String -> Maybe (a, String))
parse::Parser a -> String -> Maybe (a, String)
parse (P p) cs = p cs
instance Functor Parser where
fmap f p = P (∖cs -> do (v, cs1) <- parse p cs
return (f v, cs1))
instance Applicative Parser where
pure v = P (∖cs -> return (v, cs))
p <*> q = P (∖cs -> do (f, cs1) <- parse p cs
(v, cs2) <- parse q cs1
return (f v, cs2))
instance Monad Parser where
p >>= f = P (∖cs -> do (v, cs1) <- parse p cs
parse (f v) cs1)
return x = P (∖cs -> return (x, cs))
instance Alternative Parser where
empty = P (∖cs -> Nothing)
p <|> q = P (∖cs -> parse p cs <|> parse q cs)
parseOne::Parser Char
parseOne = P p
where p [] = Nothing
p (c:cs) = Just (c, cs)
parseSat::(Char -> Bool) -> Parser Char
parseSat f = do x <- parseOne
if f x then return x else empty
parseChar::Char -> Parser Char
parseChar x = parseSat (== x)
parseString::String -> Parser String
parseString [] = return []
parseString (x:xs) = do parseChar x
parseString xs
return (x:xs)
parseSpace::Parser ()
parseSpace = do many (parseSat isSpace)
return ()
parseNum::Parser Int
parseNum = do parseSpace
cs <- some (parseSat isDigit)
return (read cs)
parseSymbol::String -> Parser String
parseSymbol xs = do parseSpace
parseString xs
calcmp.hs
Parser (2)
17
parseFactor::Parser Int
parseFactor = parseNum
<|>
do parseSymbol "("
x <- parseExpr
parseSymbol ")"
return x
parseTerm::Parser Int
parseTerm = parseFactor >>= nextFactor
where nextFactor x = do parseSymbol "*"
y <- parseFactor
nextFactor (x * y)
<|>
do parseSymbol "/"
y <- parseFactor
nextFactor (x `div` y)
<|>
return x
parseExpr::Parser Int
parseExpr = parseTerm >>= nextTerm
where nextTerm x = do parseSymbol "+"
y <- parseTerm
nextTerm (x + y)
<|>
do parseSymbol "-"
y <- parseTerm
nextTerm (x - y)
<|>
return x
showResult::Maybe (Int, String) -> String
showResult (Just (x, [])) = show x
showResult _ = "error"
main::IO ()
main = do cs <- getContents
putStr $
unlines $
map (showResult . parse parseExpr) $
lines cs
% ./calcmp
1+2
3
(1+2)*(3+4)
21
1+2*3-4/5
7
1 2
error
1+x-5
error
実行例
Exercise 12
• Change parseExpr, parseTerm and parseFactor to return ParseTree rather and Int.• parseExpr::Parser ParseTree
• parseTerm::Parser ParseTree
• parseFactor::Parser ParseTree
where ParseTree is:
18
data ParseTree = Number Int |
Plus ParseTree ParseTree |
Minus ParseTree ParseTree |
Time ParseTree ParseTree |
Divide ParseTree ParseTree
Exercise 12 (cont.)
• main and showResult should be converted as follows:
19
parseFactor::Parser Int
parseFactor = parseNum
<|>
do parseSymbol "("
x <- parseExpr
parseSymbol ")"
return x
parseFactor::Parser ParseTree
parseFactor = do n <- parseNum
return (Number n)
<|>
do parseSymbol "("
x <- parseExpr
parseSymbol ")"
return x
main::IO ()
main = do cs <- getContents
putStr $
unlines $
map (showResult . parse (fmap eval parseExpr)) $
lines cs
• parseFactor should be converted as follows: