如何手动构造实例化 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
显然是无效的,因为它没有类型检查。如果我们检查打印输出,我们可以看到 Ident
在 expectedTree
和 actualTree
之间似乎有所不同。
从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
/splice
是 preferable ways 构造树。因此,您应该提供更多详细信息,说明为什么 q"new my.package.Foo(1)"
和 ru.reify { new Foo(1) }.tree
不足以满足您的用例。通常不需要构建相同的树,构建行为符合预期的树就足够了(但也有例外)。
我在运行时构建正确的 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
显然是无效的,因为它没有类型检查。如果我们检查打印输出,我们可以看到 Ident
在 expectedTree
和 actualTree
之间似乎有所不同。
从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
/splice
是 preferable ways 构造树。因此,您应该提供更多详细信息,说明为什么 q"new my.package.Foo(1)"
和 ru.reify { new Foo(1) }.tree
不足以满足您的用例。通常不需要构建相同的树,构建行为符合预期的树就足够了(但也有例外)。