You are on page 1of 11

ESCUELA UNIVERSITARIA DE INFORMTICA

Compiladores

PRCTICA OBLIGATORIA

Contenido de este documento

OBJETIVOS ............................................................................................................................................ 1
DESCRIPCIN DEL LENGUAJE A IMPLEMENTAR ...................................................................................... 1
EL COMPILADOR .................................................................................................................................... 1
Programa reconocedor de componentes lxicos.............................................................................. 1
Compilador del lenguaje .................................................................................................................. 4
PRUEBAS DEL COMPILADOR................................................................................................................... 8
NORMAS DE ENTREGA DE LA PRCTICA ................................................................................................. 8
APNDICE 1. LAS HERRAMIENTAS JFLEX Y BYACC/JAVA ...................................................................... 8
JFlex ................................................................................................................................................. 8
Yacc (Byacc/Java) ............................................................................................................................ 9
APNDICE 2. SOFTWARE PARA REALIZAR LA PRCTICA ...................................................................... 10
APNDICE 3. DOCUMENTACIN SOBRE EL LENGUAJE JAVA................................................................. 10

Objetivos
Diseo de una gramtica para un lenguaje propio de programacin sencillo (que
necesariamente ser diferente del lenguaje que mostramos en los ejemplos que se detallan a lo
largo de la descripcin de la prctica)
Implementacin de un programa que reconozca el lxico de un lenguaje, utilizando la
herramienta jflex
Implementacin de un compilador para el lenguaje utilizando las herramientas jflex(lex) y
byacc/JAVA, que son las versiones de lex y yacc que generan Java respectivamente.

Descripcin del lenguaje a implementar


El lenguaje de programacin ser definido por cada alumno y permitir
escribir mensajes y resultados por pantalla,
definir y asignar variables de tipo entero y de tipo carcter,
realizar operaciones condicionales IF,
realizar expresiones aritmtico-lgicas (=, <, >, <>, <=, >=, +, *,-, /)
realizacin de bucles tipo WHILE y REPEAT,
definir procedimientos y funciones con variables locales y con entrada de datos valor,
hacer comentarios,
definir, vectores de enteros y caracteres. Se deber permitir la inicializacin del vector
completo en una asignacin y el acceso a cada componente.
Para ello, cada alumno deber elegir el conjunto de palabras y signos reservados para el lenguaje (por
ejemplo, := para asignacin, etc..) y la estructura y sintaxis de cada sentencia. Se podr tomar como
ejemplo el lenguaje definido en el apndice del libro de la asignatura (pgina 764).
Para la gestin de las palabras reservadas, variables (sus tipos y mbitos)
procedimientos se disear una tabla de smbolos.

y nombres de

El compilador
Implementar un programa que, utilizando jflex y byacc/Java, compile un programa escrito en el
lenguaje diseado. Si algn alumno desea realizar esta practica en C con las herramientas de Unix,
tendr que tener en cuenta las opciones y directivas especficas para ellas. El plan de trabajo se
describe en los apartados posteriores.

Programa reconocedor de componentes lxicos


Definir una clase en jflex que reciba como entrada un programa escrito en el nuevo lenguaje y para
cada sentencia, o token, saque un mensaje indicando qu es.
Por ejemplo, para el siguiente programa

Prctica de Compiladores. Curso 2003/2004

programa suma;
variables a,b: vector(5) de entero;
i,res:entero;
# un procedimiento auxiliar
proc inicializar;
a <-- {1,1,1,1,1};
b <-- {1,1,0,1,0} ;
fin;
# programa principal
inicio
i <-- 0;
mq i<5 hacer
res <- res + a[i] * b[i];
i <-- i + 1;
fmq;
fin;

escribir res;
Figura 1. Ejemplo de programa

La salida debera ser :

c:\>

java cp . Yylex suma.cal

Es una palabra clave: <programa>


