{-# LANGUAGE FlexibleContexts #-} module CSV( Settings(..), specialChars, defaultSettings, parseRecord ) where import Text.ParserCombinators.Parsec ( (<|>), () ) import qualified Text.ParserCombinators.Parsec as Parsec import qualified Data.Char as Char data Settings = Settings { separator :: Char , quote :: Char , escape :: Char } defaultSettings = Settings { separator = ',' , quote = '\"' , escape = '\\' } specialChars :: Settings -> String specialChars s = (separator s):(quote s):(escape s):[] parseRecord :: Settings -> String -> Either Parsec.ParseError [String] parseRecord settings input = Parsec.parse (record settings) "error" input record s = do f <- (field s) `Parsec.sepBy` (Parsec.char (separator s)) Parsec.optional eol Parsec.eof return f field s = Parsec.many (Parsec.try (quoted s) <|> Parsec.many1 (fieldChar s)) >>= return . foldl1 (++) quoted s = Parsec.between (Parsec.char (quote s)) (Parsec.char (quote s)) (Parsec.many (quotedChar s)) fieldChar s = allExcept s (specialChars s) quotedChar s = allExcept s [quote s] allExcept s c = Parsec.try (escapeChar s) <|> Parsec.satisfy (\x -> (not (Char.isControl x)) && (x `notElem` c)) escapeChar s = do Parsec.char (escape s) Parsec.oneOf (specialChars s) eol = Parsec.try (Parsec.string "\r\n") <|> Parsec.try (Parsec.string "\r") <|> Parsec.try (Parsec.string "\n") "end of line"