不容易,因爲成功的匹配不會重試。舉個例子:
object X extends RegexParsers {
def p = ("a" | "aa" | "aaa" | "aaaa") ~ "ab"
}
scala> X.parseAll(X.p, "aaaab")
res1: X.ParseResult[X.~[String,String]] =
[1.2] failure: `ab' expected but `a' found
aaaab
^
的第一場比賽是成功的,在括號內的解析器,因此它繼續到下一個。那一個失敗了,所以p
失敗。如果p
是替代匹配的一部分,則可以嘗試替代方法,所以訣竅是生成一些可以處理這類事情的東西。
比方說,我們有這樣的:
def nonGreedy[T](rep: => Parser[T], terminal: => Parser[T]) = Parser { in =>
def recurse(in: Input, elems: List[T]): ParseResult[List[T] ~ T] =
terminal(in) match {
case Success(x, rest) => Success(new ~(elems.reverse, x), rest)
case _ =>
rep(in) match {
case Success(x, rest) => recurse(rest, x :: elems)
case ns: NoSuccess => ns
}
}
recurse(in, Nil)
}
然後,您可以使用它像這樣:
def p = nonGreedy("a", "ab")
順便說一句,我總是發現,尋找其他的事情是如何定義是有幫助試圖想出像上面的nonGreedy
這樣的東西。特別是,看看如何定義rep1
,以及如何更改以避免重新評估其重複參數 - 同樣的事情可能會在nonGreedy
上有用。
下面是一個完整的解決方案,稍作修改以避免使用「終端」。
trait NonGreedy extends Parsers {
def nonGreedy[T, U](rep: => Parser[T], terminal: => Parser[U]) = Parser { in =>
def recurse(in: Input, elems: List[T]): ParseResult[List[T]] =
terminal(in) match {
case _: Success[_] => Success(elems.reverse, in)
case _ =>
rep(in) match {
case Success(x, rest) => recurse(rest, x :: elems)
case ns: NoSuccess => ns
}
}
recurse(in, Nil)
}
}
class Arith extends RegexParsers with NonGreedy {
// Just to avoid recompiling the pattern each time
val select: Parser[String] = "(?i)SELECT".r
val from: Parser[String] = "(?i)FROM".r
val token: Parser[String] = "(\\s*)\\w+(\\s*)".r
val eof: Parser[String] = """\z""".r
def selectstatement: Parser[Any] = selectclause(from) ~ fromclause(eof)
def selectclause(terminal: Parser[Any]): Parser[Any] =
select ~ tokens(terminal)
def fromclause(terminal: Parser[Any]): Parser[Any] =
from ~ tokens(terminal)
def tokens(terminal: Parser[Any]): Parser[Any] =
nonGreedy(token, terminal)
}
好像我們正在處理[用PEG功能(https://en.wikipedia.org/wiki/Parsing_expression_grammar#Operational_interpretation_of_parsing_expressions)位置:雖然正則表達式匹配器可以通過匹配貪婪地啓動,但會再原路返回如果它們失敗並嘗試更短的匹配,並且CFG嘗試所有可能性,那麼PEG的'*','+'和'?'操作符總是表現得貪婪,消耗盡可能多的輸入並且永不回溯:表達式'a *'將始終消耗爲許多a在輸入字符串中連續可用,導致'(a * a)'總是失敗。 –