Skip to content

Introduccion a los Principios de Compilacion

Prologo

Cuando presionas el boton "ejecutar", como se convierte el codigo en el resultado en pantalla? Cada linea de codigo que escribes, la computadora en realidad "no la entiende" -- solo reconoce 0 y 1. El compilador es el "traductor" que convierte el lenguaje humano en lenguaje maquina. Comprender los principios de compilacion te permite entender de donde vienen los mensajes de error, por que algunos lenguajes son rapidos y otros lentos, y la logica subyacente de la optimizacion de codigo.

Que aprenderas en este articulo?

Despues de completar este capitulo, obtendras:

  • Vision global: dominar la canalizacion completa de compilacion desde el codigo fuente hasta el programa ejecutable
  • Analisis lexico: entender como el compilador divide el codigo en Tokens
  • Analisis sintactico: entender la construccion del AST (Arbol de Sintaxis Abstracta)
  • Visualizacion de AST: ver intuitivamente la estructura de arbol del codigo
  • Analisis semantico y optimizacion: entender los principios de la verificacion de tipos y la optimizacion de codigo
  • Practica de tecnicas de optimizacion: dominar tecnicas centrales como plegamiento de constantes, eliminacion de codigo muerto
  • Modelo de ejecucion: distinguir entre compilacion, interpretacion y JIT
CapituloContenidoConcepto clave
Capitulo 1Que es un compiladorAnalogia del traductor, canalizacion de compilacion
Capitulo 2Analisis lexicoToken, reglas lexicas
Capitulo 3Analisis sintacticoAST, arbol de sintaxis, precedencia
Capitulo 4Visualizacion de ASTArbol de sintaxis interactivo, tipos de nodos
Capitulo 5Analisis semantico y optimizacionVerificacion de tipos, plegamiento de constantes, eliminacion de codigo muerto
Capitulo 6Practica de tecnicas de optimizacionInline de funciones, extraccion de invariantes de bucle, propagacion de constantes
Capitulo 7Compilado vs Interpretado vs JITComparacion de tres modelos de ejecucion

0. Vision general: El "viaje de traduccion" del codigo

Imagina que eres un traductor y debes traducir una novela china al ingles. No traduciras palabra por palabra literalmente, sino que:

  1. Identificar palabras -- dividir las oraciones en palabras (analisis lexico)
  2. Entender la sintaxis -- juzgar si la estructura de la oracion es correcta (analisis sintactico)
  3. Entender la semantica -- asegurar que el significado sea coherente y sin contradicciones (analisis semantico)
  4. Pulir y optimizar -- hacer la traduccion mas natural y fluida (optimizacion de codigo)
  5. Producir la traduccion -- escribir la version final en ingles (generacion de codigo)

El compilador hace exactamente lo mismo, excepto que traduce lenguajes de programacion.

Compiler Principles: The Art of TranslationHow code becomes machine instructions
A compiler is like a translator, turning human-readable code into machine-readable instructions
The Complete Code Translation Pipeline
1
Lexical analysis
Break code into individual words called tokens
int age = 25 → [int, age, =, 25]
2
Syntax analysis
Check grammar rules and build a syntax tree
Validate whether statement structure is correct
3
Semantic analysis
Check whether the meaning of the code is valid
Check variable definitions and type compatibility
4
Intermediate code generation
Generate a machine-independent intermediate representation
Generate bytecode or intermediate representation
5
Optimization
Improve code so it runs more efficiently
Constant folding and dead-code elimination
6
Target code generation
Generate machine code or target code
Generate x86 or ARM machine instructions
Lexical analysis: tokenization
int age = 25;
Keywordint
Identifierage
Operator=
Number25
Separator;
Syntax analysis: build a tree
Assignment statement
Variableage
Operator=
Number25
Compilation vs Interpretation
Compiled languages
Source code → Compiler → Machine code
C, Go, Rust
✓ Fast execution
✓ Compile once, run many times
✗ Slow compile step
Interpreted languages
Source code → Interpreter → Line-by-line execution
Python, JavaScript, PHP
✓ Fast development
✓ Cross-platform
✗ Slower execution
Compiler Optimization
Before:
x = 5 + 3 + 2
⬇️
After:
x = 10
The compiler can optimize code automatically and improve runtime efficiency

