From ddc7d7e3d7bb8954d10d6fc89fc077e6f4e38ac8 Mon Sep 17 00:00:00 2001 From: Lucas Satabin Date: Sat, 6 May 2023 18:13:54 +0200 Subject: [PATCH] Make iterator work with a child axis --- .../fs2/data/mft/query/QueryCompiler.scala | 7 +++++- .../scala-2/fs2/data/json/jq/literals.scala | 1 + .../scala-3/fs2/data/json/jq/literals.scala | 1 + .../scala/fs2/data/json/jq/JqParser.scala | 10 ++++---- .../src/main/scala/fs2/data/json/jq/ast.scala | 3 ++- .../data/json/jq/internal/ESPCompiledJq.scala | 24 +------------------ .../data/json/jq/internal/ESPJqCompiler.scala | 16 ++++++++++--- 7 files changed, 30 insertions(+), 32 deletions(-) diff --git a/finite-state/shared/src/main/scala/fs2/data/mft/query/QueryCompiler.scala b/finite-state/shared/src/main/scala/fs2/data/mft/query/QueryCompiler.scala index 68f36931e..8003d6746 100644 --- a/finite-state/shared/src/main/scala/fs2/data/mft/query/QueryCompiler.scala +++ b/finite-state/shared/src/main/scala/fs2/data/mft/query/QueryCompiler.scala @@ -34,6 +34,8 @@ private[fs2] abstract class QueryCompiler[InTag, OutTag, Path] { type Pattern type Guard + protected val emitSelected: Boolean = true + /** A single char to be matched in a path */ type Char @@ -121,9 +123,12 @@ private[fs2] abstract class QueryCompiler[InTag, OutTag, Path] { val pat: builder.Guardable = tagOf(pattern).fold(anyNode)(aNode(_)) if (!finalTgt) { q1(pat.when(guard)) -> q2(x1, copyArgs: _*) ~ q1(x2, copyArgs: _*) - } else { + } else if (emitSelected) { q1(pat.when(guard)) -> end(x1, (copyArgs :+ copy(qcopy(x1))): _*) ~ q2(x1, copyArgs: _*) ~ q1(x2, copyArgs: _*) + } else { + q1(pat.when(guard)) -> end(x1, (copyArgs :+ qcopy(x1)): _*) ~ q2(x1, copyArgs: _*) ~ + q1(x2, copyArgs: _*) } states1 } diff --git a/json/src/main/scala-2/fs2/data/json/jq/literals.scala b/json/src/main/scala-2/fs2/data/json/jq/literals.scala index e2603c020..638cc311f 100644 --- a/json/src/main/scala-2/fs2/data/json/jq/literals.scala +++ b/json/src/main/scala-2/fs2/data/json/jq/literals.scala @@ -47,6 +47,7 @@ object literals { case Jq.Field(name) => q"_root_.fs2.data.json.jq.Jq.Field($name)" case Jq.Index(idx) => q"_root_.fs2.data.json.jq.Jq.Index($idx)" case Jq.Slice(idx1, idx2) => q"_root_.fs2.data.json.jq.Jq.Slice($idx1, $idx2)" + case Jq.Child => q"_root_.fs2.data.json.jq.Jq.Child" case Jq.RecursiveDescent => q"_root_.fs2.data.json.jq.Jq.RecursiveDescent" case Jq.Sequence(qs) => q"_root_.fs2.data.json.jq.Jq.Sequence(_root_.cats.data.NonEmptyChain.fromNonEmptyList(${qs.toNonEmptyList.widen[Jq]}))" diff --git a/json/src/main/scala-3/fs2/data/json/jq/literals.scala b/json/src/main/scala-3/fs2/data/json/jq/literals.scala index 8e7cf109e..ab3bfcb45 100644 --- a/json/src/main/scala-3/fs2/data/json/jq/literals.scala +++ b/json/src/main/scala-3/fs2/data/json/jq/literals.scala @@ -49,6 +49,7 @@ package object literals { case Jq.Field(name) => '{ Jq.Field(${ Expr(name) }) } case Jq.Index(idx) => '{ Jq.Index(${ Expr(idx) }) } case Jq.Slice(idx1, idx2) => '{ Jq.Slice(${ Expr(idx1) }, ${ Expr(idx2) }) } + case Jq.Child => '{ Jq.Child } case Jq.RecursiveDescent => '{ Jq.RecursiveDescent } case Jq.Sequence(qs) => '{ Jq.Sequence(NonEmptyChain.fromNonEmptyList(${ Expr(qs.toNonEmptyList) })) } diff --git a/json/src/main/scala/fs2/data/json/jq/JqParser.scala b/json/src/main/scala/fs2/data/json/jq/JqParser.scala index 1ecbf0b27..93efa9891 100644 --- a/json/src/main/scala/fs2/data/json/jq/JqParser.scala +++ b/json/src/main/scala/fs2/data/json/jq/JqParser.scala @@ -84,13 +84,15 @@ object JqParser { .withContext("string, index, slice 'min:max' (with min <= max), slice 'idx:', or slice ':idx'") val step: P[Filter] = - (P.char('.') *> P + (ch('.') *> P .oneOf( identifier.map(Jq.Field(_)) :: access.backtrack :: - Nil) ~ access.backtrack.repAs0[Filter]) - .map { case (fst, snd) => - fst ~ snd + Nil) + .? ~ access.backtrack.repAs0[Filter]) + .map { + case (Some(fst), snd) => fst ~ snd + case (None, access) => Jq.Identity ~ access } .repAs[Filter] diff --git a/json/src/main/scala/fs2/data/json/jq/ast.scala b/json/src/main/scala/fs2/data/json/jq/ast.scala index c35cb9ebb..68deaa5b6 100644 --- a/json/src/main/scala/fs2/data/json/jq/ast.scala +++ b/json/src/main/scala/fs2/data/json/jq/ast.scala @@ -49,7 +49,8 @@ object Jq { final case class Field(name: String) extends SimpleFilter final case class Index(idx: Int) extends SimpleFilter final case class Slice(start: Int, end: Option[Int]) extends SimpleFilter - final case object RecursiveDescent extends SimpleFilter + case object RecursiveDescent extends SimpleFilter + private[jq] final case object Child extends SimpleFilter final case class Sequence(jqs: NonEmptyChain[SimpleFilter]) extends Filter final case class Iterator(filter: Filter, inner: Jq) extends Jq diff --git a/json/src/main/scala/fs2/data/json/jq/internal/ESPCompiledJq.scala b/json/src/main/scala/fs2/data/json/jq/internal/ESPCompiledJq.scala index bbd69e7b6..bc9626b39 100644 --- a/json/src/main/scala/fs2/data/json/jq/internal/ESPCompiledJq.scala +++ b/json/src/main/scala/fs2/data/json/jq/internal/ESPCompiledJq.scala @@ -29,29 +29,7 @@ private[jq] class ESPCompiledJq[F[_]: RaiseThrowable](val esp: JqESP[F]) extends def apply(in: Stream[F, Token]): Stream[F, Token] = in.through(JsonTagger.pipe) .through(esp.pipe) - .mapAccumulate(0) { - case (0, TaggedJson.StartObjectValue(_)) => - (0, None) - case (depth, TaggedJson.Raw(Token.StartObject)) => - (depth + 1, Some(Token.StartObject)) - case (depth, TaggedJson.Raw(Token.EndObject)) => - (depth - 1, Some(Token.EndObject)) - case (depth, TaggedJson.Raw(t)) => - (depth, Some(t)) - case (depth, TaggedJson.StartArrayElement(_)) => - (depth, None) - case (depth, TaggedJson.EndArrayElement) => - (depth, None) - case (depth, TaggedJson.StartObjectValue(name)) => - (depth, Some(Token.Key(name))) - case (depth, TaggedJson.EndObjectValue) => - (depth, None) - case (depth, TaggedJson.StartJson) => - (depth, None) - case (depth, TaggedJson.EndJson) => - (depth, None) - } - .map(_._2) + .map(untag(_)) .unNone def andThen(that: CompiledJq[F]): CompiledJq[F] = diff --git a/json/src/main/scala/fs2/data/json/jq/internal/ESPJqCompiler.scala b/json/src/main/scala/fs2/data/json/jq/internal/ESPJqCompiler.scala index 329748fa2..326e5878b 100644 --- a/json/src/main/scala/fs2/data/json/jq/internal/ESPJqCompiler.scala +++ b/json/src/main/scala/fs2/data/json/jq/internal/ESPJqCompiler.scala @@ -32,6 +32,8 @@ private[jq] class ESPJqCompiler[F[_]](implicit F: MonadThrow[F], defer: Defer[F] extends QueryCompiler[TaggedJson, TaggedJson, Filter] with Compiler[F] { + override protected val emitSelected: Boolean = false + private type State[T] = StateT[F, Int, T] private def nextIdent: State[String] = @@ -75,6 +77,9 @@ private[jq] class ESPJqCompiler[F[_]](implicit F: MonadThrow[F], defer: Defer[F] Regular.chars[TaggedMatcher](TaggedMatcher.StartArray) ~ Regular.chars(TaggedMatcher.Slice(start, end)) case Jq.Index(idx) => Regular.chars[TaggedMatcher](TaggedMatcher.StartArray) ~ Regular.chars(TaggedMatcher.Index(idx)) + case Jq.Child => + (Regular.chars[TaggedMatcher](TaggedMatcher.StartArray) || + Regular.chars[TaggedMatcher](TaggedMatcher.StartObject)) ~ Regular.any case Jq.RecursiveDescent => ((Regular.chars[TaggedMatcher](TaggedMatcher.StartArray) || Regular.chars[TaggedMatcher](TaggedMatcher.StartObject)) ~ Regular.any).rep @@ -247,7 +252,7 @@ private[jq] class ESPJqCompiler[F[_]](implicit F: MonadThrow[F], defer: Defer[F] val forClause: Query[TaggedJson, Filter] = Query.ForClause( v, - prefix ~ prefix1 ~ filter, + prefix ~ prefix1 ~ filter ~ Jq.Child, Query.Node( TaggedJson.Raw(Token.StartArray), Query.Sequence( @@ -295,7 +300,7 @@ private[jq] class ESPJqCompiler[F[_]](implicit F: MonadThrow[F], defer: Defer[F] val forClause: Query[TaggedJson, Filter] = Query.ForClause( v, - prefix ~ prefix1 ~ filter, + prefix ~ prefix1 ~ filter ~ Jq.Child, Query.Node( TaggedJson.Raw(Token.StartObject), Query.Sequence( @@ -311,10 +316,15 @@ private[jq] class ESPJqCompiler[F[_]](implicit F: MonadThrow[F], defer: Defer[F] raiseError( JqException(s"object constructors may have only one iterator element, but got ${iterators.size}")) } - case Jq.Iterator(filter, inner) => + case Jq.Iterator(filter, inner: Constructor) => for { v <- nextIdent inner <- preprocess(Jq.Identity, inner) + } yield Query.ForClause(v, prefix ~ filter ~ Jq.Child, inner) + case Jq.Iterator(filter, inner) => + for { + v <- nextIdent + inner <- preprocess(Jq.Child, inner) } yield Query.ForClause(v, prefix ~ filter, inner) case filter: Filter => pure(Query.Ordpath(prefix ~ filter))