git.iko.soy/typeable/pandoc
on master help sign in
RST reader: Basic support for csv-table directive.

* Added Text.Pandoc.CSV, simple CSV parser. * Options still not supported, and we need tests. See #3533.

commita5790dd30893cf7143eb64a46fb137caf131a624 authorJohn MacFarlane <jgm@berkeley.edu> dateThu 10 Aug 2017, 11:12 (9 years ago) parente9eaf84
3 files changed+157−1
pandoc.cabal+3−1 src/Text/Pandoc/CSV.hs+102 src/Text/Pandoc/Readers/RST.hs+52
pandoc.cabal+3−1
@@ -324,7 +324,8 @@ Library
324324 doctemplates >= 0.1 && < 0.2,
325325 http-client >= 0.4.30 && < 0.6,
326326 http-client-tls >= 0.2.4 && < 0.4,
327− http-types >= 0.8 && < 0.10
327+ http-types >= 0.8 && < 0.10,
328+ csv-conduit >= 0.6 && < 0.7
328329 if os(windows)
329330 Cpp-options: -D_WINDOWS
330331 else
@@ -465,6 +466,7 @@ Library
465466 Text.Pandoc.Lua.StackInstances,
466467 Text.Pandoc.Lua.Util,
467468 Text.Pandoc.CSS,
469+ Text.Pandoc.CSV,
468470 Text.Pandoc.UUID,
469471 Text.Pandoc.BCP47
470472 Text.Pandoc.Slides,
src/Text/Pandoc/CSV.hsnew file+102
@@ -0,0 +1,102 @@
1+{-
2+Copyright (C) 2017 John MacFarlane <jgm@berkeley.edu>
3+
4+This program is free software; you can redistribute it and/or modify
5+it under the terms of the GNU General Public License as published by
6+the Free Software Foundation; either version 2 of the License, or
7+(at your option) any later version.
8+
9+This program is distributed in the hope that it will be useful,
10+but WITHOUT ANY WARRANTY; without even the implied warranty of
11+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+GNU General Public License for more details.
13+
14+You should have received a copy of the GNU General Public License
15+along with this program; if not, write to the Free Software
16+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17+-}
18+
19+{- |
20+ Module : Text.Pandoc.CSV
21+ Copyright : Copyright (C) 2017 John MacFarlane <jgm@berkeley.edu>
22+ License : GNU GPL, version 2 or above
23+ Maintainer : John MacFarlane <jgm@berkeley.edu>
24+ Stability : alpha
25+ Portability : portable
26+
27+Simple CSV parser.
28+-}
29+
30+module Text.Pandoc.CSV (
31+ CSVOptions,
32+ defaultCSVOptions,
33+ parseCSV,
34+ ParseError
35+) where
36+
37+import Text.Parsec
38+import Text.Parsec.Text (Parser)
39+import Text.Parsec.Error (ParseError)
40+import Data.Text (Text)
41+import qualified Data.Text as T
42+import Control.Monad (void)
43+
44+data CSVOptions = CSVOptions{
45+ csvDelim :: Char
46+ , csvQuote :: Char
47+ , csvKeepSpace :: Bool -- treat whitespace following delim as significant
48+ , csvEscape :: Maybe Char -- default is to double up quote
49+} deriving (Read, Show)
50+
51+defaultCSVOptions :: CSVOptions
52+defaultCSVOptions = CSVOptions{
53+ csvDelim = ','
54+ , csvQuote = '"'
55+ , csvKeepSpace = False
56+ , csvEscape = Nothing }
57+
58+parseCSV :: CSVOptions -> Text -> Either ParseError [[Text]]
59+parseCSV opts t = parse (pCSV opts) "csv" t
60+
61+pCSV :: CSVOptions -> Parser [[Text]]
62+pCSV opts =
63+ (pCSVRow opts `sepEndBy` endline) <* (spaces *> eof)
64+
65+pCSVRow :: CSVOptions -> Parser [Text]
66+pCSVRow opts = notFollowedBy blank >> pCSVCell opts `sepBy` pCSVDelim opts
67+
68+blank :: Parser ()
69+blank = try $ spaces >> (() <$ endline <|> eof)
70+
71+pCSVCell :: CSVOptions -> Parser Text
72+pCSVCell opts = pCSVQuotedCell opts <|> pCSVUnquotedCell opts
73+
74+pCSVQuotedCell :: CSVOptions -> Parser Text
75+pCSVQuotedCell opts = do
76+ char (csvQuote opts)
77+ res <- many (satisfy (\c -> c /= csvQuote opts) <|> escaped opts)
78+ char (csvQuote opts)
79+ return $ T.pack res
80+
81+escaped :: CSVOptions -> Parser Char
82+escaped opts = do
83+ case csvEscape opts of
84+ Nothing -> try $ char (csvQuote opts) >> char (csvQuote opts)
85+ Just c -> try $ char c >> noneOf "\r\n"
86+
87+pCSVUnquotedCell :: CSVOptions -> Parser Text
88+pCSVUnquotedCell opts = T.pack <$>
89+ many (satisfy $ \c -> c /= csvDelim opts && c /= '\r' && c /= '\n')
90+
91+pCSVDelim :: CSVOptions -> Parser ()
92+pCSVDelim opts = do
93+ char (csvDelim opts)
94+ if csvKeepSpace opts
95+ then return ()
96+ else skipMany (oneOf " \t")
97+
98+endline :: Parser ()
99+endline = do
100+ optional (void $ char '\r')
101+ void $ char '\n'
102+
src/Text/Pandoc/Readers/RST.hs+52
@@ -45,6 +45,7 @@ import Text.Pandoc.Builder (fromList, setMeta)
4545import Text.Pandoc.Builder (Blocks, Inlines, trimInlines)
4646import qualified Text.Pandoc.Builder as B
4747import Text.Pandoc.Class (PandocMonad, readFileFromDirs)
48+import Text.Pandoc.CSV (CSVOptions(..), defaultCSVOptions, parseCSV)
4849import Text.Pandoc.Definition
4950import Text.Pandoc.Error
5051import Text.Pandoc.ImageSize (lengthToDim, scaleDimension)
@@ -56,6 +57,8 @@ import Text.Printf (printf)
5657import Data.Text (Text)
5758import qualified Data.Text as T
5859
60+import Debug.Trace
61+
5962-- TODO:
6063-- [ ] .. parsed-literal
6164-- [ ] .. csv-table
@@ -688,6 +691,7 @@ directive' = do
688691 case label of
689692 "table" -> tableDirective top fields body'
690693 "list-table" -> listTableDirective top fields body'
694+ "csv-table" -> csvTableDirective top fields body'
691695 "line-block" -> lineBlockDirective body'
692696 "raw" -> return $ B.rawBlock (trim top) (stripTrailingNewlines body)
693697 "role" -> addNewRole top $ map (\(k,v) -> (k, trim v)) fields
@@ -820,6 +824,54 @@ listTableDirective top fields body = do
820824 takeCells _ = []
821825 normWidths ws = map (/ max 1 (sum ws)) ws
822826
827+ -- TODO
828+ -- [ ] delim:
829+ -- [ ] quote:
830+ -- [ ] keepspace:
831+ -- [ ] escape:
832+ -- [ ] widths:
833+ -- [ ] header-rows:
834+ -- [ ] header:
835+ -- [ ] url:
836+ -- [ ] file:
837+ -- [ ] encoding:
838+csvTableDirective :: PandocMonad m
839+ => String -> [(String, String)] -> String
840+ -> RSTParser m Blocks
841+csvTableDirective top fields rawcsv = do
842+ let res = parseCSV defaultCSVOptions (T.pack rawcsv)
843+ case res of
844+ Left e -> do
845+ throwError $ PandocParsecError "csv table" e
846+ Right rows -> do
847+ return $ B.rawBlock "rst" $ show rows
848+{-
849+ bs <- parseFromString' parseBlocks body
850+ title <- parseFromString' (trimInlines . mconcat <$> many inline) top
851+ let rows = takeRows $ B.toList bs
852+ headerRowsNum = fromMaybe (0 :: Int) $
853+ lookup "header-rows" fields >>= safeRead
854+ (headerRow,bodyRows,numOfCols) = case rows of
855+ x:xs -> if headerRowsNum > 0
856+ then (x, xs, length x)
857+ else ([], rows, length x)
858+ _ -> ([],[],0)
859+ widths = case trim <$> lookup "widths" fields of
860+ Just "auto" -> replicate numOfCols 0
861+ Just specs -> normWidths $ map (fromMaybe (0 :: Double) . safeRead) $
862+ splitBy (`elem` (" ," :: String)) specs
863+ _ -> replicate numOfCols 0
864+ return $ B.table title
865+ (zip (replicate numOfCols AlignDefault) widths)
866+ headerRow
867+ bodyRows
868+ where takeRows [BulletList rows] = map takeCells rows
869+ takeRows _ = []
870+ takeCells [BulletList cells] = map B.fromList cells
871+ takeCells _ = []
872+ normWidths ws = map (/ max 1 (sum ws)) ws
873+-}
874+
823875-- TODO:
824876-- - Only supports :format: fields with a single format for :raw: roles,
825877-- change Text.Pandoc.Definition.Format to fix