我真的很喜欢解析器组合器,但是当我不关心相关文本之前的文本时,我对提取数据的解决方案不满意。
考虑这个小解析器来获取货币金额:
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