Generador de Código de Tres Direcciones
Escribe código en el editor y pulsa Analizar o Ejecutar para ver el C3D generado aquí.
¿Qué es el Código de Tres Direcciones?
El Código de Tres Direcciones (C3D) es una representación intermedia de un programa que vive entre el AST y el código máquina. Se llama "tres direcciones" porque cada instrucción tiene como máximo tres operandos: un resultado y dos fuentes.
Su rol en el pipeline de compilación es servir de capa de abstracción independiente de la arquitectura: no sabe nada de registros de CPU ni instrucciones específicas de x86 o ARM. Esto facilita la optimización y la generación posterior de código máquina real.
c = a + b * 5;
t0 = b * 5 t1 = a + t0 c = t1
Los Temporales
Los temporales (t0, t1, t2...) son variables sintéticas que el generador crea para guardar resultados intermedios de subexpresiones. No tienen nombre en el código fuente — son una invención del compilador.
Un Literal (5, "hola", true) o un Identificador (variable) se usan directamente — ya tienen un valor o una ubicación conocida.
// a y 5 se usan directo t0 = a + 5
El resultado de cualquier Binario o Unario necesita un lugar donde guardarse antes de ser usado por la instrucción padre.
t0 = b * 5 // resultado binario t1 = a + t0 // usa t0
Las Etiquetas
Las etiquetas (L0, L1, L2...) son puntos de destino para los saltos (goto). En C3D, las estructuras de control como if y while se implementan completamente con etiquetas y gotos — no existen como instrucciones nativas.
if a < b goto L0
goto L1
L0:
// bloque then
goto L2
L1:
// bloque else
L2:
// continúaL0:
if a < b goto L1
goto L2
L1:
// cuerpo
goto L0
L2:
// continúaReglas de Traducción por Nodo
El generador recorre el AST con el mismo patrón que el intérprete: un switch sobre el tipo de nodo. Cada tipo tiene su regla de traducción:
Se devuelve el valor directamente como string. No se crea temporal ni se emite instrucción — el valor se usa como operando directo en la instrucción padre.
// "42" se usa directo, no hay instrucción t0 = x + 42
Se devuelve el nombre de la variable directamente. No necesita temporal — ya tiene una ubicación en memoria.
// "x" e "y" se usan directo t0 = x + y
Se genera código para el hijo izquierdo, luego el derecho. Se crea un nuevo temporal y se emite: temporal = izq OP der.
// Para: a + b * 5 t0 = b * 5 // hijo derecho primero t1 = a + t0 // luego el padre
Se genera código para el operando. Se crea un temporal y se emite: temporal = OP operando.
t0 = -x
Se genera código para la expresión del valor. Se emite una copia desde el lugar resultado hacia la variable.
t0 = a + b x = t0
Se crean 2 etiquetas (L_true, L_false). Se emite goto condicional a L_true, goto incondicional a L_false, L_true: cuerpo, L_false: fin.
Se crean 3 etiquetas (L_true, L_false, L_fin). La rama then termina con goto L_fin para saltar el else.
Se crean 3 etiquetas (L_inicio, L_cuerpo, L_fin). Al final del cuerpo se emite goto L_inicio para el loop-back.
Equivale a un while con inicializador y actualización explícita. Se descompone en: copia inicial + patrón while + copia actualización antes del loop-back.
Se emite "param arg" por cada argumento en orden, luego se emite "call nombre, N". Si se necesita el valor de retorno, se captura en un temporal.
param x param 5 t0 = call suma, 2
Se genera código para la expresión de retorno y se emite "return lugar". Sin valor, se emite "return" solo.
t0 = a + b return t0
Se emite "func nombre:" (prólogo), luego el cuerpo de la función, luego "end_func nombre" (epílogo).
func suma:
// cuerpo
end_func sumaEficiencia y Precedencia
Una ventaja clave del generador C3D sobre generar código manualmente es que la precedencia de operadores ya está garantizada por el AST.
Cuando el parser construyó el AST de a + b * 5, colocó el nodo *más profundo (como hijo del nodo +). Al recorrer el árbol de forma recursiva bottom-up, el generador procesa b * 5 primero — obteniendo t0 = b * 5 — y solo después procesa el + usando t0. No hay que preocuparse por paréntesis ni jerarquías explícitas.
+
/ \
a *
/ \
b 5// Procesa * primero (más profundo) t0 = b * 5 // Luego procesa + con t0 t1 = a + t0