diff --git a/shared/src/main/scala/scala/util/parsing/combinator/RegexParsers.scala b/shared/src/main/scala/scala/util/parsing/combinator/RegexParsers.scala index 0fe21982..acf55bb9 100644 --- a/shared/src/main/scala/scala/util/parsing/combinator/RegexParsers.scala +++ b/shared/src/main/scala/scala/util/parsing/combinator/RegexParsers.scala @@ -138,6 +138,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 additionally skips whitespace if `skipWhitespace` returns true. + */ + override def err(msg: String) = ws(super.err(msg)) + /** * A parser generator delimiting whole phrases (i.e. programs). * diff --git a/shared/src/test/scala/scala/util/parsing/combinator/RegexParsersTest.scala b/shared/src/test/scala/scala/util/parsing/combinator/RegexParsersTest.scala index 8dfcbd0d..f1c0aafe 100644 --- a/shared/src/test/scala/scala/util/parsing/combinator/RegexParsersTest.scala +++ b/shared/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 @@ -100,4 +100,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) + } } diff --git a/shared/src/test/scala/scala/util/parsing/combinator/gh29.scala b/shared/src/test/scala/scala/util/parsing/combinator/gh29.scala new file mode 100644 index 00000000..39b68667 --- /dev/null +++ b/shared/src/test/scala/scala/util/parsing/combinator/gh29.scala @@ -0,0 +1,36 @@ +package scala.util.parsing.combinator + +import org.junit.Test +import org.junit.Assert.assertEquals + +class gh29 { + object Foo extends JavaTokenParsers { + def word(x: String) = s"\\b$x\\b".r + + lazy val expr = aSentence | something + + lazy val aSentence = noun ~ verb ~ obj + + lazy val noun = word("noun") + lazy val verb = word("verb") | err("not a verb!") + lazy val obj = word("object") + + lazy val something = word("FOO") + } + + val expected = + """[1.6] error: not a verb! + +noun vedsfasdf + ^""".stripMargin + + @Test + def test(): Unit = { + val f = Foo.parseAll(Foo.expr, "noun verb object") + + assertEquals("[1.17] parsed: ((noun~verb)~object)", f.toString) + + val g = Foo.parseAll(Foo.expr, "noun vedsfasdf") + assertEquals(expected, g.toString) + } +}