From 4b415c8ae094ef892a2ce56b2a72da4178111f89 Mon Sep 17 00:00:00 2001 From: Jamie Willis Date: Sun, 25 Dec 2022 13:43:36 +0000 Subject: [PATCH] Added `Defer` instance, bump to 0.2.0. (#1) By adding `Defer[Parsley]`, there is now support for idiomatic recursive parser construction. --- README.md | 10 ++++++---- build.sbt | 7 +++++-- .../src/main/scala/parsley/DeferForParsley.scala | 9 +++++++++ parsley-cats/shared/src/main/scala/parsley/cats.scala | 8 +++++++- .../shared/src/test/scala/parsley/CatsSuite.scala | 8 ++++++++ 5 files changed, 35 insertions(+), 7 deletions(-) create mode 100644 parsley-cats/shared/src/main/scala/parsley/DeferForParsley.scala diff --git a/README.md b/README.md index 0937a51..8a904f6 100644 --- a/README.md +++ b/README.md @@ -2,16 +2,17 @@ ## What is `parsley-cats`? -The `parsley-cats` library exposes `cats` instances for `MonoidK[Parsley]`, `Monad[Parsley]`, and `FunctorFilter[Parsley]`. +The `parsley-cats` library exposes `cats` instances for `MonoidK[Parsley]`, `Monad[Parsley]`, and `FunctorFilter[Parsley]` as well as `Defer[Parsley]`. Care should still be taken to not define truly recursive parsers using the `cats` API (although monadic parser with `flatMap` -may be generally recursive, just slow). +may be generally recursive, just slow). In particular, make use of `Defer[Parsley].fix` +to handle recursion, or plain `lazy val` based construction (as in regular `parsley` use). ## How do I use it? [![parsley-cats Scala version support](https://index.scala-lang.org/j-mie6/parsley-cats/parsley-cats/latest-by-scala-version.svg?platform=jvm)](https://index.scala-lang.org/j-mie6/parsley-cats/parsley-cats) [![parsley-cats Scala version support](https://index.scala-lang.org/j-mie6/parsley-cats/parsley-cats/latest-by-scala-version.svg?platform=sjs1)](https://index.scala-lang.org/j-mie6/parsley-cats/parsley-cats) [![parsley-cats Scala version support](https://index.scala-lang.org/j-mie6/parsley-cats/parsley-cats/latest-by-scala-version.svg?platform=native0.4)](https://index.scala-lang.org/j-mie6/parsley-cats/parsley-cats) Parsley cats is distributed on Maven Central, and can be added to your project via: ```scala -libraryDependencies += "com.github.j-mie6" %% "parsley-cats" % "0.1.0" +libraryDependencies += "com.github.j-mie6" %% "parsley-cats" % "0.2.0" ``` it requires `parsley` and `cats-core` to also be dependencies of your project. The current version @@ -19,7 +20,8 @@ matrix for `parsley-cats`: | `parsley-cats` version | `parsley` version | `cats-core` version | | :--------------------: | :---------------: | :-----------------: | -| `0.1.0` | `>= 4 && < 5` | `>= 2.8 && < 3` | +| `0.1.x` | `>= 4 && < 5` | `>= 2.8 && < 3` | +| `0.2.x` | `>= 4 && < 5` | `>= 2.8 && < 3` | Documentation can be found [**here**][Link-Scaladoc] diff --git a/build.sbt b/build.sbt index 4ee190a..579d6a5 100644 --- a/build.sbt +++ b/build.sbt @@ -8,7 +8,7 @@ val Scala3 = "3.2.1" Global / onChangedBuildSource := ReloadOnSourceChanges inThisBuild(List( - tlBaseVersion := "0.1", + tlBaseVersion := "0.2", organization := "com.github.j-mie6", startYear := Some(2022), homepage := Some(url("https://github.com/j-mie6/parsley-cats")), @@ -38,7 +38,10 @@ lazy val `parsley-cats` = crossProject(JVMPlatform, JSPlatform, NativePlatform) "com.github.j-mie6" %%% "parsley" % "4.0.0" % Provided, "org.scalatest" %%% "scalatest" % "3.2.12" % Test, "org.typelevel" %%% "cats-laws" % "2.8.0" % Test, - ) + ), + mimaPreviousArtifacts := Set( + "com.github.j-mie6" %% "parsley-cats" % "0.1.0", + ), ) .jsSettings( Test / scalaJSLinkerConfig := scalaJSLinkerConfig.value.withESFeatures(_.withESVersion(ESVersion.ES2018)) diff --git a/parsley-cats/shared/src/main/scala/parsley/DeferForParsley.scala b/parsley-cats/shared/src/main/scala/parsley/DeferForParsley.scala new file mode 100644 index 0000000..ef7d11c --- /dev/null +++ b/parsley-cats/shared/src/main/scala/parsley/DeferForParsley.scala @@ -0,0 +1,9 @@ +package parsley + +import cats.Defer + +import Parsley.LazyParsley + +private [parsley] class DeferForParsley extends Defer[Parsley] { + def defer[A](p: =>parsley.Parsley[A]): parsley.Parsley[A] = ~p +} diff --git a/parsley-cats/shared/src/main/scala/parsley/cats.scala b/parsley-cats/shared/src/main/scala/parsley/cats.scala index c1dbc45..b58f0bd 100644 --- a/parsley-cats/shared/src/main/scala/parsley/cats.scala +++ b/parsley-cats/shared/src/main/scala/parsley/cats.scala @@ -3,7 +3,7 @@ */ package parsley -import cats.{Monad, MonoidK, FunctorFilter} +import cats.{Monad, MonoidK, FunctorFilter, Defer} /** Contains instances for `cats` typeclasses. * @@ -20,4 +20,10 @@ object catsinstances { with FunctorForParsley with MonoidKForParsley with FunctorFilterForParsley + + /** Instance for `cats` `Defer` typeclass, which allows for recursive parser generation. + * + * @since 0.2.0 + */ + implicit val deferForParsley: Defer[Parsley] = new DeferForParsley } diff --git a/parsley-cats/shared/src/test/scala/parsley/CatsSuite.scala b/parsley-cats/shared/src/test/scala/parsley/CatsSuite.scala index 578d99a..2e7f728 100644 --- a/parsley-cats/shared/src/test/scala/parsley/CatsSuite.scala +++ b/parsley-cats/shared/src/test/scala/parsley/CatsSuite.scala @@ -4,6 +4,7 @@ import org.scalatest.flatspec.AnyFlatSpec import org.scalatest.matchers.should.Matchers._ import org.scalactic.source.Position +import parsley.Parsley.pure import parsley.character.{item, digit, char} import parsley.catsinstances._ @@ -48,4 +49,11 @@ class CatsSuite extends AnyFlatSpec { applyLaw(monoidKLaws.monoidKLeftIdentity(item), monoidKLaws.monoidKRightIdentity(item))("", "a", "b") applyLaw(monoidKLaws.combineAllK(Vector(char('a'), char('b'), char('c'))))("", "a", "b", "c") } + + "Defer" should "allow for the construction of recursive parsers" in { + val manya = deferForParsley.fix[List[Char]] { self => + char('a') <::> self <|> pure(Nil) + } + manya.parse("aaaaaaaaaaaab") shouldBe Success(List.fill(12)('a')) + } }