在`rep`中无效Parse的返回错误

时间:2014-08-12 22:15:29

标签: scala parser-combinators

在下面的DSL中,我成功解析了" foo",然后是conj ~ noun的0次或更多次重复。

object Foo extends JavaTokenParsers { 

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

  lazy val expr  = word("foo") ~ rep(conj ~ noun)

  val noun   = word("noun")
  val conj   = word("and") | err("not a conjunction!")

}

信用:感谢Travis Brown解释了对word函数here的需求。

在测试无效连接时看起来不错。

scala> Foo.parseAll(Foo.expr, "foo an3 noun")
res29: Foo.ParseResult[Foo.~[String,List[Foo.~[java.io.Serializable,String]]]] =
[1.5] error: not a conjunction!

foo an3 noun
    ^

但是,另一项测试表明它不起作用 - foo and noun应该会成功。

scala> Foo.parseAll(Foo.expr, "foo and noun")
res31: Foo.ParseResult[Foo.~[String,List[Foo.~[java.io.Serializable,String]]]] =
[1.13] error: not a conjunction!

foo and noun
            ^

由于这个传入的字符串只包含foo and noun ,我不确定正在读取的其他字符/标记。

我已将上述err替换为failure,但这也不好:

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

foo a3nd noun
    ^

我相信Parsers#rep解释了上一条failure消息:

def rep[T](p: => Parser[T]): Parser[List[T]] = rep1(p) | success(List())

基于这个出色的answer,我的理解是rep1(p)(其中p为conj ~ noun)将失败,从而产生success(List())(因为失败允许回溯) 。但是,我并不完全确定为什么success(List())没有返回 - 失败消息显示:failure: string matching regex '\z' expected but 'a'' found - 它预计行结束。

1 个答案:

答案 0 :(得分:1)

让我们一步一步地了解解析foo and noun时会发生什么:

  • word("foo")已尝试,它会从输入中匹配并使用foo
  • rep已尝试,
  • 尝试
  • conj
    • word("and")已尝试,它会从输入中匹配并使用and
    • 所以第二个分支(err)甚至没有经过测试,
  • word("noun")已尝试,它会从输入中匹配并使用noun
  • rep开始循环:
    • word("and")已尝试,但不匹配,
    • 因此尝试err,根据其定义,会返回错误,在此处结束解析

如果err不匹配,您实际上并不想要word("and")进行测试,因为它无法匹配,原因很简单:我们已达到EOF。

因此,如果我们有更多输入,那么让我们检测EOF并尝试解析conj。让我们编写一个解析器:

def notEOF: Parser[Unit] = Parser { in =>
    if (in.atEnd) Failure("EOF", in) else Success((), in)
  }

然后:

val conj = notEOF ~> (word("and") | " *".r ~> err("not a conjunction!"))

EOF上,这会返回一个失败,因此rep可以停止循环并返回任何内容。否则,它会尝试解析and而不是错误。请注意,我使用" *".r trick确保err始终获胜。