OK, postąpiłem zgodnie z radą Erika Meijera podążając za Typami szczęśliwej ścieżki. Patrząc na to, jak ^^
jest zdefiniowana w Programowanie w Scala (która różni się od rzeczywistego kodu), zdałem sobie sprawę, że to w zasadzie tylko Map
funkcja:
def ˆˆ [U](f: T => U): Parser[U] = new Parser[U] {
def apply(in: Input) = p(in) match {
case Success(x, in1) => Success(f(x), in1)
case failure => failure
}
}
Zasadniczo to Parser[T] => Parser[U]
.
sam Parser[T]
jest funkcją Input => ParseResult[T]
i ^^
tylko zdefiniowanie nowego analizatora poprzez dostarczenie sposobu, który apply
na wywołanie albo przekształca Success[T]
do Success[U]
lub po prostu przechodzi wzdłuż Failure
.
Aby osiągnąć cel, jakim jest wstrzyknięcie nowego Failure
podczas mapowania, potrzebuję nowej funkcji odwzorowania, która ma funkcję taką jak f: T => Either[String,U]
, dzięki czemu mogę zasygnalizować komunikat o błędzie lub udane mapowanie. Wybrałem Either z ciągiem znaków, ponieważ Failure po prostu pobiera komunikat tekstowy. Nowa funkcja odwzorowania jest następnie dodawane do Parser[U]
przez domniemany klasy:
implicit class RichParser[+T](p: Parser[T]) {
def ^^? [U](f: T => Either[String,U]): Parser[U] = new Parser[U] {
def apply(in: Input) = p(in) match {
case Success(x, in1) => f(x) match {
case Left(error) => Failure(error,in1)
case Right(x1) => Success(x1,in1)
}
case failure:Failure => failure
case error:Error => error
}
}
}
Teraz keywords
można zdefiniować jako:
def keywords: Parser[List[String]] = "[" ~ repsep(keyword, ",") ~ "]" ^^? {
case _ ~ ks ~ _ =>
ks.groupBy(x => x).filter(_._2.length > 1).keys.toList match {
case Nil => Right(ks)
case x => Left("found duplicate keywords: "+x.reduce[String] { case (a, b) => s"$a, $b"})
}
}
Brilliant. Jeśli nie jest to jeszcze część głównej biblioteki (nie mogłem jej znaleźć), powinno być. – Jxek