aboutsummaryrefslogtreecommitdiff
path: root/core/src/main/scala/generic.scala
diff options
context:
space:
mode:
authorJon Pretty <jon.pretty@propensive.com>2017-05-24 20:41:10 +0100
committerJon Pretty <jon.pretty@propensive.com>2017-05-24 20:41:10 +0100
commitb039a653e9e90530a76aef42df9215c151c65b67 (patch)
treeb925339332ed2824bb4a4910800fb0e923719c1a /core/src/main/scala/generic.scala
downloadmagnolia-b039a653e9e90530a76aef42df9215c151c65b67.tar.gz
magnolia-b039a653e9e90530a76aef42df9215c151c65b67.tar.bz2
magnolia-b039a653e9e90530a76aef42df9215c151c65b67.zip
Initial checkin of messy code which appears to be a PoC
Diffstat (limited to 'core/src/main/scala/generic.scala')
-rw-r--r--core/src/main/scala/generic.scala124
1 files changed, 124 insertions, 0 deletions
diff --git a/core/src/main/scala/generic.scala b/core/src/main/scala/generic.scala
new file mode 100644
index 0000000..3355e32
--- /dev/null
+++ b/core/src/main/scala/generic.scala
@@ -0,0 +1,124 @@
+package magnolia
+
+import scala.reflect._, macros._
+import macrocompat.bundle
+
+object GlobalState {
+ var globalState: Map[AnyRef, AnyRef] = Map()
+}
+
+@bundle
+class Macros(val c: whitebox.Context) {
+
+ def getImplicit(genericType: c.universe.Type,
+ typeConstructor: c.universe.Type/*,
+ scope: Map[c.universe.Type, c.universe.TermName]*/): c.Tree = {
+
+ import c.universe._
+
+ val scope = GlobalState.globalState.asInstanceOf[Map[Type, TermName]]
+
+ scope.get(genericType).map { nm =>
+ println("substituting "+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
+ }
+ println("Managed to infer "+x)
+ x
+ }).orElse {
+ println("Failed, so recursing")
+ go(genericType, typeConstructor/*, scope*/)
+ }
+ } else {
+ println("recursing")
+ go(genericType, typeConstructor/*, scope*/)
+ }
+ }.getOrElse {
+ c.abort(c.enclosingPosition, "Could not find extractor for type "+genericType)
+ }
+ }
+
+ def go(genericType: c.universe.Type,
+ typeConstructor: c.universe.Type/*,
+ scope: Map[c.universe.Type, c.universe.TermName]*/): 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)
+ 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 { p =>
+ val ret = p.returnType
+ val imp = getImplicit(ret, typeConstructor/*, newScope*/)
+ 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) =>
+ q"(try { $b.extract(src) } catch { case e: _root_.java.lang.Exception => $a })"
+ }
+
+ Some(q"$tries.asInstanceOf[$genericType]")
+
+ } else None
+
+ val result = construct.map { c =>
+ q"""{
+ def $myName: $resultType = new $resultType {
+ def extract(src: _root_.java.lang.String): $genericType = $c
+ }
+ $myName
+ }"""
+ }
+
+ //println(s"Generated result for $genericType: $result")
+
+ result
+ }
+ def generic[T: c.WeakTypeTag, Tc: c.WeakTypeTag]: c.Tree = try {
+ import c.universe._
+
+ val genericType: Type = weakTypeOf[T]
+ val typeConstructor: Type = weakTypeOf[Tc].typeConstructor
+
+ val result = go(genericType, typeConstructor)
+
+ println(result)
+
+ result.getOrElse {
+ c.abort(c.enclosingPosition, "Could not infer extractor. Sorry.")
+ }
+ } catch {
+ case e: Exception =>
+ println("Macro failed!!! "+e)
+ //e.printStackTrace()
+ ???
+ }
+
+}
+