Compiler construction 使用ASTTransformation来“转换”;删除“;def更改Groovy脚本中的作用域

Compiler construction 使用ASTTransformation来“转换”;删除“;def更改Groovy脚本中的作用域,compiler-construction,groovy,transformation,abstract-syntax-tree,Compiler Construction,Groovy,Transformation,Abstract Syntax Tree,不管听起来多么奇怪,我需要通过修改变量的创建方式来修改一些Groovy代码 以前,当我需要通过修改代码来调用我的自定义特殊方法来“模拟”使用/类别功能时,我已经解决了ASTTransformation的一些其他相关问题 我现在需要做的是: 我需要以下类型的代码: def a = "X"; Integer b = 1; 要按照此代码操作,请执行以下操作: a = "X"; b = 1; 区别在于,第二个示例“存储”在绑定范围中 一些背景: 我正在编写一个groovy模板引擎,其中有一些代码片段

不管听起来多么奇怪,我需要通过修改变量的创建方式来修改一些Groovy代码

以前,当我需要通过修改代码来调用我的自定义特殊方法来“模拟”使用/类别功能时,我已经解决了ASTTransformation的一些其他相关问题

我现在需要做的是:

我需要以下类型的代码:

def a = "X";
Integer b = 1;
要按照此代码操作,请执行以下操作:

a = "X";
b = 1;
区别在于,第二个示例“存储”在绑定范围中

一些背景:

我正在编写一个groovy模板引擎,其中有一些代码片段(由用户提供),它们被放入groovy类/文件中的一个方法中,然后进行编译。当我运行代码片段/方法时,我需要通过绑定机制访问它创建的变量

我希望有人能在正确的方向上帮助我

提前谢谢

更新:

(谢谢你的回答)

我还有一个问题:

我认为当我修改所有声明时,它可以工作,但是如果我只修改最外层块中的声明会更好。例如:

Object aMethod() {
  def a = 1; //should be modified
  def b = 2; //should be modified
  if (expression) {
    def c = 2; //does not need to be modified
  }
}
您知道如何检测声明是否位于方法体中最外面的块(主块)中吗


-Morten

您必须将AST中的DeclarationExpressions替换为BinaryExpression。您会注意到DeclarationExpression是一个BinaryExpression,因此您只需复制所有内容。另外,不要忘记使用旧节点在新节点上调用setSourcePosition,以确保行号正确。表达式存储在ExpressionStatements中,因此替换它们并不太困难

至于在哪个阶段操作。。。我强烈建议在设置变量作用域之前执行此操作,这样您的工作量就会减少,并且以后不必删除AST中对变量的大量引用。例如,您可以在转换中执行此操作

还有一个词是关于你用这个词引入的语义变化:

(1) 字符串a=1;a=2;在这个代码示例中,您将得到两倍于存储在中的字符串。这主要是因为对于每个赋值,Groovy都将执行Groovy样式转换,其中包括对字符串的转换。这是因为变量是类型化的。如果它不是类型化的,这种转换就不会发生。“def”就是这种情况,但绑定中的变量也是如此。这意味着您的绑定将存储整数1和2,而不是字符串

(2) 另一点是范围。如果我没弄错的话,那么你想让所有变量都是全局变量。在变量只有块范围的上下文中编写的程序可能会表现出奇怪的行为。如果Groovy闭包不是同步执行的,但例如程序中更晚的分离点,则Groovy闭包中的变量可能会发生这种情况

(3) 最后一点是改变拖把的用法。获取局部变量的值时,我们不使用MOP,而是直接使用它。如果将局部变量移动到全局范围,则必须通过完整的MOP来获取值。如果在某个时刻,您有一个程序元素自己捕获调用并重复,那么您将得到一个不同的程序


也许这对你来说并不重要。。。我只是想让您知道这些事情。

您最新的问题是:您知道如何检测声明是否位于方法体中最外面的块(主块)中吗

好吧,我可能会做以下最简单的方法。从AST获取MethodNode,并使用getCode()获取包含该节点的代码。如果它是一个ExpressionStatement,那么如果它包含一个DeclarationExpression,您可以对其进行更改。如果它是一个BlockStatement,那么您将遍历其中的所有语句,并根据需要对它们进行更改,就像对单个语句进行更改一样

此解决方案很简单,但不捕获while循环中的声明

如果您可以使用更安全但也更复杂的解决方案,那么我建议使用Groovy提供的访问者之一。您可以使用int来计算嵌套级别,通过覆盖visitblockstation,您可以将计数增加1。然后覆盖VisitePressionStatement。如果嵌套级别为0且语句包含DeclarationExpression,则进行转换,否则不。。。在这种情况下,你也不需要在树上走得更深。现在对于输入方法,通常是visitMethod。如果方法代码是BlockStatement,则从级别-1开始,然后继续使用super,否则从级别0开始


至于来访者。。。通用的一个是ClassCodeVisitor,正如我在上面所描述的,只有当您想要像这样从多个方法转换代码时,它才有意义。或者您使用CodeVisistorSupport,但是您必须为额外级别使用该方法init逻辑

非常感谢你的回答!我确实看到了DeclarationExpressions和BinaryExpression之间的“连接”,但我不知道如何使用/应用它。再次感谢。当涉及到范围界定时,这不是一个问题(由于不同的代码片段是如何组合以进行交互的)。如果你不知道,我会接受你的回答!以下是工作代码: