Attoparsec: Limiting Parsers to N Bytes, or Combing Parsers?

Suppose we want to parse a 24-bit hex color value: input :: ByteString input = "af093c blah blah blah" type Color = (Word8, Word8, Word8) Attoparsec.Char8 exports a nice hexadecimal parser, but it consumes all available hex-flavored input. I'd like to make it consume exactly two bytes, so I could write my color parser like this: color :: Parser Color color = do r <- hex2 g <- hex2 b <- hex2 return $ Color (r, g, b) hex2 :: Parser Word8 hex2 = ??? So my question is "how do I write hex2?" I could easily rewrite hexadecimal, but it would be nicer to reuse its hex-handling logic. Best, Mike S Craig (908) 328 8030

On Fri, Sep 23, 2011 at 8:04 PM, Michael Craig
Suppose we want to parse a 24-bit hex color value: input :: ByteString input = "af093c blah blah blah" type Color = (Word8, Word8, Word8)
Attoparsec.Char8 exports a nice hexadecimal parser, but it consumes all available hex-flavored input. I'd like to make it consume exactly two bytes, so I could write my color parser like this:
color :: Parser Color color = do r <- hex2 g <- hex2 b <- hex2 return $ Color (r, g, b) hex2 :: Parser Word8 hex2 = ???
So my question is "how do I write hex2?" I could easily rewrite hexadecimal, but it would be nicer to reuse its hex-handling logic.
If it's easy enough to write inline, might as well do so. And it's fun with Applicative :) hex2 = (+) <$> ((*16) <$> higit) <*> higit higit = subtract (fromEnum '0') <$> satisfy isHexDigit color = Color <$> hex2 <*> hex2 <*> hex2

BTW you probably want 'data Color = Color !Word8 !Word8 !Word8'
On Fri, Sep 23, 2011 at 8:21 PM, Evan Laforge
On Fri, Sep 23, 2011 at 8:04 PM, Michael Craig
wrote: Suppose we want to parse a 24-bit hex color value: input :: ByteString input = "af093c blah blah blah" type Color = (Word8, Word8, Word8)
Attoparsec.Char8 exports a nice hexadecimal parser, but it consumes all available hex-flavored input. I'd like to make it consume exactly two bytes, so I could write my color parser like this:
color :: Parser Color color = do r <- hex2 g <- hex2 b <- hex2 return $ Color (r, g, b) hex2 :: Parser Word8 hex2 = ???
So my question is "how do I write hex2?" I could easily rewrite hexadecimal, but it would be nicer to reuse its hex-handling logic.
If it's easy enough to write inline, might as well do so. And it's fun with Applicative :)
hex2 = (+) <$> ((*16) <$> higit) <*> higit higit = subtract (fromEnum '0') <$> satisfy isHexDigit color = Color <$> hex2 <*> hex2 <*> hex2

I agree on all counts! The hex-handling logic here is so straightforward
that it's hardly worth bothering with. In fact, my application's code as it
stands looks very similar to what you wrote. I'm really asking because I
want to be more "fluent" in attoparsec.
So the question remains: is there a way to limit a parser to a finite chunk
of input? Perhaps a way to run the 'take n' parser on the input and then run
another parser on its result? This smells like monadic behavior, but of
course with different semantics than the Monad instance for Parser.
Mike S Craig
(908) 328 8030
On Fri, Sep 23, 2011 at 11:23 PM, Evan Laforge
BTW you probably want 'data Color = Color !Word8 !Word8 !Word8'
On Fri, Sep 23, 2011 at 8:21 PM, Evan Laforge
wrote: On Fri, Sep 23, 2011 at 8:04 PM, Michael Craig
wrote: Suppose we want to parse a 24-bit hex color value: input :: ByteString input = "af093c blah blah blah" type Color = (Word8, Word8, Word8)
Attoparsec.Char8 exports a nice hexadecimal parser, but it consumes all available hex-flavored input. I'd like to make it consume exactly two bytes, so I could write my color parser like this:
color :: Parser Color color = do r <- hex2 g <- hex2 b <- hex2 return $ Color (r, g, b) hex2 :: Parser Word8 hex2 = ???
So my question is "how do I write hex2?" I could easily rewrite hexadecimal, but it would be nicer to reuse its hex-handling logic.
If it's easy enough to write inline, might as well do so. And it's fun with Applicative :)
hex2 = (+) <$> ((*16) <$> higit) <*> higit higit = subtract (fromEnum '0') <$> satisfy isHexDigit color = Color <$> hex2 <*> hex2 <*> hex2

So the question remains: is there a way to limit a parser to a finite chunk of input? Perhaps a way to run the 'take n' parser on the input and then run another parser on its result? This smells like monadic behavior, but of course with different semantics than the Monad instance for Parser.
Oh yeah, well you want to lift the results of the sub-computation into the current one. So something like runSub m input = either throw return $ Attoparsec.parse m input That's not the right syntax, but you get the idea. Now you can do: runSub hexNumber =<< Attoparsec.take 2 I've done stuff like this but with more complicated monads. You pull the current state, run the sub-monad then merge its state and results into the current one. And this is similar to what ErrorT does in the 'catch' function. You want to set up the initial state of the sub parser more carefully of course so you don't get confusing line numbers in error msgs. And I imagine it's less efficient than doing the parsing in one go since you have to deal with stuff twice.

On 24/09/11 05:21, Evan Laforge wrote:
hex2 = (+)<$> ((*16)<$> higit)<*> higit higit = subtract (fromEnum '0')<$> satisfy isHexDigit color = Color<$> hex2<*> hex2<*> hex2
How is "subtract (fromEnum '0')" supposed to convert a hex digit to an Int or Word8? I think you need digitToInt (or an equivalent function) for that. Twan

Evan Laforge wrote:
hex2 = (+)<$> ((*16)<$> higit)<*> higit higit = subtract (fromEnum '0')<$> satisfy isHexDigit color = Color<$> hex2<*> hex2<*> hex2
Twan van Laarhoven wrote:
How is "subtract (fromEnum '0')" supposed to convert a hex digit to an Int or Word8? I think you need digitToInt (or an equivalent function) for that.
Right. How about: color = Color <$> hex2 <*> hex2 <*> hex2 hex2 = mkHex2 <$> hexit <*> hexit hexit = satisfy isHexDigit mkHex2 h1 h0 = fst . head $ readHex [h1, h0] Or, if you are fanatic about safety like I am, you might even write: mkHex2 h1 h0 = maybe 0 fst . listToMaybe $ readHex [h1, h0] Regards, Yitz
participants (4)
-
Evan Laforge
-
Michael Craig
-
Twan van Laarhoven
-
Yitzchak Gale