2009-11-29 6 views
11

Me pregunto si es posible obtener el MatchData generado a partir de la expresión regular correspondiente en la gramática siguiente.Accediendo a los datos de coincidencia de expresión regular de Scala Parser

object DateParser extends JavaTokenParsers { 

    .... 

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

Una opción es, por supuesto, para llevar a cabo el partido de nuevo en el interior del bloque, pero dado que el RegexParser ya ha realizado el partido que espero que pase el MatchData al bloque, o lo almacena?

Respuesta

20

Aquí es la definición implícita de que convierte su Regex en un Parser:.

/** 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)) 
     } 
    } 
    } 

Sólo adaptarlo:

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)) } 
} 

Ejemplo:

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

Gracias Daniel, funciona como un encanto –

+0

¡Excelente publicación! ¡Excelente! – fotNelton

+0

Es curioso, ¿por qué ese tipo de funcionalidad no es parte de la implementación de clase estándar (de la biblioteca)? Parece bastante útil, pero cada usuario debe implementarlo por su cuenta ... –

1

Cuando una expresión regular se utiliza en un ejemplo RegexParsers, la implícita regex def (Regex): Analizador [String] en RegexParsers se utiliza para appoly que Regex a la entrada. La instancia de coincidencia generada tras la aplicación exitosa de la RE en la entrada actual se usa para construir un Éxito en el método regex(), pero solo se usa su valor "final", por lo que cualquier subcoincidencia capturada se descarta cuando el método devoluciones.

Tal como está (en la fuente 2.7 que miré), estás de suerte, creo.

3

No, no puedes hacer esto. Si nos fijamos en la definición del analizador utilizado al convertir una expresión regular a un analizador, se tira a la basura todo el contexto y simplemente devuelve la cadena completa encontrados:

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

Usted tiene un par de otras opciones, aunque :

  • Divida su programa de análisis en varios analizadores más pequeños (para los tokens en realidad se desea extraer)
  • definen un analizador personalizado que extrae los valores que desee y devuelve un objeto de dominio en lugar de una cadena

La primera se vería como

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) 
    } 

Los medios <~ "requerir estos dos símbolos juntos, pero sólo me dé el resultado de la primera.

Los ~ significa "requieren estas dos señales juntas y atarlas en un ~ objeto.

El ? significa que el analizador es opcional y se volverá una opción.

El .getOrElse poco patrón-matchable proporciona un valor por defecto para cuando el analizador no definió un valor

+0

Gracias David, agradable solución. Voy a ir con la solución de analizador personalizado ya que mantiene la definición de la gramática más legible. –

+1

Ahora que lo pienso, un analizador personalizado también es más correcto. Cada analizador de expresiones regulares individuales permite el espacio en blanco inicial, por lo que el código que publiqué también coincidiría con cadenas como "1999 - 02 - 28". –

0

me encontré con un problema similar usando Scala 2.8.1 y tratando de analizar la entrada de la forma "nombre: valor" utilizando la clase RegexParsers:

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) 
} 

Parece que se puede agarrar los grupos emparejados después del análisis de esta manera:

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