/ / Scala parser combinators a newline-oddelený text - scala, parser-kombinátorov

Kombinátory parserov Scala a textov oddelených od nového riadku - scala, parser-combinators

Píšem gramatiku kombinétorov Scala syntaktického analyzátora, ktorá číta nové zoznamy slov s oddelenými riadkami, kde sú zoznamy oddelené jednou alebo viacerými prázdnymi riadkami. Vzhľadom na nasledujúci reťazec:

cat
mouse
horse

apple
orange
pear

Chcel by som, aby sa vrátil List(List(cat, mouse, horse), List(apple, orange, pear)).

Napísal som túto základnú gramatiku, ktorá zaobchádza s slovnými zoznammi ako slovami oddelenými od novej linky. Všimnite si, že som musel prepísať predvolenú definíciu 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))
}
}

Toto nesprávne považuje prázdne riadky za prázdne zoznamy slov, to znamená, že sa vracia

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

(Poznamenajte si prázdny zoznam v strede.)

Môžem na konci každého zoznamu zadať voliteľný koniec riadku.

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

Toto rieši prípad, keď medzi zoznamami existuje jeden prázdny riadok, ale má rovnaký problém s viacerými prázdnymi riadkami.

Pokúsil som sa zmeniť lists definícia, aby sa umožnili viaceré oddeľovače koncového radu:

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

ale to visí na vyššie uvedenom vstupe.

Aká je správna gramatika, ktorá bude spracovávať viac prázdnych riadkov ako oddeľovačov?

odpovede:

13 pre odpoveď č. 1

Mali by ste sa pokúsiť nastaviť skipWhitespace na false namiesto definovania definície medzery. Problém, ktorý máte s prázdnym zoznamom, je spôsobený tým, že repsep nepoužíva prerušenie riadku na konci zoznamu.Napríklad by ste mali analyzovať prerušenie riadku (alebo prípadne koniec vstupu) po každej položke:

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

}

Znova sú tu aj synchrónne kombinátory. Môžete získať prakticky to isté (ale s Arrayami namiesto zoznamov) s niečím oveľa jednoduchším:

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