Scala PackratParsers不应该回溯吗?

时间:2014-10-19 18:21:20

标签: scala parsing parser-combinators

我有以下代码用于简单的逻辑表达式解析器:

import scala.util.parsing.combinator.RegexParsers
import scala.util.parsing.combinator.PackratParsers


object Parsers extends RegexParsers with PackratParsers

// Entities definition
sealed trait LogicalUnit
case class Variable(name: String) extends LogicalUnit
case class Not(arg: LogicalUnit) extends LogicalUnit
case class And(arg1: LogicalUnit, arg2: LogicalUnit) extends LogicalUnit


import Parsers._

// In order of descending priority
lazy val pattern: PackratParser[LogicalUnit] =
  ((variable) | (not) | (and))

lazy val variable: PackratParser[Variable] =
  "[a-zA-Z]".r ^^ { n => Variable(n) }

lazy val not: PackratParser[Not] =
  ("!" ~> pattern) ^^ { x => Not(x) }

lazy val and: PackratParser[And] =
  ((pattern <~ "&") ~ pattern) ^^ { case a ~ b => And(a, b) }


// Execution
println(Parsers.parseAll(pattern, "!a & !b"))

因此,尝试解析字符串!a & !b并使用

失败
[1.4] failure: string matching regex `\z' expected but `&' found

!a & !b
   ^

根分析器似乎尝试将整个字符串解析为pattern -> not -> variable,并且在发现!a尚未结束时不会回溯,因此pattern -> and不均匀试过。我认为使用PackratParsers应该解决这个问题,但它没有

我做错了什么?

2 个答案:

答案 0 :(得分:4)

一旦成功接受了某些解析器,我认为没有办法让其中一个解析器回溯。如果替代方案成功,则不会尝试其他替代方案。这种行为是这些组合器实现的解析表达式语法的packrat解析方法所固有的(与无上下文语法相反,其中备选方案的顺序不相关,并且回溯行为取决于解析方法)。这就是为什么应该首先给出可能匹配更长输入的替代方案。

关于not与&的优先级,标准方法是在语法规则中编码运算符的优先级和关联性,就像对Context-Free Grammars一样。大多数解析书都会描述如何做到这一点。从幻灯片24开始,您可以在以下注释中看到一个版本:http://www.sci.usq.edu.au/courses/CSC3403/lect/syntax-1up.pdf

答案 1 :(得分:1)

我不知道具体原因,但每当我遇到解析器的这样一个问题时,我就把解析可能性的顺序从最复杂到最简单。

在你的情况下,它将是

lazy val pattern: PackratParser[LogicalUnit] = ((and) | (not) | (variable)),这使您的示例解析。

结果是Not(And(Variable(a),Not(Variable(b)))),这可能不是你想要的。

原因是a & !b是有效模式,因此可以从!a & !b开始解析not

要更改它,您可以引入括号。这是一个简单的可能性:

lazy val not: PackratParser[Not] =
  ("!" ~> term) ^^ { x => Not(x) }

lazy val term: PackratParser[LogicalUnit] = 
  variable | "(" ~> and <~ ")" 

现在结果为And(Not(Variable(a)),Not(Variable(b)))