2016-05-05 1 views
1

Вот моя попытка написать небольшой парсер для положительныхInt с:Как я могу заставить мой парсер изящно сработать, если будет сделано исключение?

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 ... 

Вместо этого я хочу, чтобы мой positiveInt анализатора неудачи изящно (возвращая Failure), когда toInt бросает исключение. Как я могу это сделать?

Легкое исправление, которое приходит на ум, состоит в том, чтобы ограничить длину строк, принятых моим регулярным выражением, но это неудовлетворительно.

Я предполагаю, что анализатор комбинатор для этого случая использования уже предусмотрен scala.util.parsing.combinator библиотеку, но я не смог найти один ...

ответ

5

Вы можете использовать комбинатор принимать частичную функцию (вдохновленный how to make scala parser fail):

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 } 
+0

Спасибо. '^?' - это комбинатор, которого я отсутствовал. В конце я пошел на: 'private def positiveInt: Parser [Int] = { def safeToInt (str: String) = Try (str.toInt) " "" \ d + "" ". R ^^ safeToInt ^? {case scala.util.Success (результат), если результат> 0 => результат} } '. – Jubobs

+1

Вы можете использовать 'Function.unlift' для преобразования функции, возвращающей' Option' в 'PartialFunction'. Например, "" "" 0 * [1-9] \ d * "" ". R ^? Function.unlift {x => Try (x.toInt) .toOption} ' – Kolmar

0

Как насчет упаковки вызова parseAll с Try()?

Try(parseAll(positiveInt, input)) 

scala.util.Try «s apply метод обернуть любое исключение в Failure[T], а затем вы можете даже использовать .toOption конвертировать любой Failure в None.

Try(parseAll(positiveInt, input)).toOption 
+0

Спасибо, но я предпочел бы решить эту проблему в источнике: 'positiveInt'. – Jubobs