Es un identificador: <suma>
Es un punto y coma: <;>
Es una palabra clave: <variables>
Es un identificador: <a>
Es una coma: <,>
Es un identificador: <b>
Son dos puntos: <:>
Es una palabra clave: <vector>
Es un parntesis: <(>
Es un entero: <5>
Es un parntesis: <)>
Es una palabra clave: <de>
Es una palabra clave: <entero>
Es un punto y coma: <;>
Es un identificador: <i>
Es una coma: <,>
Es un identificador: <res>
Son dos puntos: <:>
Es una palabra clave: <entero>
Es un punto y coma: <;>
Es un comentario: <# un procedimiento auxiliar>
Es una palabra clave: <proc>
Es un identificador: <inicializar>
Es un punto y coma: <;>
Es un identificador: <a>
Es una asignacin: <<-->
Es una llave: <{>
Es un entero: <1>
Es una coma: <,>
Es un entero: <1>
Es una coma: <,>
Es un entero: <1>
Es una coma: <,>
Es un entero: <1>
Es una coma: <,>
Es un entero: <1>
Es una llave: <}>
Es un punto y coma: <;>
Es un identificador: <b>
Es una asignacin: <<-->
Es una llave: <{>
Es un entero: <1>
Es una coma: <,>
Es un entero: <1>
Es una coma: <,>
Es un entero: <0>
Es una coma: <,>
Es un entero: <1>
Es una coma: <,>

Prctica de Compiladores. Curso 2003/2004

Es
Es
Es
Es
Es
Es
Es
Es
Es
Es
Es
Es
Es
Es
Es
Es
Es
Es
Es
Es
Es
Es
Es
Es
Es
Es
Es
Es
Es
Es
Es
Es
Es
Es
Es
Es
Es
Es
Es
Es
Es
Es
Es
Es

un entero: <0>
una llave: <}>
un punto y coma: <;>
una palabra clave: <fin>
un punto y coma: <;>
un comentario: <# programa principal>
una palabra clave: <inicio>
un identificador: <i>
una asignacin: <<-->
un entero: <0>
un punto y coma: <;>
una palabra clave: <mq>
un identificador: <i>
un operador relacional: <<>
un entero: <5>
una palabra clave: <hacer>
un identificador: <res>
un operador relacional: <<>
un operador arimtico: <->
un identificador: <res>
un operador arimtico: <+>
un identificador: <a>
un corchete: <[>
un identificador: <i>
un corchete: <]>
un operador arimtico: <*>
un identificador: <b>
un corchete: <[>
un identificador: <i>
un corchete: <]>
un punto y coma: <;>
un identificador: <i>
una asignacin: <<-->
un identificador: <i>
un operador arimtico: <+>
un entero: <1>
un punto y coma: <;>
una palabra clave: <fmq>
un punto y coma: <;>
una palabra clave: <escribir>
un identificador: <res>
un punto y coma: <;>
una palabra clave: <fin>
un punto y coma: <;>

Figura 2. Listado del resultado de reconoce.l


La estructura del fuente jflex reconoce.l se muestra en la figura 3. En l se pone como ejemplo, el
reconocimiento de un identificador que est formado por una letra minscula seguido de cero o ms
letras minsculas o nmeros.

%%
/* Para utilizar el analizador lxico por separado */
%standalone
IDENT=[a-zA-Z][a-zA-Z0-9_]*
%%
{IDENT}
{
}

System.out.println("Es un identificador: <"+ yytext() + ">");

Figura 3. Esquema del programa RECONOCE.L.

Para compilar este programa y ejecutarlo, se procede en los siguientes pasos. Cuando se escribe un
programa en jflex, lo que hay que hacer es compilarlo, utilizando para ello el comando jflex. Como
resultado genera un fichero llamado Yylex.java. Este fichero se compila utilizando el compilador de

Prctica de Compiladores. Curso 2003/2004

Java y como consecuencia se tiene una clase Yylex.class. As para el programa reconoce pedido en
esta prctica se procedera del siguiente modo :
c:\> jflex reconoce.l
c:\> javac Yylex.java

