Skip to content

Commit

Permalink
Refactoring, moving Quotation into expr, making model for decomposing…
Browse files Browse the repository at this point in the history
… exprs
  • Loading branch information
deusaquilus committed Mar 8, 2020
1 parent 63d8529 commit 1e035e8
Show file tree
Hide file tree
Showing 12 changed files with 418 additions and 247 deletions.
48 changes: 25 additions & 23 deletions src/main/scala/io/getquill/context/Context.scala
Original file line number Diff line number Diff line change
Expand Up @@ -13,19 +13,18 @@ import miniquill.quoter.Query
import miniquill.dsl.EncodingDsl
import miniquill.quoter.Quoted
import miniquill.quoter.Query
import miniquill.quoter.ScalarValueVase
import io.getquill.derived._
import miniquill.context.mirror.MirrorDecoders
import miniquill.context.mirror.Row
import miniquill.dsl.GenericDecoder
import miniquill.quoter.ScalarEncodeableVase
import miniquill.quoter.ScalarPlanter
import io.getquill.ast.Ast
import io.getquill.ast.ScalarTag
import scala.quoted.{Type => TType, _}
import miniquill.quoter.FindLifts
import io.getquill.idiom.Idiom
import io.getquill.ast.{Transform, QuotationTag}
import miniquill.quoter.QuotationVase
import miniquill.quoter.QuotationBin
import miniquill.quoter.SummonInlineEncodeables
import miniquill.quoter.InlineEncodeable
import miniquill.quoter.Encodeable
Expand Down Expand Up @@ -71,15 +70,6 @@ extends EncodingDsl
def idiom: Dialect
def naming: Naming

// TODO Need to have some implicits to auto-convert stuff inside
// of the run function itself into a quotation?

// inline def expandLifts[T](inline quoted: Quoted[T]): List[ScalarValueVase[_]] = {
// val lifts = quoted.lifts match {
// case sl: ScalarValueVase[_] => sl
// }
// }

