Không dễ dàng, vì kết quả khớp thành công không được thử lại. Hãy xem xét, ví dụ:
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
^
Kết quả đầu tiên đã thành công, trong trình phân tích cú pháp bên trong dấu ngoặc đơn, vì vậy nó được chuyển sang bước tiếp theo. Điều đó không thành công, vì vậy p
không thành công. Nếu p
là một phần của các trận đấu thay thế, giải pháp thay thế sẽ được thử, vì vậy mẹo là tạo ra thứ gì đó có thể xử lý những thứ đó.
Hãy nói rằng chúng tôi có điều này:
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)
}
Sau đó bạn có thể sử dụng nó như thế này:
def p = nonGreedy("a", "ab")
Bằng cách này, tôi luôn luôn thấy rằng nhìn vào cách những thứ khác được định nghĩa là hữu ích trong việc cố gắng tìm ra những thứ như nonGreedy
ở trên. Cụ thể, hãy xem cách rep1
được xác định và cách nó được thay đổi để tránh đánh giá lại thông số lặp lại của nó - điều tương tự có thể hữu ích trên nonGreedy
.
Đây là giải pháp đầy đủ, với một chút thay đổi để tránh tiêu thụ "thiết bị đầu cuối".
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)
}
Nó có vẻ như chúng ta đang đối phó [với tính năng PEG] (https://en.wikipedia.org/wiki/Parsing_expression_grammar#Operational_interpretation_of_parsing_expressions) ở đây: Trong khi quẹt biểu thức chính quy có thể bắt đầu bằng cách kết hợp tham lam, nhưng sau đó sẽ quay lại và thử các kết quả ngắn hơn nếu chúng thất bại và CFG cố gắng mọi khả năng, các toán tử '*', '+' và '?' của PEG luôn hoạt động một cách tham lam, tiêu thụ càng nhiều đầu vào càng tốt và không bao giờ lùi lại: Biểu thức 'a *' sẽ luôn sử dụng như nhiều của a như liên tục có sẵn trong chuỗi đầu vào, khiến '(a * a)' thất bại luôn. –