Sunteți pe pagina 1din 12

JLex y Java CUP

JLEX
Jlex no es ms que un generador de un analizador lxico parecido a LEX, el cual toma una
cadena como entrada una cadena de caracteres, y lo convierte en una secuencia de tokens.

CUP
Cup es un generador de analizadores sintcticos LALR en Java el cual recibe de entrada un
archivo con la estructura de la gramtica y su salida es un parser escrito en Java listo para
usarse.
Decid dividir el tutorial en varias secciones para hacer ms fcil el aprendizaje de estas
herramientas.

Estructura Archivo JLex


Estructura del Archivo Jlex
Un archivo de entrada Jlex, es un archivo plano con la siguiente estructura:

codigo del usuario


%%
Directivas Jlex
%%
Reglas para las Expresiones Regulares

Cdigo del Usuario


Es la parte del archivo de entrada donde se coloca el codigo java que deseamos usar en la clase
que ser generada, esto quiere decir que Jlex copiar directamente el codigo a la clase
generada, aqui deben ir los importes a otras librerias. TODO ESTO ANTES DE LOS PRIMEROS (%
%).

Directivas JLex
En esta seccon irn las directivas, o especificaciones para que opere JLEX, para obtener la salida
deseada.

Reglas para las Expresiones Regulares


En esta seccion del archivo Jlex, es donde se definen las reglas para obtener los tokens de la
cadena que se esta leyendo.
Con un ejemplo explicare mejor cada una de estas secciones.
Para el ejemplo escrib un programa que reconoce las siguientes palabras reservadas:
int, string, if, then, else, for, while
Reconoce identificadores, y enteros.
El codigo para hacer Jlex para genera el analizador lexico del ejemplo es el siguiente:

/*AQUI PUEDEN IR LOS IMPORTS */


%%
%{
/*CODIGO USUARIO*/
/*Pequea funcion para imprimir en pantalla*/

public void imprime(String foo){


System.out.println(foo)
}
%}
/*DIRECTIVAS JLEX*/
%class Yylex
%public
%full
%char
%line

%cup
%eofval{
System.out.println("FINARCHIVO");
%eofval}
entero=[0-9]
Id=[a-zA-Z][a-zA-Z0-9]*
%%
/* MANEJO DE LAS PALABRAS RESERVADAS*/
"while"

{imprime("while");

}
"int"

{imprime("int");}

"if" {imprime("if");}
"then" {imprime("then");}
"for"{imprime("for");}
/*expresion regular para un entero, tomando el conjunto definiddo
anteriormente como entero*/
/*un entero 1 o mas veces*/
({entero})+

