Binary.Get:我怎样才能更彻底地解析复杂的结构?

时间:2014-10-08 00:08:42

标签: parsing haskell dry

我正在解析一个复杂的格式,它变得非常重复。希望有人有一个更好的想法,大约是干燥的3倍。想知道根据字段的数据类型选择正确的原始解析器是否有一些魔力。

data Body = Body { raw :: ByteString }
        | Far { cpuType :: !Word8
                , stdfVer :: !Word8 }
        | Prr { headNum :: !Word8
                , siteNum :: !Word8
                , partFlg :: !Word8
                , numTest :: !Word16
                , hardBin :: !Word16
                , softBin :: !Word16
                , xCoord :: !Word16
                , yCoord :: !Word16
                , testTime :: !Word32
                , partID :: ByteString
                , partTxt :: ByteString
                , partFix :: ByteString }
        deriving (Show)

getPrr :: Get Body
getPrr = do
    headNum <- u1
    siteNum <- u1
    partFlg <- u1
    numTest <- u2
    hardBin <- u2
    softBin <- u2
    xCoord <- u2
    yCoord <- u2
    testTime <- u4
    partID <- getVarLen
    partTxt <- getVarLen
    partFix <- getVarLen
    return Prr {headNum = headNum
            ,siteNum = siteNum
            ,partFlg = partFlg
            ,numTest = numTest
            ,hardBin = hardBin
            ,softBin = softBin
            ,xCoord = xCoord
            ,yCoord = yCoord
            ,testTime = testTime
            ,partID = partID
            ,partTxt = partTxt
            ,partFix = partFix }

1 个答案:

答案 0 :(得分:2)

使用Applicative

getPrr :: Get Body
getPrr = Prr <$> u1 <*> u1 <*> u1 <*> u2 <*> u2 <*> u2 <*> u2 <*> u2 <*> u4 <*> getVarLen <*> getVarLen <*> getVarLen

让我们看看它是如何工作的:

所以我们从:

开始
Prr :: Word8 -> Word8 -> Word8 -> Word16 -> Word16 -> Word16 -> Word16 -> Word16 -> Word32 -> ByteString -> ByteString -> ByteString -> Body
u1 :: Get Word8
u2 :: Get Word16
u4 :: Get Word32
getVarLen :: Get ByteString

我们的运营商:

(<$>) :: Functor f => (a -> b) -> f a -> f b -- aka `fmap`
(<*>) :: Applicative f => f (a -> b) -> f a -> f b

因此,我们逐一合并这些部分

Prr <$> u1
    :: Get (Word8 -> Word8 -> Word16 -> Word16 -> Word16 -> Word16 -> Word16 -> Word32 -> ByteString -> ByteString -> ByteString -> Body)
Prr <$> u1 <*> u1
    :: Get (Word8 -> Word16 -> Word16 -> Word16 -> Word16 -> Word16 -> Word32 -> ByteString -> ByteString -> ByteString -> Body)
Prr <$> u1 <*> u1 <*> u1
    :: Get (Word16 -> Word16 -> Word16 -> Word16 -> Word16 -> Word32 -> ByteString -> ByteString -> ByteString -> Body)
Prr <$> u1 <*> u1 <*> u1 <*> u2
    :: Get (Word16 -> Word16 -> Word16 -> Word16 -> Word32 -> ByteString -> ByteString -> ByteString -> Body)
Prr <$> u1 <*> u1 <*> u1 <*> u2 <*> u2
    :: Get (Word16 -> Word16 -> Word16 -> Word32 -> ByteString -> ByteString -> ByteString -> Body)
Prr <$> u1 <*> u1 <*> u1 <*> u2 <*> u2 <*> u2
    :: Get (Word16 -> Word16 -> Word32 -> ByteString -> ByteString -> ByteString -> Body)
Prr <$> u1 <*> u1 <*> u1 <*> u2 <*> u2 <*> u2 <*> u2
    :: Get (Word16 -> Word32 -> ByteString -> ByteString -> ByteString -> Body)
Prr <$> u1 <*> u1 <*> u1 <*> u2 <*> u2 <*> u2 <*> u2 <*> u2
    :: Get (Word32 -> ByteString -> ByteString -> ByteString -> Body)
Prr <$> u1 <*> u1 <*> u1 <*> u2 <*> u2 <*> u2 <*> u2 <*> u2 <*> u2
    :: Get (ByteString -> ByteString -> ByteString -> Body)
Prr <$> u1 <*> u1 <*> u1 <*> u2 <*> u2 <*> u2 <*> u2 <*> u2 <*> u2 <*> getVarLen
    :: Get (ByteString -> ByteString -> Body)
Prr <$> u1 <*> u1 <*> u1 <*> u2 <*> u2 <*> u2 <*> u2 <*> u2 <*> u2 <*> getVarLen <*> getVarLen
    :: Get (ByteString -> Body)
Prr <$> u1 <*> u1 <*> u1 <*> u2 <*> u2 <*> u2 <*> u2 <*> u2 <*> u2 <*> getVarLen <*> getVarLen <*> getVarLen
    :: Get Body

值得注意的是,所有Monad都可以是Applicative s(并且在未来版本的GHC中,将需要)。

如果Get没有Applicative个实例(which it does),我们可以 从Monad实例中轻松制作一个:

instance Applicative Get where
  pure = return
  mf <*> ma = do
    f <- mf
    a <- ma
    return $ f a