Доступ к данным соответствия регулярного выражения Scala Parser - PullRequest
11 голосов
/ 29 ноября 2009

Мне интересно, возможно ли получить MatchData, сгенерированный из соответствующего регулярного выражения в грамматике ниже.

object DateParser extends JavaTokenParsers {

    ....

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

Одним из вариантов, конечно, является повторное сопоставление внутри блока, но поскольку RegexParser уже выполнил сопоставление, я надеюсь, что он передает MatchData в блок или сохраняет его?

Ответы [ 4 ]

20 голосов
/ 29 ноября 2009

Вот неявное определение, которое преобразует ваше Regex в 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))
      }
    }
  }

Просто адаптируйте его:

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

Пример:

scala> X.parseAll(X.t, "23/03/1971")
res8: X.ParseResult[(String, String, String)] = [1.11] parsed: (23,03,1971)
3 голосов
/ 29 ноября 2009

Нет, вы не можете этого сделать. Если вы посмотрите на определение синтаксического анализатора, использованного при преобразовании регулярного выражения в синтаксический анализатор, он отбрасывает весь контекст и просто возвращает полностью совпадающую строку:

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

У вас есть несколько других вариантов:

  • разбейте ваш парсер на несколько меньших парсеров (для токенов, которые вы действительно хотите извлечь)
  • определяет пользовательский анализатор, который извлекает нужные значения и возвращает объект домена вместо строки

Первый будет выглядеть

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

<~ означает «потребуйте эти два жетона вместе, но только дайте мне результат первого.

~ означает «требуется, чтобы эти два токена были вместе, и свяжите их вместе в сопоставимом с шаблоном объекте.

? означает, что синтаксический анализатор является необязательным и вернет Option.

Бит .getOrElse предоставляет значение по умолчанию для случая, когда синтаксический анализатор не определил значение.

1 голос
/ 29 ноября 2009

Когда Regex используется в экземпляре RegexParsers, неявное def regex (Regex): Parser [String] в RegexParsers используется для добавления этого Regex ко входу. Экземпляр Match, полученный при успешном применении RE на текущем входе, используется для создания Success в методе regex (), но используется только его «конечное» значение, поэтому любые захваченные суб-совпадения отбрасываются ко времени, когда этот метод возвращается.

В нынешнем виде (в источнике 2.7, на который я смотрел), вам не повезло, я считаю.

0 голосов
/ 06 марта 2011

Я столкнулся с подобной проблемой, используя scala 2.8.1 и пытаясь проанализировать ввод формы "имя: значение", используя класс 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)
}

Кажется, что после парсинга вы можете получить подходящие группы:

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