Причина ошибки в том, что line
и eol
определены как обычные поля класса val
s, но они используются в lines
до их определения. Код, который присваивает значения полям класса, выполняется последовательно в конструкторе, и line
и eol
оба по-прежнему null
, когда присваивается lines
.
Чтобы решить эту проблему, определите line
и eol
как lazy val
s или def
s, или просто поместите их перед lines
в коде.
Сам парсер также имеет некоторые проблемы. По умолчанию парсеры Scala автоматически игнорируют все пробелы, включая EOL. Учитывая, что регулярное выражение .*
без каких-либо флагов не включает в себя EOL, line
естественно означает "всю строку до разрыва строки", поэтому вам не нужно анализировать EOL вообще.
Во-вторых, синтаксический анализатор lines
, как он определен, является жадным. Он с удовольствием поглотит все, включая финал ##
. Чтобы остановить его до end
, вы можете, например, использовать комбинатор not
.
Со всеми изменениями парсер выглядит так:
object MyParser extends RegexParsers {
val begin: Parser[String] = "BEGIN"
val line: Parser[String] = """.+""".r
val lines: Parser[Seq[String]] = rep(not(end) ~> line)
val end: Parser[String] = "##"
val document: Parser[Seq[String]] =
begin ~> lines <~ end
}
Вы также можете переопределить поведение пропуска пробелов и анализировать все пробелы вручную. Это включает пробелы после BEGIN
и после ##
:
object MyParser extends RegexParsers {
override def skipWhitespace = false
val eol: Parser[Any] = "\n" | "\r\n" | "\r"
val begin: Parser[String] = "BEGIN" <~ eol
val line: Parser[String] = """.*""".r
val lines: Parser[Seq[String]] = rep(not(end) ~> line <~ eol)
val end: Parser[String] = "##"
val document: Parser[Seq[String]] =
begin ~> lines <~ end <~ whiteSpace
}
Обратите внимание, что line
определяется здесь как .*
вместо .+
. Таким образом, парсер не выйдет из строя, если на входе есть пустые строки.