简体   繁体   中英

Passing class as a parameter to StaticAnnotation macro in scala

I would like to pass a class as a parameter to a StaticAnnotation macro in Scala, let's say I need a macro that checks on DateTime instance:

class CheckDate(date: DateTime) extends StaticAnnotation {
    def macroTransform(annottees: Any*): Any = macro CheckDate.impl
}

object CheckDate {
    def impl(c: Context)(annottees: c.Expr[Any]*): c.Expr[Any] = {
        import c.universe._

        ???
}

And implementation:

@CheckDate(new DateTime(...))
class Sth(...) {...}

How can I retrieve the DateTime instance from an AST?

After a bit of tweaking I finally found a way to retrieve more complex parameters for StaticAnnotation macro in scala.

First of all I extracted all parameters through pattern matching on c.prefix.tree , which actually refers to the annotation class, like so:

val params = c.prefix.tree match {
  case q"""new CheckDate(...$paramss)""" => paramss
  case _ => c.warning(NoPosition, "Oops, something went terribly wrong!")
}

I tried to inspect the tree for params by printing the tree with showRaw(params) , and there are two possible patterns (with or without parameter name):

List(List(AssignOrNamedArg(
Ident(TermName("date")),
Apply(Select(New(Ident(TypeName("DateTime"))), termNames.CONSTRUCTOR),
List(Literal(Constant(2016)), Literal(Constant(5)), Literal(Constant(10)))))))

or

List(List(Apply(
Select(New(Ident(TypeName("DateTime"))), termNames.CONSTRUCTOR), 
List(Literal(Constant(2016)), Literal(Constant(5)), Literal(Constant(11))))))

In order to use the DateTime object we need to extract the appropriate fields and create new DateTime instances:

val d: DateTime = params match {
   case List(List(AssignOrNamedArg(
   _, Apply(_, List(Literal(Constant(y)), Literal(Constant(m)), Literal(Constant(d))))))) =>
       new DateTime(y.asInstanceOf[Int], m.asInstanceOf[Int], d.asInstanceOf[Int], 0, 0)
   case List(List(Apply(_, List(Literal(Constant(y)),
   Literal(Constant(m)), Literal(Constant(d)))))) =>
       new DateTime(y.asInstanceOf[Int], m.asInstanceOf[Int], d.asInstanceOf[Int], 0, 0)

Lot's of boilerplate actually, but the way turns out quite efficient. The key is to inspect the AST of the structure to be parsed and create appropriate cases for it.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM