{-# LANGUAGE InstanceSigs #-} import System.IO import System.Environment import Control.Monad instance Functor Parser where fmap :: (a -> b) -> Parser a -> Parser b fmap f (P g) = P $ \ s -> case g s of Nothing -> Nothing Just (r, s') -> Just (f r, s') instance Applicative Parser where instance Monad Parser where return :: a -> Parser a return x = P $ \ s -> Just (x, s) (>>=) :: Parser a -> (a -> Parser b) -> Parser b P g >>= f = P $ \ s -> case g s of Nothing -> Nothing Just (r, s') -> let P h = f r -- Parser b -- h :: String -> Maybe (b, String) in h s' data Parser a = P (String -> Maybe (a, String)) parse :: String -> Parser a -> Maybe (a, String) parse s (P h) = h s zero :: Parser a zero = P $ \ s -> Nothing (<|>) :: Parser a -> Parser a -> Parser a (<|>) (P f1) (P f2) = P $ \ s -> case f1 s of Nothing -> f2 s Just (r, s') -> Just (r, s') -- zero <|> p == p == p <|> zero -- (p1 <|> p2) <|> p3 == p1 <|> (p2 <|> p3) item :: Parser Char item = P $ \ s -> if null s then Nothing else Just (head s, tail s) eos :: Parser () eos = P $ \ s -> if null s then Just ((), []) else Nothing char :: Char -> Parser Char char x = do c <- item if x == c then return c else zero -- many p tries to use the parser p -- zero or more times. -- manyH :: Parser a -> Parser [a] -- manyH p = do -- r <- p -- rs <- manyH p -- return (r:rs) -- try p = do{r <- p; return [r]} <|> return [] -- not quite what we want -- many p = try (manyH p) many :: Parser a -> Parser [a] many p = many1 p <|> return [] -- apply p one or more times. many1 :: Parser a -> Parser [a] many1 p = do{r <- p; rs <- many p; return (r:rs)} -- sepBy p sep parses p zero or more times separated by sep -- 0 -- p -- p, p -- p, p, p sepBy :: Parser a -> Parser b -> Parser [a] sepBy p sep = sepBy1 p sep <|> return [] -- sepBy p sep parses p one or more times separated by sep -- p -- p ,p -- p ,p ,p -- ... sepBy1 :: Parser a -> Parser b -> Parser [a] sepBy1 p sep = do r <- p rs <- many (sep >> p) return (r:rs) newline :: Parser () newline = char '\n' >> return () csv :: Parser [[String]] csv = many line line :: Parser [String] line = do rs <- sepBy1 content (char ',') newline return rs content :: Parser String content = many allowedChar allowedChar :: Parser Char allowedChar = do c <- item if c `elem` "\n.;:," then zero else return c main :: IO () main = do args <- getArgs case args of [filename] -> do h <- openFile filename ReadMode str <- hGetContents h case parse str csv of Nothing -> error "failed to parse" Just (result, r) -> if null r then putStrLn $ show result else error $ "leftover string:" ++ r -- result :: [[String]] -- putStrLn $ concat $ concat result -- hClose h _ -> error "wrong arguments" --------------------------------- -- Monad transformers mapLM :: (a -> b) -> [Maybe a] -> [Maybe b] mapLM f l = fmap (fmap f) l return1 :: a -> [Maybe a] return1 x = [Just x] join1 :: [Maybe [Maybe a]] -> [Maybe a] join1 [] = [] join1 (Nothing:xs) = Nothing : join1 xs join1 (Just l : xs) = l ++ join1 xs class MonadTrans t where lift :: (Monad m, Monad (t m)) => m a -> (t m) a data MaybeT m a = MT (m (Maybe a))