2012-11-13 19 views
12

Piszę gramaturę kombinatoryczną Scalera, która odczytuje listy słów rozdzielanych znakami nowej linii, gdzie listy są oddzielone jedną lub większą liczbą pustych wierszy. Biorąc pod uwagę następujący ciąg:Kombinatory parsera Scala i tekst rozdzielany wierszami

cat 
mouse 
horse 

apple 
orange 
pear 

Chciałbym go zwrócić List(List(cat, mouse, horse), List(apple, orange, pear)).

Napisałem tę podstawową gramatykę, która traktuje listy słów jako słowa rozdzielone wierszami. Zauważ, że musiałem zastąpić domyślną definicję 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)) 
    } 
} 

To niepoprawnie traktuje puste wiersze jako pustych list wyrazów, to znaczy powraca

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

(Uwaga pusty listy w środku).

mogę umieścić opcjonalny końca linii na koniec każdej listy.

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

ten obsługuje przypadek, w którym istnieje jeden pusty wiersz między listami, ale ma ten sam problem z wieloma pustymi wierszami.

Próbuję zmienić definicję lists aby umożliwić wielu wycofanych z linii ograniczniki:

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

ale to wisi na powyższym wejścia.

Jaka jest poprawna gramatyka, która obsłuży wiele pustych linii jako ograniczniki?

Odpowiedz

13

Powinieneś spróbować ustawić skipWhitespace na false zamiast przedefiniować definicję białych znaków. Problem z pustą listą spowodowany jest tym, że repsep nie zużywa linii na końcu listy. Zamiast tego, należy przeanalizować podział wiersza (lub ewentualnie zakończyć wejścia) po każdej pozycji:

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

} 

Potem znowu, kombinatorów parser są trochę przesadą tutaj. Można dostać praktycznie to samo (ale z tablicami zamiast listy) z czymś znacznie prostszym:

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

który działa, jeśli jest tylko jeden pusty wiersz między listami słów. Jeśli istnieją _n_ puste linie, kończymy z pustymi listami _n-1_ po środku. (BTW: przykłady 'skipWhitespace' i' eoi' są bardzo przydatne.) –

+0

@ W.P.McNeill - Zaktualizowałem kod, aby wyszukać 'rep1 (eol)' pomiędzy listami łańcuchów. Czy to właśnie zamierzałeś? – DaoWen

+1

'rep1 (eol)' jest tym, czego szukałem. Dzięki. Wiem, że kombinatory parserów są tu przesadzone. Celowo uprościłem problem do celów ekspozycji. –

Powiązane problemy