Skip to content

Commit

Permalink
port over AppliedViaBuilder creation
Browse files Browse the repository at this point in the history
  • Loading branch information
arainko committed Sep 16, 2023
1 parent ed24ce4 commit 845c707
Show file tree
Hide file tree
Showing 9 changed files with 122 additions and 43 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@
.vscode
metals.sbt
target/
ast.*
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package io.github.arainko.ducktape

final class AppliedBuilder[A, B](value: A) {
inline def transform(inline config: Field2[A, B] | Case2[A, B]*): B = ???
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package io.github.arainko.ducktape

final class AppliedViaBuilder[A, B, Args <: FunctionArguments] private (value: A, function: Any) {
inline def transform(inline config: Arg2[A, B, Args] | Case2[A, B]*): B = ???
}

object AppliedViaBuilder {
private def instance[A](source: A,function: Any): AppliedViaBuilder[A, Nothing, Nothing] =
AppliedViaBuilder[A, Nothing, Nothing](source, function)

transparent inline def create[A](source: A, inline function: Any): Any = {
val inst = instance(source, function)
Function.fillInTypes(function, inst)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package io.github.arainko.ducktape

import scala.quoted.*
import scala.collection.immutable.ListMap
import io.github.arainko.ducktape.internal.modules.*
import io.github.arainko.ducktape.internal.Debug

final case class Function(args: ListMap[String, Type[?]], returnTpe: Type[?], expr: Expr[Any]) derives Debug {

def appliedTo(using Quotes)(terms: List[quotes.reflect.Term]): Expr[Any] = {
import quotes.reflect.*
Expr.betaReduce(Select.unique(expr.asTerm, "apply").appliedToArgs(terms).asExpr)
}

def fillInExprTypes[TypeFn[dest, args <: FunctionArguments]: Type](initial: Expr[TypeFn[Nothing, Nothing]])(using Quotes) = {
import quotes.reflect.*

val refinedArgs = args.foldLeft(TypeRepr.of[FunctionArguments]) {
case (acc, (name, tpe)) =>
Refinement(acc, name, tpe.repr)
}
returnTpe -> refinedArgs.asType match {
case '[retTpe] -> '[Function.IsFuncArgs[args]] => '{ $initial.asInstanceOf[TypeFn[retTpe, args]] }
}
}

}

object Function {
private type IsFuncArgs[A <: FunctionArguments] = A

def fromExpr(expr: Expr[Any])(using Quotes): Option[Function] = {
import quotes.reflect.*

unapply(expr.asTerm).map { (vals, term) =>
val returnTpe = term.tpe.asType
val args = vals.map(valDef => valDef.name -> valDef.tpt.tpe.asType).to(ListMap)
Function(args, returnTpe, expr)
}
}

transparent inline def fillInTypes[TypeFn[dest, args <: FunctionArguments]](
inline function: Any,
initial: TypeFn[Nothing, Nothing]
) = ${ fillInTypesMacro('function, 'initial) }

private def fillInTypesMacro[TypeFn[dest, args <: FunctionArguments]: Type](
function: Expr[Any],
initial: Expr[TypeFn[Nothing, Nothing]]
)(using Quotes) = {
import quotes.reflect.*

val func = fromExpr(function).getOrElse(report.errorAndAbort(s"Couldn't construct a function TODO ERROR MESSAGE"))
func.fillInExprTypes(initial)
}

private def unapply(using Quotes)(arg: quotes.reflect.Term): Option[(List[quotes.reflect.ValDef], quotes.reflect.Term)] = {
import quotes.reflect.*

arg match {
case Lambda(vals, term) => Some(vals -> term)
case Inlined(_, _, nested) => unapply(nested)
case _ => None
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ enum Plan[+E <: PlanError] {
sourceContext: Plan.Context,
destContext: Plan.Context,
argPlans: ListMap[String, Plan[E]],
function: Expr[Any]
function: Function
) extends Plan[E]

case BetweenUnwrappedWrapped(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,30 +11,34 @@ import scala.annotation.tailrec

object PlanInterpreter {

transparent inline def betweenTypeAndFunction[A](
transparent inline def transformVia[A, B, Args <: FunctionArguments](
value: A,
inline function: Any,
// inline configs: Arg[]
) = ${ createTransformationBetweenTypeAndFunction('value, 'function) }
inline configs: Arg2[A, B, Args] | CaseConfig[A, B]*
) = ${ createTransformationVia('value, 'function, 'configs) }

def createTransformationBetweenTypeAndFunction[A: Type](value: Expr[A], function: Expr[Any])(using Quotes) = {
def createTransformationVia[A: Type, B: Type, Args <: FunctionArguments: Type](
value: Expr[A],
function: Expr[Any],
configs: Expr[Seq[Arg2[A, B, Args] | CaseConfig[A, B]]]
)(using Quotes) = {
import quotes.reflect.*

Planner.betweenTypeAndFunction[A](function).refine match {
case Left(errors) =>
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](
inline def transformBetween[A, B](
value: A,
inline configs: FieldConfig[A, B] | CaseConfig[A, B]*
) = ${ createTransformationBetweenTypes[A, B]('value, 'configs) }
) = ${ createTransformationBetween[A, B]('value, 'configs) }

def createTransformationBetweenTypes[A: Type, B: Type](
def createTransformationBetween[A: Type, B: Type](
value: Expr[A],
configs: Expr[Seq[FieldConfig[A, B] | CaseConfig[A, B]]]
)(using Quotes): Expr[B] = {
Expand Down Expand Up @@ -97,7 +101,7 @@ object PlanInterpreter {
val fieldValue = value.accessFieldByName(fieldName).asExpr
recurse(plan, fieldValue).asTerm
}
Expr.betaReduce(Select.unique(function.asTerm, "apply").appliedToArgs(args.toList).asExpr)
function.appliedTo(args.toList)

case Plan.BetweenOptions(sourceTpe, destTpe, _, _, plan) =>
(sourceTpe -> destTpe) match {
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 {
PlanInterpreter.betweenTypes[ProdTest1, ProdTest2](
PlanInterpreter.transformBetween[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 @@ -81,25 +81,28 @@ final case class ProdTest2(test: Test2)



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

val aaa =
Transformer.Debug.showCode {
AppliedViaBuilder.create(p, costam)
}

aaa.transform(Arg2.const(_.int, "aasd"))

DebugMacros.code {
PlanInterpreter.betweenTypeAndFunction[Person1](p, costam)
PlanInterpreter.transformVia[Person1, Nothing, Nothing](p, costam)
}

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

summon[Mirror.Of[Test3]]

DebugMacros.code {
PlanInterpreter.betweenTypes[Test1, Test3](
PlanInterpreter.transformBetween[Test1, Test3](
Test1.Cos(Nested1(1)),
Case2.const(_.at[Test1.Empty.type], Test3.Empty1.Impl)
)
}

}

final class AppliedBuilder[A, B](value: A) {
inline def transform(inline config: Field2[A, B] | Case2[A, B]*): B = ???
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ object Structure {

case class Product(tpe: Type[?], name: String, fields: Map[String, Structure]) extends Structure
case class Coproduct(tpe: Type[?], name: String, children: Map[String, Structure]) extends Structure
case class Function(tpe: Type[?], name: String, args: ListMap[String, Structure], function: Expr[Any]) extends Structure
case class Function(tpe: Type[?], name: String, args: ListMap[String, Structure], function: io.github.arainko.ducktape.Function) extends Structure
case class Singleton(tpe: Type[?], name: String, value: Expr[Any]) extends Structure
case class Ordinary(tpe: Type[?], name: String) extends Structure
case class ValueClass(tpe: Type[?], name: String, paramTpe: Type[?], paramFieldName: String) extends Structure
Expand All @@ -38,18 +38,17 @@ object Structure {
lazy val name = struct.name
}

//TODO: Make this take in a Function as an arg instead of matching on the expr here
def fromFunction(function: Expr[Any])(using Quotes): Option[Structure.Function] = {
import quotes.reflect.*

FunctionLambda
.unapply(function.asTerm)
.map { (vals, term) =>
val tpe = term.tpe

io.github.arainko.ducktape.Function.fromExpr(function)
.map { func =>
val args =
vals.map(valDef => valDef.name -> (valDef.tpt.tpe.asType match { case '[argTpe] => Lazy(() => Structure.of[argTpe]) })).to(ListMap)
func.args.map((name, tpe) => name -> (tpe match { case '[argTpe] => Lazy(() => Structure.of[argTpe]) }))
// vals.map(valDef => valDef.name -> (valDef.tpt.tpe.asType match { case '[argTpe] => Lazy(() => Structure.of[argTpe]) })).to(ListMap)

Structure.Function(tpe.asType, tpe.show, args, function)
Structure.Function(func.returnTpe, func.returnTpe.repr.show, args, func)
}
}

Expand Down Expand Up @@ -138,16 +137,4 @@ object Structure {
import quotes.reflect.*
tupleTypeElements(tp).map { case ConstantType(StringConstant(l)) => l }
}

private object FunctionLambda {
def unapply(using Quotes)(arg: quotes.reflect.Term): Option[(List[quotes.reflect.ValDef], quotes.reflect.Term)] = {
import quotes.reflect.*

arg match {
case Lambda(vals, term) => Some(vals -> term)
case Inlined(_, _, nested) => FunctionLambda.unapply(nested)
case _ => None
}
}
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
package io.github.arainko.ducktape

import io.github.arainko.ducktape.function.FunctionArguments

opaque type Field2[A, B] = Unit

object Field2 {
Expand All @@ -14,11 +12,11 @@ object Case2 {
def const[A, B, SourceTpe, FieldTpe](selector: Selector ?=> A => SourceTpe, value: FieldTpe): Case2[A, B] = ???
}

opaque type Arg[A, B, Args <: FunctionArguments] = Unit
opaque type Arg2[A, B, Args <: FunctionArguments] = Unit

object Arg {
object Arg2 {
def const[A, B, Args <: FunctionArguments, DestArgTpe, ConstTpe](
selector: Selector ?=> Args => DestArgTpe,
value: ConstTpe
): Arg[A, B, Args] = ???
): Arg2[A, B, Args] = ???
}

0 comments on commit 845c707

Please sign in to comment.