2014-12-16 3 views
5

Potrzebuję wykonać bardziej złożone sprawdzanie składni w dopasowaniu analizatora składni niż pozwala na to standardowa notacja i obecnie robię to w aplikacji funkcji ^^. Próbka uproszczony scenariusz sprawdza zduplikowanych słów kluczowych:Błąd sygnalizacji za pomocą komendatora parowania aplikacji funkcji

def keywords: Parser[List[String]] = "[" ~ repsep(keyword, ",") ~ "]" ^^ { 
    case _ ~ ks ~ _ => 
    ks.groupBy(x => x).filter(_._2.length > 1).keys.toList match { 
     case Nil => ks 
     case x => throw new DuplicateKeywordsException(x) 
    } 
} 

To działa, jak w moim parsera wyjątek, ale chcę niepowodzenie być ujęte jako ParseResult.Failure przechwytywania wejścia gdzie to się stało . Nie mogę wymyślić, jak zasygnalizować to z bloku ^^ lub użyć jakiegoś innego konstruktu, aby osiągnąć ten sam koniec.

Odpowiedz

3

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"}) 
    } 
} 
+0

Brilliant. Jeśli nie jest to jeszcze część głównej biblioteki (nie mogłem jej znaleźć), powinno być. – Jxek

Powiązane problemy