Skip to content

Commit

Permalink
Merge pull request #533 from gnieh/json/jq/fix-epsilon
Browse files Browse the repository at this point in the history
Fix default value
  • Loading branch information
satabin authored Oct 15, 2023
2 parents 8a59890 + 9550fed commit 112aaa9
Show file tree
Hide file tree
Showing 9 changed files with 110 additions and 45 deletions.
35 changes: 20 additions & 15 deletions finite-state/shared/src/main/scala/fs2/data/esp/ESP.scala
Original file line number Diff line number Diff line change
Expand Up @@ -76,8 +76,8 @@ private[data] class ESP[F[_], Guard, InTag, OutTag](val init: Int,
TT: Tag2Tag[InTag, OutTag],
G: Evaluator[Guard, Tag[InTag]]): Pull[F, Nothing, Expr[Out]] =
e match {
case Expr.Call(q, d, args) =>
call(env, q, d, args, in, false)
case Expr.Call(q, d, args, next) =>
(call(env, q, d, args, in, false), step(env, next, in)).mapN(Expr.concat(_, _))
case Expr.Epsilon =>
Pull.pure(Expr.Epsilon)
case Expr.Open(o, next) =>
Expand All @@ -86,8 +86,13 @@ private[data] class ESP[F[_], Guard, InTag, OutTag](val init: Int,
step(env, next, in).map(Expr.Close(c, _))
case Expr.Leaf(v, next) =>
step(env, next, in).map(Expr.Leaf(v, _))
case Expr.Concat(e1, e2) =>
(step(env, e1, in), step(env, e2, in)).mapN(Expr.concat(_, _))
case Expr.Default(v, next) =>
step(env, next, in).map {
case Expr.Epsilon => Expr.Leaf(v, Expr.Epsilon)
case e @ Expr.Close(_, _) => Expr.Leaf(v, e)
case q @ Expr.Call(_, _, _, _) => Expr.Default(v, q)
case e => e
}
}

def eval[In, Out](env: Vector[Expr[Out]], depth: Int, in: Option[In], rhs: Rhs[OutTag])(implicit
Expand All @@ -99,7 +104,7 @@ private[data] class ESP[F[_], Guard, InTag, OutTag](val init: Int,
case Rhs.Call(q, d, params) =>
params
.traverse(eval(env, depth, in, _))
.map(Expr.Call(q, d(depth), _))
.map(Expr.Call(q, d(depth), _, Expr.Epsilon))
case Rhs.SelfCall(q, params) =>
params
.traverse(eval(env, depth, in, _))
Expand All @@ -110,6 +115,8 @@ private[data] class ESP[F[_], Guard, InTag, OutTag](val init: Int,
.liftTo[Pull[F, Nothing, *]](new ESPException(s"unknown parameter $i"))
case Rhs.Epsilon =>
Pull.pure(Expr.Epsilon)
case Rhs.Default(v) =>
Pull.pure(Expr.Default(Out.makeLeaf(v), Expr.Epsilon))
case Rhs.Tree(tag, inner) =>
eval(env, depth, in, inner)
.map(inner => Expr.Open(Out.makeOpen(tag), Expr.concat(inner, Expr.Close(Out.makeClose(tag), Expr.Epsilon))))
Expand Down Expand Up @@ -142,8 +149,12 @@ private[data] class ESP[F[_], Guard, InTag, OutTag](val init: Int,

private def squeeze[Out](e: Expr[Out]): (Expr[Out], List[Out]) =
e match {
case Expr.Call(_, _, _) => (e, Nil)
case Expr.Epsilon => (Expr.Epsilon, Nil)
case Expr.Call(_, _, _, _) => (e, Nil)
case Expr.Epsilon => (Expr.Epsilon, Nil)
case Expr.Default(v, Expr.Epsilon) => (Expr.Leaf(v, Expr.Epsilon), Nil)
case Expr.Default(v, e @ Expr.Close(_, _)) => squeeze(Expr.Leaf(v, e))
case Expr.Default(_, e @ (Expr.Open(_, _) | Expr.Leaf(_, _))) => squeeze(e)
case Expr.Default(_, _) => (e, Nil)
case Expr.Open(o, e) =>
val (e1, s1) = squeeze(e)
(e1, o :: s1)
Expand All @@ -153,19 +164,13 @@ private[data] class ESP[F[_], Guard, InTag, OutTag](val init: Int,
case Expr.Leaf(v, e) =>
val (e1, s1) = squeeze(e)
(e1, v :: s1)
case Expr.Concat(Expr.Epsilon, e2) =>
val (e21, s2) = squeeze(e2)
(e21, s2)
case Expr.Concat(e1, e2) =>
val (e11, s1) = squeeze(e1)
(Expr.concat(e11, e2), s1)
}

def squeezeAll[Out](e: Expr[Out]): (Expr[Out], List[Out]) = {
@tailrec
def loop(e: Expr[Out], acc: ListBuffer[Out]): (Expr[Out], List[Out]) =
e match {
case Expr.Epsilon | Expr.Call(_, _, _) => (e, acc.result())
case Expr.Epsilon | Expr.Call(_, _, _, _) => (e, acc.result())
case _ =>
val (e1, s) = squeeze(e)
loop(e1, acc ++= s)
Expand Down Expand Up @@ -204,7 +209,7 @@ private[data] class ESP[F[_], Guard, InTag, OutTag](val init: Int,
Out: Conversion[OutTag, Out],
TT: Tag2Tag[InTag, OutTag],
G: Evaluator[Guard, Tag[InTag]]): Pipe[F, In, Out] =
transform[In, Out](Chunk.empty, 0, _, Expr.Call(init, 0, Nil), new ListBuffer).stream
transform[In, Out](Chunk.empty, 0, _, Expr.Call(init, 0, Nil, Expr.Epsilon), new ListBuffer).stream

}

Expand Down
34 changes: 20 additions & 14 deletions finite-state/shared/src/main/scala/fs2/data/esp/Expr.scala
Original file line number Diff line number Diff line change
Expand Up @@ -22,29 +22,35 @@ import cats.syntax.show._

sealed trait Expr[+Out]
object Expr {
case class Call[Out](q: Int, depth: Int, params: List[Expr[Out]]) extends Expr[Out]
case class Call[Out](q: Int, depth: Int, params: List[Expr[Out]], next: Expr[Out]) extends Expr[Out]
case object Epsilon extends Expr[Nothing]
case class Open[Out](open: Out, next: Expr[Out]) extends Expr[Out]
case class Close[Out](close: Out, next: Expr[Out]) extends Expr[Out]
case class Leaf[Out](value: Out, next: Expr[Out]) extends Expr[Out]
case class Concat[Out](fst: Expr[Out], snd: Expr[Out]) extends Expr[Out]
case class Default[Out](v: Out, next: Expr[Out]) extends Expr[Out]

def concat[Out](e1: Expr[Out], e2: Expr[Out]): Expr[Out] =
(e1, e2) match {
case (Epsilon, _) => e2
case (_, Epsilon) => e1
case (Open(o, Epsilon), _) => Open(o, e2)
case (Close(c, Epsilon), _) => Close(c, e2)
case (Leaf(v, Epsilon), _) => Leaf(v, e2)
case (_, _) => Concat(e1, e2)
case (Epsilon, _) => e2
case (_, Epsilon) => e1
case (Call(q, d, p, Epsilon), _) => Call(q, d, p, e2)
case (Call(q, d, p, e1), _) => Call(q, d, p, concat(e1, e2))
case (Default(v, Epsilon), _) => Default(v, e2)
case (Default(v, e1), _) => Default(v, concat(e1, e2))
case (Open(o, Epsilon), _) => Open(o, e2)
case (Open(o, e1), _) => Open(o, concat(e1, e2))
case (Close(c, Epsilon), _) => Close(c, e2)
case (Close(c, e1), _) => Close(c, concat(e1, e2))
case (Leaf(v, Epsilon), _) => Leaf(v, e2)
case (Leaf(v, e1), _) => Leaf(v, concat(e1, e2))
}

implicit def show[Out: Show]: Show[Expr[Out]] = Show.show {
case Call(q, d, ps) => show"q${q}_$d(${(ps: List[Expr[Out]]).mkString_(", ")})"
case Epsilon => ""
case Open(o, next) => show"$o $next"
case Close(c, next) => show"$c $next"
case Leaf(l, next) => show"$l $next"
case Concat(e1, e2) => show"$e1 $e2"
case Call(q, d, ps, next) => show"q${q}_$d(${(ps: List[Expr[Out]]).mkString_(", ")}) $next"
case Epsilon => ""
case Open(o, next) => show"$o $next"
case Close(c, next) => show"$c $next"
case Leaf(l, next) => show"$l $next"
case Default(v, next) => show"($v)? $next"
}
}
4 changes: 4 additions & 0 deletions finite-state/shared/src/main/scala/fs2/data/esp/Rhs.scala
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@ object Rhs {
/** Empty RHS. */
case object Epsilon extends Rhs[Nothing]

/** Default RHS. Value is used only if the result would be epsilon */
case class Default[T](v: T) extends Rhs[T]

/** Builds a tree. */
case class Tree[OutTag](tag: OutTag, inner: Rhs[OutTag]) extends Rhs[OutTag]

Expand Down Expand Up @@ -73,6 +76,7 @@ object Rhs {
case ApplyToLeaf(_) => "$f(%)"
case Concat(fst, snd) => show"$fst $snd"
case Epsilon => ""
case Default(v) => show"($v)?"
}

}
Expand Down
3 changes: 3 additions & 0 deletions finite-state/shared/src/main/scala/fs2/data/mft/MFT.scala
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ sealed trait Rhs[+OutTag] {
object Rhs {
case class Call[OutTag](q: Int, x: Forest, parameters: List[Rhs[OutTag]]) extends Rhs[OutTag]
case object Epsilon extends Rhs[Nothing]
case class Default[OutTag](v: OutTag) extends Rhs[OutTag]
case class Param(n: Int) extends Rhs[Nothing]
case class Node[OutTag](tag: OutTag, children: Rhs[OutTag]) extends Rhs[OutTag]
case class CopyNode[OutTag](children: Rhs[OutTag]) extends Rhs[OutTag]
Expand All @@ -71,6 +72,7 @@ object Rhs {
case Call(q, x, Nil) => show"q$q($x)"
case Call(q, x, ps) => show"q$q($x${(ps: List[Rhs[O]]).mkString_(", ", ", ", "")})"
case Epsilon => ""
case Default(v) => show"($v)?"
case Param(i) => show"y$i"
case Node(tag, children) => show"<$tag>($children)"
case CopyNode(children) => show"%t($children)"
Expand Down Expand Up @@ -262,6 +264,7 @@ private[data] class MFT[Guard, InTag, OutTag](init: Int, val rules: Map[Int, Rul
case Rhs.Call(q, Forest.Second, params) => ERhs.Call(q, Depth.Value(1), params.map(translateRhs(_)))
case Rhs.Param(i) => ERhs.Param(i)
case Rhs.Epsilon => ERhs.Epsilon
case Rhs.Default(v) => ERhs.Default(v)
case Rhs.Node(tag, inner) => ERhs.Tree(tag, translateRhs(inner))
case Rhs.CopyNode(inner) => ERhs.CapturedTree(translateRhs(inner))
case Rhs.Leaf(v) => ERhs.Leaf(v)
Expand Down
3 changes: 3 additions & 0 deletions finite-state/shared/src/main/scala/fs2/data/mft/package.scala
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,9 @@ package object mft {
def leaf[OutTag](out: OutTag): Rhs[OutTag] =
Rhs.Leaf(out)

def default[OutTag](out: OutTag): Rhs[OutTag] =
Rhs.Default(out)

def copy: Rhs[Nothing] =
Rhs.CopyLeaf

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,10 +83,7 @@ private[fs2] abstract class QueryCompiler[InTag, OutTag, Path] {
// input is copied in the first argument
q0(any) -> qinit(x0, qcopy(x0))

def translatePath(path: Path,
default: Rhs[OutTag],
start: builder.StateBuilder,
end: builder.StateBuilder): Unit = {
def translatePath(path: Path, start: builder.StateBuilder, end: builder.StateBuilder): Unit = {
val regular = path2regular(path)
val dfa = regular.deriveDFA
// resolve transitions into patterns and guards
Expand Down Expand Up @@ -118,7 +115,6 @@ private[fs2] abstract class QueryCompiler[InTag, OutTag, Path] {
val states2 =
transitions.foldLeft(states1) { case (states, (pattern, guard, tgt)) =>
val finalTgt = dfa.finals.contains(tgt)
val trapTgt = dfa.trap.contains(tgt)
val (q2, states1) =
states.get(tgt) match {
case Some(q2) => (q2, states)
Expand All @@ -127,16 +123,14 @@ private[fs2] abstract class QueryCompiler[InTag, OutTag, Path] {
(q2, states.updated(tgt, q2))
}
val pat: builder.Guardable = tagOf(pattern).fold(anyNode)(aNode(_))
if (trapTgt) {
q1(pat.when(guard)) -> (if (default == eps) q2(x1, copyArgs: _*) ~ q1(x2, copyArgs: _*) else default)
} else if (!finalTgt) {
q1(pat.when(guard)) -> q2(x1, copyArgs: _*) ~ (if (default == eps) q1(x2, copyArgs: _*) else eps)
if (!finalTgt) {
q1(pat.when(guard)) -> q2(x1, copyArgs: _*) ~ q1(x2, copyArgs: _*)
} else if (emitSelected) {
q1(pat.when(guard)) -> end(x1, (copyArgs :+ copy(qcopy(x1))): _*) ~ q2(x1, copyArgs: _*) ~
(if (default == eps) q1(x2, copyArgs: _*) else eps)
q1(x2, copyArgs: _*)
} else {
q1(pat.when(guard)) -> end(x1, (copyArgs :+ qcopy(x1)): _*) ~ q2(x1, copyArgs: _*) ~
(if (default == eps) q1(x2, copyArgs: _*) else eps)
q1(x2, copyArgs: _*)
}
states1
}
Expand All @@ -155,7 +149,7 @@ private[fs2] abstract class QueryCompiler[InTag, OutTag, Path] {
val q1 = state(args = q.nargs + 1)

// compile the variable binding path
translatePath(source, eps, q, q1)
translatePath(source, q, q1)

// then the body with the bound variable
translate(result, variable :: vars, q1)
Expand All @@ -173,14 +167,27 @@ private[fs2] abstract class QueryCompiler[InTag, OutTag, Path] {
val copyArgs = List.tabulate(q.nargs)(y(_))
q(any) -> q1(x0, (copyArgs :+ qv(x0, copyArgs: _*)): _*)

case Query.Ordpath(path, default) =>
case Query.Ordpath(path, None) =>
val q1 = state(args = q.nargs + 1)

// compile the path
translatePath(path, default.map(leaf(_)).getOrElse(eps), q, q1)
translatePath(path, q, q1)

// emit the result
q1(any) -> y(q.nargs)

case Query.Ordpath(path, Some(dflt)) =>
val q0 = state(args = q.nargs)
val q1 = state(args = q.nargs + 1)

// compile the path
translatePath(path, q0, q1)

// emit the result
val copyArgs = List.tabulate(q.nargs)(y(_))
q(any) -> default(dflt) ~ q0(Forest.Self, copyArgs: _*)
q1(any) -> y(q.nargs)

case Query.Node(tag, child) =>
val q1 = state(args = q.nargs)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,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)
.through(esp.pipe[TaggedJson, TaggedJson])
.map(untag(_))
.unNone

Expand Down
3 changes: 2 additions & 1 deletion json/src/main/scala/fs2/data/json/tagged/JsonTagger.scala
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,11 @@ package data
package json
package tagged

import scala.collection.mutable.ListBuffer
import cats.Show
import cats.syntax.all._

import scala.collection.mutable.ListBuffer

private[json] sealed trait TaggedJson
private[json] object TaggedJson {
case object StartJson extends TaggedJson
Expand Down
36 changes: 36 additions & 0 deletions json/src/test/scala/fs2/data/json/jq/JqSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -421,4 +421,40 @@ object JqSpec extends SimpleIOSuite {
)
}

test("documentation") {
val source = json"""{
"field1": 0,
"field2": "test",
"field3": [1, 2, 3]
}""".lift[IO]
for {
compiled <- compiler.compile(jq"""[ { "field2": .field2, "field3": .field3[] } ]""")
result <- source.through(compiled).compile.toList
} yield expect.same(
List(
Token.StartArray,
Token.StartObject,
Token.Key("field2"),
Token.StringValue("test"),
Token.Key("field3"),
Token.NumberValue("1"),
Token.EndObject,
Token.StartObject,
Token.Key("field2"),
Token.StringValue("test"),
Token.Key("field3"),
Token.NumberValue("2"),
Token.EndObject,
Token.StartObject,
Token.Key("field2"),
Token.StringValue("test"),
Token.Key("field3"),
Token.NumberValue("3"),
Token.EndObject,
Token.EndArray
),
result
)
}

}

0 comments on commit 112aaa9

Please sign in to comment.