I have a following code:
object Test extends App {
import Macros._
val f = 1
Macros.get(f)
}
import language.experimental.macros
import scala.reflect.macros.Context
object Macros {
def get(a: Int) = macro getImpl
def getImpl(c: Context)(a: c.Expr[Int]) = {
import c.universe._
println(showRaw(a))
}
}
It return:
Expr(Select(This(newTypeName("Test")), newTermName("f")))
How to extract from termName("f")
a 1
value ?
It's possible with macros?
In general - no. f
could be abstract or defined as external call or user input or random or one of many other cases.
But in some particular cases you could get it. You know almost all that compiler knows!
Take a look at c.enclosingClass
:
object Macros {
def get(a: Int) = macro getImpl
def getImpl(c: Context)(a: c.Expr[Int]) = {
import c.universe._
println(showRaw(c.enclosingClass))
c.Expr[Unit](Literal(Constant(())))
}
}
object Test { val f = 1; Macros.get(f) }
// ModuleDef(Modifiers(), $line55.$read.$iw.$iw.$iw.$iw.Test, Template(List(Select(Ident(scala), newTypeName("AnyRef"))), emptyValDef, List(DefDef(Modifiers(), nme.CONSTRUCTOR, List(), List(List()), TypeTree(), Block(List(Apply(Select(Super(This(newTypeName("Test")), tpnme.EMPTY), nme.CONSTRUCTOR), List())), Literal(Constant(())))), ValDef(Modifiers(), newTermName("f "), TypeTree(), Literal(Constant(1))), Apply(Select(Ident(newTermName("Macros")), newTermName("get")), List(Ident(newTermName("f")))))))
Interesting part here is ValDef(Modifiers(), newTermName("f "), TypeTree(), Literal(Constant(1)))
.
We have to extract it:
object Macros {
def get(a: Int) = macro getImpl
def getImpl(c: Context)(a: c.Expr[Int]) = {
import c.universe._
val (enclosing, name) = a.tree match {
case Select(This(enclosing), name) => enclosing -> name
case _ => c.abort(c.enclosingPosition, "Not a `this` memver")
}
val impl = c.enclosingClass match {
case impl: ImplDef if impl.name.toString == enclosing.toString => impl
case impl: ImplDef => c.abort(c.enclosingPosition, "Should search in another parent")
case _ => c.abort(c.enclosingPosition, "Not an `ImplDef`")
}
val body = impl.children.collect{
case Template(_, _, body) => body
} match {
case Seq(body) => body
case _ => c.abort(c.enclosingPosition, "Should be a single template.")
}
val rhss = body.collect{
case ValDef(_, valName, _, rhs) if valName.toString == name.toString => rhs
}
val rhs = rhss match {
case Seq(rhs) => rhs
case Seq() => c.abort(c.enclosingPosition, "Not found. Maybe it's a DefDef or somethong else")
case _ => c.abort(c.enclosingPosition, "Some other error.")
}
val res = rhs match {
case Literal(Constant(i: Int)) => i
case Literal(Constant(_)) => c.abort(c.enclosingPosition, "Literal, but not an Int.")
case _ => c.abort(c.enclosingPosition, "Implemented not as literal.")
}
println(s"Int value in this particular case: $res")
c.Expr[Any](Literal(Constant(res)))
}
}
Result:
object Test { val f = 1; Macros.get(f) }
// Int value in this particular case: 1
So we have a value of f
in compile time.
I'm pretty sure this is not what you expected.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With