module Text.CSV (CSV
, Record
, Field
, csv
, parseCSV
, parseCSVFromFile
, parseCSVTest
, printCSV
) where
import Text.ParserCombinators.Parsec
import Data.List (intersperse)
type CSV = [Record]
type Record = [Field]
type Field = String
csv :: Parser CSV
csv :: Parser CSV
csv = do CSV
x <- Parser [[Char]]
record Parser [[Char]] -> ParsecT [Char] () Identity [Char] -> Parser CSV
forall s (m :: * -> *) t u a sep.
Stream s m t =>
ParsecT s u m a -> ParsecT s u m sep -> ParsecT s u m [a]
`sepEndBy` ParsecT [Char] () Identity Char
-> ParsecT [Char] () Identity [Char]
forall s (m :: * -> *) t u a.
Stream s m t =>
ParsecT s u m a -> ParsecT s u m [a]
many1 ([Char] -> ParsecT [Char] () Identity Char
forall s (m :: * -> *) u.
Stream s m Char =>
[Char] -> ParsecT s u m Char
oneOf [Char]
"\n\r")
ParsecT [Char] () Identity ()
forall s (m :: * -> *) t u.
(Stream s m t, Show t) =>
ParsecT s u m ()
eof
CSV -> Parser CSV
forall (m :: * -> *) a. Monad m => a -> m a
return CSV
x
record :: Parser Record
record :: Parser [[Char]]
record = (ParsecT [Char] () Identity [Char]
quotedField ParsecT [Char] () Identity [Char]
-> ParsecT [Char] () Identity [Char]
-> ParsecT [Char] () Identity [Char]
forall s u (m :: * -> *) a.
ParsecT s u m a -> ParsecT s u m a -> ParsecT s u m a
<|> ParsecT [Char] () Identity [Char]
field) ParsecT [Char] () Identity [Char]
-> ParsecT [Char] () Identity Char -> Parser [[Char]]
forall s (m :: * -> *) t u a sep.
Stream s m t =>
ParsecT s u m a -> ParsecT s u m sep -> ParsecT s u m [a]
`sepBy` Char -> ParsecT [Char] () Identity Char
forall s (m :: * -> *) u.
Stream s m Char =>
Char -> ParsecT s u m Char
char Char
','
field :: Parser Field
field :: ParsecT [Char] () Identity [Char]
field = ParsecT [Char] () Identity Char
-> ParsecT [Char] () Identity [Char]
forall s u (m :: * -> *) a. ParsecT s u m a -> ParsecT s u m [a]
many ([Char] -> ParsecT [Char] () Identity Char
forall s (m :: * -> *) u.
Stream s m Char =>
[Char] -> ParsecT s u m Char
noneOf [Char]
",\n\r\"")
quotedField :: Parser Field
quotedField :: ParsecT [Char] () Identity [Char]
quotedField = ParsecT [Char] () Identity Char
-> ParsecT [Char] () Identity Char
-> ParsecT [Char] () Identity [Char]
-> ParsecT [Char] () Identity [Char]
forall s (m :: * -> *) t u open close a.
Stream s m t =>
ParsecT s u m open
-> ParsecT s u m close -> ParsecT s u m a -> ParsecT s u m a
between (Char -> ParsecT [Char] () Identity Char
forall s (m :: * -> *) u.
Stream s m Char =>
Char -> ParsecT s u m Char
char Char
'"') (Char -> ParsecT [Char] () Identity Char
forall s (m :: * -> *) u.
Stream s m Char =>
Char -> ParsecT s u m Char
char Char
'"') (ParsecT [Char] () Identity [Char]
-> ParsecT [Char] () Identity [Char])
-> ParsecT [Char] () Identity [Char]
-> ParsecT [Char] () Identity [Char]
forall a b. (a -> b) -> a -> b
$
ParsecT [Char] () Identity Char
-> ParsecT [Char] () Identity [Char]
forall s u (m :: * -> *) a. ParsecT s u m a -> ParsecT s u m [a]
many ([Char] -> ParsecT [Char] () Identity Char
forall s (m :: * -> *) u.
Stream s m Char =>
[Char] -> ParsecT s u m Char
noneOf [Char]
"\"" ParsecT [Char] () Identity Char
-> ParsecT [Char] () Identity Char
-> ParsecT [Char] () Identity Char
forall s u (m :: * -> *) a.
ParsecT s u m a -> ParsecT s u m a -> ParsecT s u m a
<|> ParsecT [Char] () Identity Char -> ParsecT [Char] () Identity Char
forall tok st a. GenParser tok st a -> GenParser tok st a
try ([Char] -> ParsecT [Char] () Identity [Char]
forall s (m :: * -> *) u.
Stream s m Char =>
[Char] -> ParsecT s u m [Char]
string [Char]
"\"\"" ParsecT [Char] () Identity [Char]
-> ParsecT [Char] () Identity Char
-> ParsecT [Char] () Identity Char
forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> Char -> ParsecT [Char] () Identity Char
forall (m :: * -> *) a. Monad m => a -> m a
return Char
'"'))
parseCSV :: FilePath -> String -> Either ParseError CSV
parseCSV :: [Char] -> [Char] -> Either ParseError CSV
parseCSV = Parser CSV -> [Char] -> [Char] -> Either ParseError CSV
forall s t a.
Stream s Identity t =>
Parsec s () a -> [Char] -> s -> Either ParseError a
parse Parser CSV
csv
parseCSVFromFile :: FilePath -> IO (Either ParseError CSV)
parseCSVFromFile :: [Char] -> IO (Either ParseError CSV)
parseCSVFromFile = Parser CSV -> [Char] -> IO (Either ParseError CSV)
forall a. Parser a -> [Char] -> IO (Either ParseError a)
parseFromFile Parser CSV
csv
parseCSVTest :: String -> IO ()
parseCSVTest :: [Char] -> IO ()
parseCSVTest = Parser CSV -> [Char] -> IO ()
forall s t a.
(Stream s Identity t, Show a) =>
Parsec s () a -> s -> IO ()
parseTest Parser CSV
csv
printCSV :: CSV -> String
printCSV :: CSV -> [Char]
printCSV CSV
records = [[Char]] -> [Char]
unlines ([[Char]] -> [Char]
printRecord ([[Char]] -> [Char]) -> CSV -> [[Char]]
forall a b. (a -> b) -> [a] -> [b]
`map` CSV
records)
where printRecord :: [[Char]] -> [Char]
printRecord = [[Char]] -> [Char]
forall (t :: * -> *) a. Foldable t => t [a] -> [a]
concat ([[Char]] -> [Char])
-> ([[Char]] -> [[Char]]) -> [[Char]] -> [Char]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [Char] -> [[Char]] -> [[Char]]
forall a. a -> [a] -> [a]
intersperse [Char]
"," ([[Char]] -> [[Char]])
-> ([[Char]] -> [[Char]]) -> [[Char]] -> [[Char]]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ([Char] -> [Char]) -> [[Char]] -> [[Char]]
forall a b. (a -> b) -> [a] -> [b]
map [Char] -> [Char]
forall {t :: * -> *}. Foldable t => t Char -> [Char]
printField
printField :: t Char -> [Char]
printField t Char
f = [Char]
"\"" [Char] -> [Char] -> [Char]
forall a. [a] -> [a] -> [a]
++ (Char -> [Char]) -> t Char -> [Char]
forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap Char -> [Char]
escape t Char
f [Char] -> [Char] -> [Char]
forall a. [a] -> [a] -> [a]
++ [Char]
"\""
escape :: Char -> [Char]
escape Char
'"' = [Char]
"\"\""
escape Char
x = [Char
x]
unlines :: [[Char]] -> [Char]
unlines = [[Char]] -> [Char]
forall (t :: * -> *) a. Foldable t => t [a] -> [a]
concat ([[Char]] -> [Char])
-> ([[Char]] -> [[Char]]) -> [[Char]] -> [Char]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [Char] -> [[Char]] -> [[Char]]
forall a. a -> [a] -> [a]
intersperse [Char]
"\n"