How can I parse fixed-length, non-delimited integers with attoparsec?

351 views Asked by At

I'm trying to parse two integers from 3 characters using attoparsec. A sample input might look something like this:

341

... which I would like to parse into:

Constructor 34 1

I have two solutions that work but which are somewhat clunky:

stdK :: P.Parser Packet
stdK = do
    P.char '1'
    qstr <- P.take 2
    let q = rExt $ P.parseOnly P.decimal qstr
    n <- P.decimal
    return $ Std q n

stdK2 :: P.Parser Packet
stdK2 = do
    P.char '1'
    qn <- P.decimal
    let q = div qn 10
    let n = rem qn 10
    return $ Std q n

There must be a better way to achieve something as simple as this. Am I missing something?

1

There are 1 answers

0
jub0bs On BEST ANSWER

Your code snippet is far from being self-contained (in particular, imports and the definition of your Packet data type are missing), but you seem to be overcomplicating things.

First, define a parser for one-digit integers. Then, use the latter as a building block for a parser for two-digit integers. After that, use applicative operators to combine those two parsers and define a parser for your custom Packet data type. See below.

Note that you don't need the full power of monads; applicative parsing is sufficient, here.

-- test_attoparsec.hs

{-# LANGUAGE OverloadedStrings #-}

import Control.Applicative ((<$>))
import Data.Attoparsec.Text
import Data.Char

data Packet = Std {-# UNPACK #-} !Int
                  {-# UNPACK #-} !Int
  deriving (Show)

stdK :: Parser Packet
stdK = char '1' *> (Std <$> twoDigitInt <*> oneDigitInt)

twoDigitInt :: Parser Int
twoDigitInt = timesTenPlus <$> oneDigitInt <*> oneDigitInt
  where
    timesTenPlus x y = 10 * x + y

oneDigitInt :: Parser Int
oneDigitInt = digitToInt <$> digit

Tests in GHCi:

λ> :l test_attoparsec.hs
[1 of 1] Compiling Main             ( test_attoparsec.hs, interpreted )
Ok, modules loaded: Main.

λ> :set -XOverloadedStrings 

λ> parseOnly stdK "1341"
Right (Std 34 1)

λ> parseOnly stdK "212"
Left "1: Failed reading: satisfyWith"