Skip to content

Commit

Permalink
Initial implementation for Scala 3 DSL and derivation
Browse files Browse the repository at this point in the history
  • Loading branch information
MateuszKubuszok committed Dec 16, 2023
1 parent b8271cc commit df67bb0
Show file tree
Hide file tree
Showing 7 changed files with 372 additions and 107 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -233,11 +233,10 @@ trait ProductTypesPlatform extends ProductTypes { this: DefinitionsPlatform =>

val methodType: ?? = args.foldRight[??](Type[A].as_??) { (paramList, resultType) =>
val paramTypes = paramList.view.values.map(_.Underlying.tpe).toList
// tq returns c.Tree, to turn it to c.Type we need .tpe, which without a .typecheck is null
fromUntyped(c.typecheck(tq"(..$paramTypes) => ${resultType.Underlying.tpe}", mode = c.TYPEmode).tpe).as_??
}

println(methodType.Underlying)

import methodType.Underlying as MethodType
val tree = expr.asInstanceOfExpr[MethodType].tree
c.Expr[A](q"$tree(...${(args.map(_.map { case (paramName, _) =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,10 @@ trait ProductTypesPlatform extends ProductTypes { this: DefinitionsPlatform =>
} else None

def exprAsInstanceOfMethod[A: Type](args: List[ListMap[String, ??]])(expr: Expr[Any]): Product.Constructor[A] = {

println("\n\n\n\nWTF\n\n\n")
println(s"Arguments: $args")

val parameters: Product.Parameters = ListMap.from(for {
list <- args
pair <- list.toList
Expand All @@ -255,25 +259,84 @@ trait ProductTypesPlatform extends ProductTypes { this: DefinitionsPlatform =>
})

val constructor: Product.Arguments => Expr[A] = arguments => {

println("\n\n\n\nWTF\n\n\n")
println(s"Arguments: $args")
println(s"Arguments: $arguments")

val (constructorArguments, _) = checkArguments[A](parameters, arguments)
val methodType: ?? = null.asInstanceOf[??] // TODO: figure out the type

val methodType: ?? = args.foldRight[??](Type[A].as_??) { (paramList, resultType) =>
val fnType = fnTypeByArity.getOrElse(
paramList.size,
assertionFailed(s"Expected arity between 1 and 22 into ${Type.prettyPrint[A]}, got: ${paramList.size}")
)
val paramTypes = paramList.view.values.map(p => TypeRepr.of(using p.Underlying)).toVector

println(
s"Constructing: ${fnType.show(using Printer.TypeReprAnsiCode)} -> ${paramTypes
.map(_.show(using Printer.TypeReprAnsiCode))} + ${TypeRepr.of(using resultType.Underlying).show(using Printer.TypeReprAnsiCode)}"
)

fromUntyped(
fnType.appliedTo((paramTypes :+ TypeRepr.of(using resultType.Underlying)).toList).dealias.simplified
).as_??
// fromUntyped(AppliedType(fnType, (paramTypes :+ TypeRepr.of(using resultType.Underlying)).toList)).as_??
}

import methodType.Underlying as MethodType
val tree = expr
.asInstanceOfExpr[MethodType]
.asTerm
tree
.appliedToArgss(
args
.map(_.map { case (paramName, _) =>
println(s"Constructed: ${Type.prettyPrint[MethodType]}")
println(s"Expr: ${Expr.prettyPrint(expr.asInstanceOfExpr[MethodType])}")
val tree = expr.asInstanceOfExpr[MethodType].asTerm
val a = args
.foldLeft(tree) { (result, list) =>
val method: Symbol = result.tpe.typeSymbol.methodMember("apply").head
result
.select(method)
.appliedToArgs(list.map { (paramName, _) =>
constructorArguments(paramName).value.asTerm
}.toList)
)
}
.asExprOf[A]

println(s"Final result: ${Expr.prettyPrint(a)}")

a
// tree
// .appliedToArgss(args.map(_.map { case (paramName, _) =>
// constructorArguments(paramName).value.asTerm
// }.toList))
// .asExprOf[A]
}

Product.Constructor[A](parameters, constructor)
}

private lazy val fnTypeByArity = Map(
1 -> TypeRepr.of[scala.Function1],
2 -> TypeRepr.of[scala.Function2],
3 -> TypeRepr.of[scala.Function3],
4 -> TypeRepr.of[scala.Function4],
5 -> TypeRepr.of[scala.Function5],
6 -> TypeRepr.of[scala.Function6],
7 -> TypeRepr.of[scala.Function7],
8 -> TypeRepr.of[scala.Function8],
9 -> TypeRepr.of[scala.Function9],
10 -> TypeRepr.of[scala.Function10],
11 -> TypeRepr.of[scala.Function11],
12 -> TypeRepr.of[scala.Function12],
13 -> TypeRepr.of[scala.Function13],
14 -> TypeRepr.of[scala.Function14],
15 -> TypeRepr.of[scala.Function15],
16 -> TypeRepr.of[scala.Function16],
17 -> TypeRepr.of[scala.Function17],
18 -> TypeRepr.of[scala.Function18],
19 -> TypeRepr.of[scala.Function19],
20 -> TypeRepr.of[scala.Function20],
21 -> TypeRepr.of[scala.Function21],
22 -> TypeRepr.of[scala.Function22]
)

private val isGarbageSymbol = ((s: Symbol) => s.name) andThen isGarbage
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,81 @@ private[chimney] trait DslMacroUtils {
s"Invalid selector expression - only input value can be extracted from: $selectorTree"
}

private trait ExistentialCtor {
type Underlying <: runtime.Path
implicit val Underlying: Type[Underlying]
}
private object ExistentialCtor {

def parse(t: Tree): Either[String, ExistentialCtor] = t match {
// TODO: add support for multiple parameter lists
case Function(params, _) =>
val t = paramsToType(List(params))
// TODO: remove once everything works
println(s"""Expression:
|${Console.MAGENTA}${show(f)}${Console.RESET}
|defined as:
|${Console.MAGENTA}${showRaw(f)}${Console.RESET}
|of type:
|${Console.MAGENTA}${f.tpe}${Console.RESET}
|of type:
|${Console.MAGENTA}${showRaw(f.tpe)}${Console.RESET}
|resolved as:
|${Console.MAGENTA}$t${Console.RESET}
|""".stripMargin)
Right(new ExistentialCtor {
type Underlying = runtime.ArgumentLists
val Underlying: WeakTypeTag[runtime.ArgumentLists] = t
})
case _ =>
Left(invalidConstructor(f))
}

private def paramsToType(paramsLists: List[List[ValDef]]): WeakTypeTag[runtime.ArgumentLists] =
paramsLists
.map { paramList =>
paramList.foldRight[WeakTypeTag[? <: runtime.ArgumentList]](weakTypeTag[runtime.ArgumentList.Empty])(
constructArgumentListType
)
}
.foldRight[WeakTypeTag[? <: runtime.ArgumentLists]](weakTypeTag[runtime.ArgumentLists.Empty])(
constructArgumentListsType
)

private def constructArgumentListsType(
head: WeakTypeTag[? <: runtime.ArgumentList],
tail: WeakTypeTag[? <: runtime.ArgumentLists]
): WeakTypeTag[? <: runtime.ArgumentLists] = {
object ApplyParams {
def apply[Head <: runtime.ArgumentList: WeakTypeTag, Tail <: runtime.ArgumentLists: WeakTypeTag]
: WeakTypeTag[runtime.ArgumentLists.List[Head, Tail]] =
weakTypeTag[runtime.ArgumentLists.List[Head, Tail]]
}

ApplyParams(head, tail)
}

private def constructArgumentListType(
t: ValDef,
args: WeakTypeTag[? <: runtime.ArgumentList]
): WeakTypeTag[? <: runtime.ArgumentList] = {
object ApplyParam {
def apply[ParamName <: String: WeakTypeTag, ParamType: WeakTypeTag, Args <: runtime.ArgumentList: WeakTypeTag]
: WeakTypeTag[runtime.ArgumentList.Argument[ParamName, ParamType, Args]] =
weakTypeTag[runtime.ArgumentList.Argument[ParamName, ParamType, Args]]
}

ApplyParam(
c.WeakTypeTag(c.internal.constantType(Constant(t.name.decodedName.toString))),
c.WeakTypeTag(t.tpt.tpe),
args
)
}

private def invalidConstructor(t: Tree): String =
s"Expected function, instead got: ${Console.MAGENTA}$t${Console.RESET}: ${Console.MAGENTA}${t.tpe}${Console.RESET}"
}

// If we try to do:
//
// implicit val fieldNameT = fieldName.Underlying
Expand Down Expand Up @@ -158,68 +233,14 @@ private[chimney] trait DslMacroUtils {

def apply[Ctor <: runtime.ArgumentLists: WeakTypeTag]: Tree

final def applyFromBody(f: Tree): Tree = f match {
case Function(params, _) =>
val t = paramsToType(List(params))
// TODO: remove once everything works
println(s"""Expression:
|${Console.MAGENTA}${show(f)}${Console.RESET}
|defined as:
|${Console.MAGENTA}${showRaw(f)}${Console.RESET}
|of type:
|${Console.MAGENTA}${f.tpe}${Console.RESET}
|of type:
|${Console.MAGENTA}${showRaw(f.tpe)}${Console.RESET}
|resolved as:
|${Console.MAGENTA}$t${Console.RESET}
|""".stripMargin)
apply(t)
case _ =>
c.abort(c.enclosingPosition, invalidConstructor(f))
}

private def paramsToType(paramsLists: List[List[ValDef]]): WeakTypeTag[? <: runtime.ArgumentLists] =
paramsLists
.map { paramList =>
paramList.foldRight[WeakTypeTag[? <: runtime.ArgumentList]](weakTypeTag[runtime.ArgumentList.Empty])(
constructArgumentListType
)
}
.foldRight[WeakTypeTag[? <: runtime.ArgumentLists]](weakTypeTag[runtime.ArgumentLists.Empty])(
constructArgumentListsType
)

private def constructArgumentListsType(
head: WeakTypeTag[? <: runtime.ArgumentList],
tail: WeakTypeTag[? <: runtime.ArgumentLists]
): WeakTypeTag[? <: runtime.ArgumentLists] = {
object ApplyParams {
def apply[Head <: runtime.ArgumentList: WeakTypeTag, Tail <: runtime.ArgumentLists: WeakTypeTag]
: WeakTypeTag[runtime.ArgumentLists.List[Head, Tail]] =
weakTypeTag[runtime.ArgumentLists.List[Head, Tail]]
}

ApplyParams(head, tail)
final def applyFromBody(f: Tree): Tree = {
val ctorType = extractCtorType(f)
ctorType.Underlying
}

private def constructArgumentListType(
t: ValDef,
args: WeakTypeTag[? <: runtime.ArgumentList]
): WeakTypeTag[? <: runtime.ArgumentList] = {
object ApplyParam {
def apply[ParamName <: String: WeakTypeTag, ParamType: WeakTypeTag, Args <: runtime.ArgumentList: WeakTypeTag]
: WeakTypeTag[runtime.ArgumentList.Argument[ParamName, ParamType, Args]] =
weakTypeTag[runtime.ArgumentList.Argument[ParamName, ParamType, Args]]
}

ApplyParam(
c.WeakTypeTag(c.internal.constantType(Constant(t.name.decodedName.toString))),
c.WeakTypeTag(t.tpt.tpe),
args
)
private def extractCtorType(f: Tree): ExistentialCtor = ExistentialCtor.parse(f) match {
case Right(ctor) => ctor
case Left(error) => c.abort(c.enclosingPosition, error)
}

private def invalidConstructor(t: Tree): String =
s"Expected function, instead got: ${Console.MAGENTA}$t${Console.RESET}: ${Console.MAGENTA}${t.tpe}${Console.RESET}"
}
}
Loading

0 comments on commit df67bb0

Please sign in to comment.