(llama a jflex y le da como entra el fichero reconoce.l, como


resultado devuelve el fichero Yylex.java)
(llama al compilador de Java y como resultado genera un
fichero interpretable Yylex.class)

c:\> java cp . Yylex suma.cal

(ejecuta el programa y le da como entrada un fichero con un


programa en calculator llamado ope1.cal)

Compilador del lenguaje


Modificar el cdigo correspondiente a jflex del apartado anterior, de forma que en vez de sacar un
mensaje devuelva el token correspondiente cuando reconozca un token del lenguaje (con la sentencia
return nombre-de-token o un valor (numero o variable) cuando reconozca un identificador o
un valor numrico (ver en el siguiente apartado como se hace esto en jflex). Incluirlo en un fichero
llamado scanner.l. En este caso el fichero jflex no tiene la declaracin %standalone. Por lo
tanto, la estructura del fichero es la siguiente (la figura 4):

%%
%ignorecase
/* directiva para utilizar jflex con byacc */
%byaccj
/* declaracin de cdigo */
%{
private Parser yyparser;

%}

public Yylex(java.io.Reader r, Parser yyparser) {


this(r);
this.yyparser = yyparser;
}

/* macros */
IDENT =[a-zA-Z][a-zA-Z0-9_]*
NUMERO=[0-9]+
%%
PROGRAMA

System.err.println("Es un <PROGRAMA>");
return Parser.PROGRAMA;
}
{
System.err.println("Es un <PUNTOYCOMA>");
return Parser.PUNTOYCOMA;
}
{
yyparser.yylval = new ParserVal(yytext());
System.err.println("Es un identificador: <" +
return Parser.IDENT;
}

";"

{IDENT}

{NUMERO}

}
[\ \t\r\n]+

try {
yyparser.yylval = new ParserVal(Integer.parseInt(yytext()));
return Parser.NUMERO;
} catch (java.lang.NumberFormatException e) {
return Parser.ILEGAL;
}
{
}

yytext() + ">" );

{}

Prctica de Compiladores. Curso 2003/2004

Figura 4. Estructura del programa scanner.l.

Implementar en un fichero que llamaremos compilador.y el analizador sintctico y semntico


haciendo uso de la herramienta byacc. En el cdigo en byacc, para referenciar automticamente al
analizador lxico, basta con definir un miembro de la clase Yylex que es compilado conjuntamente con
las dems clases que forman el compilador. Luego se han de incluir las definiciones necesarias para
byacc y las reglas de la gramtica, y a la derecha de cada una de las partes derechas de la regla (y antes
del ; o | ) se van aadiendo las acciones semnticas correspondientes, en las que se va generando y
comprobando la tabla de smbolos, generando cdigo o emitiendo los correspondientes mensajes de
error, si hay errores de sintaxis o errores semnticos.
%{
%}

import java.io.*;

/* declaracion de tokens */
%token PROGRAMA PUNTOYCOMA
%token <sval> IDENT
/* aqui los otros tokens del lenguaje */
/* %type <cadena> sent_asig */
/* aqui al declaracion de las cadenas para las sentencias */
%%
/* codificacion de las producciones de la gramatica del lenguaje */
cabecera : PROGRAMA IDENT PUNTOYCOMA
{
System.out.println("class " + $2 + "{");
System.out.println($2 + "(){");
System.out.println();
System.out.println("}");
System.out.println("public static void main(String[] args) {");
System.out.println($2 + " v = new " + $2 + "();");
System.out.println("}");
System.out.println("}");
}

;
%%
/*.. aqu el cuerpo de los procedimientos para manejar la tabla de smbolos*/

/* definicin analizador lxico */


private Yylex lexer;
/* yylex(): utliza el analizador lxico */
private int yylex () {
int yyl_return = -1;
try {
yylval = new ParserVal(0);
yyl_return = lexer.yylex();
}
catch (IOException e) {
System.err.println("IO error :"+e);
}
return yyl_return;
}
/* ... resto de cdigo */
}
Figura 5. Modo de incluir el fichero scanner.l en el analizador sintctico y semntico

