gpt4 book ai didi

scala - 将闭包传递给 Scala 编译器插件

转载 作者:行者123 更新时间:2023-12-04 14:14:56 26 4
gpt4 key购买 nike

我正在尝试编写一个 Scala 编译器插件,它将允许非常通用的代码生成:类似于 C 预处理器的通用性,但类型更安全(我不确定这是否是一个糟糕的主意,但这是一个有趣的练习)。我的理想用例如下所示:

// User code. This represents some function that might take some args
// and outputs an abstract syntax tree.
def createFooTree(...): scala.reflect.runtime.universe.Tree = ...

// Later user code (maybe separate compilation?). Here the user generates
// code programmatically using the function call to |createFooTree| and inserts
// the code using insertTree.
insertTree(createFooTree(...))

重要的插件代码可能如下所示(基于 this):

class InsertTreeComponent(val global: Global)
extends PluginComponent
with TypingTransformers {
import global._
import definitions._

override val phaseName = "insertTree"

override val runsRightAfter = Some("parser")
override val runsAfter = runsRightAfter.toList
override val runsBefore = List[String]("typer")

def newPhase(prev: Phase): StdPhase = new StdPhase(prev) {
def apply(unit: CompilationUnit) {
val onTransformer = new TypingTransformer(unit) {
override def transform(tree: Tree): Tree = tree match {
case orig @ Apply(
function,
// |treeClosure| is the closure we passed, which should
// evaluate to a Tree (albeit a runtime Tree).
// The function.toString bit matches anything that looks like a
// function call with a function called |insertTree|.
treeClosure) if (function.toString == "insertTree") => {
// This function evaluates and returns the Tree, inserting it
// into the call site as automatically-generated code.
// Unfortunately, the following line isn't valid.
eval(treeClosure): Tree
}
...

知道如何做到这一点吗?请不要说“只使用宏”;至少在 2.10 中,它们还不够通用。

顺便说一句,我看到我概述的方法有两个问题:1) 编译器插件采用 AST,而不是闭包。它需要某种方式来创建闭包,可能会添加对用户代码的构建依赖。 2) 用户无权访问 scala.reflect.internal.Trees.Tree,只能访问 scala.reflect.runtime.universe.Tree,因此插件需要在两者之间进行转换。

最佳答案

您面临的实现困难部分是 2.10 中的宏不够通用的原因。它们看起来非常具有挑战性,甚至是根本性的,但我对它们最终会被击败感到乐观。以下是一些棘手的设计问题:

1) 你怎么知道你调用的函数是正确的insertTree ?如果用户编写了自己的名为 insertTree 的函数怎么办? - 那么如何区分对特殊函数的魔法调用和对用户定义函数的正常调用?为确保您需要对该函数的引用进行类型检查。但这并不容易(见下文)。

2) 您如何准确评估 createFooTree(...)称呼?和以前一样,您需要对 createFooTree 进行类型检查。部分找出它代表什么,这并不容易。

3)然后还有一个问题。如果createFooTree怎么办是在您当前正在编译的文件之一中定义的吗?然后您需要以某种方式将它及其依赖项与程序的其余部分分开,将其放入不同的编译运行中,编译它然后调用它。然后,如果函数或这些依赖项之一的编译导致宏扩展,这应该会改变编译器的某些全局状态,该怎么办?我们如何将它传播到程序的其余部分?

4)我一直在谈论类型检查。那是问题吗?显然,是的。如果你的宏可以在任何地方扩展成任何东西,那么类型检查就变得非常棘手。例如,你如何打字检查:

class C {
insertTree(createFoo(bar)) // creates `def foo = 2`, requires `bar` to be defined to operate
insertTree(createBar(foo)) // creates `def bar = 4`, requires `foo` to be defined to operate
}

5) 不过,好消息是您不必使用 scala.reflect.runtime.universe.Tree .你可以有 createFooTree依赖类型: def createFooTree[U <: scala.reflect.api.Universe with Singleton](u: Universe): u.Tree .这,或 scala.reflect.macros.Context 的方法我们在 Scala 2.10 中使用。不是很漂亮,但解决了宇宙不匹配的问题。

总而言之,我目前的感觉是静态类型语言中的宏(尤其是面向对象的语言,因为 OO 为代码段带来了大量相互依赖的方式)真的很棘手。用于修改正在编译的程序中的任意片段的类型宏的健壮模型还有待发现。

如果您希望我们可以通过电子邮件进行更详细的讨论。我们还可以合作将适当的宏的想法付诸实践。或者,如果您可以分享您的用例,我可以尝试帮助您找到适合您特定情况的解决方法。

关于scala - 将闭包传递给 Scala 编译器插件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15401982/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com