如何解决如何手动构造实例化类的Scala运行时AST?
我在运行时无法构建正确的Scala(2.13.3)语法树。假设我们定义以下类。
class Foo(x: Int)
我想为下面的代码行构建语法树。
new Foo(1)
作为参考,我们可以使用ru.reify
生成正确的语法树。我们还可以输入检查此树以确认其有效。
val expectedTree = ru.reify {
new Foo(1)
}
println(ru.showRaw(expectedTree))
// Apply(
// Select(
// New(Ident(my.package.Foo)),<-- What does Ident hold here?
// termNames.CONSTRUCTOR
// ),// List(
// Literal(Constant(1))
// )
// )
val toolBox = mirror.mkToolBox()
toolBox.typecheck(expectedTree).tpe
// my.package.Foo
但是,如果我不知道如何从头开始正确地编码相同的语法树。以下是我的初步尝试。我还使用TermName
而不是TypeName
尝试了同样的方法,并看到了相同的结果。
import ru._
val actualTree = Apply(
Select(
New(Ident(TypeName("my.package.Foo"))),termNames.CONSTRUCTOR
),List(
Literal(Constant(1))
)
)
println(ru.showRaw(actualTree))
// Apply(
// Select(
// New(Ident(TypeName("my.package.Foo"))),<-- Ident holds a TypeName
// termNames.CONSTRUCTOR
// ),// List(
// Literal(Constant(1))
// )
// )
val toolBox = mirror.mkToolBox()
toolBox.typecheck(actualTree).tpe
// ToolBoxError: reflective typecheck has Failed: not found: type my.package.Foo
actualTree
显然无效,因为它没有键入check。如果检查打印的输出,我们可以看到Ident
和expectedTree
之间的actualTree
似乎有所不同。
从API看来,Ident
必须包含一个Name
对象,但是在这里我无法确定需要哪种名称。此外,expectedTree
的打印输出并不表示Ident
完全持有Name
。这是另一种Ident
吗?手动构造new Foo(1)
的AST的正确代码是什么?
编辑: 我被要求提供有关为什么准引用和/或验证不起作用的信息。简短的答案是:这是自动编程的研究技术。
研究任务是仅使用测试套件来综合正确性。我正在实现the recently published method called "Code Building Genetic Programming"的Scala版本。
我理解在典型的反射用例中关于手动构建的警告,但我相信其他构建方法需要对程序/ AST在开发时应具有的概念有所了解。
解决方法
除了提取器的apply
方法接受Name
abstract class IdentExtractor {
def apply(name: Name): Ident
//...
}
https://github.com/scala/scala/blob/2.13.x/src/reflect/scala/reflect/api/Trees.scala#L1787
还有一种工厂方法接受Symbol
def Ident(sym: Symbol): Ident
https://github.com/scala/scala/blob/2.13.x/src/reflect/scala/reflect/api/Trees.scala#L2237
尝试
val actualTree = Apply(
Select(
New(Ident(typeOf[Foo].typeSymbol)),termNames.CONSTRUCTOR
),List(
Literal(Constant(1))
)
)
println(ru.showRaw(actualTree))
//Apply(Select(New(Ident(my.package.Foo)),termNames.CONSTRUCTOR),List(Literal(Constant(1))))
println(toolbox.typecheck(actualTree).tpe) // my.package.Foo
准引号和reify
/ splice
是preferable ways来构造树。因此,您应该提供更多详细信息,为什么q"new my.package.Foo(1)"
和ru.reify { new Foo(1) }.tree
不足以满足您的用例。通常,没有必要构建相同的树,而足以构建表现理想的树(但有例外)。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。