Created
April 1, 2014 21:29
-
-
Save Sciss/9923585 to your computer and use it in GitHub Desktop.
Wiping old computer. Make a backup of this one.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package de.sciss.lucre.macros | |
import scala.annotation.StaticAnnotation | |
import scala.reflect.macros.{BlackboxContext => Context} | |
import language.experimental.macros | |
trait Foo[A] | |
class mkCompanion extends StaticAnnotation { | |
def macroTransform(annottees: Any*) = macro mkCompanionMacro.impl | |
} | |
object mkCompanionMacro { | |
def impl1(c: Context)(annottees: c.Expr[Any]*): c.Expr[Any] = { | |
import c.universe._ | |
val inputs = annottees.map(_.tree).toList | |
println(s"---- RAW ----\n${showRaw(inputs.head)}") | |
val (validAnnottees, _) = inputs partition { | |
case x: ClassDef => true | |
case _ => false | |
} | |
for (v <- validAnnottees ) { | |
val tpe = v.tpe // <- tpe is null as the annotated type is not yet type checked! | |
val tpe2 = if (tpe == null) { | |
println(s"---- Input ----\n${showRaw(v)}") | |
val blk = c.typeCheck(Block(v)) | |
val Block((cd1 @ ClassDef(_, _, _, _)) :: Nil, _) = blk | |
println(s"---- Output ----\n${showRaw(blk)}") | |
cd1.tpe // <- fails with a compiler error (assertion failure) | |
} | |
else | |
tpe | |
println(s"type = '$tpe2'") | |
} | |
c.Expr[Any](Literal(Constant())) | |
} | |
def impl(c: Context)(annottees: c.Expr[Any]*): c.Expr[Any] = { | |
import c.universe._ | |
val inputs : List[Tree] = annottees.map(_.tree)(collection.breakOut) | |
val outputs: List[Tree] = inputs match { | |
case (cd @ ClassDef(_, cName, _, _)) :: tail => | |
val mod0: ModuleDef = tail match { | |
case (md @ ModuleDef(_, mName, _)) :: Nil if cName.decoded == mName.decoded => md | |
case Nil => | |
val cMod = cd.mods | |
var mModF = NoFlags | |
if (cMod hasFlag Flag.PRIVATE ) mModF |= Flag.PRIVATE | |
if (cMod hasFlag Flag.PROTECTED) mModF |= Flag.PROTECTED | |
if (cMod hasFlag Flag.LOCAL ) mModF |= Flag.LOCAL | |
val mMod = Modifiers(mModF, cMod.privateWithin, Nil) | |
// XXX TODO: isn't there a shortcut for creating the constructor? ast.Trees apparently has stuff for it | |
val mkSuperSelect = Select(Super(This(tpnme.EMPTY), tpnme.EMPTY), nme.CONSTRUCTOR) | |
val superCall = Apply(mkSuperSelect, Nil) | |
val constr = DefDef(NoMods, nme.CONSTRUCTOR, Nil, List(Nil), TypeTree(), | |
Block(List(superCall), Literal(Constant()))) | |
val mTemp = Template(parents = List(TypeTree(typeOf[AnyRef])), self = noSelfType, body = constr :: Nil) | |
val mName = TermName(cName.decoded) // or encoded? | |
ModuleDef(mMod, mName, mTemp) | |
case _ => c.abort(c.enclosingPosition, s"Expected a companion object for '$cName'") | |
} | |
val Template(mTempParents, mTempSelf, mTempBody0) = mod0.impl | |
// cf. http://stackoverflow.com/questions/21044957/type-of-a-macro-annottee | |
val cTpe = Ident(TypeName(cd.name.decoded)) | |
val fooName = TermName("serializer") | |
val fooDef = q"implicit def $fooName[S <: de.sciss.lucre.stm.Sys[S]]: Foo[$cTpe] = ???" | |
val mTempBody1 = mTempBody0 :+ fooDef | |
val mTemp1 = Template(mTempParents, mTempSelf, mTempBody1) | |
val mod1 = ModuleDef(mod0.mods, mod0.name, mTemp1) | |
cd :: mod1 :: Nil | |
case _ => c.abort(c.enclosingPosition, "Must annotate a class or trait") | |
} | |
c.Expr[Any](Block(outputs, Literal(Constant(())))) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment