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`