1

我想在遍历 a 的宏中对某些函数和其他变量引用进行模式匹配Tree。目前,我是这样匹配的symbol.fullname

tree match {
  case "$f($x)" if f.symbol != null
  && f.symbol.fullName == "_root_.mypackage.MyClass.myfunc" =>
    ...
  case "$f($x)" if f.symbol != null
  && f.symbol.fullName == "_root_.mypackage.MyClass2.myfunc2" =>
    ...

  case ...
}

但是,我想在编译这个宏的过程中另外检查这些函数是否确实存在,例如,我想直接使用引用而不是字符串,这样 IDE 可以告诉我是否在“myfunc”中有错字姓名。我正在考虑使用 using reify,但我不确定这是否有效。

现在让我们假设我正在寻找println而不是MyClass.myfunc. 我遇到的第一个问题是你不能reify(println)直接或任何其他函数引用,因为在 Scala 中没有函数引用,所以你必须编写reify(println _)或更具体地在这种情况下reify(println (_: String))选择我们要调用的 println 函数。使用以下代码,我收集了表达式中的所有符号,但遗憾的是只找到了 Predef(来自 Predef.println),但没有找到 println 本身:

println(reify( println (_:String) ).tree
       .collect { case x if x.symbol != null => x.symbol.fullName } )
// List(<none>, <none>, <none>, scala.Predef, <none>, <none>, scala.Predef, <none>, <none>, scala.Predef)

在 scala 中获取某物名称的任何想法(目前使用 2.12)?

4

3 回答 3

0

实际上有一个被调用的函数def symbolOf[X]: TypeSymbol可以用来从一个 Type 中获取一个 TypeSymbol。但这仅适用于 TypeSymbols,而不适用于 TermSymbols,因此我们不能对函数执行此操作。

但是,如果这是我们自己的函数,我们可以用 替换def myfunc(): Int = ...定义object myfunc { def apply(): Int = ... }。由于每个对象都有自己的类型(称为myfunc.type),我们现在可以执行以下操作

package main
object myfunc  { def apply(): Int = 1 }
object myfunc2 { def apply(): Int = 1 }

...

tree match {
  case "$f.apply($x)" if f.symbol == symbolOf[myfunc.type]  => ...
  case "$f.apply($x)" if f.symbol == symbolOf[myfunc2.type] => ...
  case ...
}
于 2020-01-16T16:47:20.093 回答
0

reify(println)编译。

你如何想象功能的象征println (_:String)?这个函数是在哪里定义的?在两种方法中定义:Predef

def println(): Unit
def println(x: Any): Unit

尝试

val mirror = scala.reflect.runtime.universe.runtimeMirror(this.getClass.getClassLoader) // at runtime
// val mirror = c.mirror // at compile time

mirror.staticClass("mypackage.MyClass").typeSignature.decl(TermName("myfunc"))
// method myfunc

或者

typeOf[MyClass].decl(TermName("myfunc"))
// method myfunc

为了MyClass#myfunc

definitions.PredefModule.typeSignature.decl(TermName("println")).alternatives
// List(method println, method println)

对彼此而言println

definitions.PredefModule.typeSignature.decl(TermName("println")).alternatives
  .filter(_.asMethod.paramLists.map(_.map(_.typeSignature)) == List(List(definitions.AnyTpe)))
// List(method println)

println(Any):Unit.

例如

def foo(x: Any): Unit = macro impl

def impl(c: blackbox.Context)(x: c.Tree): c.Tree = {
  import c.universe._

  val printlnSymb = definitions.PredefModule.typeSignature.decl(TermName("println")).alternatives
    .filter(_.asMethod.paramLists.map(_.map(_.typeSignature)) == List(List(definitions.AnyTpe)))
    .head

  x match {
    case q"$f($x)" if f.symbol == printlnSymb =>
      println("test")
  }

  q"()"
}

foo(println(1)) //Warning:scalac: test

reify( println (_:String) ).tree.collect { ...List(<none>, <none>, <none>, scala.Predef, <none>, <none>, scala.Predef, <none>, <none>, scala.Predef)因为树reify( println (_:String) ).tree未经过类型检查而产生(对于经过类型检查的树,它会产生List($anonfun, x$1, java.lang.String, scala.Predef.println, scala.Predef.println, scala.Predef, x$1, java.lang.String))。

所以另一个选择是做c.typecheck

def impl(c: blackbox.Context)(x: c.Tree): c.Tree = {
  import c.universe._

  val printlnSymb = (c.typecheck(q"println(_:Any)", silent = false) match {
    case q"($_) => $p($_)" => p
  }).symbol

//val printlnSymb = (c.typecheck(reify { println(_:Any) }.tree, silent = false) match {
//  case q"($_) => $p($_)" => p
//}).symbol

  x match {
    case q"$f($x)" if f.symbol == printlnSymb =>
      println("test")
  }

  q"()"
}
于 2019-11-28T21:42:22.530 回答
0

要获得具体表达式的符号,表达式树需要另一个显式通过类型检查,因此以下代码有效:

val mySym1 = c.typecheck(reify(mypackage.myfunction1 _).tree) match {
  case q"{(..$_) => $f(..$_)}" => f.symbol
}
val mySym2 = c.typecheck(reify(mypackage.myfunction2 _).tree) match {
  case q"{(..$_) => $f(..$_)}" => f.symbol
}

println(mySym.fullName)
// _root_.mypackage.myfunction1

然后您可以根据符号进行匹配:

tree match {
  case "$f($x)" if f.symbol != null => f.symbol match {
    case x if x == mySym1 => ...
    case x if x == mySym2 => ...
    case ...
  }
  case ...
}
于 2019-11-29T12:54:30.060 回答