Skip to content

Commit bfa5224

Browse files
authored
Improve error reporting for rep0, repAs0 (#396)
1 parent 7856e98 commit bfa5224

3 files changed

Lines changed: 39 additions & 31 deletions

File tree

core/shared/src/main/scala/cats/parse/Parser.scala

Lines changed: 17 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1099,7 +1099,11 @@ object Parser {
10991099
* this can wind up parsing nothing
11001100
*/
11011101
def repAs0[A, B](p1: Parser[A])(implicit acc: Accumulator0[A, B]): Parser0[B] =
1102-
Impl.Rep0(p1, Int.MaxValue, acc)
1102+
Impl.OneOf0(
1103+
Impl.Rep(p1, 1, Int.MaxValue, acc) ::
1104+
pure(acc.newAppender().finish()) ::
1105+
Nil
1106+
)
11031107

11041108
/** Repeat the parser 0 or more times, but no more than `max`
11051109
*
@@ -1114,12 +1118,17 @@ object Parser {
11141118
*/
11151119
def repAs0[A, B](p1: Parser[A], max: Int)(implicit acc: Accumulator0[A, B]): Parser0[B] = {
11161120
require(max >= 0, s"max should be >= 0, was $max")
1121+
val empty = acc.newAppender().finish()
11171122
if (max == 0) {
11181123
// exactly 0 times
1119-
pure(acc.newAppender().finish())
1124+
pure(empty)
11201125
} else {
11211126
// 0 or more times
1122-
Impl.Rep0(p1, max - 1, acc)
1127+
Impl.OneOf0(
1128+
Impl.Rep(p1, 1, max - 1, acc) ::
1129+
pure(empty) ::
1130+
Nil
1131+
)
11231132
}
11241133
}
11251134

@@ -1730,7 +1739,7 @@ object Parser {
17301739
case peek @ Impl.Peek(_) => peek
17311740
case s if Impl.alwaysSucceeds(s) => unit
17321741
case notPeek =>
1733-
// TODO: we can adjust Rep0/Rep to do minimal
1742+
// TODO: we can adjust Rep to do minimal
17341743
// work since we rewind after we are sure there is
17351744
// a match
17361745
Impl.Peek(void0(notPeek))
@@ -2257,9 +2266,9 @@ object Parser {
22572266
) =>
22582267
// these are always unit
22592268
someUnit.asInstanceOf[Option[A]]
2260-
case Rep0(_, _, _) | Rep(_, _, _, _) | FlatMap0(_, _) | FlatMap(_, _) | TailRecM(_, _) |
2261-
TailRecM0(_, _) | Defer(_) | Defer0(_) | GetCaret | Index | Length(_) | Fail() |
2262-
FailWith(_) | CharIn(_, _, _) | AnyChar | StringP(
2269+
case Rep(_, _, _, _) | FlatMap0(_, _) | FlatMap(_, _) | TailRecM(_, _) | TailRecM0(_, _) |
2270+
Defer(_) | Defer0(_) | GetCaret | Index | Length(_) | Fail() | FailWith(_) |
2271+
CharIn(_, _, _) | AnyChar | StringP(
22632272
_
22642273
) | OneOf(Nil) | OneOf0(Nil) | StringP0(_) | Select(_, _) | Select0(_, _) | StringIn(
22652274
_
@@ -2290,8 +2299,7 @@ object Parser {
22902299
case Length(_) | StringP(_) | StringIn(_) | Prod(_, _) | SoftProd(_, _) | Map(_, _) |
22912300
Select(_, _) | FlatMap(_, _) | TailRecM(_, _) | Defer(_) | Rep(_, _, _, _) | AnyChar |
22922301
CharIn(_, _, _) | StringP0(_) | Index | GetCaret | Prod0(_, _) | SoftProd0(_, _) |
2293-
Map0(_, _) | Select0(_, _) | FlatMap0(_, _) | TailRecM0(_, _) | Defer0(_) |
2294-
Rep0(_, _, _) =>
2302+
Map0(_, _) | Select0(_, _) | FlatMap0(_, _) | TailRecM0(_, _) | Defer0(_) =>
22952303
false
22962304
}
22972305

@@ -2365,7 +2373,6 @@ object Parser {
23652373
case UnmapDefer0(_) => pa // already unmapped
23662374
case _ => Defer0(UnmapDefer0(fn))
23672375
}
2368-
case Rep0(p, max, _) => Rep0(unmap(p), max, Accumulator0.unitAccumulator0)
23692376
case WithContextP0(ctx, p0) => WithContextP0(ctx, unmap0(p0))
23702377
case StartParser | EndParser | TailRecM0(_, _) | FlatMap0(_, _) =>
23712378
// we can't transform this significantly
@@ -2962,22 +2969,6 @@ object Parser {
29622969
}
29632970
}
29642971

2965-
case class Rep0[A, B](p1: Parser[A], maxMinusOne: Int, acc: Accumulator0[A, B])
2966-
extends Parser0[B] {
2967-
private[this] val ignore: B = null.asInstanceOf[B]
2968-
2969-
override def parseMut(state: State): B = {
2970-
if (state.capture) {
2971-
val app = acc.newAppender()
2972-
if (repCapture(p1, 0, maxMinusOne, state, app)) app.finish()
2973-
else ignore
2974-
} else {
2975-
repNoCapture(p1, 0, maxMinusOne, state)
2976-
ignore
2977-
}
2978-
}
2979-
}
2980-
29812972
/** A parser that can repeats the underlying parser multiple times
29822973
*/
29832974
case class Rep[A, B](p1: Parser[A], min: Int, maxMinusOne: Int, acc1: Accumulator[A, B])

core/shared/src/test/scala/cats/parse/ParserTest.scala

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3012,4 +3012,13 @@ class ParserTest extends munit.ScalaCheckSuite {
30123012
assertEquals(left.parse(str).void, right.parse(str).void)
30133013
}
30143014
}
3015+
3016+
property("a.rep0 ~ b is the same as (a.repAs ~ b) | (pure(Nil).with1 ~ b)") {
3017+
forAll(ParserGen.gen, ParserGen.gen, arbitrary[String]) { (a, b, str) =>
3018+
val left = a.fa.rep0 ~ b.fa
3019+
val right = (a.fa.repAs[List[a.A]] ~ b.fa) | (Parser.pure(List.empty[a.A]).with1 ~ b.fa)
3020+
3021+
assertEquals(left.parse(str), right.parse(str))
3022+
}
3023+
}
30153024
}

project/MimaExclusionRules.scala

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
1-
import com.typesafe.tools.mima.core.ProblemFilters
2-
import com.typesafe.tools.mima.core.IncompatibleMethTypeProblem
3-
import com.typesafe.tools.mima.core.IncompatibleResultTypeProblem
4-
import com.typesafe.tools.mima.core.DirectMissingMethodProblem
1+
import com.typesafe.tools.mima.core.{
2+
ProblemFilters,
3+
IncompatibleMethTypeProblem,
4+
IncompatibleResultTypeProblem,
5+
DirectMissingMethodProblem,
6+
MissingClassProblem
7+
}
8+
59
object MimaExclusionRules {
610
val parserImpl = Seq(
711
"cats.parse.Parser#Impl.mergeCharIn",
@@ -18,7 +22,11 @@ object MimaExclusionRules {
1822
).map(ProblemFilters.exclude[IncompatibleResultTypeProblem](_)) ++ Seq(
1923
"cats.parse.Parser#Impl.mergeCharIn",
2024
"cats.parse.Parser#Impl.mergeStrIn"
21-
).map(ProblemFilters.exclude[DirectMissingMethodProblem](_))
25+
).map(ProblemFilters.exclude[DirectMissingMethodProblem](_)) ++ Seq(
26+
"cats.parse.Parser$Impl$Rep0",
27+
"cats.parse.Parser$Impl$Rep0$"
28+
).map(ProblemFilters.exclude[MissingClassProblem](_))
29+
2230
// TODO: Remove these rules in future release.
2331
val bitSetUtil = Seq(
2432
"cats.parse.BitSetUtil.isSingleton",

0 commit comments

Comments
 (0)