scala – 使用类上的宏注释创建或扩展伴随对象
发布时间:2020-12-16 09:28:58 所属栏目:安全 来源:网络整理
导读:使用Scala 2.10 / 2.11宏天堂注释宏,如何添加或扩展带注释类的伴随对象?骨架: import scala.annotation.StaticAnnotationimport scala.reflect.macros._import language.experimental.macrosclass foo extends StaticAnnotation { def macroTransform(anno
使用Scala 2.10 / 2.11宏天堂注释宏,如何添加或扩展带注释类的伴随对象?骨架:
import scala.annotation.StaticAnnotation import scala.reflect.macros._ import language.experimental.macros class foo extends StaticAnnotation { def macroTransform(annottees: Any*) = macro fooMacro.impl } object fooMacro { def impl(c: Context)(annottees: c.Expr[Any]*): c.Expr[Any] = ??? } 这样,给定 trait Foo[A] 以下输入 @foo class Bar object Baz { def baz = 33 } @foo class Baz 将扩展为: object Bar { implicit def hasFoo: Foo[Bar] = ??? } class Bar object Baz { def baz = 33 implicit def hasFoo: Foo[Baz] = ??? } class Baz 这是第一次天真的尝试,现在只需添加def hasFoo = 33: 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) // or should we have parents = List(AnyRef) and body = List(DefDef(???)) val mTemp = Template(parents = Nil,self = noSelfType,body = Nil) val mName = TermName(cName.decoded) // or encoded? ModuleDef(mMod,mTemp) case _ => c.abort(c.enclosingPosition,"Expected a companion object") } val Template(mTempParents,mTempSelf,mTempBody0) = mod0.impl val fooDef = DefDef(NoMods,TermName("hasFoo"),Nil,TypeTree(typeOf[Int]),Literal(Constant(33))) val mTempBody1 = fooDef :: mTempBody0 val mTemp1 = Template(mTempParents,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(())))) } 当伴侣对象已存在时,此方法有效: object Foo @mkCompanion class Foo assert(Foo.hasFoo == 33) 但不是在创建时: @mkCompanion class Foo [error] no constructor in template: impl = Object { [error] def hasFoo(): Int = 33 [error] } 所以我仍然需要弄清楚如何提供模块构造函数…… 解决方法
这是我目前的解决方案:
import scala.annotation.StaticAnnotation import scala.reflect.macros._ import language.experimental.macros trait Foo[A] class mkCompanion extends StaticAnnotation { def macroTransform(annottees: Any*) = macro mkCompanionMacro.impl } object mkCompanionMacro { 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(_,_)) :: tail => val mod0: ModuleDef = tail match { case (md @ ModuleDef(_,mTemp)) :: 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,Nil) // XXX TODO: isn't there a shortcut for creating the constructor? val mkSuperSelect = Select(Super(This(tpnme.EMPTY),tpnme.EMPTY),nme.CONSTRUCTOR) val superCall = Apply(mkSuperSelect,Nil) val constr = DefDef(NoMods,nme.CONSTRUCTOR,List(Nil),TypeTree(),Block(List(superCall),Literal(Constant()))) val mTemp = Template(parents = List(TypeTree(typeOf[AnyRef])),body = constr :: Nil) val mName = TermName(cName.decoded) // or encoded? ModuleDef(mMod,mTemp) case _ => c.abort(c.enclosingPosition,"Expected a companion object") } val Template(mTempParents,mTempBody0) = mod0.impl // cf. http://stackoverflow.com/questions/21044957/type-of-a-macro-annottee val cTpe = Ident(TypeName(cd.name.decoded)) val fooName = TermName("hasFoo") val fooDef = q"implicit def $fooName: Foo[$cTpe] = ???" val mTempBody1 = fooDef :: mTempBody0 val mTemp1 = Template(mTempParents,mTempBody1) val mod1 = ModuleDef(mod0.mods,mTemp1) cd :: mod1 :: Nil case _ => c.abort(c.enclosingPosition,"Must annotate a class or trait") } c.Expr[Any](Block(outputs,Literal(Constant(())))) } } 测试: object Bar @mkCompanion class Bar @mkCompanion class Baz implicitly[Foo[Bar]] implicitly[Foo[Baz]] (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |