Groovy for Domain-specific Languages - Second Edition

(nextflipdebug2) #1
Chapter 8

[ 193 ]

null,
methodStatement
)
classNode.addMethod(methodNode)
}
}

The main difference you will notice here is the use of the ASTBuilder class to
help us build the method statement for our new method. In this case, we use the
buildFromCode method of ASTBuilder. This convenience function allows us to
convert a closure into the AST nodes we need. ASTBuilder will interrogate the
closure code and turn that into the equivalent nodes from the following code:


this.properties.sort { it.key }.each {
if (it.key != 'prettyPrint' && it.key != 'class')
println it.key + ": " + it.value
}

There are significant benefits to using this instead of the previous method. Firstly,
it is much clearer what code is being added by the transform. You don't need to
understand all the arcane ASTNode subtypes, and how they need to be structured.
You can already see whether your code is syntactically correct because the IDE
will show you. One disadvantage is, however, that not all code can be built using a
closure. For instance, you cannot formulate a class field expression using a closure.
It's perfect, however, for where you want to add a whole method block or to insert a
block of code into an existing method.


Build from Spec


The ASTBuilder class also provides a rich builder style DSL for building the AST.
In the next example, we will use ASTBuilder.buildFromSpec to build the entire
prettyPrint method node:


@Target([ElementType.TYPE])
@Retention(RetentionPolicy.SOURCE)
@GroovyASTTransformationClass(["PrettyAdvancedASTTransformation"])
public @interface PrettyAdvanced {

}

@GroovyASTTransformation (phase = CompilePhase.SEMANTIC_ANALYSIS)
class PrettyAdvancedASTTransformation implements ASTTransformation {

void visit(ASTNode[] nodes, SourceUnit source) {
if (!nodes) return
Free download pdf