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

  1. AST (Abstract Syntax Tree): Representación estructurada del código fuente en forma de árbol, donde cada nodo representa una construcción sintáctica.
  2. Transformaciones AST: Modificaciones que se aplican al AST durante la fase de compilación.
  3. Anotaciones: Utilizadas para marcar clases, métodos o campos que deben ser transformados.

Tipos de Transformaciones AST

  1. Transformaciones Locales: Aplicadas a elementos específicos del código, como métodos o clases.
  2. 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

  1. Anotación @ToString: Indica que se debe generar un método toString() para la clase Persona.
  2. Clase Persona: Contiene dos campos, nombre y edad.
  3. Instancia de Persona: Se crea una instancia de Persona y se imprime su representación en cadena.

Salida Esperada

Persona(Juan, 30)

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

@MiTransformacion
class MiClase {
}

def instancia = new MiClase()
instancia.saludar()

Explicación

  1. Anotación @MiTransformacion: Marca la clase MiClase para aplicar la transformación.
  2. Transformación MiTransformacionAST: Implementa la lógica para agregar un método saludar a la clase anotada.
  3. Método saludar: Imprime un mensaje cuando se llama.

Salida Esperada

¡Hola desde la transformación AST!

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

  1. 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 {
}
  1. 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)
    }
}
  1. Usar la Transformación:
@Despedida
class MiOtraClase {
}

def otraInstancia = new MiOtraClase()
otraInstancia.despedir()

Salida Esperada

¡Adiós desde la transformación AST!

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.

© Copyright 2024. Todos los derechos reservados