2012-11-13 15 views
12

Tôi đang viết ngữ pháp kết hợp trình phân tích cú pháp phân tách cú pháp đọc danh sách từ được phân tách bằng dòng mới, trong đó danh sách được phân tách bằng một hoặc nhiều dòng trống. Với chuỗi sau:Bộ phối hợp phân tích cú pháp Scala và văn bản được phân cách bằng dòng mới

cat 
mouse 
horse 

apple 
orange 
pear 

Tôi muốn trả lại số List(List(cat, mouse, horse), List(apple, orange, pear)).

Tôi đã viết ngữ pháp cơ bản này xử lý danh sách từ dưới dạng các từ được phân tách bằng dòng mới. Lưu ý rằng tôi phải ghi đè định nghĩa mặc định là 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)) 
    } 
} 

sai này đối xử với dòng trống như danh sách từ trống rỗng, tức là nó sẽ trả về

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

(Lưu ý danh sách rỗng ở giữa.)

tôi có thể đặt dấu chấm hết tùy chọn của dòng tại cuối mỗi danh sách.

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

Trường hợp này xử lý trường hợp có một dòng trống giữa các danh sách nhưng có cùng vấn đề với nhiều dòng trống.

Tôi đã cố gắng thay đổi định nghĩa lists cho phép nhiều end-of-line delimiters:

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

nhưng điều này treo trên đầu vào ở trên.

Ngữ pháp chính xác sẽ xử lý nhiều dòng trống làm dấu phân tách là gì?

Trả lời

13

Bạn nên thử đặt skipWhitespace thành false thay vì xác định lại định nghĩa khoảng trắng. Vấn đề bạn đang gặp phải với danh sách trống là do thực tế rằng repsep không tiêu thụ ngắt dòng ở cuối danh sách. Thay vào đó, bạn nên phân tích các ngắt dòng (hoặc có thể kết thúc đầu vào) sau mỗi mục:

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

} 

Sau đó, một lần nữa, combinators phân tích cú pháp là một chút quá mức cần thiết ở đây. Bạn có thể nhận được thực tế cùng một điều (nhưng với mảng thay vì danh sách) với một cái gì đó đơn giản hơn nhiều:

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

Điều đó hoạt động nếu chỉ có một dòng trống giữa các danh sách từ. Nếu có _n_ dòng trống, chúng tôi kết thúc với _n-1_ danh sách rỗng không có thật ở giữa. (BTW: các ví dụ 'skipWhitespace' và' eoi' rất hữu ích.) –

+0

@ W.P.McNeill - Tôi đã cập nhật mã để tìm 'rep1 (eol)' giữa các danh sách các chuỗi. Đó là những gì bạn đang đi? – DaoWen

+1

'rep1 (eol)' là những gì tôi đang tìm kiếm. Cảm ơn. Tôi biết rằng các bộ phối hợp phân tích cú pháp quá mức ở đây. Tôi cố tình đơn giản hóa vấn đề cho các mục đích giải trình. –