我有以下代码用于简单的逻辑表达式解析器:
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
应该解决这个问题,但它没有
我做错了什么?
答案 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)))
。