2012-11-13 4 views
12

Sto scrivendo un parser combinatore grammatica Scala che legge elenchi di parole newline delimitati, in cui le liste sono separate da una o più righe vuote. Data la seguente stringa:combinatori Scala parser e newline testo delimitato

cat 
mouse 
horse 

apple 
orange 
pear 

mi piacerebbe averlo tornare List(List(cat, mouse, horse), List(apple, orange, pear)).

Ho scritto questa grammatica di base che tratta elenchi di parole come parole di nuova riga-delimitato. Si noti che ho dovuto ignorare la definizione predefinita di 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)) 
    } 
} 

considera questo modo non corretto righe vuote come elenchi di parole vuote, cioè restituisce

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

(Si noti l'elenco vuoto al centro.)

posso porre fine facoltativa della linea a la fine di ogni lista.

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

Questo gestisce il caso in cui v'è una sola riga vuota tra le liste, ma ha lo stesso problema con più righe vuote.

Ho provato a cambiare la definizione lists per consentire più delimitatori di fine linea:

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

ma questa pende sull'ingresso sopra.

Qual è la grammatica corretta che gestirà più righe vuote come delimitatori?

risposta

13

Si dovrebbe provare a impostare skipWhitespace a false invece di ridefinire la definizione di spazio bianco. Il problema riscontrato nell'elenco vuoto è causato dal fatto che lo repsep non consuma l'interruzione di riga alla fine dell'elenco. Invece, si dovrebbe analizzare l'interruzione di linea (o forse finire di ingresso) dopo ogni elemento:

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

} 

Poi di nuovo, combinatori parser sono un po 'eccessivo qui. Potresti ottenere praticamente la stessa cosa (ma con le matrici anziché le liste) con qualcosa di molto più semplice:

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

Che funziona se c'è solo una riga vuota tra gli elenchi di parole. Se ci sono linee vuote _n_ finiamo con _n-1_ liste vuote false nel mezzo. (BTW: gli esempi di 'skipWhitespace' e' eoi' sono molto utili.) –

+0

@ W.P.McNeill - Ho aggiornato il codice per cercare 'rep1 (eol)' tra gli elenchi di stringhe. E 'quello che stavi cercando? – DaoWen

+1

'rep1 (eol)' è quello che stavo cercando. Grazie. So che i combinatori di parser sono eccessivi qui. Ho volutamente semplificato il problema ai fini dell'esposizione. –