[Haskell-cafe] Parsec - Custom Fail

Daniel Fischer daniel.is.fischer at web.de
Tue May 5 13:42:37 EDT 2009


Am Dienstag 05 Mai 2009 17:38:35 schrieb mwinter at brocku.ca:
> Thanks, but I want a nice solution not another, even more complicated,
> workaround.

I'm afraid you're out of luck there.

Parsec carries a ParseError around even for successful parses (where it's a SourcePos and 
and empty list of messages).
When binding two parsers, if the second doesn't consume any input, for the overall result 
it calls mergeErrorReply, which calls mergeError from Text.ParserCombinators.Parsec.Error:

mergeError :: ParseError -> ParseError -> ParseError
mergeError (ParseError pos msgs1) (ParseError _ msgs2)
    = ParseError pos (msgs1 ++ msgs2)

So that doesn't look at the position of the second error :(

You could change the sources of parsec, the least intrusive would probably be to modify 
mergeError:

mergeError :: ParseError -> ParseError -> ParseError
mergeError (ParseError _ []) pe2 = pe2
mergeError (ParseError pos msgs1) (ParseError _ msgs2)
    = ParseError pos (msgs1 ++ msgs2)

or you could employ an ugly workaround like

setPosAndFail :: tok -> SourcePos -> String -> GenParser tok st a
setPosAndFail dummy pos msg = do
    setPosition pos
    inp <- getInput
    setInput (dummy:inp)
    tokenPrim (const "") (\p _ _ -> p) Just
    fail msg

myFail :: SourcePos -> String -> GenParser Char st a
myFail = setPosAndFail 'a'

to pretend you actually consumed some input.
It works:

*TestWorkAround> test
Left (line 100, column 100):
Test

but isn't pretty.


More information about the Haskell-Cafe mailing list