在下面的代码中,我可以使用Parsec在每个标记之后正确解析空格:
whitespace = skipMany (space <?> "")
number :: Parser Integer
number = result <?> "number"
where
result = do {
ds <- many1 digit;
whitespace;
return (read ds)
}
table = result
where
result = [
[Infix (genParser '*' (*)) AssocLeft,
Infix (genParser '/' div) AssocLeft],
[Infix (genParser '+' (+)) AssocLeft,
Infix (genParser '-' (-)) AssocLeft]]
genParser s f = char s >> whitespace >> return f
factor = parenExpr <|> number <?> "parens or number"
where
parenExpr = do {
char '(';
x <- expr;
char ')';
whitespace;
return x
}
expr :: Parser Integer
expr = buildExpressionParser table factor <?> "expression"
但是,在尝试 解析运算符之前和后的 时,我遇到了解析错误:
whitespace = skipMany (space <?> "")
number :: Parser Integer
number = result <?> "number"
where
result = do {
ds <- many1 digit;
return (read ds)
}
table = result
where
result = [
[Infix (genParser '*' (*)) AssocLeft,
Infix (genParser '/' div) AssocLeft],
[Infix (genParser '+' (+)) AssocLeft,
Infix (genParser '-' (-)) AssocLeft]]
genParser s f = whitespace >> char s >> whitespace >> return f
factor = parenExpr <|> number <?> "parens or number"
where
parenExpr = do {
char '(';
x <- expr;
char ')';
return x
}
expr :: Parser Integer
expr = buildExpressionParser table factor <?> "expression"
解析错误是:
$ ./parsec_example < <(echo "2 * 2 * 3")
"(stdin)" (line 2, column 1):
unexpected end of input
expecting "*"
为什么会这样?是否有其他方法来解析只是运算符周围的空白区域?
答案 0 :(得分:5)
当我测试您的代码时,2 * 2 * 3
正确解析,但2 + 2
没有。解析失败,因为*
的解析器消耗了一些输入,并且在该位置没有启用回溯,因此无法尝试其他解析器。
由buildExpressionParser
创建的表达式解析器尝试依次解析每个运算符,直到成功。解析2 + 2
时,会发生以下情况:
2
与number
匹配。输入的其余部分是 + 2
(注意开头的空格)。genParser '*' (*)
应用于输入。它占用空间,但与+
字符不匹配。genParser '*' (*)
消耗了一些输入。您可以通过在try
中包装解析器的关键部分来解决此问题。这将保存输入,直到char s
成功。如果char s
失败,则buildExpressionParser
可以回溯并尝试其他中缀运算符。
genParser s f = try (whitespace >> char s) >> whitespace >> return f
这个解析器的缺点是,因为它在中缀运算符之前回溯到前导空格之前,它会重复扫描空格。通常在成功匹配后解析空格更好,比如OP的第一个解析器示例。