1. La canalizacion de seis pasos del compilador

El trabajo del compilador se puede dividir en seis etapas, como una linea de produccion de fabrica, donde cada etapa entrega su resultado a la siguiente.

How a Compiler WorksA six-step journey from source code to machine code
1
Lexical analysis→ Token stream
2
Syntax analysis→ AST syntax tree
3
Semantic analysis→ Typed AST
4
Intermediate code generation→ IR (intermediate representation)
5
Code optimization→ Optimized IR
6
Target code generation→ Machine code
1Lexical analysisOutput: Token stream
Split source code into individual words called tokens, like recognizing each word in a sentence.
Recognize keywordsRecognize identifiersRecognize numbersRecognize operatorsFilter whitespace
int x = 10 + 5;
→ [int] [x] [=] [10] [+] [5] [;]
    keyword identifier operator number operator number separator
Live lexical analysis
intKeyword
xIdentifier
=Operator
10Number
+Operator
5Number
;Separator
Three Execution Models Compared
Compiled
Source Compiler Machine code CPU execution
Fast executionMust wait for compilation
C, C++, Rust, Go
Interpreted
Source Interpreter Line-by-line execution
Run immediately while writingSlower execution
Python, Ruby, PHP
JIT
Source Bytecode JIT hot path compilation Execution
Balances performance and flexibilitySlower startup
Java, JavaScript (V8)
Core idea:A compiler is like a translator: it gradually turns human-readable code into instructions the machine can run. The six stages each do one job: identify words → understand syntax → check meaning → generate IR → optimize → generate machine code.

Canalizacion de compilacion

  1. Analisis lexico (Lexical Analysis): dividir el codigo fuente en Tokens (palabras)
  2. Analisis sintactico (Syntax Analysis): organizar los Tokens en un arbol de sintaxis (AST)
  3. Analisis semantico (Semantic Analysis): verificar si los tipos son correctos, si las variables estan declaradas
  4. Generacion de codigo intermedio (IR Generation): generar una representacion intermedia independiente de la plataforma
  5. Optimizacion de codigo (Optimization): hacer el codigo intermedio mas eficiente
  6. Generacion de codigo (Code Generation): generar codigo maquina para la plataforma destino
EtapaEntradaSalidaAnalogia
Analisis lexicoFlujo de caracteres del codigo fuenteFlujo de TokensDividir oraciones en palabras
Analisis sintacticoFlujo de TokensAST (arbol de sintaxis)Analizar la estructura de la oracion
Analisis semanticoASTAST con tiposVerificar si el significado es coherente
Codigo intermedioAST con tiposIREscribir el primer borrador
Optimizacion de codigoIRIR optimizadoPulir y recortar
Generacion de codigoIR optimizadoCodigo maquinaProducir la version final

2. Analisis lexico: dividir el codigo en "palabras"

El analisis lexico es el primer paso de la compilacion. El compilador escanea cada caracter del codigo fuente de izquierda a derecha, agrupandolos en Tokens (unidades lexicas) significativas.

🔤 Lexer: Split Code into Tokens

Enter a line of code and see lexical analysis results in real time

Al igual que cuando lees una oracion en ingles, tu cerebro agrupa automaticamente las letras en palabras, el analizador lexico agrupa caracteres en Tokens:

Codigo fuente: let x = 10 + 5;

Flujo de Tokens:
[let]   → Palabra clave (palabra reservada del lenguaje)
[x]     → Identificador (nombre de variable)
[=]     → Operador (asignacion)
[10]    → Literal numerico
[+]     → Operador (suma)
[5]     → Literal numerico
[;]     → Separador (fin de instruccion)

