我有一个Groovy
应用程序可以被我写的一个小Groovy DSL
custimized。启动时,应用程序加载多个Groovy
脚本,应用一些AST
转换,最后执行脚本中指定的任何内容。
其中一个AST
转换将几行代码插入到某些方法中。这工作正常,我可以在运行时看到不同的行为。但是,有时生成的代码不正确。虽然我使用TypeChecked
自定义程序加载了脚本,但我生成的代码从不检查以确保完整性。
为了表明我的问题,我构建了一个极端的例子。我有以下脚本:
int test = 10
println test // prints 10 when executed without AST
我加载此脚本并在test
和println
声明之间插入一行新代码:
public void visitBlockStatement(BlockStatement block) {
def assignment = (new AstBuilder().buildFromSpec {
binary {
variable "test"
token "="
constant 15
}
}).first()
def newStmt = new ExpressionStatement(assignment)
newStmt.setSourcePosition(block.statements[1])
block.statements.add(2, newStmt)
super.visitBlockStatement(block)
}
应用此AST
后,脚本打印15 。当我使用AstNodeToScriptVisitor
打印生成的脚本的Groovy
代码时,我可以看到新的分配已添加到代码中。
然而,如果我将作业的值更改为String
值:
// ...
def assignment = (new AstBuilder().buildFromSpec {
binary {
variable "test"
token "="
constant "some value"
}
}).first()
// ...
我在运行时得到GroovyCastExcpetion
。虽然生成的脚本如下所示:
int test = 10
test = "some value" // no compile error but a GroovyCastException at runtime here. WHY?
println test
TypeChecked
未引发任何错误。我在this mailing list中读到,您需要设置要生成的代码的源位置,但是我这样做仍然无法正常工作。谁能提供一些我做错的反馈?非常感谢你!
我通过将AST附加到GroovyShell
来调用AST:
def config = new CompilerConfiguration()
config.addCompilationCustomizers(
new ASTTransformationCustomizer(TypeChecked)
)
config.addCompilationCustomizers(
new ASTTransformationCustomizer(new AddAssignmentAST())
)
def shell = new GroovyShell(config)
shell.evaluate(new File("./path/to/file.groovy"))
AST本身的类看起来像这样:
@GroovyASTTransformation(phase = CompilePhase.CANONICALIZATION)
class AddAssignmentAST implements ASTTransformation {
@Override
public void visit(ASTNode[] nodes, SourceUnit source) {
def transformer = new AddAssignmentTransformer()
source.getAST().getStatementBlock().visit(transformer)
}
private class AddAssignmentTransformer extends CodeVisitorSupport {
@Override
public void visitBlockStatement(BlockStatement block) {
// already posted above
}
}
}
由于我的Groovy
脚本只包含一个块(对于这个小例子),visitBlockStatement
方法只调用一次,添加了赋值(我可以在输出更改时验证)但不是永远抛出编译时错误。