Scala Macro получает значение для имени термина

У меня есть следующий код:

использование .scala

object Test extends App {
   import Macros._

   val f = 1
   Macros.get(f) 
}

макросы.scala

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))
  }
}    

Он возвращает:

Expr(Select(This(newTypeName("Test")), newTermName("f")))

Как извлечь из termName("f") значение 1? Можно с макросами?


person mike    schedule 27.12.2013    source источник


Ответы (1)


В целом - нет. f может быть абстрактным или определяться как внешний вызов, пользовательский ввод, случайный или один из многих других случаев.

Но в некоторых частных случаях вы могли бы получить его. Вы знаете почти все, что знает компилятор!

Взгляните на 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")))))))

Интересная часть здесь ValDef(Modifiers(), newTermName("f "), TypeTree(), Literal(Constant(1))).

Мы должны извлечь его:

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)))
  }
}   

Результат:

object Test { val f = 1; Macros.get(f) }
// Int value in this particular case: 1

Итак, у нас есть значение f во время компиляции.

Я почти уверен, что это не то, что вы ожидали.

person senia    schedule 27.12.2013
comment
Например, если у вас было val f = 1 + 0 или val g = 1; val f = g, вы уже вышли из игры. Ваша эвристика взорвется, если вы захотите зафиксировать всевозможные другие случаи. - person 0__; 28.12.2013
comment
@0__: Я думаю, OP хочет получить от API компилятора что-то, что можно было бы назвать predictor of constants. - person senia; 28.12.2013
comment
Что если enclosingClass является анонимной функцией? - person jdprc06; 16.05.2016