Los cinco tipos de Token

  • Palabras clave: palabras especiales reservadas por el lenguaje, como let, if, return, function
  • Identificadores: nombres definidos por el programador, como nombres de variables, nombres de funciones
  • Literales: valores escritos directamente en el codigo, como numeros 42, cadenas "hello"
  • Operadores: simbolos que ejecutan operaciones, como +, -, =, ===
  • Separadores: simbolos que separan la estructura del codigo, como ;, ,, (, )

3. Analisis sintactico: construir el arbol de sintaxis (AST)

El analisis lexico divide el codigo en Tokens, pero estos son solo "palabras" aisladas. La tarea del analisis sintactico es organizar estos Tokens segun las reglas gramaticales en un Arbol de Sintaxis Abstracta (Abstract Syntax Tree, AST) -- que refleja la estructura del codigo y la precedencia de las operaciones.

Expresion: 1 + 2 * 3

Arbol de sintaxis:        Por que asi?
       +       Porque la precedencia de *
      / \      es mayor que +, entonces
     1   *     2 * 3 se combina primero
        / \    como un subarbol
       2   3

La importancia del AST

El AST es la "estructura de datos central" del compilador; el analisis semantico, la optimizacion y la generacion de codigo posteriores se basan en el. Las herramientas de desarrollo modernas tambien usan extensivamente el AST:

  • ESLint: analiza el codigo como AST, verifica si viola reglas
  • Prettier: analiza como AST y luego reformatea la salida
  • Babel: analiza AST → transforma → genera codigo compatible
  • Refactorizacion en IDE: renombrado seguro de variables, extraccion de funciones basada en AST
Estructura sintacticaSecuencia de TokensNodo AST
Declaracion de variablelet x = 10VariableDeclaration → Identifier + Literal
Llamada a funcionadd ( 1 , 2 )CallExpression → Identifier + Arguments
Condicionalif ( a > b )IfStatement → BinaryExpression + Block

4. Visualizacion de AST: ver el "esqueleto" del codigo

Arriba describimos la estructura del AST con texto, pero "ver" es mas intuitivo que "leer". El componente interactivo a continuacion te permite seleccionar diferentes expresiones y observar en tiempo real como se ven sus arboles de sintaxis.

🌳 AST Visualizer: See the Skeleton of Code

Choose an expression and inspect its abstract syntax tree

Syntax tree
BinaryExpression+
NumericLiteral1
BinaryExpression*
NumericLiteral2
NumericLiteral3
Parse notes
1* has higher precedence than +, so 2 * 3 groups first
22 * 3 forms a BinaryExpression subtree
31 and that subtree become the left and right operands of +
4The final + node is the root, showing the evaluation order
💡 Try AST Explorer — inspect ASTs for arbitrary code online

A traves de la visualizacion descubriras que las reglas centrales del AST son realmente simples:

Estructura del codigoNodo raiz del ASTNodos hijos
1 + 2 * 3BinaryExpression (+)Izq: NumericLiteral(1), Der: BinaryExpression(*)
let x = 10VariableDeclarationVariableDeclarator → Identifier(x) + NumericLiteral(10)
add(a, b)CallExpressionIdentifier(add) + Arguments(a, b)

Aplicaciones del AST en el desarrollo diario

Puede que no hayas escrito un compilador directamente, pero usas herramientas basadas en AST todos los dias:

  • ESLint / Prettier: analizan el codigo como AST, verifican reglas o reformatean
  • Babel / SWC: analizan AST → transforman sintaxis → generan codigo compatible
  • Refactorizacion en IDE: renombrado seguro, extraccion de funciones basada en AST
  • Tree-shaking: analiza import/export en el AST, elimina codigo no utilizado

5. Analisis semantico y optimizacion de codigo

