2012-11-13 13 views
12

मैं एक स्कैला पार्सर संयोजक व्याकरण लिख रहा हूं जो न्यूलाइन-सीमांकित शब्द सूचियों को पढ़ता है, जहां सूचियां एक या अधिक रिक्त रेखाओं से अलग होती हैं।स्कैला पार्सर संयोजक और न्यूलाइन-सीमांकित पाठ

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

आपको व्हाइटस्पेस की परिभाषा को फिर से परिभाषित करने के बजाय 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)) 
    } 

} 

फिर, पार्सर combinators overkill यहाँ एक सा है। आप व्यावहारिक रूप से वही चीज़ प्राप्त कर सकते हैं (लेकिन सूची के बजाय Arrays के साथ) कुछ आसान के साथ:

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

यह काम करता है यदि शब्द सूचियों के बीच केवल एक खाली रेखा है। यदि _n_ रिक्त रेखाएं हैं तो हम बीच में _n-1_ बोगस खाली सूचियों के साथ समाप्त होते हैं। (बीटीडब्लू: 'skipWhitespace' और' eoi' उदाहरण बहुत उपयोगी हैं।) –

+0

@ डब्ल्यू.पी.एमसीएनिल - मैंने तारों की सूचियों के बीच 'rep1 (eol)' देखने के लिए कोड अपडेट किया। क्या आप इसके लिए जा रहे थे? – DaoWen

+1

'rep1 (eol) 'जो मैं खोज रहा था। धन्यवाद। मुझे पता है कि पार्सर संयोजक यहां अधिक से अधिक हैं। मैंने जानबूझकर प्रदर्शनी के प्रयोजनों के लिए समस्या को सरल बना दिया। –

संबंधित मुद्दे