Scala 宏获取术语名称的值 [英] Scala Macro get value for term name

查看:32
本文介绍了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 值?可以用宏吗?

How to extract from termName("f") a 1 value ? It's possible with macros?

推荐答案

一般来说 - 没有.f 可以是抽象的或定义为外部调用或用户输入或随机或许多其他情况之一.

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!

看看c.enclosureClass:

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 的值.

So we have a value of f in compile time.

我很确定这不是您所期望的.

I'm pretty sure this is not what you expected.

这篇关于Scala 宏获取术语名称的值的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