lecture 15: parsers - california state polytechnic ...patriceb/cs352s17-lecture15 parsers...
TRANSCRIPT
Haskell IO Revisited
The type IO a denotes an action (a function that performs I/O operations)
type IO a = World -> (a, World)
The hidden argument world is assumed to be modified by every action and therefore enforces sequencing and execution
IO Composition
Actions are composed using the Bind operator
(>>=) :: IO a -> (a -> IO b) -> IO b
The Bind operator “binds” the returned value of an actions to a name for use by subsequent function calls
The Bind operator also takes care of “passing” the hidden variable along
The do notation
The do notation is syntactic sugar for the binding operators (>> and >>=)do putStrLn "What's your name?"
x <- getLine
putStrLn ("Have a good day, "++x)
Sequence Functions
Sequence: Takes a list of IO actions and do them one at a time returning a list of the resultssequence :: [IO a] -> IO [a]
Sequence_: Takes a list of IO actions and do them one at a time returning no resultSequence_ :: [IO a] -> IO ()
Map for Actions
mapM: maps an action typed function over a list and return an action list (of the results)mapM :: (a -> IO b) -> [a] -> IO [b]
mapM_: maps an action typed function over a list and return an actionmapM_ :: (a -> IO b) -> [a] -> IO ()
Actions are Values
Actions are only executed when neededquestions :: [ IO String ]
questions = [ ask "First Name?"
, ask "Last Name?"
, ask "Age?"
]
Here questions holds a set of actions that can be selectively executed
A Parser in Haskell
What is a Parser?A program that takes a string and returns some
representation of the structure of the string (usually a tree) according to some grammar.
parser :: String -> Tree
Building Parsers
In order to effectively implement a parser it must be decomposed into finer grained parsers, each responsible for some part of the grammar.
Each parser must Take in a string not yet parsed
Return the structure of the portion parsed
Return the remaining portion of the string to be parsed
Parsers may fail
Input to the parser may not follow the grammar. In those case parsers must be able to return a failure.
Parser a :: String -> Maybe (a, String)
Any resemblance with IO?
Type:IO a :: World -> (a, World)
Parser a :: String -> Maybe (a, String)
CompositionEach IO function returns a world to be consumed by
the next IO functionEach Parser returns a string to be consumed by the
next Parser
Returned ValueBoth IO and Parsers return a specific value in
addition to the value passed along
Monads
Both IO and Parser are example of monads
Monads are Applicative Functors
Applicative Functors are Functors
Monad, Applicative and Functor are classes in the standard prelude
Functors
An instance of the Functor class is a type in Haskell that encapsulates values of a parameterized type (a) and is capable of applying functions to the encapsulated values
Examples of Functors:[a]
Maybe a
… and Parser a
… But also (b -> a)
Functor Functions
A member of the Functor class must implement the fmapfunction
fmap: Applies a function to values “encapsulated” by the Functor
fmap :: Functor f => (a -> b) -> f a -> f b
Functors: examples
What would be fmap for the Functor [a]?
fmap = map
What would be fmap for the Functor Maybe a?
fmap f Nothing = Nothing
fmap f Just x = Just (f x)
What would be fmap for a function?
(.) i.e. composition
Applicative Functors
An instance of the Applicative (Functor) class in Haskell is a type T a such thatT a is a Functor a
T (b -> c) can be applied to a value of type T b to yield a value of type T c
Examples of Applicative:[a]
Maybe a
… and Parser a
Applicative functions
To be an instance of the Applicative class a type must implement two functions:
pure: converts a value of type a into a value of type f a
pure :: Applicative f => a -> f a
<*>: Applies the function(s) encapsulated in an Applicative Functor f1 to the values “encapsulated” by another Applicative Functor f2
(<*>)::Applicative f => f(a -> b) -> f a -> f b
Applicative functions
What would be an implementation of pure for [a]?pure x = [x]
What would be an implementation of pure for Maybe a?pure x = Just x
What would be an implementation of <*> for Maybe a?(<*>) Nothing _ = Nothing
(<*>) _ Nothing = Nothing
(<*>) (Just x) (Just y) = Just (x y)
Monads
An instance of the Monad class in Haskell is a type T a such thatT a is an Applicative Functor
Values of type T a can be input to functions of type a -> T b to yield values of type T b
Monads functions
Bind: Sequentially compose two actions, passing any value produced by the first as an argument to the second(>>=) :: m a -> (a -> m b) -> m b
Then: Sequentially compose two actions, discarding any value produced by the first(>>) :: m a -> m b -> m b
Return: Inject a value into the monadic typereturn :: a -> m a
Fail: Fail with a messagefail :: String -> m a
Default Implementations
Both Then (>>) and fail have default implementation:The default implementation of Then calls the
Bind operation
The default implementation of fail calls error
Implementation of Parsers
Type: We define a new type for parsersdata Parser a = P (String -> Maybe (a, String))
Application: It will be convenient to use the parse function to apply a parser to a string:
parse :: Parser a -> String -> Maybe (a, String)
parse (P p)s = p s
Functor Implementation
fmap :: (a -> b) -> Parser a -> Parser b
instance Functor Parser a where
fmap f p = P (\s->case parse p s of
Nothing -> Nothing
Just (v, t) -> Just (f v, t))
Applicative Implementation
pure :: Applicative Parser => a -> Parser a
(<*>)::Applicative Parser =>
Parser (a -> b) -> Parser a -> Parser b
instance Applicative Parser where
pure v = P (\s -> Just (v, s))
(<*>) = ap
Monad implementation
instance Monad Parser where
p >>= f = P(\s -> case parse p s of
Nothing -> Nothing
Just (v, t) -> parse (f v) t)
A simplest parser: atom
atom :: Parser Char
atom = P(\s -> case s of
[] -> Nothing
(x:xs) -> Just (x, xs))
Example 1parse atom "abc"
Example 2parse (atom >>= (\_ -> atom)) "abc"
A more discriminant parser
sat :: (Char -> Bool) -> Parser Char
sat p = do x <- atom
if p x then return x else P(\_ -> Nothing)
Example 1parse (sat (\x -> x == 'a')) "abc"
Example 2parse (sat (\x -> x == 'a')) "bcd"
Some handy parsers
digit, lower, upper, letter, alphanum :: Parser Char
digit = sat isDigit
lower = sat isLower
upper = sat isUpper
letter = sat isAlpha
alphanum = sat isAlphaNum
Example 1parse lower "abc"
Example 2parse digit "abc"
Getting more elaborated
char :: Char -> Parser Char
char x = sat (==x)
string :: String -> Parser String
string [] = return []
string (x:xs) = char x >> string xs >> return (x:xs)
Example 1parse (char 'a') "abc"
Example 2parse (string "ab") "abc"
We need Choices!
infixr 5 +++
(+++) :: Parser a -> Parser a -> Parser a
p +++ q = P (\s -> case parse p s of
Nothing -> parse q s
Just (v, t) -> Just (v, t))
Example 1parse (string "ab" +++ string "cd") "abc"
Example 2parse (string "cd" +++ string "ab") "abc"
And repetitions
zeroOrMore, oneOrMore :: Parser a -> Parser [a]
zeroOrMore p = oneOrMore p +++ return []
oneOrMore p = do v <- p
vs <- zeroOrMore p
return (v: vs)
Exampleparse (oneOrMore (char 'a')) "aabc"
Exampleparse (zeroOrMore (char 'b')) "aabc"
More interesting parsers
ident :: Parser String
ident = do x <- lower
xs <- zeroOrMore alphanum
return (x:xs)
nat :: Parser Int
nat = do xs <- oneOrMore digit
return (read xs)
Exampleparse ident "a1bc"
Exampleparse nat "12a1bc"
A Tokenizer
space :: Parser ()
space = do zeroOrMore (sat isSpace)
return ()
token :: Parser a -> Parser a
token p = do space
v <- p
space
return v
Exampleparse (space >> ident) " a12bcd "
Exampleparse (token ident) " a12bcd "
Syntactic Elements
identifier :: Parser String
identifier = token ident
natural :: Parser Int
natural = token nat
symbol :: String -> Parser String
symbol xs = token (string xs)
Exampleparse identifier " a1bc "
Exampleparse natural " 22 a1bc"
Application: Simple Expressions
Given the following context free grammarexpr :: term (‘+’ expr | ε)
term :: factor (‘*’ term | ε)
factor :: ‘(’ expr ‘)’ | number
number :: digit (digit)*
Let’s build a parser that evaluates an expression using the usual semantics for + and *
Expression
expr :: term (‘+’ expr | ε)
expr :: Parser Int
expr = do t <- term
(do symbol "+"
e <- expr
return (t+e)
+++ return t)
Term
term :: factor (‘*’ term | ε)
term :: Parser Int
term = do f <- factor
(do symbol "*"
e <- term
return (e*f)
+++ return f)
Factor
factor :: ‘(’ expr ‘)’ | number
factor :: Parser Int
factor = do symbol "("
e <- expr
symbol ")"
return e
+++ natural