23 abril 2008

Un compilador sencillo paso a paso (8 - Mejorando el analizador léxico)

(Tras una -larga- pausa por motivos familiares, retomemos el compilador...)

Nuestro analizador léxico es capaz de leer un cierto símbolo del código fuente, de obtener un identificador o un número entero. Es la base de lo que necesitamos, pero aún tiene carencias. Dos de ellas son especialmente graves:


  • Es habitual que los identificadores permitan usar letras y números (por ejemplo, "variable01" debería ser un nombre correcto para una variable), con la restricción de que el primer símbolo sea una letra, no un número. Nuestro analizador por ahora sólo permite que estén formados por letras.


  • No permite usar comentarios en el código fuente.



El primer cambio, en la forma de definir los identificadores, es sencillo, apenas una modificación pequeña en la función "obtenerIdentificador":


letra := obtenerLetra;
lexema := lexema + letra;
letra := obtenerLetra;
(* Puede seguir con letras o numeros *)
while upcase(letra) in ['A'..'Z','0'..'9'] do
begin
lexema := lexema + letra;
letra := obtenerLetra;
end;


El segundo cambio tampoco es mucho más difícil: si un comentario empieza en llave debemos saltar todo hasta encontrar una llave cerrada (este cambio será parte de "saltarBlancos"), así:


(* Salto comentarios entre llaves *)
if (lineaDeEntrada[posicionLineaActual+1] = '{') then
begin
while (lineaDeEntrada[posicionLineaActual+1] <> '}') do
obtenerLetra;
obtenerLetra; (* Salto la llave de cierre *)
end;


Y de forma similar, saltaríamos los comentarios entre (* y *):


(* Salto comentarios entre parentesis-asterisco *)
if (lineaDeEntrada[posicionLineaActual+1] = '(')
and (lineaDeEntrada[posicionLineaActual+2] = '*')then
begin
(* Salto el parentesis y asterisco de apertura *)
obtenerLetra;
obtenerLetra;
while (lineaDeEntrada[posicionLineaActual+1] <> '*')
and (lineaDeEntrada[posicionLineaActual+2] <> ')') do
begin
obtenerLetra;
end;
obtenerLetra; (* Salto el asterisco y parentesis de cierre *)
obtenerLetra;



Por supuesto, quedan muchas cosas por hacer. Nuestro analizador empieza a reconocer correctamente los programas que no tengan fallos, pero es muy torpe en el manejo de errores. Es habitual crear un tipo de datos llamado "Token", de forma que de cada elemento que leamos (ya sea identificador, número entero, símbolo matemático, etc) sepamos otros datos, como la posición del fichero en que se encuentra (para que los mensajes de error sean más correctos), el tipo de elemento del que se trata, su valor, etc. Lo básico que necesitaríamos sería algo como:


type token =
record
tipoToken: integer;
valor: string;
posx,posy: integer;
end;


Aun así, esto son mejoras sin las cuales nuestro compilador puede funcionar "relativamente bien" al nivel que nos interesa por ahora, es decir, si nosotros somos cuidadosos tecleando los fuentes, porque los mensajes de error serán muy poco aclaradores.

En la próxima entrega mejoraremos nuestra tabla de símbolos y empezaremos a usar variables, cuyo valor pueda realmente ser modificado durante el funcionamiento del programa.

Y posiblemente algún día completaremos el analizador léxico para que sea más fiable... posiblemente... pero, en principio, esas mejoras las haré directamente en la página del proyecto en Google Code, no aquí.