Reputation: 45
I'm trying to use Parsec to parse bencode strings. The format is 3:abc (number of characters, :, actual string).
I am given the following Parsec functions:
char :: Char -> Parser Char
char c = satisfies (== c) ("character " ++ show c)
-- | Chain two parses, discarding the value of the first parser
pThen :: Parser a -> Parser b -> Parser b
pThen pa pb = parser inner
where
inner input =
case runParser pa input of
Success (_, rest) -> runParser pb rest
Error err -> Error err
-- | Chain two parsers, feeding both the result and the remaining input from the first parser to the second parser.
with :: Parser a -> (a -> Parser b) -> Parser b
with pa f = parser inner
where
inner input =
case runParser pa input of
Success (a, rest) ->
case runParser (f a) rest of
Success (b, remaining) -> success b remaining
Error err -> Error err
Error err -> Error err
-- | Parse a number
number :: Parser Int
number = pMap read (some digit) `expecting` "number"
-- | Parser that consumes a fixed number of characters.
take :: Int -> Parser String
take nr = parser $ \input ->
uncurry success (L.splitAt nr input)
The function signature is string :: Parser String
I tried doing it like this:
string = P.with P.number (P.pThen P.char ':' P.take)
, with the idea being thet I take the integer returned by P.number, process and discard the :, then feed the integer into P.take to consume the required number of characters. What am I doing wrong?
Upvotes: 1
Views: 138
Reputation: 50819
Note that this isn't parsec
. This is some weird homebrew parsing library. I gather this is homework and your professor has provided a set of parsing primitives for you to use? A bunch of definitions seem to be missing (e.g., the definition of Parser
itself, plus satisfies
, pMap
, etc.), but I think I can guess what they do.
Your attempt:
string = P.with P.number (P.pThen P.char ':' P.take)
has all the right components, but the expression P.pThen P.char ':' P.take
is pretty broken. It's passing three arguments to pThen
, which only takes two. You want some parentheses in there:
P.pThen (P.char ':') P.take
This still isn't going to work because P.take
needs an integer argument. You can't just hope that p.with
will magically know where to supply it, so you'll want something explicit like:
P.pThen (P.char ':') (P.take n)
You want to use this expression with P.with
, something like:
P.with P.number (... P.pThen (P.char ':') (P.take n) ...)
but you want the part in parenthesis to be a function that takes the argument supplied by P.with
from the P.number
parser and binds that to variable n
so it can be passed to P.take
. You can do this with a lambda expression, so the following should work:
string = P.with P.number (\n -> P.pThen (P.char ':') (P.take n))
As an alternative syntax that does exactly the same thing, P.pThen
can be surrounded by backticks and used as binary "operator", like so:
string = P.with P.number (\n -> P.char ':' `P.pThen` P.take n)
The name of the pThen
function has been chosen to make this syntax look nice -- "first parse this, pThen
parse that".
Upvotes: 0