inline def runDynamic[T](inline quoted: Quoted[Query[T]]): Result[RunQueryResult[T]] = {
val ast = Expander.runtime[T](quoted.ast)
val lifts =
Expand All @@ -88,17 +78,17 @@ extends EncodingDsl
else
List()

val quotationVases =
val quotationBins =
lifts.collect {
case v: QuotationVase[Any] => v // Not sure why but QuotationVase[_] causes errors
case v: QuotationBin[Any] => v // Not sure why but QuotationBin[_] causes errors
}

def spliceQuotations(ast: Ast): Ast =
Transform(ast) {
case v @ QuotationTag(uid) =>
// When a quotation to splice has been found, retrieve it and continue
// splicing inside since there could be nested sections that need to be spliced
quotationVases.find(_.uid == uid) match {
quotationBins.find(_.uid == uid) match {
case Some(vase) =>
spliceQuotations(vase.quoted.ast)
// TODO Macro error if a uid can't be looked up (also show all uid secionds that currently exist)
Expand All @@ -110,19 +100,21 @@ extends EncodingDsl
val (outputAst, stmt) = idiom.translate(expandedAst)(given naming)
val queryString = stmt.toString
// summon a decoder and a expander (as well as an encoder) all three should be provided by the context
val decoder =
summonFrom {
// TODO Implicit summoning error
case decoder: Decoder[T] => decoder
}
val decoder = summonDecoder[T]
// summonFrom {
// // TODO Implicit summoning error
// case decoder: Decoder[T] => decoder
// }
val extractor = (r: ResultRow) => decoder.apply(1, r)
this.executeQuery(queryString, null, extractor, ExecutionType.Dynamic)
}

inline def summonDecoder[T]: Decoder[T] = ${ Context.summonDecoderImpl[T, ResultRow] }

inline def run[T](inline quoted: Quoted[Query[T]]): Result[RunQueryResult[T]] = {
val staticQuery = translateStatic[T](quoted)
staticQuery match {
case Some((query, lifts)) =>
case Some((query, lifts)) => // use FindLifts as part of this match?
val decoder =
summonFrom {
case decoder: Decoder[T] => decoder
Expand Down Expand Up @@ -164,7 +156,7 @@ object Context {
import miniquill.parser._
import scala.quoted._ // summonExpr is actually from here
import scala.quoted.matching._ // ... or from here
import miniquill.quoter.ScalarEncodeableVase
import miniquill.quoter.ScalarPlanter

// (**) It seems like only unlift is needed here. If a parser needs to be passed into here,
// extending it is hard (e.g. need the same approach as Literal/Dialect Class.forName stuff)
Expand All @@ -180,15 +172,25 @@ object Context {
import io.getquill.util.LoadObject
import miniquill.dsl.GenericEncoder

//inline def summonDecoder[T]: Decoder[T] = ${ summonDecoderImpl[T] }
def summonDecoderImpl[T: Type, ResultRow: Type](given qctx: QuoteContext): Expr[GenericDecoder[ResultRow, T]] = {
import qctx.tasty.{Type => TType, given, _}
summonExpr(given '[GenericDecoder[ResultRow, T]]) match {
case Some(decoder) => decoder
case None => qctx.error(s"Cannot Find decoder for ${summon[Type[T]]}"); '{???}
}
}

def eagerLiftImpl[T, PrepareRow](vvv: Expr[T])(given qctx: QuoteContext, tType: TType[T], prepareRowType: TType[PrepareRow]): Expr[T] = {
import qctx.tasty.{given, _}
val uuid = java.util.UUID.randomUUID().toString
val encoder =
summonExpr(given '[GenericEncoder[$tType, $prepareRowType]]) match {
case Some(enc) => enc
case None => qctx.error(s"Cannot Find encode for ${tType.unseal}", vvv); '{???}
// TODO return summoning error if not correct
}
'{ ScalarEncodeableVase($vvv, $encoder, ${Expr(uuid)}).unquote } //[$tType, $prepareRowType] // adding these causes assertion failed: unresolved symbols: value Context_this
'{ ScalarPlanter($vvv, $encoder, ${Expr(uuid)}).unquote } //[$tType, $prepareRowType] // adding these causes assertion failed: unresolved symbols: value Context_this
}

class ExpandTags[D <: Idiom, N <: NamingStrategy](ast: Expr[Any])(given qctx: QuoteContext, dialectTpe:TType[D], namingType:TType[N]) {
Expand Down
17 changes: 3 additions & 14 deletions src/main/scala/miniquill/parser/Parser.scala
Original file line number Diff line number Diff line change
Expand Up @@ -56,27 +56,16 @@ class Parser(given qctx:QuoteContext) extends PartialFunction[Expr[_], Ast] {
astParser.isDefinedAt(in.unseal.underlyingArgument.seal)

def astParser: PartialFunction[Expr[_], Ast] = {

// Needs to be somewhere in the beginning so 'value' will not be parsed as a functiona-apply
// i.e. since we don't want Property(..., value) to be the tree in this case.
//case '{ (ScalarValueVase.apply[$t]($value, ${scala.quoted.matching.Const(uid: String)})).value } =>
// ScalarTag(uid)

//case Unseal(Apply(TypeApply(Select(Ident("ScalarValueVase"), "apply"), List(Inferred())), List(scalaTree, Literal(Constant(uid: String))))) =>
// ScalarTag(uid)

case MatchInlineQuotation(astTree, uid) =>

case MatchInlineUnquote(astTree, uid) =>
unlift(astTree)

case MatchLift(tree, uid) =>
ScalarTag(uid)

case MatchEncodeableLift(tree, uid) =>
ScalarTag(uid) // TODO Want special scalar tag for an encodeable scalar

// MUST come after the MatchInlineQuotation because it matches
// the same kind of statement
case MatchRuntimeQuotation(tree, uid) =>
case MatchRuntimeUnquote(tree, uid) =>
QuotationTag(uid)

case `Quoted.apply`(ast, _) =>
Expand Down
95 changes: 38 additions & 57 deletions src/main/scala/miniquill/parser/QuotationParser.scala
Original file line number Diff line number Diff line change
@@ -1,13 +1,31 @@
package miniquill.parser

import io.getquill.ast.{Ident => Idnt, Constant => Const, Query => Qry, _}
import io.getquill.ast.{ Ident => Idnt, Constant => Const, Query => Qry, _}
import miniquill.quoter._
import scala.quoted._
import scala.quoted.matching._
import scala.annotation.StaticAnnotation
import scala.deriving._
import io.getquill.Embedable

class MatroshkaHelper(given val qctx: QuoteContext) {
import qctx.tasty.{Term => QTerm, given, _}

object TypedMatroshka {
// need to define a case where it won't go into matcher otherwise recursion is infinite
//@tailcall // should be tail recursive
def recurse(innerTerm: QTerm): QTerm = innerTerm match {
case Typed(innerTree, _) => recurse(innerTree)
case other => other
}

def unapply(term: QTerm): Option[QTerm] = term match {
case Typed(tree, _) => Some(recurse(tree))
case _ => None
}
}
}

class QuotationParser(given val qctx: QuoteContext) {
import qctx.tasty.{_, given _}

Expand Down Expand Up @@ -43,11 +61,11 @@ class QuotationParser(given val qctx: QuoteContext) {
}

object `Quoted.apply` {
def unapply(expr: Expr[Any]): Option[(Expr[Ast], Expr[scala.Tuple])] = expr match {
case '{ Quoted.apply[$qt]($ast, $v) } =>
def unapply(expr: Expr[Any]): Option[(Expr[Ast], Expr[List[Vase]], Expr[List[QuotationPouch]])] = expr match {
case '{ Quoted.apply[$qt]($ast, $v, $rq) } =>
//println("********************** MATCHED VASE INNER TREE **********************")
//printer.lnf(expr.unseal)
Some((ast, v))
Some((ast, v, rq))
case Unseal(TypedMatroshka(tree)) => unapply(tree.seal)
case _ =>
//println("********************** NOT MATCHED VASE INNER TREE **********************")
Expand All @@ -56,104 +74,67 @@ class QuotationParser(given val qctx: QuoteContext) {
}
}

protected object `QuotationVase.apply` {
protected object `QuotationBin.apply` {
def unapply(expr: Expr[Any]) = expr match {
case vase @ '{ QuotationVase.apply[$qt]($quotation, ${scala.quoted.matching.Const(uid: String)}) } =>
case vase @ '{ QuotationBin.apply[$qt]($quotation, ${scala.quoted.matching.Const(uid: String)}) } =>
//println("********************** MATCHED VASE APPLY **********************")
//printer.lnf(expr.unseal)
Some((quotation, uid, vase))
case _ => None
}
}

object `ScalarValueVase.apply` {
object `ScalarVase.apply` {
def unapply(expr: Expr[Any]) = expr match {
case vase @ '{ ScalarValueVase.apply[$qt]($liftValue, ${scala.quoted.matching.Const(uid: String)}) } =>
case vase @ '{ ScalarVase.apply[$qt, $prep]($liftValue, $encoder, ${scala.quoted.matching.Const(uid: String)}) } =>
Some((liftValue, uid, vase, qt))
case _ => None
}
}

object `ScalarEncodeableVase.apply` {
def unapply(expr: Expr[Any]) = expr match {
case vase @ '{ ScalarEncodeableVase.apply[$qt, $prep]($liftValue, $encoder, ${scala.quoted.matching.Const(uid: String)}) } =>
Some((liftValue, uid, vase, qt))
case _ => None
}
}

// Match the QuotationVase(...).unquote values which are tacked on to every
// Match the QuotationBin(...).unquote values which are tacked on to every
// child-quote (inside of a parent quote) when the 'unquote' function (i.e macro)
// is applied.
protected object `QuotationVase.unquote` {
protected object `QuotationBin.unquote` {
def unapply(expr: Expr[Any]) = expr match {
// When a QuotationVase is embedded into an ast
case '{ (${quotationVase}: QuotationVase[$tt]).unquote } => Some(quotationVase)
// When a QuotationBin is embedded into an ast
case '{ (${quotationBin}: QuotationBin[$tt]).unquote } => Some(quotationBin)
case _ => None
}
}

protected object `(ScalarValueVase).unquote` {
protected object `(ScalarVase).unquote` {
def unapply(expr: Expr[Any]) = expr match {
case '{ (${vase}: ScalarValueVase[$tt]).unquote } => Some(vase)
case '{ (${vase}: ScalarVase[$tt, $pt]).unquote } => Some(vase)
case _ => None
}
}

protected object `(ScalarEncodeableVase).unquote` {
def unapply(expr: Expr[Any]) = expr match {
case '{ (${vase}: ScalarEncodeableVase[$tt, $pt]).unquote } => Some(vase)
case _ => None
}
}

object MatchLift {
def unapply(expr: Expr[Any]): Option[(Expr[Any], String)] =
expr match {
case `(ScalarValueVase).unquote`(vase) =>
unapply(vase)
case `ScalarValueVase.apply`(_, uuid, vase, _) =>
Some((vase, uuid))
case _ => None
}
}

object MatchEncodeableLift {
def unapply(expr: Expr[Any]): Option[(Expr[Any], String)] =
expr match {
case `(ScalarEncodeableVase).unquote`(vase) =>
unapply(vase)
case `ScalarEncodeableVase.apply`(_, uuid, vase, _) =>
Some((vase, uuid))
case _ => None
}
}

object MatchRuntimeQuotation {
def unapply(expr: Expr[Any]): Option[(Expr[Any], String)] =
object MatchRuntimeQuotationBins {
def unapply(expr: Expr[Any]): Option[(Expr[QuotationBin[Any]], String)] =
expr match {
// case MatchQuotationRef(tree, uuid) =>
// println("******************** Runtime: Match Quotation Ref ********************")
// printer.lnf((tree.unseal, uuid))
// Some((tree, uuid))
case `QuotationVase.unquote`(innards) =>
case `QuotationBin.unquote`(innards) =>
//println("******************** Runtime: Match Unquote ********************")
//printer.lnf(innards.unseal)
unapply(innards)
// sometimes there are multiple levels of vases when references are spliced,
// we should only care about the innermost one
case `QuotationVase.apply`(_, uuid, vase) =>
case `QuotationBin.apply`(_, uuid, vase) =>
//println("******************** Runtime: Vase Apply ********************")
//printer.lnf(uuid, vase)
Some((vase, uuid))
case _ => None
}
}

object MatchInlineQuotation {
object MatchInlineUnquote {
def unapply(expr: Expr[Any]): Option[(Expr[Ast], String)] =
expr match {
case `QuotationVase.unquote`(`QuotationVase.apply`(`Quoted.apply`((astTree, _)), uuid, _)) =>
case `QuotationBin.unquote`(`QuotationBin.apply`(`Quoted.apply`((astTree, _)), uuid, _)) =>
Some((astTree, uuid))
case _ => None
}
Expand Down
19 changes: 19 additions & 0 deletions src/main/scala/miniquill/parser/SealUnseal.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package miniquill.parser

import scala.quoted._

class SealUnseal(given val qctx: QuoteContext) {
import qctx.tasty.{given, _}

object Unseal {
def unapply(t: Expr[Any]) = {
Some(t.unseal)
}
}
object Seal {
def unapply[T](e: Term) = {
implicit val ttpe: quoted.Type[T] = e.tpe.seal.asInstanceOf[quoted.Type[T]]
Some(e.seal.cast[T])
}
}
}
Loading

0 comments on commit 1e035e8

Please sign in to comment.