Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to read exact N chars with Parsec?

Tags:

haskell

parsec

I'm new to Haskell and Parsec. I wish to parse php-serialize format of string 's:numb:"string";' like

s:12:"123";6789012";

where number is count of chars. So, function looks like:

newtype PhpString = PhpString String

pString :: GenParser Char st PhpString
pString = do { string "s:"
        ; value1 <- many1 digit
        ; string ":\""
        ; value2 <- takeExactNChars (read value1) 
        ; string "\";"      
        ; return $ PhpString value2
    }
    where 
        takeExactNChars n = ???????
like image 950
viorior Avatar asked Mar 18 '13 10:03

viorior


2 Answers

As Sarah mentioned, the idiomatic parsec solution is to use the count combinator:

newtype PhpString = PhpString String

pString :: Parser PhpString
pString = do
  string "s:"
  value1 <- many1 digit
  string ":\""
  value2 <- count (read value1) 
  string "\";"      
  return $ PhpString value2

We can go a bit further and clean this parser up to be a bit more succinct too, if that interests you:

import Control.Applicative (empty)
import Text.Read

pString :: Parser PhpString
pString = do
  len <- readMaybe <$> (string "s:" *> many1 digit)
  case len of
    Just n -> PhpString <$> string ":\"" *> count n anyChar <* string "\";"
    Nothing -> empty

Or perhaps even:

pString :: Parser PhpString
pString =
  readMaybe <$> (string "s:" *> many1 digit) >>=
    maybe empty $ \n ->
      PhpString <$> string ":\"" *> count n anyChar <* string "\";"

empty from Control.Alternative fails the parser, in case the read fails.

like image 131
ocharles Avatar answered Nov 20 '22 04:11

ocharles


I would write it using replicateM from Control.Monad:

import Text.ParserCombinators.Parsec
import Control.Monad (replicateM)

pString :: Parser String
pString = do string "s:"
             n <- fmap read (many1 digit)
             string ":\""         -- Bug fix; you weren't picking up the colon
             s <- replicateM n anyChar
             string "\";"
             return s

Testing it in ghci:

*Main> parse pString "" "s:12:\"123\";6789012\";"
Right "123\";6789012"
like image 41
Chris Taylor Avatar answered Nov 20 '22 04:11

Chris Taylor