Cuando se compila un programa Zig, el compilador no trabaja directamente con el texto que ves. En su lugar, convierte ese texto en una estructura de datos llamada árbol de sintaxis (AST, por sus siglas en inglés). Este árbol es una representación jerárquica de los elementos del lenguaje, donde cada nodo corresponde a una construcción del programa: declaraciones, expresiones, tipos, etc.
¿Qué contiene un árbol de sintaxis?
Un AST típico de Zig se compone de los siguientes elementos:
- Nodos de declaración (
Decl) – representan funciones, variables, tipos, etc. - Nodos de expresión (
Expr) – representan operaciones aritméticas, llamadas de función, accesos a campos, etc. - Nodos de sentencia (
Stmt) – control de flujo comoif,while,return, etc. - Nodos de tipo – describen tipos primitivos, structs, enums, etc.
- Nodos de literal – valores constantes como números, cadenas o booleanos.
Cada nodo tiene atributos que describen la posición en el código fuente, su tipo interno y, a veces, referencias a otros nodos.
Cómo se construye el árbol en Zig
El proceso se divide en tres etapas principales:
- Lexicado (Tokenización): El texto fuente se escanea y se divide en tokens (palabras clave, identificadores, operadores, etc.).
- Análisis sintáctico (Parsing): Los tokens se agrupan siguiendo las reglas gramaticales de Zig, formando una estructura de árbol.
- Generación de AST: El árbol resultante se convierte en un AST que el compilador puede procesar para la generación de código o análisis estático.
Durante el análisis sintáctico, el compilador también construye la tabla de símbolos y verifica tipos, pero estos detalles se ocultan tras la abstracción del árbol.
Ejemplo práctico: un programa Zig simple
A continuación se muestra un pequeño programa Zig que imprime “Hola, mundo!” en la consola, con comentarios línea por línea para ilustrar cómo cada línea del código se traduce en nodos del AST.
// Importa el módulo estándar que contiene utilidades de entrada/salida.
const std = @import("std");
// Función principal que se ejecuta al iniciar el programa.
pub fn main() void {
// Obtiene un escritor (writer) para el descriptor de salida estándar (stdout).
const stdout = std.io.getStdOut().writer();
// Imprime la cadena “Hola, mundo!” seguida de un salto de línea.
// La función print recibe la cadena y un array de argumentos (vacío en este caso).
try stdout.print("Hola, mundo!\n", .{});
}
El AST generado por este fragmento tendría la siguiente jerarquía (simplificada para ilustrar la idea):
- Decl (pub fn main)
- Stmt (try)
- Expr (stdout.print)
- Identifier (stdout)
- Identifier (print)
- Literal ("Hola, mundo!\n")
- Literal (array <empty>)
Observa cómo cada parte de la sintaxis se corresponde con un nodo:
- La declaración de la función
maines un nodoDecl. - La llamada a
printdentro detryes un nodoExprde tipo llamada de función. - Los argumentos de la llamada (
"Hola, mundo!\n"y.{}) son nodosLiteral.
Representaciones comunes de nodos
A continuación se muestran algunos de los tipos de nodos más frecuentes en un AST de Zig, junto con ejemplos de código y su traducción a nodos:
Declaraciones de variables
var count: i32 = 10;
- Decl (var count)
- Type (i32)
- Init (Literal 10)
Declaraciones de funciones
fn add(a: i32, b: i32) i32 {
return a + b;
}
- Decl (fn add)
- Params (a: i32, b: i32)
- Return Type (i32)
- Stmt (return)
- Expr (+)
- Identifier (a)
- Identifier (b)
Control de flujo (if/else)
if (count > 0) {
//...
}else {
//...
}
- Stmt (if)
- Expr (>)
- Identifier (count)
- Literal (0)
- Stmt Block (then)
- ...
- Stmt Block (else)
- ...
Herramientas útiles para inspeccionar el AST
- zig fmt – formatea el código, pero también puede ser útil para ver la estructura antes de compilar.
- zig build-exe –debug-builtin – compila con opciones de depuración que pueden revelar información interna del AST.
- herramientas de depuración externas (por ejemplo,
llvm-cov,lldb) – permiten inspeccionar el código generado y, indirectamente, el árbol que lo originó.
Conclusión
El árbol de sintaxis de Zig es la base sobre la cual gira todo el proceso de compilación. Entender su estructura no solo ayuda a depurar errores de compilación, sino que también abre la puerta a extensiones, análisis estático y generación de
