diff options
author | Jon Pretty <jon.pretty@propensive.com> | 2017-06-05 14:57:41 -0600 |
---|---|---|
committer | Jon Pretty <jon.pretty@propensive.com> | 2017-06-05 14:57:41 -0600 |
commit | 0ca7b31dcd258cedda79dee1f42459454b5685b1 (patch) | |
tree | a2fd98d3d9e7ec4f177be4a470f7d9b81b18e704 | |
parent | 582844689f50ab13060c27ad24be081eba779fca (diff) | |
download | magnolia-0ca7b31dcd258cedda79dee1f42459454b5685b1.tar.gz magnolia-0ca7b31dcd258cedda79dee1f42459454b5685b1.tar.bz2 magnolia-0ca7b31dcd258cedda79dee1f42459454b5685b1.zip |
Apparent progress
-rw-r--r-- | core/src/main/scala/generic.scala | 67 | ||||
-rw-r--r-- | core/src/main/scala/generic.scala~ | 173 | ||||
-rw-r--r-- | tests/shared/src/main/scala/magnolia/main.scala | 13 |
3 files changed, 213 insertions, 40 deletions
diff --git a/core/src/main/scala/generic.scala b/core/src/main/scala/generic.scala index 86b4d24..ffb2c14 100644 --- a/core/src/main/scala/generic.scala +++ b/core/src/main/scala/generic.scala @@ -17,7 +17,7 @@ object GlobalMutableState { private[magnolia] var state: Map[Pos, ListMap[c.universe.Type forSome { val c: whitebox.Context }, c.universe.TermName forSome { val c: whitebox.Context }]] = Map() private[magnolia] def push(c: whitebox.Context)(key: c.universe.Type, value: c.universe.TermName): Unit = { - println(s"push($key)") + println(s"push($key, $value)") state = state.updated(Pos(c.enclosingPosition), state.get(Pos(c.enclosingPosition)).map { m => m.updated(key, value) }.getOrElse(ListMap(key -> value))) @@ -30,11 +30,12 @@ object GlobalMutableState { println("state = "+state) } - private[magnolia] def has(c: whitebox.Context)(key: c.universe.Type): Option[c.universe.TermName] = + private[magnolia] def has(c: whitebox.Context)(key: c.universe.Type): Option[c.universe.TermName] = { try state(Pos(c.enclosingPosition)).get(key).asInstanceOf[Option[c.universe.TermName]] catch { case e: Exception => ??? } + } private[magnolia] var searchType: AnyRef = null } @@ -70,26 +71,34 @@ abstract class GenericMacro(whiteboxContext: whitebox.Context) { def getImplicit(genericType: c.universe.Type, typeConstructor: c.universe.Type, - myName: c.universe.TermName, - count: Int): c.Tree = { + myName: c.universe.TermName): c.Tree = { import c.universe._ - println(s"getImplicit1($genericType, $count)") + //println(s"getImplicit1($genericType)") val x = GlobalMutableState.has(c)(genericType) - x.foreach { y => println("y = "+y) } val result = x.map { nm => q"$nm" }.orElse { val searchType = appliedType(typeConstructor, genericType) if(GlobalMutableState.has(c)(genericType).isEmpty) { GlobalMutableState.searchType = genericType - val inferredImplicit = try Some(c.inferImplicitValue(searchType, false, false)) catch { + val inferredImplicit = try Some({ + //println(s"ONE: $genericType -> $myName") + //GlobalMutableState.push(c)(genericType, myName) + val imp = c.inferImplicitValue(searchType, false, false) + //GlobalMutableState.pop(c) + q"""{ + def $myName = $imp + $myName + }""" + }) catch { case e: Exception => None } + object transformer extends Transformer { override def transform(tree: Tree): Tree = tree match { case ta@TypeApply(Select(Literal(Constant(method: String)), TermName("asInstanceOf")), List(tpe)) => - println(s"Found typeapply: ${tpe}") + println(s"FOUND TYPEAPPLY: ${ta}") val m = TermName(method) q"$m" case _ => super.transform(tree) @@ -97,31 +106,29 @@ abstract class GenericMacro(whiteboxContext: whitebox.Context) { } inferredImplicit.map { imp => - c.resetLocalAttrs(imp) - transformer.transform(imp) - c.typecheck(imp) + val x = c.untypecheck(imp) + val z = c.untypecheck(transformer.transform(imp)) + println("x: "+x+", z: "+z) + z }.orElse { - directInferImplicit(genericType, typeConstructor, count + 1) + directInferImplicit(genericType, typeConstructor) } } else { - directInferImplicit(genericType, typeConstructor, count + 1) + directInferImplicit(genericType, typeConstructor) } }.getOrElse { println("Really failed to find extractor for type "+genericType) c.abort(c.enclosingPosition, "Could not find extractor for type "+genericType) } - //println(" = "+result) - result } def directInferImplicit(genericType: c.universe.Type, - typeConstructor: c.universe.Type, - count: Int): Option[c.Tree] = { + typeConstructor: c.universe.Type): Option[c.Tree] = { import c.universe._ - println(s"directInferImplicit($genericType, $count)") + //println(s"directInferImplicit($genericType)") val myName: TermName = TermName(c.freshName(genericType.typeSymbol.name.encodedName.toString.toLowerCase+"Extractor")) val typeSymbol = genericType.typeSymbol @@ -137,8 +144,9 @@ abstract class GenericMacro(whiteboxContext: whitebox.Context) { case m: MethodSymbol if m.isCaseAccessor => m.asMethod }.map { param => val returnType = param.returnType + println(s"TWO: $genericType -> $myName") GlobalMutableState.push(c)(genericType, myName) - val imp = getImplicit(returnType, typeConstructor, myName, count) + val imp = getImplicit(returnType, typeConstructor, myName) GlobalMutableState.pop(c) val dereferenced = dereferenceValue(c)(q"src", param.name.toString) callDelegateMethod(c)(imp, dereferenced) @@ -148,8 +156,9 @@ abstract class GenericMacro(whiteboxContext: whitebox.Context) { } else if(isSealedTrait) { val subtypes = classType.get.knownDirectSubclasses.to[List] Some(subtypes.map(_.asType.toType).map { searchType => + println(s"THREE: $genericType -> $myName") GlobalMutableState.push(c)(genericType, myName) - val res = getImplicit(searchType, typeConstructor, myName, count) + val res = getImplicit(searchType, typeConstructor, myName) GlobalMutableState.pop(c) res }.reduce(coproductReduction(c))).map { imp => @@ -181,36 +190,32 @@ abstract class GenericMacro(whiteboxContext: whitebox.Context) { def generic[T: c.WeakTypeTag, Tc: c.WeakTypeTag]: c.Tree = try { import c.universe._ - println("Entering generic for type "+weakTypeOf[T]) + //println("Entering generic for type "+weakTypeOf[T]) val genericType: Type = weakTypeOf[T] val reentrant = genericType == GlobalMutableState.searchType - println(s"LAST TYPE = ${GlobalMutableState.searchType}; THIS TYPE = $genericType") + //println(s"previous: ${GlobalMutableState.searchType}; THIS TYPE = $genericType") val result: Option[c.Tree] = if(reentrant) { - println("Reentrant.") + //println("Reentrant.") throw ReentrantException() } else if(GlobalMutableState.searchType != null) { GlobalMutableState.has(c)(genericType) match { case None => val typeConstructor: Type = weakTypeOf[Tc].typeConstructor - directInferImplicit(genericType, typeConstructor, 0) + directInferImplicit(genericType, typeConstructor) case Some(t) => val str = t.toString val typeConstructor: Type = weakTypeOf[Tc].typeConstructor val searchType = appliedType(typeConstructor, genericType) + println(s"$str ===>>> ${searchType}") Some(q"$str.asInstanceOf[${searchType}]") } } else { val typeConstructor: Type = weakTypeOf[Tc].typeConstructor - directInferImplicit(genericType, typeConstructor, 0) + directInferImplicit(genericType, typeConstructor) } - println(result) - try result.map { tree => c.typecheck(tree) } catch { - case e: Exception => - println(result) - println("Failed to typecheck because: "+e) - } + println("Final result: "+result) result.getOrElse { println("FAIL.") diff --git a/core/src/main/scala/generic.scala~ b/core/src/main/scala/generic.scala~ new file mode 100644 index 0000000..4767435 --- /dev/null +++ b/core/src/main/scala/generic.scala~ @@ -0,0 +1,173 @@ +package magnolia + +import scala.reflect._, macros._ +import macrocompat.bundle +import scala.util.Try + +import scala.collection.immutable.ListMap + +object GlobalMutableState { + private[magnolia] var state: ListMap[AnyRef, AnyRef] = ListMap() + + private[magnolia] def push(key: AnyRef, value: AnyRef): Unit = { + state += ((key, value)) + } + + private[magnolia] def pop(): Unit = { + state = state.init + } + + private[magnolia] def has(c: whitebox.Context)(key: AnyRef): Option[c.universe.Tree] = + state.get(key).asInstanceOf[Option[c.universe.Tree]] + + private[magnolia] var searchType: AnyRef = null +} + +@bundle +class Macros(val context: whitebox.Context) extends GenericMacro(context) { + + + protected def classBody(context: whitebox.Context)(genericType: context.Type, implementation: context.Tree): context.Tree = { + import context.universe._ + q"""def extract(src: _root_.magnolia.Thing): $genericType = $implementation""" + } + + protected def dereferenceValue(context: whitebox.Context)(value: context.Tree, elem: String): context.Tree = { + import context.universe._ + q"$value.access($elem)" + } + + protected def callDelegateMethod(context: whitebox.Context)(value: context.Tree, argument: context.Tree): context.Tree = { + import context.universe._ + q"$value.extract($argument)" + } + + protected def coproductReduction(context: whitebox.Context)(left: context.Tree, right: context.Tree): context.Tree = { + import context.universe._ + q"$left.orElse($right)" + } +} + +abstract class GenericMacro(whiteboxContext: whitebox.Context) { + + val c = whiteboxContext + + def getImplicit(genericType: c.universe.Type, + typeConstructor: c.universe.Type, + myName: c.universe.TermName, + count: Int): c.Tree = { + + import c.universe._ + println(s"getImplicit($genericType, $count)") + val result = GlobalMutableState.has(c)(genericType).map { nm => q"$nm" }.orElse { + val searchType = appliedType(typeConstructor, genericType) + if(GlobalMutableState.has(c)(genericType).isEmpty) { + scala.util.Try { + GlobalMutableState.searchType = genericType + c.inferImplicitValue(searchType, false, false) + }.toOption.orElse { + println("Recursing") + directInferImplicit(genericType, typeConstructor, count + 1) + } + } else { + directInferImplicit(genericType, typeConstructor, count + 1) + } + }.getOrElse { + println("Failed 2.") + c.abort(c.enclosingPosition, "Could not find extractor for type "+genericType) + } + + + result + } + + def directInferImplicit(genericType: c.universe.Type, + typeConstructor: c.universe.Type, + count: Int): Option[c.Tree] = { + import c.universe._ + + println(s"directInferImplicit($genericType, $count)") + + val myName: TermName = TermName(c.freshName(genericType.typeSymbol.name.encodedName.toString.toLowerCase+"Extractor")) + val typeSymbol = genericType.typeSymbol + val classType = if(typeSymbol.isClass) Some(typeSymbol.asClass) else None + val isCaseClass = classType.map(_.isCaseClass).getOrElse(false) + val isSealedTrait = classType.map(_.isSealed).getOrElse(false) + val isAnyVal = genericType <:< typeOf[AnyVal] + + val resultType = appliedType(typeConstructor, genericType) + + val construct = if(isCaseClass) { + val implicits = genericType.decls.collect { + case m: MethodSymbol if m.isCaseAccessor => m.asMethod + }.map { param => + val returnType = param.returnType + GlobalMutableState.push(genericType, myName) + val imp = getImplicit(returnType, typeConstructor, myName, count) + GlobalMutableState.pop() + val dereferenced = dereferenceValue(c)(q"src", param.name.toString) + callDelegateMethod(c)(imp, dereferenced) + } + + Some(q"new $genericType(..$implicits)") + } else if(isSealedTrait) { + val subtypes = classType.get.knownDirectSubclasses.to[List] + Some(subtypes.map(_.asType.toType).map { searchType => + GlobalMutableState.push(genericType, myName) + val res = getImplicit(searchType, typeConstructor, myName, count) + GlobalMutableState.pop() + res + }.reduce(coproductReduction(c))).map { imp => + callDelegateMethod(c)(imp, q"src") + } + + } else None + + val result = construct.map { const => + + val methodImplementation = classBody(c)(genericType, const) + q"""{ + def $myName: $resultType = new $resultType { + $methodImplementation + } + $myName + }""" + } + + + result + } + + protected def classBody(c: whitebox.Context)(genericType: c.Type, implementation: c.Tree): c.Tree + protected def coproductReduction(c: whitebox.Context)(left: c.Tree, right: c.Tree): c.Tree + protected def dereferenceValue(c: whitebox.Context)(value: c.Tree, elem: String): c.Tree + protected def callDelegateMethod(c: whitebox.Context)(value: c.Tree, argument: c.Tree): c.Tree + + def generic[T: c.WeakTypeTag, Tc: c.WeakTypeTag]: c.Tree = { + import c.universe._ + + val genericType: Type = weakTypeOf[T] + val reentrant = genericType == GlobalMutableState.searchType + if(reentrant) { + println("Reentrant.") + ??? + } + val typeConstructor: Type = weakTypeOf[Tc].typeConstructor + + val result = directInferImplicit(genericType, typeConstructor, 0) + + println(result) + try result.map { tree => c.typecheck(tree) } catch { + case e: Exception => + println(result) + println("Failed to typecheck because: "+e) + } + + result.getOrElse { + println("Count not infer extractor...") + c.abort(c.enclosingPosition, "Could not infer extractor. Sorry.") + } + } + +} + diff --git a/tests/shared/src/main/scala/magnolia/main.scala b/tests/shared/src/main/scala/magnolia/main.scala index c2a7d70..aaa8ec9 100644 --- a/tests/shared/src/main/scala/magnolia/main.scala +++ b/tests/shared/src/main/scala/magnolia/main.scala @@ -7,19 +7,14 @@ case class Quux(two: Double, three: List[Bar]) extends Bar case class Bippy(four: String) class Baz(val x: Bar) extends AnyVal -//case class X(y: Y) - -//case class Y(x: X) - object Main { def main(args: Array[String]): Unit = { - println(implicitly[Extractor[Bar]].extract(Thing("42"))) - - - - + + + } + } |