Доступ к данным соответствия Scala Parser
Мне интересно, можно ли получить MatchData, сгенерированную из соответствующего регулярного выражения в грамматике ниже.
object DateParser extends JavaTokenParsers {
....
val dateLiteral = """(\d{4}[-/])?(\d\d[-/])?(\d\d)""".r ^^ {
... get MatchData
}
}
Один из вариантов, конечно, состоит в том, чтобы повторить совпадение внутри блока, но поскольку RegexParser уже выполнил соответствие, я надеюсь, что он передает MatchData в блок или сохраняет его?
Ответы
Ответ 1
Вот неявное определение, которое преобразует ваш 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)
Ответ 2
Нет, вы не можете этого сделать. Если вы посмотрите на определение Parser, используемое при преобразовании регулярного выражения в Parser, оно выбрасывает весь контекст и просто возвращает полную строку:
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)
}
<~
означает "требует, чтобы эти два токена вместе, но дали мне только результат первого.
~
означает "требуется, чтобы эти два токена вместе и связали их вместе в объекте, совместимом с шаблоном".
?
означает, что синтаксический анализатор является необязательным и возвращает параметр.
Бит .getOrElse
предоставляет значение по умолчанию, когда парсер не определил значение.
Ответ 3
Если в экземпляре RegexParsers используется Regex, в RegexParsers используется неявное def regex (Regex): Parser [String] для добавления этого регулярного выражения к входу. Экземпляр Match, полученный при успешном применении RE на текущем входе, используется для построения метода Success в методе regex(), но используется только его значение "end", поэтому любые захваченные суб-совпадения отбрасываются к тому моменту, когда этот метод возвращается.
В его нынешнем виде (в 2,7 источника, на который я смотрел), я думаю, вам повезло.
Ответ 4
Я столкнулся с аналогичной проблемой, используя scala 2.8.1 и пытаясь проанализировать ввод формы "name: value" с помощью класса 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))
}
}