Van*_*uel 1 parsing haskell parsec
在下面的代码中,我可以使用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"
Run Code Online (Sandbox Code Playgroud)
但是,在尝试仅解析运算符之前和之后的空格时,我得到一个解析错误:
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"
Run Code Online (Sandbox Code Playgroud)
解析错误是:
$ ./parsec_example < <(echo "2 * 2 * 3")
"(stdin)" (line 2, column 1):
unexpected end of input
expecting "*"
Run Code Online (Sandbox Code Playgroud)
为什么会这样?有一些其他的方法来分析周围的白色空间只是经营者?
当我测试你的代码时,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
Run Code Online (Sandbox Code Playgroud)
这个解析器的缺点是,因为它在中缀运算符之前回溯到前导空格之前,它会重复扫描空格.在成功匹配后解析空格通常更好,就像OP的第一个解析器示例一样.