From c8b9995c9a8a96f48c6f2fea78a296ee9e10a013 Mon Sep 17 00:00:00 2001 From: Raymond Dodge Date: Fri, 23 Feb 2024 23:46:39 -0500 Subject: [PATCH] [base] Add an id variant of the string concatenator repeated Also, add some unit tests for the new repeateds --- Base/src/main/scala/typeclass/Repeat.scala | 22 ++++++++++++++ .../VersonSpecificRepeatedTest.scala | 28 +++++++++++++++++ .../VersonSpecificRepeatedTestImpls.scala | 30 +++++++++++++++++++ .../test/scala/typeclass/RepeatedTest.scala | 21 +++++++++++++ 4 files changed, 101 insertions(+) create mode 100644 Base/src/test/scala-3/typeclass/VersonSpecificRepeatedTest.scala create mode 100644 Base/src/test/scala-3/typeclass/VersonSpecificRepeatedTestImpls.scala create mode 100644 Base/src/test/scala/typeclass/RepeatedTest.scala diff --git a/Base/src/main/scala/typeclass/Repeat.scala b/Base/src/main/scala/typeclass/Repeat.scala index f5dfc7f..f99af5f 100644 --- a/Base/src/main/scala/typeclass/Repeat.scala +++ b/Base/src/main/scala/typeclass/Repeat.scala @@ -86,6 +86,20 @@ trait BiRepeated[Expr[_], A, Z] /** Predefined implicit implementations of Repeated */ object Repeated extends VersionSpecificRepeated with LowPrioRepeated { + private def apply[A, Acc, Z]( + initFn: () => Acc, + appendFn: (Acc, A) => Acc, + resultFn: Acc => Z, + ): Repeated[A, Z] = { + type Acc2 = Acc + new Repeated[A, Z] { + type Acc = Acc2 + def init():Acc = initFn() + def append(acc:Acc, elem:A):Acc = appendFn(acc, elem) + def result(acc:Acc):Z = resultFn(acc) + } + } + /** * Repeated units results in a unit */ @@ -124,6 +138,14 @@ object Repeated extends VersionSpecificRepeated with LowPrioRepeated { } new RepeatedCodepoint() } + + def idConcatenateString:Repeated[String, String] = { + Repeated.apply( + () => new StringBuilder, + (acc:StringBuilder, elem:String) => acc ++= elem, + (acc:StringBuilder) => acc.toString, + ) + } } private[typeclass] trait LowPrioRepeated { diff --git a/Base/src/test/scala-3/typeclass/VersonSpecificRepeatedTest.scala b/Base/src/test/scala-3/typeclass/VersonSpecificRepeatedTest.scala new file mode 100644 index 0000000..c411be7 --- /dev/null +++ b/Base/src/test/scala-3/typeclass/VersonSpecificRepeatedTest.scala @@ -0,0 +1,28 @@ +package name.rayrobdod.stringContextParserCombinator +package typeclass +package repeated + +import scala.quoted.* +import munit.Location + +final class QuotedConcatenateStringTest extends munit.FunSuite { + inline def assertParseSuccess( + inline sc: StringContext, + inline args: Any*)( + expecting:String)( + using loc:Location + ):Unit = ${ + QuotedConcatenateStringTestImpls.assertParseSuccessImpl( + 'this, 'sc, 'args, 'expecting, 'loc) + } + + test ("0") { + assertParseSuccess(StringContext(""))("") + } + test ("1") { + assertParseSuccess(StringContext("ab"))("ab") + } + test ("many") { + assertParseSuccess(StringContext("ab", "cd", "ef"), "12", "34")("ab12cd34ef") + } +} diff --git a/Base/src/test/scala-3/typeclass/VersonSpecificRepeatedTestImpls.scala b/Base/src/test/scala-3/typeclass/VersonSpecificRepeatedTestImpls.scala new file mode 100644 index 0000000..76c4487 --- /dev/null +++ b/Base/src/test/scala-3/typeclass/VersonSpecificRepeatedTestImpls.scala @@ -0,0 +1,30 @@ +package name.rayrobdod.stringContextParserCombinator +package typeclass +package repeated + +import scala.quoted.* +import munit.Location +import Interpolator.{charWhere, ofType, end} + +object QuotedConcatenateStringTestImpls { + def assertParseSuccessImpl( + self: Expr[munit.FunSuite], + sc: Expr[StringContext], + args: Expr[Seq[Any]], + expecting: Expr[String], + loc: Expr[Location])( + using Quotes + ):Expr[Unit] = { + + val dut = (charWhere(_ => true).repeat(1).mapToExpr orElse ofType[String]) + .repeat()(using Repeated.quotedConcatenateString) + .andThen(end) + + val actual = dut.interpolate(sc, args) + + '{ + given Location = ${loc} + $self.assertEquals($actual, $expecting) + } + } +} diff --git a/Base/src/test/scala/typeclass/RepeatedTest.scala b/Base/src/test/scala/typeclass/RepeatedTest.scala new file mode 100644 index 0000000..c7c3bd4 --- /dev/null +++ b/Base/src/test/scala/typeclass/RepeatedTest.scala @@ -0,0 +1,21 @@ +package name.rayrobdod.stringContextParserCombinator +package typeclass +package repeated + +import Interpolator.idInterpolators.{charWhere, ofType, end} + +final class IdConcatenateString extends BaseInterpolatorSuite { + val dut = (charWhere(_ => true).repeat(1) orElse ofType[String]) + .repeat()(using Repeated.idConcatenateString) + .andThen(end) + + test ("0") { + assertParseSuccess(dut, ("" :: Nil, Nil), "") + } + test ("1") { + assertParseSuccess(dut, ("ab" :: Nil, Nil), "ab") + } + test ("many") { + assertParseSuccess(dut, ("ab" :: "cd" :: "ef" :: Nil, "12" :: "34" :: Nil), "ab12cd34ef") + } +}