I want to make a simple parser to parse an addition expression. Here is my code:
import Text.Parsec.Char
import Text.Parsec.String
import Text.ParserCombinators.Parsec
data Expr = Number Float |
Add Expr Expr |
number :: Parser Expr
number = do
n <- try $ many1 digit
return $ Number $ read n
add :: Parser Expr
add = do
e1 <- number
char '+'
e2 <- number
return $ Add e1 e2
expr :: Parser Expr
expr = try number <|> try add
p :: String -> Either ParseError Expr
p = parse (do{e <- expr; eof; return e}) "error"
But here is the output
ghci> parse add "err" "1+2"
Right (Add (Number 1.0) (Number 2.0))
ghci> p "1"
Right (Number 1.0)
ghci> p "1+2"
Left "error" (line 1, column 2):
unexpected '+'
expecting digit or end of input
But if I change the order of the expr combinators to
expr :: Parser Expr
expr = try add <|> try number
Then the output changes to
ghci> p "1+2"
Right (Add (Number 1.0) (Number 2.0))
Why does this happen? I thought the try keyword forces the Parsers I am combining to restart after each <|>.
I plan on making this much larger so I want to be sure I understand why this is happening now.
My actual program is larger already but this is still causing a problem independantly.
The problem you're facing is that when the string
"1+2"is parsed withnumber, it succeeds (admittedly, with some unparsed characters). The use oftryonly matters if it had failed.Perhaps another way to show this is to consider the example
try (string "a") <|> try (string "ab"). This will succeed in matching any string starting with the charactera, but it will never match on strings that start with"ab".If you had tried instead
then you may get the behavior you're looking for. In this case, the "try"d parser does not succeed until the end-of-file character is reached, so when the
+is encountered, the parse attempt ofnumber <* eoffails and then parsing starts over usingadd <* eof.