lunes, 11 de abril de 2011

Ejemplo Flex y Bison

Vamos a realizar un ejemplo de una calculadora sencilla que reconocerá las principales operaciones aritmética (+,-,* y /).

Abrimos un editor de texto y pegamos el siguiente código que será nuestro scanner

/*****************
  Definiciones
                Se colocan las cabeceras, variables y expresiones regulares

********************/

%{
  #include <stdio.h>
  #include <stdlib.h>
  #include "sintactico.tab.h"
  int linea=0;
%}
/*
Creamos todas las expresiones regulares

Creamos la definición llamada DIGITO, podemos acceder esta definición
usando {DIGITO}*/
DIGITO [0-9]
NUMERO {DIGITO}+("."{DIGITO}+)?


%%
  /***************
   Reglas
*****************/

 /* Creamos las reglas que reconocerán las cadenas que acepte
  Nuestro scanner y retornaremos el token a bison con la
  funcion return. */

{NUMERO} {yylval.real=atof(yytext); return(NUMERO);}
"="         {return(IGUAL);}
"+"         {return(MAS);}
"-"          {return(MENOS);}
";"          {return(PTOCOMA);}
"*"         {return(POR);}
"/"          {return(DIV);}
"("          {return(PAA);}
")"          {return(PAC);}
"\n"       {linea++;}
[\t\r\f] {}
" "                          {}
 /* Si en nuestra entrada tiene algún caracter que no pertenece a
   las reglas anteriores, se genera un error léxico */

.                              {printf("Error lexico en linea %d",linea);}
%%
/*
Código de Usuario

Aquí podemos realizar otras funciones, como por ejemplo ingresar
símbolos a nuestra tabal de símbolos o cualquier otra accione
del usuario. 
Todo lo que el usuario coloque en esta sección se copiara al
archvi lex.yy.c tal y como esta.
*/

  
Guardamos el archivo como lexico.l.   Luego creamos un nuevo archivo y colocamos el siguiente código.


%{

/********************
  Declaraciones en C
**********************/

  #include <stdio.h>
  #include <stdlib.h>
  #include <math.h>
  extern int yylex(void);
  extern char *yytext;
  extern int linea;
  extern FILE *yyin;
  void yyerror(char *s);
%}

/************************
  Declaraciones de Bison
*************************/

/*  Especifica la coleccion completa de tipos de datos para poder usar
   varios tipos de datos en los terminales y no terminales*/
%union
{
  float real;
}
/* Indica la produccion con la que inicia nuestra gramatica*/
%start Exp_l

/* Especificacion de termines, podemos especificar tambien su tipo  */
%token <real> NUMERO
%token MAS
%token MENOS
%token IGUAL
%token PTOCOMA
%token POR
%token DIV
%token PAA
%token PAC

/* No Terminales, que tambien podemos especificar su tipo */
%type <real> Exp
%type <real> Calc
%type <real> Exp_l
/*  Definimos las precedencias de menor a mayor */
%left MAS MENOS
%left POR DIV

%%
 /**********************
  Reglas Gramaticales
 ***********************/


Exp_l:                   Exp_l Calc  
                               |Calc
                                               ;
Calc       :  Exp PTOCOMA {printf ("%4.1f\n",$1)}                              
                                 ;
/* con el símbolo de $$ asignamos el valor semántico de toda
  la acción de la derecha y se la asignamos al no terminal de
   la izquierda, en la siguiente regla, se la asigna a Exp.
                Para poder acceder al valor de los terminales y no terminales del lado
   derecho usamos el símbolo $ y le concatenamos un numero que representa
   la posición en la que se encuentra es decir si tenemos

  A --> B NUMERO C

  Si queremos usar le valor que tiene el no terminal B usamos $1, si queremos
  usar el valor que tiene NUMERO usamos $2 y así sucesivamente.


*/
Exp :                      NUMERO {$$=$1;}
                                               |Exp MAS Exp {$$=$1+$3;}
                                               |Exp MENOS Exp {$$=$1-$3;}
                                               |Exp POR Exp {$$=$1*$3;}
                                               |Exp DIV Exp {$$=$1/$3;}
                                               |PAA Exp PAC {$$=$2;}
                                               ;
%%
/********************
  Codigo C Adicional
**********************/
void yyerror(char *s)
{
  printf("Error sintactico %s",s);
}

