Scala Packrat解析器、错误消息和引用透明度

Scala Packrat解析器、错误消息和引用透明度,scala,parsing,functional-programming,parser-combinators,Scala,Parsing,Functional Programming,Parser Combinators,我正试图使用Scala解析器组合器中的Scala的Packrat解析器将布尔表达式解析为Expr树 sealed trait Expr case class Term(term: String) extends Expr case class And(x: Expr, y: Expr) extends Expr case class Or(x: Expr, y: Expr) extends Expr 这种语法似乎很好: object Parsing extends RegexP

我正试图使用
Scala解析器组合器中的Scala的Packrat解析器将布尔表达式解析为
Expr

  sealed trait Expr
  case class Term(term: String) extends Expr
  case class And(x: Expr, y: Expr) extends Expr
  case class Or(x: Expr, y: Expr) extends Expr
这种语法似乎很好:

object Parsing extends RegexParsers with PackratParsers {

  override val skipWhitespace = false

  val validIdentifiers = List("aaa", "bbb", "ccc", "ddd")

  lazy val term: PackratParser[Term] = """\s*""".r ~> """\w+""".r flatMap { identifier =>
    if (validIdentifiers.contains(identifier))
      success(Term(identifier))
    else
      err(s"expected one of: $validIdentifiers")
  }

  lazy val and: PackratParser[And] =
    expr ~ """\s+and\s+""".r ~ (term | parensExpr) ^^ { case e1 ~ _ ~ e2 => And(e1, e2) }

  lazy val or: PackratParser[Or] =
    expr ~ """\s+or\s+""".r ~ (term | parensExpr) ^^ { case e1 ~ _ ~ e2 => Or(e1, e2) }

  lazy val parensExpr: PackratParser[Expr] = """\s*\(""".r ~> expr <~ """\s*\)""".r

  lazy val expr: PackratParser[Expr] =
    term ||| and ||| or ||| parensExpr

  lazy val root: PackratParser[Expr] =
    phrase(expr)

  def parseExpr(input: String): ParseResult[Expr] =
    parse(root, new PackratReader(new CharSequenceReader(input)))
}
但是,如果它在
右侧发现一个无效的标识符,它将向我们提供这个误导性的错误消息

println(parseExpr("aaa and invalidIdentifier"))

[1.4] failure: end of input expected
aaa and invalidIdentifier
   ^
我很确定会发生这种情况,因为
expr
将尝试所有4个选项:
/
/
parensExpr
将失败,但
term
只需
term(“aaa”)
即可成功

然后,
root
phrase
将启动并检查是否有任何输入剩余可使用,并失败,因为有:“和无效标识符”


所以我想,我要把短语往下推一级。换句话说,我改变了这一点:

  lazy val expr: PackratParser[Expr] =
    term ||| and ||| or ||| parensExpr

  lazy val root: PackratParser[Expr] =
    phrase(expr)
为此:

  lazy val expr: PackratParser[Expr] =
    term ||| and ||| or ||| parensExpr

  lazy val root: PackratParser[Expr] =
    phrase(term) ||| phrase(and) ||| phrase(or) ||| phrase(parensExpr)
现在,所有4个选项都应该失败,但我们应该看到
的错误消息,因为
比其他3个选项消耗更多的输入

我现在得到了更好的错误消息,但令我惊讶的是,一些以前有效的输入现在无效了

println(parseExpr("aaa or bbb"))

[1.4] failure: string matching regex '\s+and\s+' expected but ' ' found
aaa or bbb
   ^
我不明白为什么


事实上,即使只是一个简单的、微不足道的变化,比如:

  // before
  lazy val root: PackratParser[Expr] =
    phrase(expr)

  // after
  lazy val root: PackratParser[Expr] =
    phrase(term ||| and ||| or ||| parensExpr)
。。。中断以前有效的输入

为什么?
root
的这两个定义不应该是等价的吗?这些解析器不是引用透明的吗


更重要的是,我应该如何着手修复此问题?

我发现以下错误与您发布的代码完全一致,我相信这些是预期的错误消息:

@println(Parsing.parsexpr(“无效标识符和aaa”))
[1.18]错误:应为以下列表之一(aaa、bbb、ccc、ddd)
伤残鉴定人和aaa
^
@println(Parsing.parsexpr(“aaa和invalidIdentifier”))
[1.26]错误:应为以下列表之一(aaa、bbb、ccc、ddd)
aaa和伤残鉴定人
^
我正在将ammonite repl与ivy导入的Scala解析器组合器库一起使用:

import $ivy.`org.scala-lang.modules::scala-parser-combinators:1.1.2`

可能是库版本的问题?

不熟悉Packrat,但有一个非常惊人的解析库,它具有与Scala相似的语法:fastparse。如果重构没有那么大的开销,请看一看。我可能会为您省去一些麻烦,因为它有更好的文档。PS:这个问题在那里是可以解决的。@lprakashv当我开始尝试实现它时,我实际上在使用fastprase,但后来意识到fastprase不适合这些语法。我需要语法是左关联的(即,
x和y和z
应该被解析为
(x和y)和z
,而不是
x和(y和z)
),fastparse在左递归语法上永远递归,并溢出堆栈。另一方面,Packrat解析器使用记忆来避免堆栈溢出,非常适合左递归语法。下面有人和我一样在fastparse->Packrat解析器中遇到了同样的问题:我认为左递归语法可以通过一系列步骤来转换,以移除左递归,但我也不知道该怎么做:/这里有一个删除左递归的分步算法:注意:在您的情况下,它可能会导致更难看的树(因为您提到了左关联性),这很奇怪,我也在ammonite上尝试过,我得到了
失败:预期输入结束
错误。。。下面是我的ammonite shell的完整日志:这里只是猜测,但是,请尝试将有效标识符列表和输入字符串中的“aaa”更改为“aaaaa”。也许如果匹配更长,那么
| | |
将赋予它更高的优先级。您使用的是哪个版本的parser combinators库?与您的一样,我甚至复制粘贴了您的ivy命令。你能复制我日志中的命令吗?嘿,我真的用
试过了。。。扩展应用程序…
,这对我来说太糟糕了!我的代码实际上是这样的:
对象解析使用PackratParsers{…
println(parseExpr("aaa and bbb or ccc"))

[1.12] failure: end of input expected
aaa and bbb or ccc
           ^
  // before
  lazy val root: PackratParser[Expr] =
    phrase(expr)

  // after
  lazy val root: PackratParser[Expr] =
    phrase(term ||| and ||| or ||| parensExpr)
import $ivy.`org.scala-lang.modules::scala-parser-combinators:1.1.2`