あんまり面白くなかった(小並感)
module Data.LTSV (parseLTSV, parseRecord) where
import qualified Data.HashMap.Strict as HM
import Data.Attoparsec
import Control.Applicative
import Control.Monad
import Data.ByteString (ByteString)
import Data.Maybe (catMaybes)
type Record = HM.HashMap ByteString ByteString
type LTSV = [Record]
parseLTSV :: ByteString -> LTSV
parseLTSV = either error id . parseOnly ltsv
parseRecord :: ByteString -> Record
parseRecord = either error id . parseOnly record
ltsv :: Parser LTSV
ltsv = liftM catMaybes $ option Nothing (Just <$> record) `sepBy` nl
record :: Parser Record
record = HM.fromList <$> field `sepBy` word8 0x09
field :: Parser (ByteString, ByteString)
field = (,) <$> label <*> (word8 0x3A *> fieldValue)
label :: Parser ByteString
label = takeWhile1 (inClass "0-9A-Za-z_.-")
fieldValue :: Parser ByteString
fieldValue = Data.Attoparsec.takeWhile (inClass "\x01-\x08\x0B\x0C\x0E-\xFF")
nl :: Parser ()
nl = option () (void $ word8 0x0D) *> void (word8 0x0A)