如果抛出异常,我如何才能使我的解析器正常失败



以下是我为positiveInts:编写一个小型解析器的尝试

import scala.util.parsing.combinator.RegexParsers
object PositiveIntParser extends RegexParsers {
  private def positiveInt: Parser[Int] = """0*[1-9]d*""".r ^^ { _.toInt }
  def apply(input: String): Option[Int] = parseAll(positiveInt, input) match {
    case Success(result, _) => Some(result)
    case _ => None
  }
}

问题是,如果输入字符串太长,toInt会抛出一个NumberFormatException,这会使我的解析器崩溃:

scala> :load PositiveIntParser.scala
Loading PositiveIntParser.scala...
import scala.util.parsing.combinator.RegexParsers
defined object PositiveIntParser
scala> PositiveIntParser("12")
res0: Option[Int] = Some(12)
scala> PositiveIntParser("-12")
res1: Option[Int] = None
scala> PositiveIntParser("123123123123123123")
java.lang.NumberFormatException: For input string: "123123123123123123"
  at ...

相反,当toInt抛出异常时,我希望我的positiveInt解析器能够正常地失败(通过返回Failure)。我该怎么做?

我想到的一个简单的解决方案是限制regex所接受的字符串的长度,但这并不令人满意。

我猜scala.util.parsing.combinator库已经为这个用例提供了一个解析器组合子,但我一直找不到。。。

您可以使用接受部分函数的组合子(受如何使scala解析器失败的启发):

private def positiveInt: Parser[Int] = """0*[1-9]d*""".r ^? {
  case x if Try(x.toInt).isSuccess => x.toInt
}

如果你想避免双重转换,你可以创建一个提取器来执行匹配和转换:

object ParsedInt {
  def unapply(str: String): Option[Int] = Try(str.toInt).toOption
}
private def positiveInt: Parser[Int] = """0*[1-9]d*""".r ^? { case ParsedInt(x) => x }

也可以将阳性测试转移到案例条件中,我发现它比一个有点复杂的正则表达式更可读:

private def positiveInt: Parser[Int] = """d+""".r ^? { case ParsedInt(x) if x > 0 => x }

根据您的意见,提取也可以在单独的^^步骤中执行,如下所示:

private def positiveInt: Parser[Int] = """d+""".r ^^
  { str => Try(str.toInt)} ^? { case util.Success(x) if x > 0 => x }

Try()包装对parseAll的调用如何?

Try(parseAll(positiveInt, input))

scala.util.Tryapply方法将在Failure[T]中包装任何异常,然后您甚至可以使用.toOption将任何Failure转换为None

Try(parseAll(positiveInt, input)).toOption

最新更新