Una coma que nadie esperaba
Una tienda online guarda sus descuentos en un archivo de reglas. Alguien escribe price * 0,95, con coma, como se escribe en España. El programa rechaza la regla.
Nadie miró aún si la regla tiene sentido. Falló antes: al agrupar los caracteres en palabras. Esa primera parte de un compilador es pequeña y rápida, y es fácil equivocarse en ella.
Al final de esta lección
Nota
Puedo convertir el texto de un programa en tokens.
Palabras clave
| Palabra | Qué significa | Ejemplo |
|---|---|---|
| Token | una palabra del programa, con su tipo | NUMBER 50 |
| Analizador léxico | la parte del compilador que crea los tokens | |
| Patrón | la regla de cómo es un tipo de token | [0-9]+ |
| Coincidencia más larga | tomar todos los caracteres que el patrón admita | |
| Palabra reservada | una palabra que el lenguaje guarda para sí | if |
| Generador léxico | una herramienta que escribe el analizador desde patrones | flex |
Las celdas que rellenan los estudiantes quedan en blanco en esta vista previa.
Un token es un tipo y un texto
El analizador léxico es la primera parte del compilador propiamente dicho. Lee los caracteres de izquierda a derecha y entrega tokens.
Cada token tiene un tipo, como NUMBER o NAME, y el texto del que salió. Los espacios y los comentarios no crean tokens. Solo marcan dónde acaba uno y empieza el siguiente.
Ejemplo: if total >= 50
| Caracteres | Tipo | Texto |
|---|---|---|
| i f | KEYWORD | if |
| t o t a l | NAME | total |
| > = | OP | >= |
| 5 0 | NUMBER | 50 |
| Los tres espacios | Ningún token | Se saltan |
Las celdas que rellenan los estudiantes quedan en blanco en esta vista previa.
Un analizador léxico en Python
# Un analizador léxico mínimo: números con punto, nombres y signos.
text = "price * 1.21"
tokens = []
i = 0
while i < len(text):
ch = text[i]
if ch == " ":
i = i + 1
elif ch.isdigit():
start = i
# Sigue mientras haya cifras o un punto: la coincidencia más larga.
while i < len(text) and (text[i].isdigit() or text[i] == "."):
i = i + 1
tokens.append(["NUMBER", text[start:i]])
elif ch.isalpha():
start = i
while i < len(text) and text[i].isalpha():
i = i + 1
tokens.append(["NAME", text[start:i]])
elif ch in "+-*/":
tokens.append(["OP", ch])
i = i + 1
else:
tokens.append(["ERROR", ch])
i = i + 1
print(tokens)
# Cambia text por "price * 1,21" y vuelve a ejecutar.
flex escribe el analizador por ti
Escribir a mano el analizador de un lenguaje real es lento. Un generador léxico como flex lo escribe a partir de una lista de patrones.
En calc.l, cada línea une un patrón con lo que hay que hacer si encaja. [0-9]+ significa una o más cifras. Su acción convierte el texto en un número y entrega el token NUMBER al analizador sintáctico. Los Runs leen las reglas y reconstruyen la calculadora desde cero.
Entra en la carpeta del compilador
cd ~/compilerSe muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.
Las reglas que entregan un token
grep -n "return" calc.lSe muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.
¿Cuántas líneas tiene calc.l?
grep -c "" calc.lSe muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.
Borra todo lo que hizo make
make cleanSe muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.
Construye otra vez la calculadora
makeSe muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.
¿Cuánto mide lo que escribió flex?
wc -l lex.yy.cSe muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.
Caracteres que el analizador conoce y que no
Cada Run pasa una línea a la calculadora. Fíjate en qué mensajes vienen del analizador léxico y cuáles del sintáctico.
La última regla de calc.l, un punto solo, encaja con cualquier carácter que las demás no quisieron. Escribe unexpected character y no crea token. El analizador sintáctico ve después lo que queda.
Un decimal con coma
echo "3,5 + 1" | ./calcSe muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.
Un decimal con punto
echo "3.5 + 1" | ./calcSe muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.
Sin ningún espacio
echo "250+50" | ./calcSe muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.
Ceros a la izquierda
echo "007 + 1" | ./calcSe muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.
Un precio con su símbolo
echo "20 €" | ./calcSe muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.
Un error frecuente
Advertencia
Mucha gente cree que un analizador léxico crea un token por carácter, así que 007 serían tres números. El analizador toma la coincidencia más larga: sigue leyendo mientras el patrón encaje. Por eso 007 es un solo NUMBER, e iffy es un NAME, no la palabra if seguida de fy.
Para ir más lejos
Amplía tu analizador con textos entre comillas dobles. "oferta de verano" es un solo token STRING, sin las comillas. Una comilla que nunca se cierra es un token ERROR con el resto del texto.
Escribe primero tres pruebas tuyas. Después explica por qué un # dentro de las comillas no debe empezar un comentario.