3

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?

jub0bs
  • 60,866
  • 25
  • 183
  • 186
Frank Wang
  • 181
  • 9
  • 2
    Why do you have a hardcoded `char '1'` at the start of both the parsers? That wouldn't parse your sample input of `"341"`. – Dogbert Jun 08 '15 at 08:36
  • Sorry, I didn't explain that. The actual input would look like 1341 but the first character is just a flag for the type of data that follows. – Frank Wang Jun 08 '15 at 08:38

1 Answers1

1

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"
jub0bs
  • 60,866
  • 25
  • 183
  • 186