Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
name: Continuous integration
on:
pull_request:
branches: [ master ]
branches: [ master, series/0.2.x ]

jobs:
ci:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,10 +90,10 @@ object Transformer {
given [Source, Dest >: Source]: Identity[Source, Dest] = Identity[Source, Dest]

inline given forProducts[Source, Dest](using Mirror.ProductOf[Source], Mirror.ProductOf[Dest]): ForProduct[Source, Dest] =
ForProduct.make(DerivationMacros.deriveProductTransformer[Source, Dest])
ForProduct.make(DerivedTransformers.product[Source, Dest])

inline given forCoproducts[Source, Dest](using Mirror.SumOf[Source], Mirror.SumOf[Dest]): ForCoproduct[Source, Dest] =
ForCoproduct.make(DerivationMacros.deriveCoproductTransformer[Source, Dest])
ForCoproduct.make(DerivedTransformers.coproduct[Source, Dest])

given [Source, Dest](using Transformer[Source, Dest]): Transformer[Source, Option[Dest]] =
from => Transformer[Source, Dest].transform.andThen(Some.apply)(from)
Expand All @@ -112,8 +112,8 @@ object Transformer {
): Transformer[SourceCollection[Source], DestCollection[Dest]] = from => from.map(trans.transform).to(factory)

inline given fromAnyVal[Source <: AnyVal, Dest]: FromAnyVal[Source, Dest] =
FromAnyVal.make(DerivationMacros.deriveFromAnyValTransformer[Source, Dest])
FromAnyVal.make(DerivedTransformers.fromAnyVal[Source, Dest])

inline given toAnyVal[Source, Dest <: AnyVal]: ToAnyVal[Source, Dest] =
ToAnyVal.make(DerivationMacros.deriveToAnyValTransformer[Source, Dest])
ToAnyVal.make(DerivedTransformers.toAnyVal[Source, Dest])
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,6 @@ import io.github.arainko.ducktape.internal.macros.*
final class AppliedBuilder[Source, Dest](appliedTo: Source) {

inline def transform(inline config: BuilderConfig[Source, Dest]*): Dest =
TransformerMacros.transformConfigured(appliedTo, config)
Transformations.transformConfigured(appliedTo, config*)

}
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ final class AppliedViaBuilder[Source, Dest, Func, ArgSelector <: FunctionArgumen
inline def transform(
inline config: ArgBuilderConfig[Source, Dest, ArgSelector]*
)(using Source: Mirror.ProductOf[Source]): Dest =
ProductTransformerMacros.viaConfigured[Source, Dest, Func, ArgSelector](source, function, config*)
Transformations.viaConfigured[Source, Dest, Func, ArgSelector](source, function, config*)

}

Expand All @@ -27,6 +27,6 @@ object AppliedViaBuilder {

transparent inline def create[Source, Func](source: Source, inline func: Func)(using Func: FunctionMirror[Func]): Any = {
val builder = instance[Source, Func.Return, Func, Nothing](source, func)
FunctionMacros.namedArguments(func, builder)
Functions.refineFunctionArguments(func, builder)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,5 @@ import scala.deriving.Mirror

final class DefinitionBuilder[Source, Dest] {
inline def build(inline config: BuilderConfig[Source, Dest]*): Transformer[Source, Dest] =
from => TransformerMacros.transformConfigured(from, config)
from => Transformations.transformConfigured(from, config*)
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ final class DefinitionViaBuilder[Source, Dest, Func, ArgSelector <: FunctionArgu
inline def build(
inline config: ArgBuilderConfig[Source, Dest, ArgSelector]*
)(using Mirror.ProductOf[Source]): Transformer[Source, Dest] =
from => ProductTransformerMacros.viaConfigured[Source, Dest, Func, ArgSelector](from, function, config*)
from => Transformations.viaConfigured[Source, Dest, Func, ArgSelector](from, function, config*)

}

Expand All @@ -27,7 +27,7 @@ object DefinitionViaBuilder {
extension [Source](partial: PartiallyApplied[Source]) {
transparent inline def apply[Func](inline func: Func)(using Func: FunctionMirror[Func]): Any = {
val builder = instance[Source, Func.Return, Func, Nothing](func)
FunctionMacros.namedArguments(func, builder)
Functions.refineFunctionArguments(func, builder)
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,5 @@ object FunctionMirror extends FunctionMirror[Any => Any] {

override type Return = Any

transparent inline given [F]: FunctionMirror[F] = FunctionMacros.createMirror[F]
transparent inline given [F]: FunctionMirror[F] = Functions.deriveMirror[F]
}
Original file line number Diff line number Diff line change
@@ -1,28 +1,24 @@
package io.github.arainko.ducktape.internal.macros

import io.github.arainko.ducktape.*
import io.github.arainko.ducktape.internal.modules.MaterializedConfiguration.*
import io.github.arainko.ducktape.internal.modules.*

import scala.deriving.*
import scala.quoted.*

private[ducktape] final class CoproductTransformerMacros(using val quotes: Quotes)
extends Module,
CaseModule,
FieldModule,
SelectorModule,
MirrorModule,
ConfigurationModule {
import quotes.reflect.*
import MaterializedConfiguration.*
// Ideally should live in `modules` but due to problems with ProductTransformations and LiftTransformation
// is kept here for consistency
private[ducktape] object CoproductTransformations {

def transform[Source: Type, Dest: Type](
sourceValue: Expr[Source],
Source: Expr[Mirror.SumOf[Source]],
Dest: Expr[Mirror.SumOf[Dest]]
): Expr[Dest] = {
)(using Quotes): Expr[Dest] = {
given Cases.Source = Cases.Source.fromMirror(Source)
given Cases.Dest = Cases.Dest.fromMirror(Dest)

val ifBranches = singletonIfBranches[Source, Dest](sourceValue, Cases.source.value)
ifStatement(ifBranches).asExprOf[Dest]
}
Expand All @@ -32,7 +28,9 @@ private[ducktape] final class CoproductTransformerMacros(using val quotes: Quote
config: Expr[Seq[BuilderConfig[Source, Dest]]],
Source: Expr[Mirror.SumOf[Source]],
Dest: Expr[Mirror.SumOf[Dest]]
): Expr[Dest] = {
)(using Quotes): Expr[Dest] = {
import quotes.reflect.*

given Cases.Source = Cases.Source.fromMirror(Source)
given Cases.Dest = Cases.Dest.fromMirror(Dest)
val materializedConfig =
Expand All @@ -58,17 +56,17 @@ private[ducktape] final class CoproductTransformerMacros(using val quotes: Quote
case ConfiguredCase(config, source) =>
config match {
case Coproduct.Computed(tpe, function) =>
val cond = source.tpe.asType match {
val cond = source.tpe match {
case '[tpe] => '{ $sourceValue.isInstanceOf[tpe] }
}
val castedSource = tpe.asType match {
val castedSource = tpe match {
case '[tpe] => '{ $sourceValue.asInstanceOf[tpe] }
}
val value = '{ $function($castedSource) }
cond.asTerm -> value.asTerm

case Coproduct.Const(tpe, value) =>
val cond = source.tpe.asType match {
val cond = source.tpe match {
case '[tpe] => '{ $sourceValue.isInstanceOf[tpe] }
}
cond.asTerm -> value.asTerm
Expand All @@ -81,20 +79,27 @@ private[ducktape] final class CoproductTransformerMacros(using val quotes: Quote
private def singletonIfBranches[Source: Type, Dest: Type](
sourceValue: Expr[Source],
sourceCases: List[Case]
)(using Cases.Dest) = {
)(using Quotes, Cases.Dest) = {
import quotes.reflect.*

sourceCases.map { source =>
source -> Cases.dest
.get(source.name)
.getOrElse(abort(Failure.NoChildMapping(source.name, TypeRepr.of[Dest])))
.getOrElse(Failure.abort(Failure.NoChildMapping(source.name, summon[Type[Dest]])))
}.map { (source, dest) =>
val cond = source.tpe.asType match {
val cond = source.tpe match {
case '[tpe] => '{ $sourceValue.isInstanceOf[tpe] }
}
cond.asTerm -> dest.materializeSingleton.getOrElse(abort(Failure.CannotMaterializeSingleton(dest.tpe)))

cond.asTerm ->
dest.materializeSingleton
.getOrElse(Failure.abort(Failure.CannotMaterializeSingleton(dest.tpe)))
}
}

private def ifStatement(branches: List[(Term, Term)]): Term = {
private def ifStatement(using Quotes)(branches: List[(quotes.reflect.Term, quotes.reflect.Term)]): quotes.reflect.Term = {
import quotes.reflect.*

branches match {
case (p1, a1) :: xs =>
If(p1, a1, ifStatement(xs))
Expand All @@ -105,29 +110,3 @@ private[ducktape] final class CoproductTransformerMacros(using val quotes: Quote

private case class ConfiguredCase(config: Coproduct, subcase: Case)
}

private[ducktape] object CoproductTransformerMacros {
inline def transform[Source, Dest](source: Source)(using
Source: Mirror.SumOf[Source],
Dest: Mirror.SumOf[Dest]
): Dest = ${ transformMacro[Source, Dest]('source, 'Source, 'Dest) }

def transformMacro[Source: Type, Dest: Type](
source: Expr[Source],
Source: Expr[Mirror.SumOf[Source]],
Dest: Expr[Mirror.SumOf[Dest]]
)(using Quotes): Expr[Dest] = CoproductTransformerMacros().transform(source, Source, Dest)

inline def transformConfigured[Source, Dest](source: Source, inline config: BuilderConfig[Source, Dest]*)(using
Source: Mirror.SumOf[Source],
Dest: Mirror.SumOf[Dest]
): Dest =
${ transformConfiguredMacro[Source, Dest]('source, 'config, 'Source, 'Dest) }

def transformConfiguredMacro[Source: Type, Dest: Type](
source: Expr[Source],
config: Expr[Seq[BuilderConfig[Source, Dest]]],
Source: Expr[Mirror.SumOf[Source]],
Dest: Expr[Mirror.SumOf[Dest]]
)(using Quotes): Expr[Dest] = CoproductTransformerMacros().transformConfigured(source, config, Source, Dest)
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@ private[ducktape] object DebugMacros {
value
}

inline def code[A](inline value: A): A = ${ codeCompiletimeMacro('value) }
inline def code[A](inline value: A): A = ${ codeMacro('value) }

def codeCompiletimeMacro[A: Type](value: Expr[A])(using Quotes): Expr[A] = {
def codeMacro[A: Type](value: Expr[A])(using Quotes): Expr[A] = {
import quotes.reflect.*
val struct = Printer.TreeShortCode.show(value.asTerm)
report.info(struct)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
package io.github.arainko.ducktape.internal.macros

import io.github.arainko.ducktape.*
import io.github.arainko.ducktape.internal.macros.{ CoproductTransformations, ProductTransformations }
import io.github.arainko.ducktape.internal.modules.*

import scala.deriving.*
import scala.quoted.*

private[ducktape] object DerivationMacros {
inline def deriveProductTransformer[Source, Dest](using
private[ducktape] object DerivedTransformers {
inline def product[Source, Dest](using
Source: Mirror.ProductOf[Source],
Dest: Mirror.ProductOf[Dest]
): Transformer[Source, Dest] = ${ deriveProductTransformerMacro('Source, 'Dest) }
Expand All @@ -15,9 +17,9 @@ private[ducktape] object DerivationMacros {
Source: Expr[Mirror.ProductOf[Source]],
Dest: Expr[Mirror.ProductOf[Dest]]
)(using Quotes): Expr[Transformer[Source, Dest]] =
'{ source => ${ ProductTransformerMacros.transformMacro[Source, Dest]('source, Source, Dest) } }
'{ source => ${ ProductTransformations.transform[Source, Dest]('source, Source, Dest) } }

inline def deriveCoproductTransformer[Source, Dest](using
inline def coproduct[Source, Dest](using
Source: Mirror.SumOf[Source],
Dest: Mirror.SumOf[Dest]
): Transformer[Source, Dest] = ${ deriveCoproductTransformerMacro[Source, Dest]('Source, 'Dest) }
Expand All @@ -26,17 +28,17 @@ private[ducktape] object DerivationMacros {
Source: Expr[Mirror.SumOf[Source]],
Dest: Expr[Mirror.SumOf[Dest]]
)(using Quotes): Expr[Transformer[Source, Dest]] =
'{ source => ${ CoproductTransformerMacros.transformMacro[Source, Dest]('source, Source, Dest) } }
'{ source => ${ CoproductTransformations.transform[Source, Dest]('source, Source, Dest) } }

inline def deriveToAnyValTransformer[Source, Dest <: AnyVal]: Transformer[Source, Dest] =
inline def toAnyVal[Source, Dest <: AnyVal]: Transformer[Source, Dest] =
${ deriveToAnyValTransformerMacro[Source, Dest] }

def deriveToAnyValTransformerMacro[Source: Type, Dest <: AnyVal: Type](using Quotes): Expr[Transformer[Source, Dest]] =
'{ source => ${ ProductTransformerMacros.transformToAnyValMacro('source) } }
'{ source => ${ ProductTransformations.transformToAnyVal('source) } }

inline def deriveFromAnyValTransformer[Source <: AnyVal, Dest] =
inline def fromAnyVal[Source <: AnyVal, Dest] =
${ deriveFromAnyValTransformerMacro[Source, Dest] }

def deriveFromAnyValTransformerMacro[Source <: AnyVal: Type, Dest: Type](using Quotes): Expr[Transformer[Source, Dest]] =
'{ source => ${ ProductTransformerMacros.transformFromAnyValMacro('source) } }
'{ source => ${ ProductTransformations.transformFromAnyVal('source) } }
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,13 @@ import io.github.arainko.ducktape.internal.modules.*

import scala.quoted.*

private[ducktape] final class FunctionMacros(using val quotes: Quotes) extends Module, SelectorModule, FieldModule, MirrorModule {
import FunctionMacros.*
import quotes.reflect.*
// Ideally should live in `modules` but due to problems with ProductTransformations and LiftTransformation
// is kept here for consistency
private[ducktape] object FunctionMacros {

private val cons = TypeRepr.of[*:]
private val emptyTuple = TypeRepr.of[EmptyTuple]
private val functionArguments = TypeRepr.of[FunctionArguments]
def deriveMirror[Func: Type](using Quotes): Expr[FunctionMirror[Func]] = {
import quotes.reflect.*

def createMirror[Func: Type]: Expr[FunctionMirror[Func]] =
TypeRepr.of[Func] match {
case tpe @ AppliedType(_, tpeArgs) if tpe.isFunctionType =>
val returnTpe = tpeArgs.last
Expand All @@ -31,37 +29,29 @@ private[ducktape] final class FunctionMacros(using val quotes: Quotes) extends M

case other => report.errorAndAbort(s"FunctionMirrors can only be created for functions. Got ${other.show} instead.")
}
}

def refineFunctionArguments[Func: Type, F[x <: FunctionArguments]: Type](
function: Expr[Func],
initial: Expr[F[Nothing]]
)(using Quotes) = {
import quotes.reflect.*

def namedArguments[Func: Type, F[_ <: FunctionArguments]: Type](function: Expr[Func], initial: Expr[F[Nothing]]) =
function.asTerm match {
case func @ FunctionLambda(valDefs, _) =>
refine(functionArguments, valDefs).asType match {
refine(TypeRepr.of[FunctionArguments], valDefs).asType match {
case '[IsFuncArgs[args]] => '{ $initial.asInstanceOf[F[args]] }
}

case other => report.errorAndAbort(s"Failed to extract named arguments from ${other.show}")
}
}

private def refine(tpe: TypeRepr, valDefs: List[ValDef]) =
valDefs.foldLeft(functionArguments)((tpe, valDef) => Refinement(tpe, valDef.name, valDef.tpt.tpe))
private def refine(using Quotes)(tpe: quotes.reflect.TypeRepr, valDefs: List[quotes.reflect.ValDef]) = {
import quotes.reflect.*

}
valDefs.foldLeft(TypeRepr.of[FunctionArguments])((tpe, valDef) => Refinement(tpe, valDef.name, valDef.tpt.tpe))
}

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

transparent inline def createMirror[F]: FunctionMirror[F] = ${ createMirrorMacro[F] }

def createMirrorMacro[Func: Type](using Quotes): Expr[FunctionMirror[Func]] =
FunctionMacros().createMirror[Func]

transparent inline def namedArguments[Func, F[_ <: FunctionArguments]](
inline function: Func,
initial: F[Nothing]
)(using FunctionMirror[Func]) = ${ namedArgumentsMacro[Func, F]('function, 'initial) }

def namedArgumentsMacro[Func: Type, F[_ <: FunctionArguments]: Type](
function: Expr[Func],
initial: Expr[F[Nothing]]
)(using Quotes) = FunctionMacros().namedArguments[Func, F](function, initial)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package io.github.arainko.ducktape.internal.macros

import io.github.arainko.ducktape.function.*
import io.github.arainko.ducktape.internal.macros.FunctionMacros

private[ducktape] object Functions {
transparent inline def deriveMirror[Func]: FunctionMirror[Func] = ${ FunctionMacros.deriveMirror[Func] }

transparent inline def refineFunctionArguments[Func, F[x <: FunctionArguments]](
inline function: Func,
initial: F[Nothing]
)(using FunctionMirror[Func]): Any = ${ FunctionMacros.refineFunctionArguments[Func, F]('function, 'initial) }
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
package io.github.arainko.ducktape.internal.modules.liftTransformation
package io.github.arainko.ducktape.internal.macros

import io.github.arainko.ducktape.Transformer
import io.github.arainko.ducktape.internal.modules.liftTransformation.TransformerLambda.*
import io.github.arainko.ducktape.internal.modules.liftTransformation.*
import io.github.arainko.ducktape.internal.modules.TransformerLambda.*
import io.github.arainko.ducktape.internal.modules.*

import scala.collection.Factory
import scala.quoted.*

//TODO: if this is moved to `modules` the compiler crashes, investigate further?
private[ducktape] object LiftTransformation {

def liftTransformation[A: Type, B: Type](transformer: Expr[Transformer[A, B]], appliedTo: Expr[A])(using Quotes): Expr[B] =
Expand Down
Loading