Skip to content

Commit 617411c

Browse files
author
Amal Elshihaby
committed
Fix misleading error message when using optional or repeatedly parser #34
1 parent 647f326 commit 617411c

File tree

3 files changed

+86
-4
lines changed

3 files changed

+86
-4
lines changed

src/main/scala/scala/util/parsing/combinator/Parsers.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -884,7 +884,7 @@ trait Parsers {
884884
if (in1.atEnd)
885885
s
886886
else
887-
lastNoSuccessVar.value filterNot { _.next.pos < in1.pos } getOrElse Failure("end of input expected", in1)
887+
lastNoSuccessVar.value filterNot { _.next.pos < in1.pos } getOrElse Failure("end of input expected", in1)
888888
case ns => lastNoSuccessVar.value.getOrElse(ns)
889889
}
890890
}

src/main/scala/scala/util/parsing/combinator/RegexParsers.scala

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -137,8 +137,26 @@ trait RegexParsers extends Parsers {
137137
}
138138
}
139139

140-
override def phrase[T](p: Parser[T]): Parser[T] =
141-
super.phrase(p <~ opt("""\z""".r))
140+
/**
141+
* A parser generator delimiting whole phrases (i.e. programs).
142+
*
143+
* `phrase(p)` succeeds if `p` succeeds and no input is left over after `p`.
144+
*
145+
* @param p the parser that must consume all input for the resulting parser
146+
* to succeed.
147+
*
148+
* @return a parser that has the same result as `p`, but that only succeeds
149+
* if `p` consumed all the input.
150+
*/
151+
override def phrase[T](p: Parser[T]): Parser[T] = Parser { in =>
152+
super.phrase(p)(in) match {
153+
case s @ Success(_, _) => s
154+
case e @ Failure(msg, next) if msg == "end of input expected" =>
155+
val found = next.source.charAt(next.offset)
156+
Failure("string matching regex `\\z' expected but "+found+" found", next)
157+
case e => e
158+
}
159+
}
142160

143161
/** Parse some prefix of reader `in` with parser `p`. */
144162
def parse[T](p: Parser[T], in: Reader[Char]): ParseResult[T] =

src/test/scala/scala/util/parsing/combinator/JavaTokenParsersTest.scala

Lines changed: 65 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,71 @@ class JavaTokenParsersTest {
5252
parseFailure("with-s", 5)
5353
// we♥scala
5454
parseFailure("we\u2665scala", 3)
55-
parseFailure("with space", 6)
55+
parseFailure("with space", 5)
5656
}
5757

58+
@Test
59+
def repeatedlyParsesTest: Unit = {
60+
object TestTokenParser extends JavaTokenParsers
61+
import TestTokenParser._
62+
val p = ident ~ "(?i)AND".r.*
63+
64+
val parseResult = parseAll(p, "start")
65+
parseResult match {
66+
case Success(r, _) =>
67+
assertEquals("start", r._1)
68+
assertEquals(0, r._2.size)
69+
case _ => sys.error(parseResult.toString)
70+
}
71+
72+
val parseResult1 = parseAll(p, "start start")
73+
parseResult1 match {
74+
case e @ Failure(message, next) =>
75+
assertEquals(next.pos.column, 7)
76+
assert(message.endsWith("string matching regex `(?i)AND' expected but `s' found"))
77+
case _ => sys.error(parseResult1.toString)
78+
}
79+
80+
val parseResult2 = parseAll(p, "start AND AND")
81+
parseResult2 match {
82+
case Success(r, _) =>
83+
assertEquals("start", r._1)
84+
assertEquals("AND AND", r._2.mkString(" "))
85+
case _ => sys.error(parseResult2.toString)
86+
}
87+
}
88+
89+
@Test
90+
def optionParserTest: Unit = {
91+
object TestTokenParser extends JavaTokenParsers
92+
import TestTokenParser._
93+
val p = opt(ident)
94+
95+
val parseResult = parseAll(p, "-start")
96+
parseResult match {
97+
case Failure(message, next) =>
98+
assertEquals(next.pos.line, 1)
99+
assertEquals(next.pos.column, 1)
100+
assert(message.endsWith(s"regex `\\p{javaJavaIdentifierStart}\\p{javaJavaIdentifierPart}*' expected but `-' found"))
101+
case _ => sys.error(parseResult.toString)
102+
}
103+
104+
val parseResult2 = parseAll(p, "start ")
105+
parseResult2 match {
106+
case e @ Failure(message, next) =>
107+
assertEquals(next.pos.line, 1)
108+
assertEquals(next.pos.column, 6)
109+
assertEquals(message, s"string matching regex `\\z' expected but found")
110+
case _ => sys.error(parseResult2.toString)
111+
}
112+
113+
val parseResult3 = parseAll(p, "start")
114+
parseResult3 match {
115+
case Success(r, _) =>
116+
assertEquals(r, Some("start"))
117+
case _ => sys.error(parseResult3.toString)
118+
}
119+
}
120+
121+
58122
}

0 commit comments

Comments
 (0)