Scala解析器组合器和换行符分隔的文本

W.P*_*ill 13 scala parser-combinators

我正在编写一个Scala解析器组合语法,它读取换行符分隔的单词列表,其中列表由一个或多个空行分隔.给出以下字符串:

cat
mouse
horse

apple
orange
pear
Run Code Online (Sandbox Code Playgroud)

我想让它回来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))
    }
}
Run Code Online (Sandbox Code Playgroud)

这会错误地将空行视为空单词列表,即返回

[8.1] parsed: List(List(cat, mouse, horse), List(), List(apple, orange, pear))
Run Code Online (Sandbox Code Playgroud)

(注意中间的空列表.)

我可以在每个列表的末尾添加一个可选的行尾.

val list: Parser[List[String]] = repsep( """\w+""".r, eol) <~ opt(eol)
Run Code Online (Sandbox Code Playgroud)

这样可以处理列表之间只有一个空行的情况,但是多个空行存在同样的问题.

我尝试更改lists定义以允许多个行尾分隔符:

val lists:Parser[List[List[String]]] = repsep(list, rep(eol))
Run Code Online (Sandbox Code Playgroud)

但这依赖于上面的输入.

将多个空白行作为分隔符处理的正确语法是什么?

Dao*_*Wen 14

你应该尝试设置skipWhitespacefalse代替重新定义空白的定义.您使用空列表时遇到的问题是由于repsep不消耗列表末尾的换行符.相反,您应该在每个项目后解析换行符(或可能是输入结束):

import util.parsing.combinator.RegexParsers

object WordList extends RegexParsers {

  private val eoi = """\z""".r // end of input
  private val eol = sys.props("line.separator")
  private val separator = eoi | eol
  private val word = """\w+""".r

  override val skipWhitespace = false

  val list: Parser[List[String]] = rep(word <~ separator)

  val lists: Parser[List[List[String]]] = repsep(list, rep1(eol))

  def main(args: Array[String]) {
    val s =
      """cat
        |mouse
        |horse
        |
        |apple
        |orange
        |pear""".stripMargin

    println(parseAll(lists, s))
  }

}
Run Code Online (Sandbox Code Playgroud)

然后,解析器组合器在这里有点矫枉过正.你可以得到几乎相同的东西(但使用Arrays而不是Lists)更简单的东西:

s.split("\n{2,}").map(_.split("\n"))
Run Code Online (Sandbox Code Playgroud)