tutorial jlex cup
TRANSCRIPT
-
5/20/2018 Tutorial Jlex Cup
1/12
Tutorial Jlex Y Java Cup
Tutorial Jlex y Java Cuphttp://openfecks.wordpress.com/
por JosuOrtega
http://openfecks.wordpress.com/
http://openfecks.wordpress.com/http://openfecks.wordpress.com/http://openfecks.wordpress.com/http://openfecks.wordpress.com/ -
5/20/2018 Tutorial Jlex Cup
2/12
Tutorial Jlex Y Java Cup
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 sintcticosLALR 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.
Deciddividir el tutorial en varias secciones para hacer ms fcil
el aprendizaje de estas herramientas.
INDEX
Estructura de un Archivo Jlex
Estructura de un archivo Cup
Integracin Jlex con Cup
Compilacion y Ejecucion
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 sergenerada, esto quiere decir
que Jlex copiardirectamente el codigo a la clase generada, aqui
deben ir los importes a otras librerias. TODO ESTO ANTES DE LOS
PRIMEROS (%%).
http://openfecks.wordpress.com/
http://openfecks.wordpress.com/jlex-y-cup/http://en.wikipedia.org/wiki/Lex_programming_toolhttp://en.wikipedia.org/wiki/LALR_parserhttp://openfecks.wordpress.com/jlex-y-cup/plantilla-archivo-jlex/http://openfecks.wordpress.com/jlex-y-cup/estructura-de-archivo-cup/http://openfecks.wordpress.com/jlex-y-cup/integracion-jlex-con-cup/http://openfecks.wordpress.com/jlex-y-cup/integracion-jlex-con-cup/http://openfecks.wordpress.com/jlex-y-cup/integracion-jlex-con-cup/http://openfecks.wordpress.com/jlex-y-cup/compilacion-y-ejecucion/http://openfecks.wordpress.com/jlex-y-cup/plantilla-archivo-jlex/http://openfecks.wordpress.com/http://en.wikipedia.org/wiki/Lex_programming_toolhttp://en.wikipedia.org/wiki/LALR_parserhttp://openfecks.wordpress.com/jlex-y-cup/plantilla-archivo-jlex/http://openfecks.wordpress.com/jlex-y-cup/estructura-de-archivo-cup/http://openfecks.wordpress.com/jlex-y-cup/integracion-jlex-con-cup/http://openfecks.wordpress.com/jlex-y-cup/compilacion-y-ejecucion/http://openfecks.wordpress.com/jlex-y-cup/plantilla-archivo-jlex/http://openfecks.wordpress.com/http://openfecks.wordpress.com/jlex-y-cup/ -
5/20/2018 Tutorial Jlex Cup
3/12
Tutorial Jlex Y Java Cup
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 escribun 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("FIN DEL ARCHIVO");
%eofval}
entero=[0-9]
Id=[a-zA-Z][a-zA-Z0-9]*
%%
/* MANEJO DE LAS PALABRAS RESERVADAS*/
"while" {imprime("while");
}
http://openfecks.wordpress.com/
http://openfecks.wordpress.com/http://openfecks.wordpress.com/ -
5/20/2018 Tutorial Jlex Cup
4/12
Tutorial Jlex Y Java Cup
"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 detallarcomo se estructura un archivo de entradapara Cup.
Bsicamente un archivo para Cup tiene la siguiente estructura:
< imports java >
< codigo del usuario para el parser>
< Declaracion de Variables para la 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. Asque aqupodemos 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
http://openfecks.wordpress.com/
http://openfecks.wordpress.com/jlex-y-cup/estructura-de-archivo-cup/http://openfecks.wordpress.com/http://openfecks.wordpress.com/jlex-y-cup/estructura-de-archivo-cup/http://openfecks.wordpress.com/ -
5/20/2018 Tutorial Jlex Cup
5/12
Tutorial Jlex Y Java Cup
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 asque 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
Las variables terminales sern todos los smbolos terminales de la
gramtica y las variables No-Terminales sern todas
las variables que representaran producciones.
La sintaxis para la declaracin es la siguiente:
< tipo de dato > < id de la variable >
Donde puede ser Terminal o No terminal 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 trabajarcomo un tipo de dato Symbol.
aquse 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 escribiremosnuestra gramatica. La gramatica tiene la siguiente sintaxis :
::= < terminales o No terminales > ;
Como un no terminal puede tener mas de un lado derecho en Cup se
utiliza el simbolo |
::= < 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
http://openfecks.wordpress.com/
http://openfecks.wordpress.com/http://openfecks.wordpress.com/ -
5/20/2018 Tutorial Jlex Cup
6/12
Tutorial Jlex Y Java Cup
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 servirpara almacenar informacin de cada token
que se esta leyendo. Llamaremos a esta clase token
class token(){
int posicionX;
int posicionY;
String valor;
http://openfecks.wordpress.com/
http://openfecks.wordpress.com/jlex-y-cup/integracion-jlex-con-cup/http://openfecks.wordpress.com/jlex-y-cup/integracion-jlex-con-cup/http://openfecks.wordpress.com/jlex-y-cup/integracion-jlex-con-cup/http://openfecks.wordpress.com/http://openfecks.wordpress.com/jlex-y-cup/integracion-jlex-con-cup/http://openfecks.wordpress.com/ -
5/20/2018 Tutorial Jlex Cup
7/12
Tutorial Jlex Y Java Cup
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+"-"+yyline);
}
%}
%class lexc
%public
%char%line
%ignorecase
%cup
%full
%type java_cup.runtime.Symbol
%implements java_cup.runtime.Scanner
%eofval{
System.out.println("FIN DEL ARCHIVO");
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));
http://openfecks.wordpress.com/
http://openfecks.wordpress.com/http://openfecks.wordpress.com/ -
5/20/2018 Tutorial Jlex Cup
8/12
Tutorial Jlex Y Java Cup
"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("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_,newtoken(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;}
" " {}
http://openfecks.wordpress.com/
http://openfecks.wordpress.com/http://openfecks.wordpress.com/ -
5/20/2018 Tutorial Jlex Cup
9/12
Tutorial Jlex Y Java Cup
. {imprime("error: "+yytext());
}
Explicarel fragmento de cdigo que se utilizal lado derecho:
")" {imprime("Cierra Parentesis");
return new Symbol(csym.close_par,new
token(yytext(),yychar,yyline));}
Se llama la funcin imprime que se definial 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. Allreside
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 (Explicarmas 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("el valor 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
http://openfecks.wordpress.com/
http://openfecks.wordpress.com/http://openfecks.wordpress.com/ -
5/20/2018 Tutorial Jlex Cup
10/12
Tutorial Jlex Y Java Cup
|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+">="+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 expliquanteriormente se definen
las funciones que que utilizaran cuando se este recorriendo
la gramtica, en este caso definla 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.
http://openfecks.wordpress.com/
http://openfecks.wordpress.com/http://openfecks.wordpress.com/ -
5/20/2018 Tutorial Jlex Cup
11/12
Tutorial Jlex Y Java Cup
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.
Compilacin y Ejecucin
Es la hora del paso final compilar y ejecutar nuestro scanner y
parser.
Compilacion Jlex:
Para compilar el Jlex solo basta con escribir en consola:
$ jlex
Y si todo se genera bien creara nuestra salida en cdigo Java.
Compilacion Cup:
Para compilar Cup se presentan mas opciones como especificar la
clase que lleva las constantes de los simbolos, el paquete de la
clase, el nombre que deseamos para la salida del parser entre
otras. Para nuestro ejemplo modificaremos el nombre de la clase con
los simbolos y el nombre que recibira el parser:
$ cup -parser -symbols
Para ver las demas opciones:
$ man cup
Cdigo de ejecucin:
http://openfecks.wordpress.com/
http://openfecks.wordpress.com/jlex-y-cup/compilacion-y-ejecucion/http://openfecks.wordpress.com/jlex-y-cup/compilacion-y-ejecucion/http://openfecks.wordpress.com/jlex-y-cup/compilacion-y-ejecucion/http://openfecks.wordpress.com/jlex-y-cup/compilacion-y-ejecucion/http://openfecks.wordpress.com/jlex-y-cup/compilacion-y-ejecucion/http://openfecks.wordpress.com/http://openfecks.wordpress.com/jlex-y-cup/compilacion-y-ejecucion/http://openfecks.wordpress.com/ -
5/20/2018 Tutorial Jlex Cup
12/12
Tutorial Jlex Y Java Cup
Supongamos que nuestra salida de jlex tiene el nombre: scanner.java
y nuestra salida de Cup Parser.java. Para poder hacer que el
scanner y el parser funcionen conjuntamente las siguientes lineas
de cdigo son tiles:
En el caso que leamos un archivo de entrada:
File file=new File("url del archivo");
try{
FileReader fr=new FileReader(file);
scanner lex=new scanner(fr);
Parser miParser=new Parser(lex);
miParser.parse();
}catch(Exception e){
System.out.println(e);
}
}
La clase del scanner recibe en el constructor un Stream de
caracteres en este caso lo que se leydel archivo.
scanner lex=new scanner(fr);
Y para integrarlo al parser solo debemos enviarlo al constructor
del nuevo objeto parser creado
Parser miParser=new Parser(lex);
Para que nuestro objeto empiece a ejecutar el parser llamamos a su
metodo parse();
miParser.parse();
Si nuestra entrada no es un archivo si no un texto ledo de un
TextArea o algo parecido podemos usar la clase StringReader de Java
en lugar del FileReader.
http://openfecks.wordpress.com/
http://openfecks.wordpress.com/http://openfecks.wordpress.com/