1. CIS-IXB
UNIVERSIDAD
NACIONAL
DE LOJA
´Area de la Energ´ıa las Industrias y los Recursos Naturales No Renovables
Carrera de Ingenier´ıa en Sistemas
Desarrollo de un compilador
mediante JFlex y Cup
Proyecto de Compiladores
Noveno B
Autor:
• Rodriguez Salinas Claudio Israel
Docente: Ing. Henry-Paz.
Loja-Ecuador
2015
1
3. A. Introducci´on
La oportunidad de poder crear un analizador l´exico y un analizador sint´actico, es
una gran experiencia para mi persona como estudiante universitario, el comprender como
funciona, como se complementan entre ellos y como ha sido una herramienta para el
desarrollo de lenguajes de programaci´on, permite tener una idea m´as global de como
funcionan los compiladores todo tipo de sistema similar que se utiliza en la actualidad.
en el presente documento trataremos los temas de la creaci´on de los analizadores l´exico
y sint´actico, tratando de ser muy detallados en la descripci´on del c´odigo y en algunos casos
del funcionamiento, as´ı como dejaremos un enlace al c´odigo fuente del proyecto realizado
en java.
1 . Problema a Resolver
El problema a resolver es el siguiente:
Se recibir´a una expresi´on matem´atica, por ejemplo: 3+5-a=14, esto sera interpretado y
nos devolver´a los tokens de INT, SUMA, MENOS, ID, IGUAL e INT. Los tokens nos
permitir´an trabajar y poder usarlos con nuestro archivo CUP.
Una vez el c´odigo est´e generado, al ingresar una expresi´on matem´atica, la que menciona-
mos anteriormente, obtendremos el siguiente resultado: 3 MAS 5 MENOS a ES IGUAL
14.
Nuestro analizador sint´actico nos permitir´a revisar que las expresiones matem´aticas est´en
escritas o declaradas correctamente, por ejemplo que los n´umeros o las variables est´en
antes y despu´es de los operadores matem´aticos, y despu´es de un punto y coma se tomar´a
lo siguiente como una nueva expresi´on matem´atica.
B. Analizador L´exico
El analizador l´exico, tambi´en conocido como analizador morfol´ogico (scanner, en ingl´es)
se encarga de leer una secuencia de caracteres del programa fuente, car´acter a car´acter,
y los agrupa para formar unidades con significado propio. Estas unidades son los compo-
nentes l´exicos (tokens, en ingl´es).
Nuestro Analizador l´exico, el cual es un archivo flex es el siguiente:
El archivo empieza declarando el paquete en el que va a estar nuestro archivo l´exico, el
paquete se encuentra dentro de nuestro proyecto en java, y despu´es de compilado nos dar´a
un archivo java con el que trabajaremos nuestro programa.
package ejemplocup;
Realizamos las importaciones de los paquetes que necesitamos: en nuestro caso el paquete
de cup, para la integraci´on con el analizador sint´actico, y el paquete de reader.
import java_cup.runtime.*;
import java.io.Reader;
3
4. Cambiamos el nombre de la clase del analizador a AnalizadorLexico.
%class AnalizadorLexico
Activamos el contador de lineas, variable yyline y Activamos el contador de columna, va-
riable yycolumn, Activamos la compatibilidad con Java CUP para analizadores sint´acti-
cos(parser)
%line
%column
%cup
1 . Declaraciones
El c´odigo entre %{ y %} sera copiado ´ıntegramente en el analizador generado. Gene-
ramos un Symbol para guardar el tipo de token encontrado, y seguidamente generamos
un Symbol para el tipo de token encontrado junto con su valor.
%{
private Symbol symbol(int type) {
return new Symbol(type, yyline, yycolumn);
}
private Symbol symbol(int type, Object value) {
return new Symbol(type, yyline, yycolumn, value);
}
%}
Macro declaraciones
Declaramos expresiones regulares que despu´es usaremos en las reglas l´exicas.
Salto Declaramos lo que es un salto de linea, dependiendo del sistema operativo.
Espacio es un espacio en blanco, tabulador, salto de linea o avance de pagina, normal-
mente son ignorados.
L es la declaraci´on de los caracteres alfab´eticos que va a aceptar, aqu´ı declaramos que
recibir´a tanto may´usculas, como min´usculas, as´ı como gui´on bajo.
D es la declaraci´on de lo que aceptar´a e identificar´a como d´ıgitos o n´umeros, aqu´ı recibir´a
el cero, as´ı como los n´umeros del 0 al 9 o del 1 al 9 en todas sus combinaciones.
Salto = r|n|rn
Espacio = {Salto} | [ tf]
L = [a-zA-Z_]
D = 0 | [1-9][0-9]*
Secci´on de reglas l´exicas
Esta secci´on contiene expresiones regulares y acciones.
Las acciones son c´odigo en Java que se ejecutara cuando se encuentre una entrada valida
para la expresi´on regular correspondiente.
4
5. 2 . YYINITIAL
Es el estado inicial del analizador l´exico al escanear. Las expresiones regulares solo
ser´an comparadas si se encuentra en ese estado inicial. Es decir, cada vez que se encuen-
tra una coincidencia el scanner vuelve al estado inicial. Por lo cual se ignoran estados
intermedios.
; Regresa que el token PYC declarado en la clase sym fue encontrado.
= Regresa que el token IGUAL declarado en la clase sym fue encontrado.
+ Regresa que el token SUMA declarado en la clase sym fue encontrado.
* Regresa que el token MULT declarado en la clase sym fue encontrado.
- Regresa que el token RESTA declarado en la clase sym fue encontrado.
/ Regresa que el token DIV declarado en la clase sym fue encontrado.
{D} Si se encuentra un entero, se regresa un token String que representa un entero
y el valor que se obtuvo de la cadena yytext al convertirla a String. yytext es el
token encontrado.
{L}{L}|{D} Si se encuentra un caracter, seguido de m´as caracteres o n´umeros, se
regresa un token String que representa una cadena y el valor que se obtuvo de la
cadena yytext, al convertirla a String. yytext es el token encontrado.
{Espacio} No hace nada si encuentra el espacio en blanco.
<YYINITIAL> {
";" {return symbol(sym.PYC);}
"=" {return symbol(sym.IGUAL);}
"+" {return symbol(sym.SUMA);}
"*" {return symbol(sym.MULT);}
"-" {return symbol(sym.RESTA);}
"/" {return symbol(sym.DIV);}
{D} {return symbol(sym.INT, new String(yytext()));}
{L}({L}|{D})* {return symbol(sym.ID, new String(yytext()));}
{Espacio} { /* ignora el espacio */ }
}
Si el token contenido en la entrada no coincide con ninguna regla entonces se marca un
token ilegal
[ˆ] { throw new Error("Caracter ilegal <"+yytext()+">"); }
Todo nuestro c´odigo completo queda de la siguiente manera:
5
6. /* --------------------------Codigo de Usuario----------------------- */
package ejemplocup;
import java_cup.runtime.*;
import java.io.Reader;
%% //inicio de opciones
/* ------ Seccion de opciones y declaraciones de JFlex -------------- */
%class AnalizadorLexico
%line
%column
%cup
%{
private Symbol symbol(int type) {
return new Symbol(type, yyline, yycolumn);
}
private Symbol symbol(int type, Object value) {
return new Symbol(type, yyline, yycolumn, value);
}
%}
Salto = r|n|rn
Espacio = {Salto} | [ tf]
L = [a-zA-Z_]
D = 0 | [1-9][0-9]*
%% //fin de opciones
/* -------------------- Seccion de reglas lexicas ------------------ */
<YYINITIAL> {
";" {return symbol(sym.PYC);}
"=" {return symbol(sym.IGUAL);}
"+" {return symbol(sym.SUMA);}
"*" {return symbol(sym.MULT);}
"-" {return symbol(sym.RESTA);}
"/" {return symbol(sym.DIV);}
6
7. {D} {return symbol(sym.INT, new String(yytext()));}
{L}({L}|{D})* {return symbol(sym.ID, new String(yytext()));}
{Espacio} { /* ignora el espacio */ }
}
[ˆ] { throw new Error("Caracter ilegal <"+yytext()+">"); }
El c´odigo java para compilar el archivo flex es el siguiente:
Recordando que debemos tener la librer´ıa jflex
public static void main(String[] args) {
String path = "Direcci´on del archivo .flex";
File file=new File(path);
JFlex.Main.generate(file);
}
C. Analizador Sint´actico.
El analizador sint´actico nos permitir´a comprobar que las expresiones utilizadas, in-
gresadas o le´ıdas por el c´odigo generado de nuestro archivo flex, sea y tenga la sintaxis
adecuada, esto quiere decir, que los elementos alfanum´ericos se encuentren en el orden
adecuado, as´ı como los s´ımbolos u operadores matem´aticos.
El c´odigo de nuestro archivo CUP queda de la siguiente manera:
El archivo empieza declarando el paquete en el que va a estar nuestro archivo Sint´actico,
el paquete se encuentra dentro de nuestro proyecto en java, y despu´es de compilado nos
dar´a un archivo java con el que trabajaremos nuestro programa.
package ejemplocup;
Realizamos las importaciones de los paquetes que necesitamos: en nuestro caso el paquete
de cup,para poder utilizar sus m´etodos, y el paquete de reader, que nos permitir´a trabajar
con archivos
import java_cup.runtime.*;
import java.io.FileReader;
1 . C´odigo del parser
Este c´odigo se copia ´ıntegramente a la clase final. Agregamos el manejo de errores.
En este caso utilizaremos el m´etodo info, el cual es un m´etodo de CUP que nos regresa
un numeral y un n´umero, que representan al s´ımbolo encontrado, o que en otras palabras
el s´ımbolo que se encuentra cuyo orden no es correcto. Por ejemplo si la escritura correcta
es n´umero signo + y n´umero (3+4), y si en la expresi´on que se declara no se coloca
el primer n´umero, solamente el signo + y el siguiente n´umero, este nos regresara un #
seguido de un n´umero, el cual es una referencia de la clase Sym.java creada al momento
de compilar nuestro archivo CUP (c´odigo que colocaremos despu´es), la referencia sera del
token encontrado, en este caso el toquen del s´ımbolo + , lo cual ya nos da la idea de que
7
8. no se encontraron los tokens en el orden correcto.
En el c´odigo colocado a continuaci´on se encuentran los m´etodos para el manejo de errores,
el primer m´etodo es para un error normal, y el siguiente para un error fatal, lo que significa
que la ejecuci´on finalizar´ıa.
Este c´odigo es conocido como el parser y todo el c´odigo que se coloque dentro se copiara
´ıntegramente en nuestra clase en java cuando se compile el archivo CUP.
2 . Identificaci´on de Errores
La siguiente secci´on de c´odigo se maneja un error encontrado, al interpretar lo que nos
devuelve el m´etodo info de nuestro analizador sint´actico. Aqu´ı comparamos lo recibido en
el m´etodo info.toString() y las constantes Symbol generadas por el archivo CUP dentro
de la clase Sym. todo esto va dentro del m´etodo de reporte de errores que se encuentra
m´as abajo.
if("#3".equals(info.toString())){
message=("Expresi´on de SUMA repetida o SIN NUMERO O
VARIABLE AL INICIO");
}
A continuaci´on el c´odigo completo de como queda el parser, como mencionamos ante-
riormente el manejo de errores, junto con las condiciones para identificar el tipo de error
sint´actico.
parser code {:
public void report_error(String message, Object info) {
StringBuilder m = new StringBuilder("Error");
if (info instanceof java_cup.runtime.Symbol) {
java_cup.runtime.Symbol s = ((java_cup.runtime.Symbol) info);
if (s.left >= 0) {
m.append(" in line "+(s.left+1));
if (s.right >= 0)
m.append(", column "+(s.right+1));
}
}
if("#3".equals(info.toString())){
message=("Expresi´on de SUMA repetida o SIN NUMERO O
VARIABLE AL INICIO");
}
if("#2".equals(info.toString())){
message=("Expresi´on SIN NUMERO O VARIABLE AL FINAL");
}
if("#8".equals(info.toString())){
message=("Error en la VARIABLE");
}
8
9. if("#5".equals(info.toString())){
message=("Expresi´on de MULTIPLICACI´ON repetida o
SIN NUMERO O VARIABLE AL INICIO");
}
if("#4".equals(info.toString())){
message=("Expresi´on de RESTA repetida o SIN NUMERO O
VARIABLE AL INICIO");
}
if("#6".equals(info.toString())){
message=("Expresi´on de DIVISI´ON repetida o SIN NUMERO O
VARIABLE AL INICIO");
}
if("#7".equals(info.toString())){
message=("Expresi´on de IGUALDAD repetida o SIN NUMERO O
VARIABLE AL INICIO");
}
m.append(" : "+message);
System.err.println(m);
}
public void report_fatal_error(String message, Object info) {
//report_error(message, info);
//System.exit(1);
}
:};
3 . Declaraci´on de s´ımbolos terminales y no terminales
Terminales son los tokens obtenidos por el analizador l´exico.
Terminales que no tienen un valor son listados primero, los terminales que tienen un valor
como los enteros son listados en la segunda o dem´as lineas. En este caso trabajaremos
con los tokens antes mencionados, y declararemos como String a los n´umeros y a los
caracteres, ya que para nuestro ejercicio no necesitamos hacer c´alculos num´ericos, entonces
manejaremos los dos tokens como cadenas.
terminal PYC, SUMA, RESTA, MULT, DIV, IGUAL;
terminal String ID, INT;
No terminales usados en la secci´on gramatical.
Primero se lista los no terminales que tienen un valor Object y despu´es se lista los no
terminales que tienen un entero o String. Un Object se refiere a que no tienen tipo,
pudiendo ser entero o String.
non terminal Object expr_list, expr_part;
non terminal String variable, expr, termino;
9
10. 4 . Secci´on de la gram´atica
Esta es la gram´atica de nuestro analizador, como est´an definidas los t´erminos y las
variables, as´ı como que es una expresi´on y como debe de estar escrita.
expr_list ::= expr_list expr_part
| expr_part
expr_part ::= expr PYC
expr ::= expr SUMA factor
| expr RESTA factor
| expr MULT factor
| expr DIV factor
| expr IGUAL factor
| variable
variable ::= ID
| termino
termino ::= INT
Expr list es la ra´ız de la gram´atica. Una expr list puede ser una expr list seguida de una
expr part, o puede ser una expr part. Un terminal o no terminal se define termino ::=
termino1 termino2 ... termino N.; donde termino puede ser terminal o no terminal, solo
se permite un s´ımbolo a la izquierda. La — sirve para indicar que es una producci´on u
otra.
Debemos pasar de s´ımbolos no terminales a s´ımbolos terminales. Una gram´atica que no
termina en s´ımbolos terminales se dice que no reduce, y por lo tanto nunca se finaliza su
procesado.
expr_list ::= expr_list expr_part
|
expr_part
;
Expr part se compone de una expr seguida de PYC, entre {: :} se coloca el c´odigo en
java para las acciones a tomar al cumplirse esta condici´on, en nuestro caso solo har´a un
salto de linea y continuara leyendo el archivo donde estar´an las expresiones matem´aticas,
para continuar el an´alisis.
expr_part ::= expr:e PYC
{:
System.out.println("");
:}
;
10
11. Expr Puede ser una expresi´on que inicia por numero entero SUMA expr, de esa forma
se realiza la interpretaci´on de una suma, as´ı como de RESTA MULT, DIV e IGUAL.
Tambi´en puede ser una variable, con esto se devuelve la variable en una cadena o String.
En RESULT se almacena el valor de las acciones o en nuestro caso del String, y se pasa
al siguiente nivel de la gram´atica.
expr ::= expr:e SUMA variable:v
{:
if(e!=null){
System.out.print(e+" MAS "+v);
}else{System.out.print(" MAS "+v);}
:}
|
expr:e RESTA variable:v
{:
if(e!=null){
System.out.print(e+" MENOS "+v);
}else{
System.out.print(" MENOS "+v);}
:}
|
expr:e MULT variable:v
{:
if(e!=null){
System.out.print(e+" MULTIPLICADO POR "+v);
}else{
System.out.print(" MULTIPLICADO POR "+v);}
:}
|
expr:e DIV variable:v
{:
if(e!=null){
System.out.print(e+" DIVIDIDO PARA "+v);
}else{
System.out.print(" DIVIDIDO PARA "+v);}
:}
|
expr:e IGUAL variable:v
{:
if(e!=null){
System.out.print(e+" ES IGUAL A "+v);
}else{
System.out.print(" ES IGUAL A "+v);}
:}
|
variable:n
11
12. {:
RESULT = n;
:}
;
Variable Esta puede ser una cadena ID, la cual es de tipo String o un termino, en ambos
casos la variable se guarda o almacena en RESULT.
variable ::= ID:i
{:
RESULT = i;
:}
|
termino:t
{:
RESULT = t;
:}
;
Termino Finalmente tenemos la definici´on en la gram´atica de lo que es un termino, es
lo m´as b´asico de nuestro nivel de gram´atica, este es un entero definido como INT, que
es procesado como una cadena, como indicamos antes esto nos permitir´a trabajar de una
manera m´as sencilla.
termino ::= INT:e
{:
RESULT = e;
:}
;
El c´odigo para compilar nuestro archivo CUP es el siguiente:
Pondremos el path del archivo CUP, donde dice NombreArchivo se especificara el nombre
del archivo java generado a partir del archivo CUP.
String archSintactico = "Path del archivo .cup";
String[] asintactico = {"-parser", "NombreArchivo", archSintactico};
java_cup.Main.main(asintactico);
Despu´es de todo esto las lineas de c´odigo para que se ejecuten los archivos de nuestro
compilador son:
AnalizadorPalabras asin = new AnalizadorPalabras(
new AnalizadorLexico( new FileReader("test.txt")));
Object result = asin.parse().value;
Estas lineas ejecutaran el analizador l´exico, el sint´actico y podremos ver los resultados.
12
13. D. Aut´omata
El aut´omata de nuestro proyecto es muy sencillo, pero nos permite entender como
funciona, y es el siguiente:
En el dibujo podemos observar los s´ımbolos aceptados as´ı como lo que nos retorna, recal-
cando el funcionamiento de los tokens INT y ID que pueden contener varios caracteres.
Todo el proyecto realizado en c´odigo java puede encontrarse en el siguiente link https:
//github.com/israelrodri/CompiladorJflexCup
13