int main(int argc,char **argv)
{
  if (argc>1)
                yyin=fopen(argv[1],"rt");
  else
                yyin=stdin;

  yyparse();
  return 0;
}

Guardamos este archivo con el nombre sintáctico.y y con eso ya tenemos nuestro scanner y nuestro parser terminado.  Para compilar estos archivos usamos los comandos

Compilando sintactico.y
~>  bison -d sintactico.y

El parámetro –d, crea el fichero t.tab.h, que contiene los identificadores de los tokens de bison usados por flex

Compilando lexico.l
~> flex lexico.l

Compilando arhivos generados y crear ejecutable
~> cc lex.yy.c sintactico.tab.c -o analizador -lfl -lm

Esto nos genera un ejecutable llamado analizador.

Muchas veces necesitamos modificar nuestro archivo sintáctico.y o lexico.l y tenemos que compilar todo  cada vez que hacemos un cambio, para no estar escribiendo los comandos cada vez que realizamos un cambio, crearemos un script, que al ejecutarlo realizara todos los comandos de compilación. Para eso creamos un nuevo archivo en blanco y escribimos


#!/bin/bash
bison -d sintactico.y
flex lexico.l
cc lex.yy.c sintactico.tab.c -o analizador -lfl –lm


Guardamos este archivo con cualquier nombre, por ejemplo compilar.sh.  Ahora cambiaremos las propiedades de este archivo para poder ejecutar.  Le damos clic derecho sobre este archivo y en la pestaña permisos elegimos la opción de “Permitir ejecutar el archivo como un programa”, cerramos esa ventana.






Para poder compilar, desde consola nos ubicamos donde se encuentra este archivo .sh y escribimos

./compilar.sh

Esto nos genera nuestro ejecutable que podemos correr para poder probar nuestra calculadora.  Para ejecutar este ejemplo usamos el comando

./analizador

Ingresamos algunas expresiones y el resultado que obtenemos es:




 El ejemplo completo lo puedes descargar aquí.









16 comentarios:

  1. A que buen ejemplo hobit eres todo un genio!

    ResponderEliminar
  2. Excelente aporte, hasta que al fin un ejemplo son errores

    ResponderEliminar
  3. tiene razon!!! otros ejemplos siempre salen con error.
    Podrias hacer otro ejemplo paresido a este??

    ResponderEliminar
    Respuestas
    1. En este mismo blog esta tambien un videotutorial con otro ejemplo. http://inghobbit.blogspot.com/2011/10/ejemplo-flex-y-bison.html

      Eliminar
  4. muy buenisimo post, me ha sido de gran ayuda ing.

    ResponderEliminar
  5. me gustaría saber si me podría orientar mas con un ejemplo para un mini-pascal "que reconozca palabras reservadas" ... ? att. rolando

    ResponderEliminar
  6. hola me podrias ayudar con un problema necesito hacer un compilador que reste unos valores de unas columnas y me devuelva los valores

    ResponderEliminar
  7. ing. como podria modificar esta calculadora, para que de entrada sea numeracion postfija polaca de tal forma que la traduccion como salida sea numeracion infija?....

    ResponderEliminar
    Respuestas
    1. Lo que tienes que modificar es el analizador sintactico de tal forma que reconozca la numeracion postfija, luego en las acciones hacer la traduccion a numeracion infija, intenta hacerlo y si te quedas en algun punto vere como te puedo ayudar pero intentalo primero. Exitos!!!!

      Eliminar
  8. Hola, quise compilar los archivos generados (cc lex.yy.c sintactico.tab.c -o analizador -lfl -lm) pero me sale un error...
    Esto:
    /usr/bin/ld: cannot find -lfl
    collect2: ld devolvió el estado de salida 1
    Tiene que ver que se de otra distro de linux?

    ResponderEliminar
  9. Disculpa, no podrías subir un ejemplo que al introducirle información revuelta con direcciones de correo electrónico solo te extraiga e imprima las direcciones de correo sin la demás basura.?

    ResponderEliminar
  10. Como ago una que solo reste multiplique y el % o mod???

    ResponderEliminar
  11. Como ago una que solo reste multiplique y el % o mod???

    ResponderEliminar
  12. Ingeniero tengo un problema el cual me pide que ingresen 5 numeros enteros y dar como respuestas el mayor menor y promedio de esos numeros el inconveniente es que me lo piden en windows

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

    ResponderEliminar