module RefactoredParser ( Parser -- exports the type name but not the constructors ,parse, success,failure,sat,pmap,item,char,digit, (+++),(<:>),(>*>),(>->),(<-<), oneOrMore,zeroOrMore,chain ) {---------------------- Week 4B part II Aim: reusable Parser combinators including a new type for the Parser, but no export of the constructor ----------------------} where import Data.Char ------------------ u = undefined newtype Parser a = P (String -> Maybe (a,String)) parse :: Parser a -> String -> Maybe(a,String) -- run a parser on a given string parse (P f) = f ------------------- -- Basic Parsers, dependent on internal structure -- -- success and fail failure :: Parser a -- always fails failure = P $ \s -> Nothing success :: a -> Parser a -- succeeds without looking at the string success a = P $ \s -> Just (a,s) -- Parse any single character item = P $ \s -> case s of (c:s') -> Just(c,s') _ -> Nothing -- (+++) parse either using p or else using q infixr 5 +++ (+++) :: Parser a -> Parser a -> Parser a p +++ q = P $ \s -> case (parse p s) of Just (a,s') -> Just (a,s') Nothing -> parse q s -- (p >*> f) parse using p to produce a. -- Then parse using f a infixl 1 >*> (>*>) :: Parser a -> (a -> Parser b) -> Parser b p >*> f = P $ \s -> case parse p s of Just(a,s') -> parse (f a) s' Nothing -> Nothing -- example: parse any lowercase letter -- followed by its uppercase equivalent aA or bB etc. lowerUpper = item >*> \c -> char (toUpper c) ----------------------------------------------- -- Parsers below do not depend on the internal -- representation of Parser -- sat p parse a single character satisfying property p sat :: (Char -> Bool) -> Parser Char sat p = item >*> \c -> if p c then success c else failure char c = sat (==c) digit = sat isDigit -- pmap modifies the result of a parser pmap :: (a -> b) -> Parser a -> Parser b pmap f p = p >*> \a -> success (f a) (>->) :: Parser a -> Parser b -> Parser b p >-> q = p >*> \_ -> q (<-<) :: Parser b -> Parser a -> Parser b p <-< q = p >*> \a -> q >-> success a (<:>):: Parser a -> Parser [a] -> Parser [a] p <:> q = p >*> \a -> pmap (a:) q oneOrMore, zeroOrMore :: Parser a -> Parser [a] zeroOrMore p = oneOrMore p +++ success [] oneOrMore p = p <:> zeroOrMore p chain :: Parser a -> Parser b -> Parser [a] -- parse a list of as which are separated by bs chain p q = p <:> zeroOrMore (q >-> p) -- example: comma separated digits "1,2,3" diglist = chain digit (char ',')