En el post anterior vimos qué es LLVM y por qué cambió la forma de construir compiladores.
Ahora vamos a lo interesante: crear un mini lenguaje propio y compilarlo a LLVM IR
Vamos a construir un lenguaje súper simple que permita esto:
sum(10, 20)
Y lo transforme en LLVM IR listo para compilar.
Nuestro “compilador” va a tener:
- Parser (muy básico)
- AST (árbol)
- Generador de LLVM IR
Paso 1: Definir el lenguaje
Nuestro lenguaje soporta:
Función: sum(a, b)
Números enteros
Ejemplo válido:
sum(4, 5)
Paso 2: Representar el AST
En pseudocódigo:
java id="y0yqg3"
interface Expr {}
class Number implements Expr {
int value;
}
class Sum implements Expr {
Expr left;
Expr right;
}
Paso 3: Parser (ultra simple)
Para simplificar, parseamos a mano:
java id="x7p0a1"
Expr parse(String input) {
// sum(10,20)
String inside = input.substring(4, input.length() - 1);
String[] parts = inside.split(",");
return new Sum(
new Number(Integer.parseInt(parts[0])),
new Number(Integer.parseInt(parts[1]))
);
}
Sí, es naive, pero suficiente para entender el flujo
Paso 4: Generar LLVM IR
Acá está la magia 💣
java id="l6c0qw"
String generate(Expr expr) {
if (expr instanceof Sum s) {
return """
define i32 @main() {
%a = add i32 %d, %d
ret i32 %a
}
""".formatted(
((Number)s.left).value,
((Number)s.right).value
);
}
throw new RuntimeException("Unsupported");
}
Resultado
Entrada:
sum(10, 20)
Salida:
llvm id="0a8v8l"
define i32 @main() {
%a = add i32 10, 20
ret i32 %a
}
Ya es LLVM IR válido
Paso 5: Ejecutarlo
Guardás el archivo como main.ll y usás:
bash id="3e7twz"
lli main.ll
o lo compilás con:
bash id="z4c9yb"
llc main.ll
Aunque el ejemplo es simple, reproduce el flujo real:
1. Texto → AST
2. AST → IR
3. IR → código ejecutable
Exactamente lo que hacen lenguajes como Rust o compiladores como Clang
Lo difícil NO es generar código máquina.
Lo difícil es definir bien el lenguaje y su semántica.
LLVM te resuelve:
- optimización
- portabilidad
- generación de código

No hay comentarios.:
Publicar un comentario