El analisis sintactico asegura que el codigo sea "estructuralmente correcto", pero eso no significa que su "significado sea correcto". El analisis semantico verifica que el significado del codigo sea valido, y la optimizacion de codigo hace que el programa se ejecute mas rapido.

Compilation PracticeFrom code to executable file
Input code
Compilation steps
1
Preprocess
gcc -E hello.c -o hello.i
Process #include and expand macros
2
Compile
gcc -S hello.i -o hello.s
Generate assembly code
3
Assemble
gcc -c hello.s -o hello.o
Generate object file
4
Link
gcc hello.o -o hello
Generate executable file
Generated files
📄
hello.c
Source code file
📝
hello.i
Preprocessed file
⚙️
hello.s
Assembly code file
📦
hello.o
Object file
🚀
hello
Executable file
Common compiler tools
GCC
GNU Compiler Collection
Clang
LLVM C/C++ compiler
MSVC
Microsoft Visual C++

4.1 Analisis semantico: verificar si el "significado" es correcto

Contenido de verificacionEjemploResultado
Verificacion de tiposint x = "hello"Tipos incompatibles
Verificacion de ambitoUsar variable no declarada yLa variable no existe
Inferencia de tipos1 + 2.0Inferir resultado como float
Verificacion de parametrosadd(1, 2, 3) pero la funcion solo acepta 2 parametrosCantidad de parametros no coincide

Los errores que has visto provienen en su mayoria del analisis semantico

  • TypeError: Cannot read properties of undefined -- verificacion de tipos
  • ReferenceError: x is not defined -- verificacion de ambito
  • Expected 2 arguments, but got 3 -- verificacion de parametros

4.2 Optimizacion de codigo: hacer que el programa sea mas rapido

Antes de generar el codigo final, el compilador realiza diversas optimizaciones en el codigo intermedio. Estas optimizaciones son transparentes para el programador, pero pueden mejorar significativamente el rendimiento.

Tecnica de optimizacionAntesDespuesPrincipio
Plegamiento de constantesx = 10 + 5x = 15Calcular el resultado en tiempo de compilacion
Eliminacion de codigo muertoif (false) { ... }Eliminar directamenteCodigo que nunca se ejecutara
Propagacion de constantesx = 15; y = x * 2y = 30Reemplazar directamente con valores conocidos
Extraccion de invariantes de bucleCalcular repetidamente len = arr.length dentro del bucleMover fuera del bucleEvitar calculos repetidos

6. Practica de tecnicas de optimizacion: como el compilador hace el codigo mas rapido

Arriba mencionamos los nombres de varias tecnicas de optimizacion, ahora veamos en detalle como lo hace el compilador. El componente interactivo a continuacion muestra las 5 optimizaciones de compilador mas comunes; puedes comparar intuitivamente las diferencias antes y despues de la optimizacion.

⚡ Compiler Optimization: Make Code Faster Automatically

Choose an optimization technique and see how the compiler improves code

📝 Before optimization
const width = 10
const height = 20
const area = width * height  // computed at runtime
console.log(area)
Compiler optimization
🚀 After optimization
const area = 200  // computed during compilation
console.log(200)
How Constant folding works
The compiler sees that width and height are constants, so it computes 10 * 20 = 200 during compilation. Runtime no longer needs a multiplication.
Performance gain:
30%

Los compiladores modernos y motores JIT (como V8, GCC, LLVM) aplican automaticamente decenas de optimizaciones. Como desarrollador, no necesitas hacerlas manualmente, pero entenderlas te ayuda a:

  • Escribir codigo mas facil de optimizar: por ejemplo, usar const en lugar de let, el compilador puede hacer plegamiento de constantes mas facilmente
  • Entender las diferencias de rendimiento: por que las funciones pequenas son mas rapidas que las grandes? Porque el compilador puede hacer inline de ellas
  • Evitar la "desoptimizacion": ciertos patrones de escritura impiden la optimizacion del compilador, como eval() y with
