2012-11-13 49 views
12

我在写一个Scala分析器组合语法,它读取换行符分隔的单词列表,其中列表由一个或多个空行分隔。鉴于以下字符串: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)) 

但这挂在上述输入。

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

回答

13

您应该尝试将skipWhitespace设置为false而不是重新定义空白的定义。您遇到空列表时遇到的问题是由于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)) 
    } 

} 

话又说回来,解析器组合都有点矫枉过正这里。你可以得到几乎相同的东西(但用数组而不是列表):

s.split("\n{2,}").map(_.split("\n")) 
+0

如果在单词列表之间只有一个空白行,那么这就行得通。如果有_n_空行,我们会在中间以_n-1_伪空列表结束。 (顺便说一句:'skipWhitespace'和'eoi'的例子非常有帮助。) –

+0

@ W.P.McNeill - 我更新了代码以在字符串列表之间寻找'rep1(eol)'。那是你要去的吗? – DaoWen

+1

'rep1(eol)'是我一直在寻找的东西。谢谢。我知道解析器组合器在这里是过度的。为了说明的目的,我故意简化了这个问题。 –