RegexParsers的自定义错误

有人可以帮我理解以下行为: parseAll(parseIf,“if bla blablaa”)应该导致。相反,我总是得到字符串匹配regex'是\ b​​'预期但'b'找到。 我想它与空格有关,因为“如果bla是blablaa”(注意开头的空格)会导致相同的行为。我尝试使用StandardTokenParsers,一切正常。但不幸的是,STP不支持正则表达式。 后续问题:我如何改变RegexParsers所以它使用一系列字符串而不是一系列字符?这会使错误报告变得更加容易。

lazy val parseIf = roleGiverIf ~ giverRole

lazy val roleGiverIf =
  kwIf ~> identifier | failure("""A rule must begin with if""")
lazy val giverRole =
  kwIs ~> identifier | failure("""is expected""")

lazy val keyword =
  kwIf | kwAnd | kwThen | kwOf | kwIs | kwFrom | kwTo

lazy val identifier =
  not(keyword) ~ roleEntityLiteral
// ...

def roleEntityLiteral: Parser[String] =
  """([^"\p{Cntrl}\\]|\\[\\/bfnrt]|\\u[a-fA-F0-9]{4})\S*""".r 
def kwIf: Parser[String] = "If\\b".r
def kwIs: Parser[String] = "is\\b".r

// ...

parseAll(parseIf, "If bla blablaa") match {
  case Success(parseIf, _) => println(parseIf)
  case Failure(msg, _) => println("Failure: " + msg)
  case Error(msg, _) => println("Error: " + msg)
1

1 答案

这个问题很奇怪。当您调用 | 并且双方都失败时,选择了失败发生在 last 的一侧,并且有利于左侧的那个。

当您尝试使用 giverRole 直接解析时,它会产生您期望的结果。但是,如果在失败之前添加成功匹配,则会产生您看到的结果。

原因是相当微妙的 - 我只是通过在所有解析器上撒上 log 语句来找到它。要理解它,您必须了解 RegexParser 如何跳过空格。具体来说,在 accept 上跳过空格。因为 failure 不会调用 accept ,所以它不会跳过空格。

虽然 kwIs 的失败发生在 b 上,但是当跳过空格时, failure 的失败在之后的空格上发生 >如果</代码> 的。这里:

If bla blablaa
   ^ kwIs fails here
  ^ failure fails here

因此, kwIs 上的错误消息优先于我提到​​的规则。

您可以通过使解析器跳过空格而不匹配任何内容来解决此问题。重要的是,此模式始终匹配,否则您将收到更加混乱的错误消息。这是我认为有效的建议:

"\\b|$".r ~ failure("is expected")

另一种解决方案是使用 acceptIfacceptMatch 而不是使用隐式正则表达式接受,在这种情况下,您可以提供定制的错误消息。

0
额外
@awertos我终于找到了问题所在。第一个解决方案比我想象的更合适 - 我只是通过不消耗任何非空格字符来改变它以使错误出现在适当的位置。
额外 作者 Daniel C. Sobral,
我编写了一个具有正则表达式和词法能力的解析器,并按照你的建议使用了acceptIf。但是上面的例子不能按预期工作仍然很奇怪。谢谢你的帮助
额外 作者 awertos,