{imprime("entero"+}

{Id} {imprime("Identificador");}
/*con la siguiente linesa ignoramos los espacios en blanco*/
(" ") {System.out.println("espacio/*con esta ignoramos los saltos de
linea, tabulaciones,*/
[\t\r\n\f] {}
/*error lexico:*/
. {System.out.println("error
}/*error lexico:*/. {System.out.println("error
El cdigo escrito dentro de los corchetes es el el cdigo que queremos que se ejecute cada vez
que el scanner encuentra los tokens a su izquierda. Este cdigo queda sin modificar a la hora de
generar el archivo de salida java

Estructura de Archivo Cup


A continuacin detallar como se estructura un archivo de entrada para Cup.

Bsicamente un archivo para Cup tiene la siguiente estructura:

< imports java >


< codigo del usuario para el parser>
<codigo del usuario para las acciones de la gramatica>
< Declaracion de Variables para la gramatica>
<Gramatica>
Imports: En esta seccin creo que no tengo que ampliar mucho desde que programamos Java
sabemos como son los imports de librerias.
Cdigo del Usuario para el Parser: Como el cdigo Java es generado por la herramienta es
muy difcil modificar lo en el archivo de salida. As que aqu podemos declarar mtodos y
variables que pensamos usar en la clase resultante. Si se declaran variables o
mtodos pblicos en esta seccin estos podran ser accedidos por otras clases.
Se declara:

parser code {: /* Codigo del parser*/:}


Cdigo del Usuario para las Acciones de la Gramtica: Como nuestro propsito es el de
generar un Compilador con estas herramientas o un interprete, necesitamos generar una salida
ya sea esta errores semnticos, sintcticos o traduccin a un cdigo equivalente, para esto
tenemos que hacer uso de traducciones dirigidas por sintaxis.
Sera muy engorroso programar largas funciones en cada accin del gramtica as que estas las
podemos declarar en esta seccin y solo mandarlas a llamar en cada accin gramatical.
Se declara de la siguiente manera:

action code {:/*Codigo para las acciones*/:}


Declaracin de Variables para la Gramtica : En esta seccin toca declarar las variables que
se utilizaran en la gramtica, estas variables pueden ser de dos tipos:

Variables Terminales < terminal>

Variables No Terminales <non terminal>

Las variables terminales sern todos los smbolos terminales de la gramtica y las variables NoTerminales sern todas las variables que representaran producciones.
La sintaxis para la declaracin es la siguiente:
<tipo de variable> < tipo de dato > < id de la variable >
Donde <tipo de variable > puede ser Terminal o No terminal
<tipo de dato> puede ser cualquier tipo de dato primitivo de Java o uno creado por nosotros
mismos. Si se no se especifica el tipo de dato Cup lo trabajar como un tipo de dato Symbol.
<id de la variable > aqu se especifica el id de la variable, se puede usar una lista de
identificadores separadas por coma si deseamos variables del mismo tipo.
Gramtica: En esta seccin del archivo es donde escribiremos nuestra gramatica. La gramatica
tiene la siguiente sintaxis :

<non terminal > ::= < terminales o No terminales >

Como un no terminal puede tener mas de un lado derecho en Cup se utiliza el simbolo |

<non terminal > ::= < terminales o No terminales >


|<terminales o No terminales>

Como es esperado se pueden escribir muchas producciones.


Ejemplo de un archivo cup para una Expresion Booleana:

/*Por el momento dejaremos el action code y parser code vacios esto se


explicara mas a detalle en otra seccion del tutorial*/
action code{::}
parser code{::}

/*Declaracion de variables no terminales*/


non terminal COND, OREXP,ANDEXP,IGEXP,CMP,SIMBOLOSCOMPARAR,TIPO_DATO;
/*DECLARACION DE VARIABLES TERMINALES */
terminal or_,and_,igual_igual,no_igual,mayor, menor,
mayor_igual,menor_igual,
open_par,close_par,id,numero,true,false;
Start with COND; // start with sirve para indicarle al parser con que
produccion empezar
COND::=OREXP;
OREXP::=OREXP or_ ANDEXP
|ANDEXP;

ANDEXP::=ANDEXP and_ IGEXP


|IGEXP;

IGEXP::= IGEXP igual_igual CMP


|IGEXP no_igual CMP
|CMP;

SIMBOLOS_COMPARAR::=mayor
|menor
|mayor_igual
|menor_igual;

CMP::= CMP SIMBOLOS_COMPARAR TIPO_DATO


|TIPO_DATO
|open_par COND close_par ;
TIPO_DATO::= id
|numero
|true
|false;

Integracin Jlex con Cup

Ya que sabemos como hacer archivos de entrada para Jlex y Cup ahora es hora de hacer que
funcionen en conjunto. Para hacer de mas ilustrativo el ejemplo usaremos una clase externa al
scanner y al parser que nos servir para almacenar informacin de cada token que se esta
leyendo. Llamaremos a esta clase token

class token(){
int posicionX;
int posicionY;
String valor;
public token(String val,int x,int y){
this.valor=val;
this.posicionX=x;
this.posicionY=y;
}
public int getX(){return this.posicionX;}
public int getY(){ return this.posicionY;}
public String getValor(){return this.valor;}
}
Seguiremos con el ejemplo de la expresin condicional , para esto debemos escribir el archivo
jlex para que reconozca las palabras reservadas terminales del lenguaje. El archivo quedara
de la siguiente forma:

import java_cup.runtime.Symbol;
%%
%{
public void imprime(String str){
System.out.println(str+"-"+yychar+"-"+yylinec
%public
%char
%line
%ignorecase
%cup
%full
%type java_cup.runtime.Symbol

%implements java_cup.runtime.Scanner
%eofval{
System.out.println("FINARCHIVO");
return null;
%eofval}
letra=[a-zA-Z]
entero=[0-9]
id=[a-zA-Z][A-Za-z0-9]*
%%
"(" {imprime("Abre Parentesis");
return new Symbol(csym.open_par,new token(yytext(),yychar,yyline));
}
")" {imprime("Cierra Parentesis");
return new Symbol(csym.close_par,new
token(yytext(),yychar,yyline));
"true" {
imprime("true");
return new Symbol(csym.true_,new token(yytext(),yychar,yyline));
}
"false" {
imprime("false");
return new Symbol(csym.false_,new
token(yytext(),yychar,yyline));
}
"<=" {imprime("menor igual");
return new Symbol(csym.menor_igual,new
token(yytext(),yychar,yyline));
}
">=" {imprime("mayor igual");
return new Symbol(csym.mayor_igual,new
token(yytext(),yychar,yyline));
}
"||" {imprime("or");

return new Symbol(csym.or_,new token(yytext(),yychar,yyline));


}
"&&" {imprime("and");
return new Symbol(csym.and_,new token(yytext(),yychar,yyline));
}
"==" {imprime("igual_igual");
return new Symbol(csym.igual_igual,new
token(yytext(),yychar,yyline));
}
"!=" {imprime("no igual");
return new Symbol(csym.no_igual,new
token(yytext(),yychar,yyline));
}
({id})+("_")*({id})* {imprime("id");
return new Symbol(csym.id,new token(yytext(),yychar,yyline));
}
{entero}+ {imprime("entero");
return new Symbol(csym.entero,new
token(yytext(),yychar,yyline));
}
[\t\r\f]

{}

[\n] {yychar=0;}
" " {}
. {imprime("error: "+yytext());
}
Explicar el fragmento de cdigo que se utiliz al lado derecho:

")" {imprime("Cierra Parentesis");


return new Symbol(csym.close_par,new
token(yytext(),yychar,yyline));}
Se llama la funcin imprime que se defini al inicio del archivo. Ahora bien, el scanner es
construido de tal manera que dentro de el existe una funcion con una sentencia de control donde
se decide que tipo de token se esta leyendo y que valor retornar. All reside el hecho de escribir

el return. Como podemos ver se retorna un tipo de dato Symbol, este en su constructor recibe
dos parmetros :

El primero que es un entero que es declarado en la clase sym generada por cup
(Explicar mas adelante)

El segundo que recibe es de tipo Object, esto nos facilita poder enviar cualquier tipo de
dato que deseemos., en este caso fue un tipo token, el que hemos definido al inicio de
esta seccin
.Una vez terminado nuestro archivo jlex es hora de escribir el archivo cup:

action code{:
public void ImprimeValor(String str){
System.out.println("elr del token"+str) ;
}
:}
parser code{:
public void syntax_error(Symbol st){
token t=(token)st.value;
report_error("Error Sintactico:"+ t.getValue()+"- "+t.getX()
+"-"+t.getY(),null);
:}

/*Declaracion de variables no terminales*/


non terminal token COND,
OREXP,ANDEXP,IGEXP,CMP,SIMBOLOSCOMPARAR,TIPO_DATO;
/*DECLARACION DE VARIABLES TERMINALES */
terminal token or_,and_,igual_igual,no_igual,mayor, menor,
mayor_igual,menor_igual,
open_par,close_par,id,numero,true,false;
Start with COND; // start with sirve para indicarle al parser con que
produccion empezar
COND::=OREXP;
OREXP::=OREXP or_ ANDEXP
|ANDEXP;

ANDEXP::=ANDEXP and_ IGEXP


|IGEXP;

IGEXP::= IGEXP igual_igual CMP


|IGEXP no_igual CMP
|CMP;

SIMBOLOS_COMPARAR::=mayor:m{:RESULT=m:}
|menor:m{:RESULT=m:}
|mayor_igual:m{:RESULT=m:}
|menor_igual:m{:RESULT=m:};

CMP::= CMP:c SIMBOLOS_COMPARAR:sc TIPO_DATO:t{:


String val1=c.getValor();
String val2=t.getValor();
if(sc.getValor().equals(">")){
ImprimeValor(val1+"mayor"+val2);
}
if(sc.getValor().equals("<")){
ImprimeValor(val1+"menor"+val2);
}
if(sc.getValor().equals("<=")){
ImprimeValor(val1+"menor igual"+val2);
}
if(sc.getValor().equals(">=")){
ImprimeValor(val1+">="+val2);
}
:}
|TIPO_DATO:T{:RESULT=T;:}
|open_par COND:c close_par{:RESULT=c;:} ;
TIPO_DATO::= id:i{:RESULT=i; :}
|numero:n{:RESULT=n;:}

|true:t {:RESULT=t;:}
|false:f{:RESULT=t;:};
Llego la hora de explicar cada parte de el archivo cup:

En la seccin action code como expliqu anteriormente se definen las funciones que que
utilizaran cuando se este recorriendo la gramtica, en este caso defin la funcin ImprimeValor
que recibe de parmetro una cadena, lo nico que hace es imprimir el valor de la cadena que
recibe de parmetro.

En la seccin parser code se encuentran los mtodos propios del parser, aqui hice un
override de la funcin syntax_error. Esta funcin nos permite ejecutar una accin cuando el
parser encuentra un error sintctico.

En la declaracin de terminales y no terminales se definen como tipo token para poder


manejar usarlos de una forma mas cmoda en las acciones de la gramtica.

Ahora vamos con las acciones en la gramatica, Cup permite agregar acciones como las
que podemos ver en el ejemplo anterior estas pueden ir en cualquier lugar del lado derecho de
la produccin en este tipo de herramientas se sugiere ponerlas al final para evitar
ambigedades ya que Cup toma la produccin como un smbolo las en la gramtica y al estar
en el medio pueden haber errores de reduccin o de movimiento del parser. La sintaxis para
para las acciones es: {: /*acciones*/ :} donde acciones puede ser cualquier sentencia
de cdigo.

La variable RESULT es usada por CUP para devolver el valor al padre de


la produccin, bsicamente devuelve el valor asignado al no terminal del lado derecho. Este
valor debe ser el mismo tipo de dato que el no terminal obviamente.
En el ejemplo se imprimirn los operadores relacionales en forma de texto junto con los valores
de cada miembro de la expresin.

S-ar putea să vă placă și