Homework-New/Hasklet2.hs

178 lines
4.5 KiB
Haskell

{-# LANGUAGE LambdaCase #-}
module Hasklet2 where
import Control.Applicative (liftA2)
import qualified Control.Applicative as CA
import Data.Bifunctor
--
-- * Parser type
--
-- | Given a string, a parser either fails or returns a parsed value and
-- the rest of the string to be parsed.
newtype Parser a = Parser { runParser :: String -> Maybe (a, String) }
instance Functor Parser where
fmap f (Parser nf) = Parser $ (first f<$>) <$> nf
instance Applicative Parser where
pure v = Parser $ Just . (,) v
pf <*> pa = Parser $ \s -> do
(f, s') <- runParser pf s
(v, s'') <- runParser pa s'
return (f v, s'')
--
-- * Single character parsers
--
-- | Match the end of the input string.
end :: Parser ()
end = Parser $ \case
"" -> Just ((), "")
_ -> Nothing
-- | Return the next character if it satisfies the given predicate.
nextIf :: (Char -> Bool) -> Parser Char
nextIf f = Parser $ \case
(c:s') | f c -> Just (c,s')
_ -> Nothing
-- | Parse the given character.
char :: Char -> Parser Char
char c = nextIf (c ==)
-- | Parse one of the given characters.
oneOf :: [Char] -> Parser Char
oneOf cs = nextIf (`elem` cs)
-- | Parse a particular class of character.
lower, upper, digit, space :: Parser Char
lower = oneOf ['a'..'z']
upper = oneOf ['A'..'Z']
digit = oneOf ['0'..'9']
space = oneOf " \t\n\r"
-- | Parse a digit as an integer.
digitInt :: Parser Int
digitInt = flip (-) (fromEnum '0') . fromEnum <$> digit
--
-- * Alternative and repeating parsers
--
-- | Run the first parser. If it succeeds, return the result. Otherwise run
-- the second parser.
--
-- >>> runParser (upper <|> digit) "Hi"
-- Just ('H',"i")
--
-- >>> runParser (upper <|> digit) "42"
-- Just ('4',"2")
--
-- >>> runParser (upper <|> digit) "w00t"
-- Nothing
--
(<|>) :: Parser a -> Parser a -> Parser a
p1 <|> p2 = Parser $ \s -> runParser p1 s CA.<|> runParser p2 s
-- | Parse a sequence of one or more items, returning the results as a list.
-- Parses the longest possible sequence (i.e. until the given parser fails).
--
-- >>> runParser (many1 lower) "abcDEF123"
-- Just ("abc","DEF123")
--
-- >>> runParser (many1 lower) "ABCdef123"
-- Nothing
--
-- >>> runParser (many1 (lower <|> upper)) "ABCdef123"
-- Just ("ABCdef","123")
--
-- >>> runParser (many1 digitInt) "123abc"
-- Just ([1,2,3],"abc")
--
many1 :: Parser a -> Parser [a]
many1 p = liftA2 (:) p (many p)
-- | Parse a sequence of zero or more items, returning the results as a list.
--
-- >>> runParser (many lower) "abcDEF123"
-- Just ("abc","DEF123")
--
-- >>> runParser (many lower) "ABCdef123"
-- Just ("","ABCdef123")
--
-- >>> runParser (many (lower <|> upper)) "abcDEF123"
-- Just ("abcDEF","123")
--
-- >>> runParser (many digitInt) "123abc"
-- Just ([1,2,3],"abc")
--
-- >>> runParser (many digitInt) "abc123"
-- Just ([],"abc123")
--
many :: Parser a -> Parser [a]
many p = liftA2 (:) p (many p) <|> pure []
-- | Parse a natural number into a Haskell integer.
--
-- >>> runParser nat "123abc"
-- Just (123,"abc")
--
-- >>> runParser nat "abc"
-- Nothing
--
nat :: Parser Int
nat = foldl ((+).(*10)) 0 <$> many1 digitInt
parenth :: Parser a -> Parser b -> Parser (a, b)
parenth p1 p2 = liftA2 (,) (char '(' *> p1 <* char ',') (p2 <* char ')')
--
-- * Parsing structured data
--
-- | Parse a pair of natural numbers into a Haskell pair of integers. You can
-- assume there are no spaces within the substring encoding the pair,
-- although you're welcome to try to generalize it to handle whitespace too,
-- e.g. before/after parentheses and the comma.
--
-- This may get a little bit hairy, but the ugliness here will motivate some
-- key abstractions later. :-)
--
-- >>> runParser natPair "(123,45) 678"
-- Just ((123,45)," 678")
--
-- >>> runParser natPair "(123,45"
-- Nothing
--
-- >>> runParser natPair "(123,x) 678"
-- Nothing
--
natPair = parenth nat nat
-- | A simple tree data structure, isomorphic to arbitrarily nested pairs with
-- integers at the leaves.
data Tree
= Leaf Int
| Node Tree Tree
deriving (Eq,Show)
-- | Parse a tree encoded as arbitrarily nested pairs. This is basically just
-- the 'natPair' parser, now with recursion.
--
-- >>> runParser natTree "((1,2),3) abc"
-- Just (Node (Node (Leaf 1) (Leaf 2)) (Leaf 3)," abc")
--
-- >>> runParser natTree "(1,((100,101),10))"
-- Just (Node (Leaf 1) (Node (Node (Leaf 100) (Leaf 101)) (Leaf 10)),"")
--
natTree :: Parser Tree
natTree = (uncurry Node <$> parenth natTree natTree) <|> (Leaf <$> nat)