I'm just starting to learn parsing strings with Parsec, and I'm faced with the following problem which I can't get to wrap my mind around:
The following code contains three parser runs, two of which will obviously fail. The strange thing is that my custom failure message will only occur with the second run, not with the third.
import Text.Parsec
import Text.Parsec.String
ps :: Parser String
ps = (string "123") <|> (string "456") <|> fail "my-failure"
main = do
putStrLn $ "A: " ++ show (parse ps "" "123")
putStrLn $ "\nB: " ++ show (parse ps "" "789")
putStrLn $ "\nC: " ++ show (parse ps "" "45x")
Output:
A: Right "123"
B: Left (line 1, column 1):
unexpected "7"
expecting "123" or "456"
my-failure
C: Left (line 1, column 1):
unexpected "x"
expecting "456"
What would be the correct way to have my failure message occur always when the part left of the second <|>
fails? Can I override any previously occured error?
The <|>
combinator in Parsec only tries the next option when a parser doesn't consume any input. In your case, the parser string "456"
matches the beginning of "45x"
, so no further alternatives are tried. If you need arbitrary look-ahead, you need to use the try
function.
ps :: Parser String
ps = string "123" <|> try (string "456") <|> fail "my-failure"
From Parsec's documentation of <|>
:
This combinator implements choice. The parser p <|> q first applies p. If it succeeds, the value of p is returned. If p fails without consuming any input, parser q is tried. This combinator is defined equal to the mplus member of the MonadPlus class and the (<|>) member of Alternative.
The parser is called predictive since q is only tried when parser p didn't consume any input (i.e.. the look ahead is 1). This non-backtracking behaviour allows for both an efficient implementation of the parser combinators and the generation of good error messages.