分析器测试的意外结果

时间:2014-08-13 16:53:35

标签: scala parser-combinators

在以下解析器中:

object Foo extends JavaTokenParsers { 

  def word(x: String) = s"\\b$x\\b".r

  lazy val expr  = (foo ~ (conjGuard ~> rep(conj ~ noun))) | foo

  def noun      = {println("noun"); word("noun")}
  def conj      = {println("conj"); word("and") }
  def conjGuard = {println("conjGuard"); guard(conj) | f }
  def f         = {println("failure"); (" *".r) ~ failure("bad conj!")}

  def foo = {println("foo"); word("foo")}
}

为什么以下示例打印出conj作为最后一次打印输出?它来自哪里?

scala> Foo.parseAll(Foo.expr, "foo and noun")
foo
conjGuard
conj
conj
noun
conj
res71: Foo.ParseResult[java.io.Serializable] = [1.13] parsed: 
        (foo~List((and~noun)))

另外,为什么这个例子中没有foo首先打印出来?

scala> Foo.parseAll(Foo.expr, "foo an3 noun")
conj
failure
foo
res72: Foo.ParseResult[java.io.Serializable] =
[1.5] failure: string matching regex `\z' expected but `a' found

foo an3 noun
    ^

2 个答案:

答案 0 :(得分:2)

  

为什么[it]打印出conj作为最后一次打印输出?

您使用rep组合器,它将尝试连续多次解析conj ~ noun。因此,在解析foo and noun之后,它将尝试再次解析conjconj将失败,因为没有更多输入,并且rep将不会失败并且只返回成功的解析

要尝试第二次无法解析conj必须再次请求conj解析器,因此输出中的最后一个conj

  

另外,为什么在这个例子中不首先打印foo?

您使用def(而不是vallazy val)在您使用println的任何地方定义了解析器,并(正确地)思考,否则您只能获得第一个{ {1}},但您与println陷入同一陷阱:lazy val expr中的第一个fooconjGuardconj只会被评估一次。

Foo.expr替换为lazy val,一切恢复正常。


为了更好地了解会发生什么,您可以使用def解析器而不是log

println

例如:

object Foo2 extends JavaTokenParsers { 

  def word(x: String) = s"\\b$x\\b".r

  def expr  = (foo ~ (conjGuard ~> rep(conj ~ noun))) | foo

  def noun      = log(word("noun"))("noun")
  def conj      = log(word("and"))("conj")
  def conjGuard = log(guard(conj) | f)("conjGuard")
  def f         = log((" *".r) ~ failure("bad conj!"))("failure")

  def foo = log(word("foo"))("foo")
}

答案 1 :(得分:1)

  

为什么以下示例打印出最后一个打印输出?它来自哪里?

它再次尝试conj(在rep(conj ~ noun)中),因为可能需要将另一个重复添加到列表中。即使考虑到输入已经结束,对conj的这个调用可能会返回一个解析器,该解析器在空输入时成功。

  

另外,在这个例子中,为什么不首先打印出来?

然而,这对我来说也是令人惊讶的。