Я пишу грамматику комбинатора парсера Scala, которая читает списки слов с разделителями строк, в которых списки разделены одной или несколькими пустыми строками. Учитывая следующую строку:
cat
mouse
horse
apple
orange
pear
Я хотел бы вернуть его List(List(cat, mouse, horse), List(apple, orange, pear))
.
Я написал эту основную грамматику, которая рассматривает списки слов как слова, разделенные линией. Обратите внимание, что мне пришлось переопределить определение whitespace
по умолчанию.
import util.parsing.combinator.RegexParsers
object WordList extends RegexParsers {
private val eol = sys.props("line.separator")
override val whiteSpace = """[ \t]+""".r
val list: Parser[List[String]] = repsep( """\w+""".r, eol)
val lists: Parser[List[List[String]]] = repsep(list, eol)
def main(args: Array[String]) {
val s =
"""cat
|mouse
|horse
|
|apple
|orange
|pear""".stripMargin
println(parseAll(lists, s))
}
}
Это неправильно обрабатывает пустые строки как пустые списки слов, т.е. возвращает
[8.1] parsed: List(List(cat, mouse, horse), List(), List(apple, orange, pear))
(Обратите внимание на пустой список посередине.)
Я могу поместить необязательный конец строки в конце каждого списка.
val list: Parser[List[String]] = repsep( """\w+""".r, eol) <~ opt(eol)
Это обрабатывает случай, когда есть одна пустая строка между списками, но имеет ту же проблему с несколькими пустыми строками.
Я попытался изменить определение lists
, чтобы разрешить несколько ограничителей конца строки:
val lists:Parser[List[List[String]]] = repsep(list, rep(eol))
но это зависает на указанном выше входе.
Какова правильная грамматика, которая будет обрабатывать несколько пустых строк в качестве разделителей?