0

在下面的代码中,我可以使用 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 "*"

为什么会这样?是否有其他方法可以仅解析运算符周围的空白?

4

1 回答 1

4

当我测试您的代码时,2 * 2 * 3解析正确,但2 + 2没有。解析失败是因为解析器*消耗了一些输入并且在该位置未启用回溯,因此无法尝试其他解析器。

由创建的表达式解析器buildExpressionParser尝试依次解析每个运算符,直到一个成功。解析2 + 2时,会出现以下情况:

  • 第一个2由 匹配number。其余的输入是 + 2(注意开头的空格)。
  • 解析器genParser '*' (*)应用于输入。它占用空间,但与字符不匹配+
  • 其他中缀运算符解析器自动失败,因为某些输入被genParser '*' (*).

您可以通过将解析器的关键部分包装在try. 这将保存输入,直到 afterchar s成功。如果char s失败,则buildExpressionParser可以回溯并尝试另一个中缀运算符。

genParser s f = try (whitespace >> char s) >> whitespace >> return f

这个解析器的缺点是,因为它回溯到中缀运算符之前的前导空格之前,它会重复扫描空格。通常在成功匹配后解析空格会更好,就像 OP 的第一个解析器示例一样。

于 2012-11-25T17:24:36.317 回答