En el ejemplo se ha escrito el cdigo armazn para generar una cabecera de un programa. El cdigo
generado, resultado de ejecutar el compilador sera con un programa que nicamente consistiera de la
cabecera sera:

Prctica de Compiladores. Curso 2003/2004

class suma{
suma(){
}
public static void main(String[] args) {
suma v = new suma();
}
}
Figura 6. Cdigo generado por el compilador

Tanto para las variables como para los procedimientos es necesario definir una tabla de smbolos, en la
que se guardar el nombre de cada smbolo, distinguiendo de qu tipo es y el nombre que tiene.
Cuando se declara una nueva variable o procedimiento, hay que comprobar si ya est declarado antes
(en cuyo caso se emite un mensaje de error). Por otro lado, cuando se usa una variable o un
procedimiento hay que comprobar si existe una variable o procedimiento, respectivamente, con ese
nombre; caso de que no, se emite el correspondiente mensaje de error. Tanto la definicin de la tabla
de smbolos como las funciones y procedimientos necesarios para manejarla se implementaran en Java
en la zona de declaracin de cdigo de usuario, indicada en la figura 5, despus de incluir el cdigo
correspondiente a byacc.
La generacin de cdigo se har en un lenguaje Java, para la cul se explica como generar el cdigo de
un programa vaco. Para generar el cdigo, se asocia a las reglas que correspondan, una accin
semntica que simplemente escriba en el lenguaje elegido, en este caso Java, el trozo de cdigo que
realiza lo que indica la regla. Por ejemplo, si tenemos la siguiente regla
Cabecera PROGRAMA Nombre ;

que reconoce la cabecera de un programa la accin correspondiente es la mostrada en la Figura 5,


donde PROGRAMA, IDENT, y PUNTOCOMA son el nombre de tres tokens (la palabra reservada
programa, el identificador de nombre de programa y el punto y coma) que una vez reconocidos por
el analizador lxico, indican que la regla que define la cabecera es cierta, con lo cual se puede ejecutar
la accin semntica correspondiente que dice que se escriba la palabra PROGRAM, en contenido del
token IDENT (que se indica con $2 ya que ocupa la posicin 2 dentro de la parte derecha de la regla;
esto se explica ms detalladamente en el siguiente apartado), despus un punto y coma y luego se salta
una lnea; es decir, se genera cdigo en Java para describir la cabecera de un programa.
(Nos referimos al apndice 1, donde se explica cmo se compila cdigo en jflex y en byacc y cmo, a
continuacin, se genera un programa que hace de compilador del lenguaje)
El fichero compilador.y tendr la estructura, del listado de la figura 7.
El proceso de compilacin de un fichero en byacc es el siguiente:
c:\> yacc J compilador. y
(llama a byacc el ejecutable se llama yacc- y genera los
ficheros Parser.java y ParserVal.java. El flag J indica la generacin de Java.)
c:\> jflex scanner.l
(llama a jflex, compila el scanner. Genera Yylex.java. No
es necesario hacerlo cada vez que se recompila compilador.y.)
c:\> javac Parser.java ParserVal.java Yylex.java

(compila el cdigo generado por byacc y genera las clases

Parser.class, ParserVal.class y Yylex.class).


c:\> java cp . Parser suma.cal
(Compila el programa suma.cal. Saca

resultado por pantalla)

c:\> java cp . Parser suma.cal > suma.java

el

Prctica de Compiladores. Curso 2003/2004

c:\> javac suma.java


c:\> java cp . suma

%{
%}

(compila el programa suma.cal. Escribe el resultado en el


fichero suma.java)
(compila el fichero suma.java)
suma.java (ejecuta la clase suma)

import java.io.*;

