我一直盯着自己对这个问题视而不见,我想这可能是一个真正愚蠢的问题。但我必须吞下自己的骄傲。
我有这个组合器解析器,它没有像我想象的那样回溯。我一直在将它简化为一个小例子而没有完全删除上下文。感觉像“foobar” - 例子更难阅读。我走了:
@RunWith(classOf[JUnitRunner])
class ParserBacktrackTest extends RegexParsers with Spec with ShouldMatchers {
override def skipWhitespace = false
lazy val optSpace = opt(whiteSpace)
lazy val number = """\d+([\.]\d+)?""".r
lazy val numWithOptSpace = number <~ optSpace
private def litre = numWithOptSpace <~ ("litre" | "l")
def volume = litre ^^ { case _ => "volume" }
private def namedPieces = numWithOptSpace <~ ("pcs") ^^ { case _ => "explPcs" }
private def implicitPieces = number ^^ { case _ => "implPcs" }
protected def unitAmount = namedPieces | implicitPieces
def nameOfIngredient = ".*".r
def amount = volume | unitAmount
// def amount = unitAmount
protected def ingredient = (amount <~ whiteSpace) ~ nameOfIngredient
describe("IngredientParser") {
it("should parse volume") {
shouldParse("1 litre lime")
}
it("should parse explicit pieces") {
shouldParse("1 pcs lime")
}
it("should parse implicit pieces") {
shouldParse("1 lime")
}
}
def shouldParse(row: String) = {
val result = parseAll(ingredient, row)
result match {
case Success(value, _) => println(value)
case x => println(x)
}
result.successful should be(true)
}
}
所以会发生的是第三次测试失败:
(volume~lime)
(explPcs~lime)
[1.4] failure: string matching regex `\s+' expected but `i' found
1 lime
^
所以似乎litre-parser
消耗了l然后当它找不到任何空间时失败了。但我会认为它会回溯并尝试下一个生产规则。显然implicitPieces
解析器解析了这一行,因为如果删除前面的卷解析器(删除注释),它就会成功
(implPcs~litre lime)
(explPcs~lime)
(implPcs~lime)
为什么不amount
回溯?我误解了什么?
答案 0 :(得分:4)
我只想张贴一个说明我的误解的最小例子。我认为这会成功:
def foo = "foo" | "fo"
def obar = "obar"
def foobar = foo ~ obar
describe("foobar-parser") {
it("should parse it") {
shouldParseWith(foobar, "foobar")
}
}
但是对|
的回溯不会那样。析取解析器将使用“foo”并且永远不会将其恢复。
必须将其标准化,以便将析取移动到顶层:
def workingFooBar = ("foo" ~ obar) | ("fo" ~ obar)
答案 1 :(得分:2)
它没有回溯,因为1 lime
ingredient
从amount
amount
以volume
volume
以litre
和litre
已成功使用1 l
1 lime
所以litre
,volume
和amount
都成功了!这就是为什么整个事情继续ingredient
的第二部分,即whiteSpace
。
HTH!