Skip to content

Commit

Permalink
Merge pull request #157 from Grupo-Abraxas/preserve_syntax
Browse files Browse the repository at this point in the history
Preserve syntax
  • Loading branch information
krabbit93 committed May 20, 2022
2 parents f8a2327 + 2a5c677 commit 09889bb
Show file tree
Hide file tree
Showing 5 changed files with 225 additions and 14 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,11 @@ object CypherFragment {
def apply[A](name: String): Alias[A] = new Alias(name)

final case class Fixed[+A](override val name: String) extends Alias[A](name) with CypherStatement.Alias.Fixed

final case class Preserving(expressions: Seq[Expr[_] with CypherStatement.Alias]) {
def also(others: Seq[Expr[_] with CypherStatement.Alias]): Preserving = Preserving(expressions ++ others)
def and(others: Expr[_] with CypherStatement.Alias*): Preserving = Preserving(expressions ++ others)
}
}

final case class Func[+A](func: String, params: List[Expr[_]]) extends Expr[A]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import scala.reflect.macros.blackbox

import cats.data.NonEmptyList

import com.arkondata.slothql.cypher.CypherFragment.Expr.Alias.Preserving
import com.arkondata.slothql.cypher.{ CypherFragment => CF }

class CypherSyntaxWithMacros(override val c: blackbox.Context) extends CypherSyntaxPatternMacros(c) {
Expand All @@ -13,12 +14,12 @@ class CypherSyntaxWithMacros(override val c: blackbox.Context) extends CypherSyn
def with1[T1: WeakTypeTag, R: WeakTypeTag](t1: c.Expr[CF.Expr[T1]])(
query: c.Expr[CF.Expr.Alias[T1] => CF.Query.Query0[R]]
): c.Expr[CF.Query.Query0[R]] =
withImpl(query.tree, wildcard = false, t1.tree -> weakTypeOf[T1])
withImpl(query.tree, wildcard = false, None, t1.tree -> weakTypeOf[T1])

def with2[T1: WeakTypeTag, T2: WeakTypeTag, R: WeakTypeTag](t1: c.Expr[CF.Expr[T1]], t2: c.Expr[CF.Expr[T2]])(
query: c.Expr[(CF.Expr.Alias[T1], CF.Expr.Alias[T2]) => CF.Query.Query0[R]]
): c.Expr[CF.Query.Query0[R]] =
withImpl(query.tree, wildcard = false, t1.tree -> weakTypeOf[T1], t2.tree -> weakTypeOf[T2])
withImpl(query.tree, wildcard = false, None, t1.tree -> weakTypeOf[T1], t2.tree -> weakTypeOf[T2])

def with3[T1: WeakTypeTag, T2: WeakTypeTag, T3: WeakTypeTag, R: WeakTypeTag](
t1: c.Expr[CF.Expr[T1]],
Expand All @@ -30,6 +31,7 @@ class CypherSyntaxWithMacros(override val c: blackbox.Context) extends CypherSyn
withImpl(
query.tree,
wildcard = false,
None,
t1.tree -> weakTypeOf[T1],
t2.tree -> weakTypeOf[T2],
t3.tree -> weakTypeOf[T3]
Expand All @@ -46,6 +48,7 @@ class CypherSyntaxWithMacros(override val c: blackbox.Context) extends CypherSyn
withImpl(
query.tree,
wildcard = false,
None,
t1.tree -> weakTypeOf[T1],
t2.tree -> weakTypeOf[T2],
t3.tree -> weakTypeOf[T3],
Expand All @@ -72,6 +75,7 @@ class CypherSyntaxWithMacros(override val c: blackbox.Context) extends CypherSyn
withImpl(
query.tree,
wildcard = false,
None,
t1.tree -> weakTypeOf[T1],
t2.tree -> weakTypeOf[T2],
t3.tree -> weakTypeOf[T3],
Expand All @@ -82,19 +86,24 @@ class CypherSyntaxWithMacros(override val c: blackbox.Context) extends CypherSyn
def withWild0[R: WeakTypeTag](wildcard: c.Expr[**.type])(
query: c.Expr[CF.Query.Query0[R]]
): c.Expr[CF.Query.Query0[R]] =
withImpl(query.tree, wildcard = true)
withImpl(query.tree, wildcard = true, None)

def withPreserving0[R: WeakTypeTag](preserving: c.Expr[Preserving])(
query: c.Expr[CF.Query.Query0[R]]
): c.Expr[CF.Query.Query0[R]] =
withImpl(query.tree, wildcard = false, Some(preserving))

def withWild1[T1: WeakTypeTag, R: WeakTypeTag](wildcard: c.Expr[**.type], t1: c.Expr[CF.Expr[T1]])(
query: c.Expr[CF.Expr.Alias[T1] => CF.Query.Query0[R]]
): c.Expr[CF.Query.Query0[R]] =
withImpl(query.tree, wildcard = true, t1.tree -> weakTypeOf[T1])
withImpl(query.tree, wildcard = true, None, t1.tree -> weakTypeOf[T1])

def withWild2[T1: WeakTypeTag, T2: WeakTypeTag, R: WeakTypeTag](
wildcard: c.Expr[**.type],
t1: c.Expr[CF.Expr[T1]],
t2: c.Expr[CF.Expr[T2]]
)(query: c.Expr[(CF.Expr.Alias[T1], CF.Expr.Alias[T2]) => CF.Query.Query0[R]]): c.Expr[CF.Query.Query0[R]] =
withImpl(query.tree, wildcard = true, t1.tree -> weakTypeOf[T1], t2.tree -> weakTypeOf[T2])
withImpl(query.tree, wildcard = true, None, t1.tree -> weakTypeOf[T1], t2.tree -> weakTypeOf[T2])

def withWild3[T1: WeakTypeTag, T2: WeakTypeTag, T3: WeakTypeTag, R: WeakTypeTag](
wildcard: c.Expr[**.type],
Expand All @@ -107,6 +116,7 @@ class CypherSyntaxWithMacros(override val c: blackbox.Context) extends CypherSyn
withImpl(
query.tree,
wildcard = true,
None,
t1.tree -> weakTypeOf[T1],
t2.tree -> weakTypeOf[T2],
t3.tree -> weakTypeOf[T3]
Expand All @@ -124,6 +134,7 @@ class CypherSyntaxWithMacros(override val c: blackbox.Context) extends CypherSyn
withImpl(
query.tree,
wildcard = true,
None,
t1.tree -> weakTypeOf[T1],
t2.tree -> weakTypeOf[T2],
t3.tree -> weakTypeOf[T3],
Expand Down Expand Up @@ -151,6 +162,97 @@ class CypherSyntaxWithMacros(override val c: blackbox.Context) extends CypherSyn
withImpl(
query.tree,
wildcard = true,
None,
t1.tree -> weakTypeOf[T1],
t2.tree -> weakTypeOf[T2],
t3.tree -> weakTypeOf[T3],
t4.tree -> weakTypeOf[T4],
t5.tree -> weakTypeOf[T5]
)

def withPreserving1[T1: WeakTypeTag, R: WeakTypeTag](preserving: c.Expr[Preserving], t1: c.Expr[CF.Expr[T1]])(
query: c.Expr[CF.Expr.Alias[T1] => CF.Query.Query0[R]]
): c.Expr[CF.Query.Query0[R]] =
withImpl(query.tree, wildcard = false, preserving = Some(preserving), t1.tree -> weakTypeOf[T1])

def withPreserving2[T1: WeakTypeTag, T2: WeakTypeTag, R: WeakTypeTag](
preserving: c.Expr[Preserving],
t1: c.Expr[CF.Expr[T1]],
t2: c.Expr[CF.Expr[T2]]
)(query: c.Expr[(CF.Expr.Alias[T1], CF.Expr.Alias[T2]) => CF.Query.Query0[R]]): c.Expr[CF.Query.Query0[R]] =
withImpl(
query.tree,
wildcard = false,
preserving = Some(preserving),
t1.tree -> weakTypeOf[T1],
t2.tree -> weakTypeOf[T2]
)

def withPreserving3[T1: WeakTypeTag, T2: WeakTypeTag, T3: WeakTypeTag, R: WeakTypeTag](
preserving: c.Expr[Preserving],
t1: c.Expr[CF.Expr[T1]],
t2: c.Expr[CF.Expr[T2]],
t3: c.Expr[CF.Expr[T3]]
)(
query: c.Expr[(CF.Expr.Alias[T1], CF.Expr.Alias[T2], CF.Expr.Alias[T3]) => CF.Query.Query0[R]]
): c.Expr[CF.Query.Query0[R]] =
withImpl(
query.tree,
wildcard = false,
Some(preserving),
t1.tree -> weakTypeOf[T1],
t2.tree -> weakTypeOf[T2],
t3.tree -> weakTypeOf[T3]
)

def withPreserving4[T1: WeakTypeTag, T2: WeakTypeTag, T3: WeakTypeTag, T4: WeakTypeTag, R: WeakTypeTag](
preserving: c.Expr[Preserving],
t1: c.Expr[CF.Expr[T1]],
t2: c.Expr[CF.Expr[T2]],
t3: c.Expr[CF.Expr[T3]],
t4: c.Expr[CF.Expr[T4]]
)(
query: c.Expr[(CF.Expr.Alias[T1], CF.Expr.Alias[T2], CF.Expr.Alias[T3], CF.Expr.Alias[T4]) => CF.Query.Query0[R]]
): c.Expr[CF.Query.Query0[R]] =
withImpl(
query.tree,
wildcard = false,
Some(preserving),
t1.tree -> weakTypeOf[T1],
t2.tree -> weakTypeOf[T2],
t3.tree -> weakTypeOf[T3],
t4.tree -> weakTypeOf[T4]
)

def withPreserving5[
T1: WeakTypeTag,
T2: WeakTypeTag,
T3: WeakTypeTag,
T4: WeakTypeTag,
T5: WeakTypeTag,
R: WeakTypeTag
](
preserving: c.Expr[Preserving],
t1: c.Expr[CF.Expr[T1]],
t2: c.Expr[CF.Expr[T2]],
t3: c.Expr[CF.Expr[T3]],
t4: c.Expr[CF.Expr[T4]],
t5: c.Expr[CF.Expr[T5]]
)(
query: c.Expr[
(
CF.Expr.Alias[T1],
CF.Expr.Alias[T2],
CF.Expr.Alias[T3],
CF.Expr.Alias[T4],
CF.Expr.Alias[T5]
) => CF.Query.Query0[R]
]
): c.Expr[CF.Query.Query0[R]] =
withImpl(
query.tree,
wildcard = false,
Some(preserving),
t1.tree -> weakTypeOf[T1],
t2.tree -> weakTypeOf[T2],
t3.tree -> weakTypeOf[T3],
Expand All @@ -160,7 +262,12 @@ class CypherSyntaxWithMacros(override val c: blackbox.Context) extends CypherSyn

protected type ExprWithInnerType = (Tree, Type)

protected def withImpl[R](query: Tree, wildcard: Boolean, exprs: ExprWithInnerType*): c.Expr[CF.Query.Query0[R]] = {
protected def withImpl[R](
query: Tree,
wildcard: Boolean,
preserving: Option[c.Expr[Preserving]],
exprs: ExprWithInnerType*
): c.Expr[CF.Query.Query0[R]] = {
val (args, body0) = query match {
case Function(args, body) => args -> body
case _ => Nil -> query
Expand All @@ -176,13 +283,24 @@ class CypherSyntaxWithMacros(override val c: blackbox.Context) extends CypherSyn
((nme, name), bind, ret)
}
.unzip3

val preserveValues = preserving.map { p =>
q"${p.tree}.expressions.toList.map(e => _root_.com.arkondata.slothql.cypher.CypherFragment.Return.Expr(e, None))"
}

val rebind = argNames.toMap
val return0 = (wildcard, returns) match {
case (false, Seq()) => c.abort(c.enclosingPosition, "No variables bound at WITH clause")
case (false, Seq(single)) => single
case (false, seq) => q"_root_.com.arkondata.slothql.cypher.CypherFragment.Return.Tuple(_root_.scala.List(..$seq))"
case (true, Seq()) => q"_root_.com.arkondata.slothql.cypher.CypherFragment.Return.Wildcard"
case (true, seq) =>
val return0 = (wildcard, preserveValues, returns) match {
case (false, None, Seq()) => c.abort(c.enclosingPosition, "No variables bound at WITH clause")
case (false, Some(pv), Seq()) => q"_root_.com.arkondata.slothql.cypher.CypherFragment.Return.Tuple($pv)"
case (false, None, Seq(single)) => single
case (false, Some(pv), Seq(single)) =>
q"_root_.com.arkondata.slothql.cypher.CypherFragment.Return.Tuple($pv :+ $single)"
case (false, None, seq) =>
q"_root_.com.arkondata.slothql.cypher.CypherFragment.Return.Tuple(_root_.scala.List(..$seq))"
case (false, Some(pv), seq) =>
q"_root_.com.arkondata.slothql.cypher.CypherFragment.Return.Tuple($pv ++ _root_.scala.List(..$seq))"
case (true, _, Seq()) => q"_root_.com.arkondata.slothql.cypher.CypherFragment.Return.Wildcard"
case (true, _, seq) =>
q"_root_.com.arkondata.slothql.cypher.CypherFragment.Return.WildcardTuple(_root_.scala.List(..$seq))"
}
val (ops, body1) = body0 match {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import cats.data.{ Ior, NonEmptyList }
import shapeless.{ ::, =:!=, |∨|, ops, HList, HNil, Refute, Unpack1 }

import com.arkondata.slothql.cypher.CypherFragment.Clause.Write
import com.arkondata.slothql.cypher.CypherFragment.Expr.Alias.Preserving
import com.arkondata.slothql.cypher.syntax.OnCreate.PartialCreateApply
import com.arkondata.slothql.cypher.syntax.OnMatch.PartialMatchApply
import com.arkondata.slothql.cypher.{ CypherFragment => CF }
Expand Down Expand Up @@ -108,6 +109,8 @@ package object syntax extends CypherSyntaxLowPriorityImplicits {
@compileTimeOnly("would have been replaced at With.apply")
def limit(n: CF.Expr.Input[Long]): Nothing = ???

def preserving(expressions: CF.Expr[_] with CypherStatement.Alias*): Preserving = Preserving(expressions)

def references[T1, R](n: CF.Expr[T1] with CypherStatement.Alias)(
query: => CF.Query.Query0[R]
): CF.Query.Query0[R] = CF.Query.Clause(CF.Clause.With(CF.Return.Expr(n, None), None), query)
Expand Down Expand Up @@ -136,6 +139,9 @@ package object syntax extends CypherSyntaxLowPriorityImplicits {
def apply[R](wildcard: **.type)(query: CF.Query.Query0[R]): CF.Query.Query0[R] =
macro CypherSyntaxWithMacros.withWild0[R]

def apply[R](preserving: Preserving)(query: CF.Query.Query0[R]): CF.Query.Query0[R] =
macro CypherSyntaxWithMacros.withPreserving0[R]

// 1 Expr

def apply[T1, R](t1: CF.Expr[T1])(query: CF.Expr.Alias[T1] => CF.Query.Query0[R]): CF.Query.Query0[R] =
Expand All @@ -146,6 +152,11 @@ package object syntax extends CypherSyntaxLowPriorityImplicits {
): CF.Query.Query0[R] =
macro CypherSyntaxWithMacros.withWild1[T1, R]

def apply[T1, R](preserving: Preserving, t1: CF.Expr[T1])(
query: CF.Expr.Alias[T1] => CF.Query.Query0[R]
): CF.Query.Query0[R] =
macro CypherSyntaxWithMacros.withPreserving1[T1, R]

// 2 Exprs

def apply[T1, T2, R](t1: CF.Expr[T1], t2: CF.Expr[T2])(
Expand All @@ -158,6 +169,11 @@ package object syntax extends CypherSyntaxLowPriorityImplicits {
): CF.Query.Query0[R] =
macro CypherSyntaxWithMacros.withWild2[T1, T2, R]

def apply[T1, T2, R](preserving: Preserving, t1: CF.Expr[T1], t2: CF.Expr[T2])(
query: (CF.Expr.Alias[T1], CF.Expr.Alias[T2]) => CF.Query.Query0[R]
): CF.Query.Query0[R] =
macro CypherSyntaxWithMacros.withPreserving2[T1, T2, R]

// 3 Exprs

def apply[T1, T2, T3, R](t1: CF.Expr[T1], t2: CF.Expr[T2], t3: CF.Expr[T3])(
Expand All @@ -170,6 +186,11 @@ package object syntax extends CypherSyntaxLowPriorityImplicits {
): CF.Query.Query0[R] =
macro CypherSyntaxWithMacros.withWild3[T1, T2, T3, R]

def apply[T1, T2, T3, R](preserving: Preserving, t1: CF.Expr[T1], t2: CF.Expr[T2], t3: CF.Expr[T3])(
query: (CF.Expr.Alias[T1], CF.Expr.Alias[T2], CF.Expr.Alias[T3]) => CF.Query.Query0[R]
): CF.Query.Query0[R] =
macro CypherSyntaxWithMacros.withPreserving3[T1, T2, T3, R]

// 4 Exprs

def apply[T1, T2, T3, T4, R](t1: CF.Expr[T1], t2: CF.Expr[T2], t3: CF.Expr[T3], t4: CF.Expr[T4])(
Expand All @@ -182,6 +203,17 @@ package object syntax extends CypherSyntaxLowPriorityImplicits {
): CF.Query.Query0[R] =
macro CypherSyntaxWithMacros.withWild4[T1, T2, T3, T4, R]

def apply[T1, T2, T3, T4, R](
preserving: Preserving,
t1: CF.Expr[T1],
t2: CF.Expr[T2],
t3: CF.Expr[T3],
t4: CF.Expr[T4]
)(
query: (CF.Expr.Alias[T1], CF.Expr.Alias[T2], CF.Expr.Alias[T3], CF.Expr.Alias[T4]) => CF.Query.Query0[R]
): CF.Query.Query0[R] =
macro CypherSyntaxWithMacros.withPreserving4[T1, T2, T3, T4, R]

// 5 Exprs

def apply[T1, T2, T3, T4, T5, R](
Expand Down Expand Up @@ -219,6 +251,24 @@ package object syntax extends CypherSyntaxLowPriorityImplicits {
): CF.Query.Query0[R] =
macro CypherSyntaxWithMacros.withWild5[T1, T2, T3, T4, T5, R]

def apply[T1, T2, T3, T4, T5, R](
preserving: Preserving,
t1: CF.Expr[T1],
t2: CF.Expr[T2],
t3: CF.Expr[T3],
t4: CF.Expr[T4],
t5: CF.Expr[T5]
)(
query: (
CF.Expr.Alias[T1],
CF.Expr.Alias[T2],
CF.Expr.Alias[T3],
CF.Expr.Alias[T4],
CF.Expr.Alias[T5]
) => CF.Query.Query0[R]
): CF.Query.Query0[R] =
macro CypherSyntaxWithMacros.withPreserving5[T1, T2, T3, T4, T5, R]

}

object Unwind {
Expand Down Expand Up @@ -432,6 +482,9 @@ package object syntax extends CypherSyntaxLowPriorityImplicits {
def unapply(any: Any): Option[(Path, Node)] = ???
}

// Simplify Expression With //
def *(expressions: CF.Expr[_] with CypherStatement.Alias*): Preserving = With.preserving(expressions: _*)

// // // // // // // // // // // // // // // // //
// // // // // Return Expressions // // // // //
// // // // // // // // // // // // // // // // //
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,10 @@ trait CypherSyntaxBaseSpec extends AnyWordSpec with Matchers {

def returns[R](implicit correct: T =:= R): Assertion = {
val (CypherStatement.Complete(template, params), _) = query.toCypherF(cypherGen)
template shouldBe expectedTemplate
params shouldBe expectedParams
withClue(s"complete template: $template\n") {
template shouldBe expectedTemplate
params shouldBe expectedParams
}
}
}

Expand Down
Loading

0 comments on commit 09889bb

Please sign in to comment.