aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJon Pretty <jon.pretty@propensive.com>2017-05-25 11:26:28 -0600
committerJon Pretty <jon.pretty@propensive.com>2017-05-25 11:26:28 -0600
commit17cfdb350fa38454a76ed5370ac6f36c3a532d3e (patch)
tree56ab2e93523f870687ad1bf9dedcb2a4ada920c0
parent134cd5e20030e558dfb75e07f5e3eb3211680e0c (diff)
downloadmagnolia-17cfdb350fa38454a76ed5370ac6f36c3a532d3e.tar.gz
magnolia-17cfdb350fa38454a76ed5370ac6f36c3a532d3e.tar.bz2
magnolia-17cfdb350fa38454a76ed5370ac6f36c3a532d3e.zip
Progress with mutual recursion
-rw-r--r--core/src/main/scala/generic.scala101
-rw-r--r--examples/src/main/scala/example.scala8
-rw-r--r--tests/shared/src/main/scala/magnolia/main.scala10
3 files changed, 52 insertions, 67 deletions
diff --git a/core/src/main/scala/generic.scala b/core/src/main/scala/generic.scala
index 63f3d8f..c045cbc 100644
--- a/core/src/main/scala/generic.scala
+++ b/core/src/main/scala/generic.scala
@@ -3,74 +3,58 @@ package magnolia
import scala.reflect._, macros._
import macrocompat.bundle
-object GlobalState {
- var globalState: Map[AnyRef, AnyRef] = Map()
+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.TermName] =
+ state.get(key).asInstanceOf[Option[c.universe.TermName]]
}
@bundle
class Macros(val c: whitebox.Context) {
+ def dereference(path: c.Tree, elem: String): c.Tree = path
+
def getImplicit(genericType: c.universe.Type,
- typeConstructor: c.universe.Type/*,
- scope: Map[c.universe.Type, c.universe.TermName]*/): c.Tree = {
+ typeConstructor: c.universe.Type,
+ myName: c.universe.TermName): c.Tree = {
import c.universe._
- val scope = GlobalState.globalState.asInstanceOf[Map[Type, TermName]]
-
- scope.get(genericType) match {
- case Some(ref) =>
- q"$ref"
- case None =>
-
- val searchType = appliedType(typeConstructor, genericType)
- println(s"${scope.keySet} vs $genericType")
- println(s"inferring on $genericType")
- try c.inferImplicitValue(searchType, false, false) catch {
- case e: Exception =>
- go(genericType, typeConstructor/*, scope*/)
- }
- }
-
- scope.get(genericType).map { nm =>
- println("substituting "+nm)
- q"$nm"
- }.orElse {
+ GlobalMutableState.push(genericType, myName)
+
+ val result = GlobalMutableState.has(c)(genericType).map { nm => q"$nm" }.orElse {
val searchType = appliedType(typeConstructor, genericType)
- println(s"${scope.keySet} vs $genericType")
- if(!scope.keySet.contains(genericType)) {
- println(s"inferring on $genericType")
- Option({
- val x = try c.inferImplicitValue(searchType, false, false) catch {
- case e: Exception => null
+ if(GlobalMutableState.has(c)(genericType).isEmpty) {
+ val inferredImplicit =
+ try Some(c.inferImplicitValue(searchType, false, false)) catch {
+ case e: Exception => None
}
- println("Managed to infer "+x)
- x
- }).orElse {
- println("Failed, so recursing")
- go(genericType, typeConstructor/*, scope*/)
+
+ inferredImplicit.orElse {
+ directInferImplicit(genericType, typeConstructor)
}
} else {
- println("recursing")
- go(genericType, typeConstructor/*, scope*/)
+ directInferImplicit(genericType, typeConstructor)
}
}.getOrElse {
c.abort(c.enclosingPosition, "Could not find extractor for type "+genericType)
}
+
+ GlobalMutableState.pop()
+
+ result
}
- def go(genericType: c.universe.Type,
- typeConstructor: c.universe.Type/*,
- scope: Map[c.universe.Type, c.universe.TermName]*/): Option[c.Tree] = {
+ def directInferImplicit(genericType: c.universe.Type,
+ typeConstructor: c.universe.Type): Option[c.Tree] = {
import c.universe._
- println(s"go($genericType, ${GlobalState.globalState})")
-
-
val myName = TermName(c.freshName("extractor$"))
- println(s"before: ${GlobalState.globalState}")
- GlobalState.globalState = GlobalState.globalState + (genericType -> myName)
- println(s"after: ${GlobalState.globalState}")
val typeSymbol = genericType.typeSymbol
val classType = if(typeSymbol.isClass) Some(typeSymbol.asClass) else None
val isCaseClass = classType.map(_.isCaseClass).getOrElse(false)
@@ -84,20 +68,17 @@ class Macros(val c: whitebox.Context) {
case m: MethodSymbol if m.isCaseAccessor => m.asMethod
}.map { p =>
val ret = p.returnType
- val imp = getImplicit(ret, typeConstructor/*, newScope*/)
+ val imp = getImplicit(ret, typeConstructor, myName)
q"$imp.extract(src)"
}
Some(q"new $genericType(..$implicits)")
} else if(isSealedTrait) {
- //println(s"$resultType a sealed trait")
val subtypes = classType.get.knownDirectSubclasses.to[List]
- val tries = subtypes.map(_.asType.toType).map(t => getImplicit(t, typeConstructor/*, newScope*/)).foldLeft(q"null": c.Tree) { (a, b) =>
+ Some(subtypes.map(_.asType.toType).map(t => getImplicit(t, typeConstructor, myName)).foldLeft(q"null": c.Tree) { (a, b) =>
q"(try { $b.extract(src) } catch { case e: _root_.java.lang.Exception => $a })"
- }
-
- Some(q"$tries.asInstanceOf[$genericType]")
+ })
} else None
@@ -110,28 +91,24 @@ class Macros(val c: whitebox.Context) {
}"""
}
- //println(s"Generated result for $genericType: $result")
+ //GlobalMutableState.pop()
+
+ println(result)
result
}
- def generic[T: c.WeakTypeTag, Tc: c.WeakTypeTag]: c.Tree = try {
+
+ def generic[T: c.WeakTypeTag, Tc: c.WeakTypeTag]: c.Tree = {
import c.universe._
val genericType: Type = weakTypeOf[T]
val typeConstructor: Type = weakTypeOf[Tc].typeConstructor
- val result = go(genericType, typeConstructor)
-
- println(result)
+ val result = directInferImplicit(genericType, typeConstructor)
result.getOrElse {
c.abort(c.enclosingPosition, "Could not infer extractor. Sorry.")
}
- } catch {
- case e: Exception =>
- println("Macro failed!!! "+e)
- //e.printStackTrace()
- ???
}
}
diff --git a/examples/src/main/scala/example.scala b/examples/src/main/scala/example.scala
index 7e9d80b..a031fbe 100644
--- a/examples/src/main/scala/example.scala
+++ b/examples/src/main/scala/example.scala
@@ -15,8 +15,14 @@ object Extractor extends Extractor_1 {
implicit val intExtractor: Extractor[Int] = Extractor(_.toInt)
implicit val stringExtractor: Extractor[String] = Extractor(identity)
implicit val doubleExtractor: Extractor[Double] = Extractor(_.toDouble)
+
}
-trait Extractor_1 {
+trait Extractor_1 extends Extractor_2 {
+ implicit def listExtractor[T: Extractor]: Extractor[List[T]] = new Extractor[List[T]] {
+ def extract(source: String): List[T] = List(implicitly[Extractor[T]].extract(source))
+ }
+}
+trait Extractor_2 {
implicit def generic[T]: Extractor[T] = macro Macros.generic[T, Extractor[_]]
}
diff --git a/tests/shared/src/main/scala/magnolia/main.scala b/tests/shared/src/main/scala/magnolia/main.scala
index 6bf5f74..f0f2412 100644
--- a/tests/shared/src/main/scala/magnolia/main.scala
+++ b/tests/shared/src/main/scala/magnolia/main.scala
@@ -1,17 +1,19 @@
package magnolia
sealed trait Bar
-case class Foo(one: Int) extends Bar
-case class Quux(two: Int, bar: Bar) extends Bar
-case class Bippy(four: Int, bar: Bar)
+case class Foo(one: String) extends Bar
+case class Quux(two: String, bar: Bar) extends Bar
+case class Bippy(four: String, bar: List[Bar]) extends Bar
case class Baz(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("hello world"))
+ println(implicitly[Extractor[Bar]].extract("42"))
}
}