Как можно игнорировать несовпадающий предыдущий текст при использовании комбинаторов парсера Scala? - PullRequest
2 голосов
/ 03 апреля 2012

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

Рассмотрим этот небольшой анализатор, чтобы получить денежные суммы:

import scala.util.parsing.combinator._

case class Amount(number: Double, currency: String)

object MyParser extends JavaTokenParsers {
  def number = floatingPointNumber ^^ (_.toDouble)
  def currency = """\w+""".r ^? ({
    case "USD" => "USD"
    case "EUR" => "EUR"
  }, "Unknown currency code: " + _)

  def amount = (number ~ currency) ^^ {
    case num ~ curr => Amount(num, curr)
  } | currency ~ number ^^ {
    case curr ~ num => Amount(num, curr)
  }

  def junk = """\S+""".r
  def amountNested: Parser[Any] = amount | junk ~> amountNested
}

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

scala> MyParser.parse(MyParser.amount, "101.41 EUR")
res7: MyParser.ParseResult[Amount] = [1.11] parsed: Amount(101.41,EUR)

scala> MyParser.parse(MyParser.amount, "EUR 102.13")
res8: MyParser.ParseResult[Amount] = [1.11] parsed: Amount(102.13,EUR)

Однако происходит сбой, если перед ним находится несоответствующий текст:

scala> MyParser.parse(MyParser.amount, "I have 101.41 EUR")
res9: MyParser.ParseResult[Amount] = 
[1.2] failure: Unknown currency code: I

I have 101.41 EUR
 ^

Мое решение - парсер amountNested, в котором он рекурсивно пытается найти Amount. Это работает, но дает ParseResult[Any]:

scala> MyParser.parse(MyParser.amountNested, "I have 101.41 EUR")
res10: MyParser.ParseResult[Any] = [1.18] parsed: Amount(101.41,EUR)

Эта потеря информации о типе (которую, конечно, можно «извлечь» с помощью сопоставления с образцом), к сожалению, кажется, что любой успех будет содержать Amount.

Есть ли способ продолжать поиск моего ввода ("I have 101.41 EUR"), пока у меня не будет совпадения или нет, но без Parser[Any]?

Глядя на ScalaDocs , кажется, что метод * на Parser может помочь, но все, что я получаю, это сбои или бесконечные циклы, когда я пытаюсь что-то вроде:

def amount2 = ("""\S+""".r *) ~> amount

1 Ответ

1 голос
/ 03 апреля 2012

Он проверяет все правильно, если вы объявляете сумму, созданную как Parser [Amount]:

def amountNested: Parser[Amount] = amount | junk ~> amountNested
...