使用Scala宏获取具体化函数的名称
我希望在遍历使用Scala宏获取具体化函数的名称,scala,macros,scala-macros,Scala,Macros,Scala Macros,我希望在遍历树的宏中对某些函数和其他变量引用进行模式匹配。目前,我正在根据符号进行匹配。全名如下: 树匹配{ 如果f.symbol!=null,则为案例“$f($x)” &&f.symbol.fullName==“\u root\uu.mypackage.MyClass.myfunc”=> ... 如果f.symbol!=null,则为案例“$f($x)” &&f.symbol.fullName=“\u root\uu.mypackage.MyClass2.myfunc2”=> ... 案例 }
树的宏中对某些函数和其他变量引用进行模式匹配。目前,我正在根据符号进行匹配。全名如下:
树匹配{
如果f.symbol!=null,则为案例“$f($x)”
&&f.symbol.fullName==“\u root\uu.mypackage.MyClass.myfunc”=>
...
如果f.symbol!=null,则为案例“$f($x)”
&&f.symbol.fullName=“\u root\uu.mypackage.MyClass2.myfunc2”=>
...
案例
}
但是,我想在编译这个宏的过程中另外检查这些函数是否确实存在,例如,我不想使用字符串,而是直接使用引用,这样IDE就可以告诉我“myfunc”名称是否有输入错误。我曾考虑使用具体化,但我不确定这是否有效
现在让我们假设我正在寻找println
,而不是MyClass.myfunc
。我遇到的第一个问题是,您不能直接reify(println)
或任何其他函数引用,因为Scala中没有函数引用,所以您必须编写reify(println)
,或者更具体地说,在本例中reify(println(:String))
来选择我们要调用的println函数。通过以下代码,我收集了表达式中的所有符号,但遗憾的是,只找到了Predef(来自Predef.println),而没有找到println本身:
println(具体化(println(:字符串)).tree
.collect{如果x.symbol!=null=>x.symbol.fullName},则为案例x)
//列表(、scala.Predef、、scala.Predef、、scala.Predef、、scala.Predef)
在scala(目前使用的是2.12)中获取某物名称有什么想法吗?reify(println)
编译
您如何想象函数的符号println(uux0:String)
该功能定义在哪里?在Predef
中定义了两种方法:
def println(): Unit
def println(x: Any): Unit
试一试
或
对于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(任何):单位
比如说
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{…
只生成列表(,,scala.Predef,,scala.Predef,,scala.Predef)
,因为treereify(println(:String)).tree
未进行类型检查(对于它生成的类型检查过的树List($anonfun,x$1,java.lang.String,scala.Predef.println,scala.Predef.println,scala.Predef,x$1,java.lang.String)
)
因此,另一种选择是执行c.类型检查
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"()"
}
具体化(println)
编译
你如何想象函数的符号println(u:String)
?在哪里定义了函数?在Predef
中定义了两种方法:
def println(): Unit
def println(x: Any): Unit
试一试
或
对于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(任何):单位
比如说
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{…
只生成列表(,,scala.Predef,,scala.Predef,,scala.Predef)
,因为treereify(println(:String)).tree
未进行类型检查(对于它生成的类型检查过的树List($anonfun,x$1,java.lang.String,scala.Predef.println,scala.Predef.println,scala.Predef,x$1,java.lang.String)
)
因此,另一种选择是执行c.类型检查
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"()"
}
要获取具体化表达式的符号,表达式树需要另一个显式通过类型检查,因此以下代码可以工作:
val mySym1=c.typecheck(具体化(mypackage.myfunction1)).tree)匹配{
案例q“{(..$\}=>f(..$\}”=>f.symbol
}
val mySym2=c.typecheck(具体化(mypackage.myfunction2_).tree)匹配{
案例q“{(..$\}=>f(..$\}”=>f.symbol
}
println(mySym.fullName)
//\u根目录\u.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 ...
}
要获取具体化表达式的符号,表达式树需要另一个显式通过类型检查,因此以下代码可以工作:
val mySym1=c.typecheck(具体化(mypackage.myfunction1)).tree)匹配{
案例q“{(..$\}=>f(..$\}”=>f.symbol
}
val mySym2=c.typecheck(具体化(mypackage.myfunction2_).tree)匹配{
案例q“{(..$\}=>f(..$\}”=>f.symbol
}
println(mySym.fullName)
//\u根目录\u.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 ...
}
实际上,有一个名为def symbolOf[X]:TypeSymbol
的函数,可用于从类型中获取TypeSymbol。但这只适用于TypeSymbol,而不适用于TermSymbol,因此我们无法对函数执行此操作
但是,如果这是我们自己的函数,我们可以将def myfunc():Int=…
中的定义替换为object myfunc{def apply():Int=…}
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 ...
}
实际上,有一个名为def symbolOf[X]:TypeSymbol
的函数,可用于从类型中获取TypeSymbol。但这只适用于TypeSymbol,而不适用于TermSymbol,因此我们无法对函数执行此操作
但是,如果这是我们自己的函数,我们可以将def myfunc():Int=…
中的定义替换为object myfunc{def apply():Int=…}
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 ...
}
Thx!顺便说一句,reify(prinln)
起作用,但只有bc它变成了reify(println())
,所以它是一个调用而不是引用。另外,这个问题的目的在于println不是一个字符串/准注释(没有用引号括起来),所以我不明白为什么你给出了很多例子,其中println用引号括起来……另一方面,你似乎是对的,对typecheck的调用丢失了。我仍然不明白为什么reify返回一个非类型树,因为它显然是这样的