2013-07-03 2 views
6

Je voudrais implémenter un analyseur pour un langage défini à l'aide de Scala Parser Combinators. Cependant, le logiciel qui va compiler la langue n'implémente pas toutes les fonctionnalités de la langue, donc je voudrais échouer si ces fonctionnalités sont utilisées. J'ai essayé de forger un petit exemple ci-dessous:Erreurs et échecs dans les combinateurs Scala Parser

object TestFail extends JavaTokenParsers { 
    def test: Parser[String] = 
    "hello" ~ "world" ^^ { case _ => ??? } | 
    "hello" ~ ident ^^ { case "hello" ~ id => s"hi, $id" } 
} 

à savoir, l'analyseur gain de cause sur « bonjour » + un certain identifiant, mais échoue si l'identifiant est « monde ». Je vois qu'il existe des parseurs fail() et err() dans la classe Parsers, mais je n'arrive pas à comprendre comment les utiliser, car ils retournent Parser [Nothing] au lieu d'un String. La documentation ne semble pas couvrir ce cas d'utilisation ...

Répondre

7

Dans ce cas, vous voulez err, pas failure, puisque si le premier analyseur d'une disjonction échoue, vous passerez juste à la seconde, ce qui n'est pas ce que vous vouloir.

L'autre problème est que ^^ est l'équivalent de map, mais vous voulez flatMap, puisque err("whatever") est un Parser[Nothing], pas Nothing. Vous pouvez utiliser la méthode flatMap sur Parser, mais dans ce contexte, il est plus idiomatiques d'utiliser le (complètement équivalent) opérateur >>:

object TestFail extends JavaTokenParsers { 
    def test: Parser[String] = 
    "hello" ~> "world" >> (x => err(s"Can't say hello to the $x!")) | 
    "hello" ~ ident ^^ { case "hello" ~ id => s"hi, $id" } 
} 

Ou, un peu plus simplement:

object TestFail extends JavaTokenParsers { 
    def test: Parser[String] = 
    "hello" ~ "world" ~> err(s"Can't say hello to the world!") | 
    "hello" ~ ident ^^ { case "hello" ~ id => s"hi, $id" } 
} 

Les deux approches devraient fais ce que tu veux.

+0

C'est exactement ce que je cherchais. Les opérateurs >>, ~> (et <~) sont-ils documentés quelque part (en dehors du Scaladoc qui n'était pas assez détaillé pour moi)? – scand1sk

+0

@ scand1sk: Voir la classe [Parsers # Parser] (http://www.scala-lang.org/api/current/index.html#scala.util.parsing.combinator.Parsers$Parser) pour la documentation. – senia

+1

Je suppose que '" bonjour "~" monde ">>' est une faute de frappe, il devrait y avoir '" bonjour "~>" monde ">>' pour utiliser '$ x'. – senia

3

Vous pouvez utiliser la méthode ^?:

object TestFail extends JavaTokenParsers { 
    def test: Parser[String] = 
    "hello" ~> ident ^? (
     { case id if id != "world" => s"hi, $id" }, 
     s => s"Should not use '$s' here." 
) 
} 
+0

Malheureusement, cette solution serait trop lourde dans mon projet d'analyseur global ... – scand1sk

+0

@ scand1sk: '>>' est la meilleure solution dans votre cas. Mais '^?' Vous permet d'utiliser une méthode d'addition comme ceci: 'case _ ~ id si isValidId (id) =>'. – senia

Questions connexes