【问题标题】:Scala Macros: Convert/parse a Tree to a NameScala 宏:将树转换/解析为名称
【发布时间】:2013-08-27 14:39:30
【问题描述】:

这是一个简化的示例,但问题仍然存在。

我想使用宏(基于 scala 的伪代码)来实现这一点:

(a: Int) => {
  val z = "toShort"
  a.z
}

如果我具体化它,我会得到类似的东西:

Function(
  List(
    ValDef(
      Modifiers(Flag.PARAM),
      newTermName("a"),
      Ident(scala.Int),
      EmptyTree
    )
  ),
  Block(
    List(
      ValDef(
        Modifiers(),
        newTermName("z"),
        TypeTree(),
        Literal(Constant("toShort"))
      )
    ),
    Apply(
      Select(
        Ident(newTermName("a")),
        newTermName("toShort")
      ),
      List()
    )
  )
)

我不知道如何访问一个值,然后将其用作 TermName。

我尝试用newTermName(c.Expr[String](Select(Ident(newTermName("z")))).splice) 替换newTermName("toShort"),但编译器似乎不喜欢:

宏扩展期间的异常: java.lang.UnsupportedOperationException: 你调用的函数没有被 > 编译器拼接。 这意味着涉及跨阶段评估,需要显式调用。 如果您确定这不是疏忽,请将 scala-compiler.jar 添加到类路径中, 导入scala.tools.reflect.Eval 并改为调用<your expr>.eval

我也按照编译器的建议尝试了“eval”:newTermName(c.eval(c.Expr[String](...)),但都没有成功。

我怎样才能将像Select(Ident(newTermName("z"))) 这样的树(这是对本地val 值的访问)转换为a Name 一个可以用作newTermName 参数的字符串?有可能吗?

更新:

这是gist给你带来的真正问题!

提前致谢,

【问题讨论】:

  • newTermName("z") 已经返回Name。我不明白那个有什么问题。
  • 您的意思是要从现有树中提取它吗?喜欢val Select(Ident(name)) = tree
  • 我想用val z 的值作为newTermName 方法的参数。 Select(Ident(newTermName("z"))) 是对值的访问,然后是:newTermName("<value of z>")。所以可以这样做:a.toShort.

标签: scala macros scala-macros


【解决方案1】:

我很难理解您想要实现的目标,以及为什么您在各处都使用 Trees。树是非常低级的,很难使用,很棘手,而且很难理解代码的作用。 Quasiquotes (http://docs.scala-lang.org/overviews/macros/quasiquotes.html) 确实是可行的方法,由于宏天堂插件 (http://docs.scala-lang.org/overviews/macros/paradise.html),您可以在 scala 2.10.x 生产版本中使用它们。可以直接写q"(a: Int) => {val z = "toShort"; a.z}",直接得到刚才输入的树表达式。

要回答您的问题,首先要记住宏是在编译时评估的。因此,它们无法生成依赖于运行时值的代码。这就是编译器抱怨您的splice 的原因。但是如果你传递一个可以在编译时计算的值,通常是一个文字,那么你可以使用 eval 在你的宏代码中获取它的值。正如 scaladoc 中所指出的那样,Eval 确实遇到了一个错误。它应该只在无类型树上调用。因此,在 s: c.Expr[String] 表达式上调用 eval 的方法是 val s2 = c.eval(c.Expr[String](c.resetAllAttrs(c.tree.duplicate))),它会为您提供 String,然后您可以在代码中正常使用,例如 q"(a: Int) => a.${newTermName(s2)}"

总而言之,假设您创建了一个宏,该宏将从一个对象及其String 字段之一输出字符串值。它会给出类似的东西

def attr[A](a: A, field: String): String = macro attrImpl[A]

def attrImpl[A: c.WeakTypeTag](c: Context)(a: c.Expr[A], field: c.Expr[String]) = {
  import c.universe._

  val s = c.eval(c.Expr[String](c.resetAllAttrs(field.tree.duplicate)))
  c.Expr[String](q"a.${newTermName(s)}")

}

REPL 会话测试:

scala> object a { val field1 = "field1"; val field2 = "field2" }
defined module a

scala> attr(a, "field1")
res0: String = field1

scala> attr(a, "field2")
res1: String = field2

要了解编译时和运行时之间的区别,您可以在 REPL 中思考以下结果 ;-)

scala> val s = "field1"; attr(a, s)
error: exception during macro expansion: 
scala.tools.reflect.ToolBoxError: reflective compilation has failed: 

$iw is not an enclosing class
    at scala.tools.reflect.ToolBoxFactory$ToolBoxImpl$ToolBoxGlobal.throwIfErrors(ToolBoxFactory.scala:311)
    at scala.tools.reflect.ToolBoxFactory$ToolBoxImpl$ToolBoxGlobal.compile(ToolBoxFactory.scala:244)
    at scala.tools.reflect.ToolBoxFactory$ToolBoxImpl.compile(ToolBoxFactory.scala:408)
    at scala.tools.reflect.ToolBoxFactory$ToolBoxImpl.eval(ToolBoxFactory.scala:411)
    at scala.reflect.macros.runtime.Evals$class.eval(Evals.scala:16)
    at scala.reflect.macros.runtime.Context.eval(Context.scala:6)
    at .attrImpl(<console>:14)


scala> val s = "field1"
s: String = field1

scala> attr(a, s)
res3: String = field1

希望对你有帮助;))

【讨论】:

  • 感谢您的回答!可能 quasiquotes 是要走的路,但它在尚未正式发布的 scala 2.11 上可用。我已经把 a.z 和 z 作为函数内部定义的值,以便在编译时可用,避免运行时值。你能用这个例子的可能解决方案来扩展你的答案吗? c.eval(c.Expr[String](Select(Ident(newTermName("z"))))) 不起作用:\。查看第 197 行的要点。
  • 您可以在docs.scala-lang.org/overviews/macros/paradise.html中描述的 2.10.x 生产版本中使用包含准引号的宏天堂插件
  • 再次感谢,我认为它们仅在 2.11 中可用...您知道为什么 c.Expr[Unit](q"""println("hello world!")""") 正常工作而 c.Expr[Int =&gt; Short](q"""(a: Int) =&gt; a.toShort""") 不工作吗?抛出此错误:\ (scala: Unable to read an event from: rO0ABXNyADlvcmcuamV0YnJhaW5zLmpwcy5pbmNyZW1lbnRhbC... Error: Unknown source file: embeddedFile--QuasiquoteCompat.scala@36a12aaa83f74d5b8ce323d542d159c0 java.lang.RuntimeException: Unknown source file: embeddedFile--QuasiquoteCompat.scala@36a12aaa83f74d5b8ce323d542d159c0
  • 我无法在 REPL 中重现该问题。也许你身边的这个 IDE 或项目配置。
  • 这很奇怪,我正在使用带有规范而不是 REPL 的 IntelliJ IDEA,但无论如何感谢您的回答,真的很有帮助!!! :)
猜你喜欢
  • 2016-09-20
  • 2017-07-13
  • 2018-03-20
  • 1970-01-01
  • 2013-02-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多