Analizador léxico, sintáctico y semántico con JFlex y CUP

La siguiente receta explica con un ejemplo como construir un analizador léxico con JFlex y un analizador sintáctico y semántico con CUP (obviamente para Java). Además, muestra el uso de producciones de error.

Requisitos

  • jflex - analizador léxico para Java
  • cup - analizador sintáctico/semántico LALR para Java

Construcción

El ejemplo consiste en un conversor de unidades métricas. Admitiría textos de entradas del siguiente tipo:

Entrada.txt

Km 1 mm;
dm 3 dm;
Hm 393 m;
Dm 54 cm, mm 3429 dm;
Km 23 Dm;
dm 322 cm, Hm 0923 mm;

y devuelve en consola:

1 Km  son 1000000 mm;
3 dm son 3 dm;
393 Hm son 39300 m;
...
 

Un archivo flex tiene la siguiente estructura (cada sección se separa mediante %%) :

  1. Código de usuario
  2. Opciones y declaraciones
  3. Reglas léxicas

Conversor.lex

//* ------------Sección codigo-usuario -------- */
import java_cup.runtime.*;
%%
/*- Sección de opciones y declaraciones -*/
/*-OPCIONES --*/
/* Nombre de la clase generada para el analizador lexico */
%class conversor
/* Indicar funcionamiento autonomo*/
%standalone
%8bit
/* Acceso a la columna y fila actual de analisis CUP */
%line
%column
/* Habilitar la compatibilidad con el interfaz CUP para el generador sintactico*/
%cup
/*-- DECLARACIONES --*/
%{/*Crear un nuevo objeto java_cup.runtime.Symbol con información sobre el token actual sin valor*/
 
  private Symbol symbol(int type){
    return new Symbol(type,yyline,yycolumn);
  }
/* Crear un nuevo objeto java_cup.runtime.Symbol con información sobre el token actual con valor*/
  private Symbol symbol(int type,Object value){
    return new Symbol(type,yyline,yycolumn,value);
  }
%}
/*-- MACRO DECLARACIONES --*/
LineTerminator = \r|\n|\r\n
WhiteSpace     = {LineTerminator} | [ \t\f]
//finConversion	= [,;]
num_int = [0-9]+
%%
/*-------- Sección de reglas lexicas ------ */
<YYINITIAL> {
//{finConversion}{ return symbol.FCONVERSION}
 
","	{ return symbol(sym.COMA);}
";"	{ return  symbol(sym.PUNTOCOMA);}
"mm"	{ return  symbol(sym.MM); }
"cm"	{ return  symbol(sym.CM); }
"dm"	{ return  symbol(sym.DCM); }
"m"	{ return  symbol(sym.M); }
"Dm"	{ return  symbol(sym.DCAM); }
"Hm"	{ return  symbol(sym.HM); }
"Km"	{ return  symbol(sym.KM); }
{num_int}	{ return symbol(sym.NUMBER, new Integer(yytext())); }
 
{WhiteSpace}	{ }
. {System.out.println("token ilegal <" + yytext()+ ">  linea: " + (yyline+1) + " columna: " + (yycolumn+1));}
}

En cuanto a un fichero CUP se pueden diferenciar cinco partes:

  1. Especificaciones de “package” e “imports”.
  2. Componentes de código de usuario.
  3. Lista de símbolos de la gramática(terminales­no terminales).
  4. Declaraciones de precedencia.
  5. Especificación de la gramática.

Conversor.cup

/*­­­­Sección de declaraciones package e imports­­­­­­­­­*/
import java_cup.runtime.*;
/*­­­­ Sección componentes de código de usuario ­­­­*/
parser code {:
    public void report_error(String message, Object info) {
       	StringBuffer m = new StringBuffer("Error");
		System.out.println("Mensaje: "+message);
		System.out.println("info: "+info.toString());
		if(info instanceof java_cup.runtime.Symbol) {
			java_cup.runtime.Symbol s=((java_cup.runtime.Symbol)info);
			/* Comprueba si el numero de línea es mayor o igual que cero */
			if(s.left >= 0) {
				m.append(" en linea "+(s.left+1));
				/*Comprueba si el numero de columna es mayoro igual que cero */
				if (s.right >= 0)
					m.append(", y columna "+(s.right+1));
			}
		}
		m.append(" : "+message);
		System.err.println(m);
    }
    public void report_fatal_error(String message, Object info) {
       	report_error(message, info);
		System.exit(1);
    }
 
public void conversion(String a, String b, int n){
 
	int c=0,d=0;	
	System.out.print(n+" ");	
	if (a=="Km"){ c=6; System.out.print("Km"); }
	else if (a=="Hm"){ c=5; System.out.print("Hm"); }
	else if (a=="Dm"){ c=4; System.out.print("Dm"); }
	else if (a=="m"){ c=3; System.out.print("m"); }
	else if (a=="dm"){ c=2; System.out.print("dm"); }
	else if (a=="cm"){ c=1; System.out.print("cm"); }
	else if (a=="mm"){ c=0; System.out.print("mm"); }	 
 
	if (b=="Km") d=6;
	else if (b=="Hm") d=5;
	else if (b=="Dm") d=4;
	else if (b=="m") d=3;
	else if (b=="dm") d=2; 
	else if (b=="cm") d=1;
	else if (b=="mm") d=0;
 
	System.out.print(" son "+n*Math.pow(10,c-d)+" ");	
 
}
 
:};
/*­­­ Declaración de la lista de símbolos de la gramática ­­­­­*/
 
