Skip to content

Commit

Permalink
add .via backed by the new backend
Browse files Browse the repository at this point in the history
  • Loading branch information
arainko committed Sep 3, 2023
1 parent a125c4e commit feb16c7
Show file tree
Hide file tree
Showing 5 changed files with 83 additions and 15 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,27 @@ import scala.annotation.tailrec

object Interpreter {

inline def transformPlanned[A, B](value: A, inline configs: Config[A, B]*) = ${ createTransformation[A, B]('value, 'configs) }
transparent inline def betweenTypeAndFunction[A](value: A, inline function: Any) = ${ createTransformationBetweenTypeAndFunction('value, 'function) }

def createTransformation[A: Type, B: Type](value: Expr[A], configs: Expr[Seq[Config[A, B]]])(using Quotes): Expr[B] = {
def createTransformationBetweenTypeAndFunction[A: Type](value: Expr[A], function: Expr[Any])(using Quotes) = {
import quotes.reflect.*

val plan = Planner.createPlan[A, B]
val plan = Planner.betweenTypeAndFunction[A](function)
refinePlan(plan) match {
case Left(errors) =>
val rendered = errors.map(err => s"${err.message} @ ${err.sourceContext.render}").mkString("\n")
report.errorAndAbort(rendered)
case Right(totalPlan) =>
recurse(totalPlan, value)
}
}

inline def betweenTypes[A, B](value: A, inline configs: Config[A, B]*) = ${ createTransformationBetweenTypes[A, B]('value, 'configs) }

def createTransformationBetweenTypes[A: Type, B: Type](value: Expr[A], configs: Expr[Seq[Config[A, B]]])(using Quotes): Expr[B] = {
import quotes.reflect.*

val plan = Planner.betweenTypes[A, B]
val config = Configuration.parse(configs)
val reconfiguredPlan = config.foldLeft(plan) { (plan, config) => plan.configure(config) }
println(s"OG PLAN: ${plan.show}")
Expand Down Expand Up @@ -45,9 +60,8 @@ object Interpreter {
recurse(fieldPlans.values.toList ::: next, errors)
case Plan.BetweenCoproducts(_, _, _, _, casePlans) =>
recurse(casePlans.toList ::: next, errors)
case Plan.Via(_, _, _, _, argPlans, _) =>
???
// recurse(argPlans)
case Plan.BetweenProductFunction(_, _, _, _, argPlans, _) =>
recurse(argPlans.values.toList ::: next, errors)
case Plan.BetweenOptions(_, _, _, _, plan) => recurse(plan :: next, errors)
case Plan.BetweenNonOptionOption(_, _, _, _, plan) => recurse(plan :: next, errors)
case Plan.BetweenCollections(_, _, _, _, _, plan) => recurse(plan :: next, errors)
Expand Down Expand Up @@ -97,15 +111,16 @@ object Interpreter {
}.toList
ifStatement(branches).asExpr

case Plan.Via(sourceTpe, destTpe, _, _, argPlans, function) =>
case Plan.BetweenProductFunction(sourceTpe, destTpe, _, _, argPlans, function) =>
val args = argPlans.map {
case (fieldName, p: Plan.Configured) =>
recurse(p, value).asTerm
case (fieldName, plan) =>
val fieldValue = value.accessFieldByName(fieldName).asExpr
recurse(plan, fieldValue).asTerm
}
function.asTerm.appliedToArgs(args.toList).asExpr
Expr.betaReduce(Select.unique(function.asTerm, "apply").appliedToArgs(args.toList).asExpr)

case Plan.BetweenOptions(sourceTpe, destTpe, _, _, plan) =>
(sourceTpe -> destTpe) match {
case '[src] -> '[dest] =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ enum Plan[+E <: PlanError] {
config: Configuration
) extends Plan[Nothing]

case Via(
case BetweenProductFunction(
sourceTpe: Type[?],
destTpe: Type[?],
sourceContext: Plan.Context,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,31 @@ package io.github.arainko.ducktape

import io.github.arainko.ducktape.Plan.Context
import io.github.arainko.ducktape.internal.modules.*
import io.github.arainko.ducktape.internal.Debug

import scala.quoted.*

object Planner {
import Structure.*

def createPlan[Source: Type, Dest: Type](using Quotes): Plan[Plan.Error] =
def betweenTypeAndFunction[Source: Type](function: Expr[Any])(using Quotes): Plan[Plan.Error] =
Structure
.fromFunction(function)
.map { destStruct =>
val sourceStruct = Structure.of[Source]
recurse(sourceStruct, destStruct, Plan.Context.empty(Type.of[Source]), Plan.Context.empty(destStruct.tpe))
}
.getOrElse(
Plan.Error(
Type.of[Source],
Type.of[Any],
Plan.Context.empty(Type.of[Source]),
Plan.Context.empty(Type.of[Any]),
"Couldn't create a transformation plan from a function"
)
)

def betweenTypes[Source: Type, Dest: Type](using Quotes): Plan[Plan.Error] =
recurseAndCreatePlan[Source, Dest](Plan.Context.empty(Type.of[Source]), Plan.Context.empty(Type.of[Dest]))

private def recurseAndCreatePlan[Source: Type, Dest: Type](sourceContext: Plan.Context, destContext: Plan.Context)(using
Expand All @@ -24,8 +42,33 @@ object Planner {
dest: Structure,
sourceContext: Plan.Context,
destContext: Plan.Context
)(using Quotes): Plan[Plan.Error] =
)(using Quotes): Plan[Plan.Error] = {
import quotes.reflect.*

(source.force -> dest.force) match {
case (source: Product, dest: Function) =>
val argPlans = dest.args.map { (destField, destFieldStruct) =>
val updatedDestContext = destContext.add(Path.Segment.Field(destFieldStruct.tpe, destField))
val plan =
source.fields
.get(destField)
.map { sourceStruct =>
val updatedSourceContext = sourceContext.add(Path.Segment.Field(sourceStruct.tpe, destField))
recurse(sourceStruct, destFieldStruct, updatedSourceContext, updatedDestContext)
}
.getOrElse(
Plan.Error(
Type.of[Nothing],
destFieldStruct.tpe,
sourceContext, // TODO: Revise
updatedDestContext, // TODO: Revise
s"No field named '$destField' found in ${source.tpe.repr.show}"
)
)
destField -> plan
}
Plan.BetweenProductFunction(source.tpe, dest.tpe, sourceContext, destContext, argPlans, dest.function)

case UserDefinedTransformation(transformer) =>
Plan.UserDefined(source.tpe, dest.tpe, sourceContext, destContext, transformer)

Expand Down Expand Up @@ -126,6 +169,7 @@ object Planner {
s"Couldn't build a transformation plan between ${source.tpe.repr.show} and ${dest.tpe.repr.show}"
)
}
}

object UserDefinedTransformation {
def unapply(structs: (Structure, Structure))(using Quotes): Option[Expr[UserDefinedTransformer[?, ?]]] = {
Expand All @@ -150,7 +194,7 @@ object Planner {
inline def print[A, B] = ${ printMacro[A, B] }

def printMacro[A: Type, B: Type](using Quotes): Expr[Unit] = {
val plan = createPlan[A, B]
val plan = betweenTypes[A, B]
quotes.reflect.report.info(plan.show)
'{}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ final case class ProdTest2(test: Test2)
- fill in a missing Source Case
*/
DebugMacros.code {
Interpreter.transformPlanned[ProdTest1, ProdTest2](
Interpreter.betweenTypes[ProdTest1, ProdTest2](
ProdTest1(Test1.Cos(Nested1(1))),
Field2.const(_.test.at[Test2.Cos].int.additional, 1), // missing field
Field2.const(_.test.at[Test2.Cos].int.int, 123), // overriden field
Expand All @@ -79,12 +79,20 @@ final case class ProdTest2(test: Test2)
)
}



def costam(int: Int, str: String) = ???

DebugMacros.code {
Interpreter.betweenTypeAndFunction[Person1](p, costam)
}

val lub = if (1 == 2) Test3.Empty else Test3.Empty1.Impl

summon[Mirror.Of[Test3]]

DebugMacros.code {
Interpreter.transformPlanned[Test1, Test3](
Interpreter.betweenTypes[Test1, Test3](
Test1.Cos(Nested1(1)),
Case2.const(_.at[Test1.Empty.type], Test3.Empty1.Impl)
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ object Structure {
lazy val name = struct.name
}

def fromFunction(function: Expr[Any])(using Quotes) = {
def fromFunction(function: Expr[Any])(using Quotes): Option[Structure.Function] = {
import quotes.reflect.*

FunctionLambda
Expand All @@ -65,6 +65,7 @@ object Structure {
val paramTpe = valueClassRepr.memberType(param)
Structure.ValueClass(summon[Type[A]], valueClassRepr.show, paramTpe.asType, param.name)
case other =>

Structure.Ordinary(summon[Type[A]], TypeRepr.of[A].show)
}
case Some(value) =>
Expand Down

0 comments on commit feb16c7

Please sign in to comment.