Парсер комбинатора scala для сохранения исходного ввода - PullRequest
2 голосов
/ 26 января 2012

Я хотел бы составить парсер из другого парсера, чтобы использовать потребляемый ввод в качестве аргумента конструкции ast.

Скажем, у меня есть

def ingredient = amount ~ nameOfIngredient ^^ {
  case amount ~ name => Ingredient(name, amount)
}

Что я ищуэто способ иметь другой синтаксический анализатор для создания элемента:

case class RecipeRow(orginalText: String, ingredient: Ingredient)

Так что я ищу способ извлечь исходный потребляемый ввод для синтаксического анализатора в композиции.Может быть что-то вроде:

def recipeRow = ingredient withConsumedInput ^^ {
  case (amount ~ name, consumed) => RecipeRow(consumed, Ingredient(name, amount))
}

Я думаю, подпись в этом случае будет:

def withConsumedInput [U](p: => Parser[U]): Parser[(U, String)]

Есть ли другой простой способ получить то, что я хочу, или мне нужно написать эту вещь?Такое ощущение, что это, вероятно, лучший способ ...

1 Ответ

4 голосов
/ 26 января 2012

Не просто, на самом деле.

Давайте начнем с Parser: что это может нам дать?Что ж, Parser расширяет Input => ParseResult, поэтому мы должны извлечь информацию из любого из них.

Тип Input является псевдонимом, в любом случае RegexParsers для scala.util.parsing.input.Reader[Char].Там очень мало, чтобы помочь нам, если только это не будет Reader из CharSequence, в этом случае мы можем использовать source и offset.Давайте использовать это тогда.

Теперь, ParseResult имеет много подклассов, но нас интересует только Success, который имеет поле next: Input.Используя это, мы можем попробовать это:

def withConsumedInput [U](p: => Parser[U]): Parser[(U, String)] = new Parser[(U, String)] {
  def apply(in: Input) = p(in) match {
    case Success(result, next) =>
      val parsedString = in.source.subSequence(in.offset, next.offset).toString
      Success(result -> parsedString, next)
    case other: NoSuccess      => other
  }
}

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

def withConsumedInput [U](p: => Parser[U]): Parser[(U, String)] = new Parser[(U, String)] {
  def apply(in: Input) = p(in) match {
    case Success(result, next) =>
      val parsedString = in.source.subSequence(handleWhiteSpace(in.source, in.offset), next.offset).toString
      Success(result -> parsedString, next)
    case other: NoSuccess      => other
  }
}
...