微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

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

我正在尝试编写一个 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 2.10中使用scala.reflect.macros.Context的方法.不是很漂亮,但解决了宇宙不匹配的问题. 作为一个底线,我目前的感觉是静态类型语言中的宏(特别是在面向对象语言中,因为OO为代码片段相互依赖带来了一堆惊人的方法)真的很棘手.尚未发现用于修改正在编译的程序中的任意片段的类型宏的鲁棒模型. 如果您希望我们可以通过电子邮件进行更详细的讨论.我们还可以合作,使适当的宏的想法成为现实.或者,如果您可以共享您的用例,我可以尝试帮助您找到适合您特定情况的解决方法.

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