Una nómina que cumple la gramática
Un script de nóminas termina con show salary + bonus. La gramática lo acepta. Todos los tokens son correctos.
Pero bonus solo existía dentro de un bloque que ya se cerró. El analizador sintáctico comprueba la forma de una frase, no si tiene sentido. De eso se ocupa la parte siguiente del compilador.
Al final de esta lección
Nota
Puedo encontrar los errores que un analizador sintáctico no ve.
Palabras clave
| Palabra | Qué significa | Ejemplo |
|---|---|---|
| Análisis semántico | comprobar que un programa tiene sentido | |
| Declaración | la línea que crea un nombre | int step |
| Ámbito | la parte del programa donde existe un nombre | |
| Tabla de símbolos | la lista que guarda el compilador de los nombres vivos | |
| Tipo | la clase de valor que guarda algo | int |
| Aviso | un mensaje que no detiene la compilación | warning |
Las celdas que rellenan los estudiantes quedan en blanco en esta vista previa.
El compilador lleva una lista de nombres
Mientras recorre el árbol, el compilador lleva una tabla de símbolos. Cada { abre un ámbito nuevo encima. Cada declaración añade un nombre al ámbito de arriba. Cada } tira el ámbito de arriba.
Para comprobar un nombre, busca en el ámbito de arriba, luego en el de debajo, y así hasta el final. Si no lo encuentra, el nombre no está declarado.
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 líneas que declaran un int
grep -n "int " scope.cSe muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.
Ejemplo: la tabla de símbolos de scope.c
| Línea | Qué pasa | Nombres vivos |
|---|---|---|
| 4 y 5 | main declara unused y total | unused, total |
| 6 | El bucle abre un ámbito y declara i | unused, total | i |
| 7 | El cuerpo del bucle declara step | unused, total | i | step |
| 9 | El bucle termina y sus ámbitos se tiran | unused, total |
| 10 | printf usa step: no está | unused, total |
Las celdas que rellenan los estudiantes quedan en blanco en esta vista previa.
Compila sin comprobaciones extra
gcc -c scope.cSe muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.
Ahora pide los avisos habituales
gcc -Wall -c scope.cSe muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.
Cada llamada tiene que encajar
El compilador comprueba también que cada llamada encaje con su declaración. square recibe un int y devuelve un int.
Los Runs hacen dos copias de hello.c con sed. En args.c, la llamada pasa dos argumentos. En noret.c, square acaba con un return sin valor. Las dos copias tienen la forma correcta para la gramática.
Una copia con un argumento de más
sed 's/square(7)/square(7, 2)/' hello.c > args.cSe muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.
Compila la primera copia
gcc -c args.cSe muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.
Una copia que no devuelve nada
sed 's/return n \* n/return/' hello.c > noret.cSe muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.
Compila la segunda copia
gcc -c noret.cSe muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.
Un error para la compilación, un aviso no
La última copia, fmt.c, pide a printf un texto con %s y le da un número. gcc sabe cómo lee printf su formato, así que lo nota.
Esta vez da un aviso. La compilación sigue, y ls muestra que fmt.o existe. Tras un error, en cambio, no se escribe ningún archivo objeto.
Una copia con el formato equivocado
sed 's/is %d/is %s/' hello.c > fmt.cSe muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.
Compila la tercera copia
gcc -c fmt.cSe muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.
¿Qué archivos objeto existen?
ls fmt.o noret.oSe 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 aviso se puede ignorar porque el programa se compila igual. Un aviso es el compilador diciendo que algo es legal pero casi seguro está mal. Si ejecutas fmt.c, printf trata el 49 como la dirección de un texto. Lee cada aviso como un fallo hasta saber por qué es seguro.
Python comprueba mientras ejecuta
C comprueba nombres y tipos antes de ejecutar: eso es comprobación estática. Python los comprueba cuando llega a la línea, así que el fallo aparece en ese momento.
Los dos Runs arrancan un programa. Ninguno de los dos fallos se descubre hasta que Python llega a él.
Un nombre que Python no conoce
python3 -c "print(price)"Se muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.
Un valor del tipo equivocado
python3 -c "print(len(5))"Se muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.
Para ir más lejos
Añade a tu comprobador un tercer problema, "sombra". Sale cuando un var interior usa el nombre de un ámbito exterior, como a en una de las pruebas.
No es un error: el programa sigue teniendo sentido. Explica por qué conviene avisar de todos modos, con un ejemplo de nómina donde la sombra confunde.