【问题标题】:Groovy Compile time AST transformation: Assignment to a fieldGroovy 编译时 AST 转换:分配给字段
【发布时间】:2014-07-31 07:54:05
【问题描述】:

我目前正在尝试实现一些 Groovy 编译时 AST 转换,但遇到了麻烦:

如何为字段赋值语句指定 AST 转换?即 AST 转换应该转换以下代码:

class MyClass {
    @MyTransformation
    String myField

    public void init() {
    }
}

变成类似的东西

class MyClass {
    String myField

    public void init() {
        this.myField = "initialized!"
    }
}

我尝试使用这个 AST 构建器调用:

def ast = new AstBuilder().buildFromSpec {
        expression{
            declaration {
                variable "myField"
                token "="
                constant "initialized!"
            }
        }
}

但是在声明类的“init”方法中插入结果语句后,它反而插入了一个变量赋值,如

java.lang.Object myField = "initialized!"

我查看了Ast Builder TestCase 中包含的示例,但它们仅涵盖类主体中的字段声明,而不是对字段的分配。我自己使用fieldNode 的尝试都导致了编译器错误。我将编译阶段设置为INSTRUCTION_SELECTION;我觉得应该没问题。

我如何实现这一目标?首选基于AstBuilder#buildFromSpec 方法的解决方案,但我们将非常感谢任何帮助。

【问题讨论】:

    标签: groovy abstract-syntax-tree


    【解决方案1】:

    我通常建议不要使用 AST 构建器。它对原型设计很有用,但您并不能真正控制它生成的内容。特别是,在这里,它无法处理您创建的变量表达式应该引用字段节点的事实。 AST Builder 非常适合学习 AST,但恕我直言,不应在生产代码中使用。

    这是一个自包含的示例,演示了如何实现您想要的。 @ASTTest 中的代码将对应于您的转换代码:

    import groovy.transform.ASTTest                                                         
    import org.codehaus.groovy.ast.expr.BinaryExpression                            
    import org.codehaus.groovy.ast.expr.VariableExpression                          
    import org.codehaus.groovy.ast.expr.ConstantExpression                          
    import org.codehaus.groovy.ast.stmt.ExpressionStatement                         
    import org.codehaus.groovy.syntax.Token                                         
    import org.codehaus.groovy.syntax.Types      
    
    class MyClass {                                                                         
        String myField                                                                      
    
        @ASTTest(phase=SEMANTIC_ANALYSIS,value={                                            
                def classNode = node.declaringClass                                         
                def field = classNode.getDeclaredField('myField')                           
                def assignment = new BinaryExpression(                                      
                  new VariableExpression(field),                                              
                  Token.newSymbol(Types.EQUAL, 0, 0),                                         
                  new ConstantExpression('initialized!')                                      
                )                                                                                   
                node.code.addStatement(new ExpressionStatement(assignment))                 
        })                                                                          
        public void init() {                                                                
        }                                                                                   
    }                                                                                       
    def c = new MyClass()                                                                   
    c.init()                                                                                
    println c.myField
    

    希望这会有所帮助!

    【讨论】:

    • 谢谢,这是一个很好的答案。它不仅解决了我当前的问题,而且您还为我指出了最佳实践和我不熟悉的 @ASTTest 注释。
    猜你喜欢
    • 2014-02-27
    • 1970-01-01
    • 2016-09-07
    • 1970-01-01
    • 1970-01-01
    • 2017-12-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多