我想做一个简单的解析器来解析加法表达式。下面是我的代码:
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"
但是这里是输出
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
但是如果我改变expr
组合子的顺序为
expr :: Parser Expr
expr = try add <|> try number
然后输出变为
ghci> p "1+2"
Right (Add (Number 1.0) (Number 2.0))
为什么会发生这种情况?我认为try
关键字强制我正在组合的解析器在每个<|>
之后重新启动。
我打算把它做得更大,所以我想确定我理解为什么现在会发生这种情况。
我的实际程序已经更大了,但这仍然独立地引起问题。
您面临的问题是,当字符串"1+2"
用number
解析时,它接替(不可否认,有一些未解析的字符)。try
的使用只有在失败时才有意义。
也许另一种显示这一点的方法是考虑示例try (string "a") <|> try (string "ab")
。这将成功匹配任何以字符a
开头的字符串,但它永远不会匹配以字符"ab"
开头的字符串。
如果你试过了
exprAll :: Parser Expr
exprAll = try (number <* eof) <|> try (add <* eof)
那么你可能会得到你想要的行为。在这种情况下,"try"d解析器在到达文件结束字符之前不会成功,所以当遇到+
时,number <* eof
的解析尝试失败,然后使用add <* eof
重新开始解析。