2012-11-13 12 views
12

リストが1つ以上の空白行で区切られた改行区切りの単語リストを読み込むScalaパーサーコンビネーター文法を書いています。私はそれがList(List(cat, mouse, horse), List(apple, orange, pear))を返却したいと思いスカラパーサーコンバイナーと改行文字区切りテキスト

cat 
mouse 
horse 

apple 
orange 
pear 

:次の文字列を考えます。

私は、単語リストを改行で区切られた単語として扱うこの基本文法を書いています。デフォルトの定義whitespaceをオーバーライドしなければならないことに注意してください。それは

[8.1] parsed: List(List(cat, mouse, horse), List(), List(apple, orange, pear)) 

を返します。すなわち

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)) 
    } 
} 

はこの間違って、空の単語リストとして空白行を扱います(途中で空のリストを注意してください。)

私はでラインのオプションの終わりを置くことができます各リストの終わり。

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

これは、リスト間の単一の空白行がある場合を扱うが、複数の空白行と同じ問題があります。

Iは、複数のエンド・オブ・ラインデリミタを可能にする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

単語リストの間に空白行が1つしかない場合は、これは機能します(リストの代わりに配列を使用すると同じことが起こります)。空白行が_n_ある場合、真ん中に_n-1_偽の空リストがあります。 –

+0

@ W.P.McNeill - 文字列のリスト間で 'rep1(eol)'を探すようにコードを更新しました。それはあなたが行ったことですか? – DaoWen

+1

'rep1(eol)'は私が探していたものです。ありがとう。私は、パーサーのコンビネータがここで過労であることを知っています。私は、説明の目的で意図的に問題を単純化しました。 –

関連する問題