/* Produciones de flujo normal */
terminal COMA, PUNTOCOMA, MM, DCM, CM, M, DCAM, HM, KM;
terminal Integer NUMBER;
non terminal Object programa, linea, expresiones, medida;
 
/* ­­­­­­­­­­­­­­ Declaración de la gramática  ­­­­­­­­­­­ */
programa ::= programa linea | linea;
 
linea ::= error NUMBER medida PUNTOCOMA {: parser.report_error("revise la sintaxis",null); :} |
	medida:m1 NUMBER:n medida:m2 PUNTOCOMA {: parser.conversion(m1.toString(),m2.toString(),n.intValue());
 	System.out.println(m2); :} | 
	expresiones medida:m1 NUMBER:n medida:m2 PUNTOCOMA
 	{: parser.conversion(m1.toString(),m2.toString(),n.intValue()); 
	System.out.println(m2); :} ;
 
expresiones ::= expresiones medida:m1 NUMBER:n medida:m2 COMA 
	{: parser.conversion(m1.toString(),m2.toString(),n.intValue()); 
	System.out.println(m2); :} | 
	medida:m1 NUMBER:n medida:m2 COMA {: parser.conversion(m1.toString(),m2.toString(),n.intValue());
 	System.out.println(m2); :} |
	error NUMBER medida COMA {: parser.report_error("revise las medidas en mayusculas",null); :};
 
medida ::= KM {: RESULT="Km"; :} | HM {: RESULT="Hm"; :} | DCAM {: RESULT="Dm"; :} |
	M {: RESULT="m"; :} | DCM {: RESULT="dm"; :} |
	CM {: RESULT="cm"; :} | MM {: RESULT="mm"; :};

Necesitas un fichero que cree un objeto parser y comience el análisis.

Main.java

import java_cup.runtime.Symbol;
import java.io.*;
class Main {
    static boolean do_debug_parse = false;
    static public void main(String[] args) throws java.io.IOException {
		parser parser_obj = new parser(new conversor(new FileReader(args[0])));
		Symbol parse_tree = null;
		try {
			if (do_debug_parse)
				 parse_tree = parser_obj.debug_parse();
			else parse_tree = parser_obj.parse();
			System.out.println("Entrada correcta");
		} catch (Exception e) {
			System.out.println("Horror");
		}
 
    }
}

Makefile

all: java
	javac *.java
 
java:
	jflex *.lex
	cup *.cup
 
clean:
	$(RM) *.class
	$(RM) parser.java sym.java
	$(RM) *~

Referencias

JFlex
CUP

Comentarios

Opciones de visualización de comentarios

Seleccione la forma que prefiera para mostrar los comentarios y haga clic en «Guardar las opciones» para activar los cambios.

re

It is great that we are able to receive the loans and that opens up completely new opportunities.

Imagen de jch1989

como ejecuto

salu2 a todos, con una consulta como ejecuto el cogio del analizador lexico, copio elcodigo completo o como
le ago para poder ver si funciona o no,

yo tengo q acer un analizador lexico para una calculadora estadistica si me pudieran ayudar se los agradeceria mucho, gracias
y suerte
ESTE ES MI CORREO: chiriatomo@hotmail.com
jchiricoc1989@gmail.com

"EL CONOCIMIENTO ES UNA FORMA DE SER LIBRE"

Imagen de magmax

Yo comenzaría...

Pues yo comenzaría por añadirle la letra "h", ya que parece que la de tu teclado no funciona. Y las tildes.

Cuando yo hago una pregunta intento que se me entienda correctamente. Xq sta claro q si nos pnemos, to2 pdems scribir komo n 1 movil, pero ya que no nos cobran por tecla pulsada, está bien tratarlas a todas por igual...

Un saludo.

Miguel Ángel García
http://magmax.org

Muy bueno

Muchas gracias buen samaritano. Me ha servido para poder coger un ejemplo que no sólo diga hola y tener por donde ir empezando.

Thanks!!