Introducción
Las Transformaciones AST (Abstract Syntax Tree) son una característica poderosa de Groovy que permite modificar el árbol de sintaxis abstracta del código durante la compilación. Esto permite a los desarrolladores agregar comportamientos adicionales, optimizar el código y realizar verificaciones en tiempo de compilación.
Conceptos Clave
- AST (Abstract Syntax Tree): Representación estructurada del código fuente en forma de árbol, donde cada nodo representa una construcción sintáctica.
- Transformaciones AST: Modificaciones que se aplican al AST durante la fase de compilación.
- Anotaciones: Utilizadas para marcar clases, métodos o campos que deben ser transformados.
Tipos de Transformaciones AST
- Transformaciones Locales: Aplicadas a elementos específicos del código, como métodos o clases.
- Transformaciones Globales: Aplicadas a todo el código fuente durante la compilación.
Ejemplo Práctico: @ToString
Una de las transformaciones AST más comunes en Groovy es la anotación @ToString, que genera automáticamente un método toString() para una clase.
Ejemplo de Código
import groovy.transform.ToString
@ToString
class Persona {
String nombre
int edad
}
def persona = new Persona(nombre: 'Juan', edad: 30)
println persona.toString()Explicación
- Anotación
@ToString: Indica que se debe generar un métodotoString()para la clasePersona. - Clase
Persona: Contiene dos campos,nombreyedad. - Instancia de
Persona: Se crea una instancia dePersonay se imprime su representación en cadena.
Salida Esperada
Creación de Transformaciones AST Personalizadas
Paso 1: Crear la Anotación
import java.lang.annotation.ElementType
import java.lang.annotation.Retention
import java.lang.annotation.RetentionPolicy
import java.lang.annotation.Target
@Retention(RetentionPolicy.SOURCE)
@Target([ElementType.TYPE])
@interface MiTransformacion {
}Paso 2: Implementar la Transformación
import org.codehaus.groovy.ast.*
import org.codehaus.groovy.ast.builder.AstBuilder
import org.codehaus.groovy.ast.expr.*
import org.codehaus.groovy.ast.stmt.*
import org.codehaus.groovy.control.*
import org.codehaus.groovy.transform.*
@GroovyASTTransformation(phase = CompilePhase.SEMANTIC_ANALYSIS)
class MiTransformacionAST implements ASTTransformation {
void visit(ASTNode[] nodes, SourceUnit source) {
if (nodes.length != 2 || !(nodes[0] instanceof AnnotationNode) || !(nodes[1] instanceof AnnotatedNode)) {
return
}
AnnotatedNode parent = (AnnotatedNode) nodes[1]
if (parent instanceof ClassNode) {
ClassNode classNode = (ClassNode) parent
addMetodoSaludar(classNode)
}
}
private void addMetodoSaludar(ClassNode classNode) {
MethodNode metodoSaludar = new MethodNode(
'saludar',
ACC_PUBLIC,
ClassHelper.VOID_TYPE,
[] as Parameter[],
[] as ClassNode[],
new BlockStatement(
[new ExpressionStatement(new MethodCallExpression(
new VariableExpression('this'),
'println',
new ConstantExpression('¡Hola desde la transformación AST!')
))],
new VariableScope()
)
)
classNode.addMethod(metodoSaludar)
}
}Paso 3: Usar la Transformación
Explicación
- Anotación
@MiTransformacion: Marca la claseMiClasepara aplicar la transformación. - Transformación
MiTransformacionAST: Implementa la lógica para agregar un métodosaludara la clase anotada. - Método
saludar: Imprime un mensaje cuando se llama.
Salida Esperada
Ejercicio Práctico
Ejercicio
Crea una transformación AST que agregue un método despedir a cualquier clase anotada con @Despedida. El método debe imprimir "¡Adiós desde la transformación AST!".
Solución
- Crear la Anotación
@Despedida:
import java.lang.annotation.ElementType
import java.lang.annotation.Retention
import java.lang.annotation.RetentionPolicy
import java.lang.annotation.Target
@Retention(RetentionPolicy.SOURCE)
@Target([ElementType.TYPE])
@interface Despedida {
}- Implementar la Transformación
DespedidaAST:
import org.codehaus.groovy.ast.*
import org.codehaus.groovy.ast.builder.AstBuilder
import org.codehaus.groovy.ast.expr.*
import org.codehaus.groovy.ast.stmt.*
import org.codehaus.groovy.control.*
import org.codehaus.groovy.transform.*
@GroovyASTTransformation(phase = CompilePhase.SEMANTIC_ANALYSIS)
class DespedidaAST implements ASTTransformation {
void visit(ASTNode[] nodes, SourceUnit source) {
if (nodes.length != 2 || !(nodes[0] instanceof AnnotationNode) || !(nodes[1] instanceof AnnotatedNode)) {
return
}
AnnotatedNode parent = (AnnotatedNode) nodes[1]
if (parent instanceof ClassNode) {
ClassNode classNode = (ClassNode) parent
addMetodoDespedir(classNode)
}
}
private void addMetodoDespedir(ClassNode classNode) {
MethodNode metodoDespedir = new MethodNode(
'despedir',
ACC_PUBLIC,
ClassHelper.VOID_TYPE,
[] as Parameter[],
[] as ClassNode[],
new BlockStatement(
[new ExpressionStatement(new MethodCallExpression(
new VariableExpression('this'),
'println',
new ConstantExpression('¡Adiós desde la transformación AST!')
))],
new VariableScope()
)
)
classNode.addMethod(metodoDespedir)
}
}- Usar la Transformación:
Salida Esperada
Conclusión
Las transformaciones AST en Groovy son una herramienta poderosa para modificar y mejorar el código durante la compilación. Permiten agregar comportamientos adicionales, realizar optimizaciones y verificaciones en tiempo de compilación. Con la práctica, puedes crear transformaciones personalizadas que se adapten a tus necesidades específicas y mejoren la eficiencia y la calidad de tu código.
Curso de Programación Groovy
Módulo 1: Introducción a Groovy
Módulo 2: Sintaxis de Groovy y Características del Lenguaje
Módulo 3: Programación Orientada a Objetos en Groovy
Módulo 4: Características Avanzadas de Groovy
Módulo 5: Groovy en la Práctica
- Entrada/Salida de Archivos
- Trabajando con XML y JSON
- Acceso a Bases de Datos
- Desarrollo Web con Groovy
Módulo 6: Pruebas y Depuración
Módulo 7: Ecosistema y Herramientas de Groovy
- Herramienta de Construcción Gradle
- Framework de Pruebas Spock
- Framework Grails
- Otras Bibliotecas y Herramientas de Groovy
Módulo 8: Mejores Prácticas y Temas Avanzados
- Estilo de Código y Convenciones
- Optimización del Rendimiento
- Consideraciones de Seguridad
- Concurrencia en Groovy
