--- /dev/null
+module TSV (
+ parseTSV, formatTSV
+) where
+import Data.List
+
+-- Simple implementation of tab-separated values with a trailing newline and no
+-- quoting support.
+
+splitList :: Eq a => a -> Bool -> [a] -> [[a]]
+splitList delim terminated =
+ let sl [] | terminated = []
+ sl l = case break (== delim) l of
+ (hh, []) -> [hh]
+ (hh, {-delim-} _ : t) -> hh : sl t
+ in sl
+
+-- We use the Eq only to check the validity...
+joinList :: Eq a => a -> Bool -> [[a]] -> [a]
+joinList delim terminated l =
+ if any (any (== delim)) l then error "joinList: item contains delimiter"
+ else if terminated
+ then concatMap (++ [delim]) l
+ else intercalate [delim] l
+
+type TSV = [[String]]
+
+formatTSV :: TSV -> String
+formatTSV = joinList '\n' True . map (joinList '\t' False)
+
+parseTSV :: String -> TSV
+parseTSV = map (splitList '\t' False) . splitList '\n' True