Skip to content

Commit 5b90691

Browse files
soyaSethTisue
soya
authored andcommitted
[backport] backport scala#234 and scala#327 to 1.1.x branch
fixes scala#233 on 1.1.x branch
1 parent f8504d7 commit 5b90691

File tree

4 files changed

+137
-34
lines changed

4 files changed

+137
-34
lines changed

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

Lines changed: 62 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -137,13 +137,21 @@ trait Parsers {
137137
* @param next The parser's remaining input
138138
*/
139139
case class Success[+T](result: T, override val next: Input) extends ParseResult[T] {
140-
def map[U](f: T => U) = Success(f(result), next)
141-
def mapPartial[U](f: PartialFunction[T, U], error: T => String): ParseResult[U]
142-
= if(f.isDefinedAt(result)) Success(f(result), next)
143-
else Failure(error(result), next)
140+
def lastFailure: Option[Failure] = None
144141

145-
def flatMapWithNext[U](f: T => Input => ParseResult[U]): ParseResult[U]
146-
= f(result)(next)
142+
def map[U](f: T => U) = Success(f(result), next, lastFailure)
143+
144+
def mapPartial[U](f: PartialFunction[T, U], error: T => String): ParseResult[U] =
145+
if(f.isDefinedAt(result)) Success(f(result), next, lastFailure)
146+
else Failure(error(result), next)
147+
148+
def flatMapWithNext[U](f: T => Input => ParseResult[U]): ParseResult[U] = f(result)(next) match {
149+
case s @ Success(result, rest) =>
150+
val failure = selectLastFailure(this.lastFailure, s.lastFailure)
151+
Success(result, rest, failure)
152+
case f: Failure => selectLastFailure(Some(f), lastFailure).get
153+
case e: Error => e
154+
}
147155

148156
def filterWithError(p: T => Boolean, error: T => String, position: Input): ParseResult[T] =
149157
if (p(result)) this
@@ -192,10 +200,16 @@ trait Parsers {
192200
/** The toString method of a Failure yields an error message. */
193201
override def toString = "["+next.pos+"] failure: "+msg+"\n\n"+next.pos.longString
194202

195-
def append[U >: Nothing](a: => ParseResult[U]): ParseResult[U] = { val alt = a; alt match {
196-
case Success(_, _) => alt
197-
case ns: NoSuccess => if (alt.next.pos < next.pos) this else alt
198-
}}
203+
def append[U >: Nothing](a: => ParseResult[U]): ParseResult[U] = {
204+
val alt = a
205+
206+
alt match {
207+
case s @ Success(result, rest) =>
208+
val failure = selectLastFailure(Some(this), s.lastFailure)
209+
Success(result, rest, failure)
210+
case ns: NoSuccess => if (alt.next.pos < next.pos) this else alt
211+
}
212+
}
199213
}
200214

201215
/** The fatal failure case of ParseResult: contains an error-message and
@@ -214,6 +228,19 @@ trait Parsers {
214228
def Parser[T](f: Input => ParseResult[T]): Parser[T]
215229
= new Parser[T]{ def apply(in: Input) = f(in) }
216230

231+
private[combinator] def Success[U](res: U, next: Input, failure: Option[Failure]): ParseResult[U] =
232+
new Success(res, next) { override val lastFailure: Option[Failure] = failure }
233+
234+
private[combinator] def selectLastFailure(failure0: Option[Failure], failure1: Option[Failure]): Option[Failure] =
235+
(failure0, failure1) match {
236+
case (Some(f0), Some(f1)) =>
237+
if(f0.next.pos < f1.next.pos) Some(f1)
238+
else Some(f0)
239+
case (Some(f0), _) => Some(f0)
240+
case (_, Some(f1)) => Some(f1)
241+
case _ => None
242+
}
243+
217244
def OnceParser[T](f: Input => ParseResult[T]): Parser[T] with OnceParser[T]
218245
= new Parser[T] with OnceParser[T] { def apply(in: Input) = f(in) }
219246

@@ -634,7 +661,7 @@ trait Parsers {
634661
*/
635662
def acceptIf(p: Elem => Boolean)(err: Elem => String): Parser[Elem] = Parser { in =>
636663
if (in.atEnd) Failure("end of input", in)
637-
else if (p(in.first)) Success(in.first, in.rest)
664+
else if (p(in.first)) Success(in.first, in.rest, None)
638665
else Failure(err(in.first), in)
639666
}
640667

@@ -653,7 +680,7 @@ trait Parsers {
653680
*/
654681
def acceptMatch[U](expected: String, f: PartialFunction[Elem, U]): Parser[U] = Parser{ in =>
655682
if (in.atEnd) Failure("end of input", in)
656-
else if (f.isDefinedAt(in.first)) Success(f(in.first), in.rest)
683+
else if (f.isDefinedAt(in.first)) Success(f(in.first), in.rest, None)
657684
else Failure(expected+" expected", in)
658685
}
659686

@@ -686,7 +713,7 @@ trait Parsers {
686713
* @param v The result for the parser
687714
* @return A parser that always succeeds, with the given result `v`
688715
*/
689-
def success[T](v: T) = Parser{ in => Success(v, in) }
716+
def success[T](v: T) = Parser{ in => Success(v, in, None) }
690717

691718
/** A helper method that turns a `Parser` into one that will
692719
* print debugging information to stdout before and after
@@ -751,19 +778,24 @@ trait Parsers {
751778
lazy val p = p0 // lazy argument
752779
val elems = new ListBuffer[T]
753780

754-
def continue(in: Input): ParseResult[List[T]] = {
781+
def continue(in: Input, failure: Option[Failure]): ParseResult[List[T]] = {
755782
val p0 = p // avoid repeatedly re-evaluating by-name parser
756-
@tailrec def applyp(in0: Input): ParseResult[List[T]] = p0(in0) match {
757-
case Success(x, rest) => elems += x ; applyp(rest)
783+
@tailrec def applyp(in0: Input, failure: Option[Failure]): ParseResult[List[T]] = p0(in0) match {
784+
case s @ Success(x, rest) =>
785+
val selectedFailure = selectLastFailure(s.lastFailure, failure)
786+
elems += x
787+
applyp(rest, selectedFailure)
758788
case e @ Error(_, _) => e // still have to propagate error
759-
case _ => Success(elems.toList, in0)
789+
case f: Failure =>
790+
val selectedFailure = selectLastFailure(failure, Some(f))
791+
Success(elems.toList, in0, selectedFailure)
760792
}
761793

762-
applyp(in)
794+
applyp(in, failure)
763795
}
764796

765797
first(in) match {
766-
case Success(x, rest) => elems += x ; continue(rest)
798+
case s @ Success(x, rest) => elems += x ; continue(rest, s.lastFailure)
767799
case ns: NoSuccess => ns
768800
}
769801
}
@@ -783,14 +815,14 @@ trait Parsers {
783815
val elems = new ListBuffer[T]
784816
val p0 = p // avoid repeatedly re-evaluating by-name parser
785817

786-
@tailrec def applyp(in0: Input): ParseResult[List[T]] =
787-
if (elems.length == num) Success(elems.toList, in0)
818+
@tailrec def applyp(in0: Input, failure: Option[Failure]): ParseResult[List[T]] =
819+
if (elems.length == num) Success(elems.toList, in0, failure)
788820
else p0(in0) match {
789-
case Success(x, rest) => elems += x ; applyp(rest)
821+
case s @ Success(x, rest) => elems += x ; applyp(rest, s.lastFailure)
790822
case ns: NoSuccess => ns
791823
}
792824

793-
applyp(in)
825+
applyp(in, None)
794826
}
795827

796828
/** A parser generator for non-empty repetitions.
@@ -872,7 +904,7 @@ trait Parsers {
872904
def not[T](p: => Parser[T]): Parser[Unit] = Parser { in =>
873905
p(in) match {
874906
case Success(_, _) => Failure("Expected failure", in)
875-
case _ => Success((), in)
907+
case _ => Success((), in, None)
876908
}
877909
}
878910

@@ -886,7 +918,7 @@ trait Parsers {
886918
*/
887919
def guard[T](p: => Parser[T]): Parser[T] = Parser { in =>
888920
p(in) match{
889-
case s@ Success(s1,_) => Success(s1, in)
921+
case s@ Success(s1,_) => Success(s1, in, s.lastFailure)
890922
case e => e
891923
}
892924
}
@@ -901,7 +933,7 @@ trait Parsers {
901933
*/
902934
def positioned[T <: Positional](p: => Parser[T]): Parser[T] = Parser { in =>
903935
p(in) match {
904-
case Success(t, in1) => Success(if (t.pos == NoPosition) t setPos in.pos else t, in1)
936+
case s @ Success(t, in1) => Success(if (t.pos == NoPosition) t setPos in.pos else t, in1, s.lastFailure)
905937
case ns: NoSuccess => ns
906938
}
907939
}
@@ -919,7 +951,10 @@ trait Parsers {
919951
def apply(in: Input) = p(in) match {
920952
case s @ Success(out, in1) =>
921953
if (in1.atEnd) s
922-
else Failure("end of input expected", in1)
954+
else s.lastFailure match {
955+
case Some(failure) => failure
956+
case _ => Failure("end of input expected", in1)
957+
}
923958
case ns => ns
924959
}
925960
}

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

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ trait RegexParsers extends Parsers {
9494
j += 1
9595
}
9696
if (i == s.length)
97-
Success(source.subSequence(start, j).toString, in.drop(j - offset))
97+
Success(source.subSequence(start, j).toString, in.drop(j - offset), None)
9898
else {
9999
val found = if (start == source.length()) "end of source" else "'"+source.charAt(start)+"'"
100100
Failure("'"+s+"' expected but "+found+" found", in.drop(start - offset))
@@ -111,7 +111,8 @@ trait RegexParsers extends Parsers {
111111
(r findPrefixMatchOf (new SubSequence(source, start))) match {
112112
case Some(matched) =>
113113
Success(source.subSequence(start, start + matched.end).toString,
114-
in.drop(start + matched.end - offset))
114+
in.drop(start + matched.end - offset),
115+
None)
115116
case None =>
116117
val found = if (start == source.length()) "end of source" else "'"+source.charAt(start)+"'"
117118
Failure("string matching regex '"+r+"' expected but "+found+" found", in.drop(start - offset))

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

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ class JavaTokenParsersTest {
3333
def parseFailure(s: String, errorColPos: Int): Unit = {
3434
val parseResult = parseAll(ident, s)
3535
parseResult match {
36-
case Failure(_, next) =>
36+
case Failure(msg, next) =>
3737
val pos = next.pos
3838
assertEquals(1, pos.line)
3939
assertEquals(errorColPos, pos.column)
@@ -53,7 +53,7 @@ class JavaTokenParsersTest {
5353
parseFailure("with-s", 5)
5454
// we♥scala
5555
parseFailure("we\u2665scala", 3)
56-
parseFailure("with space", 6)
56+
parseFailure("with space", 5)
5757
}
5858

5959
@Test
@@ -75,7 +75,7 @@ class JavaTokenParsersTest {
7575
case e @ Failure(message, next) =>
7676
assertEquals(next.pos.line, 1)
7777
assertEquals(next.pos.column, 7)
78-
assert(message.endsWith(s"end of input expected"))
78+
assert(message.endsWith("string matching regex '(?i)AND' expected but 's' found"))
7979
case _ => sys.error(parseResult1.toString)
8080
}
8181

@@ -99,7 +99,7 @@ class JavaTokenParsersTest {
9999
case Failure(message, next) =>
100100
assertEquals(next.pos.line, 1)
101101
assertEquals(next.pos.column, 1)
102-
assert(message.endsWith(s"end of input expected"))
102+
assert(message.endsWith(s"identifier expected but '-' found"))
103103
case _ => sys.error(parseResult.toString)
104104
}
105105

shared/src/test/scala/scala/util/parsing/combinator/RegexParsersTest.scala

Lines changed: 68 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
package scala.util.parsing.combinator
22

33
import org.junit.Test
4-
import org.junit.Assert.assertEquals
4+
import org.junit.Assert.{ assertEquals, assertTrue }
55

66
class RegexParsersTest {
77
@Test
@@ -100,4 +100,71 @@ class RegexParsersTest {
100100
val success = parseAll(twoWords, "first second").asInstanceOf[Success[(String, String)]]
101101
assertEquals(("second", "first"), success.get)
102102
}
103+
104+
@Test
105+
def hierarchicalRepSuccess: Unit = {
106+
case class Node(a: String, b: String)
107+
108+
object parser extends RegexParsers {
109+
def top: Parser[List[List[Node]]] = rep(nodes)
110+
def nodes: Parser[List[Node]] = "{" ~> rep(node) <~ "}"
111+
def node: Parser[Node] = "[a-z]+".r ~ ":" ~ "[a-z]+".r ^^ { case a ~ _ ~ b => Node(a, b) }
112+
}
113+
114+
import parser._
115+
116+
val success0 = parseAll(top, "{ a : b c : d}").get
117+
assertEquals(List(List(Node("a", "b"), Node("c", "d"))), success0)
118+
val success1 = parseAll(top, "{ a : b } { c : d }").get
119+
assertEquals(List(List(Node("a", "b")), List(Node("c", "d"))), success1)
120+
val success2 = parseAll(top, "{} {}").get
121+
assertEquals(List(List(), List()), success2)
122+
val success3 = parseAll(top, "").get
123+
assertEquals(List(), success3)
124+
}
125+
126+
@Test
127+
def hierarchicalRepFailure: Unit = {
128+
case class Node(a: String, b: String)
129+
130+
object parser extends RegexParsers {
131+
def top: Parser[List[List[Node]]] = rep(nodes)
132+
def nodes: Parser[List[Node]] = "{" ~> rep(node) <~ "}"
133+
def node: Parser[Node] = "[a-z]+".r ~ ":" ~ "[a-z]+".r ^^ { case a ~ _ ~ b => Node(a, b) }
134+
}
135+
136+
def test(src: String, expect: String, column: Int): Unit = {
137+
import parser._
138+
val result = parseAll(top, src)
139+
result match {
140+
case Failure(msg, next) =>
141+
assertEquals(column, next.pos.column)
142+
assertEquals(expect, msg)
143+
case _ =>
144+
sys.error(result.toString)
145+
}
146+
}
147+
148+
test("{ a : b c : }", "string matching regex '[a-z]+' expected but '}' found", 13)
149+
test("{", "'}' expected but end of source found", 2)
150+
}
151+
152+
@Test
153+
def ifElseTest: Unit = {
154+
object parser extends RegexParsers {
155+
def top: Parser[List[Unit]] = rep(ifelse)
156+
def ifelse: Parser[Unit] = "IF" ~ condition ~ "THEN" ~ "1"~ "END" ^^ { _ => }
157+
def condition: Parser[String] = "TRUE" | "FALSE"
158+
}
159+
160+
import parser._
161+
val res = parseAll(top, "IF FALSE THEN 1 IF TRUE THEN 1 END")
162+
res match {
163+
case Failure(msg, next) =>
164+
assertEquals(17, next.pos.column)
165+
assertEquals("'END' expected but 'I' found", msg)
166+
case _ =>
167+
sys.error(res.toString)
168+
}
169+
}
103170
}

0 commit comments

Comments
 (0)