Re: [Haskell] Parsec question: attempted 'notMatching' combinator

[To Haskell-cafe...] At 16:57 17/02/04 -0500, Andrew Pimlott wrote:
On Tue, Feb 17, 2004 at 07:48:52PM +0000, Graham Klyne wrote:
Thanks! That got me going, though not with quite what you suggested.
I ended up with this: [[ notMatching :: Show a => GenParser tok st a -> GenParser tok st () notMatching p = do { a <- try p ; unexpected (show a) } <|> return () ]] which does the required job for me.
Oops, that does look better. I knew the try had to go somewhere. :-)
The only remaining problem is when p succeeds but does not consume any input, eg eof. In this case, the <|> return () cannot distinguish it from p failing.
I didn't realize at first what a dirty trick this function uses: It distinguishes success or failure of p by whether any input from the first part (before <|> return ()) was consumed. I don't think it is possible (or desirable!) to get this approach 100% right: By "erasing" the success of p, you lose the information you need.
What about a more prosaic implementation:
notFollowedBy' :: Show a => GenParser tok st a -> GenParser tok st () notFollowedBy' p = do res <- do a <- try p; return $ Just a <|> return Nothing case res of Just a -> unexpected (show a) Nothing -> return ()
I don't see why that would work where the above case does not; i.e. when p consumes no input. Or indeed, your later, preferred version: [[ notFollowedBy' :: Show a => GenParser tok st a -> GenParser tok st () notFollowedBy' p = join $ do a <- try p; return (unexpected (show a)) <|> return (return ()) ]]
This works for the tests I've tried, but there's one little quirk with error reporting:
I never really cared so much about that bit! #g --
aNoBC = do char 'a' notFollowedBy' $ do char 'b'; char 'c'
*Main> parseTest (aNoBC >> char 'e') "abe" parse error at (line 1, column 2): unexpected "e" expecting "c" or "e"
It seems that parsec both misreports which token is unexpected (should be "b", and thinks that the failure to match "c" is a problem, even though notFollowedBy' succeeded.
Using your version caused the notMatching parser to be equivalent to: return () presumably, because the failure was protected by the try combinator?
Or perhaps more accurately, the success (of p) was protected by the try! Ie, the unexpected and the try together undid the tell-tale token consumption of p.
Andrew
------------ Graham Klyne For email: http://www.ninebynine.org/#Contact

On Wed, Feb 18, 2004 at 10:48:39AM +0000, Graham Klyne wrote:
[To Haskell-cafe...]
At 16:57 17/02/04 -0500, Andrew Pimlott wrote:
On Tue, Feb 17, 2004 at 07:48:52PM +0000, Graham Klyne wrote:
[[ notMatching :: Show a => GenParser tok st a -> GenParser tok st () notMatching p = do { a <- try p ; unexpected (show a) } <|> return () ]]
notFollowedBy' :: Show a => GenParser tok st a -> GenParser tok st () notFollowedBy' p = do res <- do a <- try p; return $ Just a <|> return Nothing case res of Just a -> unexpected (show a) Nothing -> return ()
I don't see why that would work where the above case does not; i.e. when p consumes no input.
Because (in my version) when p succeeds, the left side of <|> succeeds; in the original, the left side of <|> always failed. Instead of throwing the error immediately after p succeeds, I "package it up" in Just a and throw it later. So there is no possibility for the right side of <|> to be evaluated "by accident", as there was in the original. I think the key is to understand the paragraph in a previous message about the original implementation being a "dirty trick". Andrew
participants (2)
-
Andrew Pimlott
-
Graham Klyne