45
CS 352 Lecture 15: Parsers Dr. Patrice Brémond-Grégoire CS352 Spring 2017

Lecture 15: Parsers - California State Polytechnic ...patriceb/CS352S17-Lecture15 Parsers (Slides).pdf · Lecture 15 Haskell IO Revisited Creating Parsers in Haskell. Haskell IO

  • Upload
    ngokien

  • View
    224

  • Download
    2

Embed Size (px)

Citation preview

CS 352Lecture 15: Parsers

Dr. Patrice Brémond-GrégoireCS352 Spring 2017

Lecture 15

Haskell IO Revisited

Creating Parsers in Haskell

Haskell IO

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

Parsers

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

Some Useful ConceptsFunctors

Applicative Functors

Monads

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

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

Back to Parsers

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

Eval

eval :: String -> Int

eval xs = case parse expr xs of

Just (n, []) -> n

Just (n, rs) -> error (show n

++ " and residual input "++ rs)

Nothing -> error "Invalid expression"

Example 1eval "1+2*4"

Example 2eval "(1+2)*4 and so on"