为什么解析器组合器在发生故障时不会回溯?

时间:2016-01-13 23:35:04

标签: error-handling parser-combinators peg

我查看了解析器组合器上的Artima guide,它说我们需要将失败(msg)附加到语法规则中,以使错误报告对用户有意义

def value: Parser[Any] =
    obj | stringLit | num | "true" | "false" | failure("illegal start of value")

这破坏了我对这些解析器中使用的递归机制的理解。一方面,Artima指南有意义地说,如果所有产品都失败,那么解析器将到达返回给用户的failure("illegal start of value")。然而,一旦我们理解语法不是价值选择列表而是树而不是一棵树,它就没有意义了。也就是说,值解析器是在输入处感测到值时调用的节点。这意味着调用解析器(也是父节点)会检测值解析失败并继续使用值兄弟替代。假设所有价值替代方案也都失败了。 Grandparser将尝试其替代品。反过来失败,该过程向上展开,直到起始符号解析器失败。那么,错误信息是什么?似乎最顶层解析器的最后一个替代方案被报告为错误的。

要弄明白,谁是对的,我创建了一个演示,其中program是最顶层的(起始符号)解析器

import scala.util.parsing.combinator._

object ExprParserTest extends App with JavaTokenParsers {

    // Grammar
    val declaration = wholeNumber ~ "to" ~ wholeNumber | ident | failure("declaration not found")
    val term = wholeNumber | ident ; lazy val expr: Parser[_] = term ~ rep ("+" ~ expr)
    lazy val statement: Parser[_] = ident ~ " = " ~ expr | "if" ~ expr ~ "then" ~ rep(statement) ~ "else" ~ rep(statement)
    val program  = rep(declaration) ~ rep(statement)

    // Test
    println(parseAll(program, "1 to 2")) // OK
    println(parseAll(program, "1 to '2")) // failure, regex `-?\d+' expected but `'' found at '2
    println(parseAll(program, "abc")) // OK


}

由于1 to '2额外',它失败了program -> declaration -> num "to" num。是的,它似乎停留在ident规则中,甚至没有尝试failure("declaration not found")declaration -> ident | failure()替代方案!出于同样的原因,我没有回复这些陈述。所以,我的猜测和Artima指南似乎都不适合解析器实际上在做什么。我想知道:解析器组合器中规则感知,回溯和错误报告背后的真正逻辑是什么?为什么错误消息表明没有回溯到statements,也没有回复{{1}}? Artima指南建议将失败()最终置于我们看到或忽略的最终位置,因为回溯逻辑应该是什么?

Isn解析器组合只是一个普通的哑PEG吗?它behaves like predictive parser。我预计它是PEG,因此,起始符号解析器应该返回所有失败的分支,并想知道为什么/如何实际的解析器设法选择最合适的失败。

1 个答案:

答案 0 :(得分:0)

许多解析器组合器都会回溯,除非他们在'或者#39;块。作为速度优化,他们将承诺第一次成功'或'项目而不是回溯。所以1)尽量避免' |'尽可能在你的语法中,2)如果使用' |'是不可避免的,首先放置最长或最不可能匹配的项目。