Si hacemos que el generador de código sea un módulo independiente (lo elegante sería que fuera un "objeto"; para nosotros será simplemente unas cuantas funciones recopiladas en una misma "unit"), conseguiremos varias ventajas:
- Más legibilidad de lo que podríamos considerar el "código fuente principal".
- Más facilidad para reemplazar esa "unit" por otra cuando queramos generar código para otra máquina de destino distinta.
- En un compilador "real" se suele generar primero un código intermedio, parecido al ensamblador (en ocasiones puede ser algo más "sofisticado", como un árbol sintáctico, que a nosotros ahora nos pilla bastante lejos).
- A este código se le suele echar un primer vistazo (automático) para intentar optimizar las cosas que la generación de código ha hecho de forma mejorable.
- A continuación se genera el código máquina.
- Finalmente, se suele intentar optimizar también ese código máquina.
Claramente, nosotros no hacemos nada de eso (al menos por ahora): nos limitamos a generar código máquina en una sola pasada.
Conociendo nuestras limitaciones, buscamos que nuestro "código principal" (lo que dentro de poco llamaremos el analizador sintáctico) sea más simple y legible que antes, así:
program cpcPaChi;
(* Chi - Un compilador de Pascal chiquitito para CPC
Por Nacho Cabanes - Version 0.03
Versiones hasta la fecha:
Num. Fecha Cambios
---------------------------------------------------
0.04 27-Mar-2008 Primera version con generador de codigo
independiente (en la unit "uPaChiG")
0.03 25-Mar-2008 Creado un analizador lexico independiente
(en la unit "uPaChiL")
0.02 23-Mar-2008 Admite CLS, LOCATE, WRITECHAR,
que lee desde fichero. Solo permite
una orden en cada linea, con formato fijo
0.01 21-Mar-2008 Preliminar: solo acepta CLS por teclado
y genera el codigo correspondiente
*)
uses
upachil, (* Analizador lexico *)
upachig; (* Generador de codigo *)
var
nombreOrigen, nombreDestino: string;
orden: string;
(* Analiza las opciones que teclee el usuario. Por ahora,
solo el nombre del fichero a compilar *)
procedure analizarOpciones;
begin
if paramcount >= 1 then
nombreOrigen := paramstr(1)
else
begin
writeln('No se ha indicado el nombre del fichero');
halt;
end;
abrirFicheroEntrada( nombreOrigen );
abrirFicheroDestino( 'salida.bas' );
end;
(* Genera el codigo de destino: codigo maquina de Amstrad CPC
dentro de un cargador en Basic *)
procedure analizarUnaOrden;
var
x, y: byte;
letra: char;
codError: integer;
begin
orden := upcase(obtenerIdentificador);
if orden = 'CLS' then
begin
genCLS;
end
else
if orden = 'WRITECHAR' then
begin
leerSimbolo('(');
leerSimbolo('''');
letra := obtenerLetra;
leerSimbolo('''');
leerSimbolo(')');
genWRITECHAR(letra);
end
else
if orden = 'LOCATE' then
begin
leerSimbolo('(');
val(obtenerEntero,x,codError);
leerSimbolo(',');
val(obtenerEntero,y,codError);
leerSimbolo(')');
genLOCATE(x,y);
end
else
if orden = '' then
begin
(* orden vacia *)
end
else
begin
writeln('Error: orden no reconocida. Se esperaba: CLS, LOCATE o WRITECHAR');
(*halt;*)
end;
end;
(* Cuerpo del programa *)
begin
writeln('Compilando...');
analizarOpciones;
while not finFicheroEntrada do
begin
analizarUnaOrden;
end;
generarCodigoFinal;
cerrarFicheroEntrada;
cerrarFicheroDestino;
end.
Esto lo conseguiríamos con una analizador sintáctico como éste:
unit upachig;
(* cpcPaChi - Un compilador de Pascal chiquitito para CPC
Por Nacho Cabanes
uPachiG - unidad "CodeGen" (generador de codigo)
Versiones hasta la fecha:
Num. Fecha Cambios
---------------------------------------------------
0.04 27-Mar-2008 Primera version del generador de codigo:
genera codigo para CLS, LOCATE, WRITECHAR
y el codigo final del cargador BASIC
para un codigo maquina que estara dentro
de ordenes DATA.
*)
interface
function abrirFicheroDestino(nombre: string): boolean;
procedure cerrarFicheroDestino;
procedure genCLS;
procedure genLOCATE(x,y: byte);
procedure genWRITECHAR(letra: char);
procedure generarCodigoFinal;
implementation
const
lineaActual: integer = 10;
longitudTotal: integer = 0;
var
ficheroDestino: text;
(* Abre el fichero de entrada. Devuelve FALSE si no se
ha podido *)
function abrirFicheroDestino(nombre: string): boolean;
begin
assign( ficheroDestino, nombre );
{$I-}
rewrite( ficheroDestino );
{$I+}
if ioResult <> 0 then
begin
abrirFicheroDestino := false;
end;
end;
(* Generar codigo para CLS *)
procedure genCLS;
begin
writeln( ficheroDestino, lineaActual,' DATA CD,6C,BB: '' CALL &BB6C - CLS' );
lineaActual := lineaActual + 10;
longitudTotal := longitudTotal + 3;
end;
(* Generar codigo para LOCATE *)
procedure genLOCATE(x,y: byte);
begin
writeln( ficheroDestino, lineaActual,' DATA 3E,',
hexStr(x,2), ': '' LD A, ',x );
lineaActual := lineaActual + 10;
writeln( ficheroDestino, lineaActual,' DATA CD,6F,BB: '' CALL &BB6F - CURSOR COLUMN' );
lineaActual := lineaActual + 10;
writeln( ficheroDestino, lineaActual,' DATA 3E,',
hexStr(y,2), ': '' LD A, ',y );
lineaActual := lineaActual + 10;
writeln( ficheroDestino, lineaActual,' DATA CD,72,BB: '' CALL &BB72 - CURSOR ROW' );
lineaActual := lineaActual + 10;
longitudTotal := longitudTotal + 10;
end;
(* Generar codigo para WRITECHAR *)
procedure genWRITECHAR(letra: char);
begin
writeln( ficheroDestino, lineaActual,' DATA 3E,',
hexStr(ord(letra),2), ': '' LD A, "',letra, '"' );
lineaActual := lineaActual + 10;
writeln( ficheroDestino, lineaActual,' DATA CD,5A,BB: '' CALL &BB5A - WRITECHAR' );
lineaActual := lineaActual + 10;
longitudTotal := longitudTotal + 5;
end;
(* Cerrar fichero de destino (al final de la generacion) *)
procedure cerrarFicheroDestino;
begin
close(ficheroDestino);
end;
(* Genera el codigo de destino final: cargador Basic *)
procedure generarCodigoFinal;
begin
append( ficheroDestino );
writeln( ficheroDestino, lineaActual,' DATA C9: '' RET' );
writeln( ficheroDestino, lineaActual+10,' longitud = ',longitudTotal );
writeln( ficheroDestino, lineaActual+20,' MEMORY 39999' );
writeln( ficheroDestino, lineaActual+30,' FOR n=40000 TO 40000+longitud' );
writeln( ficheroDestino, lineaActual+40,' READ a$:POKE n,VAL("&"+a$)' );
writeln( ficheroDestino, lineaActual+50,' NEXT' );
writeln( ficheroDestino, lineaActual+60,' CALL 40000' );
end;
begin
end.
Por cierto... leer código fuente en un Blog no especialmente cómodo. Si quieres leer esta información con sintaxis coloreada en colores, o echar un vistazo más detenido al fuente, o descargarlo a partir de un fichero ZIP, puedes mirar la página del proyecto en Google Code:
http://code.google.com/p/cpcpachi/
No hay comentarios:
Publicar un comentario