From dc9e9cf67bb459d3e7fd0969054f837846461036 Mon Sep 17 00:00:00 2001 From: Antoine Gourlay Date: Wed, 6 Aug 2014 16:53:39 +0200 Subject: [PATCH] make RegexParser.err handle whitespace like literal and regex. This overrides `err` in RegexParser to make it consume whitespace just like regex and literal. The original motivation was: object parser extends RegexParsers { def num = "\\d+".r def twoNums = num ~ (num | err("error!")) } // succeeds parser.parseAll(twoNums, "42 721") // fails with a parsing Failure instead of an Error // because err doesn't consume the whitespace but the regex does. parser.parseAll(twoNums, "42 foo") This may change the output of some parsers that failed to parse input (from a Failure to an Error). Fixes #29 --- build.sbt | 3 +++ .../util/parsing/combinator/RegexParsers.scala | 16 ++++++++++++++++ .../parsing/combinator/RegexParsersTest.scala | 17 ++++++++++++++++- 3 files changed, 35 insertions(+), 1 deletion(-) diff --git a/build.sbt b/build.sbt index 4c513616..2a4aea81 100644 --- a/build.sbt +++ b/build.sbt @@ -1,4 +1,5 @@ import com.typesafe.tools.mima.plugin.{MimaPlugin, MimaKeys} +import com.typesafe.tools.mima.core.{ProblemFilters, MissingMethodProblem} scalaModuleSettings @@ -31,3 +32,5 @@ test in Test := { MimaKeys.reportBinaryIssues.value (test in Test).value } + +MimaKeys.binaryIssueFilters += ProblemFilters.exclude[MissingMethodProblem]("scala.util.parsing.combinator.RegexParsers.scala$util$parsing$combinator$RegexParsers$$super$err") diff --git a/src/main/scala/scala/util/parsing/combinator/RegexParsers.scala b/src/main/scala/scala/util/parsing/combinator/RegexParsers.scala index f345fae0..e05d71e2 100644 --- a/src/main/scala/scala/util/parsing/combinator/RegexParsers.scala +++ b/src/main/scala/scala/util/parsing/combinator/RegexParsers.scala @@ -137,6 +137,22 @@ trait RegexParsers extends Parsers { } } + // we might want to make it public/protected in a future version + private def ws[T](p: Parser[T]): Parser[T] = new Parser[T] { + def apply(in: Input) = { + val offset = in.offset + val start = handleWhiteSpace(in.source, offset) + p(in.drop (start - offset)) + } + } + + /** + * @inheritdoc + * + * This parser additionnal skips whitespace if `skipWhitespace` returns true. + */ + override def err(msg: String) = ws(super.err(msg)) + override def phrase[T](p: Parser[T]): Parser[T] = super.phrase(p <~ opt("""\z""".r)) diff --git a/src/test/scala/scala/util/parsing/combinator/RegexParsersTest.scala b/src/test/scala/scala/util/parsing/combinator/RegexParsersTest.scala index f894af3f..f97b02bb 100644 --- a/src/test/scala/scala/util/parsing/combinator/RegexParsersTest.scala +++ b/src/test/scala/scala/util/parsing/combinator/RegexParsersTest.scala @@ -1,7 +1,7 @@ package scala.util.parsing.combinator import org.junit.Test -import org.junit.Assert.assertEquals +import org.junit.Assert.{assertEquals,assertTrue} class RegexParsersTest { @Test @@ -73,4 +73,19 @@ class RegexParsersTest { val success = parseAll(twoWords, "first second").asInstanceOf[Success[(String, String)]] assertEquals(("second", "first"), success.get) } + + @Test + def errorConsumesWhitespace: Unit = { + object parser extends RegexParsers { + def num = "\\d+".r + + def twoNums = num ~ (num | err("error!")) + } + import parser._ + + // this used to return a Failure (for the second num) + val error = parseAll(twoNums, "458 bar") + assertTrue(s"expected an Error but got: ${error.getClass.getName}", error.isInstanceOf[Error]) + assertEquals("error!", error.asInstanceOf[Error].msg) + } }