LTSV format parser in Haskell

はじめに

少し前から LTSV が話題になっています.ログ出力データの処理がやりやすそうです.

上記サイトに載っているとおり仕様がシンプルですから,パーサの練習問題としてもってこいな気がしました.

attoparsec による実装

ByteString の扱いがうまくできていないような気もしますが,おそらく期待する動作になるはずです.

{-# LANGUAGE OverloadedStrings #-}

import Data.ByteString (ByteString, pack)
import Data.Attoparsec.ByteString
import Data.Word (Word8)
import Control.Applicative ( (<*), (<|>) )

type Field  = (ByteString, ByteString)
type Record = [Field]
type LTSV   = [Record]

tab, cr, lf, colon :: Parser Word8
tab   = word8 9
cr    = word8 13
lf    = word8 10
colon = word8 58

-- |
-- LTSV format parser.
--
-- >>> parseOnly ltsv "aaa:111\tbbb:222\nccc:333\tddd:444"
-- Right [[("aaa","111"),("bbb","222")],[("ccc","333"),("ddd","444")]]
--
ltsv :: Parser LTSV
ltsv = do
    rs <- many' recordNL
    r  <- record
    return $ if null r            -- 0 or 1 を表す方法は...?
                 then rs
                 else rs ++ [r]

recordNL :: Parser Record
recordNL = record <* nl
    where
        nl = (cr >> lf) <|> lf

record :: Parser Record
record = sepBy field tab
    where
        field = do
            l <- label <* colon
            v <- value
            return (pack l, pack v)
        label = many1 $ satisfy $ inClass "0-9A-Za-z_.-"
        value = many' $ satisfy $ \w -> w `notElem` [9, 10, 13]

attoparsec-conduit パッケージと組み合わせることで,ストリーミングデータに適用可能となります. (上記コードの recordNLsinkParser に渡す感じ)

とりあえず

repository