如何手动构造实例化 Class 的 Scala 运行时 AST?

How to manually construct an Scala runtime AST that instantiates a Class?

我在运行时构建正确的 Scala (2.13.3) 语法树时遇到问题。假设我们定义如下class.

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显然是无效的,因为它没有类型检查。如果我们检查打印输出,我们可以看到 IdentexpectedTreeactualTree 之间似乎有所不同。

从API看来,Ident似乎必须持有一个Name对象,但我不知道这里需要哪种名称。此外,expectedTree 的打印输出根本不表示 Ident 持有 Name。这是另一种 Ident 吗?手动构造 new Foo(1) 的 AST 的正确代码是什么?

编辑: 我被要求提供有关为什么 quasiquotes and/or reify 不适用于我的用例的信息。简短的回答是:这是自动编程的研究技术。

研究任务是仅使用测试套件合成一个正确的。我正在实现 the recently published method called "Code Building Genetic Programming".

的 Scala 版本

我理解在典型的反射用例中针对手动构造的警告,但我相信其他构造方法需要一些关于 program/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

Quasiquotes 和 reify/splicepreferable ways 构造树。因此,您应该提供更多详细信息,说明为什么 q"new my.package.Foo(1)"ru.reify { new Foo(1) }.tree 不足以满足您的用例。通常不需要构建相同的树,构建行为符合预期的树就足够了(但也有例外)。