Conditional parsing and casting in Attoparsec

562 Views Asked by At

I'm parsing a length-encoded binary stream and I'm trying to get this code compiling. The combinatorrent code ( https://github.com/jlouis/combinatorrent/blob/master/src/Protocol/Wire.hs ) was very helpful to get me moving along but now I'm stuck. How do I use the frame_length in the return statement?

data FrameCont = FINAL | MORE | BADCONT
    deriving (Show, Eq)
frame_cont 0x00      = FINAL
frame_cont 0x01      = MORE
frame_cont otherwise = BADCONT

data FrameSize = Small Word8 | Jumbo B.ByteString
    deriving (Show)

get_fc = do
    raw_cont <- AP.anyWord8
    guard((frame_cont raw_cont) /= BADCONT) AP.<?> "State must be either MORE or FINAL"
    return raw_cont

parser = do
    frame_length <- AP.anyWord8
    case frame_length of
        0x255      ->  return (Jumbo <$> AP.take 8, get_fc, AP.take (fromIntegral frame_length))
        otherwise  ->  return (Small otherwise, get_fc, AP.take (fromIntegral frame_length))

Also: how do I use pass (AP.take 8) into a Word64?

1

There are 1 best solutions below

2
On

The first components of the two branches of parser have different types, the Jumbo branch's is a Parser FrameSize, the other a plain FrameSize. If parser should have the type Parser (Parser FrameSize, Parser Word8, Parser ByteString), simply change the first component of the second branch to return (Small otherwise). However, it seems more likely that you want parser :: Parser (FrameSize, Word8, ByteString), so you have to run the other parsers inside parser. I think

parser = do
    frame_length <- AP.anyWord8
    frame_size <- case frame_length of
                    0xFF -> Jumbo <$> AP.take 8   -- 0xFF == 255, 0x255 == 597
                    flen -> return (Small flen)
    fc <- get_fc
    bs <- AP.take (fromIntegral frame_length)
    return (frame_size, fc, bs)

is at least close to what you want.

A note on style: You mix camelCase and underscore_separated words, better settle for one (the predominant style in Haskell is camelCase, so I advise choosing that).