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


blog comments powered by Disqus