如何在 Scala 宏中将 context.universe.Annotation 转换为 MyAnnotation
How to convert context.universe.Annotation to MyAnnotation in a Scala Macro
我使用的是 Scala 2.11.1,我有一个注解
case class MyAnnotation(id: String, message: String) extends StaticAnnotation
我想创建一个宏 MessageFromMyAnnotation
来转换以下代码
class Foo {
@MyAnnotation("001", "James Bond 001")
def foox = {
... // my messy code
val x = MessageFromMyAnnotation("001")
... // my messy code
}
}
至
class Foo {
@MyAnnotation("001", "James Bond 001")
def foox = {
... // my messy code
val x = "Hello world, James Bond 001"
... // my messy code
}
}
简而言之,宏在其封闭元素上找到 message
of @MyAnnotation
其 id = "001"
和 return "Hello world, " + message
这是宏
object MessageFromMyAnnotation {
def apply(id: String) = macro impl
def impl(c: Context)(id: c.Expr[String]): c.Expr[String] = {
c.internal.enclosingOwner.annotations.filter( anno =>
anno.tpe =:= c.universe.typeOf[MyAnnotation] &&
anno.asInstanceOf[MyAnnotation].id == id.value //this does not work
) match {
case anno :: Nil => c.universe.reify("Hello world, " + ...)
case x => c.abort(c.enclosingPosition, c.universe.showRaw(x))
}
}
}
我想将 cuniverse.Annotation
类型的 anno
转换为 MyAnnotation
并将其 id
与 c.Expr[String]
类型的参数 id
进行比较,但是 anno.asInstanceOf[MyAnnotation]
产生 ClassCastException
并且 id.value
给我一条错误消息
cannot use value except for signatures of macro implementations
所以,请帮我解决两个问题:
- 如何将
cuniverse.Annotation
类型的 anno
转换为 MyAnnotation
- 如何将其
id
与类型 c.Expr[String]
的参数 id
进行比较
感谢@Imm的建议,我已经成功了:
You don't have an instance of MyAnnotation - this is compile time, you only
have an AST that represents the call. You can get the Expr that's the
parameter given to the cuniverse.Annotation, and either splice it, or pattern
match it as a String literal and then take the value out of that.
这是代码
object MessageFromMyAnnotation {
def apply(id: String) = macro impl
def impl(c: Context)(id: c.Expr[String]): c.Expr[String] = {
import c.universe._
id match { case Expr(Literal(Constant(idVal: String))) =>
(for { Annotation(tpe,
Literal(Constant(annoIdVal: String)) ::
Literal(Constant(annoMessageVal: String)) ::
Nil, _) <- c.internal.enclosingOwner.annotations
if tpe =:= typeOf[MyAnnotation] && idVal == annoIdVal
} yield (annoIdVal, annoMessageVal)
) match {
case (annoIdVal, annoMessageVal) :: Nil =>
reify(c.literal("Hello world, " + annoMessageVal).splice)
case matchedMore :: thanOne :: Nil => c.abort(c.enclosingPosition, "Found more than one @MyAnnotation with the same id")
case x => c.abort(c.enclosingPosition, "Not Found @MyAnnotation with the specified id")
}
}
}
}
我使用的是 Scala 2.11.1,我有一个注解
case class MyAnnotation(id: String, message: String) extends StaticAnnotation
我想创建一个宏 MessageFromMyAnnotation
来转换以下代码
class Foo {
@MyAnnotation("001", "James Bond 001")
def foox = {
... // my messy code
val x = MessageFromMyAnnotation("001")
... // my messy code
}
}
至
class Foo {
@MyAnnotation("001", "James Bond 001")
def foox = {
... // my messy code
val x = "Hello world, James Bond 001"
... // my messy code
}
}
简而言之,宏在其封闭元素上找到 message
of @MyAnnotation
其 id = "001"
和 return "Hello world, " + message
这是宏
object MessageFromMyAnnotation {
def apply(id: String) = macro impl
def impl(c: Context)(id: c.Expr[String]): c.Expr[String] = {
c.internal.enclosingOwner.annotations.filter( anno =>
anno.tpe =:= c.universe.typeOf[MyAnnotation] &&
anno.asInstanceOf[MyAnnotation].id == id.value //this does not work
) match {
case anno :: Nil => c.universe.reify("Hello world, " + ...)
case x => c.abort(c.enclosingPosition, c.universe.showRaw(x))
}
}
}
我想将 cuniverse.Annotation
类型的 anno
转换为 MyAnnotation
并将其 id
与 c.Expr[String]
类型的参数 id
进行比较,但是 anno.asInstanceOf[MyAnnotation]
产生 ClassCastException
并且 id.value
给我一条错误消息
cannot use value except for signatures of macro implementations
所以,请帮我解决两个问题:
- 如何将
cuniverse.Annotation
类型的anno
转换为MyAnnotation
- 如何将其
id
与类型c.Expr[String]
的参数
id
进行比较
感谢@Imm的建议,我已经成功了:
You don't have an instance of MyAnnotation - this is compile time, you only
have an AST that represents the call. You can get the Expr that's the
parameter given to the cuniverse.Annotation, and either splice it, or pattern
match it as a String literal and then take the value out of that.
这是代码
object MessageFromMyAnnotation {
def apply(id: String) = macro impl
def impl(c: Context)(id: c.Expr[String]): c.Expr[String] = {
import c.universe._
id match { case Expr(Literal(Constant(idVal: String))) =>
(for { Annotation(tpe,
Literal(Constant(annoIdVal: String)) ::
Literal(Constant(annoMessageVal: String)) ::
Nil, _) <- c.internal.enclosingOwner.annotations
if tpe =:= typeOf[MyAnnotation] && idVal == annoIdVal
} yield (annoIdVal, annoMessageVal)
) match {
case (annoIdVal, annoMessageVal) :: Nil =>
reify(c.literal("Hello world, " + annoMessageVal).splice)
case matchedMore :: thanOne :: Nil => c.abort(c.enclosingPosition, "Found more than one @MyAnnotation with the same id")
case x => c.abort(c.enclosingPosition, "Not Found @MyAnnotation with the specified id")
}
}
}
}