def someVariable = new ConstantExpression("someValue"); def returnStatement = new ReturnStatement( new ConstructorCallExpression( ClassHelper.make(SomeCoolClass), new ArgumentListExpression(someVariable) ) );
def someVariable = macro { "someValue" } def returnStatement = macro { return new SomeCoolClass($v{ someVariable }) }
def constructorCall = macro { new SomeCoolClass($v{ macro { "someValue" } }) }
Pass the string with the code, at the output we have the list of ASTNodes:
List<ASTNode> nodes = new AstBuilder().buildFromString("\"Hello\"")
Benefits
- The input is a string that can be taken from anywhere;
- Does not require an understanding of how ASTNode works;
- Allows you to specify a CompilePhase;
- Generates almost 100% valid code;
- Reliable - does not require a change in your code if the ASTNode structure in Groovy has changed.
disadvantages
- IDE will not help you with syntax checking;
- IDE refactorings will not work either;
- Some entities cannot be created - for example, a class field declaration.
Pass the closure (aka Closure) with the code, we have a list of nodes at the output:
List<ASTNode> nodes = new AstBuilder().buildFromCode { "Hello" }
Benefits (except the advantages of the previous method)
- The IDE allows you to use autocomplete, syntax checking and refactoring in the closure.
Disadvantages:
- This method does not solve the problem of the inability to generate a number of entities;
- Compiles the code, which is why it is not always possible to use cunning constructions, or a class that does not exist;
- The main drawback for me: the call to buildFromCode requires that the method be called just by creating AstBuilder:
new AstBuilder().buildFromCode { ... }
At the same time, you cannot even take AstBuilder to a separate field or local variable (therefore, the Groovy authors even had to resort to AstTransformation for this AstTransformation in order not to write a lot of code)
This method takes a closure (by the way, you can vote for my Issue or comment Pull Request so that the beautiful DelegatesTo annotation appears on this method), which is a DSL for building AST:
List<ASTNode> nodes = new AstBuilder().buildFromSpec { block { returnStatement { constant "Hello" } } }
Benefits
- Allows you to use Groovy logic to build nodes;
- Provides the ability to design almost any existing ASTNode;
- An important plus, because The AST generation theme in Groovy is not well documented: Fully documented and has extensive use cases in TestCase
disadvantages
- Sometimes it’s hard to understand exactly what you need to call to get the desired result;
- Less verbose than calling node constructors, but it still remains so;
- Strange implementation - for example, some methods accept Class instead of ClassNode, which reduces its use to nothing;
- Unreliable - AST can change with major releases of the language;
- You should know exactly how your AST should look like in a specific compilation phase;
- The IDE does not yet support autocomplete for this DSL (see my comment about the Pull Request).
It is also worth mentioning that you can combine these methods:
List<ASTNode> result = new AstBuilder().buildFromSpec { method('myMethod', Opcodes.ACC_PUBLIC, String) { parameters { parameter 'parameter': String.class } exceptions {} block { owner.expression.addAll new AstBuilder().buildFromCode { println 'Hello from a synthesized method!' println "Parameter value: $parameter" } } annotations {} } }
def someVariable = new ConstantExpression("someValue"); def returnStatement = new ReturnStatement( new ConstructorCallExpression( ClassHelper.make(SomeCoolClass), new ArgumentListExpression(someVariable) ) );
def someVariable = macro { "someValue" }; def returnStatement = macro { return new SomeCoolClass($v{ someVariable }) }
macro { return mySuperVariable }
and (new AstBuilder()).buildFromCode { return mySuperVariable }.first().expressions.first()
List<ASTNode> result = new AstBuilder().buildFromSpec { method('myMethod', Opcodes.ACC_PUBLIC, String) { parameters { parameter 'parameter': String.class } exceptions {} block { owner.expression.addAll macro { println 'Hello from a synthesized method!' println "Parameter value: $parameter" } } annotations {} } }
Source: https://habr.com/ru/post/205084/
All Articles