Pencocokan multiline Scala regex dengan pandangan ke depan negatif

Saya sedang menulis DSL menggunakan kombinator parser Scala. Saya baru-baru ini mengubah kelas dasar saya dari StandardTokenParsers menjadi JavaTokenParsers untuk memanfaatkan fitur regex yang menurut saya saya perlukan untuk satu bagian terakhir dari teka-teki. (lihat Mengurai string multiline yang dibatasi menggunakan scala StandardTokenParser)

Apa yang saya coba lakukan adalah mengekstrak blok teks yang dibatasi oleh beberapa karakter ({{ dan }} dalam contoh ini). Blok teks ini dapat mencakup beberapa baris. Apa yang saya miliki sejauh ini adalah:

  def docBlockRE = regex("""(?s)(?!}}).*""".r)
  def docBlock: Parser[DocString] =
      "{{" ~> docBlockRE <~ "}}" ^^ { case str => new DocString(str) }}

di mana DocString adalah kelas kasus di DSL saya. Namun, ini tidak berhasil. Gagal jika saya memberinya makan sebagai berikut:

{{
abc
}}

{{
abc
}}

Saya tidak yakin mengapa ini gagal. Jika saya memasang pembungkus Deubg, miliki pembungkus debug di sekitar parser (http://jim-mcbeath.blogspot.com/2011/07/debugging-scala-parser-combinators.html) Saya mendapatkan yang berikut:

docBlock.apply for token 
   at position 10.2 offset 165 returns [19.1] failure: `}}' expected but end of source found

Jika saya mencoba satu blok yang dibatasi dengan banyak baris:

{{
abc
def
}}

maka itu juga gagal diurai dengan:

docBlock.apply for token 
  at position 10.2 offset 165 returns [16.1] failure: `}}' expected but end of source found

Jika saya menghapus direktif DOTALL (?s) maka saya dapat mengurai beberapa blok baris tunggal (yang tidak banyak membantu saya).

Apakah ada cara untuk menggabungkan ekspresi reguler multi-baris dengan pandangan ke depan negatif?

Satu masalah lain yang saya hadapi dengan pendekatan ini adalah, apa pun yang saya lakukan, pembatas penutup harus berada pada baris terpisah dari teks. Kalau tidak, saya mendapatkan pesan kesalahan yang sama seperti yang saya lihat di atas. Sepertinya pandangan negatif ke depan tidak berjalan sesuai harapan saya.


person melston    schedule 16.07.2014    source sumber


Jawaban (2)


Dalam konteks:

scala> val rr = """(?s).*?(?=}})""".r
rr: scala.util.matching.Regex = (?s).*?(?=}})

scala> object X extends JavaTokenParsers {val r: Parser[String] = rr; val b: Parser[String] = "{{" ~>r<~"}}" ^^ { case s => s } }
defined object X

scala> X.parseAll(X.b, """{{ abc
     | def
     | }}""")
res15: X.ParseResult[String] =
[3.3] parsed: abc
def

Lebih lanjut untuk menunjukkan perbedaan dalam keserakahan:

scala> val rr = """(?s)(.*?)(?=}})""".r.unanchored
rr: scala.util.matching.UnanchoredRegex = (?s)(.*?)(?=}})

scala> def f(s: String) = s match { case rr(x) => x case _ => "(none)" }
f: (s: String)String

scala> f("something }} }}")
res3: String = "something "

scala> val rr = """(?s)(.*)(?=}})""".r.unanchored
rr: scala.util.matching.UnanchoredRegex = (?s)(.*)(?=}})

scala> def f(s: String) = s match { case rr(x) => x case _ => "(none)" }
f: (s: String)String

scala> f("something }} }}")
res4: String = "something }} "

Pandangan ke depan hanya berarti "pastikan ini mengikuti saya, tapi jangan dikonsumsi."

Pandangan ke depan yang negatif berarti memastikan ia tidak mengikuti saya.

person som-snytt    schedule 16.07.2014
comment
Terima kasih banyak. Saya lupa tentang keserakahan .* yang memakan semua teks saya hingga dan termasuk pembatas penutup. Saya pikir pandangan negatif ke depan akan mengatasi hal itu, tetapi ternyata tidak. Pandangan ke depan yang tidak memakan waktu sedikit membingungkan. Saya kira saya harus memikirkan kembali mengapa pandangan ke depan yang negatif tidak berhasil dan pandangan ke depan yang positif berhasil. - person melston; 16.07.2014

Untuk mencocokkan {{the entire bracket}}, gunakan ekspresi reguler ini:

(?s)\{\{.*?\}\}

Lihat pertandingannya di demo.

Untuk mencocokkan {{inside the brackets}}, gunakan ini:

(?s)(?<=\{\{).*?(?=\}\})

Lihat pertandingannya di demo.

Penjelasan

  • (?s) mengaktifkan mode DOTALL, memungkinkan titik cocok dengan garis
  • Pengukur bintang di .*? dibuat "malas" oleh ? sehingga titik hanya cocok sebanyak yang diperlukan. Tanpa ?, .* akan mengambil kecocokan terpanjang, pertama-tama mencocokkan seluruh string kemudian melakukan backtrack hanya sejauh yang diperlukan untuk memungkinkan token berikutnya cocok.
  • (?<=\{\{) adalah pandangan ke belakang yang menegaskan bahwa yang mendahuluinya adalah {{
  • (?=\}\}) adalah pandangan ke depan yang menyatakan bahwa berikut ini adalah }}

Referensi

person zx81    schedule 16.07.2014
comment
Terima kasih. Saya akan melihat ini. Saya cukup sering menangani ekspresi reguler. Mereka tampaknya telah banyak berubah selama sekitar 10 tahun terakhir dan saya belum bisa mengimbanginya. :) Jawaban ini dan som-snytt lebih disukai daripada yang saya temukan. - person melston; 16.07.2014
comment
Baiklah, jika Anda memiliki pertanyaan, beri tahu saya. :) - person zx81; 16.07.2014