Мне действительно нравятся комбинаторы синтаксического анализа, но я не доволен решением, которое я придумала для извлечения данных, когда меня не волнует текст перед соответствующим текстом.
Рассмотрим этот небольшой анализатор, чтобы получить денежные суммы:
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