01 septiembre 2008

Un compilador sencillo paso a paso (16 - Validando y ampliando)

Antes de avanzar más, llega el momento de probar la corrección de todo lo realizado, para evitar despistes que sean cada vez más difíciles de encontrar y más costosos de corregir.

La forma habitual es crear una serie de casos de prueba cuyo resultado sea conocido. Para nosotros se trataría de fuentes de prueba que permitan comprobar que se reconoce correctamente el lenguaje de origen y que se convierte correctamente al lenguaje de destino.

Deberíamos poner bajo prueba cada una de las posibilidades del lenguajes. Nosotros lo haremos de forma un poco menos exhaustiva (y, por tanto, menos fiable), con un único fuente que utilice todo lo que hemos implementado: constantes, variables, saltos, escritura de caracteres y de cadenas, cambio de modo de pantalla y de colores, comprobación de condiciones ciertas y falsas, repeticiones de varios tipos...

El fuente de prueba ha sido éste:


program ej16; { Ejemplo 16 de CpcPaChi }

(* Prueba todas las funcionalidades de
CpcPaChi disponibles hasta la version
0.16 *)

const
MODO = 1;

var
i, j, k: byte;

label
escribirSinCambiarColor;

procedure inicializa;
begin
cpcMode(MODO);
cpcInk(0,BLACK);
cpcInk(1,BRIGHTCYAN);
cpcInk(2,BRIGHTBLUE);
cpcInk(3,BLUE,CYAN);
{ paper(0);}
pen(2);
end;


begin
inicializa;

locate(2,2);
writeString('Prueba de FOR: 3 asteriscos ');
for i := 1 to 3 do
begin
pen(i);
writeChar('*');
end;
pen(1);
locate(2,3);
writeString('Prueba de IF: 3 es ');
i := 3;
if i=3 then writeString('=3 ');
if i>3 then writeString('>3 ');
if i>2 then writeString('>2 ');
if i>=3 then writeString('>=3 ');
if i>=4 then writeString('>=4 ');
if i<3 then writeString('<3 ');
if i<4 then writeString('<4 ');
if i<=3 then writeString('<=3 ');
if i<=2 then writeString('<=2 ');
if i<>2 then writeString('<>2 ');
if i<>3 then writeString('<>3 ');

pen(2);
locate(2,4);
writeString('Prueba de WHILE: 6+ ');
j := 5;
while j <= 10 do
begin
writeChar('+');
inc(j);
end;

pen(2);
locate(2,5);
writeString('Prueba de REPEAT: 5- ');
pen(3);
k := 10;
repeat
writeChar('-');
dec(k);
until k = 5;

locate(2,6);
pen(1);
writeString('For sin begin..end: ');
for j := 1 to 10 do
begin
writeChar('.');
end;

locate(2,7);
writeString('Prueba de GOTO');
goto escribirSinCambiarColor;
pen(2);
escribirSinCambiarColor:
writeString(' (Sin cambiar color)');

locate(2,8);
for i := 5 to 3 do
writeString('Esto no deberia escribirse');

end.


Con él, se ha podido descubrir varios fallos que tenía la versión 0.15, como:


  • Al añadir la comprobación basada en Tokens, había introducido un fallo en la declaración de constantes: debía terminar en coma, en vez de en punto y coma.

  • Todavía no estaba creado el código para reconocer correctamente las comparaciones , >=.

  • Cuando se insertaban elementos en la tabla de símbolos, no se comprobaba si ésta ya se había llenado.

  • En la segunda pasada, sólo se buscaba una coincidencia parcial de una cadena dentro de otra, lo que podría hacer que la etiqueta FIN_IF_10 se reemplazara por el valor de FIN_IF_1.



También ha servido para alguna pequeña mejora adicional como:


  • En el analizador sintáctico, la variable "orden" era global, lo que podría llegar a dar problemas cuando una función de análisis llame a otra distinta, como por ejemplo el caso de un IF o un FOR que contengan una sentencia compuesta.

  • Teníamos un "devolverLetra" que nos venía bien en el análisis sintáctico, para poder leer una letra más adelante (y así distinguir por ejemplo entre un : y un := ) pero no teníamos un "devolverToken", que es cómodo para situaciones como la comprobación de un ELSE.

  • La compatibilidad con Turbo Pascal 7 era limitada, por algún detalle como usar la función "upcase" para convertir toda una frase a mayúsculas, mientras que TP7 sólo permite usarla para un "char". Como primer acercamiento a una compatibilidad mejorada, he creado una función "mayusc" que convierte un string, usando "upcase" para cada letra.

  • El analizador léxico tenía una variable "depurando" que permitía ver más información de la habitual en pantalla; ahora esta posibilidad se encuentra también en el analizador sintático.



De paso, se podía haber hecho alguna otra mejora pendiente, como que el espacio para variables se reserve al final y no al principio, o que las cadenas de texto se almacenen al final del código, en vez de entre medias, para que sea más legible el código máquina resultante. Pero he preferido no hacerlo, para que todas las versiones tengan una cantidad de trabajo similar: una cantidad de trabajo mayor en un tiempo parecido puede suponer una gran cantidad de fallos... y no hay necesidad...


Como siempre, para más detalles, todo el código está en la página del proyecto en Google Code:

http://code.google.com/p/cpcpachi/