
Hi. I'm just learning Parsec and Haskell. It is a great library, and I see how "many" lets you parse 0 or more items, "many1" parses 1 or more items, and "count" parses exactly n items. However, there is no combinator that parses between m and n items. What would be the best implementation for this?

Ashish Agarwal schrieb:
Hi. I'm just learning Parsec and Haskell. It is a great library, and I see how "many" lets you parse 0 or more items, "many1" parses 1 or more items, and "count" parses exactly n items. However, there is no combinator that parses between m and n items. What would be the best implementation for this?
I would write an "upTo" parser with the same type as "count" that parses not exactly but at most n items. Your desired parser is than the concatenated results of "count m p" and "upTo (n - m) p" (achieved by "liftM2 (++)"). For "upTo" a recursive definition seems best (other may come up with tricky combinator application.) "upTo 0 p" (or something less than 0) returns "[]" and "upTo n p" is an "option [] ..." parser of one "p" result followed by the "upTo (n - 1) p" result: "option [] (liftM2 (:) p (upTo (n - 1) p))" HTH Christian Another possibility is to use "many" and check if the resulting list has the desired length (if not fail), but that may consume too many tokens that subsequent parsers are supposed to consume.

Thanks. So I tried upTo n p = liftM2 (:) p (upTo (n-1) p) which doesn't quite work because the recursion does not have a base case. The following gets me closer: upTo n p = if n <= 0 then return [] else liftM2 (:) p (upTo (n-1) p)
parse (upTo 3 digit) "" "123ab" Right "123"
However:
parse (upTo 4 digit) "" "123ab" Left (line 1, column 4): unexpected "a" expecting digit
The semantics of (upTo n p) should be to parse at most n tokens, but if less
than n tokens are available that should still be a successful parse. And the
next token should be the first one upTo failed on.
I attempted to use the "try" parser in various locations but that doesn't
seem to help, or maybe I'm using it incorrectly.
On Sat, Oct 17, 2009 at 5:15 AM, Christian Maeder
Ashish Agarwal schrieb:
Hi. I'm just learning Parsec and Haskell. It is a great library, and I see how "many" lets you parse 0 or more items, "many1" parses 1 or more items, and "count" parses exactly n items. However, there is no combinator that parses between m and n items. What would be the best implementation for this?
I would write an "upTo" parser with the same type as "count" that parses not exactly but at most n items. Your desired parser is than the concatenated results of "count m p" and "upTo (n - m) p" (achieved by "liftM2 (++)").
For "upTo" a recursive definition seems best (other may come up with tricky combinator application.) "upTo 0 p" (or something less than 0) returns "[]" and "upTo n p" is an "option [] ..." parser of one "p" result followed by the "upTo (n - 1) p" result:
"option [] (liftM2 (:) p (upTo (n - 1) p))"
HTH Christian
Another possibility is to use "many" and check if the resulting list has the desired length (if not fail), but that may consume too many tokens that subsequent parsers are supposed to consume.

On Mon, Oct 19, 2009 at 6:22 PM, Ashish Agarwal
The semantics of (upTo n p) should be to parse at most n tokens, but if less than n tokens are available that should still be a successful parse. And the next token should be the first one upTo failed on. I attempted to use the "try" parser in various locations but that doesn't seem to help, or maybe I'm using it incorrectly.
First, you should probably use pattern matching rather than if for your base case/recursive case distinction, it's clearer (at least most Haskellers seems to think it is) :
upTo 0 p = return []
second, option was conceived for these case where you want to try a parser and returns something if it fail :
upTo n p = option [] $ liftM2 (:) p (upTo (n-1) p)
Even then, be careful of putting a try before any multi-token parser that could fail harmlessly during the upTo. -- Jedaï

Right. I took "option" out for some reason. The following definitions have
the desired behavior. I also added a base case to fromTo because otherwise
the call to count would consume m tokens even if n-m < 0, which is probably
not what we'd expect.
upTo :: Int -> GenParser tok st a -> GenParser tok st [a]
upTo n p
| n <= 0 = return []
| otherwise = option [] $ liftM2 (:) p (upTo (n-1) p)
fromTo :: Int -> Int -> GenParser tok st a -> GenParser tok st [a]
fromTo m n p
| n-m < 0 = return []
| otherwise = liftM2 (++) (count m p) (upTo (n-m) p)
Thanks!
On Mon, Oct 19, 2009 at 12:55 PM, Chaddaï Fouché
The semantics of (upTo n p) should be to parse at most n tokens, but if less than n tokens are available that should still be a successful parse. And
On Mon, Oct 19, 2009 at 6:22 PM, Ashish Agarwal
wrote: the next token should be the first one upTo failed on. I attempted to use the "try" parser in various locations but that doesn't seem to help, or maybe I'm using it incorrectly.
First, you should probably use pattern matching rather than if for your base case/recursive case distinction, it's clearer (at least most Haskellers seems to think it is) :
upTo 0 p = return []
second, option was conceived for these case where you want to try a parser and returns something if it fail :
upTo n p = option [] $ liftM2 (:) p (upTo (n-1) p)
Even then, be careful of putting a try before any multi-token parser that could fail harmlessly during the upTo.
-- Jedaï
participants (3)
-
Ashish Agarwal
-
Chaddaï Fouché
-
Christian Maeder