domingo, 24 de octubre de 2010

Flex y Bison en un solo analizador

Anteriormente habiamos hablado acerca de la forma de configurar Flex (http://rolandopalermo.blogspot.com/2009/12/como-configurar-flex-en-windows-y-no.html) y también como hacerlo con Bison (http://rolandopalermo.blogspot.com/2010/01/configurando-bison-en-windows-para.html) pero hasta ahora no se había explicado la forma en cómo se puede hacer que ambas herramientas trabajen juntas. Bueno, este post abordará este tema ya que si bien es cierto que la configuración de estas herramientas es sencilla el hacerlos trabajar juntas no lo es tanto.

Bueno, lo primero que tenemos que hacer es tener nuestro Flex y Bison configurados como expliqué en los post anteriores. Luego de eso debemos tener nuestros archivos tanto para flex como para bison listos, claro que la forma de cómo se estructuran estos archivos no es parte de este post. Aca les voy a mostrar los archivos que usé para un proyecto de la universidad (Shark Analyzer 3.0) sobre un lenguaje de programación que estabamos definiendo. Antes que nada un screenshot de lo que es el analizador que hicimos.


Archivo para FLEX
%{
   #include <stdio.h>
   #include <conio.h>
   #include "parser.h"  
%}
%option noyywrap
%option yylineno
letra [a-zA-Z]
digito [0-9]
binario [0-1]
ignora " "|\t|\n
operarit *|+|-|/
operlog &|$
comparador <|>|<=|>=|==|!=
%%
{ignora}+                    {;}
"Entero"                     {printf("Palabra reservada para tipo de dato entero\n");return PRENTERO;}
"Real"                       {printf("Palabra reservada para tipo de dato real\n");return PRREAL;}
"Booleano"                   {printf("Palabra reservada para tipo de dato booleano\n");return PRBOOLEANO;}
"Caracter"                   {printf("Palabra reservada para tipo de dato caracter\n");return PRCARACTER;}
"Si"                         {printf("Palabra reservada para condicional\n");return PRSI;}
"Sino"                       {printf("Palabra reservada para otro condicional\n");return PRSINO;}
"SinoSi"                     {printf("Palabra reservada para definir condicionales secundarias\n");return PRSINOSI;}
"Entonces"                   {printf("Palabra reservada para definir accion a realizar\n");return PRENTONCES;}
"FinSi"                      {printf("Palabra reservada finalizar condicional\n");return PRFINSI;}
"Para"                       {printf("Palabra reservada para bucle de tipo Para\n");return PRPARA;}
"FinPara"                    {printf("Palabra reservada para fin de bucle de tipo Para\n");return PRFINPARA;}
"Mientras"                   {printf("Palabra reservada para bucle de tipo Mientras\n");return PRMIENTRAS;}
"Hacer"                      {printf("Palabra reservada para indicar que se empieza algo\n");return PRHACER;}
"FinMientras"                {printf("Palabra reservada fin de bucle de tipo Mientras\n");return PRFINMIENTRAS;}
"FinHacerMientras"           {printf("Palabra reservada para indicar fin de bucle Hacer-Mientras\n");return PRFINHACERMIENTRAS;}
"Funcion"                    {printf("Palabra reservada para declaracion de funciones\n");return PRFUNCION;}
"Estructura"                 {printf("Palabra reservada para declaracion de estructuras\n");return PRESTRUCTURA;}
"FinFuncion"                 {printf("Palabra reservada para finalizar funcion\n");return PRFINFUNCION;}
"Retorna"                    {printf("Palabra reservada para retorno de funcion\n");return PRRETORNA;}
"SinValor"                   {printf("Palabra reservada para funcion sin valor de retorno\n");return PRSINVALOR;}
"Definir"                    {printf("Palabra reservada para definir funciones\n");return PRDEFINIR;}
"Constante"                  {printf("Palabra reservada para definir constantes\n");return PRCONSTANTE;}
"Entrada"                    {printf("Palabra reservada para definir entradas\n");return PRENTRADA;}
"Salida"                     {printf("Palabra reservada para definir salidas\n");return PRSALIDA;}
{letra}({letra}|{digito})*   {printf("Identificador\n");return IDENT;}
{letra}+                     {printf("Caracter\n");return CARACTER;}
{binario}+                   {printf("Binario\n");return BOOLEANO;} 
{digito}+                    {printf("Entero\n");return ENTERO;}
{digito}+"."{digito}+        {printf("Real\n");return REAL;}
{comparador}                 {printf("Comparador\n");return COMPARADOR;}
":="                         {printf("Asignador\n");return ASIG;}
";"                          {printf("Fin sentencia\n");return PCOMA;}
"!="                         {printf("Diferente\n");return DIF;}
","              {printf("Coma\n");return COMA;}
"=="                         {printf("Igual\n");return IGUAL;}
"."                          {printf("Punto\n");return PTO;}
">="                         {printf("Signo mayor-igual\n");return MAIGU;}
"<="                         {printf("Signo menor-igual\n");return MEIGU;}
"("                          {printf("(\n");return PARIZ;}
")"                          {printf(")\n");return PARDE;}
">"                          {printf(">\n");return MAYOR;}
"<"                          {printf("<\n");return MENOR;}
"{"                          {printf("{\n");return LLIZ;}
"}"                          {printf("}\n");return LLDE;}
"+"                          {printf("+\n");return MAS;}
"-"                          {printf("-\n");return MENOS;}
"*"                          {printf("*\n");return POR;}
"/"                          {printf("/\n");return ENTRE;}
"&"                          {printf("&\n");return YLOG;}
"$"                          {printf("Operador Logico\n");return OLOG;}
.                            {printf("ERROR LEXICO EN LINEA %d \n",yylineno);}
%%
int main(int argc,char *argv[])
{
 if ((yyin = fopen(argv[1], "rt")) == NULL)
 {
  printf("\nNo se puede abrir el archivo: %s\n", argv[1]);
 }
 else
 {
  //yylex();
  yyparse();
 }
 fclose(yyin);
 return 0;
}
Lo único diferente con respecto al ejemplo que mostré en el post de configuración de flex es que ahora no invocaremos a la función yylex() sino que ahora se invocará a la función yyparse(). Adicionalmente vemos que en la cabecera se invoca a la librería "parser.h" la cuál debe contener las deficiones para cada toquen de nuestra gramática.
 
Arhcivo parser.h
#ifndef YYSTYPE
#define YYSTYPE int
#endif
#define PRENTERO 257
#define PRREAL         258
#define PRBOOLEANO 259
#define PRCARACTER 260
#define PRSI         261
#define PRSINO         262
#define PRSINOSI 263
#define PRENTONCES 264
#define PRFINSI         265
#define PRPARA         266
#define PRFINPARA 267
#define PRMIENTRAS 268
#define PRHACER         269
#define PRFINMIENTRAS 270
#define PRFINHACERMIENTRAS  271
#define PRFUNCION     272
#define PRFINFUNCION     273
#define PRRETORNA     274
#define PRSINVALOR     275
#define PRESTRUCTURA     276
#define MAS 277
#define MENOS 278
#define POR 279
#define ENTRE 280
#define OLOG 281
#define YLOG 282
#define PRDEFINIR 283
#define PRCONSTANTE 284
#define IDENT 285
#define ENTERO 286
#define REAL 287
#define BOOLEANO 288
#define CARACTER 289
#define COMPARADOR 290
#define ASIG 291
#define PCOMA 292
#define DIF 293
#define COMA 294
#define IGUAL 295
#define PTO 296
#define MAIGU 297
#define MEIGU 298
#define PARIZ 299
#define PARDE 300
#define MAYOR 301
#define MENOR 302
#define LLIZ 303
#define LLDE 304
#define PRSALIDA 305
#define PRENTRADA 306

extern YYSTYPE yylval;

Cone esto ahora debemos definir el archivo para bison.

Archivo para Bison:
%{
int yystopparser=0;
%}


%token PRENTERO PRREAL PRBOOLEANO PRCARACTER PRSI PRSINO PRSINOSI PRENTONCES PRFINSI PRPARA  
PRFINPARA PRMIENTRAS PRHACER PRFINMIENTRAS PRFINHACERMIENTRAS PRFUNCION PRFINFUNCION PRRETORNA PRSINVALOR PRESTRUCTURA MAS MENOS POR ENTRE OLOG YLOG  
PRDEFINIR PRCONSTANTE IDENT ENTERO REAL BOOLEANO CARACTER COMPARADOR ASIG PCOMA DIF COMA IGUAL PTO   
MAIGU MEIGU PARIZ PARDE MAYOR MENOR LLIZ LLDE PRSALIDA PRENTRADA
%start inicio

%%
inicio  : funcion
  | constante 
  | estructura
  ;

estructura : PRESTRUCTURA IDENT LLIZ n
n  : declaracion n
  | declaracion nn
nn  : LLDE IDENT PCOMA o
o  : estructura
  | constante
  | funcion
  ;

constante : PRDEFINIR PRCONSTANTE e
e  : PRENTERO IDENT f
  | PRREAL IDENT f
  | PRBOOLEANO IDENT f
f  : ASIG g
g  : ENTERO PCOMA y
  | REAL PCOMA y
  | BOOLEANO PCOMA y
y  : constante
  | estructura 
  | funcion  
  ; 

funcion  : PRFUNCION j
j  : PRENTERO k
  | PRREAL k
  | PRBOOLEANO k
  | PRCARACTER k
  | PRSINVALOR k
k  : IDENT PARIZ l
l  : kl
kl  : declaracion l
  | ll
ll  : PARDE sentencia m
m  : PRRETORNA IDENT p
  | p
p  : PRFINFUNCION funcion
  | PRFINFUNCION 
  ;

comparacion : IDENT COMPARADOR b
b  : IDENT
  | REAL
  | ENTERO
  | BOOLEANO
  ;

declaracion : PRENTERO c
  | PRREAL c
  | PRBOOLEANO c
  | PRCARACTER c
c  : IDENT PCOMA
  ;

asignacion  : IDENT ASIG a
a  : IDENT PCOMA
  | ENTERO PCOMA 
  | REAL PCOMA
  | BOOLEANO PCOMA
  | oparitmetica PCOMA
  ;

mientras : PRMIENTRAS PARIZ x
x  : comparacion y
  | oplogica y
y  : PARDE sentencia PRFINMIENTRAS
  ;

hacermientras : PRHACER sentencia PRFINHACERMIENTRAS PRMIENTRAS PARIZ cc
cc  : comparacion PARDE
  | oplogica PARDE
  ;

para  : PRPARA PARIZ asignacion comparacion PCOMA asignacion PARIZ sentencia PRFINPARA
  ;

si  : PRSI PARIZ sh
sh  : comparacion shh
  | oplogica shh
shh  : PARDE PRENTONCES sentencia h
h  : PRFINSI
  | PRSINOSI PARIZ comparacion PARDE PRENTONCES sentencia i
i  : h
  | z
z  : PRSINO PRENTONCES sentencia PRFINSI
  ;

sentencia : declaracion sentencia 
  | declaracion 
  | asignacion sentencia 
  | asignacion
  | mientras sentencia 
  | mientras
  | hacermientras sentencia 
  | hacermientras 
  | para sentencia
  | para
  | si sentencia 
  | si
  | entrada sentencia
  | entrada
  | salida sentencia
  | salida
  ;

salida  : PRSALIDA ASIG LLIZ LLDE PCOMA
  ;

entrada  : PRENTRADA IDENT PCOMA
  ;

oparitmetica : MAS PARIZ q
  | POR PARIZ q
  | suma
  | producto
q  : oparitmetica PARDE r
suma  : MAS r t
producto : POR r t
t  : r u
u  : t
  | 
r  : IDENT
  | REAL
  | ENTERO
  ;


oplogica : OLOG PARIZ qq
  | YLOG PARIZ qq
  | yy
  | oo
qq  : oplogica PARDE rr
oo  : OLOG rr tt
yy  : YLOG rr tt
tt  : rr uu
uu  : tt
  | 
rr  : comparacion 
  ;
Con esos tres archivos ya casi estamos listos. También dentro del directorio donde están trabajando (Directorio en donde se encuentra el archivo de flex y bison) deben crear el siguiente archivo:

Archivo error.c
void  yyerror(char * msg)
{
 printf ("%s\n", msg);
}

Ahora solo nos queda compilar los scripts de flex y bison del siguiente modo:

Con esto nuestro directorio de trabajo debería quedar algo así:


Ahora si usan DevC++ solo deben crear un proyecto y añadir todos los archivos ahí presentes. Observen además que para correr el programa no se nos pedirá que ingresemos el nombre de del archivo sino que lo debemos mandar como parámetro, en otras palabras el archivo generado no se debe ejecutar con doble clic sino por medio de la consola del windows. Creo que una imágen vale más que mil palabras:


El hecho que el programa tenga una forma especial de ejecutarse es que nosotros hicimos un proyecto en la Universidad en donde desde una aplicación en Java invocabamos a un ejecutable generado en C que se encargaba de realizar el procesamiento de la gramática y esa salida la mostrabamos en Java (que lógicamente presenta una mejor interfaz de usuario). Esto lo cuento a modo de anécdota por si tuvieran que hacer algo similar.

Y bueno, lógicamente también les pondré unos ejemplos de código. El primero es el que está escrito correctamente:

Codigo00.txt
Estructura est 
{
Entero num;
Entero nfgj;
Booleano ggnum;
} est1;

Definir Constante Entero numero := 145 ;

Funcion Entero miFuncion( Caracter Cadena; ) 

   Car:=Carro;
   Entero Num;

   Mientras ( c<d )

      Car:=Carro;
      Entero bandera;
      Booleano boole;
      boole:=1;

      Mientras ( a>=4 )

         Car:=Carro;
         Entero bandera;
         Booleano boole;
         boole:=1;

  Para ( a:=14; a<=25; a:= + 5 4 ;(
  
  
  Si ( a==4 ) Entonces
    Bool:=1;
     FinSi
  
  FinPara
  

      FinMientras

   FinMientras

   Si ( a==4 ) Entonces
  Bool:=1;
   FinSi

   Retorna Numero 

FinFuncion
Y el programa Shark Analyzer 3.0 lo analiza de la siguiente manera:


Y el siguiente código está mal escrito
Codigo01.txt
Definir Constante Entero numero := 145 ;
Definir Booleano nume := 1 ;
Funcion Entero ( Entero num; Real fre; Booleano boll;) 
   Mientras (  )
      Booleano ghjfg;
      bocfgh:=1;
      Mientras ( a>=4 )
         Car:=Carro;
         Entero bandera;
         Booleano boole;
         boole:=1; 
 Si ( a==4 ) 
   Bool:=1;
   Booleano boolean;
 SinoSi (a==5) Entonces
  Bool:=2;
 Sino Entonces
  Bool:=3;
    FinSi
      FinMientras
   FinMientras
   Retorna Carro
FinFuncion 
Shark Analyzer 3.0 nos muestra lo siguiente:


También puedes descargar el jar de Shark Analyzer y probar tus propios códigos (claro, en función de la gramática mostrarda en el script de bison).

También puedes cambiar la gramática y de esta manera el archivo ejecutable que te genera tu compilador de C lo puedes usar en lugar de kernel.exe, de este modo Shark Analyzer usará tus gramáticas y no las que trae por defecto en su kernel. exe. Eso si, recuerda que para que la consola de salida de Shark Analyzer marque de rojo las salidas donde se ha encontrado un error léxico o si ha ocurrido un error sintáctico, en el archivo de flex esta línea:
.                            {printf("ERROR LEXICO EN LINEA %d \n",yylineno);}
Siempre debe empezar con "ERROR", en otras palabras esa línea no debería ser modificada del archivo de Flex.
Cualquier duda o sugerencia no dudes en escribirla.

33 comentarios:

  1. Mmmm, no entiendo bien tu pregunta amigo.

    ResponderEliminar
  2. Osea, si en vez de C puedo utilizar C++

    ResponderEliminar
  3. No se si logras entender mi pregunta ahora. Es que tengo que hacer uno en C++

    ResponderEliminar
  4. Mmmm, depende de los scripts de flex y bison.

    ResponderEliminar
  5. mm como asi?, yo hice uno sencillo pero al final no me compila, y entonces leyendo me tope con Flex y Bison

    ResponderEliminar
  6. Si tu trabajo es hacer un compilador, te recomiendo leas estos post:

    http://rolandopalermo.blogspot.com/2009/12/como-configurar-flex-en-windows-y-no.html

    http://rolandopalermo.blogspot.com/2010/01/configurando-bison-en-windows-para.html

    ResponderEliminar
  7. jeje ya me los lei antes que este articulo, pero no se si sirve para C++, ya que en C no lo puedo hacer

    ResponderEliminar
  8. Hola rolando me encontre este post y queria preguntarte algo ya pude compilar los archivos de flex y bison tambien ya hice el parser.h mi duda es con el error.c es que estoy utilizando dev c++ hago un proyecto nuevo lo nombro asi error para que me haga el archivo y le pongo las lineas que pero no me deja correrlo help me.

    que estoy haciendo mal espero tu respuesta

    ResponderEliminar
  9. Hola. Bueno, en primer lugar te recomienda que revises este post:

    http://rolandopalermo.blogspot.com/2010/01/configurando-bison-en-windows-para.html

    Aquí hay un proyecto en DevC++ y puedes usarlo de plantilla para agregar los archivos que tú usas.

    Saludos.

    ResponderEliminar
  10. que tal genial. pero cuando ejecuto el archivo yy.c me arroja este error

    4 C:\Users\Administrador\lexico.l parser.h: No such file or directory.

    y si hice el archivo parser.h o como lo hago?

    ResponderEliminar
  11. ya logre quitar ese error, lo que hice fue copiar el archivo parser.h a la carpeta dodne devc buca las librerias, pero ahora me apaarece el siguiente error [Linker error] undefined reference to `yyparse'

    ld returned 1 exit status

    ResponderEliminar
  12. otra pregunta, como hago si quiero q una vez q termine de realizar el analisis quiero que ejecute el codigo y "haga" lo que se supone q programe

    ResponderEliminar
  13. creo q por fin lo lgre pero ahora cuando ejecuto el programa, solo aparece en la consola presione una tecla para continuar y todo se cierra. alguna idea de que pueda estar mal?

    ResponderEliminar
  14. que tal quisiera saber como poder crear el parse.h ya que de eso no tenes un comando de como lo haces

    ResponderEliminar
  15. Para eso no se necesita de ningún comando amigo, solo definir las variables tal como está descrito en el post. Saludos.

    ResponderEliminar
  16. Este comentario ha sido eliminado por el autor.

    ResponderEliminar
  17. de donde saco los archivos .o ??

    ResponderEliminar
  18. a mi no me sale, al momento de copilar en el proyecto de devc++ me sale un mensaje de error que dice "yyparse" undeclared (first use this function)

    me podrian ayduar ah que se deba???
    grasias

    ResponderEliminar
  19. Hola, al compilar en DevC++ me manda este error, alguna idea?

    C:\Dev-Cpp\bin\flex.l In function `int main(int, char**)':
    47 C:\Dev-Cpp\bin\flex.l `yyparse' undeclared (first use this function)
    (Each undeclared identifier is reported only once for each function it appears in.)
    51:4 C:\Dev-Cpp\bin\flex.l [Warning] no newline at end of file
    C:\Dev-Cpp\bin\Makefile.win [Build Error] [../../GnuWin32/bin/lex.yy.o] Error 1

    ResponderEliminar
  20. HOLA AMIGO ROLANDO HE LOGRADO COMPILAR EL ARCHIVO FLEX,E CREADO EL ARCHIVO PARSER.H EN UN BLOCK DE NOTAS Y EL ARCHIVO DE BISON AUN NO LO AGO PERO YA TENIENDO LOS DOS PRIMEROS ARCHIVOS LOS COMPILO Y BOTA EL SIGUIENTE ERROR.
    [Linker error] undefined reference to `yyparse'
    ld returned 1 exit status
    C:\Users\fredy\Desktop\analizador lexico\Makefile.win [Build Error] [analilex.exe] Error 1

    SI PUDIERAS DARME UNA SOLUCION A ESTE ERROR
    GRACIAS DE ANTEMANO

    ResponderEliminar
  21. Tienes que generar el archivo de Bison también pues él es el encargado de generar la función yyparse. Si lo que quieres es solo un analizador léxico te recomiendo este post: http://rolandopalermo.blogspot.com/2009/12/como-configurar-flex-en-windows-y-no.html
    Saludos.

    ResponderEliminar
  22. por mas que busco no puedo solucionar este error

    79 C:\cf\flex.l `yyparse' undeclared (first use this function)

    no se que anda mal ...respondeme porfavor

    ResponderEliminar
  23. Gracias , me sirvió de mucho ... Gracias++
    Me sirvió para mi Proyecto Final de Compiladores

    ResponderEliminar
  24. Buenas Tardes, Saludos Rolando, gracias por tu aporte, estoy tomando tus ejemplos como practica para desarrollar una tarea que nos mandaron pero no logro correr ningun codigo ejemplo sin que de un error de syntaxis, te comento que copie y pegue todo lo que colocaste y lo he compilado de la siguiente forma:

    bison -yd ejemplo.y ; flex ejemplo.l; gcc y.tab.c lex.yy.c -lfl -o ejecutable
    Obteniendo varios warnings:
    ejemplo.l:45: warning, la regla no se puede aplicar
    ejemplo.l:52: warning, la regla no se puede aplicar
    ejemplo.l:54: warning, la regla no se puede aplicar
    ejemplo.l:56: warning, la regla no se puede aplicar
    ejemplo.l:57: warning, la regla no se puede aplicar
    ejemplo.l:60: warning, la regla no se puede aplicar
    ejemplo.l:61: warning, la regla no se puede aplicar

    Al momento de ejecutar estos son los resultados:
    ./ejecutable codigo1.shark

    Palabra reservada para declaracion de estructuras
    syntax error

    Queria saber si hay algo que falte en los codigos que muestras, pido disculpas de antemano cualquier molestia que pueda causarte.

    Muchas Gracias!

    ResponderEliminar
  25. Verifica estos dos artículos:

    http://rolandopalermo.blogspot.com/2009/12/como-configurar-flex-en-windows-y-no.html

    http://rolandopalermo.blogspot.com/2010/01/configurando-bison-en-windows-para.html

    Tienes que empezar a usar cada uno de manera independiente y luego ya tendrás tu entorno bien configurado para poder trabajar el ejemplo posteado aquí.

    Saludos.

    ResponderEliminar
  26. Disculpa Rolando todo el tutorial me parece muy bueno, pero me podrias ayudar sobre la manera de como integrar los archivos al dev c++, porque no me funciona, por favor

    ResponderEliminar
  27. Una pregunta a mi me aparece un error con el archivo ejecutable que es el c te agradeceria tu asesoria

    ResponderEliminar
  28. Muy buen aporte. En la universidad nos dejaron hacer un compilador y lo estoy haciendo en base a esto, pero como creaste los archivos error.o y parser.y? compilo lexico.l y sintactico.y y todo bien pero despues al crear el exe me tira los siguientes errores: C:\DOCUME1\SERVER01\CONFIG1\Temp\ccWTSwrG.o:y.tab.c:<.text+0x397>: undifined reference to 'yyerror' C:\DOCUME1\SERVER01\CONFIG1\Temp\ccWTSwrG.o:y.tab.c:<.text+0x4b7>: undifined reference to 'yyerror'
    collect2: ld devolvia estado de salida 1
    Por favor cualquier ayuda te agradecería mucho.

    ResponderEliminar
  29. Agradesco el aporte Rolando Palermo, me sirvio para mi proyecto de la universidad de compiladores, me sirvio de base para realizar un compilador de C.
    Gracias saludos.

    ResponderEliminar
  30. hola que tal rolando un saludo y una felicitacion por tu pagina, la verdad esque tengo un proyecto y tengo que hacer un analizador no se si tengas algunos tutoriales o algo que me pueda echar la mano gracias.

    ResponderEliminar
  31. buena tarde rolando espero y me ayudes con algo bien sencillo jajaja creo , tu a la hora de ejecutar tienes un archivo llamado codigo1.shark que onda con eso ??? espero y me respondas de antemano gracias

    ResponderEliminar