/* declaracion de tokens */
%token PROGRAMA PUNTOYCOMA
%token <sval> IDENT
/* aqui los otros tokens del lenguaje */
/* %type <cadena> sent_asig */
/* aqui al declaracion de las cadenas para las sentencias */
%%
/* codificacion de las producciones de la gramatica del lenguaje */
cabecera : PROGRAMA IDENT PUNTOYCOMA
{
System.out.println("class " + $2 + "{");
System.out.println($2 + "(){");
System.out.println();
System.out.println("}");

System.out.println("public static void main(String[] args) {");


System.out.println($2 + " v = new " + $2 + "();");
System.out.println("}");
System.out.println("}");

%%
/*.. aqu el cuerpo de los procedimientos para manejar la tabla de smbolos*/
/* definicin analizador lxico */
private Yylex lexer;
/* yylex(): utliza el analizador lxico */
private int yylex () {
int yyl_return = -1;
try {
yylval = new ParserVal(0);
yyl_return = lexer.yylex();
}
catch (IOException e) {
System.err.println("IO error :"+e);
}
return yyl_return;
}
/* Mtodo para manejar errores */
public void yyerror (String error) {
System.err.println ("Error: " + error);
}
/* Constructor: inicializa el analizador lxico */
public Parser(Reader r) {
lexer = new Yylex(r, this);
}
/* main */
public static void main(String args[]) throws IOException {
Parser yyparser;
if ( args.length > 0 ) {
// parse a file
yyparser = new Parser(new FileReader(args[0]));
yyparser.yyparse();
} else {

Prctica de Compiladores. Curso 2003/2004

System.err.println("usage: java -cp . Parser <file input>");

}
Figura 7. Listado de un ejemplo del fichero compilador.y

Pruebas del compilador


Para probar el compilador habr que definir un conjunto de programas ejemplo escritos en el lenguaje
diseado, tres correctos, y seis incorrectos (con diferentes tipos de errores: sintcticos, semnticos,
lxicos).

Normas de entrega de la prctica


El alumno deber entregar con la practica por correo electrnico a la direccin
(carlos@lsi.uned.es) una memoria en la que se incluir el siguiente material :
Gramtica del lenguaje
Listado del fuente jflex, reconoce.l
Listado del compilador en byacc/Java; listado del fichero compilador.y y el fichero scanner.l
Listado de los programas de prueba a los que se someti el compilador y sus respectivas salidas,
entre los que se incluirn por lo menos, los indicados en el apartado anterior.
Clases correspondientes a compilador y el analizado lxico.
Clases correspondientes a reconoce.l
Programas compilados que se utilizaron de prueba y que estn correctamente programados
La fecha tope de entrega de la prctica es el da 14 de junio de 2004.

Apndice 1. Las herramientas JFlex y byacc/Java


En el libro de texto de la asignatura Compiladores. Principios, tcnicas y herramientas (Aho, A. ;
Sheti, R. ; Ullman, J.D.) se incluye una introduccin de las herramientas lex y yacc. Aqu vamos a
explicar las peculiaridades de las herramientas que elegidas, jflex y byacc/Java.

JFlex
En relacin a aspectos derivados de la programacin en JFlex, cabe destacar :
La estructura de un fichero JFLEX es:
Cdigo de usuario
%%
opciones y declaraciones
%%
reglas lxicas
Para devolver un token se hace con return nombre-de-token.
Cuando se reconoce un token NUMERO, no basta con que jflex devuelva que es un token
NUMERO sino que adems debe devolver el valor de ste. Esto se hace con la siguiente accin,
cuando se reconoce la expresin regular correspondiente a un numero:
{

try {
yyparser.yylval = Interger.parser(yytext());
return Parser.NUMERO;
} catch (java.lang.NumberFormatException e) {
return Parser.ILEGAL;
}

Prctica de Compiladores. Curso 2003/2004

Cuando se reconoce un token de tipo identificador, hay que devolver que es un token IDENT, y
el nombre de ste. Cuando se reconoce la expresin regular correspondiente a un
identificador, se tiene que ejecutar la siguiente accin:
{

yyparser.yylval = new ParserVal(yytext());


System.err.println("Es un identificador: <" +
return Parser.IDENT;

yytext() + ">" );

Los comentarios se tratan en el analizador lxico. Para ello se incluye el siguiente par
expresin-regular/accin donde salta todos los caracteres despus del carcter # y hasta que
llega a salto de lnea ; y como accin no realiza nada
"#"[^\n\r]*

{}

Para compilar cdigo en jflex y generar cdigo en Java o cualquier otro lenguaje:
Cuando se escribe un programa en jflex, lo que hay que hacer es compilarlo, utilizando para ello el
comando jflex <fichero.l>. Como resultado genera un fichero Yylex.java. Este fichero se compila
utilizando el compilador de Java y como consecuencia se tiene una clase. As para el programa
reconoce pedido en esta prctica se procedera del siguiente modo :
c:\> jflex reconoce. l
c:\> javac Yylex.java
c:\> java cp . Yylex

(llama a jflex y le da como entra el fichero reconoce.l, como


resultado devuelve el fichero Yylex.java)
(llama al compilador de Java y como resultado genera una clase
Yylex.class)
(ejecuta el programa y le da como entrada un fichero con
suma.cal
un programa en calculator llamado oper1.cal)

Yacc (Byacc/Java)
Con relacin a aspectos de programacin:
La estructura de un fuente byacc/Java es:
declaraciones
%%
acciones
%%
cdigo de usuario

Se puede emplear cualquier definicin en la parte de cdigo de usuario.


La clase ParserVal es la que se utiliza para acceder a los atributos semnticos. Se puede
ver su composicin sin ms que inspeccionar el fichero ParserVal.java.

Para compilar un fichero y generar cdigo en Java para el compilador :


Cuando se compila un fichero en byacc, a partir de las clusulas %token de generan las
correpondientes definiciones. El proceso de compilacin de un fichero en byacc es el siguiente:
(llama a byacc/Java, y genera los ficheros Parser.java y
ParserVal.java)
(llama a jflex, compila el scanner.)
ParserVal.java Yylex.java
(compila el cdigo generado por byacc
y genera las clases Parser.class, ParserVal.class y

c:\> yacc J compilador.y


c:\> jflex scanner.l
c:\> javac Parser.java

Yylex.class).
c:\> java cp . Parser suma.cal
(Compila

pantalla)

el programa

suma.cal.

Saca el resultado por

Prctica de Compiladores. Curso 2003/2004

10

c:\> java cp . Parser suma.cal > suma.java

(compila el programa suma.cal. Escribe el resultado en el fichero


suma.java)
La realizacin de la prctica es obligatoria.
Solo hay una convocatoria de prcticas, de forma que si no se aprueba la prctica de junio o no se
presenta, la prctica se considera suspensa.

Apndice 2. Software para realizar la prctica


Los archivos ejemplos de este anunciado y el software para realizar la prctica se encuentran
disponibles en el CD-ROM de la escuela.
http://garoe.lsi.uned.es/compiladores

Apndice 3. Mquina Virtual de Java y documentacin sobre el lenguaje


Java
Para realizar la prctica se requiere tener instalada una versin de la mquina virtual. Las herramientas
proporcionadas han sido probadas con la versin 1.4.0, aunque no tiene por qu dar problemas con
versiones inferiores. Se puede obtener una en el CD-ROM de la escuela, en el material de la asignatura
de Lenguajes de Programacin.
Se puede encontrar documentacin sobre el lenguaje Java en el CD-ROM, en el material asociado a la
asignatura de Lenguajes de Programacin.
Algunas pautas a tener en cuenta en la generacin de cdigo:
No es necesario especificar modificadores de acceso public, protected, private.
Todos los miembros pueden tener el modificador de acceso por defecto.
Todas las clases en Java que se deseen interpretar tienen que tener un mtodo main, cuyo
prototipo es public static void main(String args[]).
Una buena estrategia a seguir en la generacin de cdigo es generar las variables del
programa como variables miembro, y los procedimientos y funciones como mtodos.

You might also like