Ответ 1
Не легко, потому что успешный матч не повторен. Рассмотрим, например:
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)
}