diff --git a/src/main/scala/scala/util/parsing/combinator/Parsers.scala b/src/main/scala/scala/util/parsing/combinator/Parsers.scala index 16754646..f6011d3a 100644 --- a/src/main/scala/scala/util/parsing/combinator/Parsers.scala +++ b/src/main/scala/scala/util/parsing/combinator/Parsers.scala @@ -884,7 +884,7 @@ trait Parsers { if (in1.atEnd) s else - lastNoSuccessVar.value filterNot { _.next.pos < in1.pos } getOrElse Failure("end of input expected", in1) + lastNoSuccessVar.value filterNot { _.next.pos < in1.pos } getOrElse Failure("end of input expected", in1) case ns => lastNoSuccessVar.value.getOrElse(ns) } } diff --git a/src/main/scala/scala/util/parsing/combinator/RegexParsers.scala b/src/main/scala/scala/util/parsing/combinator/RegexParsers.scala index f345fae0..52e781d0 100644 --- a/src/main/scala/scala/util/parsing/combinator/RegexParsers.scala +++ b/src/main/scala/scala/util/parsing/combinator/RegexParsers.scala @@ -137,8 +137,19 @@ trait RegexParsers extends Parsers { } } + /** + * A parser generator delimiting whole phrases (i.e. programs). + * + * `phrase(p)` succeeds if `p` succeeds and no input is left over after `p`. + * + * @param p the parser that must consume all input for the resulting parser + * to succeed. + * + * @return a parser that has the same result as `p`, but that only succeeds + * if `p` consumed all the input. + */ override def phrase[T](p: Parser[T]): Parser[T] = - super.phrase(p <~ opt("""\z""".r)) + super.phrase(p <~ "".r) /** Parse some prefix of reader `in` with parser `p`. */ def parse[T](p: Parser[T], in: Reader[Char]): ParseResult[T] = diff --git a/src/test/scala/scala/util/parsing/combinator/JavaTokenParsersTest.scala b/src/test/scala/scala/util/parsing/combinator/JavaTokenParsersTest.scala index 5851a655..28cf1ba7 100644 --- a/src/test/scala/scala/util/parsing/combinator/JavaTokenParsersTest.scala +++ b/src/test/scala/scala/util/parsing/combinator/JavaTokenParsersTest.scala @@ -55,4 +55,67 @@ class JavaTokenParsersTest { parseFailure("with space", 6) } + @Test + def repeatedlyParsesTest: Unit = { + object TestTokenParser extends JavaTokenParsers + import TestTokenParser._ + val p = ident ~ "(?i)AND".r.* + + val parseResult = parseAll(p, "start") + parseResult match { + case Success(r, _) => + assertEquals("start", r._1) + assertEquals(0, r._2.size) + case _ => sys.error(parseResult.toString) + } + + val parseResult1 = parseAll(p, "start start") + parseResult1 match { + case e @ Failure(message, next) => + assertEquals(next.pos.column, 7) + assert(message.endsWith("string matching regex `(?i)AND' expected but `s' found")) + case _ => sys.error(parseResult1.toString) + } + + val parseResult2 = parseAll(p, "start AND AND") + parseResult2 match { + case Success(r, _) => + assertEquals("start", r._1) + assertEquals("AND AND", r._2.mkString(" ")) + case _ => sys.error(parseResult2.toString) + } + } + + @Test + def optionParserTest: Unit = { + object TestTokenParser extends JavaTokenParsers + import TestTokenParser._ + val p = opt(ident) + + val parseResult = parseAll(p, "-start") + parseResult match { + case Failure(message, next) => + assertEquals(next.pos.line, 1) + assertEquals(next.pos.column, 1) + assert(message.endsWith(s"regex `\\p{javaJavaIdentifierStart}\\p{javaJavaIdentifierPart}*' expected but `-' found")) + case _ => sys.error(parseResult.toString) + } + + val parseResult2 = parseAll(p, "start ") + parseResult2 match { + case Success(r, _) => + assertEquals(r, Some("start")) + case _ => + sys.error(parseResult2.toString) + } + + val parseResult3 = parseAll(p, "start") + parseResult3 match { + case Success(r, _) => + assertEquals(r, Some("start")) + case _ => sys.error(parseResult3.toString) + } + } + + }