Tecnica de optimizacionCondicion de activacionImpacto en rendimientoQue puede hacer el desarrollador
Plegamiento de constantesExpresiones con todas constantesEliminar calculo en tiempo de ejecucionUsar mas declaraciones const
Eliminacion de codigo muertoCodigo inalcanzable o resultado no utilizadoReducir tamano del codigoLimpiar codigo no utilizado oportunamente
Extraccion de invariantes de bucleCalculo invariante dentro del bucleReducir calculos repetidosLa extraccion manual tambien es un buen habito
Inline de funcionesFunciones pequenas llamadas frecuentementeEliminar overhead de llamadasMantener funciones pequenas y enfocadas
Propagacion de constantesValor de variable determinable en compilacionToda la cadena de calculo se eliminaUsar constantes en lugar de numeros magicos

7. Compilado vs Interpretado vs JIT

Despues de escribir el codigo, hay tres "formas de traduccion" para ejecutarlo. Estas tres formas tienen ventajas y desventajas, y determinan directamente las caracteristicas de rendimiento y los escenarios de uso del lenguaje.

🔄 Compiled vs Interpreted vs JIT

Click an execution mode to see how code moves from source to running program

📝
Source code
main.c
⚙️
Compiler
Full compilation
📦
Machine code
Binary executable
🚀
Run directly
CPU runs it directly
Run speed
Very fast
Startup
Slow; compile first
Portability
Recompile required
Representative languages:CC++RustGo
DimensionCompiladoInterpretadoJIT (Compilacion en tiempo real)
ProcesoCompilar todo a codigo maquina primero, luego ejecutarLeer y ejecutar linea por linea, traducir al vueloInterpretar primero, luego compilar el codigo caliente
Velocidad de ejecucionLa mas rapidaLa mas lentaMedia (codigo caliente cercano al compilado)
Velocidad de inicioLenta (necesita compilacion)Rapida (ejecucion directa)Media (necesita calentamiento)
MultiplataformaNecesita recompilarNaturalmente multiplataformaMultiplataforma
Lenguajes representativosC, Rust, GoPython, RubyJavaScript (V8), Java

Por que JavaScript es tan rapido?

El compilador JIT del motor V8 monitorea que codigo se ejecuta con frecuencia (codigo caliente) y luego lo compila en codigo maquina altamente optimizado. Asi que aunque JavaScript es un "lenguaje interpretado", en V8 su rendimiento puede acercarse al de los lenguajes compilados. Esta es tambien la base que permite que Node.js funcione en el lado del servidor.


Resumen

Los principios de compilacion no son un conocimiento que solo los desarrolladores de compiladores necesitan conocer. Comprender el proceso de compilacion te ayuda a entender mejor los mensajes de error, elegir el lenguaje adecuado y escribir codigo mas eficiente.

Repaso de los puntos clave de este capitulo:

  1. El compilador es un traductor: convierte codigo legible por humanos en instrucciones ejecutables por maquinas
  2. Canalizacion de seis pasos: analisis lexico → analisis sintactico → analisis semantico → codigo intermedio → optimizacion → generacion de codigo
  3. El analisis lexico divide en Tokens: divide el flujo de caracteres en unidades significativas como palabras clave, identificadores, operadores
  4. El analisis sintactico construye el AST: organiza los Tokens en una estructura de arbol segun las reglas gramaticales, reflejando la precedencia de operaciones
  5. El analisis semantico asegura la correccion: verificacion de tipos, verificacion de ambito; la mayoria de los errores que has visto provienen de aqui
  6. El compilador optimiza automaticamente: tecnicas como plegamiento de constantes, eliminacion de codigo muerto, inline de funciones hacen que el codigo se acelere automaticamente
  7. Tres modelos de ejecucion: compilado el mas rapido, interpretado el mas flexible, JIT combina ambos

Lectura adicional