From eddfcd9c2f2c942990bb3c757833bc1e000dea72 Mon Sep 17 00:00:00 2001 From: Michael Ahlers Date: Sat, 11 May 2024 06:44:41 -0600 Subject: [PATCH 01/40] Result type --- src/main/scala/ahlers/tree/path/Mapped.scala | 26 ++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 src/main/scala/ahlers/tree/path/Mapped.scala diff --git a/src/main/scala/ahlers/tree/path/Mapped.scala b/src/main/scala/ahlers/tree/path/Mapped.scala new file mode 100644 index 0000000..75c4f9a --- /dev/null +++ b/src/main/scala/ahlers/tree/path/Mapped.scala @@ -0,0 +1,26 @@ +package ahlers.tree.path + +case class Mapped() +object Mapped { + + case class Path(toSegments: Seq[Path.Segment]) + + object Path { + sealed trait Segment + + object Segment { + case class Key(toKey: String) extends Segment + case class Index(toIndex: Int) extends Segment + } + } + + sealed trait Value + + object Value { + case class Text(toText: String) extends Value + case class Integer(toBigInt: BigInt) extends Value + case class Decimal(toBigDecimal: BigDecimal) extends Value + case class Boolean(toBoolean: Boolean) extends Value + } + +} From ffebe7ab7d34bda660bb0bf218903e3a45dc1f71 Mon Sep 17 00:00:00 2001 From: Michael Ahlers Date: Sat, 11 May 2024 06:43:48 -0600 Subject: [PATCH 02/40] Depend on Parsley --- dependencies.sbt | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/dependencies.sbt b/dependencies.sbt index 12092bc..6df1cc6 100644 --- a/dependencies.sbt +++ b/dependencies.sbt @@ -1,5 +1,12 @@ ThisBuild / scalaVersion := "2.13.14" +/** + * Parsley is a fast and modern parser combinator library for Scala. + * @see [[https://github.com/j-mie6/parsley]] + */ +libraryDependencies += + "com.github.j-mie6" %% "parsley" % "4.5.2" + /** * ScalaTest is the most flexible and most popular testing tool in the Scala ecosystem. * @see [[https://www.scalatest.org/]] From 880b98ddb5f295cf85abbafa3088a7df69d1789d Mon Sep 17 00:00:00 2001 From: Michael Ahlers Date: Sat, 11 May 2024 10:48:48 -0600 Subject: [PATCH 03/40] Relax unused imports (for now) --- compiler.sbt | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 compiler.sbt diff --git a/compiler.sbt b/compiler.sbt new file mode 100644 index 0000000..7fc6b54 --- /dev/null +++ b/compiler.sbt @@ -0,0 +1,3 @@ +import org.typelevel.scalacoptions.ScalacOptions + +tpolecatExcludeOptions += ScalacOptions.warnUnusedImports From 81009c99dd91ec8ed1fe81ca8661c00cc27dc4e5 Mon Sep 17 00:00:00 2001 From: Michael Ahlers Date: Sat, 11 May 2024 11:38:00 -0600 Subject: [PATCH 04/40] Start sketching operator and selector grammar --- .../ahlers/tree/path/JsonPathParser.scala | 105 ++++++++++++++++++ 1 file changed, 105 insertions(+) create mode 100644 src/main/scala/ahlers/tree/path/JsonPathParser.scala diff --git a/src/main/scala/ahlers/tree/path/JsonPathParser.scala b/src/main/scala/ahlers/tree/path/JsonPathParser.scala new file mode 100644 index 0000000..1421e17 --- /dev/null +++ b/src/main/scala/ahlers/tree/path/JsonPathParser.scala @@ -0,0 +1,105 @@ +package ahlers.tree.path + +import ahlers.tree.path.JsonPathParser.Name.Wildcard +import parsley.Parsley +import parsley.Parsley._ +import parsley.character._ +import parsley.combinator.sepBy + +object JsonPathParser extends App { + + val leftParenthesis: Parsley[Char] = char('(') + val rightParenthesis: Parsley[Char] = char(')') + val leftBracket: Parsley[Char] = char('[') + val rightBracket: Parsley[Char] = char(']') + val comma: Parsley[Char] = char(',') + val colon: Parsley[Char] = char(':') + val dot: Parsley[Char] = char('.') + val singleQuote: Parsley[Char] = char('\'') + + sealed trait Name + object Name { + case object Wildcard extends Name { + val parser: Parsley[Wildcard.type] = string("*").as(Wildcard) + } + + case class Literal(toText: String) extends Name + object Literal { + val parser: Parsley[Literal] = stringOfSome(satisfy(_.isLetterOrDigit)).map(Literal(_)) + } + + val parser: Parsley[Name] = Wildcard.parser | Literal.parser + } + + case class Index(toInt: Int) + object Index { + val parser: Parsley[Index] = satisfy(_.isDigit).map(_.toInt).map(Index(_)) + } + + sealed trait Operator + object Operator { + + sealed trait Anchor extends Operator + object Anchor { + + case object Root extends Anchor { + val parser: Parsley[Root.type] = char('$').as(Root) + } + + case object Node extends Anchor { + val parser: Parsley[Node.type] = char('@').as(Node) + } + + val parser: Parsley[Anchor] = Root.parser | Node.parser + } + + case object DeepScan extends Operator { + val parser: Parsley[DeepScan.type] = string("..").as(DeepScan) + } + + case class Child(toName: Name) extends Operator + object Child { + val parser: Parsley[Child] = dot *> Name.parser.map(Child(_)) + } + + case class ChildSubscript(toNames: Seq[Name]) extends Operator + object ChildSubscript { + val parser: Parsley[Operator.ChildSubscript] = { + val quotedParser: Parsley[Name.Literal] = singleQuote *> Name.Literal.parser <~ singleQuote + (leftBracket *> sepBy(quotedParser <|> Wildcard.parser, whitespaces *> comma <* whitespaces) <* rightBracket).map(ChildSubscript(_)) + } + } + + case class IndexSubscript(toIndexes: Seq[Index]) extends Operator + object IndexSubscript { + val parser: Parsley[Operator.IndexSubscript] = (leftBracket *> sepBy(Index.parser, comma) <* rightBracket).map(IndexSubscript(_)) + } + + sealed trait ArraySlice extends Operator + object ArraySlice { + case class LeftBound(start: Index) extends ArraySlice + object LeftBound { + val parser: Parsley[LeftBound] = (leftBracket *> Index.parser <~ colon <* rightBracket).map(LeftBound(_)) + } + + case class RightBound(end: Index) extends ArraySlice + object RightBound { + val parser: Parsley[RightBound] = (leftBracket *> colon ~> Index.parser <* rightBracket).map(RightBound(_)) + } + + case class BothBounds(start: Index, end: Index) extends ArraySlice + object BothBounds { + val parser: Parsley[BothBounds] = (leftBracket *> Index.parser <~> colon *> Index.parser <* rightBracket).map((BothBounds(_, _)).tupled) + } + + val parser: Parsley[ArraySlice] = LeftBound.parser | RightBound.parser | BothBounds.parser + } + + case class Select(toOperator: Seq[Operator]) extends Operator + object Select { + val parser: Parsley[Select] = many(Child.parser | ChildSubscript.parser | IndexSubscript.parser | ArraySlice.parser | DeepScan.parser).map(Select(_)) + } + + val operator: Parsley[Operator] = Anchor.parser *> Select.parser + } +} From 08cf748a36533556e5a46f77616e83ac3799167c Mon Sep 17 00:00:00 2001 From: Michael Ahlers Date: Sat, 11 May 2024 12:15:51 -0600 Subject: [PATCH 05/40] diffx instances for Parsley --- src/test/scala/parsley/diffx/instances.scala | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 src/test/scala/parsley/diffx/instances.scala diff --git a/src/test/scala/parsley/diffx/instances.scala b/src/test/scala/parsley/diffx/instances.scala new file mode 100644 index 0000000..ee494d0 --- /dev/null +++ b/src/test/scala/parsley/diffx/instances.scala @@ -0,0 +1,20 @@ +package parsley.diffx + +import com.softwaremill.diffx.Diff +import com.softwaremill.diffx.DiffResultValue +import parsley.Failure +import parsley.Result +import parsley.Success + +object instances { + + implicit def diffSuccess[A: Diff]: Diff[Success[A]] = Diff.derived + implicit def diffFailure[E: Diff]: Diff[Failure[E]] = Diff[E].contramap(_.msg) + + implicit def diffResult[E: Diff, A: Diff]: Diff[Result[E, A]] = { + case (left: Success[A], right: Success[A], context) => Diff[Success[A]].apply(left, right, context) + case (left: Failure[E], right: Failure[E], context) => Diff[Failure[E]].apply(left, right, context) + case (left, right, _) => DiffResultValue(left, right) + } + +} From d7994a3ecb7e77fc82942c8f2af5303af947733d Mon Sep 17 00:00:00 2001 From: Michael Ahlers Date: Sat, 11 May 2024 12:16:10 -0600 Subject: [PATCH 06/40] Split out wildcard --- src/main/scala/ahlers/tree/path/parser/term.scala | 9 +++++++++ src/main/scala/ahlers/tree/path/term/Wildcard.scala | 3 +++ .../scala/ahlers/tree/path/term/diffx/instances.scala | 10 ++++++++++ 3 files changed, 22 insertions(+) create mode 100644 src/main/scala/ahlers/tree/path/parser/term.scala create mode 100644 src/main/scala/ahlers/tree/path/term/Wildcard.scala create mode 100644 src/test/scala/ahlers/tree/path/term/diffx/instances.scala diff --git a/src/main/scala/ahlers/tree/path/parser/term.scala b/src/main/scala/ahlers/tree/path/parser/term.scala new file mode 100644 index 0000000..5786714 --- /dev/null +++ b/src/main/scala/ahlers/tree/path/parser/term.scala @@ -0,0 +1,9 @@ +package ahlers.tree.path.parser + +import ahlers.tree.path.term.Wildcard +import parsley.Parsley +import parsley.character.char + +object term { + val wildcard: Parsley[Wildcard.type] = char('*').as(Wildcard) +} diff --git a/src/main/scala/ahlers/tree/path/term/Wildcard.scala b/src/main/scala/ahlers/tree/path/term/Wildcard.scala new file mode 100644 index 0000000..33d58f1 --- /dev/null +++ b/src/main/scala/ahlers/tree/path/term/Wildcard.scala @@ -0,0 +1,3 @@ +package ahlers.tree.path.term + +object Wildcard diff --git a/src/test/scala/ahlers/tree/path/term/diffx/instances.scala b/src/test/scala/ahlers/tree/path/term/diffx/instances.scala new file mode 100644 index 0000000..f624859 --- /dev/null +++ b/src/test/scala/ahlers/tree/path/term/diffx/instances.scala @@ -0,0 +1,10 @@ +package ahlers.tree.path.term.diffx + +import ahlers.tree.path.term.Wildcard +import com.softwaremill.diffx.Diff + +object instances { + + implicit val diffWildcard: Diff[Wildcard.type] = Diff.derived + +} From 8861c08a87aaa7c71ffc0438017ae08bafcb53bf Mon Sep 17 00:00:00 2001 From: Michael Ahlers Date: Sat, 11 May 2024 12:20:42 -0600 Subject: [PATCH 07/40] Add tests --- .../ahlers/tree/path/parser/TermSpec.scala | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 src/test/scala/ahlers/tree/path/parser/TermSpec.scala diff --git a/src/test/scala/ahlers/tree/path/parser/TermSpec.scala b/src/test/scala/ahlers/tree/path/parser/TermSpec.scala new file mode 100644 index 0000000..8445505 --- /dev/null +++ b/src/test/scala/ahlers/tree/path/parser/TermSpec.scala @@ -0,0 +1,29 @@ +package ahlers.tree.path.parser + +import ahlers.tree.path.term.Wildcard +import ahlers.tree.path.term.diffx.instances._ +import com.softwaremill.diffx.scalatest.DiffShouldMatcher.convertToAnyShouldMatcher +import org.scalatest.matchers.should.Matchers._ +import org.scalatest.wordspec.AnyWordSpec +import org.scalatestplus.scalacheck.ScalaCheckPropertyChecks._ +import parsley.Failure +import parsley.Success +import parsley.diffx.instances._ + +class TermSpec extends AnyWordSpec { + + "Wildcard" should { + """accept "*"""" in { + term.wildcard.parse("*").shouldMatchTo(Success(Wildcard)) + } + + "reject else" in { + forAll { input: String => + whenever(input != "*") { + term.wildcard.parse(input).shouldBe(a[Failure[_]]) + } + } + } + } + +} From 9a34d99cb2239b0d339335aaf28a5d434f68c715 Mon Sep 17 00:00:00 2001 From: Michael Ahlers Date: Sat, 11 May 2024 12:32:01 -0600 Subject: [PATCH 08/40] Factor out name --- .../scala/ahlers/tree/path/parser/term.scala | 4 ++++ .../scala/ahlers/tree/path/term/Name.scala | 3 +++ .../ahlers/tree/path/term/Wildcard.scala | 4 +++- .../ahlers/tree/path/parser/TermSpec.scala | 20 ++++++++++++++++++- .../tree/path/term/diffx/instances.scala | 3 ++- .../tree/path/term/scalacheck/instances.scala | 17 ++++++++++++++++ 6 files changed, 48 insertions(+), 3 deletions(-) create mode 100644 src/main/scala/ahlers/tree/path/term/Name.scala create mode 100644 src/test/scala/ahlers/tree/path/term/scalacheck/instances.scala diff --git a/src/main/scala/ahlers/tree/path/parser/term.scala b/src/main/scala/ahlers/tree/path/parser/term.scala index 5786714..1db1928 100644 --- a/src/main/scala/ahlers/tree/path/parser/term.scala +++ b/src/main/scala/ahlers/tree/path/parser/term.scala @@ -1,9 +1,13 @@ package ahlers.tree.path.parser +import ahlers.tree.path.term.Name import ahlers.tree.path.term.Wildcard import parsley.Parsley import parsley.character.char +import parsley.character.satisfy +import parsley.character.stringOfSome object term { val wildcard: Parsley[Wildcard.type] = char('*').as(Wildcard) + val name: Parsley[Name] = stringOfSome(satisfy(_.isLetterOrDigit)).map(Name) } diff --git a/src/main/scala/ahlers/tree/path/term/Name.scala b/src/main/scala/ahlers/tree/path/term/Name.scala new file mode 100644 index 0000000..caf967c --- /dev/null +++ b/src/main/scala/ahlers/tree/path/term/Name.scala @@ -0,0 +1,3 @@ +package ahlers.tree.path.term + +case class Name(toText: String) diff --git a/src/main/scala/ahlers/tree/path/term/Wildcard.scala b/src/main/scala/ahlers/tree/path/term/Wildcard.scala index 33d58f1..0672323 100644 --- a/src/main/scala/ahlers/tree/path/term/Wildcard.scala +++ b/src/main/scala/ahlers/tree/path/term/Wildcard.scala @@ -1,3 +1,5 @@ package ahlers.tree.path.term -object Wildcard +case object Wildcard { + val toText: String = "*" +} diff --git a/src/test/scala/ahlers/tree/path/parser/TermSpec.scala b/src/test/scala/ahlers/tree/path/parser/TermSpec.scala index 8445505..fc610f2 100644 --- a/src/test/scala/ahlers/tree/path/parser/TermSpec.scala +++ b/src/test/scala/ahlers/tree/path/parser/TermSpec.scala @@ -1,7 +1,9 @@ package ahlers.tree.path.parser +import ahlers.tree.path.term.Name import ahlers.tree.path.term.Wildcard import ahlers.tree.path.term.diffx.instances._ +import ahlers.tree.path.term.scalacheck.instances._ import com.softwaremill.diffx.scalatest.DiffShouldMatcher.convertToAnyShouldMatcher import org.scalatest.matchers.should.Matchers._ import org.scalatest.wordspec.AnyWordSpec @@ -19,7 +21,23 @@ class TermSpec extends AnyWordSpec { "reject else" in { forAll { input: String => - whenever(input != "*") { + whenever("*" != input) { + term.wildcard.parse(input).shouldBe(a[Failure[_]]) + } + } + } + } + + "Name" should { + """accept alpha-numeric""" in { + forAll { name: Name => + term.name.parse(name.toText).shouldMatchTo(Success(name)) + } + } + + "reject else" in { + forAll { input: String => + whenever(!input.matches("^[a-zA-Z0-9]+$")) { term.wildcard.parse(input).shouldBe(a[Failure[_]]) } } diff --git a/src/test/scala/ahlers/tree/path/term/diffx/instances.scala b/src/test/scala/ahlers/tree/path/term/diffx/instances.scala index f624859..9e2f8da 100644 --- a/src/test/scala/ahlers/tree/path/term/diffx/instances.scala +++ b/src/test/scala/ahlers/tree/path/term/diffx/instances.scala @@ -1,10 +1,11 @@ package ahlers.tree.path.term.diffx -import ahlers.tree.path.term.Wildcard +import ahlers.tree.path.term.{Name, Wildcard} import com.softwaremill.diffx.Diff object instances { implicit val diffWildcard: Diff[Wildcard.type] = Diff.derived + implicit val diffName: Diff[Name] = Diff.derived } diff --git a/src/test/scala/ahlers/tree/path/term/scalacheck/instances.scala b/src/test/scala/ahlers/tree/path/term/scalacheck/instances.scala new file mode 100644 index 0000000..01f064c --- /dev/null +++ b/src/test/scala/ahlers/tree/path/term/scalacheck/instances.scala @@ -0,0 +1,17 @@ +package ahlers.tree.path.term.scalacheck + +import ahlers.tree.path.term.Name +import ahlers.tree.path.term.Wildcard +import org.scalacheck.Arbitrary +import org.scalacheck.Gen + +object instances { + + implicit val arbWildcard: Arbitrary[Wildcard.type] = Arbitrary(Gen.const(Wildcard)) + + implicit val arbName: Arbitrary[Name] = Arbitrary(for { + head <- Gen.alphaNumChar + tail <- Gen.alphaNumStr + } yield Name(s"$head$tail")) + +} From 208547ae97d4e1bc547a176a14c24ea2beeada97 Mon Sep 17 00:00:00 2001 From: Michael Ahlers Date: Sat, 11 May 2024 12:50:22 -0600 Subject: [PATCH 09/40] Only accept ASCII letters --- .../scala/ahlers/tree/path/parser/term.scala | 9 +++++- .../ahlers/tree/path/parser/TermSpec.scala | 30 +++++++++++++++++++ 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/src/main/scala/ahlers/tree/path/parser/term.scala b/src/main/scala/ahlers/tree/path/parser/term.scala index 1db1928..b023320 100644 --- a/src/main/scala/ahlers/tree/path/parser/term.scala +++ b/src/main/scala/ahlers/tree/path/parser/term.scala @@ -1,5 +1,6 @@ package ahlers.tree.path.parser +import ahlers.tree.path.term.Index import ahlers.tree.path.term.Name import ahlers.tree.path.term.Wildcard import parsley.Parsley @@ -8,6 +9,12 @@ import parsley.character.satisfy import parsley.character.stringOfSome object term { + val index: Parsley[Index] = stringOfSome(_.isDigit).map(_.toInt).map(Index) + + val name: Parsley[Name] = { + val isValid: Set[Char] = ('a' to 'z').toSet ++ ('A' to 'Z').toSet ++ ('0' to '9').toSet + stringOfSome(satisfy(isValid)).map(Name) + } + val wildcard: Parsley[Wildcard.type] = char('*').as(Wildcard) - val name: Parsley[Name] = stringOfSome(satisfy(_.isLetterOrDigit)).map(Name) } diff --git a/src/test/scala/ahlers/tree/path/parser/TermSpec.scala b/src/test/scala/ahlers/tree/path/parser/TermSpec.scala index fc610f2..3587fea 100644 --- a/src/test/scala/ahlers/tree/path/parser/TermSpec.scala +++ b/src/test/scala/ahlers/tree/path/parser/TermSpec.scala @@ -44,4 +44,34 @@ class TermSpec extends AnyWordSpec { } } + "Name" should { + """accept alpha-numeric""" in { + forAll { name: Name => + term.name.parse(name.toText).shouldMatchTo(Success(name)) + } + } + + "reject else" in { + forAll { input: String => + whenever(!input.matches("^[a-zA-Z0-9]+$")) { + term.name.parse(input).shouldBe(a[Failure[_]]) + } + } + } + } + + "Wildcard" should { + """accept "*"""" in { + term.wildcard.parse("*").shouldMatchTo(Success(Wildcard)) + } + + "reject else" in { + forAll { input: String => + whenever("*" != input) { + term.wildcard.parse(input).shouldBe(a[Failure[_]]) + } + } + } + } + } From 58f4fd4434965e13ee3cf681a4b1d5d56596927c Mon Sep 17 00:00:00 2001 From: Michael Ahlers Date: Sat, 11 May 2024 12:50:28 -0600 Subject: [PATCH 10/40] Factor out index term --- .../scala/ahlers/tree/path/term/Index.scala | 5 ++++ .../ahlers/tree/path/parser/TermSpec.scala | 27 +++++-------------- .../tree/path/term/diffx/instances.scala | 7 +++-- .../tree/path/term/scalacheck/instances.scala | 6 ++++- 4 files changed, 22 insertions(+), 23 deletions(-) create mode 100644 src/main/scala/ahlers/tree/path/term/Index.scala diff --git a/src/main/scala/ahlers/tree/path/term/Index.scala b/src/main/scala/ahlers/tree/path/term/Index.scala new file mode 100644 index 0000000..c629e41 --- /dev/null +++ b/src/main/scala/ahlers/tree/path/term/Index.scala @@ -0,0 +1,5 @@ +package ahlers.tree.path.term + +case class Index(toInt: Int) { + val toText:String = toInt.toString +} diff --git a/src/test/scala/ahlers/tree/path/parser/TermSpec.scala b/src/test/scala/ahlers/tree/path/parser/TermSpec.scala index 3587fea..add8476 100644 --- a/src/test/scala/ahlers/tree/path/parser/TermSpec.scala +++ b/src/test/scala/ahlers/tree/path/parser/TermSpec.scala @@ -1,5 +1,6 @@ package ahlers.tree.path.parser +import ahlers.tree.path.term.Index import ahlers.tree.path.term.Name import ahlers.tree.path.term.Wildcard import ahlers.tree.path.term.diffx.instances._ @@ -14,31 +15,17 @@ import parsley.diffx.instances._ class TermSpec extends AnyWordSpec { - "Wildcard" should { - """accept "*"""" in { - term.wildcard.parse("*").shouldMatchTo(Success(Wildcard)) - } - - "reject else" in { - forAll { input: String => - whenever("*" != input) { - term.wildcard.parse(input).shouldBe(a[Failure[_]]) - } - } - } - } - - "Name" should { - """accept alpha-numeric""" in { - forAll { name: Name => - term.name.parse(name.toText).shouldMatchTo(Success(name)) + "Index" should { + """accept positive or negative numeric""" in { + forAll { index: Index => + term.index.parse(index.toText).shouldMatchTo(Success(index)) } } "reject else" in { forAll { input: String => - whenever(!input.matches("^[a-zA-Z0-9]+$")) { - term.wildcard.parse(input).shouldBe(a[Failure[_]]) + whenever(!input.matches("^[0-9]+$")) { + term.index.parse(input).shouldBe(a[Failure[_]]) } } } diff --git a/src/test/scala/ahlers/tree/path/term/diffx/instances.scala b/src/test/scala/ahlers/tree/path/term/diffx/instances.scala index 9e2f8da..d3d8804 100644 --- a/src/test/scala/ahlers/tree/path/term/diffx/instances.scala +++ b/src/test/scala/ahlers/tree/path/term/diffx/instances.scala @@ -1,11 +1,14 @@ package ahlers.tree.path.term.diffx -import ahlers.tree.path.term.{Name, Wildcard} +import ahlers.tree.path.term.Index +import ahlers.tree.path.term.Name +import ahlers.tree.path.term.Wildcard import com.softwaremill.diffx.Diff object instances { - implicit val diffWildcard: Diff[Wildcard.type] = Diff.derived + implicit val diffIndex: Diff[Index] = Diff.derived implicit val diffName: Diff[Name] = Diff.derived + implicit val diffWildcard: Diff[Wildcard.type] = Diff.derived } diff --git a/src/test/scala/ahlers/tree/path/term/scalacheck/instances.scala b/src/test/scala/ahlers/tree/path/term/scalacheck/instances.scala index 01f064c..f34a46b 100644 --- a/src/test/scala/ahlers/tree/path/term/scalacheck/instances.scala +++ b/src/test/scala/ahlers/tree/path/term/scalacheck/instances.scala @@ -1,17 +1,21 @@ package ahlers.tree.path.term.scalacheck +import ahlers.tree.path.term.Index import ahlers.tree.path.term.Name import ahlers.tree.path.term.Wildcard +//import magnolify.scalacheck.semiauto._ import org.scalacheck.Arbitrary import org.scalacheck.Gen object instances { - implicit val arbWildcard: Arbitrary[Wildcard.type] = Arbitrary(Gen.const(Wildcard)) + implicit val arbIndex: Arbitrary[Index] = Arbitrary(Gen.posNum[Int].map(Index)) implicit val arbName: Arbitrary[Name] = Arbitrary(for { head <- Gen.alphaNumChar tail <- Gen.alphaNumStr } yield Name(s"$head$tail")) + implicit val arbWildcard: Arbitrary[Wildcard.type] = Arbitrary(Gen.const(Wildcard)) + } From 0a1cea45b61c5ca8ca6f10a159b2a3bb827fc0ce Mon Sep 17 00:00:00 2001 From: Michael Ahlers Date: Sat, 11 May 2024 12:59:19 -0600 Subject: [PATCH 11/40] Split out anchors --- .../ahlers/tree/path/operator/Operator.scala | 17 +++++++ .../ahlers/tree/path/parser/operator.scala | 13 ++++++ .../tree/path/operator/diffx/instances.scala | 12 +++++ .../path/operator/scalacheck/instances.scala | 13 ++++++ .../tree/path/parser/OperatorSpec.scala | 44 +++++++++++++++++++ 5 files changed, 99 insertions(+) create mode 100644 src/main/scala/ahlers/tree/path/operator/Operator.scala create mode 100644 src/main/scala/ahlers/tree/path/parser/operator.scala create mode 100644 src/test/scala/ahlers/tree/path/operator/diffx/instances.scala create mode 100644 src/test/scala/ahlers/tree/path/operator/scalacheck/instances.scala create mode 100644 src/test/scala/ahlers/tree/path/parser/OperatorSpec.scala diff --git a/src/main/scala/ahlers/tree/path/operator/Operator.scala b/src/main/scala/ahlers/tree/path/operator/Operator.scala new file mode 100644 index 0000000..9801749 --- /dev/null +++ b/src/main/scala/ahlers/tree/path/operator/Operator.scala @@ -0,0 +1,17 @@ +package ahlers.tree.path.operator + +sealed trait Operator +object Operator { + + sealed trait Anchor + object Anchor { + case object RootElement extends Operator with Anchor { + val toText: String = "$" + } + + case object CurrentNode extends Operator with Anchor { + val toText: String = "@" + } + } + +} diff --git a/src/main/scala/ahlers/tree/path/parser/operator.scala b/src/main/scala/ahlers/tree/path/parser/operator.scala new file mode 100644 index 0000000..03b102b --- /dev/null +++ b/src/main/scala/ahlers/tree/path/parser/operator.scala @@ -0,0 +1,13 @@ +package ahlers.tree.path.parser + +import ahlers.tree.path.operator.Operator.Anchor.CurrentNode +import ahlers.tree.path.operator.Operator.Anchor.RootElement +import parsley.Parsley +import parsley.character.char + +object operator { + + val currentNode: Parsley[CurrentNode.type] = char('@').as(CurrentNode) + val rootElement: Parsley[RootElement.type] = char('$').as(RootElement) + +} diff --git a/src/test/scala/ahlers/tree/path/operator/diffx/instances.scala b/src/test/scala/ahlers/tree/path/operator/diffx/instances.scala new file mode 100644 index 0000000..834c170 --- /dev/null +++ b/src/test/scala/ahlers/tree/path/operator/diffx/instances.scala @@ -0,0 +1,12 @@ +package ahlers.tree.path.operator.diffx + +import ahlers.tree.path.operator.Operator.Anchor.CurrentNode +import ahlers.tree.path.operator.Operator.Anchor.RootElement +import com.softwaremill.diffx.Diff + +object instances { + + implicit val diffCurrentNode: Diff[CurrentNode.type] = Diff.derived + implicit val diffRootElement: Diff[RootElement.type] = Diff.derived + +} diff --git a/src/test/scala/ahlers/tree/path/operator/scalacheck/instances.scala b/src/test/scala/ahlers/tree/path/operator/scalacheck/instances.scala new file mode 100644 index 0000000..6a89abd --- /dev/null +++ b/src/test/scala/ahlers/tree/path/operator/scalacheck/instances.scala @@ -0,0 +1,13 @@ +package ahlers.tree.path.operator.scalacheck + +import ahlers.tree.path.operator.Operator.Anchor.CurrentNode +import ahlers.tree.path.operator.Operator.Anchor.RootElement +import org.scalacheck.Arbitrary +import org.scalacheck.Gen + +object instances { + + implicit val arbCurrentNode: Arbitrary[CurrentNode.type] = Arbitrary(Gen.const(CurrentNode)) + implicit val arbRootElement: Arbitrary[RootElement.type] = Arbitrary(Gen.const(RootElement)) + +} diff --git a/src/test/scala/ahlers/tree/path/parser/OperatorSpec.scala b/src/test/scala/ahlers/tree/path/parser/OperatorSpec.scala new file mode 100644 index 0000000..4d880a5 --- /dev/null +++ b/src/test/scala/ahlers/tree/path/parser/OperatorSpec.scala @@ -0,0 +1,44 @@ +package ahlers.tree.path.parser + +import ahlers.tree.path.operator.Operator.Anchor.CurrentNode +import ahlers.tree.path.operator.Operator.Anchor.RootElement +import ahlers.tree.path.operator.diffx.instances._ +import com.softwaremill.diffx.scalatest.DiffShouldMatcher.convertToAnyShouldMatcher +import org.scalatest.matchers.should.Matchers._ +import org.scalatest.wordspec.AnyWordSpec +import org.scalatestplus.scalacheck.ScalaCheckPropertyChecks._ +import parsley.Failure +import parsley.Success +import parsley.diffx.instances._ + +class OperatorSpec extends AnyWordSpec { + + "CurrentNode" should { + """accept "$"""" in { + operator.currentNode.parse("@").shouldMatchTo(Success(CurrentNode)) + } + + "reject else" in { + forAll { input: String => + whenever("@" != input) { + term.wildcard.parse(input).shouldBe(a[Failure[_]]) + } + } + } + } + + "RootElement" should { + """accept "$"""" in { + operator.rootElement.parse("$").shouldMatchTo(Success(RootElement)) + } + + "reject else" in { + forAll { input: String => + whenever("$" != input) { + term.wildcard.parse(input).shouldBe(a[Failure[_]]) + } + } + } + } + +} From feedc40c6856b70660e4f0cb807cf823af5af67f Mon Sep 17 00:00:00 2001 From: Michael Ahlers Date: Sat, 11 May 2024 13:09:25 -0600 Subject: [PATCH 12/40] Split out deep-scan and revise tests --- .../ahlers/tree/path/operator/Operator.scala | 4 ++ .../ahlers/tree/path/parser/operator.scala | 6 +++ .../scala/ahlers/tree/path/term/Index.scala | 2 +- .../tree/path/operator/diffx/instances.scala | 4 ++ .../path/operator/scalacheck/instances.scala | 5 +++ .../tree/path/parser/OperatorSpec.scala | 42 +++++++++++++++++-- .../ahlers/tree/path/parser/TermSpec.scala | 18 +++++--- .../tree/path/term/scalacheck/instances.scala | 1 - 8 files changed, 70 insertions(+), 12 deletions(-) diff --git a/src/main/scala/ahlers/tree/path/operator/Operator.scala b/src/main/scala/ahlers/tree/path/operator/Operator.scala index 9801749..d0384c1 100644 --- a/src/main/scala/ahlers/tree/path/operator/Operator.scala +++ b/src/main/scala/ahlers/tree/path/operator/Operator.scala @@ -14,4 +14,8 @@ object Operator { } } + case object DeepScan extends Operator { + val toText: String = ".." + } + } diff --git a/src/main/scala/ahlers/tree/path/parser/operator.scala b/src/main/scala/ahlers/tree/path/parser/operator.scala index 03b102b..6fe0345 100644 --- a/src/main/scala/ahlers/tree/path/parser/operator.scala +++ b/src/main/scala/ahlers/tree/path/parser/operator.scala @@ -1,13 +1,19 @@ package ahlers.tree.path.parser +import ahlers.tree.path.operator.Operator.Anchor import ahlers.tree.path.operator.Operator.Anchor.CurrentNode import ahlers.tree.path.operator.Operator.Anchor.RootElement +import ahlers.tree.path.operator.Operator.DeepScan import parsley.Parsley import parsley.character.char +import parsley.character.string object operator { val currentNode: Parsley[CurrentNode.type] = char('@').as(CurrentNode) val rootElement: Parsley[RootElement.type] = char('$').as(RootElement) + val anchor: Parsley[Anchor] = currentNode | rootElement + + val deepScan: Parsley[DeepScan.type] = string("..").as(DeepScan) } diff --git a/src/main/scala/ahlers/tree/path/term/Index.scala b/src/main/scala/ahlers/tree/path/term/Index.scala index c629e41..eb2758f 100644 --- a/src/main/scala/ahlers/tree/path/term/Index.scala +++ b/src/main/scala/ahlers/tree/path/term/Index.scala @@ -1,5 +1,5 @@ package ahlers.tree.path.term case class Index(toInt: Int) { - val toText:String = toInt.toString + val toText: String = toInt.toString } diff --git a/src/test/scala/ahlers/tree/path/operator/diffx/instances.scala b/src/test/scala/ahlers/tree/path/operator/diffx/instances.scala index 834c170..9d5fed8 100644 --- a/src/test/scala/ahlers/tree/path/operator/diffx/instances.scala +++ b/src/test/scala/ahlers/tree/path/operator/diffx/instances.scala @@ -1,12 +1,16 @@ package ahlers.tree.path.operator.diffx +import ahlers.tree.path.operator.Operator.Anchor import ahlers.tree.path.operator.Operator.Anchor.CurrentNode import ahlers.tree.path.operator.Operator.Anchor.RootElement +import ahlers.tree.path.operator.Operator.DeepScan import com.softwaremill.diffx.Diff object instances { implicit val diffCurrentNode: Diff[CurrentNode.type] = Diff.derived + implicit val diffDeepScan: Diff[DeepScan.type] = Diff.derived implicit val diffRootElement: Diff[RootElement.type] = Diff.derived + implicit val diffAnchor: Diff[Anchor] = Diff.derived } diff --git a/src/test/scala/ahlers/tree/path/operator/scalacheck/instances.scala b/src/test/scala/ahlers/tree/path/operator/scalacheck/instances.scala index 6a89abd..681e9cb 100644 --- a/src/test/scala/ahlers/tree/path/operator/scalacheck/instances.scala +++ b/src/test/scala/ahlers/tree/path/operator/scalacheck/instances.scala @@ -1,13 +1,18 @@ package ahlers.tree.path.operator.scalacheck +import ahlers.tree.path.operator.Operator.Anchor import ahlers.tree.path.operator.Operator.Anchor.CurrentNode import ahlers.tree.path.operator.Operator.Anchor.RootElement +import ahlers.tree.path.operator.Operator.DeepScan +import magnolify.scalacheck.semiauto._ import org.scalacheck.Arbitrary import org.scalacheck.Gen object instances { implicit val arbCurrentNode: Arbitrary[CurrentNode.type] = Arbitrary(Gen.const(CurrentNode)) + implicit val arbDeepScan: Arbitrary[DeepScan.type] = Arbitrary(Gen.const(DeepScan)) implicit val arbRootElement: Arbitrary[RootElement.type] = Arbitrary(Gen.const(RootElement)) + implicit val arbAnchor: Arbitrary[Anchor] = ArbitraryDerivation[Anchor] } diff --git a/src/test/scala/ahlers/tree/path/parser/OperatorSpec.scala b/src/test/scala/ahlers/tree/path/parser/OperatorSpec.scala index 4d880a5..84ee653 100644 --- a/src/test/scala/ahlers/tree/path/parser/OperatorSpec.scala +++ b/src/test/scala/ahlers/tree/path/parser/OperatorSpec.scala @@ -1,7 +1,9 @@ package ahlers.tree.path.parser +import ahlers.tree.path.operator.Operator.Anchor import ahlers.tree.path.operator.Operator.Anchor.CurrentNode import ahlers.tree.path.operator.Operator.Anchor.RootElement +import ahlers.tree.path.operator.Operator.DeepScan import ahlers.tree.path.operator.diffx.instances._ import com.softwaremill.diffx.scalatest.DiffShouldMatcher.convertToAnyShouldMatcher import org.scalatest.matchers.should.Matchers._ @@ -13,29 +15,61 @@ import parsley.diffx.instances._ class OperatorSpec extends AnyWordSpec { + "Anchor" should { + val parser = operator.anchor + + """accept "$"""" in { + parser.parse("$").shouldMatchTo(Success(RootElement: Anchor)) + } + + """accept "@"""" in { + parser.parse("@").shouldMatchTo(Success(CurrentNode: Anchor)) + } + } + "CurrentNode" should { + val parser = operator.currentNode + """accept "$"""" in { - operator.currentNode.parse("@").shouldMatchTo(Success(CurrentNode)) + parser.parse("@").shouldMatchTo(Success(CurrentNode)) } "reject else" in { forAll { input: String => whenever("@" != input) { - term.wildcard.parse(input).shouldBe(a[Failure[_]]) + parser.parse(input).shouldBe(a[Failure[_]]) } } } } "RootElement" should { + val parser = operator.rootElement + """accept "$"""" in { - operator.rootElement.parse("$").shouldMatchTo(Success(RootElement)) + parser.parse("$").shouldMatchTo(Success(RootElement)) } "reject else" in { forAll { input: String => whenever("$" != input) { - term.wildcard.parse(input).shouldBe(a[Failure[_]]) + parser.parse(input).shouldBe(a[Failure[_]]) + } + } + } + } + + "DeepScan" should { + val parser = operator.deepScan + + """accept ".."""" in { + parser.parse("..").shouldMatchTo(Success(DeepScan)) + } + + "reject else" in { + forAll { input: String => + whenever(".." != input) { + parser.parse(input).shouldBe(a[Failure[_]]) } } } diff --git a/src/test/scala/ahlers/tree/path/parser/TermSpec.scala b/src/test/scala/ahlers/tree/path/parser/TermSpec.scala index add8476..eea63e8 100644 --- a/src/test/scala/ahlers/tree/path/parser/TermSpec.scala +++ b/src/test/scala/ahlers/tree/path/parser/TermSpec.scala @@ -16,46 +16,52 @@ import parsley.diffx.instances._ class TermSpec extends AnyWordSpec { "Index" should { + val parser = term.index + """accept positive or negative numeric""" in { forAll { index: Index => - term.index.parse(index.toText).shouldMatchTo(Success(index)) + parser.parse(index.toText).shouldMatchTo(Success(index)) } } "reject else" in { forAll { input: String => whenever(!input.matches("^[0-9]+$")) { - term.index.parse(input).shouldBe(a[Failure[_]]) + parser.parse(input).shouldBe(a[Failure[_]]) } } } } "Name" should { + val parser = term.name + """accept alpha-numeric""" in { forAll { name: Name => - term.name.parse(name.toText).shouldMatchTo(Success(name)) + parser.parse(name.toText).shouldMatchTo(Success(name)) } } "reject else" in { forAll { input: String => whenever(!input.matches("^[a-zA-Z0-9]+$")) { - term.name.parse(input).shouldBe(a[Failure[_]]) + parser.parse(input).shouldBe(a[Failure[_]]) } } } } "Wildcard" should { + val parser = term.wildcard + """accept "*"""" in { - term.wildcard.parse("*").shouldMatchTo(Success(Wildcard)) + parser.parse("*").shouldMatchTo(Success(Wildcard)) } "reject else" in { forAll { input: String => whenever("*" != input) { - term.wildcard.parse(input).shouldBe(a[Failure[_]]) + parser.parse(input).shouldBe(a[Failure[_]]) } } } diff --git a/src/test/scala/ahlers/tree/path/term/scalacheck/instances.scala b/src/test/scala/ahlers/tree/path/term/scalacheck/instances.scala index f34a46b..20dc9d8 100644 --- a/src/test/scala/ahlers/tree/path/term/scalacheck/instances.scala +++ b/src/test/scala/ahlers/tree/path/term/scalacheck/instances.scala @@ -3,7 +3,6 @@ package ahlers.tree.path.term.scalacheck import ahlers.tree.path.term.Index import ahlers.tree.path.term.Name import ahlers.tree.path.term.Wildcard -//import magnolify.scalacheck.semiauto._ import org.scalacheck.Arbitrary import org.scalacheck.Gen From d5161c61c63b62d163609f1c552dce34d0bf1d65 Mon Sep 17 00:00:00 2001 From: Michael Ahlers Date: Sat, 11 May 2024 13:54:35 -0600 Subject: [PATCH 13/40] Handle dot-noted child --- compiler.sbt | 3 + .../ahlers/tree/path/operator/Operator.scala | 23 +++- .../ahlers/tree/path/parser/operator.scala | 7 ++ .../tree/path/operator/diffx/instances.scala | 9 +- .../path/operator/scalacheck/instances.scala | 11 +- .../tree/path/parser/OperatorSpec.scala | 106 +++++++++++++++--- .../ahlers/tree/path/parser/TermSpec.scala | 23 ++-- 7 files changed, 150 insertions(+), 32 deletions(-) diff --git a/compiler.sbt b/compiler.sbt index 7fc6b54..3d5f997 100644 --- a/compiler.sbt +++ b/compiler.sbt @@ -1,3 +1,6 @@ import org.typelevel.scalacoptions.ScalacOptions tpolecatExcludeOptions += ScalacOptions.warnUnusedImports + +Test / tpolecatExcludeOptions += ScalacOptions.warnValueDiscard +Test / tpolecatExcludeOptions += ScalacOptions.warnNonUnitStatement diff --git a/src/main/scala/ahlers/tree/path/operator/Operator.scala b/src/main/scala/ahlers/tree/path/operator/Operator.scala index d0384c1..14b4d0d 100644 --- a/src/main/scala/ahlers/tree/path/operator/Operator.scala +++ b/src/main/scala/ahlers/tree/path/operator/Operator.scala @@ -1,15 +1,18 @@ package ahlers.tree.path.operator +import ahlers.tree.path.term.Name +import ahlers.tree.path.term.Wildcard + sealed trait Operator object Operator { - sealed trait Anchor + sealed trait Anchor extends Operator object Anchor { - case object RootElement extends Operator with Anchor { + case object RootElement extends Anchor { val toText: String = "$" } - case object CurrentNode extends Operator with Anchor { + case object CurrentNode extends Anchor { val toText: String = "@" } } @@ -18,4 +21,18 @@ object Operator { val toText: String = ".." } + sealed trait DotNotatedChild extends Operator { + def toText: String + } + object DotNotatedChild { + case class MatchingName(toName: Name) extends DotNotatedChild { + override val toText: String = s".${toName.toText}" + } + + case object MatchingWildcard extends DotNotatedChild { + val toWildcard: Wildcard.type = Wildcard + override val toText: String = s".${toWildcard.toText}" + } + } + } diff --git a/src/main/scala/ahlers/tree/path/parser/operator.scala b/src/main/scala/ahlers/tree/path/parser/operator.scala index 6fe0345..f784a31 100644 --- a/src/main/scala/ahlers/tree/path/parser/operator.scala +++ b/src/main/scala/ahlers/tree/path/parser/operator.scala @@ -4,6 +4,9 @@ import ahlers.tree.path.operator.Operator.Anchor import ahlers.tree.path.operator.Operator.Anchor.CurrentNode import ahlers.tree.path.operator.Operator.Anchor.RootElement import ahlers.tree.path.operator.Operator.DeepScan +import ahlers.tree.path.operator.Operator.DotNotatedChild +import ahlers.tree.path.parser.term.name +import ahlers.tree.path.parser.term.wildcard import parsley.Parsley import parsley.character.char import parsley.character.string @@ -16,4 +19,8 @@ object operator { val deepScan: Parsley[DeepScan.type] = string("..").as(DeepScan) + val dotNotatedChildMatchingName: Parsley[DotNotatedChild.MatchingName] = (char('.') *> name).map(DotNotatedChild.MatchingName) + val dotNotatedChildMatchingWildcard: Parsley[DotNotatedChild.MatchingWildcard.type] = (char('.') *> wildcard).as(DotNotatedChild.MatchingWildcard) + val dotNotatedChild: Parsley[DotNotatedChild] = char('.') *> (name.map(DotNotatedChild.MatchingName) | wildcard.as(DotNotatedChild.MatchingWildcard)) + } diff --git a/src/test/scala/ahlers/tree/path/operator/diffx/instances.scala b/src/test/scala/ahlers/tree/path/operator/diffx/instances.scala index 9d5fed8..fa4e7ee 100644 --- a/src/test/scala/ahlers/tree/path/operator/diffx/instances.scala +++ b/src/test/scala/ahlers/tree/path/operator/diffx/instances.scala @@ -4,13 +4,20 @@ import ahlers.tree.path.operator.Operator.Anchor import ahlers.tree.path.operator.Operator.Anchor.CurrentNode import ahlers.tree.path.operator.Operator.Anchor.RootElement import ahlers.tree.path.operator.Operator.DeepScan +import ahlers.tree.path.operator.Operator.DotNotatedChild +import ahlers.tree.path.term.diffx.instances._ import com.softwaremill.diffx.Diff object instances { implicit val diffCurrentNode: Diff[CurrentNode.type] = Diff.derived - implicit val diffDeepScan: Diff[DeepScan.type] = Diff.derived implicit val diffRootElement: Diff[RootElement.type] = Diff.derived implicit val diffAnchor: Diff[Anchor] = Diff.derived + implicit val diffDeepScan: Diff[DeepScan.type] = Diff.derived + + implicit val diffDotNotatedChildMatchingName: Diff[DotNotatedChild.MatchingName] = Diff.derived + implicit val diffDotNotatedChildMatchingWildcard: Diff[DotNotatedChild.MatchingWildcard.type] = Diff.derived + implicit val diffDotNotatedChild: Diff[DotNotatedChild] = Diff.derived + } diff --git a/src/test/scala/ahlers/tree/path/operator/scalacheck/instances.scala b/src/test/scala/ahlers/tree/path/operator/scalacheck/instances.scala index 681e9cb..45da181 100644 --- a/src/test/scala/ahlers/tree/path/operator/scalacheck/instances.scala +++ b/src/test/scala/ahlers/tree/path/operator/scalacheck/instances.scala @@ -4,15 +4,24 @@ import ahlers.tree.path.operator.Operator.Anchor import ahlers.tree.path.operator.Operator.Anchor.CurrentNode import ahlers.tree.path.operator.Operator.Anchor.RootElement import ahlers.tree.path.operator.Operator.DeepScan +import ahlers.tree.path.operator.Operator.DotNotatedChild +import ahlers.tree.path.term.Name +import ahlers.tree.path.term.scalacheck.instances._ import magnolify.scalacheck.semiauto._ import org.scalacheck.Arbitrary +import org.scalacheck.Arbitrary.arbitrary import org.scalacheck.Gen object instances { implicit val arbCurrentNode: Arbitrary[CurrentNode.type] = Arbitrary(Gen.const(CurrentNode)) - implicit val arbDeepScan: Arbitrary[DeepScan.type] = Arbitrary(Gen.const(DeepScan)) implicit val arbRootElement: Arbitrary[RootElement.type] = Arbitrary(Gen.const(RootElement)) implicit val arbAnchor: Arbitrary[Anchor] = ArbitraryDerivation[Anchor] + implicit val arbDeepScan: Arbitrary[DeepScan.type] = Arbitrary(Gen.const(DeepScan)) + + implicit val arbDotNotatedChildMatchingName: Arbitrary[DotNotatedChild.MatchingName] = Arbitrary(arbitrary[Name].map(DotNotatedChild.MatchingName)) + implicit val arbDotNotatedChildMatchingWildcard: Arbitrary[DotNotatedChild.MatchingWildcard.type] = ArbitraryDerivation[DotNotatedChild.MatchingWildcard.type] + implicit val arbDotNotatedChild: Arbitrary[DotNotatedChild] = ArbitraryDerivation[DotNotatedChild] + } diff --git a/src/test/scala/ahlers/tree/path/parser/OperatorSpec.scala b/src/test/scala/ahlers/tree/path/parser/OperatorSpec.scala index 84ee653..6c2c89f 100644 --- a/src/test/scala/ahlers/tree/path/parser/OperatorSpec.scala +++ b/src/test/scala/ahlers/tree/path/parser/OperatorSpec.scala @@ -4,8 +4,11 @@ import ahlers.tree.path.operator.Operator.Anchor import ahlers.tree.path.operator.Operator.Anchor.CurrentNode import ahlers.tree.path.operator.Operator.Anchor.RootElement import ahlers.tree.path.operator.Operator.DeepScan +import ahlers.tree.path.operator.Operator.DotNotatedChild import ahlers.tree.path.operator.diffx.instances._ +import ahlers.tree.path.operator.scalacheck.instances._ import com.softwaremill.diffx.scalatest.DiffShouldMatcher.convertToAnyShouldMatcher +import org.scalatest.Checkpoints.Checkpoint import org.scalatest.matchers.should.Matchers._ import org.scalatest.wordspec.AnyWordSpec import org.scalatestplus.scalacheck.ScalaCheckPropertyChecks._ @@ -16,27 +19,38 @@ import parsley.diffx.instances._ class OperatorSpec extends AnyWordSpec { "Anchor" should { - val parser = operator.anchor + val parser = operator.anchor + val pattern = "^[@\\$]$".r - """accept "$"""" in { - parser.parse("$").shouldMatchTo(Success(RootElement: Anchor)) + s"""accept $pattern""" in { + val checkpoint = new Checkpoint() + + checkpoint(parser.parse(CurrentNode.toText).shouldMatchTo(Success(CurrentNode: Anchor))) + checkpoint(parser.parse(RootElement.toText).shouldMatchTo(Success(RootElement: Anchor))) + + checkpoint.reportAll() } - """accept "@"""" in { - parser.parse("@").shouldMatchTo(Success(CurrentNode: Anchor)) + "reject else" in { + forAll { input: String => + whenever(!pattern.matches(input)) { + parser.parse(input).shouldBe(a[Failure[_]]) + } + } } } "CurrentNode" should { - val parser = operator.currentNode + val parser = operator.currentNode + val pattern = "^@$".r - """accept "$"""" in { - parser.parse("@").shouldMatchTo(Success(CurrentNode)) + s"""accept $pattern""" in { + parser.parse(CurrentNode.toText).shouldMatchTo(Success(CurrentNode)) } "reject else" in { forAll { input: String => - whenever("@" != input) { + whenever(!pattern.matches(input)) { parser.parse(input).shouldBe(a[Failure[_]]) } } @@ -44,15 +58,16 @@ class OperatorSpec extends AnyWordSpec { } "RootElement" should { - val parser = operator.rootElement + val parser = operator.rootElement + val pattern = "^\\$$".r - """accept "$"""" in { - parser.parse("$").shouldMatchTo(Success(RootElement)) + s"""accept $pattern""" in { + parser.parse(RootElement.toText).shouldMatchTo(Success(RootElement)) } "reject else" in { forAll { input: String => - whenever("$" != input) { + whenever(!pattern.matches(input)) { parser.parse(input).shouldBe(a[Failure[_]]) } } @@ -60,15 +75,72 @@ class OperatorSpec extends AnyWordSpec { } "DeepScan" should { - val parser = operator.deepScan + val parser = operator.deepScan + val pattern = "^\\.\\.$".r + + s"""accept $pattern""" in { + parser.parse(DeepScan.toText).shouldMatchTo(Success(DeepScan)) + } + + "reject else" in { + forAll { input: String => + whenever(!pattern.matches(input)) { + parser.parse(input).shouldBe(a[Failure[_]]) + } + } + } + } + + "DotNotatedChild.MatchingName" should { + val parser = operator.dotNotatedChildMatchingName + val pattern = "^\\.[a-zA-Z0-9]+$".r + + s"""accept $pattern""" in { + forAll { matchingName: DotNotatedChild.MatchingName => + parser.parse(matchingName.toText).shouldMatchTo(Success(matchingName)) + } + } - """accept ".."""" in { - parser.parse("..").shouldMatchTo(Success(DeepScan)) + "reject else" in { + forAll { input: String => + whenever(!pattern.matches(input)) { + parser.parse(input).shouldBe(a[Failure[_]]) + } + } + } + } + + "DotNotatedChild.MatchingWildcard" should { + val parser = operator.dotNotatedChildMatchingWildcard + val pattern = "^\\.\\*$".r + + s"""accept $pattern""" in { + parser.parse(DotNotatedChild.MatchingWildcard.toText).shouldMatchTo(Success(DotNotatedChild.MatchingWildcard)) + } + + "reject else" in { + forAll { input: String => + whenever(!pattern.matches(input)) { + parser.parse(input).shouldBe(a[Failure[_]]) + } + } + } + } + + "DotNotatedChild" should { + val parser = operator.dotNotatedChild + val pattern = "^\\.([a-zA-Z0-9]+|\\*)$".r + + s"""accept $pattern""" in { + forAll { dotNotatedChild: DotNotatedChild => + println(dotNotatedChild.toText) + parser.parse(dotNotatedChild.toText).shouldMatchTo(Success(dotNotatedChild)) + } } "reject else" in { forAll { input: String => - whenever(".." != input) { + whenever(!pattern.matches(input)) { parser.parse(input).shouldBe(a[Failure[_]]) } } diff --git a/src/test/scala/ahlers/tree/path/parser/TermSpec.scala b/src/test/scala/ahlers/tree/path/parser/TermSpec.scala index eea63e8..e89ef57 100644 --- a/src/test/scala/ahlers/tree/path/parser/TermSpec.scala +++ b/src/test/scala/ahlers/tree/path/parser/TermSpec.scala @@ -16,9 +16,10 @@ import parsley.diffx.instances._ class TermSpec extends AnyWordSpec { "Index" should { - val parser = term.index + val parser = term.index + val pattern = "^[0-9]+$".r - """accept positive or negative numeric""" in { + s"""accept $pattern""" in { forAll { index: Index => parser.parse(index.toText).shouldMatchTo(Success(index)) } @@ -26,7 +27,7 @@ class TermSpec extends AnyWordSpec { "reject else" in { forAll { input: String => - whenever(!input.matches("^[0-9]+$")) { + whenever(!pattern.matches(input)) { parser.parse(input).shouldBe(a[Failure[_]]) } } @@ -34,9 +35,10 @@ class TermSpec extends AnyWordSpec { } "Name" should { - val parser = term.name + val parser = term.name + val pattern = "^[a-zA-Z0-9]+$".r - """accept alpha-numeric""" in { + s"""accept $pattern""" in { forAll { name: Name => parser.parse(name.toText).shouldMatchTo(Success(name)) } @@ -44,7 +46,7 @@ class TermSpec extends AnyWordSpec { "reject else" in { forAll { input: String => - whenever(!input.matches("^[a-zA-Z0-9]+$")) { + whenever(!pattern.matches(input)) { parser.parse(input).shouldBe(a[Failure[_]]) } } @@ -52,15 +54,16 @@ class TermSpec extends AnyWordSpec { } "Wildcard" should { - val parser = term.wildcard + val parser = term.wildcard + val pattern = "^\\*$".r - """accept "*"""" in { - parser.parse("*").shouldMatchTo(Success(Wildcard)) + s"""accept $pattern""" in { + parser.parse(Wildcard.toText).shouldMatchTo(Success(Wildcard)) } "reject else" in { forAll { input: String => - whenever("*" != input) { + whenever(!pattern.matches(input)) { parser.parse(input).shouldBe(a[Failure[_]]) } } From 969f2a15d6c9cc94784b1a849dc1fa14b1e9d42c Mon Sep 17 00:00:00 2001 From: Michael Ahlers Date: Sat, 11 May 2024 13:54:56 -0600 Subject: [PATCH 14/40] Formatting --- src/main/scala/ahlers/tree/path/operator/Operator.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/scala/ahlers/tree/path/operator/Operator.scala b/src/main/scala/ahlers/tree/path/operator/Operator.scala index 14b4d0d..31dc728 100644 --- a/src/main/scala/ahlers/tree/path/operator/Operator.scala +++ b/src/main/scala/ahlers/tree/path/operator/Operator.scala @@ -31,7 +31,7 @@ object Operator { case object MatchingWildcard extends DotNotatedChild { val toWildcard: Wildcard.type = Wildcard - override val toText: String = s".${toWildcard.toText}" + override val toText: String = s".${toWildcard.toText}" } } From 93ec57005e353d86ad05292273f07901041d125a Mon Sep 17 00:00:00 2001 From: Michael Ahlers Date: Sat, 11 May 2024 13:58:52 -0600 Subject: [PATCH 15/40] Combine existing parsers --- src/main/scala/ahlers/tree/path/parser/operator.scala | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/scala/ahlers/tree/path/parser/operator.scala b/src/main/scala/ahlers/tree/path/parser/operator.scala index f784a31..bb5b646 100644 --- a/src/main/scala/ahlers/tree/path/parser/operator.scala +++ b/src/main/scala/ahlers/tree/path/parser/operator.scala @@ -8,6 +8,7 @@ import ahlers.tree.path.operator.Operator.DotNotatedChild import ahlers.tree.path.parser.term.name import ahlers.tree.path.parser.term.wildcard import parsley.Parsley +import parsley.Parsley.atomic import parsley.character.char import parsley.character.string @@ -21,6 +22,6 @@ object operator { val dotNotatedChildMatchingName: Parsley[DotNotatedChild.MatchingName] = (char('.') *> name).map(DotNotatedChild.MatchingName) val dotNotatedChildMatchingWildcard: Parsley[DotNotatedChild.MatchingWildcard.type] = (char('.') *> wildcard).as(DotNotatedChild.MatchingWildcard) - val dotNotatedChild: Parsley[DotNotatedChild] = char('.') *> (name.map(DotNotatedChild.MatchingName) | wildcard.as(DotNotatedChild.MatchingWildcard)) + val dotNotatedChild: Parsley[DotNotatedChild] = atomic(dotNotatedChildMatchingName) | atomic(dotNotatedChildMatchingWildcard) } From 53bafaf44d0dd492f549f8e84cde65b5134eefcc Mon Sep 17 00:00:00 2001 From: Michael Ahlers Date: Sat, 11 May 2024 13:59:27 -0600 Subject: [PATCH 16/40] Formatting --- dependencies.sbt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dependencies.sbt b/dependencies.sbt index 6df1cc6..1cb199e 100644 --- a/dependencies.sbt +++ b/dependencies.sbt @@ -5,7 +5,7 @@ ThisBuild / scalaVersion := "2.13.14" * @see [[https://github.com/j-mie6/parsley]] */ libraryDependencies += - "com.github.j-mie6" %% "parsley" % "4.5.2" + "com.github.j-mie6" %% "parsley" % "4.5.2" /** * ScalaTest is the most flexible and most popular testing tool in the Scala ecosystem. From ddc52c75c401ef467dfa070b881717e00347ae63 Mon Sep 17 00:00:00 2001 From: Michael Ahlers Date: Sat, 11 May 2024 14:08:37 -0600 Subject: [PATCH 17/40] Support bracket-notated children --- .../ahlers/tree/path/operator/Operator.scala | 19 +++++++++++++++++ .../ahlers/tree/path/parser/operator.scala | 10 +++++++++ .../tree/path/operator/diffx/instances.scala | 7 +++++++ .../path/operator/scalacheck/instances.scala | 7 +++++++ .../tree/path/parser/OperatorSpec.scala | 21 ++++++++++++++++++- 5 files changed, 63 insertions(+), 1 deletion(-) diff --git a/src/main/scala/ahlers/tree/path/operator/Operator.scala b/src/main/scala/ahlers/tree/path/operator/Operator.scala index 31dc728..2756176 100644 --- a/src/main/scala/ahlers/tree/path/operator/Operator.scala +++ b/src/main/scala/ahlers/tree/path/operator/Operator.scala @@ -35,4 +35,23 @@ object Operator { } } + case class BracketNotatedChildren(toNames: Seq[BracketNotatedChildren.Child]) { + val toText: String = toNames.map(_.toText).mkString("[", ",", "]") + } + object BracketNotatedChildren { + sealed trait Child { + def toText: String + } + object Child { + case class MatchingName(toName: Name) extends Child { + override val toText: String = s"'${toName.toText}'" + } + + case object MatchingWildcard extends Child { + val toWildcard: Wildcard.type = Wildcard + override val toText: String = s"${toWildcard.toText}" + } + } + } + } diff --git a/src/main/scala/ahlers/tree/path/parser/operator.scala b/src/main/scala/ahlers/tree/path/parser/operator.scala index bb5b646..dfbc110 100644 --- a/src/main/scala/ahlers/tree/path/parser/operator.scala +++ b/src/main/scala/ahlers/tree/path/parser/operator.scala @@ -3,6 +3,7 @@ package ahlers.tree.path.parser import ahlers.tree.path.operator.Operator.Anchor import ahlers.tree.path.operator.Operator.Anchor.CurrentNode import ahlers.tree.path.operator.Operator.Anchor.RootElement +import ahlers.tree.path.operator.Operator.BracketNotatedChildren import ahlers.tree.path.operator.Operator.DeepScan import ahlers.tree.path.operator.Operator.DotNotatedChild import ahlers.tree.path.parser.term.name @@ -11,6 +12,8 @@ import parsley.Parsley import parsley.Parsley.atomic import parsley.character.char import parsley.character.string +import parsley.character.whitespaces +import parsley.combinator.sepBy object operator { @@ -24,4 +27,11 @@ object operator { val dotNotatedChildMatchingWildcard: Parsley[DotNotatedChild.MatchingWildcard.type] = (char('.') *> wildcard).as(DotNotatedChild.MatchingWildcard) val dotNotatedChild: Parsley[DotNotatedChild] = atomic(dotNotatedChildMatchingName) | atomic(dotNotatedChildMatchingWildcard) + val bracketNotatedChildren: Parsley[BracketNotatedChildren] = { + import BracketNotatedChildren.Child + val childMatchingName: Parsley[Child.MatchingName] = (char('\'') *> name <* char('\'')).map(Child.MatchingName) + val childMatchingWildcard: Parsley[Child.MatchingWildcard.type] = wildcard.as(Child.MatchingWildcard) + (char('[') *> sepBy(atomic(childMatchingName) | atomic(childMatchingWildcard), whitespaces *> char(',') <* whitespaces) <* char(']')).map(BracketNotatedChildren(_)) + } + } diff --git a/src/test/scala/ahlers/tree/path/operator/diffx/instances.scala b/src/test/scala/ahlers/tree/path/operator/diffx/instances.scala index fa4e7ee..3c5110c 100644 --- a/src/test/scala/ahlers/tree/path/operator/diffx/instances.scala +++ b/src/test/scala/ahlers/tree/path/operator/diffx/instances.scala @@ -3,6 +3,7 @@ package ahlers.tree.path.operator.diffx import ahlers.tree.path.operator.Operator.Anchor import ahlers.tree.path.operator.Operator.Anchor.CurrentNode import ahlers.tree.path.operator.Operator.Anchor.RootElement +import ahlers.tree.path.operator.Operator.BracketNotatedChildren import ahlers.tree.path.operator.Operator.DeepScan import ahlers.tree.path.operator.Operator.DotNotatedChild import ahlers.tree.path.term.diffx.instances._ @@ -20,4 +21,10 @@ object instances { implicit val diffDotNotatedChildMatchingWildcard: Diff[DotNotatedChild.MatchingWildcard.type] = Diff.derived implicit val diffDotNotatedChild: Diff[DotNotatedChild] = Diff.derived + implicit val diffBracketNotatedChildren: Diff[BracketNotatedChildren] = { + import BracketNotatedChildren.Child + implicit val diffChild: Diff[Child] = Diff.derived + Diff.derived + } + } diff --git a/src/test/scala/ahlers/tree/path/operator/scalacheck/instances.scala b/src/test/scala/ahlers/tree/path/operator/scalacheck/instances.scala index 45da181..0048ee9 100644 --- a/src/test/scala/ahlers/tree/path/operator/scalacheck/instances.scala +++ b/src/test/scala/ahlers/tree/path/operator/scalacheck/instances.scala @@ -3,6 +3,7 @@ package ahlers.tree.path.operator.scalacheck import ahlers.tree.path.operator.Operator.Anchor import ahlers.tree.path.operator.Operator.Anchor.CurrentNode import ahlers.tree.path.operator.Operator.Anchor.RootElement +import ahlers.tree.path.operator.Operator.BracketNotatedChildren import ahlers.tree.path.operator.Operator.DeepScan import ahlers.tree.path.operator.Operator.DotNotatedChild import ahlers.tree.path.term.Name @@ -24,4 +25,10 @@ object instances { implicit val arbDotNotatedChildMatchingWildcard: Arbitrary[DotNotatedChild.MatchingWildcard.type] = ArbitraryDerivation[DotNotatedChild.MatchingWildcard.type] implicit val arbDotNotatedChild: Arbitrary[DotNotatedChild] = ArbitraryDerivation[DotNotatedChild] + implicit val arbBracketNotatedChildren: Arbitrary[BracketNotatedChildren] = { + import BracketNotatedChildren.Child + implicit val arbChild: Arbitrary[Child] = ArbitraryDerivation[Child] + ArbitraryDerivation[BracketNotatedChildren] + } + } diff --git a/src/test/scala/ahlers/tree/path/parser/OperatorSpec.scala b/src/test/scala/ahlers/tree/path/parser/OperatorSpec.scala index 6c2c89f..b61c82c 100644 --- a/src/test/scala/ahlers/tree/path/parser/OperatorSpec.scala +++ b/src/test/scala/ahlers/tree/path/parser/OperatorSpec.scala @@ -3,6 +3,7 @@ package ahlers.tree.path.parser import ahlers.tree.path.operator.Operator.Anchor import ahlers.tree.path.operator.Operator.Anchor.CurrentNode import ahlers.tree.path.operator.Operator.Anchor.RootElement +import ahlers.tree.path.operator.Operator.BracketNotatedChildren import ahlers.tree.path.operator.Operator.DeepScan import ahlers.tree.path.operator.Operator.DotNotatedChild import ahlers.tree.path.operator.diffx.instances._ @@ -133,7 +134,6 @@ class OperatorSpec extends AnyWordSpec { s"""accept $pattern""" in { forAll { dotNotatedChild: DotNotatedChild => - println(dotNotatedChild.toText) parser.parse(dotNotatedChild.toText).shouldMatchTo(Success(dotNotatedChild)) } } @@ -147,4 +147,23 @@ class OperatorSpec extends AnyWordSpec { } } + "BracketNotatedChild" should { + val parser = operator.bracketNotatedChildren + val pattern = "^\\[('[a-zA-Z0-9]+'|\\*)\\w*(,\\w*'[a-zA-Z0-9]+'|\\*)\\]$".r + + s"""accept $pattern""" in { + forAll { bracketNotatedChildren: BracketNotatedChildren => + parser.parse(bracketNotatedChildren.toText).shouldMatchTo(Success(bracketNotatedChildren)) + } + } + + "reject else" in { + forAll { input: String => + whenever(!pattern.matches(input)) { + parser.parse(input).shouldBe(a[Failure[_]]) + } + } + } + } + } From 970d250d5c26ab33cfca80fa5460850389f16f34 Mon Sep 17 00:00:00 2001 From: Michael Ahlers Date: Sat, 11 May 2024 14:13:22 -0600 Subject: [PATCH 18/40] Support array indexes --- .../ahlers/tree/path/operator/Operator.scala | 5 +++++ .../ahlers/tree/path/parser/operator.scala | 4 ++++ .../tree/path/operator/diffx/instances.scala | 7 +++---- .../path/operator/scalacheck/instances.scala | 3 +++ .../tree/path/parser/OperatorSpec.scala | 20 +++++++++++++++++++ 5 files changed, 35 insertions(+), 4 deletions(-) diff --git a/src/main/scala/ahlers/tree/path/operator/Operator.scala b/src/main/scala/ahlers/tree/path/operator/Operator.scala index 2756176..d6c798e 100644 --- a/src/main/scala/ahlers/tree/path/operator/Operator.scala +++ b/src/main/scala/ahlers/tree/path/operator/Operator.scala @@ -1,5 +1,6 @@ package ahlers.tree.path.operator +import ahlers.tree.path.term.Index import ahlers.tree.path.term.Name import ahlers.tree.path.term.Wildcard @@ -54,4 +55,8 @@ object Operator { } } + case class ArrayIndexes(toIndexes: Seq[Index]) { + val toText: String = toIndexes.map(_.toText).mkString("[", ",", "]") + } + } diff --git a/src/main/scala/ahlers/tree/path/parser/operator.scala b/src/main/scala/ahlers/tree/path/parser/operator.scala index dfbc110..a9da068 100644 --- a/src/main/scala/ahlers/tree/path/parser/operator.scala +++ b/src/main/scala/ahlers/tree/path/parser/operator.scala @@ -3,9 +3,11 @@ package ahlers.tree.path.parser import ahlers.tree.path.operator.Operator.Anchor import ahlers.tree.path.operator.Operator.Anchor.CurrentNode import ahlers.tree.path.operator.Operator.Anchor.RootElement +import ahlers.tree.path.operator.Operator.ArrayIndexes import ahlers.tree.path.operator.Operator.BracketNotatedChildren import ahlers.tree.path.operator.Operator.DeepScan import ahlers.tree.path.operator.Operator.DotNotatedChild +import ahlers.tree.path.parser.term.index import ahlers.tree.path.parser.term.name import ahlers.tree.path.parser.term.wildcard import parsley.Parsley @@ -34,4 +36,6 @@ object operator { (char('[') *> sepBy(atomic(childMatchingName) | atomic(childMatchingWildcard), whitespaces *> char(',') <* whitespaces) <* char(']')).map(BracketNotatedChildren(_)) } + val arrayIndexes: Parsley[ArrayIndexes] = (char('[') *> sepBy(index, whitespaces *> char(',') <* whitespaces) <* char(']')).map(ArrayIndexes) + } diff --git a/src/test/scala/ahlers/tree/path/operator/diffx/instances.scala b/src/test/scala/ahlers/tree/path/operator/diffx/instances.scala index 3c5110c..624626b 100644 --- a/src/test/scala/ahlers/tree/path/operator/diffx/instances.scala +++ b/src/test/scala/ahlers/tree/path/operator/diffx/instances.scala @@ -1,11 +1,8 @@ package ahlers.tree.path.operator.diffx -import ahlers.tree.path.operator.Operator.Anchor +import ahlers.tree.path.operator.Operator.{Anchor, ArrayIndexes, BracketNotatedChildren, DeepScan, DotNotatedChild} import ahlers.tree.path.operator.Operator.Anchor.CurrentNode import ahlers.tree.path.operator.Operator.Anchor.RootElement -import ahlers.tree.path.operator.Operator.BracketNotatedChildren -import ahlers.tree.path.operator.Operator.DeepScan -import ahlers.tree.path.operator.Operator.DotNotatedChild import ahlers.tree.path.term.diffx.instances._ import com.softwaremill.diffx.Diff @@ -27,4 +24,6 @@ object instances { Diff.derived } + implicit val diffArrayIndexes: Diff[ArrayIndexes] = Diff.derived + } diff --git a/src/test/scala/ahlers/tree/path/operator/scalacheck/instances.scala b/src/test/scala/ahlers/tree/path/operator/scalacheck/instances.scala index 0048ee9..7b90325 100644 --- a/src/test/scala/ahlers/tree/path/operator/scalacheck/instances.scala +++ b/src/test/scala/ahlers/tree/path/operator/scalacheck/instances.scala @@ -3,6 +3,7 @@ package ahlers.tree.path.operator.scalacheck import ahlers.tree.path.operator.Operator.Anchor import ahlers.tree.path.operator.Operator.Anchor.CurrentNode import ahlers.tree.path.operator.Operator.Anchor.RootElement +import ahlers.tree.path.operator.Operator.ArrayIndexes import ahlers.tree.path.operator.Operator.BracketNotatedChildren import ahlers.tree.path.operator.Operator.DeepScan import ahlers.tree.path.operator.Operator.DotNotatedChild @@ -31,4 +32,6 @@ object instances { ArbitraryDerivation[BracketNotatedChildren] } + implicit val arbArrayIndexes: Arbitrary[ArrayIndexes] = ArbitraryDerivation[ArrayIndexes] + } diff --git a/src/test/scala/ahlers/tree/path/parser/OperatorSpec.scala b/src/test/scala/ahlers/tree/path/parser/OperatorSpec.scala index b61c82c..1fcd80a 100644 --- a/src/test/scala/ahlers/tree/path/parser/OperatorSpec.scala +++ b/src/test/scala/ahlers/tree/path/parser/OperatorSpec.scala @@ -3,6 +3,7 @@ package ahlers.tree.path.parser import ahlers.tree.path.operator.Operator.Anchor import ahlers.tree.path.operator.Operator.Anchor.CurrentNode import ahlers.tree.path.operator.Operator.Anchor.RootElement +import ahlers.tree.path.operator.Operator.ArrayIndexes import ahlers.tree.path.operator.Operator.BracketNotatedChildren import ahlers.tree.path.operator.Operator.DeepScan import ahlers.tree.path.operator.Operator.DotNotatedChild @@ -166,4 +167,23 @@ class OperatorSpec extends AnyWordSpec { } } + "ArrayIndexes" should { + val parser = operator.arrayIndexes + val pattern = "^\\[[0-9]+\\w*(,\\w*[0-9]+)\\]$".r + + s"""accept $pattern""" in { + forAll { arrayIndexes: ArrayIndexes => + parser.parse(arrayIndexes.toText).shouldMatchTo(Success(arrayIndexes)) + } + } + + "reject else" in { + forAll { input: String => + whenever(!pattern.matches(input)) { + parser.parse(input).shouldBe(a[Failure[_]]) + } + } + } + } + } From 1b65db41f78ef1d9ee68cc266444549e33486f5c Mon Sep 17 00:00:00 2001 From: Michael Ahlers Date: Sat, 11 May 2024 14:21:32 -0600 Subject: [PATCH 19/40] Support array slices --- .../ahlers/tree/path/operator/Operator.scala | 15 ++++ .../ahlers/tree/path/parser/operator.scala | 6 ++ .../tree/path/operator/diffx/instances.scala | 7 +- .../path/operator/scalacheck/instances.scala | 6 ++ .../tree/path/parser/OperatorSpec.scala | 81 ++++++++++++++++++- 5 files changed, 112 insertions(+), 3 deletions(-) diff --git a/src/main/scala/ahlers/tree/path/operator/Operator.scala b/src/main/scala/ahlers/tree/path/operator/Operator.scala index d6c798e..89139d7 100644 --- a/src/main/scala/ahlers/tree/path/operator/Operator.scala +++ b/src/main/scala/ahlers/tree/path/operator/Operator.scala @@ -59,4 +59,19 @@ object Operator { val toText: String = toIndexes.map(_.toText).mkString("[", ",", "]") } + sealed trait ArraySlice extends Operator { + def toText: String + } + object ArraySlice { + case class LeftBounded(start: Index) extends ArraySlice { + override val toText: String = s"[${start.toText}:]" + } + case class RightBounded(end: Index) extends ArraySlice { + override val toText: String = s"[:${end.toText}]" + } + case class Bounded(start: Index, end: Index) extends ArraySlice { + override val toText: String = s"[${start.toText}:${end.toText}]" + } + } + } diff --git a/src/main/scala/ahlers/tree/path/parser/operator.scala b/src/main/scala/ahlers/tree/path/parser/operator.scala index a9da068..6c41cfc 100644 --- a/src/main/scala/ahlers/tree/path/parser/operator.scala +++ b/src/main/scala/ahlers/tree/path/parser/operator.scala @@ -4,6 +4,7 @@ import ahlers.tree.path.operator.Operator.Anchor import ahlers.tree.path.operator.Operator.Anchor.CurrentNode import ahlers.tree.path.operator.Operator.Anchor.RootElement import ahlers.tree.path.operator.Operator.ArrayIndexes +import ahlers.tree.path.operator.Operator.ArraySlice import ahlers.tree.path.operator.Operator.BracketNotatedChildren import ahlers.tree.path.operator.Operator.DeepScan import ahlers.tree.path.operator.Operator.DotNotatedChild @@ -38,4 +39,9 @@ object operator { val arrayIndexes: Parsley[ArrayIndexes] = (char('[') *> sepBy(index, whitespaces *> char(',') <* whitespaces) <* char(']')).map(ArrayIndexes) + val arraySliceLeftBounded: Parsley[ArraySlice.LeftBounded] = (char('[') *> index <~ char(':') <* char(']')).map(ArraySlice.LeftBounded) + val arraySliceRightBounded: Parsley[ArraySlice.RightBounded] = (char('[') *> char(':') ~> index <~ char(']')).map(ArraySlice.RightBounded) + val arraySliceBounded: Parsley[ArraySlice.Bounded] = (char('[') *> index <~> char(':') *> index <~ char(']')).map((ArraySlice.Bounded(_, _)).tupled) + val arraySlice: Parsley[ArraySlice] = atomic(arraySliceLeftBounded) | atomic(arraySliceRightBounded) | atomic(arraySliceBounded) + } diff --git a/src/test/scala/ahlers/tree/path/operator/diffx/instances.scala b/src/test/scala/ahlers/tree/path/operator/diffx/instances.scala index 624626b..411c80b 100644 --- a/src/test/scala/ahlers/tree/path/operator/diffx/instances.scala +++ b/src/test/scala/ahlers/tree/path/operator/diffx/instances.scala @@ -1,6 +1,6 @@ package ahlers.tree.path.operator.diffx -import ahlers.tree.path.operator.Operator.{Anchor, ArrayIndexes, BracketNotatedChildren, DeepScan, DotNotatedChild} +import ahlers.tree.path.operator.Operator.{Anchor, ArrayIndexes, ArraySlice, BracketNotatedChildren, DeepScan, DotNotatedChild} import ahlers.tree.path.operator.Operator.Anchor.CurrentNode import ahlers.tree.path.operator.Operator.Anchor.RootElement import ahlers.tree.path.term.diffx.instances._ @@ -26,4 +26,9 @@ object instances { implicit val diffArrayIndexes: Diff[ArrayIndexes] = Diff.derived + implicit val diffArraySliceLeftBounded: Diff[ArraySlice.LeftBounded] = Diff.derived + implicit val diffArraySliceRightBounded: Diff[ArraySlice.RightBounded] = Diff.derived + implicit val diffArraySliceBounded: Diff[ArraySlice.Bounded] = Diff.derived + implicit val diffArraySlice: Diff[ArraySlice] = Diff.derived + } diff --git a/src/test/scala/ahlers/tree/path/operator/scalacheck/instances.scala b/src/test/scala/ahlers/tree/path/operator/scalacheck/instances.scala index 7b90325..316896d 100644 --- a/src/test/scala/ahlers/tree/path/operator/scalacheck/instances.scala +++ b/src/test/scala/ahlers/tree/path/operator/scalacheck/instances.scala @@ -4,6 +4,7 @@ import ahlers.tree.path.operator.Operator.Anchor import ahlers.tree.path.operator.Operator.Anchor.CurrentNode import ahlers.tree.path.operator.Operator.Anchor.RootElement import ahlers.tree.path.operator.Operator.ArrayIndexes +import ahlers.tree.path.operator.Operator.ArraySlice import ahlers.tree.path.operator.Operator.BracketNotatedChildren import ahlers.tree.path.operator.Operator.DeepScan import ahlers.tree.path.operator.Operator.DotNotatedChild @@ -34,4 +35,9 @@ object instances { implicit val arbArrayIndexes: Arbitrary[ArrayIndexes] = ArbitraryDerivation[ArrayIndexes] + implicit val arbArraySliceLeftBounded: Arbitrary[ArraySlice.LeftBounded] = ArbitraryDerivation[ArraySlice.LeftBounded] + implicit val arbArraySliceRightBounded: Arbitrary[ArraySlice.RightBounded] = ArbitraryDerivation[ArraySlice.RightBounded] + implicit val arbArraySliceBounded: Arbitrary[ArraySlice.Bounded] = ArbitraryDerivation[ArraySlice.Bounded] + implicit val arbArraySlice: Arbitrary[ArraySlice] = ArbitraryDerivation[ArraySlice] + } diff --git a/src/test/scala/ahlers/tree/path/parser/OperatorSpec.scala b/src/test/scala/ahlers/tree/path/parser/OperatorSpec.scala index 1fcd80a..ba9bdfd 100644 --- a/src/test/scala/ahlers/tree/path/parser/OperatorSpec.scala +++ b/src/test/scala/ahlers/tree/path/parser/OperatorSpec.scala @@ -4,6 +4,7 @@ import ahlers.tree.path.operator.Operator.Anchor import ahlers.tree.path.operator.Operator.Anchor.CurrentNode import ahlers.tree.path.operator.Operator.Anchor.RootElement import ahlers.tree.path.operator.Operator.ArrayIndexes +import ahlers.tree.path.operator.Operator.ArraySlice import ahlers.tree.path.operator.Operator.BracketNotatedChildren import ahlers.tree.path.operator.Operator.DeepScan import ahlers.tree.path.operator.Operator.DotNotatedChild @@ -98,8 +99,8 @@ class OperatorSpec extends AnyWordSpec { val pattern = "^\\.[a-zA-Z0-9]+$".r s"""accept $pattern""" in { - forAll { matchingName: DotNotatedChild.MatchingName => - parser.parse(matchingName.toText).shouldMatchTo(Success(matchingName)) + forAll { dotNotatedChild: DotNotatedChild.MatchingName => + parser.parse(dotNotatedChild.toText).shouldMatchTo(Success(dotNotatedChild)) } } @@ -186,4 +187,80 @@ class OperatorSpec extends AnyWordSpec { } } + "ArraySlice.LeftBounded" should { + val parser = operator.arraySliceLeftBounded + val pattern = "^\\[\\d+:\\]$".r + + s"""accept $pattern""" in { + forAll { arraySlice: ArraySlice.LeftBounded => + parser.parse(arraySlice.toText).shouldMatchTo(Success(arraySlice)) + } + } + + "reject else" in { + forAll { input: String => + whenever(!pattern.matches(input)) { + parser.parse(input).shouldBe(a[Failure[_]]) + } + } + } + } + + "ArraySlice.RightBounded" should { + val parser = operator.arraySliceRightBounded + val pattern = "^\\[:\\d+\\]$".r + + s"""accept $pattern""" in { + forAll { arraySlice: ArraySlice.RightBounded => + parser.parse(arraySlice.toText).shouldMatchTo(Success(arraySlice)) + } + } + + "reject else" in { + forAll { input: String => + whenever(!pattern.matches(input)) { + parser.parse(input).shouldBe(a[Failure[_]]) + } + } + } + } + + "ArraySlice.Bounded" should { + val parser = operator.arraySliceBounded + val pattern = "^\\[\\d+:\\d+\\]$".r + + s"""accept $pattern""" in { + forAll { arraySlice: ArraySlice.Bounded => + parser.parse(arraySlice.toText).shouldMatchTo(Success(arraySlice)) + } + } + + "reject else" in { + forAll { input: String => + whenever(!pattern.matches(input)) { + parser.parse(input).shouldBe(a[Failure[_]]) + } + } + } + } + + "ArraySlice" should { + val parser = operator.arraySlice + val pattern = "^(\\[\\d+:\\]|\\[:\\d+\\]|\\[\\d+:\\d+\\])$".r + + s"""accept $pattern""" in { + forAll { arraySlice: ArraySlice => + parser.parse(arraySlice.toText).shouldMatchTo(Success(arraySlice)) + } + } + + "reject else" in { + forAll { input: String => + whenever(!pattern.matches(input)) { + parser.parse(input).shouldBe(a[Failure[_]]) + } + } + } + } + } From d301d3274e73ee02c0f07e7be70e404e0dcab67d Mon Sep 17 00:00:00 2001 From: Michael Ahlers Date: Sat, 11 May 2024 14:28:47 -0600 Subject: [PATCH 20/40] Remove notion of anchoring --- .../ahlers/tree/path/JsonPathParser.scala | 105 ------------------ .../ahlers/tree/path/operator/Operator.scala | 18 +-- .../ahlers/tree/path/parser/operator.scala | 14 +-- .../scala/ahlers/tree/path/parser/term.scala | 4 - .../ahlers/tree/path/term/Wildcard.scala | 5 - .../tree/path/operator/diffx/instances.scala | 8 +- .../path/operator/scalacheck/instances.scala | 10 +- .../tree/path/parser/OperatorSpec.scala | 31 ++---- .../ahlers/tree/path/parser/TermSpec.scala | 19 ---- .../tree/path/term/diffx/instances.scala | 6 +- .../tree/path/term/scalacheck/instances.scala | 3 - 11 files changed, 34 insertions(+), 189 deletions(-) delete mode 100644 src/main/scala/ahlers/tree/path/JsonPathParser.scala delete mode 100644 src/main/scala/ahlers/tree/path/term/Wildcard.scala diff --git a/src/main/scala/ahlers/tree/path/JsonPathParser.scala b/src/main/scala/ahlers/tree/path/JsonPathParser.scala deleted file mode 100644 index 1421e17..0000000 --- a/src/main/scala/ahlers/tree/path/JsonPathParser.scala +++ /dev/null @@ -1,105 +0,0 @@ -package ahlers.tree.path - -import ahlers.tree.path.JsonPathParser.Name.Wildcard -import parsley.Parsley -import parsley.Parsley._ -import parsley.character._ -import parsley.combinator.sepBy - -object JsonPathParser extends App { - - val leftParenthesis: Parsley[Char] = char('(') - val rightParenthesis: Parsley[Char] = char(')') - val leftBracket: Parsley[Char] = char('[') - val rightBracket: Parsley[Char] = char(']') - val comma: Parsley[Char] = char(',') - val colon: Parsley[Char] = char(':') - val dot: Parsley[Char] = char('.') - val singleQuote: Parsley[Char] = char('\'') - - sealed trait Name - object Name { - case object Wildcard extends Name { - val parser: Parsley[Wildcard.type] = string("*").as(Wildcard) - } - - case class Literal(toText: String) extends Name - object Literal { - val parser: Parsley[Literal] = stringOfSome(satisfy(_.isLetterOrDigit)).map(Literal(_)) - } - - val parser: Parsley[Name] = Wildcard.parser | Literal.parser - } - - case class Index(toInt: Int) - object Index { - val parser: Parsley[Index] = satisfy(_.isDigit).map(_.toInt).map(Index(_)) - } - - sealed trait Operator - object Operator { - - sealed trait Anchor extends Operator - object Anchor { - - case object Root extends Anchor { - val parser: Parsley[Root.type] = char('$').as(Root) - } - - case object Node extends Anchor { - val parser: Parsley[Node.type] = char('@').as(Node) - } - - val parser: Parsley[Anchor] = Root.parser | Node.parser - } - - case object DeepScan extends Operator { - val parser: Parsley[DeepScan.type] = string("..").as(DeepScan) - } - - case class Child(toName: Name) extends Operator - object Child { - val parser: Parsley[Child] = dot *> Name.parser.map(Child(_)) - } - - case class ChildSubscript(toNames: Seq[Name]) extends Operator - object ChildSubscript { - val parser: Parsley[Operator.ChildSubscript] = { - val quotedParser: Parsley[Name.Literal] = singleQuote *> Name.Literal.parser <~ singleQuote - (leftBracket *> sepBy(quotedParser <|> Wildcard.parser, whitespaces *> comma <* whitespaces) <* rightBracket).map(ChildSubscript(_)) - } - } - - case class IndexSubscript(toIndexes: Seq[Index]) extends Operator - object IndexSubscript { - val parser: Parsley[Operator.IndexSubscript] = (leftBracket *> sepBy(Index.parser, comma) <* rightBracket).map(IndexSubscript(_)) - } - - sealed trait ArraySlice extends Operator - object ArraySlice { - case class LeftBound(start: Index) extends ArraySlice - object LeftBound { - val parser: Parsley[LeftBound] = (leftBracket *> Index.parser <~ colon <* rightBracket).map(LeftBound(_)) - } - - case class RightBound(end: Index) extends ArraySlice - object RightBound { - val parser: Parsley[RightBound] = (leftBracket *> colon ~> Index.parser <* rightBracket).map(RightBound(_)) - } - - case class BothBounds(start: Index, end: Index) extends ArraySlice - object BothBounds { - val parser: Parsley[BothBounds] = (leftBracket *> Index.parser <~> colon *> Index.parser <* rightBracket).map((BothBounds(_, _)).tupled) - } - - val parser: Parsley[ArraySlice] = LeftBound.parser | RightBound.parser | BothBounds.parser - } - - case class Select(toOperator: Seq[Operator]) extends Operator - object Select { - val parser: Parsley[Select] = many(Child.parser | ChildSubscript.parser | IndexSubscript.parser | ArraySlice.parser | DeepScan.parser).map(Select(_)) - } - - val operator: Parsley[Operator] = Anchor.parser *> Select.parser - } -} diff --git a/src/main/scala/ahlers/tree/path/operator/Operator.scala b/src/main/scala/ahlers/tree/path/operator/Operator.scala index 89139d7..64bbb9b 100644 --- a/src/main/scala/ahlers/tree/path/operator/Operator.scala +++ b/src/main/scala/ahlers/tree/path/operator/Operator.scala @@ -2,20 +2,20 @@ package ahlers.tree.path.operator import ahlers.tree.path.term.Index import ahlers.tree.path.term.Name -import ahlers.tree.path.term.Wildcard sealed trait Operator object Operator { - sealed trait Anchor extends Operator - object Anchor { - case object RootElement extends Anchor { - val toText: String = "$" - } + case object RootElement extends Operator { + val toText: String = "$" + } - case object CurrentNode extends Anchor { - val toText: String = "@" - } + case object CurrentNode extends Operator { + val toText: String = "@" + } + + case object Wildcard { + val toText: String = "*" } case object DeepScan extends Operator { diff --git a/src/main/scala/ahlers/tree/path/parser/operator.scala b/src/main/scala/ahlers/tree/path/parser/operator.scala index 6c41cfc..a59db26 100644 --- a/src/main/scala/ahlers/tree/path/parser/operator.scala +++ b/src/main/scala/ahlers/tree/path/parser/operator.scala @@ -1,16 +1,8 @@ package ahlers.tree.path.parser -import ahlers.tree.path.operator.Operator.Anchor -import ahlers.tree.path.operator.Operator.Anchor.CurrentNode -import ahlers.tree.path.operator.Operator.Anchor.RootElement -import ahlers.tree.path.operator.Operator.ArrayIndexes -import ahlers.tree.path.operator.Operator.ArraySlice -import ahlers.tree.path.operator.Operator.BracketNotatedChildren -import ahlers.tree.path.operator.Operator.DeepScan -import ahlers.tree.path.operator.Operator.DotNotatedChild +import ahlers.tree.path.operator.Operator.{ArrayIndexes, ArraySlice, BracketNotatedChildren, CurrentNode, DeepScan, DotNotatedChild, RootElement, Wildcard} import ahlers.tree.path.parser.term.index import ahlers.tree.path.parser.term.name -import ahlers.tree.path.parser.term.wildcard import parsley.Parsley import parsley.Parsley.atomic import parsley.character.char @@ -21,8 +13,10 @@ import parsley.combinator.sepBy object operator { val currentNode: Parsley[CurrentNode.type] = char('@').as(CurrentNode) + val rootElement: Parsley[RootElement.type] = char('$').as(RootElement) - val anchor: Parsley[Anchor] = currentNode | rootElement + + val wildcard: Parsley[Wildcard.type] = char('*').as(Wildcard) val deepScan: Parsley[DeepScan.type] = string("..").as(DeepScan) diff --git a/src/main/scala/ahlers/tree/path/parser/term.scala b/src/main/scala/ahlers/tree/path/parser/term.scala index b023320..a7d64f3 100644 --- a/src/main/scala/ahlers/tree/path/parser/term.scala +++ b/src/main/scala/ahlers/tree/path/parser/term.scala @@ -2,9 +2,7 @@ package ahlers.tree.path.parser import ahlers.tree.path.term.Index import ahlers.tree.path.term.Name -import ahlers.tree.path.term.Wildcard import parsley.Parsley -import parsley.character.char import parsley.character.satisfy import parsley.character.stringOfSome @@ -15,6 +13,4 @@ object term { val isValid: Set[Char] = ('a' to 'z').toSet ++ ('A' to 'Z').toSet ++ ('0' to '9').toSet stringOfSome(satisfy(isValid)).map(Name) } - - val wildcard: Parsley[Wildcard.type] = char('*').as(Wildcard) } diff --git a/src/main/scala/ahlers/tree/path/term/Wildcard.scala b/src/main/scala/ahlers/tree/path/term/Wildcard.scala deleted file mode 100644 index 0672323..0000000 --- a/src/main/scala/ahlers/tree/path/term/Wildcard.scala +++ /dev/null @@ -1,5 +0,0 @@ -package ahlers.tree.path.term - -case object Wildcard { - val toText: String = "*" -} diff --git a/src/test/scala/ahlers/tree/path/operator/diffx/instances.scala b/src/test/scala/ahlers/tree/path/operator/diffx/instances.scala index 411c80b..4c430d4 100644 --- a/src/test/scala/ahlers/tree/path/operator/diffx/instances.scala +++ b/src/test/scala/ahlers/tree/path/operator/diffx/instances.scala @@ -1,16 +1,16 @@ package ahlers.tree.path.operator.diffx -import ahlers.tree.path.operator.Operator.{Anchor, ArrayIndexes, ArraySlice, BracketNotatedChildren, DeepScan, DotNotatedChild} -import ahlers.tree.path.operator.Operator.Anchor.CurrentNode -import ahlers.tree.path.operator.Operator.Anchor.RootElement +import ahlers.tree.path.operator.Operator.{ArrayIndexes, ArraySlice, BracketNotatedChildren, CurrentNode, DeepScan, DotNotatedChild, RootElement, Wildcard} import ahlers.tree.path.term.diffx.instances._ import com.softwaremill.diffx.Diff object instances { implicit val diffCurrentNode: Diff[CurrentNode.type] = Diff.derived + implicit val diffRootElement: Diff[RootElement.type] = Diff.derived - implicit val diffAnchor: Diff[Anchor] = Diff.derived + + implicit val diffWildcard: Diff[Wildcard.type] = Diff.derived implicit val diffDeepScan: Diff[DeepScan.type] = Diff.derived diff --git a/src/test/scala/ahlers/tree/path/operator/scalacheck/instances.scala b/src/test/scala/ahlers/tree/path/operator/scalacheck/instances.scala index 316896d..1a1180e 100644 --- a/src/test/scala/ahlers/tree/path/operator/scalacheck/instances.scala +++ b/src/test/scala/ahlers/tree/path/operator/scalacheck/instances.scala @@ -1,13 +1,13 @@ package ahlers.tree.path.operator.scalacheck -import ahlers.tree.path.operator.Operator.Anchor -import ahlers.tree.path.operator.Operator.Anchor.CurrentNode -import ahlers.tree.path.operator.Operator.Anchor.RootElement import ahlers.tree.path.operator.Operator.ArrayIndexes import ahlers.tree.path.operator.Operator.ArraySlice import ahlers.tree.path.operator.Operator.BracketNotatedChildren +import ahlers.tree.path.operator.Operator.CurrentNode import ahlers.tree.path.operator.Operator.DeepScan import ahlers.tree.path.operator.Operator.DotNotatedChild +import ahlers.tree.path.operator.Operator.RootElement +import ahlers.tree.path.operator.Operator.Wildcard import ahlers.tree.path.term.Name import ahlers.tree.path.term.scalacheck.instances._ import magnolify.scalacheck.semiauto._ @@ -18,8 +18,10 @@ import org.scalacheck.Gen object instances { implicit val arbCurrentNode: Arbitrary[CurrentNode.type] = Arbitrary(Gen.const(CurrentNode)) + implicit val arbRootElement: Arbitrary[RootElement.type] = Arbitrary(Gen.const(RootElement)) - implicit val arbAnchor: Arbitrary[Anchor] = ArbitraryDerivation[Anchor] + + implicit val arbWildcard: Arbitrary[Wildcard.type] = Arbitrary(Gen.const(Wildcard)) implicit val arbDeepScan: Arbitrary[DeepScan.type] = Arbitrary(Gen.const(DeepScan)) diff --git a/src/test/scala/ahlers/tree/path/parser/OperatorSpec.scala b/src/test/scala/ahlers/tree/path/parser/OperatorSpec.scala index ba9bdfd..878d3b1 100644 --- a/src/test/scala/ahlers/tree/path/parser/OperatorSpec.scala +++ b/src/test/scala/ahlers/tree/path/parser/OperatorSpec.scala @@ -1,17 +1,9 @@ package ahlers.tree.path.parser -import ahlers.tree.path.operator.Operator.Anchor -import ahlers.tree.path.operator.Operator.Anchor.CurrentNode -import ahlers.tree.path.operator.Operator.Anchor.RootElement -import ahlers.tree.path.operator.Operator.ArrayIndexes -import ahlers.tree.path.operator.Operator.ArraySlice -import ahlers.tree.path.operator.Operator.BracketNotatedChildren -import ahlers.tree.path.operator.Operator.DeepScan -import ahlers.tree.path.operator.Operator.DotNotatedChild +import ahlers.tree.path.operator.Operator._ import ahlers.tree.path.operator.diffx.instances._ import ahlers.tree.path.operator.scalacheck.instances._ import com.softwaremill.diffx.scalatest.DiffShouldMatcher.convertToAnyShouldMatcher -import org.scalatest.Checkpoints.Checkpoint import org.scalatest.matchers.should.Matchers._ import org.scalatest.wordspec.AnyWordSpec import org.scalatestplus.scalacheck.ScalaCheckPropertyChecks._ @@ -21,17 +13,12 @@ import parsley.diffx.instances._ class OperatorSpec extends AnyWordSpec { - "Anchor" should { - val parser = operator.anchor - val pattern = "^[@\\$]$".r + "RootElement" should { + val parser = operator.rootElement + val pattern = "^\\$$".r s"""accept $pattern""" in { - val checkpoint = new Checkpoint() - - checkpoint(parser.parse(CurrentNode.toText).shouldMatchTo(Success(CurrentNode: Anchor))) - checkpoint(parser.parse(RootElement.toText).shouldMatchTo(Success(RootElement: Anchor))) - - checkpoint.reportAll() + parser.parse(RootElement.toText).shouldMatchTo(Success(RootElement)) } "reject else" in { @@ -60,12 +47,12 @@ class OperatorSpec extends AnyWordSpec { } } - "RootElement" should { - val parser = operator.rootElement - val pattern = "^\\$$".r + "Wildcard" should { + val parser = operator.wildcard + val pattern = "^\\*$".r s"""accept $pattern""" in { - parser.parse(RootElement.toText).shouldMatchTo(Success(RootElement)) + parser.parse(Wildcard.toText).shouldMatchTo(Success(Wildcard)) } "reject else" in { diff --git a/src/test/scala/ahlers/tree/path/parser/TermSpec.scala b/src/test/scala/ahlers/tree/path/parser/TermSpec.scala index e89ef57..dea4402 100644 --- a/src/test/scala/ahlers/tree/path/parser/TermSpec.scala +++ b/src/test/scala/ahlers/tree/path/parser/TermSpec.scala @@ -2,7 +2,6 @@ package ahlers.tree.path.parser import ahlers.tree.path.term.Index import ahlers.tree.path.term.Name -import ahlers.tree.path.term.Wildcard import ahlers.tree.path.term.diffx.instances._ import ahlers.tree.path.term.scalacheck.instances._ import com.softwaremill.diffx.scalatest.DiffShouldMatcher.convertToAnyShouldMatcher @@ -52,22 +51,4 @@ class TermSpec extends AnyWordSpec { } } } - - "Wildcard" should { - val parser = term.wildcard - val pattern = "^\\*$".r - - s"""accept $pattern""" in { - parser.parse(Wildcard.toText).shouldMatchTo(Success(Wildcard)) - } - - "reject else" in { - forAll { input: String => - whenever(!pattern.matches(input)) { - parser.parse(input).shouldBe(a[Failure[_]]) - } - } - } - } - } diff --git a/src/test/scala/ahlers/tree/path/term/diffx/instances.scala b/src/test/scala/ahlers/tree/path/term/diffx/instances.scala index d3d8804..ab55655 100644 --- a/src/test/scala/ahlers/tree/path/term/diffx/instances.scala +++ b/src/test/scala/ahlers/tree/path/term/diffx/instances.scala @@ -2,13 +2,11 @@ package ahlers.tree.path.term.diffx import ahlers.tree.path.term.Index import ahlers.tree.path.term.Name -import ahlers.tree.path.term.Wildcard import com.softwaremill.diffx.Diff object instances { - implicit val diffIndex: Diff[Index] = Diff.derived - implicit val diffName: Diff[Name] = Diff.derived - implicit val diffWildcard: Diff[Wildcard.type] = Diff.derived + implicit val diffIndex: Diff[Index] = Diff.derived + implicit val diffName: Diff[Name] = Diff.derived } diff --git a/src/test/scala/ahlers/tree/path/term/scalacheck/instances.scala b/src/test/scala/ahlers/tree/path/term/scalacheck/instances.scala index 20dc9d8..8532cb0 100644 --- a/src/test/scala/ahlers/tree/path/term/scalacheck/instances.scala +++ b/src/test/scala/ahlers/tree/path/term/scalacheck/instances.scala @@ -2,7 +2,6 @@ package ahlers.tree.path.term.scalacheck import ahlers.tree.path.term.Index import ahlers.tree.path.term.Name -import ahlers.tree.path.term.Wildcard import org.scalacheck.Arbitrary import org.scalacheck.Gen @@ -15,6 +14,4 @@ object instances { tail <- Gen.alphaNumStr } yield Name(s"$head$tail")) - implicit val arbWildcard: Arbitrary[Wildcard.type] = Arbitrary(Gen.const(Wildcard)) - } From 84e8d300ac3d1d9888a110ecbb8552131e8741de Mon Sep 17 00:00:00 2001 From: Michael Ahlers Date: Sat, 11 May 2024 14:31:57 -0600 Subject: [PATCH 21/40] Rename packages --- .../{operator => operators}/Operator.scala | 6 ++--- .../path/{parser => parsers}/operator.scala | 8 +++---- .../tree/path/{parser => parsers}/term.scala | 6 ++--- .../tree/path/{term => terms}/Index.scala | 2 +- .../tree/path/{term => terms}/Name.scala | 2 +- .../diffx/instances.scala | 6 ++--- .../scalacheck/instances.scala | 24 +++++++++---------- .../{parser => parsers}/OperatorSpec.scala | 8 +++---- .../path/{parser => parsers}/TermSpec.scala | 10 ++++---- .../{term => terms}/diffx/instances.scala | 6 ++--- .../scalacheck/instances.scala | 6 ++--- 11 files changed, 42 insertions(+), 42 deletions(-) rename src/main/scala/ahlers/tree/path/{operator => operators}/Operator.scala (94%) rename src/main/scala/ahlers/tree/path/{parser => parsers}/operator.scala (88%) rename src/main/scala/ahlers/tree/path/{parser => parsers}/term.scala (77%) rename src/main/scala/ahlers/tree/path/{term => terms}/Index.scala (69%) rename src/main/scala/ahlers/tree/path/{term => terms}/Name.scala (51%) rename src/test/scala/ahlers/tree/path/{operator => operators}/diffx/instances.scala (85%) rename src/test/scala/ahlers/tree/path/{operator => operators}/scalacheck/instances.scala (75%) rename src/test/scala/ahlers/tree/path/{parser => parsers}/OperatorSpec.scala (97%) rename src/test/scala/ahlers/tree/path/{parser => parsers}/TermSpec.scala (85%) rename src/test/scala/ahlers/tree/path/{term => terms}/diffx/instances.scala (60%) rename src/test/scala/ahlers/tree/path/{term => terms}/scalacheck/instances.scala (73%) diff --git a/src/main/scala/ahlers/tree/path/operator/Operator.scala b/src/main/scala/ahlers/tree/path/operators/Operator.scala similarity index 94% rename from src/main/scala/ahlers/tree/path/operator/Operator.scala rename to src/main/scala/ahlers/tree/path/operators/Operator.scala index 64bbb9b..ef1cc5f 100644 --- a/src/main/scala/ahlers/tree/path/operator/Operator.scala +++ b/src/main/scala/ahlers/tree/path/operators/Operator.scala @@ -1,7 +1,7 @@ -package ahlers.tree.path.operator +package ahlers.tree.path.operators -import ahlers.tree.path.term.Index -import ahlers.tree.path.term.Name +import ahlers.tree.path.terms.Index +import ahlers.tree.path.terms.Name sealed trait Operator object Operator { diff --git a/src/main/scala/ahlers/tree/path/parser/operator.scala b/src/main/scala/ahlers/tree/path/parsers/operator.scala similarity index 88% rename from src/main/scala/ahlers/tree/path/parser/operator.scala rename to src/main/scala/ahlers/tree/path/parsers/operator.scala index a59db26..44b12ff 100644 --- a/src/main/scala/ahlers/tree/path/parser/operator.scala +++ b/src/main/scala/ahlers/tree/path/parsers/operator.scala @@ -1,8 +1,8 @@ -package ahlers.tree.path.parser +package ahlers.tree.path.parsers -import ahlers.tree.path.operator.Operator.{ArrayIndexes, ArraySlice, BracketNotatedChildren, CurrentNode, DeepScan, DotNotatedChild, RootElement, Wildcard} -import ahlers.tree.path.parser.term.index -import ahlers.tree.path.parser.term.name +import ahlers.tree.path.operators.Operator.{ArrayIndexes, ArraySlice, BracketNotatedChildren, CurrentNode, DeepScan, DotNotatedChild, RootElement, Wildcard} +import ahlers.tree.path.parsers.term.index +import ahlers.tree.path.parsers.term.name import parsley.Parsley import parsley.Parsley.atomic import parsley.character.char diff --git a/src/main/scala/ahlers/tree/path/parser/term.scala b/src/main/scala/ahlers/tree/path/parsers/term.scala similarity index 77% rename from src/main/scala/ahlers/tree/path/parser/term.scala rename to src/main/scala/ahlers/tree/path/parsers/term.scala index a7d64f3..a16f791 100644 --- a/src/main/scala/ahlers/tree/path/parser/term.scala +++ b/src/main/scala/ahlers/tree/path/parsers/term.scala @@ -1,7 +1,7 @@ -package ahlers.tree.path.parser +package ahlers.tree.path.parsers -import ahlers.tree.path.term.Index -import ahlers.tree.path.term.Name +import ahlers.tree.path.terms.Index +import ahlers.tree.path.terms.Name import parsley.Parsley import parsley.character.satisfy import parsley.character.stringOfSome diff --git a/src/main/scala/ahlers/tree/path/term/Index.scala b/src/main/scala/ahlers/tree/path/terms/Index.scala similarity index 69% rename from src/main/scala/ahlers/tree/path/term/Index.scala rename to src/main/scala/ahlers/tree/path/terms/Index.scala index eb2758f..bae5360 100644 --- a/src/main/scala/ahlers/tree/path/term/Index.scala +++ b/src/main/scala/ahlers/tree/path/terms/Index.scala @@ -1,4 +1,4 @@ -package ahlers.tree.path.term +package ahlers.tree.path.terms case class Index(toInt: Int) { val toText: String = toInt.toString diff --git a/src/main/scala/ahlers/tree/path/term/Name.scala b/src/main/scala/ahlers/tree/path/terms/Name.scala similarity index 51% rename from src/main/scala/ahlers/tree/path/term/Name.scala rename to src/main/scala/ahlers/tree/path/terms/Name.scala index caf967c..860ed9c 100644 --- a/src/main/scala/ahlers/tree/path/term/Name.scala +++ b/src/main/scala/ahlers/tree/path/terms/Name.scala @@ -1,3 +1,3 @@ -package ahlers.tree.path.term +package ahlers.tree.path.terms case class Name(toText: String) diff --git a/src/test/scala/ahlers/tree/path/operator/diffx/instances.scala b/src/test/scala/ahlers/tree/path/operators/diffx/instances.scala similarity index 85% rename from src/test/scala/ahlers/tree/path/operator/diffx/instances.scala rename to src/test/scala/ahlers/tree/path/operators/diffx/instances.scala index 4c430d4..ab00e7d 100644 --- a/src/test/scala/ahlers/tree/path/operator/diffx/instances.scala +++ b/src/test/scala/ahlers/tree/path/operators/diffx/instances.scala @@ -1,7 +1,7 @@ -package ahlers.tree.path.operator.diffx +package ahlers.tree.path.operators.diffx -import ahlers.tree.path.operator.Operator.{ArrayIndexes, ArraySlice, BracketNotatedChildren, CurrentNode, DeepScan, DotNotatedChild, RootElement, Wildcard} -import ahlers.tree.path.term.diffx.instances._ +import ahlers.tree.path.operators.Operator.{ArrayIndexes, ArraySlice, BracketNotatedChildren, CurrentNode, DeepScan, DotNotatedChild, RootElement, Wildcard} +import ahlers.tree.path.terms.diffx.instances._ import com.softwaremill.diffx.Diff object instances { diff --git a/src/test/scala/ahlers/tree/path/operator/scalacheck/instances.scala b/src/test/scala/ahlers/tree/path/operators/scalacheck/instances.scala similarity index 75% rename from src/test/scala/ahlers/tree/path/operator/scalacheck/instances.scala rename to src/test/scala/ahlers/tree/path/operators/scalacheck/instances.scala index 1a1180e..a52a60e 100644 --- a/src/test/scala/ahlers/tree/path/operator/scalacheck/instances.scala +++ b/src/test/scala/ahlers/tree/path/operators/scalacheck/instances.scala @@ -1,15 +1,15 @@ -package ahlers.tree.path.operator.scalacheck - -import ahlers.tree.path.operator.Operator.ArrayIndexes -import ahlers.tree.path.operator.Operator.ArraySlice -import ahlers.tree.path.operator.Operator.BracketNotatedChildren -import ahlers.tree.path.operator.Operator.CurrentNode -import ahlers.tree.path.operator.Operator.DeepScan -import ahlers.tree.path.operator.Operator.DotNotatedChild -import ahlers.tree.path.operator.Operator.RootElement -import ahlers.tree.path.operator.Operator.Wildcard -import ahlers.tree.path.term.Name -import ahlers.tree.path.term.scalacheck.instances._ +package ahlers.tree.path.operators.scalacheck + +import ahlers.tree.path.operators.Operator.ArrayIndexes +import ahlers.tree.path.operators.Operator.ArraySlice +import ahlers.tree.path.operators.Operator.BracketNotatedChildren +import ahlers.tree.path.operators.Operator.CurrentNode +import ahlers.tree.path.operators.Operator.DeepScan +import ahlers.tree.path.operators.Operator.DotNotatedChild +import ahlers.tree.path.operators.Operator.RootElement +import ahlers.tree.path.operators.Operator.Wildcard +import ahlers.tree.path.terms.Name +import ahlers.tree.path.terms.scalacheck.instances._ import magnolify.scalacheck.semiauto._ import org.scalacheck.Arbitrary import org.scalacheck.Arbitrary.arbitrary diff --git a/src/test/scala/ahlers/tree/path/parser/OperatorSpec.scala b/src/test/scala/ahlers/tree/path/parsers/OperatorSpec.scala similarity index 97% rename from src/test/scala/ahlers/tree/path/parser/OperatorSpec.scala rename to src/test/scala/ahlers/tree/path/parsers/OperatorSpec.scala index 878d3b1..cab1287 100644 --- a/src/test/scala/ahlers/tree/path/parser/OperatorSpec.scala +++ b/src/test/scala/ahlers/tree/path/parsers/OperatorSpec.scala @@ -1,8 +1,8 @@ -package ahlers.tree.path.parser +package ahlers.tree.path.parsers -import ahlers.tree.path.operator.Operator._ -import ahlers.tree.path.operator.diffx.instances._ -import ahlers.tree.path.operator.scalacheck.instances._ +import ahlers.tree.path.operators.Operator._ +import ahlers.tree.path.operators.diffx.instances._ +import ahlers.tree.path.operators.scalacheck.instances._ import com.softwaremill.diffx.scalatest.DiffShouldMatcher.convertToAnyShouldMatcher import org.scalatest.matchers.should.Matchers._ import org.scalatest.wordspec.AnyWordSpec diff --git a/src/test/scala/ahlers/tree/path/parser/TermSpec.scala b/src/test/scala/ahlers/tree/path/parsers/TermSpec.scala similarity index 85% rename from src/test/scala/ahlers/tree/path/parser/TermSpec.scala rename to src/test/scala/ahlers/tree/path/parsers/TermSpec.scala index dea4402..2206215 100644 --- a/src/test/scala/ahlers/tree/path/parser/TermSpec.scala +++ b/src/test/scala/ahlers/tree/path/parsers/TermSpec.scala @@ -1,9 +1,9 @@ -package ahlers.tree.path.parser +package ahlers.tree.path.parsers -import ahlers.tree.path.term.Index -import ahlers.tree.path.term.Name -import ahlers.tree.path.term.diffx.instances._ -import ahlers.tree.path.term.scalacheck.instances._ +import ahlers.tree.path.terms.Index +import ahlers.tree.path.terms.Name +import ahlers.tree.path.terms.diffx.instances._ +import ahlers.tree.path.terms.scalacheck.instances._ import com.softwaremill.diffx.scalatest.DiffShouldMatcher.convertToAnyShouldMatcher import org.scalatest.matchers.should.Matchers._ import org.scalatest.wordspec.AnyWordSpec diff --git a/src/test/scala/ahlers/tree/path/term/diffx/instances.scala b/src/test/scala/ahlers/tree/path/terms/diffx/instances.scala similarity index 60% rename from src/test/scala/ahlers/tree/path/term/diffx/instances.scala rename to src/test/scala/ahlers/tree/path/terms/diffx/instances.scala index ab55655..8240a97 100644 --- a/src/test/scala/ahlers/tree/path/term/diffx/instances.scala +++ b/src/test/scala/ahlers/tree/path/terms/diffx/instances.scala @@ -1,7 +1,7 @@ -package ahlers.tree.path.term.diffx +package ahlers.tree.path.terms.diffx -import ahlers.tree.path.term.Index -import ahlers.tree.path.term.Name +import ahlers.tree.path.terms.Index +import ahlers.tree.path.terms.Name import com.softwaremill.diffx.Diff object instances { diff --git a/src/test/scala/ahlers/tree/path/term/scalacheck/instances.scala b/src/test/scala/ahlers/tree/path/terms/scalacheck/instances.scala similarity index 73% rename from src/test/scala/ahlers/tree/path/term/scalacheck/instances.scala rename to src/test/scala/ahlers/tree/path/terms/scalacheck/instances.scala index 8532cb0..4914b26 100644 --- a/src/test/scala/ahlers/tree/path/term/scalacheck/instances.scala +++ b/src/test/scala/ahlers/tree/path/terms/scalacheck/instances.scala @@ -1,7 +1,7 @@ -package ahlers.tree.path.term.scalacheck +package ahlers.tree.path.terms.scalacheck -import ahlers.tree.path.term.Index -import ahlers.tree.path.term.Name +import ahlers.tree.path.terms.Index +import ahlers.tree.path.terms.Name import org.scalacheck.Arbitrary import org.scalacheck.Gen From 1dc867d4c029d8b2553b8e23de6ec82b68a12635 Mon Sep 17 00:00:00 2001 From: Michael Ahlers Date: Sat, 11 May 2024 14:32:08 -0600 Subject: [PATCH 22/40] Formatting --- .../ahlers/tree/path/parsers/operator.scala | 9 ++++++++- .../tree/path/operators/diffx/instances.scala | 19 +++++++++++++------ 2 files changed, 21 insertions(+), 7 deletions(-) diff --git a/src/main/scala/ahlers/tree/path/parsers/operator.scala b/src/main/scala/ahlers/tree/path/parsers/operator.scala index 44b12ff..2dcccdb 100644 --- a/src/main/scala/ahlers/tree/path/parsers/operator.scala +++ b/src/main/scala/ahlers/tree/path/parsers/operator.scala @@ -1,6 +1,13 @@ package ahlers.tree.path.parsers -import ahlers.tree.path.operators.Operator.{ArrayIndexes, ArraySlice, BracketNotatedChildren, CurrentNode, DeepScan, DotNotatedChild, RootElement, Wildcard} +import ahlers.tree.path.operators.Operator.ArrayIndexes +import ahlers.tree.path.operators.Operator.ArraySlice +import ahlers.tree.path.operators.Operator.BracketNotatedChildren +import ahlers.tree.path.operators.Operator.CurrentNode +import ahlers.tree.path.operators.Operator.DeepScan +import ahlers.tree.path.operators.Operator.DotNotatedChild +import ahlers.tree.path.operators.Operator.RootElement +import ahlers.tree.path.operators.Operator.Wildcard import ahlers.tree.path.parsers.term.index import ahlers.tree.path.parsers.term.name import parsley.Parsley diff --git a/src/test/scala/ahlers/tree/path/operators/diffx/instances.scala b/src/test/scala/ahlers/tree/path/operators/diffx/instances.scala index ab00e7d..9459a9a 100644 --- a/src/test/scala/ahlers/tree/path/operators/diffx/instances.scala +++ b/src/test/scala/ahlers/tree/path/operators/diffx/instances.scala @@ -1,6 +1,13 @@ package ahlers.tree.path.operators.diffx -import ahlers.tree.path.operators.Operator.{ArrayIndexes, ArraySlice, BracketNotatedChildren, CurrentNode, DeepScan, DotNotatedChild, RootElement, Wildcard} +import ahlers.tree.path.operators.Operator.ArrayIndexes +import ahlers.tree.path.operators.Operator.ArraySlice +import ahlers.tree.path.operators.Operator.BracketNotatedChildren +import ahlers.tree.path.operators.Operator.CurrentNode +import ahlers.tree.path.operators.Operator.DeepScan +import ahlers.tree.path.operators.Operator.DotNotatedChild +import ahlers.tree.path.operators.Operator.RootElement +import ahlers.tree.path.operators.Operator.Wildcard import ahlers.tree.path.terms.diffx.instances._ import com.softwaremill.diffx.Diff @@ -24,11 +31,11 @@ object instances { Diff.derived } - implicit val diffArrayIndexes: Diff[ArrayIndexes] = Diff.derived + implicit val diffArrayIndexes: Diff[ArrayIndexes] = Diff.derived - implicit val diffArraySliceLeftBounded: Diff[ArraySlice.LeftBounded] = Diff.derived - implicit val diffArraySliceRightBounded: Diff[ArraySlice.RightBounded] = Diff.derived - implicit val diffArraySliceBounded: Diff[ArraySlice.Bounded] = Diff.derived - implicit val diffArraySlice: Diff[ArraySlice] = Diff.derived + implicit val diffArraySliceLeftBounded: Diff[ArraySlice.LeftBounded] = Diff.derived + implicit val diffArraySliceRightBounded: Diff[ArraySlice.RightBounded] = Diff.derived + implicit val diffArraySliceBounded: Diff[ArraySlice.Bounded] = Diff.derived + implicit val diffArraySlice: Diff[ArraySlice] = Diff.derived } From 1f99cfa562c6ad84982b9252c54e002a7bcba271 Mon Sep 17 00:00:00 2001 From: Michael Ahlers Date: Sat, 11 May 2024 14:37:49 -0600 Subject: [PATCH 23/40] Split out from companion object --- .../ahlers/tree/path/operators/Operator.scala | 99 +++++++++---------- .../ahlers/tree/path/parsers/operator.scala | 16 +-- .../tree/path/operators/diffx/instances.scala | 16 +-- .../path/operators/scalacheck/instances.scala | 16 +-- .../tree/path/parsers/OperatorSpec.scala | 2 +- 5 files changed, 72 insertions(+), 77 deletions(-) diff --git a/src/main/scala/ahlers/tree/path/operators/Operator.scala b/src/main/scala/ahlers/tree/path/operators/Operator.scala index ef1cc5f..f17a3bf 100644 --- a/src/main/scala/ahlers/tree/path/operators/Operator.scala +++ b/src/main/scala/ahlers/tree/path/operators/Operator.scala @@ -3,75 +3,70 @@ package ahlers.tree.path.operators import ahlers.tree.path.terms.Index import ahlers.tree.path.terms.Name -sealed trait Operator -object Operator { +trait Operator { + def toText: String +} - case object RootElement extends Operator { - val toText: String = "$" - } +case object RootElement extends Operator { + override val toText: String = "$" +} - case object CurrentNode extends Operator { - val toText: String = "@" - } +case object CurrentNode extends Operator { + override val toText: String = "@" +} + +case object Wildcard extends Operator { + override val toText: String = "*" +} + +case object DeepScan extends Operator { + override val toText: String = ".." +} - case object Wildcard { - val toText: String = "*" +sealed trait DotNotatedChild extends Operator +object DotNotatedChild { + case class MatchingName(toName: Name) extends DotNotatedChild { + val toText: String = s".${toName.toText}" } - case object DeepScan extends Operator { - val toText: String = ".." + case object MatchingWildcard extends DotNotatedChild { + val toWildcard: Wildcard.type = Wildcard + val toText: String = s".${toWildcard.toText}" } +} - sealed trait DotNotatedChild extends Operator { +case class BracketNotatedChildren(toNames: Seq[BracketNotatedChildren.Child]) extends Operator { + override val toText: String = toNames.map(_.toText).mkString("[", ",", "]") +} +object BracketNotatedChildren { + sealed trait Child { def toText: String } - object DotNotatedChild { - case class MatchingName(toName: Name) extends DotNotatedChild { - override val toText: String = s".${toName.toText}" + object Child { + case class MatchingName(toName: Name) extends Child { + val toText: String = s"'${toName.toText}'" } - case object MatchingWildcard extends DotNotatedChild { + case object MatchingWildcard extends Child { val toWildcard: Wildcard.type = Wildcard - override val toText: String = s".${toWildcard.toText}" + val toText: String = s"${toWildcard.toText}" } } +} - case class BracketNotatedChildren(toNames: Seq[BracketNotatedChildren.Child]) { - val toText: String = toNames.map(_.toText).mkString("[", ",", "]") - } - object BracketNotatedChildren { - sealed trait Child { - def toText: String - } - object Child { - case class MatchingName(toName: Name) extends Child { - override val toText: String = s"'${toName.toText}'" - } - - case object MatchingWildcard extends Child { - val toWildcard: Wildcard.type = Wildcard - override val toText: String = s"${toWildcard.toText}" - } - } - } +case class ArrayIndexes(toIndexes: Seq[Index]) extends Operator { + override val toText: String = toIndexes.map(_.toText).mkString("[", ",", "]") +} - case class ArrayIndexes(toIndexes: Seq[Index]) { - val toText: String = toIndexes.map(_.toText).mkString("[", ",", "]") +sealed trait ArraySlice extends Operator +object ArraySlice { + case class LeftBounded(start: Index) extends ArraySlice { + val toText: String = s"[${start.toText}:]" } - - sealed trait ArraySlice extends Operator { - def toText: String + case class RightBounded(end: Index) extends ArraySlice { + val toText: String = s"[:${end.toText}]" } - object ArraySlice { - case class LeftBounded(start: Index) extends ArraySlice { - override val toText: String = s"[${start.toText}:]" - } - case class RightBounded(end: Index) extends ArraySlice { - override val toText: String = s"[:${end.toText}]" - } - case class Bounded(start: Index, end: Index) extends ArraySlice { - override val toText: String = s"[${start.toText}:${end.toText}]" - } + case class Bounded(start: Index, end: Index) extends ArraySlice { + val toText: String = s"[${start.toText}:${end.toText}]" } - } diff --git a/src/main/scala/ahlers/tree/path/parsers/operator.scala b/src/main/scala/ahlers/tree/path/parsers/operator.scala index 2dcccdb..45f8d06 100644 --- a/src/main/scala/ahlers/tree/path/parsers/operator.scala +++ b/src/main/scala/ahlers/tree/path/parsers/operator.scala @@ -1,13 +1,13 @@ package ahlers.tree.path.parsers -import ahlers.tree.path.operators.Operator.ArrayIndexes -import ahlers.tree.path.operators.Operator.ArraySlice -import ahlers.tree.path.operators.Operator.BracketNotatedChildren -import ahlers.tree.path.operators.Operator.CurrentNode -import ahlers.tree.path.operators.Operator.DeepScan -import ahlers.tree.path.operators.Operator.DotNotatedChild -import ahlers.tree.path.operators.Operator.RootElement -import ahlers.tree.path.operators.Operator.Wildcard +import ahlers.tree.path.operators.ArrayIndexes +import ahlers.tree.path.operators.ArraySlice +import ahlers.tree.path.operators.BracketNotatedChildren +import ahlers.tree.path.operators.CurrentNode +import ahlers.tree.path.operators.DeepScan +import ahlers.tree.path.operators.DotNotatedChild +import ahlers.tree.path.operators.RootElement +import ahlers.tree.path.operators.Wildcard import ahlers.tree.path.parsers.term.index import ahlers.tree.path.parsers.term.name import parsley.Parsley diff --git a/src/test/scala/ahlers/tree/path/operators/diffx/instances.scala b/src/test/scala/ahlers/tree/path/operators/diffx/instances.scala index 9459a9a..61cd56f 100644 --- a/src/test/scala/ahlers/tree/path/operators/diffx/instances.scala +++ b/src/test/scala/ahlers/tree/path/operators/diffx/instances.scala @@ -1,13 +1,13 @@ package ahlers.tree.path.operators.diffx -import ahlers.tree.path.operators.Operator.ArrayIndexes -import ahlers.tree.path.operators.Operator.ArraySlice -import ahlers.tree.path.operators.Operator.BracketNotatedChildren -import ahlers.tree.path.operators.Operator.CurrentNode -import ahlers.tree.path.operators.Operator.DeepScan -import ahlers.tree.path.operators.Operator.DotNotatedChild -import ahlers.tree.path.operators.Operator.RootElement -import ahlers.tree.path.operators.Operator.Wildcard +import ahlers.tree.path.operators.ArrayIndexes +import ahlers.tree.path.operators.ArraySlice +import ahlers.tree.path.operators.BracketNotatedChildren +import ahlers.tree.path.operators.CurrentNode +import ahlers.tree.path.operators.DeepScan +import ahlers.tree.path.operators.DotNotatedChild +import ahlers.tree.path.operators.RootElement +import ahlers.tree.path.operators.Wildcard import ahlers.tree.path.terms.diffx.instances._ import com.softwaremill.diffx.Diff diff --git a/src/test/scala/ahlers/tree/path/operators/scalacheck/instances.scala b/src/test/scala/ahlers/tree/path/operators/scalacheck/instances.scala index a52a60e..b8994b5 100644 --- a/src/test/scala/ahlers/tree/path/operators/scalacheck/instances.scala +++ b/src/test/scala/ahlers/tree/path/operators/scalacheck/instances.scala @@ -1,13 +1,13 @@ package ahlers.tree.path.operators.scalacheck -import ahlers.tree.path.operators.Operator.ArrayIndexes -import ahlers.tree.path.operators.Operator.ArraySlice -import ahlers.tree.path.operators.Operator.BracketNotatedChildren -import ahlers.tree.path.operators.Operator.CurrentNode -import ahlers.tree.path.operators.Operator.DeepScan -import ahlers.tree.path.operators.Operator.DotNotatedChild -import ahlers.tree.path.operators.Operator.RootElement -import ahlers.tree.path.operators.Operator.Wildcard +import ahlers.tree.path.operators.ArrayIndexes +import ahlers.tree.path.operators.ArraySlice +import ahlers.tree.path.operators.BracketNotatedChildren +import ahlers.tree.path.operators.CurrentNode +import ahlers.tree.path.operators.DeepScan +import ahlers.tree.path.operators.DotNotatedChild +import ahlers.tree.path.operators.RootElement +import ahlers.tree.path.operators.Wildcard import ahlers.tree.path.terms.Name import ahlers.tree.path.terms.scalacheck.instances._ import magnolify.scalacheck.semiauto._ diff --git a/src/test/scala/ahlers/tree/path/parsers/OperatorSpec.scala b/src/test/scala/ahlers/tree/path/parsers/OperatorSpec.scala index cab1287..a8bb210 100644 --- a/src/test/scala/ahlers/tree/path/parsers/OperatorSpec.scala +++ b/src/test/scala/ahlers/tree/path/parsers/OperatorSpec.scala @@ -1,6 +1,6 @@ package ahlers.tree.path.parsers -import ahlers.tree.path.operators.Operator._ +import ahlers.tree.path.operators._ import ahlers.tree.path.operators.diffx.instances._ import ahlers.tree.path.operators.scalacheck.instances._ import com.softwaremill.diffx.scalatest.DiffShouldMatcher.convertToAnyShouldMatcher From 8a8dd340956c795537d52740f52241a8306ddffa Mon Sep 17 00:00:00 2001 From: Michael Ahlers Date: Sat, 11 May 2024 14:49:56 -0600 Subject: [PATCH 24/40] Split out and unseal --- .../tree/path/operators/ArrayIndexes.scala | 7 ++ .../tree/path/operators/ArraySlice.scala | 17 +++++ .../operators/BracketNotatedChildren.scala | 23 +++++++ .../tree/path/operators/CurrentNode.scala | 5 ++ .../ahlers/tree/path/operators/DeepScan.scala | 5 ++ .../tree/path/operators/DotNotatedChild.scala | 16 +++++ .../ahlers/tree/path/operators/Operator.scala | 67 ------------------- .../tree/path/operators/RootElement.scala | 5 ++ .../ahlers/tree/path/operators/Wildcard.scala | 5 ++ .../scala/ahlers/tree/path/terms/Index.scala | 4 +- .../scala/ahlers/tree/path/terms/Name.scala | 2 +- .../scala/ahlers/tree/path/terms/Term.scala | 5 ++ .../tree/path/operators/diffx/instances.scala | 3 + 13 files changed, 94 insertions(+), 70 deletions(-) create mode 100644 src/main/scala/ahlers/tree/path/operators/ArrayIndexes.scala create mode 100644 src/main/scala/ahlers/tree/path/operators/ArraySlice.scala create mode 100644 src/main/scala/ahlers/tree/path/operators/BracketNotatedChildren.scala create mode 100644 src/main/scala/ahlers/tree/path/operators/CurrentNode.scala create mode 100644 src/main/scala/ahlers/tree/path/operators/DeepScan.scala create mode 100644 src/main/scala/ahlers/tree/path/operators/DotNotatedChild.scala create mode 100644 src/main/scala/ahlers/tree/path/operators/RootElement.scala create mode 100644 src/main/scala/ahlers/tree/path/operators/Wildcard.scala create mode 100644 src/main/scala/ahlers/tree/path/terms/Term.scala diff --git a/src/main/scala/ahlers/tree/path/operators/ArrayIndexes.scala b/src/main/scala/ahlers/tree/path/operators/ArrayIndexes.scala new file mode 100644 index 0000000..b096a34 --- /dev/null +++ b/src/main/scala/ahlers/tree/path/operators/ArrayIndexes.scala @@ -0,0 +1,7 @@ +package ahlers.tree.path.operators + +import ahlers.tree.path.terms.Index + +case class ArrayIndexes(toIndexes: Seq[Index]) extends Operator { + override val toText: String = toIndexes.map(_.toText).mkString("[", ",", "]") +} diff --git a/src/main/scala/ahlers/tree/path/operators/ArraySlice.scala b/src/main/scala/ahlers/tree/path/operators/ArraySlice.scala new file mode 100644 index 0000000..afb46ba --- /dev/null +++ b/src/main/scala/ahlers/tree/path/operators/ArraySlice.scala @@ -0,0 +1,17 @@ +package ahlers.tree.path.operators + +import ahlers.tree.path.terms.Index + +sealed trait ArraySlice extends Operator + +object ArraySlice { + case class LeftBounded(start: Index) extends ArraySlice { + val toText: String = s"[${start.toText}:]" + } + case class RightBounded(end: Index) extends ArraySlice { + val toText: String = s"[:${end.toText}]" + } + case class Bounded(start: Index, end: Index) extends ArraySlice { + val toText: String = s"[${start.toText}:${end.toText}]" + } +} diff --git a/src/main/scala/ahlers/tree/path/operators/BracketNotatedChildren.scala b/src/main/scala/ahlers/tree/path/operators/BracketNotatedChildren.scala new file mode 100644 index 0000000..7cfbecf --- /dev/null +++ b/src/main/scala/ahlers/tree/path/operators/BracketNotatedChildren.scala @@ -0,0 +1,23 @@ +package ahlers.tree.path.operators + +import ahlers.tree.path.terms.Name + +case class BracketNotatedChildren(toNames: Seq[BracketNotatedChildren.Child]) extends Operator { + override val toText: String = toNames.map(_.toText).mkString("[", ",", "]") +} + +object BracketNotatedChildren { + sealed trait Child { + def toText: String + } + object Child { + case class MatchingName(toName: Name) extends Child { + val toText: String = s"'${toName.toText}'" + } + + case object MatchingWildcard extends Child { + val toWildcard: Wildcard.type = Wildcard + val toText: String = s"${toWildcard.toText}" + } + } +} diff --git a/src/main/scala/ahlers/tree/path/operators/CurrentNode.scala b/src/main/scala/ahlers/tree/path/operators/CurrentNode.scala new file mode 100644 index 0000000..4146d91 --- /dev/null +++ b/src/main/scala/ahlers/tree/path/operators/CurrentNode.scala @@ -0,0 +1,5 @@ +package ahlers.tree.path.operators + +case object CurrentNode extends Operator { + override val toText: String = "@" +} diff --git a/src/main/scala/ahlers/tree/path/operators/DeepScan.scala b/src/main/scala/ahlers/tree/path/operators/DeepScan.scala new file mode 100644 index 0000000..50d3e4a --- /dev/null +++ b/src/main/scala/ahlers/tree/path/operators/DeepScan.scala @@ -0,0 +1,5 @@ +package ahlers.tree.path.operators + +case object DeepScan extends Operator { + override val toText: String = ".." +} diff --git a/src/main/scala/ahlers/tree/path/operators/DotNotatedChild.scala b/src/main/scala/ahlers/tree/path/operators/DotNotatedChild.scala new file mode 100644 index 0000000..c3da5a4 --- /dev/null +++ b/src/main/scala/ahlers/tree/path/operators/DotNotatedChild.scala @@ -0,0 +1,16 @@ +package ahlers.tree.path.operators + +import ahlers.tree.path.terms.Name + +sealed trait DotNotatedChild extends Operator + +object DotNotatedChild { + case class MatchingName(toName: Name) extends DotNotatedChild { + val toText: String = s".${toName.toText}" + } + + case object MatchingWildcard extends DotNotatedChild { + val toWildcard: Wildcard.type = Wildcard + val toText: String = s".${toWildcard.toText}" + } +} diff --git a/src/main/scala/ahlers/tree/path/operators/Operator.scala b/src/main/scala/ahlers/tree/path/operators/Operator.scala index f17a3bf..0ed856a 100644 --- a/src/main/scala/ahlers/tree/path/operators/Operator.scala +++ b/src/main/scala/ahlers/tree/path/operators/Operator.scala @@ -1,72 +1,5 @@ package ahlers.tree.path.operators -import ahlers.tree.path.terms.Index -import ahlers.tree.path.terms.Name - trait Operator { def toText: String } - -case object RootElement extends Operator { - override val toText: String = "$" -} - -case object CurrentNode extends Operator { - override val toText: String = "@" -} - -case object Wildcard extends Operator { - override val toText: String = "*" -} - -case object DeepScan extends Operator { - override val toText: String = ".." -} - -sealed trait DotNotatedChild extends Operator -object DotNotatedChild { - case class MatchingName(toName: Name) extends DotNotatedChild { - val toText: String = s".${toName.toText}" - } - - case object MatchingWildcard extends DotNotatedChild { - val toWildcard: Wildcard.type = Wildcard - val toText: String = s".${toWildcard.toText}" - } -} - -case class BracketNotatedChildren(toNames: Seq[BracketNotatedChildren.Child]) extends Operator { - override val toText: String = toNames.map(_.toText).mkString("[", ",", "]") -} -object BracketNotatedChildren { - sealed trait Child { - def toText: String - } - object Child { - case class MatchingName(toName: Name) extends Child { - val toText: String = s"'${toName.toText}'" - } - - case object MatchingWildcard extends Child { - val toWildcard: Wildcard.type = Wildcard - val toText: String = s"${toWildcard.toText}" - } - } -} - -case class ArrayIndexes(toIndexes: Seq[Index]) extends Operator { - override val toText: String = toIndexes.map(_.toText).mkString("[", ",", "]") -} - -sealed trait ArraySlice extends Operator -object ArraySlice { - case class LeftBounded(start: Index) extends ArraySlice { - val toText: String = s"[${start.toText}:]" - } - case class RightBounded(end: Index) extends ArraySlice { - val toText: String = s"[:${end.toText}]" - } - case class Bounded(start: Index, end: Index) extends ArraySlice { - val toText: String = s"[${start.toText}:${end.toText}]" - } -} diff --git a/src/main/scala/ahlers/tree/path/operators/RootElement.scala b/src/main/scala/ahlers/tree/path/operators/RootElement.scala new file mode 100644 index 0000000..49fa8e6 --- /dev/null +++ b/src/main/scala/ahlers/tree/path/operators/RootElement.scala @@ -0,0 +1,5 @@ +package ahlers.tree.path.operators + +case object RootElement extends Operator { + override val toText: String = "$" +} diff --git a/src/main/scala/ahlers/tree/path/operators/Wildcard.scala b/src/main/scala/ahlers/tree/path/operators/Wildcard.scala new file mode 100644 index 0000000..93f30a7 --- /dev/null +++ b/src/main/scala/ahlers/tree/path/operators/Wildcard.scala @@ -0,0 +1,5 @@ +package ahlers.tree.path.operators + +case object Wildcard extends Operator { + override val toText: String = "*" +} diff --git a/src/main/scala/ahlers/tree/path/terms/Index.scala b/src/main/scala/ahlers/tree/path/terms/Index.scala index bae5360..62ed3a2 100644 --- a/src/main/scala/ahlers/tree/path/terms/Index.scala +++ b/src/main/scala/ahlers/tree/path/terms/Index.scala @@ -1,5 +1,5 @@ package ahlers.tree.path.terms -case class Index(toInt: Int) { - val toText: String = toInt.toString +case class Index(toInt: Int) extends Term { + override val toText: String = toInt.toString } diff --git a/src/main/scala/ahlers/tree/path/terms/Name.scala b/src/main/scala/ahlers/tree/path/terms/Name.scala index 860ed9c..7a9ebc6 100644 --- a/src/main/scala/ahlers/tree/path/terms/Name.scala +++ b/src/main/scala/ahlers/tree/path/terms/Name.scala @@ -1,3 +1,3 @@ package ahlers.tree.path.terms -case class Name(toText: String) +case class Name(override val toText: String) extends Term diff --git a/src/main/scala/ahlers/tree/path/terms/Term.scala b/src/main/scala/ahlers/tree/path/terms/Term.scala new file mode 100644 index 0000000..aa70312 --- /dev/null +++ b/src/main/scala/ahlers/tree/path/terms/Term.scala @@ -0,0 +1,5 @@ +package ahlers.tree.path.terms + +trait Term { + def toText: String +} diff --git a/src/test/scala/ahlers/tree/path/operators/diffx/instances.scala b/src/test/scala/ahlers/tree/path/operators/diffx/instances.scala index 61cd56f..7e235e6 100644 --- a/src/test/scala/ahlers/tree/path/operators/diffx/instances.scala +++ b/src/test/scala/ahlers/tree/path/operators/diffx/instances.scala @@ -6,6 +6,7 @@ import ahlers.tree.path.operators.BracketNotatedChildren import ahlers.tree.path.operators.CurrentNode import ahlers.tree.path.operators.DeepScan import ahlers.tree.path.operators.DotNotatedChild +import ahlers.tree.path.operators.Operator import ahlers.tree.path.operators.RootElement import ahlers.tree.path.operators.Wildcard import ahlers.tree.path.terms.diffx.instances._ @@ -38,4 +39,6 @@ object instances { implicit val diffArraySliceBounded: Diff[ArraySlice.Bounded] = Diff.derived implicit val diffArraySlice: Diff[ArraySlice] = Diff.derived + // implicit val diffOperator: Diff[Operator] = Diff.derived + } From 8fc392c6cdf14351ecfae16a1ec1033b59fc85d5 Mon Sep 17 00:00:00 2001 From: Michael Ahlers Date: Sat, 11 May 2024 15:09:59 -0600 Subject: [PATCH 25/40] Support any operator parser --- .../ahlers/tree/path/parsers/operator.scala | 15 ++++++++-- .../ahlers/tree/path/operators/algebra.scala | 30 +++++++++++++++++++ .../tree/path/operators/diffx/instances.scala | 11 ++++++- .../path/operators/scalacheck/instances.scala | 13 ++++++++ .../tree/path/parsers/OperatorSpec.scala | 10 +++++++ 5 files changed, 76 insertions(+), 3 deletions(-) create mode 100644 src/test/scala/ahlers/tree/path/operators/algebra.scala diff --git a/src/main/scala/ahlers/tree/path/parsers/operator.scala b/src/main/scala/ahlers/tree/path/parsers/operator.scala index 45f8d06..a1aba21 100644 --- a/src/main/scala/ahlers/tree/path/parsers/operator.scala +++ b/src/main/scala/ahlers/tree/path/parsers/operator.scala @@ -6,6 +6,7 @@ import ahlers.tree.path.operators.BracketNotatedChildren import ahlers.tree.path.operators.CurrentNode import ahlers.tree.path.operators.DeepScan import ahlers.tree.path.operators.DotNotatedChild +import ahlers.tree.path.operators.Operator import ahlers.tree.path.operators.RootElement import ahlers.tree.path.operators.Wildcard import ahlers.tree.path.parsers.term.index @@ -19,10 +20,10 @@ import parsley.combinator.sepBy object operator { - val currentNode: Parsley[CurrentNode.type] = char('@').as(CurrentNode) - val rootElement: Parsley[RootElement.type] = char('$').as(RootElement) + val currentNode: Parsley[CurrentNode.type] = char('@').as(CurrentNode) + val wildcard: Parsley[Wildcard.type] = char('*').as(Wildcard) val deepScan: Parsley[DeepScan.type] = string("..").as(DeepScan) @@ -45,4 +46,14 @@ object operator { val arraySliceBounded: Parsley[ArraySlice.Bounded] = (char('[') *> index <~> char(':') *> index <~ char(']')).map((ArraySlice.Bounded(_, _)).tupled) val arraySlice: Parsley[ArraySlice] = atomic(arraySliceLeftBounded) | atomic(arraySliceRightBounded) | atomic(arraySliceBounded) + val any: Parsley[Operator] = + atomic(rootElement) | + atomic(currentNode) | + atomic(wildcard) | + atomic(deepScan) | + atomic(dotNotatedChild) | + atomic(bracketNotatedChildren) | + atomic(arrayIndexes) | + atomic(arraySlice) + } diff --git a/src/test/scala/ahlers/tree/path/operators/algebra.scala b/src/test/scala/ahlers/tree/path/operators/algebra.scala new file mode 100644 index 0000000..5610dd4 --- /dev/null +++ b/src/test/scala/ahlers/tree/path/operators/algebra.scala @@ -0,0 +1,30 @@ +package ahlers.tree.path.operators + +private object algebra { + + sealed abstract class IsOperator private (val toOperator: Operator) + object IsOperator { + case object OfRootElement extends IsOperator(RootElement) + case object OfCurrentNode extends IsOperator(CurrentNode) + case object OfWildcard extends IsOperator(Wildcard) + case object OfDeepScan extends IsOperator(DeepScan) + case class OfDotNotation(override val toOperator: DotNotatedChild) extends IsOperator(toOperator) + case class OfBracketNotation(override val toOperator: BracketNotatedChildren) extends IsOperator(toOperator) + case class OfArrayIndexes(override val toOperator: ArrayIndexes) extends IsOperator(toOperator) + case class OfArraySlice(override val toOperator: ArraySlice) extends IsOperator(toOperator) + case class OfUnknown(override val toOperator: Operator) extends IsOperator(toOperator) + + def apply(operator: Operator): IsOperator = operator match { + case RootElement => OfRootElement + case CurrentNode => OfCurrentNode + case Wildcard => OfWildcard + case DeepScan => OfDeepScan + case dotNotatedChild: DotNotatedChild => OfDotNotation(dotNotatedChild) + case bracketNotatedChildren: BracketNotatedChildren => OfBracketNotation(bracketNotatedChildren) + case arrayIndexes: ArrayIndexes => OfArrayIndexes(arrayIndexes) + case arraySlice: ArraySlice => OfArraySlice(arraySlice) + case operator => OfUnknown(operator) + } + } + +} diff --git a/src/test/scala/ahlers/tree/path/operators/diffx/instances.scala b/src/test/scala/ahlers/tree/path/operators/diffx/instances.scala index 7e235e6..ac3da8a 100644 --- a/src/test/scala/ahlers/tree/path/operators/diffx/instances.scala +++ b/src/test/scala/ahlers/tree/path/operators/diffx/instances.scala @@ -9,6 +9,7 @@ import ahlers.tree.path.operators.DotNotatedChild import ahlers.tree.path.operators.Operator import ahlers.tree.path.operators.RootElement import ahlers.tree.path.operators.Wildcard +import ahlers.tree.path.operators.algebra.IsOperator import ahlers.tree.path.terms.diffx.instances._ import com.softwaremill.diffx.Diff @@ -39,6 +40,14 @@ object instances { implicit val diffArraySliceBounded: Diff[ArraySlice.Bounded] = Diff.derived implicit val diffArraySlice: Diff[ArraySlice] = Diff.derived - // implicit val diffOperator: Diff[Operator] = Diff.derived + implicit val diffOperator: Diff[Operator] = { + implicit val diffIsOperator: Diff[IsOperator] = { + import IsOperator.OfUnknown + implicit val diffOfUnknown: Diff[OfUnknown] = Diff.useEquals + Diff.derived + } + + Diff[IsOperator].contramap(IsOperator(_)) + } } diff --git a/src/test/scala/ahlers/tree/path/operators/scalacheck/instances.scala b/src/test/scala/ahlers/tree/path/operators/scalacheck/instances.scala index b8994b5..3183bb4 100644 --- a/src/test/scala/ahlers/tree/path/operators/scalacheck/instances.scala +++ b/src/test/scala/ahlers/tree/path/operators/scalacheck/instances.scala @@ -6,8 +6,10 @@ import ahlers.tree.path.operators.BracketNotatedChildren import ahlers.tree.path.operators.CurrentNode import ahlers.tree.path.operators.DeepScan import ahlers.tree.path.operators.DotNotatedChild +import ahlers.tree.path.operators.Operator import ahlers.tree.path.operators.RootElement import ahlers.tree.path.operators.Wildcard +import ahlers.tree.path.operators.algebra.IsOperator import ahlers.tree.path.terms.Name import ahlers.tree.path.terms.scalacheck.instances._ import magnolify.scalacheck.semiauto._ @@ -42,4 +44,15 @@ object instances { implicit val arbArraySliceBounded: Arbitrary[ArraySlice.Bounded] = ArbitraryDerivation[ArraySlice.Bounded] implicit val arbArraySlice: Arbitrary[ArraySlice] = ArbitraryDerivation[ArraySlice] + implicit val arbOperator: Arbitrary[Operator] = Arbitrary(Gen.oneOf( + arbitrary[CurrentNode.type], + arbitrary[RootElement.type], + arbitrary[Wildcard.type], + arbitrary[DeepScan.type], + arbitrary[DotNotatedChild], + arbitrary[BracketNotatedChildren], + arbitrary[ArrayIndexes], + arbitrary[ArraySlice], + )) + } diff --git a/src/test/scala/ahlers/tree/path/parsers/OperatorSpec.scala b/src/test/scala/ahlers/tree/path/parsers/OperatorSpec.scala index a8bb210..6f1847f 100644 --- a/src/test/scala/ahlers/tree/path/parsers/OperatorSpec.scala +++ b/src/test/scala/ahlers/tree/path/parsers/OperatorSpec.scala @@ -250,4 +250,14 @@ class OperatorSpec extends AnyWordSpec { } } + "Operator" should { + val parser = operator.any + + """accept any""" in { + forAll { operator: Operator => + parser.parse(operator.toText).shouldMatchTo(Success(operator)) + } + } + } + } From d7a6bdb453662061d2bf84810f159927f8769c5b Mon Sep 17 00:00:00 2001 From: Michael Ahlers Date: Sat, 11 May 2024 15:10:32 -0600 Subject: [PATCH 26/40] There's no commonality between index and name --- src/main/scala/ahlers/tree/path/terms/Index.scala | 4 ++-- src/main/scala/ahlers/tree/path/terms/Name.scala | 2 +- src/main/scala/ahlers/tree/path/terms/Term.scala | 5 ----- 3 files changed, 3 insertions(+), 8 deletions(-) delete mode 100644 src/main/scala/ahlers/tree/path/terms/Term.scala diff --git a/src/main/scala/ahlers/tree/path/terms/Index.scala b/src/main/scala/ahlers/tree/path/terms/Index.scala index 62ed3a2..bae5360 100644 --- a/src/main/scala/ahlers/tree/path/terms/Index.scala +++ b/src/main/scala/ahlers/tree/path/terms/Index.scala @@ -1,5 +1,5 @@ package ahlers.tree.path.terms -case class Index(toInt: Int) extends Term { - override val toText: String = toInt.toString +case class Index(toInt: Int) { + val toText: String = toInt.toString } diff --git a/src/main/scala/ahlers/tree/path/terms/Name.scala b/src/main/scala/ahlers/tree/path/terms/Name.scala index 7a9ebc6..860ed9c 100644 --- a/src/main/scala/ahlers/tree/path/terms/Name.scala +++ b/src/main/scala/ahlers/tree/path/terms/Name.scala @@ -1,3 +1,3 @@ package ahlers.tree.path.terms -case class Name(override val toText: String) extends Term +case class Name(toText: String) diff --git a/src/main/scala/ahlers/tree/path/terms/Term.scala b/src/main/scala/ahlers/tree/path/terms/Term.scala deleted file mode 100644 index aa70312..0000000 --- a/src/main/scala/ahlers/tree/path/terms/Term.scala +++ /dev/null @@ -1,5 +0,0 @@ -package ahlers.tree.path.terms - -trait Term { - def toText: String -} From adc9acbcb1cb9fbb79f562d0a95780647361605f Mon Sep 17 00:00:00 2001 From: Michael Ahlers Date: Sat, 11 May 2024 15:29:06 -0600 Subject: [PATCH 27/40] Sketch expressions --- .../tree/path/expressions/Expression.scala | 5 ++++ .../tree/path/expressions/Logical.scala | 5 ++++ .../tree/path/expressions/Selector.scala | 7 +++++ .../ahlers/tree/path/parsers/expression.scala | 10 +++++++ .../tree/path/expressions/algebra.scala | 18 +++++++++++++ .../path/expressions/diffx/instances.scala | 26 +++++++++++++++++++ .../expressions/scalacheck/instances.scala | 22 ++++++++++++++++ .../ahlers/tree/path/operators/algebra.scala | 18 ++++++------- 8 files changed, 102 insertions(+), 9 deletions(-) create mode 100644 src/main/scala/ahlers/tree/path/expressions/Expression.scala create mode 100644 src/main/scala/ahlers/tree/path/expressions/Logical.scala create mode 100644 src/main/scala/ahlers/tree/path/expressions/Selector.scala create mode 100644 src/main/scala/ahlers/tree/path/parsers/expression.scala create mode 100644 src/test/scala/ahlers/tree/path/expressions/algebra.scala create mode 100644 src/test/scala/ahlers/tree/path/expressions/diffx/instances.scala create mode 100644 src/test/scala/ahlers/tree/path/expressions/scalacheck/instances.scala diff --git a/src/main/scala/ahlers/tree/path/expressions/Expression.scala b/src/main/scala/ahlers/tree/path/expressions/Expression.scala new file mode 100644 index 0000000..61dc524 --- /dev/null +++ b/src/main/scala/ahlers/tree/path/expressions/Expression.scala @@ -0,0 +1,5 @@ +package ahlers.tree.path.expressions + +trait Expression { + def toText: String +} diff --git a/src/main/scala/ahlers/tree/path/expressions/Logical.scala b/src/main/scala/ahlers/tree/path/expressions/Logical.scala new file mode 100644 index 0000000..31785b7 --- /dev/null +++ b/src/main/scala/ahlers/tree/path/expressions/Logical.scala @@ -0,0 +1,5 @@ +package ahlers.tree.path.expressions + +case class Logical() extends Expression { + override val toText: String = "Logical" +} diff --git a/src/main/scala/ahlers/tree/path/expressions/Selector.scala b/src/main/scala/ahlers/tree/path/expressions/Selector.scala new file mode 100644 index 0000000..eaeac3e --- /dev/null +++ b/src/main/scala/ahlers/tree/path/expressions/Selector.scala @@ -0,0 +1,7 @@ +package ahlers.tree.path.expressions + +import ahlers.tree.path.operators.Operator + +case class Selector(toOperators: Seq[Operator]) extends Expression { + val toText: String = toOperators.map(_.toText).mkString +} diff --git a/src/main/scala/ahlers/tree/path/parsers/expression.scala b/src/main/scala/ahlers/tree/path/parsers/expression.scala new file mode 100644 index 0000000..2d6e684 --- /dev/null +++ b/src/main/scala/ahlers/tree/path/parsers/expression.scala @@ -0,0 +1,10 @@ +package ahlers.tree.path.parsers + +import ahlers.tree.path.expressions.Selector +import parsley.Parsley + +object expression { + + // val selector:Parsley[Selector] = + +} diff --git a/src/test/scala/ahlers/tree/path/expressions/algebra.scala b/src/test/scala/ahlers/tree/path/expressions/algebra.scala new file mode 100644 index 0000000..0b7a6b4 --- /dev/null +++ b/src/test/scala/ahlers/tree/path/expressions/algebra.scala @@ -0,0 +1,18 @@ +package ahlers.tree.path.expressions + +private object algebra { + + sealed abstract class IsExpression private (val toExpression: Expression) + object IsExpression { + case class OfSelector(override val toExpression: Selector) extends IsExpression(toExpression) + case class OfLogical(override val toExpression: Logical) extends IsExpression(toExpression) + case class OfUnknown(override val toExpression: Expression) extends IsExpression(toExpression) + + def apply(expression: Expression): IsExpression = expression match { + case expression: Selector => OfSelector(expression) + case expression: Logical => OfLogical(expression) + case expression => OfUnknown(expression) + } + } + +} diff --git a/src/test/scala/ahlers/tree/path/expressions/diffx/instances.scala b/src/test/scala/ahlers/tree/path/expressions/diffx/instances.scala new file mode 100644 index 0000000..b42391c --- /dev/null +++ b/src/test/scala/ahlers/tree/path/expressions/diffx/instances.scala @@ -0,0 +1,26 @@ +package ahlers.tree.path.expressions.diffx + +import ahlers.tree.path.expressions.Expression +import ahlers.tree.path.expressions.Logical +import ahlers.tree.path.expressions.Selector +import ahlers.tree.path.expressions.algebra.IsExpression +import ahlers.tree.path.operators.diffx.instances._ +import com.softwaremill.diffx.Diff + +object instances { + + implicit val diffSelector: Diff[Selector] = Diff.derived + + implicit val diffLogical: Diff[Logical] = Diff.derived + + implicit val diffExpression: Diff[Expression] = { + implicit val diffIsExpression: Diff[IsExpression] = { + import IsExpression.OfUnknown + implicit val diffOfUnknown: Diff[OfUnknown] = Diff.useEquals + Diff.derived + } + + Diff[IsExpression].contramap(IsExpression(_)) + } + +} diff --git a/src/test/scala/ahlers/tree/path/expressions/scalacheck/instances.scala b/src/test/scala/ahlers/tree/path/expressions/scalacheck/instances.scala new file mode 100644 index 0000000..3b1ca86 --- /dev/null +++ b/src/test/scala/ahlers/tree/path/expressions/scalacheck/instances.scala @@ -0,0 +1,22 @@ +package ahlers.tree.path.expressions.scalacheck + +import ahlers.tree.path.expressions.Expression +import ahlers.tree.path.expressions.Logical +import ahlers.tree.path.expressions.Selector +import ahlers.tree.path.operators.scalacheck.instances._ +import magnolify.scalacheck.semiauto._ +import org.scalacheck.Arbitrary +import org.scalacheck.Arbitrary.arbitrary +import org.scalacheck.Gen + +object instances { + + implicit val arbSelector: Arbitrary[Selector] = ArbitraryDerivation[Selector] + implicit val arbLogical: Arbitrary[Logical] = ArbitraryDerivation[Logical] + + implicit val arbExpression: Arbitrary[Expression] = Arbitrary(Gen.oneOf( + arbitrary[Selector], + arbitrary[Logical], + )) + +} diff --git a/src/test/scala/ahlers/tree/path/operators/algebra.scala b/src/test/scala/ahlers/tree/path/operators/algebra.scala index 5610dd4..fb0b0da 100644 --- a/src/test/scala/ahlers/tree/path/operators/algebra.scala +++ b/src/test/scala/ahlers/tree/path/operators/algebra.scala @@ -15,15 +15,15 @@ private object algebra { case class OfUnknown(override val toOperator: Operator) extends IsOperator(toOperator) def apply(operator: Operator): IsOperator = operator match { - case RootElement => OfRootElement - case CurrentNode => OfCurrentNode - case Wildcard => OfWildcard - case DeepScan => OfDeepScan - case dotNotatedChild: DotNotatedChild => OfDotNotation(dotNotatedChild) - case bracketNotatedChildren: BracketNotatedChildren => OfBracketNotation(bracketNotatedChildren) - case arrayIndexes: ArrayIndexes => OfArrayIndexes(arrayIndexes) - case arraySlice: ArraySlice => OfArraySlice(arraySlice) - case operator => OfUnknown(operator) + case RootElement => OfRootElement + case CurrentNode => OfCurrentNode + case Wildcard => OfWildcard + case DeepScan => OfDeepScan + case operator: DotNotatedChild => OfDotNotation(operator) + case operator: BracketNotatedChildren => OfBracketNotation(operator) + case operator: ArrayIndexes => OfArrayIndexes(operator) + case operator: ArraySlice => OfArraySlice(operator) + case operator => OfUnknown(operator) } } From c02b3e9d519da4b59ca5d828b5d57529f68f51aa Mon Sep 17 00:00:00 2001 From: Michael Ahlers Date: Sat, 11 May 2024 15:44:21 -0600 Subject: [PATCH 28/40] Fix bug with non-empty sequences --- .../ahlers/tree/path/parsers/operator.scala | 6 +-- .../ahlers/tree/path/operators/algebra.scala | 22 ++++---- .../tree/path/parsers/OperatorSpec.scala | 51 ++++++++++--------- 3 files changed, 42 insertions(+), 37 deletions(-) diff --git a/src/main/scala/ahlers/tree/path/parsers/operator.scala b/src/main/scala/ahlers/tree/path/parsers/operator.scala index a1aba21..5fb956a 100644 --- a/src/main/scala/ahlers/tree/path/parsers/operator.scala +++ b/src/main/scala/ahlers/tree/path/parsers/operator.scala @@ -16,7 +16,7 @@ import parsley.Parsley.atomic import parsley.character.char import parsley.character.string import parsley.character.whitespaces -import parsley.combinator.sepBy +import parsley.combinator.sepBy1 object operator { @@ -36,10 +36,10 @@ object operator { import BracketNotatedChildren.Child val childMatchingName: Parsley[Child.MatchingName] = (char('\'') *> name <* char('\'')).map(Child.MatchingName) val childMatchingWildcard: Parsley[Child.MatchingWildcard.type] = wildcard.as(Child.MatchingWildcard) - (char('[') *> sepBy(atomic(childMatchingName) | atomic(childMatchingWildcard), whitespaces *> char(',') <* whitespaces) <* char(']')).map(BracketNotatedChildren(_)) + (char('[') *> sepBy1(atomic(childMatchingName) | atomic(childMatchingWildcard), whitespaces *> char(',') <* whitespaces) <* char(']')).map(BracketNotatedChildren(_)) } - val arrayIndexes: Parsley[ArrayIndexes] = (char('[') *> sepBy(index, whitespaces *> char(',') <* whitespaces) <* char(']')).map(ArrayIndexes) + val arrayIndexes: Parsley[ArrayIndexes] = (char('[') *> sepBy1(index, whitespaces *> char(',') <* whitespaces) <* char(']')).map(ArrayIndexes) val arraySliceLeftBounded: Parsley[ArraySlice.LeftBounded] = (char('[') *> index <~ char(':') <* char(']')).map(ArraySlice.LeftBounded) val arraySliceRightBounded: Parsley[ArraySlice.RightBounded] = (char('[') *> char(':') ~> index <~ char(']')).map(ArraySlice.RightBounded) diff --git a/src/test/scala/ahlers/tree/path/operators/algebra.scala b/src/test/scala/ahlers/tree/path/operators/algebra.scala index fb0b0da..1b87781 100644 --- a/src/test/scala/ahlers/tree/path/operators/algebra.scala +++ b/src/test/scala/ahlers/tree/path/operators/algebra.scala @@ -4,23 +4,23 @@ private object algebra { sealed abstract class IsOperator private (val toOperator: Operator) object IsOperator { - case object OfRootElement extends IsOperator(RootElement) - case object OfCurrentNode extends IsOperator(CurrentNode) - case object OfWildcard extends IsOperator(Wildcard) - case object OfDeepScan extends IsOperator(DeepScan) - case class OfDotNotation(override val toOperator: DotNotatedChild) extends IsOperator(toOperator) - case class OfBracketNotation(override val toOperator: BracketNotatedChildren) extends IsOperator(toOperator) - case class OfArrayIndexes(override val toOperator: ArrayIndexes) extends IsOperator(toOperator) - case class OfArraySlice(override val toOperator: ArraySlice) extends IsOperator(toOperator) - case class OfUnknown(override val toOperator: Operator) extends IsOperator(toOperator) + case object OfRootElement extends IsOperator(RootElement) + case object OfCurrentNode extends IsOperator(CurrentNode) + case object OfWildcard extends IsOperator(Wildcard) + case object OfDeepScan extends IsOperator(DeepScan) + case class OfDotNotatedChild(override val toOperator: DotNotatedChild) extends IsOperator(toOperator) + case class OfBracketNotatedChildren(override val toOperator: BracketNotatedChildren) extends IsOperator(toOperator) + case class OfArrayIndexes(override val toOperator: ArrayIndexes) extends IsOperator(toOperator) + case class OfArraySlice(override val toOperator: ArraySlice) extends IsOperator(toOperator) + case class OfUnknown(override val toOperator: Operator) extends IsOperator(toOperator) def apply(operator: Operator): IsOperator = operator match { case RootElement => OfRootElement case CurrentNode => OfCurrentNode case Wildcard => OfWildcard case DeepScan => OfDeepScan - case operator: DotNotatedChild => OfDotNotation(operator) - case operator: BracketNotatedChildren => OfBracketNotation(operator) + case operator: DotNotatedChild => OfDotNotatedChild(operator) + case operator: BracketNotatedChildren => OfBracketNotatedChildren(operator) case operator: ArrayIndexes => OfArrayIndexes(operator) case operator: ArraySlice => OfArraySlice(operator) case operator => OfUnknown(operator) diff --git a/src/test/scala/ahlers/tree/path/parsers/OperatorSpec.scala b/src/test/scala/ahlers/tree/path/parsers/OperatorSpec.scala index 6f1847f..c9b6eca 100644 --- a/src/test/scala/ahlers/tree/path/parsers/OperatorSpec.scala +++ b/src/test/scala/ahlers/tree/path/parsers/OperatorSpec.scala @@ -3,7 +3,7 @@ package ahlers.tree.path.parsers import ahlers.tree.path.operators._ import ahlers.tree.path.operators.diffx.instances._ import ahlers.tree.path.operators.scalacheck.instances._ -import com.softwaremill.diffx.scalatest.DiffShouldMatcher.convertToAnyShouldMatcher +import com.softwaremill.diffx.scalatest.DiffShouldMatcher._ import org.scalatest.matchers.should.Matchers._ import org.scalatest.wordspec.AnyWordSpec import org.scalatestplus.scalacheck.ScalaCheckPropertyChecks._ @@ -18,7 +18,8 @@ class OperatorSpec extends AnyWordSpec { val pattern = "^\\$$".r s"""accept $pattern""" in { - parser.parse(RootElement.toText).shouldMatchTo(Success(RootElement)) + val operator = RootElement + parser.parse(operator.toText).shouldMatchTo(Success(operator)) } "reject else" in { @@ -35,7 +36,8 @@ class OperatorSpec extends AnyWordSpec { val pattern = "^@$".r s"""accept $pattern""" in { - parser.parse(CurrentNode.toText).shouldMatchTo(Success(CurrentNode)) + val operator = CurrentNode + parser.parse(operator.toText).shouldMatchTo(Success(operator)) } "reject else" in { @@ -52,7 +54,8 @@ class OperatorSpec extends AnyWordSpec { val pattern = "^\\*$".r s"""accept $pattern""" in { - parser.parse(Wildcard.toText).shouldMatchTo(Success(Wildcard)) + val operator = Wildcard + parser.parse(operator.toText).shouldMatchTo(Success(operator)) } "reject else" in { @@ -69,7 +72,8 @@ class OperatorSpec extends AnyWordSpec { val pattern = "^\\.\\.$".r s"""accept $pattern""" in { - parser.parse(DeepScan.toText).shouldMatchTo(Success(DeepScan)) + val operator = DeepScan + parser.parse(operator.toText).shouldMatchTo(Success(operator)) } "reject else" in { @@ -86,8 +90,8 @@ class OperatorSpec extends AnyWordSpec { val pattern = "^\\.[a-zA-Z0-9]+$".r s"""accept $pattern""" in { - forAll { dotNotatedChild: DotNotatedChild.MatchingName => - parser.parse(dotNotatedChild.toText).shouldMatchTo(Success(dotNotatedChild)) + forAll { operator: DotNotatedChild.MatchingName => + parser.parse(operator.toText).shouldMatchTo(Success(operator)) } } @@ -105,7 +109,8 @@ class OperatorSpec extends AnyWordSpec { val pattern = "^\\.\\*$".r s"""accept $pattern""" in { - parser.parse(DotNotatedChild.MatchingWildcard.toText).shouldMatchTo(Success(DotNotatedChild.MatchingWildcard)) + val operator = DotNotatedChild.MatchingWildcard + parser.parse(operator.toText).shouldMatchTo(Success(operator)) } "reject else" in { @@ -122,8 +127,8 @@ class OperatorSpec extends AnyWordSpec { val pattern = "^\\.([a-zA-Z0-9]+|\\*)$".r s"""accept $pattern""" in { - forAll { dotNotatedChild: DotNotatedChild => - parser.parse(dotNotatedChild.toText).shouldMatchTo(Success(dotNotatedChild)) + forAll { operator: DotNotatedChild => + parser.parse(operator.toText).shouldMatchTo(Success(operator)) } } @@ -141,8 +146,8 @@ class OperatorSpec extends AnyWordSpec { val pattern = "^\\[('[a-zA-Z0-9]+'|\\*)\\w*(,\\w*'[a-zA-Z0-9]+'|\\*)\\]$".r s"""accept $pattern""" in { - forAll { bracketNotatedChildren: BracketNotatedChildren => - parser.parse(bracketNotatedChildren.toText).shouldMatchTo(Success(bracketNotatedChildren)) + forAll { operator: BracketNotatedChildren => + parser.parse(operator.toText).shouldMatchTo(Success(operator)) } } @@ -160,8 +165,8 @@ class OperatorSpec extends AnyWordSpec { val pattern = "^\\[[0-9]+\\w*(,\\w*[0-9]+)\\]$".r s"""accept $pattern""" in { - forAll { arrayIndexes: ArrayIndexes => - parser.parse(arrayIndexes.toText).shouldMatchTo(Success(arrayIndexes)) + forAll { operator: ArrayIndexes => + parser.parse(operator.toText).shouldMatchTo(Success(operator)) } } @@ -179,8 +184,8 @@ class OperatorSpec extends AnyWordSpec { val pattern = "^\\[\\d+:\\]$".r s"""accept $pattern""" in { - forAll { arraySlice: ArraySlice.LeftBounded => - parser.parse(arraySlice.toText).shouldMatchTo(Success(arraySlice)) + forAll { operator: ArraySlice.LeftBounded => + parser.parse(operator.toText).shouldMatchTo(Success(operator)) } } @@ -198,8 +203,8 @@ class OperatorSpec extends AnyWordSpec { val pattern = "^\\[:\\d+\\]$".r s"""accept $pattern""" in { - forAll { arraySlice: ArraySlice.RightBounded => - parser.parse(arraySlice.toText).shouldMatchTo(Success(arraySlice)) + forAll { operator: ArraySlice.RightBounded => + parser.parse(operator.toText).shouldMatchTo(Success(operator)) } } @@ -217,8 +222,8 @@ class OperatorSpec extends AnyWordSpec { val pattern = "^\\[\\d+:\\d+\\]$".r s"""accept $pattern""" in { - forAll { arraySlice: ArraySlice.Bounded => - parser.parse(arraySlice.toText).shouldMatchTo(Success(arraySlice)) + forAll { operator: ArraySlice.Bounded => + parser.parse(operator.toText).shouldMatchTo(Success(operator)) } } @@ -236,8 +241,8 @@ class OperatorSpec extends AnyWordSpec { val pattern = "^(\\[\\d+:\\]|\\[:\\d+\\]|\\[\\d+:\\d+\\])$".r s"""accept $pattern""" in { - forAll { arraySlice: ArraySlice => - parser.parse(arraySlice.toText).shouldMatchTo(Success(arraySlice)) + forAll { operator: ArraySlice => + parser.parse(operator.toText).shouldMatchTo(Success(operator)) } } @@ -253,7 +258,7 @@ class OperatorSpec extends AnyWordSpec { "Operator" should { val parser = operator.any - """accept any""" in { + """accept any operator""" in { forAll { operator: Operator => parser.parse(operator.toText).shouldMatchTo(Success(operator)) } From d2cb094f1df2c3998ac696ff524fdda999f2c515 Mon Sep 17 00:00:00 2001 From: Michael Ahlers Date: Sat, 11 May 2024 15:45:53 -0600 Subject: [PATCH 29/40] Exclude logical (for now) --- .../ahlers/tree/path/parsers/expression.scala | 6 +++- .../expressions/scalacheck/instances.scala | 12 +++++-- .../path/operators/scalacheck/instances.scala | 14 ++++++-- .../tree/path/parsers/ExpressionSpec.scala | 34 +++++++++++++++++++ 4 files changed, 59 insertions(+), 7 deletions(-) create mode 100644 src/test/scala/ahlers/tree/path/parsers/ExpressionSpec.scala diff --git a/src/main/scala/ahlers/tree/path/parsers/expression.scala b/src/main/scala/ahlers/tree/path/parsers/expression.scala index 2d6e684..e24db5b 100644 --- a/src/main/scala/ahlers/tree/path/parsers/expression.scala +++ b/src/main/scala/ahlers/tree/path/parsers/expression.scala @@ -1,10 +1,14 @@ package ahlers.tree.path.parsers +import ahlers.tree.path.expressions.Expression import ahlers.tree.path.expressions.Selector import parsley.Parsley +import parsley.Parsley.some object expression { - // val selector:Parsley[Selector] = + val selector: Parsley[Selector] = some(operator.any).map(Selector) + + val any: Parsley[Expression] = selector } diff --git a/src/test/scala/ahlers/tree/path/expressions/scalacheck/instances.scala b/src/test/scala/ahlers/tree/path/expressions/scalacheck/instances.scala index 3b1ca86..0a3e5d6 100644 --- a/src/test/scala/ahlers/tree/path/expressions/scalacheck/instances.scala +++ b/src/test/scala/ahlers/tree/path/expressions/scalacheck/instances.scala @@ -3,6 +3,7 @@ package ahlers.tree.path.expressions.scalacheck import ahlers.tree.path.expressions.Expression import ahlers.tree.path.expressions.Logical import ahlers.tree.path.expressions.Selector +import ahlers.tree.path.operators.Operator import ahlers.tree.path.operators.scalacheck.instances._ import magnolify.scalacheck.semiauto._ import org.scalacheck.Arbitrary @@ -11,12 +12,17 @@ import org.scalacheck.Gen object instances { - implicit val arbSelector: Arbitrary[Selector] = ArbitraryDerivation[Selector] - implicit val arbLogical: Arbitrary[Logical] = ArbitraryDerivation[Logical] + implicit val arbSelector: Arbitrary[Selector] = Arbitrary(for { + head <- arbitrary[Operator] + tail <- arbitrary[Seq[Operator]] + } yield Selector(head +: tail)) + + implicit val arbLogical: Arbitrary[Logical] = ArbitraryDerivation[Logical] implicit val arbExpression: Arbitrary[Expression] = Arbitrary(Gen.oneOf( arbitrary[Selector], - arbitrary[Logical], + arbitrary[Selector], + // arbitrary[Logical], )) } diff --git a/src/test/scala/ahlers/tree/path/operators/scalacheck/instances.scala b/src/test/scala/ahlers/tree/path/operators/scalacheck/instances.scala index 3183bb4..41080b4 100644 --- a/src/test/scala/ahlers/tree/path/operators/scalacheck/instances.scala +++ b/src/test/scala/ahlers/tree/path/operators/scalacheck/instances.scala @@ -10,6 +10,7 @@ import ahlers.tree.path.operators.Operator import ahlers.tree.path.operators.RootElement import ahlers.tree.path.operators.Wildcard import ahlers.tree.path.operators.algebra.IsOperator +import ahlers.tree.path.terms.Index import ahlers.tree.path.terms.Name import ahlers.tree.path.terms.scalacheck.instances._ import magnolify.scalacheck.semiauto._ @@ -31,13 +32,20 @@ object instances { implicit val arbDotNotatedChildMatchingWildcard: Arbitrary[DotNotatedChild.MatchingWildcard.type] = ArbitraryDerivation[DotNotatedChild.MatchingWildcard.type] implicit val arbDotNotatedChild: Arbitrary[DotNotatedChild] = ArbitraryDerivation[DotNotatedChild] - implicit val arbBracketNotatedChildren: Arbitrary[BracketNotatedChildren] = { + implicit val arbBracketNotatedChildren: Arbitrary[BracketNotatedChildren] = Arbitrary { import BracketNotatedChildren.Child implicit val arbChild: Arbitrary[Child] = ArbitraryDerivation[Child] - ArbitraryDerivation[BracketNotatedChildren] + + for { + head <- arbitrary[Child] + tail <- arbitrary[Seq[Child]] + } yield BracketNotatedChildren(head +: tail) } - implicit val arbArrayIndexes: Arbitrary[ArrayIndexes] = ArbitraryDerivation[ArrayIndexes] + implicit val arbArrayIndexes: Arbitrary[ArrayIndexes] = Arbitrary(for { + head <- arbitrary[Index] + tail <- arbitrary[Seq[Index]] + } yield ArrayIndexes(head +: tail)) implicit val arbArraySliceLeftBounded: Arbitrary[ArraySlice.LeftBounded] = ArbitraryDerivation[ArraySlice.LeftBounded] implicit val arbArraySliceRightBounded: Arbitrary[ArraySlice.RightBounded] = ArbitraryDerivation[ArraySlice.RightBounded] diff --git a/src/test/scala/ahlers/tree/path/parsers/ExpressionSpec.scala b/src/test/scala/ahlers/tree/path/parsers/ExpressionSpec.scala new file mode 100644 index 0000000..c250a32 --- /dev/null +++ b/src/test/scala/ahlers/tree/path/parsers/ExpressionSpec.scala @@ -0,0 +1,34 @@ +package ahlers.tree.path.parsers + +import ahlers.tree.path.expressions.Expression +import ahlers.tree.path.expressions.Selector +import ahlers.tree.path.expressions.diffx.instances._ +import ahlers.tree.path.expressions.scalacheck.instances._ +import com.softwaremill.diffx.scalatest.DiffShouldMatcher._ +import org.scalatest.wordspec.AnyWordSpec +import org.scalatestplus.scalacheck.ScalaCheckPropertyChecks._ +import parsley.Success +import parsley.diffx.instances._ + +class ExpressionSpec extends AnyWordSpec { + + "Selector" should { + val parser = expression.selector + + s"""accept sequence of operators""" in { + forAll { expression: Selector => + parser.parse(expression.toText).shouldMatchTo(Success(expression)) + } + } + } + + "Expression" should { + val parser = expression.any + s"""accept any expression""" in { + forAll { expression: Expression => + parser.parse(expression.toText).shouldMatchTo(Success(expression)) + } + } + } + +} From 33ace809b7299e1893fcc87d95f35c6c2ee7db04 Mon Sep 17 00:00:00 2001 From: Michael Ahlers Date: Sat, 11 May 2024 15:46:23 -0600 Subject: [PATCH 30/40] Remove relax --- compiler.sbt | 3 --- 1 file changed, 3 deletions(-) diff --git a/compiler.sbt b/compiler.sbt index 3d5f997..7fc6b54 100644 --- a/compiler.sbt +++ b/compiler.sbt @@ -1,6 +1,3 @@ import org.typelevel.scalacoptions.ScalacOptions tpolecatExcludeOptions += ScalacOptions.warnUnusedImports - -Test / tpolecatExcludeOptions += ScalacOptions.warnValueDiscard -Test / tpolecatExcludeOptions += ScalacOptions.warnNonUnitStatement From 50f6a27e654f711ec6af76e8f18492029d233577 Mon Sep 17 00:00:00 2001 From: Michael Ahlers Date: Sun, 12 May 2024 17:34:45 -0600 Subject: [PATCH 31/40] IsEqual filter operator --- .../path/filterOperators/FilterOperator.scala | 5 +++ .../tree/path/filterOperators/IsEqual.scala | 5 +++ .../tree/path/parsers/filterOperator.scala | 11 ++++++ .../tree/path/filterOperators/algebra.scala | 16 +++++++++ .../filterOperators/diffx/instances.scala | 23 +++++++++++++ .../scalacheck/instances.scala | 23 +++++++++++++ .../path/parsers/FilterOperatorSpec.scala | 34 +++++++++++++++++++ 7 files changed, 117 insertions(+) create mode 100644 src/main/scala/ahlers/tree/path/filterOperators/FilterOperator.scala create mode 100644 src/main/scala/ahlers/tree/path/filterOperators/IsEqual.scala create mode 100644 src/main/scala/ahlers/tree/path/parsers/filterOperator.scala create mode 100644 src/test/scala/ahlers/tree/path/filterOperators/algebra.scala create mode 100644 src/test/scala/ahlers/tree/path/filterOperators/diffx/instances.scala create mode 100644 src/test/scala/ahlers/tree/path/filterOperators/scalacheck/instances.scala create mode 100644 src/test/scala/ahlers/tree/path/parsers/FilterOperatorSpec.scala diff --git a/src/main/scala/ahlers/tree/path/filterOperators/FilterOperator.scala b/src/main/scala/ahlers/tree/path/filterOperators/FilterOperator.scala new file mode 100644 index 0000000..bf67133 --- /dev/null +++ b/src/main/scala/ahlers/tree/path/filterOperators/FilterOperator.scala @@ -0,0 +1,5 @@ +package ahlers.tree.path.filterOperators + +trait FilterOperator { + def toText: String +} diff --git a/src/main/scala/ahlers/tree/path/filterOperators/IsEqual.scala b/src/main/scala/ahlers/tree/path/filterOperators/IsEqual.scala new file mode 100644 index 0000000..d99b5c6 --- /dev/null +++ b/src/main/scala/ahlers/tree/path/filterOperators/IsEqual.scala @@ -0,0 +1,5 @@ +package ahlers.tree.path.filterOperators + +object IsEqual extends FilterOperator { + override val toText: String = "==" +} diff --git a/src/main/scala/ahlers/tree/path/parsers/filterOperator.scala b/src/main/scala/ahlers/tree/path/parsers/filterOperator.scala new file mode 100644 index 0000000..d277ecf --- /dev/null +++ b/src/main/scala/ahlers/tree/path/parsers/filterOperator.scala @@ -0,0 +1,11 @@ +package ahlers.tree.path.parsers + +import ahlers.tree.path.filterOperators.IsEqual +import parsley.Parsley +import parsley.character.string + +object filterOperator { + + val isEqual: Parsley[IsEqual.type] = string("==").as(IsEqual) + +} diff --git a/src/test/scala/ahlers/tree/path/filterOperators/algebra.scala b/src/test/scala/ahlers/tree/path/filterOperators/algebra.scala new file mode 100644 index 0000000..d37f097 --- /dev/null +++ b/src/test/scala/ahlers/tree/path/filterOperators/algebra.scala @@ -0,0 +1,16 @@ +package ahlers.tree.path.filterOperators + +private object algebra { + + sealed abstract class IsFilterOperator private (val toFilterOperator: FilterOperator) + object IsFilterOperator { + case object OfEqual extends IsFilterOperator(IsEqual) + case class OfUnknown(override val toFilterOperator: FilterOperator) extends IsFilterOperator(toFilterOperator) + + def apply(operator: FilterOperator): IsFilterOperator = operator match { + case IsEqual => OfEqual + case operator => OfUnknown(operator) + } + } + +} diff --git a/src/test/scala/ahlers/tree/path/filterOperators/diffx/instances.scala b/src/test/scala/ahlers/tree/path/filterOperators/diffx/instances.scala new file mode 100644 index 0000000..1c8e52a --- /dev/null +++ b/src/test/scala/ahlers/tree/path/filterOperators/diffx/instances.scala @@ -0,0 +1,23 @@ +package ahlers.tree.path.filterOperators.diffx + +import ahlers.tree.path.filterOperators.FilterOperator +import ahlers.tree.path.filterOperators.IsEqual +import ahlers.tree.path.filterOperators.algebra.IsFilterOperator +import ahlers.tree.path.terms.diffx.instances._ +import com.softwaremill.diffx.Diff + +object instances { + + implicit val diffIsEqual: Diff[IsEqual.type] = Diff.derived + + implicit val diffFilterOperator: Diff[FilterOperator] = { + implicit val diffIsFilterOperator: Diff[IsFilterOperator] = { + import IsFilterOperator.OfUnknown + implicit val diffOfUnknown: Diff[OfUnknown] = Diff.useEquals + Diff.derived + } + + Diff[IsFilterOperator].contramap(IsFilterOperator(_)) + } + +} diff --git a/src/test/scala/ahlers/tree/path/filterOperators/scalacheck/instances.scala b/src/test/scala/ahlers/tree/path/filterOperators/scalacheck/instances.scala new file mode 100644 index 0000000..1d553a2 --- /dev/null +++ b/src/test/scala/ahlers/tree/path/filterOperators/scalacheck/instances.scala @@ -0,0 +1,23 @@ +package ahlers.tree.path.filterOperators.scalacheck + +import ahlers.tree.path.filterOperators.FilterOperator +import ahlers.tree.path.filterOperators.IsEqual +import ahlers.tree.path.filterOperators.algebra.IsFilterOperator +import ahlers.tree.path.terms.Index +import ahlers.tree.path.terms.Name +import ahlers.tree.path.terms.scalacheck.instances._ +import magnolify.scalacheck.semiauto._ +import org.scalacheck.Arbitrary +import org.scalacheck.Arbitrary.arbitrary +import org.scalacheck.Gen + +object instances { + + implicit val arbEqual: Arbitrary[IsEqual.type] = Arbitrary(Gen.const(IsEqual)) + + implicit val arbFilterOperator: Arbitrary[FilterOperator] = Arbitrary(Gen.oneOf( + arbitrary[IsEqual.type], + arbitrary[IsEqual.type], + )) + +} diff --git a/src/test/scala/ahlers/tree/path/parsers/FilterOperatorSpec.scala b/src/test/scala/ahlers/tree/path/parsers/FilterOperatorSpec.scala new file mode 100644 index 0000000..98e0848 --- /dev/null +++ b/src/test/scala/ahlers/tree/path/parsers/FilterOperatorSpec.scala @@ -0,0 +1,34 @@ +package ahlers.tree.path.parsers + +import ahlers.tree.path.filterOperators.IsEqual +import ahlers.tree.path.filterOperators.diffx.instances._ +import ahlers.tree.path.filterOperators.scalacheck.instances._ +import com.softwaremill.diffx.scalatest.DiffShouldMatcher._ +import org.scalatest.matchers.should.Matchers._ +import org.scalatest.wordspec.AnyWordSpec +import org.scalatestplus.scalacheck.ScalaCheckPropertyChecks._ +import parsley.Failure +import parsley.Success +import parsley.diffx.instances._ + +class FilterOperatorSpec extends AnyWordSpec { + + "IsEqual" should { + val parser = filterOperator.isEqual + val pattern = "^==$".r + + s"""accept $pattern""" in { + val filterOperator = IsEqual + parser.parse(filterOperator.toText).shouldMatchTo(Success(filterOperator)) + } + + "reject else" in { + forAll { input: String => + whenever(!pattern.matches(input)) { + parser.parse(input).shouldBe(a[Failure[_]]) + } + } + } + } + +} From edf19a361941b664fdd20737b8d6a8bb270a16f7 Mon Sep 17 00:00:00 2001 From: Michael Ahlers Date: Sun, 12 May 2024 17:36:39 -0600 Subject: [PATCH 32/40] IsNotEqual filter operator --- .../path/filterOperators/IsNotEqual.scala | 5 +++++ .../tree/path/parsers/filterOperator.scala | 4 +++- .../tree/path/filterOperators/algebra.scala | 8 +++++--- .../filterOperators/diffx/instances.scala | 5 +++-- .../path/parsers/FilterOperatorSpec.scala | 19 +++++++++++++++++++ 5 files changed, 35 insertions(+), 6 deletions(-) create mode 100644 src/main/scala/ahlers/tree/path/filterOperators/IsNotEqual.scala diff --git a/src/main/scala/ahlers/tree/path/filterOperators/IsNotEqual.scala b/src/main/scala/ahlers/tree/path/filterOperators/IsNotEqual.scala new file mode 100644 index 0000000..087ea3f --- /dev/null +++ b/src/main/scala/ahlers/tree/path/filterOperators/IsNotEqual.scala @@ -0,0 +1,5 @@ +package ahlers.tree.path.filterOperators + +object IsNotEqual extends FilterOperator { + override val toText: String = "!=" +} diff --git a/src/main/scala/ahlers/tree/path/parsers/filterOperator.scala b/src/main/scala/ahlers/tree/path/parsers/filterOperator.scala index d277ecf..75be1f8 100644 --- a/src/main/scala/ahlers/tree/path/parsers/filterOperator.scala +++ b/src/main/scala/ahlers/tree/path/parsers/filterOperator.scala @@ -1,11 +1,13 @@ package ahlers.tree.path.parsers import ahlers.tree.path.filterOperators.IsEqual +import ahlers.tree.path.filterOperators.IsNotEqual import parsley.Parsley import parsley.character.string object filterOperator { - val isEqual: Parsley[IsEqual.type] = string("==").as(IsEqual) + val isEqual: Parsley[IsEqual.type] = string("==").as(IsEqual) + val isNotEqual: Parsley[IsNotEqual.type] = string("!=").as(IsNotEqual) } diff --git a/src/test/scala/ahlers/tree/path/filterOperators/algebra.scala b/src/test/scala/ahlers/tree/path/filterOperators/algebra.scala index d37f097..c4ca6e9 100644 --- a/src/test/scala/ahlers/tree/path/filterOperators/algebra.scala +++ b/src/test/scala/ahlers/tree/path/filterOperators/algebra.scala @@ -4,12 +4,14 @@ private object algebra { sealed abstract class IsFilterOperator private (val toFilterOperator: FilterOperator) object IsFilterOperator { - case object OfEqual extends IsFilterOperator(IsEqual) + case object OfIsEqual extends IsFilterOperator(IsEqual) + case object OfIsNotEqual extends IsFilterOperator(IsNotEqual) case class OfUnknown(override val toFilterOperator: FilterOperator) extends IsFilterOperator(toFilterOperator) def apply(operator: FilterOperator): IsFilterOperator = operator match { - case IsEqual => OfEqual - case operator => OfUnknown(operator) + case IsEqual => OfIsEqual + case IsNotEqual => OfIsNotEqual + case operator => OfUnknown(operator) } } diff --git a/src/test/scala/ahlers/tree/path/filterOperators/diffx/instances.scala b/src/test/scala/ahlers/tree/path/filterOperators/diffx/instances.scala index 1c8e52a..00d0cb1 100644 --- a/src/test/scala/ahlers/tree/path/filterOperators/diffx/instances.scala +++ b/src/test/scala/ahlers/tree/path/filterOperators/diffx/instances.scala @@ -1,7 +1,6 @@ package ahlers.tree.path.filterOperators.diffx -import ahlers.tree.path.filterOperators.FilterOperator -import ahlers.tree.path.filterOperators.IsEqual +import ahlers.tree.path.filterOperators.{FilterOperator, IsEqual, IsNotEqual} import ahlers.tree.path.filterOperators.algebra.IsFilterOperator import ahlers.tree.path.terms.diffx.instances._ import com.softwaremill.diffx.Diff @@ -10,6 +9,8 @@ object instances { implicit val diffIsEqual: Diff[IsEqual.type] = Diff.derived + implicit val diffIsNotEqual: Diff[IsNotEqual.type] = Diff.derived + implicit val diffFilterOperator: Diff[FilterOperator] = { implicit val diffIsFilterOperator: Diff[IsFilterOperator] = { import IsFilterOperator.OfUnknown diff --git a/src/test/scala/ahlers/tree/path/parsers/FilterOperatorSpec.scala b/src/test/scala/ahlers/tree/path/parsers/FilterOperatorSpec.scala index 98e0848..93cc878 100644 --- a/src/test/scala/ahlers/tree/path/parsers/FilterOperatorSpec.scala +++ b/src/test/scala/ahlers/tree/path/parsers/FilterOperatorSpec.scala @@ -1,6 +1,7 @@ package ahlers.tree.path.parsers import ahlers.tree.path.filterOperators.IsEqual +import ahlers.tree.path.filterOperators.IsNotEqual import ahlers.tree.path.filterOperators.diffx.instances._ import ahlers.tree.path.filterOperators.scalacheck.instances._ import com.softwaremill.diffx.scalatest.DiffShouldMatcher._ @@ -31,4 +32,22 @@ class FilterOperatorSpec extends AnyWordSpec { } } + "IsNotEqual" should { + val parser = filterOperator.isNotEqual + val pattern = "^!=$".r + + s"""accept $pattern""" in { + val filterOperator = IsNotEqual + parser.parse(filterOperator.toText).shouldMatchTo(Success(filterOperator)) + } + + "reject else" in { + forAll { input: String => + whenever(!pattern.matches(input)) { + parser.parse(input).shouldBe(a[Failure[_]]) + } + } + } + } + } From a9a1dc3579924baa415d030d3345f938ba5bfc5c Mon Sep 17 00:00:00 2001 From: Michael Ahlers Date: Sun, 12 May 2024 17:38:02 -0600 Subject: [PATCH 33/40] Add references --- .../ahlers/tree/path/filterOperators/FilterOperator.scala | 3 +++ src/main/scala/ahlers/tree/path/operators/Operator.scala | 3 +++ 2 files changed, 6 insertions(+) diff --git a/src/main/scala/ahlers/tree/path/filterOperators/FilterOperator.scala b/src/main/scala/ahlers/tree/path/filterOperators/FilterOperator.scala index bf67133..5a8f737 100644 --- a/src/main/scala/ahlers/tree/path/filterOperators/FilterOperator.scala +++ b/src/main/scala/ahlers/tree/path/filterOperators/FilterOperator.scala @@ -1,5 +1,8 @@ package ahlers.tree.path.filterOperators +/** + * @see [[https://github.com/json-path/JsonPath/blob/45333e0a310af70ad48d34d306da30af1e8e6314/README.md#filter-operators]] + */ trait FilterOperator { def toText: String } diff --git a/src/main/scala/ahlers/tree/path/operators/Operator.scala b/src/main/scala/ahlers/tree/path/operators/Operator.scala index 0ed856a..9a062b3 100644 --- a/src/main/scala/ahlers/tree/path/operators/Operator.scala +++ b/src/main/scala/ahlers/tree/path/operators/Operator.scala @@ -1,5 +1,8 @@ package ahlers.tree.path.operators +/** + * @see [[https://github.com/json-path/JsonPath/blob/45333e0a310af70ad48d34d306da30af1e8e6314/README.md#operators]] + */ trait Operator { def toText: String } From 213f738883d7e5dc52570b1c8ac18f7b4a2c2049 Mon Sep 17 00:00:00 2001 From: Michael Ahlers Date: Sun, 12 May 2024 17:41:09 -0600 Subject: [PATCH 34/40] Define all remaining filter operators --- .../scala/ahlers/tree/path/filterOperators/HasSize.scala | 5 +++++ .../scala/ahlers/tree/path/filterOperators/IsAnyOf.scala | 5 +++++ .../scala/ahlers/tree/path/filterOperators/IsEmpty.scala | 5 +++++ .../ahlers/tree/path/filterOperators/IsGreaterThan.scala | 5 +++++ .../tree/path/filterOperators/IsGreaterThanOrEqual.scala | 5 +++++ src/main/scala/ahlers/tree/path/filterOperators/IsIn.scala | 5 +++++ .../ahlers/tree/path/filterOperators/IsLessThan.scala | 5 +++++ .../tree/path/filterOperators/IsLessThanOrEqual.scala | 5 +++++ .../tree/path/filterOperators/IsMatchExpression.scala | 5 +++++ .../scala/ahlers/tree/path/filterOperators/IsNoneOf.scala | 5 +++++ .../scala/ahlers/tree/path/filterOperators/IsNotIn.scala | 7 +++++++ .../ahlers/tree/path/filterOperators/IsSubsetOf.scala | 5 +++++ 12 files changed, 62 insertions(+) create mode 100644 src/main/scala/ahlers/tree/path/filterOperators/HasSize.scala create mode 100644 src/main/scala/ahlers/tree/path/filterOperators/IsAnyOf.scala create mode 100644 src/main/scala/ahlers/tree/path/filterOperators/IsEmpty.scala create mode 100644 src/main/scala/ahlers/tree/path/filterOperators/IsGreaterThan.scala create mode 100644 src/main/scala/ahlers/tree/path/filterOperators/IsGreaterThanOrEqual.scala create mode 100644 src/main/scala/ahlers/tree/path/filterOperators/IsIn.scala create mode 100644 src/main/scala/ahlers/tree/path/filterOperators/IsLessThan.scala create mode 100644 src/main/scala/ahlers/tree/path/filterOperators/IsLessThanOrEqual.scala create mode 100644 src/main/scala/ahlers/tree/path/filterOperators/IsMatchExpression.scala create mode 100644 src/main/scala/ahlers/tree/path/filterOperators/IsNoneOf.scala create mode 100644 src/main/scala/ahlers/tree/path/filterOperators/IsNotIn.scala create mode 100644 src/main/scala/ahlers/tree/path/filterOperators/IsSubsetOf.scala diff --git a/src/main/scala/ahlers/tree/path/filterOperators/HasSize.scala b/src/main/scala/ahlers/tree/path/filterOperators/HasSize.scala new file mode 100644 index 0000000..6412067 --- /dev/null +++ b/src/main/scala/ahlers/tree/path/filterOperators/HasSize.scala @@ -0,0 +1,5 @@ +package ahlers.tree.path.filterOperators + +object HasSize extends FilterOperator { + override val toText: String = "size" +} diff --git a/src/main/scala/ahlers/tree/path/filterOperators/IsAnyOf.scala b/src/main/scala/ahlers/tree/path/filterOperators/IsAnyOf.scala new file mode 100644 index 0000000..6e645e5 --- /dev/null +++ b/src/main/scala/ahlers/tree/path/filterOperators/IsAnyOf.scala @@ -0,0 +1,5 @@ +package ahlers.tree.path.filterOperators + +object IsAnyOf extends FilterOperator { + override val toText: String = "anyof" +} diff --git a/src/main/scala/ahlers/tree/path/filterOperators/IsEmpty.scala b/src/main/scala/ahlers/tree/path/filterOperators/IsEmpty.scala new file mode 100644 index 0000000..54751ab --- /dev/null +++ b/src/main/scala/ahlers/tree/path/filterOperators/IsEmpty.scala @@ -0,0 +1,5 @@ +package ahlers.tree.path.filterOperators + +object IsEmpty extends FilterOperator { + override val toText: String = "empty" +} diff --git a/src/main/scala/ahlers/tree/path/filterOperators/IsGreaterThan.scala b/src/main/scala/ahlers/tree/path/filterOperators/IsGreaterThan.scala new file mode 100644 index 0000000..e45b1a9 --- /dev/null +++ b/src/main/scala/ahlers/tree/path/filterOperators/IsGreaterThan.scala @@ -0,0 +1,5 @@ +package ahlers.tree.path.filterOperators + +object IsGreaterThan extends FilterOperator { + override val toText: String = ">" +} diff --git a/src/main/scala/ahlers/tree/path/filterOperators/IsGreaterThanOrEqual.scala b/src/main/scala/ahlers/tree/path/filterOperators/IsGreaterThanOrEqual.scala new file mode 100644 index 0000000..fd6ccdd --- /dev/null +++ b/src/main/scala/ahlers/tree/path/filterOperators/IsGreaterThanOrEqual.scala @@ -0,0 +1,5 @@ +package ahlers.tree.path.filterOperators + +object IsGreaterThanOrEqual extends FilterOperator { + override val toText: String = ">=" +} diff --git a/src/main/scala/ahlers/tree/path/filterOperators/IsIn.scala b/src/main/scala/ahlers/tree/path/filterOperators/IsIn.scala new file mode 100644 index 0000000..df4a2a1 --- /dev/null +++ b/src/main/scala/ahlers/tree/path/filterOperators/IsIn.scala @@ -0,0 +1,5 @@ +package ahlers.tree.path.filterOperators + +object IsIn extends FilterOperator { + override val toText: String = "in" +} diff --git a/src/main/scala/ahlers/tree/path/filterOperators/IsLessThan.scala b/src/main/scala/ahlers/tree/path/filterOperators/IsLessThan.scala new file mode 100644 index 0000000..0515ad1 --- /dev/null +++ b/src/main/scala/ahlers/tree/path/filterOperators/IsLessThan.scala @@ -0,0 +1,5 @@ +package ahlers.tree.path.filterOperators + +object IsLessThan extends FilterOperator { + override val toText: String = "<" +} diff --git a/src/main/scala/ahlers/tree/path/filterOperators/IsLessThanOrEqual.scala b/src/main/scala/ahlers/tree/path/filterOperators/IsLessThanOrEqual.scala new file mode 100644 index 0000000..7eea6b2 --- /dev/null +++ b/src/main/scala/ahlers/tree/path/filterOperators/IsLessThanOrEqual.scala @@ -0,0 +1,5 @@ +package ahlers.tree.path.filterOperators + +object IsLessThanOrEqual extends FilterOperator { + override val toText: String = "<=" +} diff --git a/src/main/scala/ahlers/tree/path/filterOperators/IsMatchExpression.scala b/src/main/scala/ahlers/tree/path/filterOperators/IsMatchExpression.scala new file mode 100644 index 0000000..6e0fc83 --- /dev/null +++ b/src/main/scala/ahlers/tree/path/filterOperators/IsMatchExpression.scala @@ -0,0 +1,5 @@ +package ahlers.tree.path.filterOperators + +object IsMatchExpression extends FilterOperator { + override val toText: String = "=~" +} diff --git a/src/main/scala/ahlers/tree/path/filterOperators/IsNoneOf.scala b/src/main/scala/ahlers/tree/path/filterOperators/IsNoneOf.scala new file mode 100644 index 0000000..269e76a --- /dev/null +++ b/src/main/scala/ahlers/tree/path/filterOperators/IsNoneOf.scala @@ -0,0 +1,5 @@ +package ahlers.tree.path.filterOperators + +object IsNoneOf extends FilterOperator { + override val toText: String = "noneof" +} diff --git a/src/main/scala/ahlers/tree/path/filterOperators/IsNotIn.scala b/src/main/scala/ahlers/tree/path/filterOperators/IsNotIn.scala new file mode 100644 index 0000000..4bc9c04 --- /dev/null +++ b/src/main/scala/ahlers/tree/path/filterOperators/IsNotIn.scala @@ -0,0 +1,7 @@ +package ahlers.tree.path.filterOperators + +object IsNotIn extends FilterOperator { + + /** \m/ */ + override val toText: String = "nin" +} diff --git a/src/main/scala/ahlers/tree/path/filterOperators/IsSubsetOf.scala b/src/main/scala/ahlers/tree/path/filterOperators/IsSubsetOf.scala new file mode 100644 index 0000000..ee3ba39 --- /dev/null +++ b/src/main/scala/ahlers/tree/path/filterOperators/IsSubsetOf.scala @@ -0,0 +1,5 @@ +package ahlers.tree.path.filterOperators + +object IsSubsetOf extends FilterOperator { + override val toText: String = "subset" +} From 2e57d2c5a6c24d5b4f5a4940035a43849ac80543 Mon Sep 17 00:00:00 2001 From: Michael Ahlers Date: Sun, 12 May 2024 17:44:32 -0600 Subject: [PATCH 35/40] Finish algebra --- .../{IsEqual.scala => IsEqualTo.scala} | 2 +- ...sMatchExpression.scala => IsMatchOf.scala} | 2 +- .../{IsNotEqual.scala => IsNotEqualTo.scala} | 2 +- .../tree/path/parsers/filterOperator.scala | 8 +++---- .../tree/path/filterOperators/algebra.scala | 22 ++++++++++++++----- .../filterOperators/diffx/instances.scala | 6 ++--- .../scalacheck/instances.scala | 8 +++---- .../path/parsers/FilterOperatorSpec.scala | 8 +++---- 8 files changed, 35 insertions(+), 23 deletions(-) rename src/main/scala/ahlers/tree/path/filterOperators/{IsEqual.scala => IsEqualTo.scala} (65%) rename src/main/scala/ahlers/tree/path/filterOperators/{IsMatchExpression.scala => IsMatchOf.scala} (61%) rename src/main/scala/ahlers/tree/path/filterOperators/{IsNotEqual.scala => IsNotEqualTo.scala} (64%) diff --git a/src/main/scala/ahlers/tree/path/filterOperators/IsEqual.scala b/src/main/scala/ahlers/tree/path/filterOperators/IsEqualTo.scala similarity index 65% rename from src/main/scala/ahlers/tree/path/filterOperators/IsEqual.scala rename to src/main/scala/ahlers/tree/path/filterOperators/IsEqualTo.scala index d99b5c6..9ce7c93 100644 --- a/src/main/scala/ahlers/tree/path/filterOperators/IsEqual.scala +++ b/src/main/scala/ahlers/tree/path/filterOperators/IsEqualTo.scala @@ -1,5 +1,5 @@ package ahlers.tree.path.filterOperators -object IsEqual extends FilterOperator { +object IsEqualTo extends FilterOperator { override val toText: String = "==" } diff --git a/src/main/scala/ahlers/tree/path/filterOperators/IsMatchExpression.scala b/src/main/scala/ahlers/tree/path/filterOperators/IsMatchOf.scala similarity index 61% rename from src/main/scala/ahlers/tree/path/filterOperators/IsMatchExpression.scala rename to src/main/scala/ahlers/tree/path/filterOperators/IsMatchOf.scala index 6e0fc83..d397a18 100644 --- a/src/main/scala/ahlers/tree/path/filterOperators/IsMatchExpression.scala +++ b/src/main/scala/ahlers/tree/path/filterOperators/IsMatchOf.scala @@ -1,5 +1,5 @@ package ahlers.tree.path.filterOperators -object IsMatchExpression extends FilterOperator { +object IsMatchOf extends FilterOperator { override val toText: String = "=~" } diff --git a/src/main/scala/ahlers/tree/path/filterOperators/IsNotEqual.scala b/src/main/scala/ahlers/tree/path/filterOperators/IsNotEqualTo.scala similarity index 64% rename from src/main/scala/ahlers/tree/path/filterOperators/IsNotEqual.scala rename to src/main/scala/ahlers/tree/path/filterOperators/IsNotEqualTo.scala index 087ea3f..d69d767 100644 --- a/src/main/scala/ahlers/tree/path/filterOperators/IsNotEqual.scala +++ b/src/main/scala/ahlers/tree/path/filterOperators/IsNotEqualTo.scala @@ -1,5 +1,5 @@ package ahlers.tree.path.filterOperators -object IsNotEqual extends FilterOperator { +object IsNotEqualTo extends FilterOperator { override val toText: String = "!=" } diff --git a/src/main/scala/ahlers/tree/path/parsers/filterOperator.scala b/src/main/scala/ahlers/tree/path/parsers/filterOperator.scala index 75be1f8..c90dbe1 100644 --- a/src/main/scala/ahlers/tree/path/parsers/filterOperator.scala +++ b/src/main/scala/ahlers/tree/path/parsers/filterOperator.scala @@ -1,13 +1,13 @@ package ahlers.tree.path.parsers -import ahlers.tree.path.filterOperators.IsEqual -import ahlers.tree.path.filterOperators.IsNotEqual +import ahlers.tree.path.filterOperators.IsEqualTo +import ahlers.tree.path.filterOperators.IsNotEqualTo import parsley.Parsley import parsley.character.string object filterOperator { - val isEqual: Parsley[IsEqual.type] = string("==").as(IsEqual) - val isNotEqual: Parsley[IsNotEqual.type] = string("!=").as(IsNotEqual) + val isEqual: Parsley[IsEqualTo.type] = string("==").as(IsEqualTo) + val isNotEqual: Parsley[IsNotEqualTo.type] = string("!=").as(IsNotEqualTo) } diff --git a/src/test/scala/ahlers/tree/path/filterOperators/algebra.scala b/src/test/scala/ahlers/tree/path/filterOperators/algebra.scala index c4ca6e9..7533ef1 100644 --- a/src/test/scala/ahlers/tree/path/filterOperators/algebra.scala +++ b/src/test/scala/ahlers/tree/path/filterOperators/algebra.scala @@ -4,14 +4,26 @@ private object algebra { sealed abstract class IsFilterOperator private (val toFilterOperator: FilterOperator) object IsFilterOperator { - case object OfIsEqual extends IsFilterOperator(IsEqual) - case object OfIsNotEqual extends IsFilterOperator(IsNotEqual) + case object OfIsEqual extends IsFilterOperator(IsEqualTo) + case object OfIsNotEqual extends IsFilterOperator(IsNotEqualTo) + case object OfIsLessThan extends IsFilterOperator(IsLessThan) + case object OfIsLessThanOrEqual extends IsFilterOperator(IsLessThanOrEqual) + case object OfIsGreaterThan extends IsFilterOperator(IsGreaterThan) + case object OfIsGreaterThanOrEqual extends IsFilterOperator(IsGreaterThanOrEqual) + case object OfIsMatchOf extends IsFilterOperator(IsMatchOf) + case object OfIsIn extends IsFilterOperator(IsIn) + case object OfIsNotIn extends IsFilterOperator(IsNotIn) + case object OfIsSubsetOf extends IsFilterOperator(IsSubsetOf) + case object OfIsAnyOf extends IsFilterOperator(IsAnyOf) + case object OfIsNoneOf extends IsFilterOperator(IsNoneOf) + case object OfHasSize extends IsFilterOperator(HasSize) + case object OfIsEmpty extends IsFilterOperator(IsEmpty) case class OfUnknown(override val toFilterOperator: FilterOperator) extends IsFilterOperator(toFilterOperator) def apply(operator: FilterOperator): IsFilterOperator = operator match { - case IsEqual => OfIsEqual - case IsNotEqual => OfIsNotEqual - case operator => OfUnknown(operator) + case IsEqualTo => OfIsEqual + case IsNotEqualTo => OfIsNotEqual + case operator => OfUnknown(operator) } } diff --git a/src/test/scala/ahlers/tree/path/filterOperators/diffx/instances.scala b/src/test/scala/ahlers/tree/path/filterOperators/diffx/instances.scala index 00d0cb1..6f8cb91 100644 --- a/src/test/scala/ahlers/tree/path/filterOperators/diffx/instances.scala +++ b/src/test/scala/ahlers/tree/path/filterOperators/diffx/instances.scala @@ -1,15 +1,15 @@ package ahlers.tree.path.filterOperators.diffx -import ahlers.tree.path.filterOperators.{FilterOperator, IsEqual, IsNotEqual} +import ahlers.tree.path.filterOperators.{FilterOperator, IsEqualTo, IsNotEqualTo} import ahlers.tree.path.filterOperators.algebra.IsFilterOperator import ahlers.tree.path.terms.diffx.instances._ import com.softwaremill.diffx.Diff object instances { - implicit val diffIsEqual: Diff[IsEqual.type] = Diff.derived + implicit val diffIsEqual: Diff[IsEqualTo.type] = Diff.derived - implicit val diffIsNotEqual: Diff[IsNotEqual.type] = Diff.derived + implicit val diffIsNotEqual: Diff[IsNotEqualTo.type] = Diff.derived implicit val diffFilterOperator: Diff[FilterOperator] = { implicit val diffIsFilterOperator: Diff[IsFilterOperator] = { diff --git a/src/test/scala/ahlers/tree/path/filterOperators/scalacheck/instances.scala b/src/test/scala/ahlers/tree/path/filterOperators/scalacheck/instances.scala index 1d553a2..1c694d5 100644 --- a/src/test/scala/ahlers/tree/path/filterOperators/scalacheck/instances.scala +++ b/src/test/scala/ahlers/tree/path/filterOperators/scalacheck/instances.scala @@ -1,7 +1,7 @@ package ahlers.tree.path.filterOperators.scalacheck import ahlers.tree.path.filterOperators.FilterOperator -import ahlers.tree.path.filterOperators.IsEqual +import ahlers.tree.path.filterOperators.IsEqualTo import ahlers.tree.path.filterOperators.algebra.IsFilterOperator import ahlers.tree.path.terms.Index import ahlers.tree.path.terms.Name @@ -13,11 +13,11 @@ import org.scalacheck.Gen object instances { - implicit val arbEqual: Arbitrary[IsEqual.type] = Arbitrary(Gen.const(IsEqual)) + implicit val arbEqual: Arbitrary[IsEqualTo.type] = Arbitrary(Gen.const(IsEqualTo)) implicit val arbFilterOperator: Arbitrary[FilterOperator] = Arbitrary(Gen.oneOf( - arbitrary[IsEqual.type], - arbitrary[IsEqual.type], + arbitrary[IsEqualTo.type], + arbitrary[IsEqualTo.type], )) } diff --git a/src/test/scala/ahlers/tree/path/parsers/FilterOperatorSpec.scala b/src/test/scala/ahlers/tree/path/parsers/FilterOperatorSpec.scala index 93cc878..baad4c8 100644 --- a/src/test/scala/ahlers/tree/path/parsers/FilterOperatorSpec.scala +++ b/src/test/scala/ahlers/tree/path/parsers/FilterOperatorSpec.scala @@ -1,7 +1,7 @@ package ahlers.tree.path.parsers -import ahlers.tree.path.filterOperators.IsEqual -import ahlers.tree.path.filterOperators.IsNotEqual +import ahlers.tree.path.filterOperators.IsEqualTo +import ahlers.tree.path.filterOperators.IsNotEqualTo import ahlers.tree.path.filterOperators.diffx.instances._ import ahlers.tree.path.filterOperators.scalacheck.instances._ import com.softwaremill.diffx.scalatest.DiffShouldMatcher._ @@ -19,7 +19,7 @@ class FilterOperatorSpec extends AnyWordSpec { val pattern = "^==$".r s"""accept $pattern""" in { - val filterOperator = IsEqual + val filterOperator = IsEqualTo parser.parse(filterOperator.toText).shouldMatchTo(Success(filterOperator)) } @@ -37,7 +37,7 @@ class FilterOperatorSpec extends AnyWordSpec { val pattern = "^!=$".r s"""accept $pattern""" in { - val filterOperator = IsNotEqual + val filterOperator = IsNotEqualTo parser.parse(filterOperator.toText).shouldMatchTo(Success(filterOperator)) } From a6a0c8f3a1e51c95eb36f359e795703d64dcf750 Mon Sep 17 00:00:00 2001 From: Michael Ahlers Date: Sun, 12 May 2024 17:50:14 -0600 Subject: [PATCH 36/40] Finish all test support --- .../filterOperators/diffx/instances.scala | 20 +++++++--- .../scalacheck/instances.scala | 37 ++++++++++++++----- 2 files changed, 43 insertions(+), 14 deletions(-) diff --git a/src/test/scala/ahlers/tree/path/filterOperators/diffx/instances.scala b/src/test/scala/ahlers/tree/path/filterOperators/diffx/instances.scala index 6f8cb91..7f89346 100644 --- a/src/test/scala/ahlers/tree/path/filterOperators/diffx/instances.scala +++ b/src/test/scala/ahlers/tree/path/filterOperators/diffx/instances.scala @@ -1,15 +1,25 @@ package ahlers.tree.path.filterOperators.diffx -import ahlers.tree.path.filterOperators.{FilterOperator, IsEqualTo, IsNotEqualTo} +import ahlers.tree.path.filterOperators._ import ahlers.tree.path.filterOperators.algebra.IsFilterOperator -import ahlers.tree.path.terms.diffx.instances._ import com.softwaremill.diffx.Diff object instances { - implicit val diffIsEqual: Diff[IsEqualTo.type] = Diff.derived - - implicit val diffIsNotEqual: Diff[IsNotEqualTo.type] = Diff.derived + implicit val diffIsEqual: Diff[IsEqualTo.type] = Diff.derived + implicit val diffIsNotEqual: Diff[IsNotEqualTo.type] = Diff.derived + implicit val diffIsLessThan: Diff[IsLessThan.type] = Diff.derived + implicit val diffIsLessThanOrEqual: Diff[IsLessThanOrEqual.type] = Diff.derived + implicit val diffIsGreaterThan: Diff[IsGreaterThan.type] = Diff.derived + implicit val diffIsGreaterThanOrEqual: Diff[IsGreaterThanOrEqual.type] = Diff.derived + implicit val diffIsMatchOf: Diff[IsMatchOf.type] = Diff.derived + implicit val diffIsIn: Diff[IsIn.type] = Diff.derived + implicit val diffIsNotIn: Diff[IsNotIn.type] = Diff.derived + implicit val diffIsSubsetOf: Diff[IsSubsetOf.type] = Diff.derived + implicit val diffIsAnyOf: Diff[IsAnyOf.type] = Diff.derived + implicit val diffIsNoneOf: Diff[IsNoneOf.type] = Diff.derived + implicit val diffHasSize: Diff[HasSize.type] = Diff.derived + implicit val diffIsEmpty: Diff[IsEmpty.type] = Diff.derived implicit val diffFilterOperator: Diff[FilterOperator] = { implicit val diffIsFilterOperator: Diff[IsFilterOperator] = { diff --git a/src/test/scala/ahlers/tree/path/filterOperators/scalacheck/instances.scala b/src/test/scala/ahlers/tree/path/filterOperators/scalacheck/instances.scala index 1c694d5..a991f49 100644 --- a/src/test/scala/ahlers/tree/path/filterOperators/scalacheck/instances.scala +++ b/src/test/scala/ahlers/tree/path/filterOperators/scalacheck/instances.scala @@ -1,23 +1,42 @@ package ahlers.tree.path.filterOperators.scalacheck -import ahlers.tree.path.filterOperators.FilterOperator -import ahlers.tree.path.filterOperators.IsEqualTo -import ahlers.tree.path.filterOperators.algebra.IsFilterOperator -import ahlers.tree.path.terms.Index -import ahlers.tree.path.terms.Name -import ahlers.tree.path.terms.scalacheck.instances._ -import magnolify.scalacheck.semiauto._ +import ahlers.tree.path.filterOperators._ import org.scalacheck.Arbitrary import org.scalacheck.Arbitrary.arbitrary import org.scalacheck.Gen object instances { - implicit val arbEqual: Arbitrary[IsEqualTo.type] = Arbitrary(Gen.const(IsEqualTo)) + implicit val arbIsEqual: Arbitrary[IsEqualTo.type] = Arbitrary(Gen.const(IsEqualTo)) + implicit val arbIsNotEqual: Arbitrary[IsNotEqualTo.type] = Arbitrary(Gen.const(IsNotEqualTo)) + implicit val arbIsLessThan: Arbitrary[IsLessThan.type] = Arbitrary(Gen.const(IsLessThan)) + implicit val arbIsLessThanOrEqual: Arbitrary[IsLessThanOrEqual.type] = Arbitrary(Gen.const(IsLessThanOrEqual)) + implicit val arbIsGreaterThan: Arbitrary[IsGreaterThan.type] = Arbitrary(Gen.const(IsGreaterThan)) + implicit val arbIsGreaterThanOrEqual: Arbitrary[IsGreaterThanOrEqual.type] = Arbitrary(Gen.const(IsGreaterThanOrEqual)) + implicit val arbIsMatchOf: Arbitrary[IsMatchOf.type] = Arbitrary(Gen.const(IsMatchOf)) + implicit val arbIsIn: Arbitrary[IsIn.type] = Arbitrary(Gen.const(IsIn)) + implicit val arbIsNotIn: Arbitrary[IsNotIn.type] = Arbitrary(Gen.const(IsNotIn)) + implicit val arbIsSubsetOf: Arbitrary[IsSubsetOf.type] = Arbitrary(Gen.const(IsSubsetOf)) + implicit val arbIsAnyOf: Arbitrary[IsAnyOf.type] = Arbitrary(Gen.const(IsAnyOf)) + implicit val arbIsNoneOf: Arbitrary[IsNoneOf.type] = Arbitrary(Gen.const(IsNoneOf)) + implicit val arbHasSize: Arbitrary[HasSize.type] = Arbitrary(Gen.const(HasSize)) + implicit val arbIsEmpty: Arbitrary[IsEmpty.type] = Arbitrary(Gen.const(IsEmpty)) implicit val arbFilterOperator: Arbitrary[FilterOperator] = Arbitrary(Gen.oneOf( arbitrary[IsEqualTo.type], - arbitrary[IsEqualTo.type], + arbitrary[IsNotEqualTo.type], + arbitrary[IsLessThan.type], + arbitrary[IsLessThanOrEqual.type], + arbitrary[IsGreaterThan.type], + arbitrary[IsGreaterThanOrEqual.type], + arbitrary[IsMatchOf.type], + arbitrary[IsIn.type], + arbitrary[IsNotIn.type], + arbitrary[IsSubsetOf.type], + arbitrary[IsAnyOf.type], + arbitrary[IsNoneOf.type], + arbitrary[HasSize.type], + arbitrary[IsEmpty.type], )) } From 4d2811752f2254d7c2fa81caacf434cba4d8ee51 Mon Sep 17 00:00:00 2001 From: Michael Ahlers Date: Sun, 12 May 2024 17:53:04 -0600 Subject: [PATCH 37/40] Finish remaining parsers --- .../tree/path/parsers/filterOperator.scala | 25 +++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/src/main/scala/ahlers/tree/path/parsers/filterOperator.scala b/src/main/scala/ahlers/tree/path/parsers/filterOperator.scala index c90dbe1..38f9669 100644 --- a/src/main/scala/ahlers/tree/path/parsers/filterOperator.scala +++ b/src/main/scala/ahlers/tree/path/parsers/filterOperator.scala @@ -1,13 +1,34 @@ package ahlers.tree.path.parsers +import ahlers.tree.path.filterOperators.HasSize +import ahlers.tree.path.filterOperators.IsAnyOf import ahlers.tree.path.filterOperators.IsEqualTo +import ahlers.tree.path.filterOperators.IsIn +import ahlers.tree.path.filterOperators.IsLessThan +import ahlers.tree.path.filterOperators.IsMatchOf +import ahlers.tree.path.filterOperators.IsNoneOf import ahlers.tree.path.filterOperators.IsNotEqualTo +import ahlers.tree.path.filterOperators.IsNotIn +import ahlers.tree.path.filterOperators.IsSubsetOf import parsley.Parsley +import parsley.character.char import parsley.character.string object filterOperator { - val isEqual: Parsley[IsEqualTo.type] = string("==").as(IsEqualTo) - val isNotEqual: Parsley[IsNotEqualTo.type] = string("!=").as(IsNotEqualTo) + val isEqual: Parsley[IsEqualTo.type] = string("==").as(IsEqualTo) + val isNotEqual: Parsley[IsNotEqualTo.type] = string("!=").as(IsNotEqualTo) + val isLessThan: Parsley[IsLessThan.type] = char('<').as(IsLessThan) + val isLessThanOrEqual: Parsley[IsLessThan.type] = string("<=").as(IsLessThan) + val isGreaterThan: Parsley[IsLessThan.type] = char('>').as(IsLessThan) + val isGreaterThanOrEqual: Parsley[IsLessThan.type] = string(">=").as(IsLessThan) + val isMatchOf: Parsley[IsMatchOf.type] = string("=~").as(IsMatchOf) + val isIn: Parsley[IsIn.type] = string("in").as(IsIn) + val isNotIn: Parsley[IsNotIn.type] = string("nin").as(IsNotIn) + val isSubsetOf: Parsley[IsSubsetOf.type] = string("subsetof").as(IsSubsetOf) + val isAnyOf: Parsley[IsAnyOf.type] = string("anyof").as(IsAnyOf) + val isNoneOf: Parsley[IsNoneOf.type] = string("noneof").as(IsNoneOf) + val hasSize: Parsley[HasSize.type] = string("size").as(HasSize) + val isEmpty: Parsley[IsEqualTo.type] = string("empty").as(IsEqualTo) } From 66397224020140ab72af8be9cb156c0af382837b Mon Sep 17 00:00:00 2001 From: Michael Ahlers Date: Sun, 12 May 2024 17:58:21 -0600 Subject: [PATCH 38/40] Make naming consistent --- ...ual.scala => IsGreaterThanOrEqualTo.scala} | 2 +- ...rEqual.scala => IsLessThanOrEqualTo.scala} | 2 +- .../tree/path/filterOperators/algebra.scala | 22 ++++++++++++++----- .../filterOperators/diffx/instances.scala | 4 ++-- .../scalacheck/instances.scala | 8 +++---- 5 files changed, 25 insertions(+), 13 deletions(-) rename src/main/scala/ahlers/tree/path/filterOperators/{IsGreaterThanOrEqual.scala => IsGreaterThanOrEqualTo.scala} (59%) rename src/main/scala/ahlers/tree/path/filterOperators/{IsLessThanOrEqual.scala => IsLessThanOrEqualTo.scala} (60%) diff --git a/src/main/scala/ahlers/tree/path/filterOperators/IsGreaterThanOrEqual.scala b/src/main/scala/ahlers/tree/path/filterOperators/IsGreaterThanOrEqualTo.scala similarity index 59% rename from src/main/scala/ahlers/tree/path/filterOperators/IsGreaterThanOrEqual.scala rename to src/main/scala/ahlers/tree/path/filterOperators/IsGreaterThanOrEqualTo.scala index fd6ccdd..9131e60 100644 --- a/src/main/scala/ahlers/tree/path/filterOperators/IsGreaterThanOrEqual.scala +++ b/src/main/scala/ahlers/tree/path/filterOperators/IsGreaterThanOrEqualTo.scala @@ -1,5 +1,5 @@ package ahlers.tree.path.filterOperators -object IsGreaterThanOrEqual extends FilterOperator { +object IsGreaterThanOrEqualTo extends FilterOperator { override val toText: String = ">=" } diff --git a/src/main/scala/ahlers/tree/path/filterOperators/IsLessThanOrEqual.scala b/src/main/scala/ahlers/tree/path/filterOperators/IsLessThanOrEqualTo.scala similarity index 60% rename from src/main/scala/ahlers/tree/path/filterOperators/IsLessThanOrEqual.scala rename to src/main/scala/ahlers/tree/path/filterOperators/IsLessThanOrEqualTo.scala index 7eea6b2..6d6d49a 100644 --- a/src/main/scala/ahlers/tree/path/filterOperators/IsLessThanOrEqual.scala +++ b/src/main/scala/ahlers/tree/path/filterOperators/IsLessThanOrEqualTo.scala @@ -1,5 +1,5 @@ package ahlers.tree.path.filterOperators -object IsLessThanOrEqual extends FilterOperator { +object IsLessThanOrEqualTo extends FilterOperator { override val toText: String = "<=" } diff --git a/src/test/scala/ahlers/tree/path/filterOperators/algebra.scala b/src/test/scala/ahlers/tree/path/filterOperators/algebra.scala index 7533ef1..0c735fb 100644 --- a/src/test/scala/ahlers/tree/path/filterOperators/algebra.scala +++ b/src/test/scala/ahlers/tree/path/filterOperators/algebra.scala @@ -7,9 +7,9 @@ private object algebra { case object OfIsEqual extends IsFilterOperator(IsEqualTo) case object OfIsNotEqual extends IsFilterOperator(IsNotEqualTo) case object OfIsLessThan extends IsFilterOperator(IsLessThan) - case object OfIsLessThanOrEqual extends IsFilterOperator(IsLessThanOrEqual) + case object OfIsLessThanOrEqualTo extends IsFilterOperator(IsLessThanOrEqualTo) case object OfIsGreaterThan extends IsFilterOperator(IsGreaterThan) - case object OfIsGreaterThanOrEqual extends IsFilterOperator(IsGreaterThanOrEqual) + case object OfIsGreaterThanOrEqualTo extends IsFilterOperator(IsGreaterThanOrEqualTo) case object OfIsMatchOf extends IsFilterOperator(IsMatchOf) case object OfIsIn extends IsFilterOperator(IsIn) case object OfIsNotIn extends IsFilterOperator(IsNotIn) @@ -21,9 +21,21 @@ private object algebra { case class OfUnknown(override val toFilterOperator: FilterOperator) extends IsFilterOperator(toFilterOperator) def apply(operator: FilterOperator): IsFilterOperator = operator match { - case IsEqualTo => OfIsEqual - case IsNotEqualTo => OfIsNotEqual - case operator => OfUnknown(operator) + case IsEqualTo => OfIsEqual + case IsNotEqualTo => OfIsNotEqual + case IsLessThan => OfIsLessThan + case IsLessThanOrEqualTo => OfIsLessThanOrEqualTo + case IsGreaterThan => OfIsGreaterThan + case IsGreaterThanOrEqualTo => OfIsGreaterThanOrEqualTo + case IsMatchOf => OfIsMatchOf + case IsIn => OfIsIn + case IsNotIn => OfIsNotIn + case IsSubsetOf => OfIsSubsetOf + case IsAnyOf => OfIsAnyOf + case IsNoneOf => OfIsNoneOf + case HasSize => OfHasSize + case IsEmpty => OfIsEmpty + case operator => OfUnknown(operator) } } diff --git a/src/test/scala/ahlers/tree/path/filterOperators/diffx/instances.scala b/src/test/scala/ahlers/tree/path/filterOperators/diffx/instances.scala index 7f89346..6500528 100644 --- a/src/test/scala/ahlers/tree/path/filterOperators/diffx/instances.scala +++ b/src/test/scala/ahlers/tree/path/filterOperators/diffx/instances.scala @@ -9,9 +9,9 @@ object instances { implicit val diffIsEqual: Diff[IsEqualTo.type] = Diff.derived implicit val diffIsNotEqual: Diff[IsNotEqualTo.type] = Diff.derived implicit val diffIsLessThan: Diff[IsLessThan.type] = Diff.derived - implicit val diffIsLessThanOrEqual: Diff[IsLessThanOrEqual.type] = Diff.derived + implicit val diffIsLessThanOrEqualTo: Diff[IsLessThanOrEqualTo.type] = Diff.derived implicit val diffIsGreaterThan: Diff[IsGreaterThan.type] = Diff.derived - implicit val diffIsGreaterThanOrEqual: Diff[IsGreaterThanOrEqual.type] = Diff.derived + implicit val diffIsGreaterThanOrEqualTo: Diff[IsGreaterThanOrEqualTo.type] = Diff.derived implicit val diffIsMatchOf: Diff[IsMatchOf.type] = Diff.derived implicit val diffIsIn: Diff[IsIn.type] = Diff.derived implicit val diffIsNotIn: Diff[IsNotIn.type] = Diff.derived diff --git a/src/test/scala/ahlers/tree/path/filterOperators/scalacheck/instances.scala b/src/test/scala/ahlers/tree/path/filterOperators/scalacheck/instances.scala index a991f49..a324419 100644 --- a/src/test/scala/ahlers/tree/path/filterOperators/scalacheck/instances.scala +++ b/src/test/scala/ahlers/tree/path/filterOperators/scalacheck/instances.scala @@ -10,9 +10,9 @@ object instances { implicit val arbIsEqual: Arbitrary[IsEqualTo.type] = Arbitrary(Gen.const(IsEqualTo)) implicit val arbIsNotEqual: Arbitrary[IsNotEqualTo.type] = Arbitrary(Gen.const(IsNotEqualTo)) implicit val arbIsLessThan: Arbitrary[IsLessThan.type] = Arbitrary(Gen.const(IsLessThan)) - implicit val arbIsLessThanOrEqual: Arbitrary[IsLessThanOrEqual.type] = Arbitrary(Gen.const(IsLessThanOrEqual)) + implicit val arbIsLessThanOrEqualTo: Arbitrary[IsLessThanOrEqualTo.type] = Arbitrary(Gen.const(IsLessThanOrEqualTo)) implicit val arbIsGreaterThan: Arbitrary[IsGreaterThan.type] = Arbitrary(Gen.const(IsGreaterThan)) - implicit val arbIsGreaterThanOrEqual: Arbitrary[IsGreaterThanOrEqual.type] = Arbitrary(Gen.const(IsGreaterThanOrEqual)) + implicit val arbIsGreaterThanOrEqualTo: Arbitrary[IsGreaterThanOrEqualTo.type] = Arbitrary(Gen.const(IsGreaterThanOrEqualTo)) implicit val arbIsMatchOf: Arbitrary[IsMatchOf.type] = Arbitrary(Gen.const(IsMatchOf)) implicit val arbIsIn: Arbitrary[IsIn.type] = Arbitrary(Gen.const(IsIn)) implicit val arbIsNotIn: Arbitrary[IsNotIn.type] = Arbitrary(Gen.const(IsNotIn)) @@ -26,9 +26,9 @@ object instances { arbitrary[IsEqualTo.type], arbitrary[IsNotEqualTo.type], arbitrary[IsLessThan.type], - arbitrary[IsLessThanOrEqual.type], + arbitrary[IsLessThanOrEqualTo.type], arbitrary[IsGreaterThan.type], - arbitrary[IsGreaterThanOrEqual.type], + arbitrary[IsGreaterThanOrEqualTo.type], arbitrary[IsMatchOf.type], arbitrary[IsIn.type], arbitrary[IsNotIn.type], From 583a40cc4bbf190308d2ae29d978d2f9dfe313e6 Mon Sep 17 00:00:00 2001 From: Michael Ahlers Date: Sun, 12 May 2024 18:01:58 -0600 Subject: [PATCH 39/40] Fix mismatches; finish parsers and tests --- .../tree/path/parsers/filterOperator.scala | 32 +-- .../filterOperators/diffx/instances.scala | 24 +- .../scalacheck/instances.scala | 24 +- .../path/parsers/FilterOperatorSpec.scala | 236 +++++++++++++++++- 4 files changed, 274 insertions(+), 42 deletions(-) diff --git a/src/main/scala/ahlers/tree/path/parsers/filterOperator.scala b/src/main/scala/ahlers/tree/path/parsers/filterOperator.scala index 38f9669..e9d254d 100644 --- a/src/main/scala/ahlers/tree/path/parsers/filterOperator.scala +++ b/src/main/scala/ahlers/tree/path/parsers/filterOperator.scala @@ -2,9 +2,13 @@ package ahlers.tree.path.parsers import ahlers.tree.path.filterOperators.HasSize import ahlers.tree.path.filterOperators.IsAnyOf +import ahlers.tree.path.filterOperators.IsEmpty import ahlers.tree.path.filterOperators.IsEqualTo +import ahlers.tree.path.filterOperators.IsGreaterThan +import ahlers.tree.path.filterOperators.IsGreaterThanOrEqualTo import ahlers.tree.path.filterOperators.IsIn import ahlers.tree.path.filterOperators.IsLessThan +import ahlers.tree.path.filterOperators.IsLessThanOrEqualTo import ahlers.tree.path.filterOperators.IsMatchOf import ahlers.tree.path.filterOperators.IsNoneOf import ahlers.tree.path.filterOperators.IsNotEqualTo @@ -16,19 +20,19 @@ import parsley.character.string object filterOperator { - val isEqual: Parsley[IsEqualTo.type] = string("==").as(IsEqualTo) - val isNotEqual: Parsley[IsNotEqualTo.type] = string("!=").as(IsNotEqualTo) - val isLessThan: Parsley[IsLessThan.type] = char('<').as(IsLessThan) - val isLessThanOrEqual: Parsley[IsLessThan.type] = string("<=").as(IsLessThan) - val isGreaterThan: Parsley[IsLessThan.type] = char('>').as(IsLessThan) - val isGreaterThanOrEqual: Parsley[IsLessThan.type] = string(">=").as(IsLessThan) - val isMatchOf: Parsley[IsMatchOf.type] = string("=~").as(IsMatchOf) - val isIn: Parsley[IsIn.type] = string("in").as(IsIn) - val isNotIn: Parsley[IsNotIn.type] = string("nin").as(IsNotIn) - val isSubsetOf: Parsley[IsSubsetOf.type] = string("subsetof").as(IsSubsetOf) - val isAnyOf: Parsley[IsAnyOf.type] = string("anyof").as(IsAnyOf) - val isNoneOf: Parsley[IsNoneOf.type] = string("noneof").as(IsNoneOf) - val hasSize: Parsley[HasSize.type] = string("size").as(HasSize) - val isEmpty: Parsley[IsEqualTo.type] = string("empty").as(IsEqualTo) + val isEqualTo: Parsley[IsEqualTo.type] = string("==").as(IsEqualTo) + val isNotEqualTo: Parsley[IsNotEqualTo.type] = string("!=").as(IsNotEqualTo) + val isLessThan: Parsley[IsLessThan.type] = char('<').as(IsLessThan) + val isLessThanOrEqualTo: Parsley[IsLessThanOrEqualTo.type] = string("<=").as(IsLessThanOrEqualTo) + val isGreaterThan: Parsley[IsGreaterThan.type] = char('>').as(IsGreaterThan) + val isGreaterThanOrEqualTo: Parsley[IsGreaterThanOrEqualTo.type] = string(">=").as(IsGreaterThanOrEqualTo) + val isMatchOf: Parsley[IsMatchOf.type] = string("=~").as(IsMatchOf) + val isIn: Parsley[IsIn.type] = string("in").as(IsIn) + val isNotIn: Parsley[IsNotIn.type] = string("nin").as(IsNotIn) + val isSubsetOf: Parsley[IsSubsetOf.type] = string("subsetof").as(IsSubsetOf) + val isAnyOf: Parsley[IsAnyOf.type] = string("anyof").as(IsAnyOf) + val isNoneOf: Parsley[IsNoneOf.type] = string("noneof").as(IsNoneOf) + val hasSize: Parsley[HasSize.type] = string("size").as(HasSize) + val isEmpty: Parsley[IsEmpty.type] = string("empty").as(IsEmpty) } diff --git a/src/test/scala/ahlers/tree/path/filterOperators/diffx/instances.scala b/src/test/scala/ahlers/tree/path/filterOperators/diffx/instances.scala index 6500528..f8aa147 100644 --- a/src/test/scala/ahlers/tree/path/filterOperators/diffx/instances.scala +++ b/src/test/scala/ahlers/tree/path/filterOperators/diffx/instances.scala @@ -6,20 +6,20 @@ import com.softwaremill.diffx.Diff object instances { - implicit val diffIsEqual: Diff[IsEqualTo.type] = Diff.derived - implicit val diffIsNotEqual: Diff[IsNotEqualTo.type] = Diff.derived - implicit val diffIsLessThan: Diff[IsLessThan.type] = Diff.derived + implicit val diffIsEqual: Diff[IsEqualTo.type] = Diff.derived + implicit val diffIsNotEqual: Diff[IsNotEqualTo.type] = Diff.derived + implicit val diffIsLessThan: Diff[IsLessThan.type] = Diff.derived implicit val diffIsLessThanOrEqualTo: Diff[IsLessThanOrEqualTo.type] = Diff.derived - implicit val diffIsGreaterThan: Diff[IsGreaterThan.type] = Diff.derived + implicit val diffIsGreaterThan: Diff[IsGreaterThan.type] = Diff.derived implicit val diffIsGreaterThanOrEqualTo: Diff[IsGreaterThanOrEqualTo.type] = Diff.derived - implicit val diffIsMatchOf: Diff[IsMatchOf.type] = Diff.derived - implicit val diffIsIn: Diff[IsIn.type] = Diff.derived - implicit val diffIsNotIn: Diff[IsNotIn.type] = Diff.derived - implicit val diffIsSubsetOf: Diff[IsSubsetOf.type] = Diff.derived - implicit val diffIsAnyOf: Diff[IsAnyOf.type] = Diff.derived - implicit val diffIsNoneOf: Diff[IsNoneOf.type] = Diff.derived - implicit val diffHasSize: Diff[HasSize.type] = Diff.derived - implicit val diffIsEmpty: Diff[IsEmpty.type] = Diff.derived + implicit val diffIsMatchOf: Diff[IsMatchOf.type] = Diff.derived + implicit val diffIsIn: Diff[IsIn.type] = Diff.derived + implicit val diffIsNotIn: Diff[IsNotIn.type] = Diff.derived + implicit val diffIsSubsetOf: Diff[IsSubsetOf.type] = Diff.derived + implicit val diffIsAnyOf: Diff[IsAnyOf.type] = Diff.derived + implicit val diffIsNoneOf: Diff[IsNoneOf.type] = Diff.derived + implicit val diffHasSize: Diff[HasSize.type] = Diff.derived + implicit val diffIsEmpty: Diff[IsEmpty.type] = Diff.derived implicit val diffFilterOperator: Diff[FilterOperator] = { implicit val diffIsFilterOperator: Diff[IsFilterOperator] = { diff --git a/src/test/scala/ahlers/tree/path/filterOperators/scalacheck/instances.scala b/src/test/scala/ahlers/tree/path/filterOperators/scalacheck/instances.scala index a324419..ff38cf0 100644 --- a/src/test/scala/ahlers/tree/path/filterOperators/scalacheck/instances.scala +++ b/src/test/scala/ahlers/tree/path/filterOperators/scalacheck/instances.scala @@ -7,20 +7,20 @@ import org.scalacheck.Gen object instances { - implicit val arbIsEqual: Arbitrary[IsEqualTo.type] = Arbitrary(Gen.const(IsEqualTo)) - implicit val arbIsNotEqual: Arbitrary[IsNotEqualTo.type] = Arbitrary(Gen.const(IsNotEqualTo)) - implicit val arbIsLessThan: Arbitrary[IsLessThan.type] = Arbitrary(Gen.const(IsLessThan)) + implicit val arbIsEqual: Arbitrary[IsEqualTo.type] = Arbitrary(Gen.const(IsEqualTo)) + implicit val arbIsNotEqual: Arbitrary[IsNotEqualTo.type] = Arbitrary(Gen.const(IsNotEqualTo)) + implicit val arbIsLessThan: Arbitrary[IsLessThan.type] = Arbitrary(Gen.const(IsLessThan)) implicit val arbIsLessThanOrEqualTo: Arbitrary[IsLessThanOrEqualTo.type] = Arbitrary(Gen.const(IsLessThanOrEqualTo)) - implicit val arbIsGreaterThan: Arbitrary[IsGreaterThan.type] = Arbitrary(Gen.const(IsGreaterThan)) + implicit val arbIsGreaterThan: Arbitrary[IsGreaterThan.type] = Arbitrary(Gen.const(IsGreaterThan)) implicit val arbIsGreaterThanOrEqualTo: Arbitrary[IsGreaterThanOrEqualTo.type] = Arbitrary(Gen.const(IsGreaterThanOrEqualTo)) - implicit val arbIsMatchOf: Arbitrary[IsMatchOf.type] = Arbitrary(Gen.const(IsMatchOf)) - implicit val arbIsIn: Arbitrary[IsIn.type] = Arbitrary(Gen.const(IsIn)) - implicit val arbIsNotIn: Arbitrary[IsNotIn.type] = Arbitrary(Gen.const(IsNotIn)) - implicit val arbIsSubsetOf: Arbitrary[IsSubsetOf.type] = Arbitrary(Gen.const(IsSubsetOf)) - implicit val arbIsAnyOf: Arbitrary[IsAnyOf.type] = Arbitrary(Gen.const(IsAnyOf)) - implicit val arbIsNoneOf: Arbitrary[IsNoneOf.type] = Arbitrary(Gen.const(IsNoneOf)) - implicit val arbHasSize: Arbitrary[HasSize.type] = Arbitrary(Gen.const(HasSize)) - implicit val arbIsEmpty: Arbitrary[IsEmpty.type] = Arbitrary(Gen.const(IsEmpty)) + implicit val arbIsMatchOf: Arbitrary[IsMatchOf.type] = Arbitrary(Gen.const(IsMatchOf)) + implicit val arbIsIn: Arbitrary[IsIn.type] = Arbitrary(Gen.const(IsIn)) + implicit val arbIsNotIn: Arbitrary[IsNotIn.type] = Arbitrary(Gen.const(IsNotIn)) + implicit val arbIsSubsetOf: Arbitrary[IsSubsetOf.type] = Arbitrary(Gen.const(IsSubsetOf)) + implicit val arbIsAnyOf: Arbitrary[IsAnyOf.type] = Arbitrary(Gen.const(IsAnyOf)) + implicit val arbIsNoneOf: Arbitrary[IsNoneOf.type] = Arbitrary(Gen.const(IsNoneOf)) + implicit val arbHasSize: Arbitrary[HasSize.type] = Arbitrary(Gen.const(HasSize)) + implicit val arbIsEmpty: Arbitrary[IsEmpty.type] = Arbitrary(Gen.const(IsEmpty)) implicit val arbFilterOperator: Arbitrary[FilterOperator] = Arbitrary(Gen.oneOf( arbitrary[IsEqualTo.type], diff --git a/src/test/scala/ahlers/tree/path/parsers/FilterOperatorSpec.scala b/src/test/scala/ahlers/tree/path/parsers/FilterOperatorSpec.scala index baad4c8..289b87f 100644 --- a/src/test/scala/ahlers/tree/path/parsers/FilterOperatorSpec.scala +++ b/src/test/scala/ahlers/tree/path/parsers/FilterOperatorSpec.scala @@ -1,7 +1,19 @@ package ahlers.tree.path.parsers +import ahlers.tree.path.filterOperators.HasSize +import ahlers.tree.path.filterOperators.IsAnyOf +import ahlers.tree.path.filterOperators.IsEmpty import ahlers.tree.path.filterOperators.IsEqualTo +import ahlers.tree.path.filterOperators.IsGreaterThan +import ahlers.tree.path.filterOperators.IsGreaterThanOrEqualTo +import ahlers.tree.path.filterOperators.IsIn +import ahlers.tree.path.filterOperators.IsLessThan +import ahlers.tree.path.filterOperators.IsLessThanOrEqualTo +import ahlers.tree.path.filterOperators.IsMatchOf +import ahlers.tree.path.filterOperators.IsNoneOf import ahlers.tree.path.filterOperators.IsNotEqualTo +import ahlers.tree.path.filterOperators.IsNotIn +import ahlers.tree.path.filterOperators.IsSubsetOf import ahlers.tree.path.filterOperators.diffx.instances._ import ahlers.tree.path.filterOperators.scalacheck.instances._ import com.softwaremill.diffx.scalatest.DiffShouldMatcher._ @@ -14,8 +26,8 @@ import parsley.diffx.instances._ class FilterOperatorSpec extends AnyWordSpec { - "IsEqual" should { - val parser = filterOperator.isEqual + "IsEqualTo" should { + val parser = filterOperator.isEqualTo val pattern = "^==$".r s"""accept $pattern""" in { @@ -32,8 +44,8 @@ class FilterOperatorSpec extends AnyWordSpec { } } - "IsNotEqual" should { - val parser = filterOperator.isNotEqual + "IsNotEqualTo" should { + val parser = filterOperator.isNotEqualTo val pattern = "^!=$".r s"""accept $pattern""" in { @@ -50,4 +62,220 @@ class FilterOperatorSpec extends AnyWordSpec { } } + "IsLessThan" should { + val parser = filterOperator.isLessThan + val pattern = "^<$".r + + s"""accept $pattern""" in { + val filterOperator = IsLessThan + parser.parse(filterOperator.toText).shouldMatchTo(Success(filterOperator)) + } + + "reject else" in { + forAll { input: String => + whenever(!pattern.matches(input)) { + parser.parse(input).shouldBe(a[Failure[_]]) + } + } + } + } + + "IsLessThanOrEqualTo" should { + val parser = filterOperator.isLessThanOrEqualTo + val pattern = "^<=$".r + + s"""accept $pattern""" in { + val filterOperator = IsLessThanOrEqualTo + parser.parse(filterOperator.toText).shouldMatchTo(Success(filterOperator)) + } + + "reject else" in { + forAll { input: String => + whenever(!pattern.matches(input)) { + parser.parse(input).shouldBe(a[Failure[_]]) + } + } + } + } + + "IsGreaterThan" should { + val parser = filterOperator.isGreaterThan + val pattern = "^>$".r + + s"""accept $pattern""" in { + val filterOperator = IsGreaterThan + parser.parse(filterOperator.toText).shouldMatchTo(Success(filterOperator)) + } + + "reject else" in { + forAll { input: String => + whenever(!pattern.matches(input)) { + parser.parse(input).shouldBe(a[Failure[_]]) + } + } + } + } + + "IsGreaterThanOrEqualTo" should { + val parser = filterOperator.isGreaterThanOrEqualTo + val pattern = "^>=$".r + + s"""accept $pattern""" in { + val filterOperator = IsGreaterThanOrEqualTo + parser.parse(filterOperator.toText).shouldMatchTo(Success(filterOperator)) + } + + "reject else" in { + forAll { input: String => + whenever(!pattern.matches(input)) { + parser.parse(input).shouldBe(a[Failure[_]]) + } + } + } + } + + "IsMatchOf" should { + val parser = filterOperator.isMatchOf + val pattern = "^~=$".r + + s"""accept $pattern""" in { + val filterOperator = IsMatchOf + parser.parse(filterOperator.toText).shouldMatchTo(Success(filterOperator)) + } + + "reject else" in { + forAll { input: String => + whenever(!pattern.matches(input)) { + parser.parse(input).shouldBe(a[Failure[_]]) + } + } + } + } + + "IsIn" should { + val parser = filterOperator.isIn + val pattern = "^in$".r + + s"""accept $pattern""" in { + val filterOperator = IsIn + parser.parse(filterOperator.toText).shouldMatchTo(Success(filterOperator)) + } + + "reject else" in { + forAll { input: String => + whenever(!pattern.matches(input)) { + parser.parse(input).shouldBe(a[Failure[_]]) + } + } + } + } + + "IsNotIn" should { + val parser = filterOperator.isNotIn + val pattern = "^nin$".r + + s"""accept $pattern""" in { + val filterOperator = IsNotIn + parser.parse(filterOperator.toText).shouldMatchTo(Success(filterOperator)) + } + + "reject else" in { + forAll { input: String => + whenever(!pattern.matches(input)) { + parser.parse(input).shouldBe(a[Failure[_]]) + } + } + } + } + + "IsSubsetOf" should { + val parser = filterOperator.isSubsetOf + val pattern = "^subsetof$".r + + s"""accept $pattern""" in { + val filterOperator = IsSubsetOf + parser.parse(filterOperator.toText).shouldMatchTo(Success(filterOperator)) + } + + "reject else" in { + forAll { input: String => + whenever(!pattern.matches(input)) { + parser.parse(input).shouldBe(a[Failure[_]]) + } + } + } + } + + "IsAnyOf" should { + val parser = filterOperator.isAnyOf + val pattern = "^anyof$".r + + s"""accept $pattern""" in { + val filterOperator = IsAnyOf + parser.parse(filterOperator.toText).shouldMatchTo(Success(filterOperator)) + } + + "reject else" in { + forAll { input: String => + whenever(!pattern.matches(input)) { + parser.parse(input).shouldBe(a[Failure[_]]) + } + } + } + } + + "IsNoneOf" should { + val parser = filterOperator.isNoneOf + val pattern = "^noneof$".r + + s"""accept $pattern""" in { + val filterOperator = IsNoneOf + parser.parse(filterOperator.toText).shouldMatchTo(Success(filterOperator)) + } + + "reject else" in { + forAll { input: String => + whenever(!pattern.matches(input)) { + parser.parse(input).shouldBe(a[Failure[_]]) + } + } + } + } + + "HasSize" should { + val parser = filterOperator.hasSize + val pattern = "^size$".r + + s"""accept $pattern""" in { + val filterOperator = HasSize + parser.parse(filterOperator.toText).shouldMatchTo(Success(filterOperator)) + } + + "reject else" in { + forAll { input: String => + whenever(!pattern.matches(input)) { + parser.parse(input).shouldBe(a[Failure[_]]) + } + } + } + } + + "IsEmpty" should { + val parser = filterOperator.isEmpty + val pattern = "^empty$".r + + s"""accept $pattern""" in { + val filterOperator = IsEmpty + parser.parse(filterOperator.toText).shouldMatchTo(Success(filterOperator)) + } + + "reject else" in { + forAll { input: String => + whenever(!pattern.matches(input)) { + parser.parse(input).shouldBe(a[Failure[_]]) + } + } + } + } + } From ed162124d5c01d1d6e7ad92c3e215e95a6cdf7b2 Mon Sep 17 00:00:00 2001 From: Michael Ahlers Date: Sun, 12 May 2024 18:02:39 -0600 Subject: [PATCH 40/40] Fix term --- .../scala/ahlers/tree/path/filterOperators/IsSubsetOf.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/scala/ahlers/tree/path/filterOperators/IsSubsetOf.scala b/src/main/scala/ahlers/tree/path/filterOperators/IsSubsetOf.scala index ee3ba39..bd246e0 100644 --- a/src/main/scala/ahlers/tree/path/filterOperators/IsSubsetOf.scala +++ b/src/main/scala/ahlers/tree/path/filterOperators/IsSubsetOf.scala @@ -1,5 +1,5 @@ package ahlers.tree.path.filterOperators object IsSubsetOf extends FilterOperator { - override val toText: String = "subset" + override val toText: String = "subsetof" }