gpt4 book ai didi

scala - 使用类上的宏注释创建或扩展伴随对象

转载 作者:行者123 更新时间:2023-12-04 12:32:16 25 4
gpt4 key购买 nike

使用 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, mName, mTemp)
case _ => c.abort(c.enclosingPosition, "Expected a companion object")
}
val Template(mTempParents, mTempSelf, mTempBody0) = mod0.impl
val fooDef = DefDef(NoMods, TermName("hasFoo"), Nil, Nil,
TypeTree(typeOf[Int]), Literal(Constant(33)))
val mTempBody1 = fooDef :: mTempBody0
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(()))))
}

这在伴随对象已经存在时有效:
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(_, cName, _, _)) :: tail =>
val mod0: ModuleDef = tail match {
case (md @ ModuleDef(_, mName, 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, cMod.privateWithin, 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, 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, "Expected a companion object")
}

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("hasFoo")
val fooDef = q"implicit def $fooName: Foo[$cTpe] = ???"
val mTempBody1 = fooDef :: mTempBody0
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(()))))
}
}

测试:
object Bar
@mkCompanion class Bar

@mkCompanion class Baz

implicitly[Foo[Bar]]
implicitly[Foo[Baz]]

关于scala - 使用类上的宏注释创建或扩展伴随对象,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21032869/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com