2014-06-20 2 views
3

Je suis à la recherche d'une fonction pour combiner dynamiquement Scala Parser Combinators. Par exemple, si je veux le faire statiquement Je peux écrire:Quelle serait la fonction pour combiner dynamiquement les combinateurs d'analyseurs de Scala?

def aDirective: Parser[String] = "a" ^^ { case _ => "a" } 
def bDirective: Parser[String] = "b" ^^ { case _ => "b" } 

def combinedDirective: Parser[List[String]] = 
    aDirective ~ bDirective ^^ { case a ~ b => List(a, b) } 

Cependant, au lieu de coder cette statiquement Je veux être en mesure de le faire de façon dynamique, aux fins de générer des combinaisons de parseurs.

Par exemple:

def aDirective: Parser[String] = "a" ^^ { case _ => "a" } 
def bDirective: Parser[String] = "b" ^^ { case _ => "b" } 

def combinedDirective: Parser[List[String]] = 
    combine(List(aDirective, bDirective)) 

def combine(parsers: List[Parser[T]): Parser[List[T]] = ??? 

Je pense Je dois aller dans une liste de parseurs à un Parser de la liste des résultats. J'ai donc essayé d'écrire une signature pour une fonction nommée combine.

Pour l'instant je ne peux pas comprendre comment implémenter la fonction combine. Quel que soit le moyen que j'utilise, il semble que ce soit un problème que je ne sais pas comment résoudre en ce moment. Par exemple, comment puis-je construire un analyseur initial pour un pli? J'ai essayé d'expérimenter diverses constructions foldLeft et reduceLeft, mais je n'arrive pas à y parvenir.

J'utilise Scala 2.11. Des idées?

Répondre

3

C'est un sequencing operation et Scalaz fournit un raccourci (normalement, vous auriez pas besoin de la définition explicite de l'instance passe-partout avec Scalaz, mais this is a special case):

import scala.util.parsing.combinator.RegexParsers 
import scalaz._, Scalaz._ 

object MyParser extends RegexParsers { 
    implicit val pm = std.util.parsing.combinator.parser.parserMonad(this) 

    def aDirective: Parser[String] = "a" ^^ { case _ => "a" } 
    def bDirective: Parser[String] = "b" ^^ { case _ => "b" } 

    def combine[T](parsers: List[Parser[T]]): Parser[List[T]] = parsers.sequenceU 

    def combinedDirective: Parser[List[String]] = 
    combine(List(aDirective, bDirective)) 
} 

Et puis:

scala> MyParser.parseAll(MyParser.combinedDirective, "ab") 
res0: MyParser.ParseResult[List[String]] = [1.3] parsed: List(a, b) 

Vous pouvez également le définir vous-même avec un pli:

import scala.util.parsing.combinator.RegexParsers 

object MyParser extends RegexParsers { 
    def aDirective: Parser[String] = "a" ^^ { case _ => "a" } 
    def bDirective: Parser[String] = "b" ^^ { case _ => "b" } 

    def combine[T](parsers: List[Parser[T]]): Parser[List[T]] = 
    parsers.foldRight(success(List.empty[T])) { 
     case (p, acc) => for { 
     pRes <- p 
     accRes <- acc 
     } yield pRes :: accRes 
    } 

    def combinedDirective: Parser[List[String]] = 
    combine(List(aDirective, bDirective)) 
} 

Et ça marchera exactement de la même manière. L'astuce consiste simplement à obtenir la base de droite - il doit être un analyseur qui réussit toujours avec la liste vide comme valeur.


Mise à jour: si vous définissez une classe, pas un objet, l'approche Scalaz ci-dessus ne fonctionne pas (pour un certain nombre de raisons étranges, bref le this est pas assez stable). Vous pouvez définir votre propre monade exemple assez facilement, cependant:

class MyParser extends RegexParsers { 
    implicit val pm = new Monad[Parser] { 
    def point[A](a: => A): Parser[A] = success(a) 
    def bind[A, B](fa: Parser[A])(f: A => Parser[B]): Parser[B] = fa flatMap f 
    } 

    def aDirective: Parser[String] = "a" ^^ { case _ => "a" } 
    def bDirective: Parser[String] = "b" ^^ { case _ => "b" } 

    def combine[T](parsers: List[Parser[T]]): Parser[List[T]] = parsers.sequenceU 

    def combinedDirective: Parser[List[String]] = 
    combine(List(aDirective, bDirective)) 
} 

En fait, vous n'avez pas besoin d'une instance monade ici pour utiliser sequence, juste un foncteur applicatif, mais la définition est en fait un peu plus pratique et la monade instance peut être utile dans d'autres cas.

+0

Pour l'exemple Scalaz que vous fournissez, j'obtiens quelques erreurs de compilation. J'utilise Scala 2.11 et Scalaz 7.0.6. La seule différence entre le vôtre et le mien est que j'utilise une 'classe' au lieu d'un' objet', pas sûr que cela devrait être important? Les erreurs de compilation que je vois sont: – adamretter

+0

Erreur: (14, 71) Implicite non trouvé: scalaz.Unapply [scalaz.Applicative, TestParser.this.Parser [T]]. Impossible de désappliquer le type 'TestParser.this.Parser [T]' dans un constructeur de type de type 'M [_]' qui est classé par la classe de type 'scalaz.Applicative'. Vérifiez que la classe de type est définie en compilant 'implicitly [scalaz.Applicative [type constructeur]]' et examinez les implicits dans l'objet Unapply, qui ne couvre que le type commun 'shapes'. Def combine [T] (analyseurs: Liste [Analyseur [T]]): Analyseur [Liste [T]] = analyseurs.sequenceU ^ – adamretter

+0

Erreur: (14, 71) pas assez d'arguments pour la séquence de la méthode U: (implicite G: scalaz.Unapply [scalaz.Applicative, TestParser.this.Parser [T]]) G.M [liste [G.A]]. Paramètre de valeur non spécifiée G. def combine [T] (analyseurs: List [Parser [T]]): Parser [List [T]] = parsers.sequenceU ^ – adamretter

Questions connexes