| {-# LANGUAGE LambdaCase #-} | |
| module Parser | |
|   ( Parser() | |
|   , module X | |
|   , Parser.any | |
|   , satisfy | |
|   , string | |
|   , digit | |
|   , number | |
|   , spaces | |
|   , reserved | |
|   , lexeme | |
|   , (<?>) | |
|   , runParser | |
|   , between ) where | |
| 
 | |
| import Control.Applicative as X | |
| import Control.Monad as X | |
| 
 | |
| import Data.Char | |
| 
 | |
| newtype Parser a | |
|   = Parser { parse :: String -> Either String (a, String) } | |
| 
 | |
| runParser :: Parser a -> String -> Either String a | |
| runParser (Parser p) s = fst <$> p s | |
| 
 | |
| (<?>) :: Parser a -> String -> Parser a | |
| p <?> err = p <|> fail err | |
| infixl 2 <?> | |
| 
 | |
| instance Functor Parser where | |
|   fn `fmap` (Parser p) = Parser go where | |
|     go st = case p st of | |
|       Left e            -> Left e | |
|       Right (res, str') -> Right (fn res, str') | |
| 
 | |
| instance Applicative Parser where | |
|   pure x = Parser $ \str -> Right (x, str) | |
|   (Parser p) <*> (Parser p') = Parser go where | |
|     go st = case p st of | |
|       Left e -> Left e | |
|       Right (fn, st') -> case p' st' of | |
|         Left e' -> Left e' | |
|         Right (v, st'') -> Right (fn v, st'') | |
| 
 | |
| instance Alternative Parser where | |
|   empty = fail "nothing" | |
|   (Parser p) <|> (Parser p') = Parser go where | |
|     go st = case p st of | |
|       Left _  -> p' st | |
|       Right x -> Right x | |
| 
 | |
| instance Monad Parser where | |
|   return = pure | |
|   (Parser p) >>= f = Parser go where | |
|     go s = case p s of | |
|       Left e -> Left e | |
|       Right (x, s') -> parse (f x) s' | |
|   fail m = Parser go where | |
|     go = Left . go' | |
|     go' []     = "expected " ++ m ++ ", got to the end of stream" | |
|     go' (x:xs) = "expected " ++ m ++ ", got '" ++ x:"'" | |
|      | |
| 
 | |
| any :: Parser Char | |
| any = Parser go where | |
|   go []     = Left "any: end of file" | |
|   go (x:xs) = Right (x,xs) | |
| 
 | |
| satisfy :: (Char -> Bool) -> Parser Char | |
| satisfy f = do | |
|   x <- Parser.any | |
|   if f x | |
|     then return x | |
|     else fail "a solution to the function" | |
| 
 | |
| 
 | |
| char :: Char -> Parser Char | |
| char c = satisfy (c ==) <?> "literal " ++ [c] | |
| 
 | |
| oneOf :: String -> Parser Char | |
| oneOf s = satisfy (`elem` s) <?> "one of '" ++ s ++ "'" | |
| 
 | |
| string :: String -> Parser String | |
| string [] = return [] | |
| string (x:xs) = do | |
|   char x | |
|   string xs | |
|   return $ x:xs | |
| 
 | |
| natural :: Parser Integer | |
| natural = read <$> some (satisfy isDigit) | |
| 
 | |
| lexeme :: Parser a -> Parser a | |
| lexeme = (<* spaces) | |
| 
 | |
| reserved :: String -> Parser String | |
| reserved = lexeme . string | |
| 
 | |
| spaces :: Parser String | |
| spaces = many $ oneOf " \n\r" | |
| 
 | |
| digit :: Parser Char | |
| digit = satisfy isDigit | |
| 
 | |
| number :: Parser Int | |
| number = do | |
|   s <- string "-" <|> empty | |
|   cs <- some digit | |
|   return $ read (s ++ cs) | |
| 
 | |
| between :: Parser b -> Parser c -> Parser a -> Parser a | |
| between o c x = o *> x <* c | |
| 
 | |
| contents :: Parser a -> Parser a | |
| contents x = spaces *> x <* spaces | |
| 
 | |
| sep :: Parser b -> Parser a -> Parser [a] | |
| sep s c = sep1 s c <|> return [] | |
| 
 | |
| sep1 :: Parser b -> Parser a -> Parser [a] | |
| sep1 s c = do | |
|   x <- c | |
|   xs <- many $ s >> c | |
|   return $ x:xs | |
| 
 | |
| option :: a -> Parser a -> Parser a | |
| option x p = p <|> return x | |
| 
 | |
| optionMaybe :: Parser a -> Parser (Maybe a) | |
| optionMaybe p = option Nothing $ Just <$> p | |
| 
 | |
| optional :: Parser a -> Parser () | |
| optional p = void p <|> return () | |
| 
 | |
| eof :: Parser () | |
| eof = Parser go where | |
|   go (x:_) = Left $ "expected eof, got '" ++ x:"'" | |
|   go []    = Right ((), [])
 |