{-# 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 ((), [])