在下面的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
- 它预计行结束。
答案 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
始终获胜。