Blame src/Text/Pandoc/CSV.hs

Packit Service d2f85f
{-
Packit Service d2f85f
Copyright (C) 2017 John MacFarlane <jgm@berkeley.edu>
Packit Service d2f85f
Packit Service d2f85f
This program is free software; you can redistribute it and/or modify
Packit Service d2f85f
it under the terms of the GNU General Public License as published by
Packit Service d2f85f
the Free Software Foundation; either version 2 of the License, or
Packit Service d2f85f
(at your option) any later version.
Packit Service d2f85f
Packit Service d2f85f
This program is distributed in the hope that it will be useful,
Packit Service d2f85f
but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit Service d2f85f
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit Service d2f85f
GNU General Public License for more details.
Packit Service d2f85f
Packit Service d2f85f
You should have received a copy of the GNU General Public License
Packit Service d2f85f
along with this program; if not, write to the Free Software
Packit Service d2f85f
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
Packit Service d2f85f
-}
Packit Service d2f85f
Packit Service d2f85f
{- |
Packit Service d2f85f
   Module      : Text.Pandoc.CSV
Packit Service d2f85f
   Copyright   : Copyright (C) 2017 John MacFarlane <jgm@berkeley.edu>
Packit Service d2f85f
   License     : GNU GPL, version 2 or above
Packit Service d2f85f
   Maintainer  : John MacFarlane <jgm@berkeley.edu>
Packit Service d2f85f
   Stability   : alpha
Packit Service d2f85f
   Portability : portable
Packit Service d2f85f
Packit Service d2f85f
Simple CSV parser.
Packit Service d2f85f
-}
Packit Service d2f85f
Packit Service d2f85f
module Text.Pandoc.CSV (
Packit Service d2f85f
  CSVOptions(..),
Packit Service d2f85f
  defaultCSVOptions,
Packit Service d2f85f
  parseCSV,
Packit Service d2f85f
  ParseError
Packit Service d2f85f
) where
Packit Service d2f85f
Packit Service d2f85f
import Control.Monad (void)
Packit Service d2f85f
import Data.Text (Text)
Packit Service d2f85f
import qualified Data.Text as T
Packit Service d2f85f
import Text.Parsec
Packit Service d2f85f
import Text.Parsec.Text (Parser)
Packit Service d2f85f
Packit Service d2f85f
data CSVOptions = CSVOptions{
Packit Service d2f85f
    csvDelim     :: Char
Packit Service d2f85f
  , csvQuote     :: Char
Packit Service d2f85f
  , csvKeepSpace :: Bool -- treat whitespace following delim as significant
Packit Service d2f85f
  , csvEscape    :: Maybe Char -- default is to double up quote
Packit Service d2f85f
} deriving (Read, Show)
Packit Service d2f85f
Packit Service d2f85f
defaultCSVOptions :: CSVOptions
Packit Service d2f85f
defaultCSVOptions = CSVOptions{
Packit Service d2f85f
    csvDelim = ','
Packit Service d2f85f
  , csvQuote = '"'
Packit Service d2f85f
  , csvKeepSpace = False
Packit Service d2f85f
  , csvEscape = Nothing }
Packit Service d2f85f
Packit Service d2f85f
parseCSV :: CSVOptions -> Text -> Either ParseError [[Text]]
Packit Service d2f85f
parseCSV opts t = parse (pCSV opts) "csv" t
Packit Service d2f85f
Packit Service d2f85f
pCSV :: CSVOptions -> Parser [[Text]]
Packit Service d2f85f
pCSV opts =
Packit Service d2f85f
  (pCSVRow opts `sepEndBy` endline) <* (spaces *> eof)
Packit Service d2f85f
Packit Service d2f85f
pCSVRow :: CSVOptions -> Parser [Text]
Packit Service d2f85f
pCSVRow opts = notFollowedBy blank >> pCSVCell opts `sepBy` pCSVDelim opts
Packit Service d2f85f
Packit Service d2f85f
blank :: Parser ()
Packit Service d2f85f
blank = try $ spaces >> (() <$ endline <|> eof)
Packit Service d2f85f
Packit Service d2f85f
pCSVCell :: CSVOptions -> Parser Text
Packit Service d2f85f
pCSVCell opts = pCSVQuotedCell opts <|> pCSVUnquotedCell opts
Packit Service d2f85f
Packit Service d2f85f
pCSVQuotedCell :: CSVOptions -> Parser Text
Packit Service d2f85f
pCSVQuotedCell opts = do
Packit Service d2f85f
  char (csvQuote opts)
Packit Service d2f85f
  res <- many (satisfy (\c -> c /= csvQuote opts &&
Packit Service d2f85f
                              Just c /= csvEscape opts) <|> escaped opts)
Packit Service d2f85f
  char (csvQuote opts)
Packit Service d2f85f
  return $ T.pack res
Packit Service d2f85f
Packit Service d2f85f
escaped :: CSVOptions -> Parser Char
Packit Service d2f85f
escaped opts =
Packit Service d2f85f
  case csvEscape opts of
Packit Service d2f85f
       Nothing -> try $ char (csvQuote opts) >> char (csvQuote opts)
Packit Service d2f85f
       Just c  -> try $ char c >> noneOf "\r\n"
Packit Service d2f85f
Packit Service d2f85f
pCSVUnquotedCell :: CSVOptions -> Parser Text
Packit Service d2f85f
pCSVUnquotedCell opts = T.pack <$>
Packit Service d2f85f
  many (satisfy (\c -> c /= csvDelim opts && c /= '\r' && c /= '\n'
Packit Service d2f85f
                  && c /= csvQuote opts))
Packit Service d2f85f
Packit Service d2f85f
pCSVDelim :: CSVOptions -> Parser ()
Packit Service d2f85f
pCSVDelim opts = do
Packit Service d2f85f
  char (csvDelim opts)
Packit Service d2f85f
  if csvKeepSpace opts
Packit Service d2f85f
     then return ()
Packit Service d2f85f
     else skipMany (oneOf " \t")
Packit Service d2f85f
Packit Service d2f85f
endline :: Parser ()
Packit Service d2f85f
endline = do
Packit Service d2f85f
  optional (void $ char '\r')
Packit Service d2f85f
  void $ char '\n'