2009-11-29 3 views
11

Ich frage mich, ob es möglich ist, MatchData aus dem passenden regulären Ausdruck in der folgenden Grammatik zu generieren.Auf Übereinstimmungsdaten des regulären Ausdrucks von Scala Parser zugreifen

object DateParser extends JavaTokenParsers { 

    .... 

    val dateLiteral = """(\d{4}[-/])?(\d\d[-/])?(\d\d)""".r ^^ { 
     ... get MatchData 
    } 
} 

Eine Möglichkeit ist natürlich, das Spiel wieder in dem Block durchzuführen, aber da die RegexParser durchgeführt bereits das Spiel Ich hoffe, dass es das Match an den Block geht, oder speichert sie?

Antwort

20

Hier ist die implizite Definition ist, dass Ihr Regex in eine Parser umwandelt.

/** A parser that matches a regex string */ 
    implicit def regex(r: Regex): Parser[String] = new Parser[String] { 
    def apply(in: Input) = { 
     val source = in.source 
     val offset = in.offset 
     val start = handleWhiteSpace(source, offset) 
     (r findPrefixMatchOf (source.subSequence(start, source.length))) match { 
     case Some(matched) => 
      Success(source.subSequence(start, start + matched.end).toString, 
        in.drop(start + matched.end - offset)) 
     case None => 
      Failure("string matching regex `"+r+"' expected but `"+in.first+"' found", in.drop(start - offset)) 
     } 
    } 
    } 

einfach daran anpassen:

object X extends RegexParsers { 
    /** A parser that matches a regex string and returns the Match */ 
    def regexMatch(r: Regex): Parser[Regex.Match] = new Parser[Regex.Match] { 
    def apply(in: Input) = { 
     val source = in.source 
     val offset = in.offset 
     val start = handleWhiteSpace(source, offset) 
     (r findPrefixMatchOf (source.subSequence(start, source.length))) match { 
     case Some(matched) => 
      Success(matched, 
        in.drop(start + matched.end - offset)) 
     case None => 
      Failure("string matching regex `"+r+"' expected but `"+in.first+"' found", in.drop(start - offset)) 
     } 
    } 
    } 
    val t = regexMatch("""(\d\d)/(\d\d)/(\d\d\d\d)""".r) ^^ { case m => (m.group(1), m.group(2), m.group(3)) } 
} 

Beispiel:

scala> X.parseAll(X.t, "23/03/1971") 
res8: X.ParseResult[(String, String, String)] = [1.11] parsed: (23,03,1971) 
+0

Dank Daniel, funktioniert wie ein Charme –

+0

Toller Beitrag! Ausgezeichnet! – fotNelton

+0

Es ist merkwürdig, warum diese Art von Funktionalität nicht Teil der Standard (Bibliothek) Klassenimplementierung ist? Es sieht ziemlich nützlich aus, aber jeder Benutzer sollte es selbst implementieren ... –

1

Wenn ein regulärer Ausdruck in einer RegexParsers Instanz verwendet wird, wird die implizite def regex (Regex): Parser [String] in RegexParsers verwendet, das Regex an den Eingang appoly. Die Match-Instanz, die bei erfolgreicher Anwendung des RE an der aktuellen Eingabe zurückgegeben wird, wird verwendet, um in der regex() -Methode ein Success-Objekt zu konstruieren, aber nur dessen "end" -Wert wird verwendet, sodass alle erfassten Sub-Übereinstimmungen zum Zeitpunkt dieser Methode verworfen werden kehrt zurück.

So wie es aussieht (in der 2.7 Quelle, die ich angeschaut habe), hast du kein Glück, glaube ich.

3

Nein, das geht nicht. Wenn Sie bei der Definition der verwendeten Parser aussehen, wenn Sie einen regulären Ausdruck zu einem Parser konvertieren, wirft sie alle Kontext entfernt und gibt nur die voll angepasst string:

http://lampsvn.epfl.ch/trac/scala/browser/scala/tags/R_2_7_7_final/src/library/scala/util/parsing/combinator/RegexParsers.scala?view=markup#L55

Sie haben ein paar andere Optionen, obwohl :

  • Ihre Parser in mehrere kleinere Parser brechen (für die Token wollen Sie eigentlich extrahieren)
  • einen benutzerdefinierten Parser definieren, die die Werte extrahiert Sie wollen und gibt einen Domain-Objekt anstelle einer Zeichenfolge

Die erste wie

val separator = "-" | "/" 
    val year = ("""\d{4}"""r) <~ separator 
    val month = ("""\d\d"""r) <~ separator 
    val day = """\d\d"""r 

    val date = ((year?) ~ (month?) ~ day) map { 
    case year ~ month ~ day => 
     (year.getOrElse("2009"), month.getOrElse("11"), day) 
    } 

Die <~ Mittel „erfordern diese beiden Token zusammen, aber geben Sie mir nur das Ergebnis der ersten aussehen würde.

Die ~ Mittel „erfordern diese beiden Token zusammen und binden sie zusammen in einem Muster-anpassungsfähigen ~ Objekt.

Die ? bedeutet, dass der Parser ist optional und wird eine Option zurückzukehren.

The .getOrElse Bit stellt einen Standardwert für, wenn der Parser einen Wert nicht definiert haben

+0

Dank David, schön Lösung. Ich werde mit der benutzerdefinierten Parserlösung gehen, da sie die Grammatikdefinition besser lesbar macht. –

+1

Nun, da ich darüber nachdenke, ist ein benutzerdefinierter Parser auch korrekter. Jeder einzelne Regex-Parser erlaubt führende Leerzeichen, so dass der von mir gepostete Code auch Strings wie "1999 - 02 - 28" enthält. –

0

ich in ein ähnliches Problem lief scala mit 2,8.1 und versuchen Eingang des Formulars zu analysieren „name: value“ der RegexParsers-Klasse:

package scalucene.query 

import scala.util.matching.Regex 
import scala.util.parsing.combinator._ 

object QueryParser extends RegexParsers { 
    override def skipWhitespace = false 

    private def quoted = regex(new Regex("\"[^\"]+")) 
    private def colon = regex(new Regex(":")) 
    private def word = regex(new Regex("\\w+")) 
    private def fielded = (regex(new Regex("[^:]+")) <~ colon) ~ word 
    private def term = (fielded | word | quoted) 

    def parseItem(str: String) = parse(term, str) 
} 

Es scheint, dass Sie die angepassten Gruppen nach dem Parsen wie diese greifen kann:

QueryParser.parseItem("nameExample:valueExample") match { 
    case QueryParser.Success(result:scala.util.parsing.combinator.Parsers$$tilde, _) => { 
     println("Name: " + result.productElement(0) + " value: " + result.productElement(1)) 
    } 
}