3
votes

I'm using Parsec to parse some expressions (see this question for more context), and most relevant part of my code is:

statement :: Parser Stmt
statement = assignStmt <|> simpleStmt

assignStmt :: Parser Stmt
assignStmt =
    do var  <- identifier
       reservedOp "="
       expr <- expression
       return $ Assign var expr

simpleStmt :: Parser Stmt
simpleStmt =
    do expr <- expression
       return $ Simple expr

In action:

boobla> foo = 100 + ~100
167
boobla> foo
parser error: (line 1, column 4):
unexpected end of input
expecting letter or digit or "="

Second expression should have evaluated to 167, value of foo.

I thought that when Parsec would try to extract token reservedOp "=", it should have failed because there is no such token in the string, then it was to try second function simpleStmt and succeed with it. But it works differently: it expects more input and just throws this exception.

What should I use to make assignStmt fail if there is no more characters in the string (or in current line). foo = 10 should be parsed with assignStmt and foo should be parsed with simpleStmt.

1
I think you need to say try assignStmt <|> simpleStmt. Look up the documentation for try. - MathematicalOrchid
@MathematicalOrchid, it works. Please post it as an answer with a little description of what's happening. If you don't want to spend your time, just say - I will post it as community wiki :-) - Mark Karpov

1 Answers

8
votes

You're missing the try function.

By default, the <|> operator will try the left parser, and if it fails without consuming any characters, it will try the right parser.

However, if — as in your case — the parser fails after having consumed some characters, and the right parser is never tried. Note that this is often the behavior you want; if you had something like

parseForLoop <|> parseWhileLoop

then if the input is something like "for break", then that's not a valid for-loop, and there's no point trying to parse it as a while-loop, since that will surely fail also.

The try combinator changes this behaviour. Specifically, it makes a failed parser appear to have consumed no input. (This has a space penalty; the input could have been thrown away, but try makes it hang around.)