Skip to content

Commit

Permalink
Ready for 1.3, version bumps and deprecated old combinator
Browse files Browse the repository at this point in the history
  • Loading branch information
j-mie6 committed Jan 9, 2024
1 parent 384dd8b commit dbc89a6
Show file tree
Hide file tree
Showing 8 changed files with 70 additions and 45 deletions.
30 changes: 16 additions & 14 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,19 @@ import org.scalajs.linker.interface.ESVersion
import com.typesafe.tools.mima.core._

val projectName = "parsley-cats"
val Scala213 = "2.13.10"
val Scala212 = "2.12.17"
val Scala3 = "3.2.1"
val Scala213 = "2.13.12"
val Scala212 = "2.12.18"
val Scala3 = "3.3.1"

Global / onChangedBuildSource := ReloadOnSourceChanges

inThisBuild(List(
tlBaseVersion := "1.2",
tlBaseVersion := "1.3",
organization := "com.github.j-mie6",
organizationName := "Parsley-Cats Contributors <https://github.com/j-mie6/parsley-cats/graphs/contributors>",
startYear := Some(2022),
homepage := Some(url("https://github.com/j-mie6/parsley-cats")),
licenses := List("BSD-3-Clause" -> url("https://opensource.org/licenses/BSD-3-Clause")),
developers := List(
tlGitHubDev("j-mie6", "Jamie Willis")
),
versionScheme := Some("early-semver"),
crossScalaVersions := Seq(Scala213, Scala212, Scala3),
scalaVersion := Scala213,
Expand All @@ -30,8 +28,9 @@ inThisBuild(List(
ProblemFilters.exclude[MissingClassProblem]("parsley.MonoidKForParsley"),
),
// CI Configuration
tlCiReleaseBranches := Seq("master"),
tlSonatypeUseLegacyHost := false,
tlCiReleaseBranches := Seq("staging/1.3"),
tlCiScalafmtCheck := false,
tlCiHeaderCheck := true,
githubWorkflowJavaVersions := Seq(JavaSpec.temurin("8"), JavaSpec.temurin("11"), JavaSpec.temurin("17")),
))

Expand All @@ -43,13 +42,16 @@ lazy val `parsley-cats` = crossProject(JVMPlatform, JSPlatform, NativePlatform)
.in(file("parsley-cats"))
.settings(
name := projectName,
headerLicenseStyle := HeaderLicenseStyle.SpdxSyntax,
headerEmptyLine := false,

resolvers ++= Opts.resolver.sonatypeOssReleases, // Will speed up MiMA during fast back-to-back releases
libraryDependencies ++= Seq(
"org.typelevel" %%% "cats-core" % "2.8.0",
"com.github.j-mie6" %%% "parsley" % "4.0.0",
"org.scalatest" %%% "scalatest" % "3.2.12" % Test,
"com.github.j-mie6" %%% "parsley" % "4.5.0",
"org.scalatest" %%% "scalatest" % "3.2.17" % Test,
"org.typelevel" %%% "cats-laws" % "2.8.0" % Test,
),
)
.jsSettings(
Test / scalaJSLinkerConfig := scalaJSLinkerConfig.value.withESFeatures(_.withESVersion(ESVersion.ES2018))

Test / testOptions += Tests.Argument(TestFrameworks.ScalaTest, "-oI"),
)
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ private [parsley] trait ApplicativeForParsley extends Applicative[Parsley] {
override def ap[A, B](mf: Parsley[A => B])(mx: Parsley[A]): Parsley[B] = mf <*> mx

override def replicateA[A](n: Int, mx: Parsley[A]): Parsley[List[A]] = parsley.combinator.exactly(n, mx)
override def replicateA_[A](n: Int, mx: Parsley[A]): Parsley[Unit] = parsley.combinator.skip(mx, (1 until n).map(_ => mx): _*)
override def replicateA_[A](n: Int, mx: Parsley[A]): Parsley[Unit] = replicateA(n, mx).void

// Maps and Tuples
override def map2[A, B, Z](mx: Parsley[A], my: Parsley[B])(f: (A, B) => Z): Parsley[Z] = lift2(f, mx, my)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
package parsley.cats

import parsley.Parsley
import parsley.registers.{RegisterMaker, RegisterMethods}
import parsley.state.{RefMaker, StateCombinators}

import cats.{Alternative, Monad}

Expand All @@ -17,17 +17,17 @@ private [parsley] trait MonadForParsley extends Monad[Parsley] {
}

// Monad Overrides
override def ifM[B](mx: Parsley[Boolean])(ifTrue: => Parsley[B], ifFalse: => Parsley[B]): Parsley[B] = parsley.combinator.ifP(mx, ifTrue, ifFalse)
override def ifM[B](mx: Parsley[Boolean])(ifTrue: => Parsley[B], ifFalse: => Parsley[B]): Parsley[B] = parsley.combinator.ifS(mx, ifTrue, ifFalse)
override def whileM_[A](p: Parsley[Boolean])(body: =>Parsley[A]): Parsley[Unit] = {
parsley.combinator.when(p, parsley.combinator.whileP(body ~> p))
parsley.combinator.whenS(p, parsley.combinator.whileS(body ~> p))
}
override def untilM_[A](body: Parsley[A])(p: => Parsley[Boolean]): Parsley[Unit] = parsley.combinator.whileP(body *> p.map(!_))
override def untilM_[A](body: Parsley[A])(p: => Parsley[Boolean]): Parsley[Unit] = parsley.combinator.whileS(body *> p.map(!_))

override def whileM[G[_]: Alternative, A](p: Parsley[Boolean])(body: => Parsley[A]): Parsley[G[A]] = {
val G = implicitly[Alternative[G]]
G.empty[A].makeReg { acc =>
G.empty[A].makeRef { acc =>
whileM_(p) {
acc.modify(body.map(x => (xs: G[A]) => G.appendK(xs, x)))
acc.update(body.map(x => (xs: G[A]) => G.appendK(xs, x)))
} *> acc.get
}
}
Expand All @@ -44,19 +44,19 @@ private [parsley] trait MonadForParsley extends Monad[Parsley] {

override def iterateUntil[A](mx: Parsley[A])(p: A => Boolean): Parsley[A] = {
lazy val loop: Parsley[A] = mx.persist { mx =>
parsley.combinator.ifP(mx.map(p), mx, loop)
parsley.combinator.ifS(mx.map(p), mx, loop)
}
loop
}
override def iterateWhile[A](mx: Parsley[A])(p: A => Boolean): Parsley[A] = {
lazy val loop: Parsley[A] = mx.persist { mx =>
parsley.combinator.ifP(mx.map(p), loop, mx)
parsley.combinator.ifS(mx.map(p), loop, mx)
}
loop
}
override def ifElseM[A](branches: (Parsley[Boolean], Parsley[A])*)(els: Parsley[A]): Parsley[A] = {
branches.foldRight(els) {
case ((cond, t), e) => parsley.combinator.ifP(cond, t, e)
case ((cond, t), e) => parsley.combinator.ifS(cond, t, e)
}
}
}
39 changes: 35 additions & 4 deletions parsley-cats/shared/src/main/scala/parsley/cats/combinator.scala
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,12 @@ package parsley.cats

import cats.data.NonEmptyList

import parsley.Parsley, Parsley.notFollowedBy
import parsley.combinator.{many, manyUntil}
import parsley.Parsley, Parsley.{notFollowedBy, many}
import parsley.combinator.{manyTill}
import parsley.lift.lift2

import scala.annotation.nowarn

/** This module contains pre-made combinators that are very useful for a variety of purposes, specialised to `cats`.
*
* In particular, it contains functionality found normally in `parsley.combinator`, but returning the `cats` `NonEmptyList`
Expand Down Expand Up @@ -46,6 +48,34 @@ object combinator {
*/
def some[A](p: Parsley[A]): Parsley[NonEmptyList[A]] = nonEmptyList(p, many(p))

/** This combinator repeatedly parses a given parser '''one''' or more times, until the `end` parser succeeds, collecting the results into a list.
*
* First ensures that trying to parse `end` fails, then tries to parse `p`. If it succeed then it will repeatedly: try to parse `end`, if it fails
* '''without consuming input''', then parses `p`, which must succeed. When `end` does succeed, this combinator will return all of the results
* generated by `p`, `x,,1,,` through `x,,n,,` (with `n >= 1`), in a non-empty list: `NonEmptyList.of(x,,1,,, .., x,,n,,)`. The parser `p` must succeed
* at least once before `end` succeeds.
*
* @example This can be useful for scanning comments: {{{
* scala> import parsley.character.{string, item, endOfLine}
* scala> import parsley.cats.combinator.someTill
* scala> val comment = string("//") *> someTill(item, endOfLine)
* scala> p.parse("//hello world")
* val res0 = Failure(..)
* scala> p.parse("//hello world\n")
* val res1 = Success(NonEmptyList.of('h', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd'))
* scala> p.parse("//\n")
* val res2 = Failure(..)
* scala> p.parse("//a\n")
* val res3 = Success(NonEmptyList.of('a'))
* }}}
*
* @param p the parser to execute multiple times.
* @param end the parser that stops the parsing of `p`.
* @return a parser that parses `p` until `end` succeeds, returning the non-empty list of all the successful results.
* @since 1.3.0
*/
def someTill[A](p: Parsley[A], end: Parsley[_]): Parsley[NonEmptyList[A]] = notFollowedBy(end) *> (nonEmptyList(p, manyTill(p, end)))

/** This combinator repeatedly parses a given parser '''one''' or more times, until the `end` parser succeeds, collecting the results into a list.
*
* First ensures that trying to parse `end` fails, then tries to parse `p`. If it succeed then it will repeatedly: try to parse `end`, if it fails
Expand All @@ -72,7 +102,8 @@ object combinator {
* @return a parser that parses `p` until `end` succeeds, returning the non-empty list of all the successful results.
* @since 1.2.0
*/
def someUntil[A](p: Parsley[A], end: Parsley[_]): Parsley[NonEmptyList[A]] = notFollowedBy(end) *> (nonEmptyList(p, manyUntil(p, end)))
@deprecated("This combinator will be removed in 2.0.0, use someTill instead", "1.3.0")
def someUntil[A](p: Parsley[A], end: Parsley[_]): Parsley[NonEmptyList[A]] = notFollowedBy(end) *> (nonEmptyList(p, manyTill(p, end)))

/** This combinator parses '''one''' or more occurrences of `p`, separated by `sep`.
*
Expand Down Expand Up @@ -127,7 +158,7 @@ object combinator {
* @since 1.2.0
*/
def sepEndBy1[A](p: Parsley[A], sep: =>Parsley[_]): Parsley[NonEmptyList[A]] = parsley.combinator.sepEndBy1(p, sep).map { xxs =>
val (x::xs) = xxs
val (x::xs) = xxs: @nowarn
NonEmptyList(x, xs)
}

Expand Down
3 changes: 1 addition & 2 deletions parsley-cats/shared/src/test/scala/parsley/ParsleyTest.scala
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,7 @@ import org.scalatest.Assertions
import org.scalatest.flatspec.AnyFlatSpec
import org.scalatest.matchers.should.Matchers

import parsley.combinator.eof
import parsley.Result
import parsley.Parsley.eof
import parsley.errors.{ErrorBuilder, tokenextractors}
import org.scalatest.Inside
import org.scalactic.source.Position
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ package parsley.cats
import Predef.{ArrowAssoc => _}

import parsley.{ParsleyTest, Success, Failure}
import parsley.implicits.character.{charLift, stringLift}
import parsley.syntax.character.{charLift, stringLift}
import parsley.cats.combinator._
import cats.data.NonEmptyList

Expand Down
2 changes: 1 addition & 1 deletion project/build.properties
Original file line number Diff line number Diff line change
@@ -1 +1 @@
sbt.version=1.8.2
sbt.version=1.9.8
19 changes: 6 additions & 13 deletions project/plugins.sbt
Original file line number Diff line number Diff line change
@@ -1,23 +1,16 @@
val sbtTypelevelVersion = "0.4.19"
val sbtTypelevelVersion = "0.6.5"

libraryDependencySchemes ++= Seq(
"org.scala-native" % "sbt-scala-native" % VersionScheme.Always,
"org.scala-lang.modules" %% "scala-xml" % VersionScheme.Always,
)

//addSbtPlugin("org.typelevel" % "sbt-typelevel" % sbtTypelevelVersion) // disabled because I don't want headers and formatting checks
addSbtPlugin("org.typelevel" % "sbt-typelevel-settings" % sbtTypelevelVersion)
addSbtPlugin("org.typelevel" % "sbt-typelevel-ci-release" % sbtTypelevelVersion)
addSbtPlugin("org.typelevel" % "sbt-typelevel" % sbtTypelevelVersion)

// Cross Project Setup
addSbtPlugin("org.portable-scala" % "sbt-scalajs-crossproject" % "1.2.0")
addSbtPlugin("org.portable-scala" % "sbt-scala-native-crossproject" % "1.2.0")
addSbtPlugin("org.scala-js" % "sbt-scalajs" % "1.11.0")
addSbtPlugin("org.scala-native" % "sbt-scala-native" % "0.4.8")
addSbtPlugin("org.scala-js" % "sbt-scalajs" % "1.13.2")
addSbtPlugin("org.scala-native" % "sbt-scala-native" % "0.4.15")

// Other
// This is here purely to enable the niceness settings
addSbtPlugin("ch.epfl.scala" % "sbt-bloop" % "1.4.8")
addSbtPlugin("com.beautiful-scala" % "sbt-scalastyle" % "1.5.1")
addSbtPlugin("org.scoverage" % "sbt-scoverage" % "2.0.5")
addSbtPlugin("org.jmotor.sbt" % "sbt-dependency-updates" % "1.2.7")
addSbtPlugin("org.scoverage" % "sbt-scoverage" % "2.0.9")
addSbtPlugin("com.timushev.sbt" % "sbt-rewarn" % "0.1.3")

0 comments on commit dbc89a6

Please sign in to comment.