/ / Anwenden Parser-Kombinator auf Fall-Klasse - Scala, Parser-Kombinatoren

Wenden Sie den Parser-Kombinator auf die Fallklasse an - scala, parser-combinators

Ich schaue mir Scala an Parser-Kombinierer eine Zeichenfolge analysieren (keine Zeilenumbrüche, erfundenes Beispiel).

Die Zeichenfolge besteht aus vielen verschiedenen Teilen, die ich getrennt extrahieren und eine Fallklasse auffüllen möchte.

case class MyRecord(foo: String, bar: String, baz: String, bam: String, bat: String)

object MyParser extends scala.util.parsing.combinator.RegexParsers {

val foo: Parser[String] = "foo"
val bar: Parser[String] = "bar"
val baz: Parser[String] = "baz"
val bam: Parser[String] = "bam"
val bat: Parser[String] = "bat"

val expression: Parser[MyRecord] =
foo ~ bar ~ baz ~ bam ~ bat ^^ {
case foo ~ bar ~ baz ~ bam ~ bat => MyRecord(foo, bar, baz, bam, bat)
}

}

Das funktioniert ganz gut, aber gibt es eine Möglichkeit, die Teile der übereinstimmenden Ergebnisse direkt auf die Fallklasse anzuwenden, ohne sie zu dekonstruieren?

val expression: Parser[MyRecord] =
foo ~ bar ~ baz ~ bam ~ bat ^^ MyRecord

Weitere Informationen: Die Zeichenfolge, die ich analysiere, ist ziemlich lang und komplex (in Wirklichkeit ist es eine ganze Datei voller langer komplexer Strings), so dass es nicht in Frage kommt, zu regexp zu wechseln.

Antworten:

5 für die Antwort № 1

Es ist möglich mit Formlos2 Bibliothek. Für gegeben:

 object MyParser extends scala.util.parsing.combinator.RegexParsers
import MyParser._

val foo: Parser[String] = "foo"
val bar: Parser[String] = "bar"
val car: Parser[String] = "car"

case class Record(f: String, b: String, c: String)

Sie können Parser mit generischen kombinieren foldRight intead von ~:

 import shapeless._
object f extends Poly2 {
implicit def parser[T, U <: HList] =
at[Parser[T], Parser[U]]{(a, b) =>
for {aa <- a; bb <- b} yield aa :: bb
}
}

val p: Parser[Record] = (foo :: bar :: car :: HNil)
.foldRight(success(HNil))(f).map(Generic[Record].from)

Ergebnis:

 scala> parseAll(p, "foo bar car").get
res50: Record = Record(foo,bar,car)

P.S. Das Problem mit der eingebauten Scala-Funktionalität ist, dass sie gebaut wurden ~-basierter typisierter Binärbaum, der schwer zu traversieren und zu Tupel abzuflachen ist. Formlos löst dieses Problem - es hat es selbst ::Binärer Baum genannt HListEs ist ähnlich, hat aber interessante Operationen, wie die Umwandlung in Tupel oder Fallklassen (wahrscheinlich Makro-basiert). In diesem Beispiel verwende ich foldLeft Shapeless-Hlist und For-Verständnis zu bauen (erweitert um flatMap Parser), um Parser zu kombinieren, da sie monadische Natur haben. In formlos müssen Sie definieren foldLeft"s Handler als eine Menge von generischen implitits, die generische Eingaben (wie T oder U).

Sie können meine f Objekt, um beliebige Parser typsicher zu kombinieren (Sie können hier sogar verschiedene Typen kombinieren - das ist in Ordnung).


Zweitens, weniger allgemein, so ist:

implicit class as2[A, B](t: Parser[A ~ B]){ def ^^^^[T] (co: (A, B) => T) = t map {tt => val (a ~ b) = tt; co(a, b)} }
implicit class as3[A, B, C](t: Parser[A ~ B ~ C]){ def ^^^^[T] (co: (A, B, C) => T) = t map {tt => val (a ~ b ~ c) = tt; co(a, b, c)} }
...
implicit class as21 ...

Verwendung:

scala> val p = foo ~ bar ~ car ^^^^ Record
p: MyParser.Parser[Record] = Parser ()

scala> parseAll(p, "foo bar car").get
res53: Record = Record(foo,bar,car)

Es ist nicht so cool, benötigt aber keine externen Bibliotheken.