Professional Documents
Culture Documents
Flex y Bison
Flex y Bison
Reseña Histórica
El alumno recordará que la sistematización de la construcción de Procesadores de Lenguaje ha
sido posible gracias a los siguientes hechos históricos (menciónanoslos más relevantes) que
relacionan la evolución de materias muy dispares: Matemáticas (formalización de la
lógica)Lingüística Teoría de máquinas (electrónica digital)Abu Ja’far Mohammed ibn Musa al-
Jowârizmî, matemático persa, publicó hacia el año 825 un tratado de aritmética y su nombre se
utiliza para llamar algoritmo a todo conjunto de reglas que permiten obtener un resultado
determinado a partir de ciertos datos de partidaEdad Media, surge la leyenda de la cabeza
parlante de Alberto Magno, tal cabezafue supuestamente destruida por su discípulo Tomás de
Aquino.La zairja árabe consistía en algo similar, una máquina pensante que
discurríamecánicamente combinando las letras del alfabeto arábigo a cada una de las cuales se
asociaba una idea filosófica Raimundo Lullio “Ars Magna” (1235-1315), intenta superar, en
versión cristiana, lazairja con una máquina basada en círculos concéntricos giratorios en el que
coloca un abecedario de ideas sobre Dios y el universo y que, con esta disposición, permitían ser
combinadas (en razonamientos) de forma mecánica. Siglos XVII y XVIII Gottfried Wilhelm Leibniz
(1646-1716): álgebra universal. En su producción filosófica un joven (apenas 20 años) Leibniz
concibe en De arte combinatoria la idea de diseñar una notación y reglas similares a la
matemática para los pensamientos y revisa el Ars Magna.En Mathesis universalis da el nombre
de Logica Mathjematica sive Mathesisuniversalis sive Logistica sive Logica MathematicorumEl
objetivo final es que, como dice literalmente, “cuando surja una controversia entre dos filósofos
no será preciso que gasten en discutir más tiempo del que gastarían dos calculadores. Pues
bastará con tomar asiento, echar mano de pluma y ábaco y decirse el uno al otro: ¡calculemos!”
Que es flex
Un generador de analizadores léxicos el primero y un generador de analizadores gramaticales el
segundo,son dos herramientas útiles para crear programas que reaccionen a una entrada de
datos con una estructura y unlenguaje predeterminado, como por ejemplo, podemos crear
compiladores, interpretes y analizadores de linea decomando.Flex es un una herramienta que
permite generar analizadores léxicos. A partir de un conjunto de expresionesregulares, Flex busca
concordancias en un fichero de entrada y ejecuta acciones asociadas a estas expresiones.Uno de
los usos principales de Flex es como acompañante del analizador de gramáticas Bison (o de
Yacc).Los analizadores Bison necesitan una función llamda ‘yylex()’ para devolverles el siguiente
token de la entrada.Esa función devuelve el tipo del próximo token y además puede poner
cualquier valor asociado en la variableglobal yylval. Para usar Flex con Bison, normalmente se
especifica la opción –d de Bison para que genera elfichero ‘y.tab.h’ que contiene las definiciones
de todos los ‘%tokens’ que aparecen el fuente Bison.
Como se instala Flex y Bison
2. Instalar el software en la unidad C: (para explicar a partir del punto 4 se tendrá como
hipótesis de que flex y bison han sido instalados en la ruta: C:\GnuWin32\ donde
contiene una subcarpeta llamada bin donde se encuentran los programas
respectivos)
3. Flex y bison son aplicaciones de consola, por lo que se deberá entrar al Símbolo del
sistema y tipear líneas de comando para ejecutar Flex. Una alternativa es crear un
archivo de proceso por lotes (*.bat) que contenga las líneas de comando para la
ejecución de Flex y Bison y/o la compilación del archivo generado.
4. Si deseas que flex y bison se integren al conjunto de variables del entorno (esto te va
• Selecciona “Propiedades”
• Clic en la pestaña “Opciones Avanzadas”
• Presiona el botón “Variables de entorno”
• En la ventana de variables de entorno, ubicarse en la sección “Variables del sistema”
luego haz clic en PATH y luego en el botón “Modificar” (si no está hacer clic en “Nueva”
y agregar PATH) • En la nueva ventana, escribir la ruta completa al directorio “bin” de
la aplicación flex/bison. Si existe otro valor, separarlos con comas.
• Aceptar los cambios y luego reiniciar el sistema operativo.
Cuando tengas listo podrás llamar a flex/bison desde el símbolo del sistema sin necesidad de
ubicarte en la carpeta donde ha sido instalado flex/bison.
Patrones en flex
Los patrones en la entrada se escriben utilizando un conjunto extendido de expresiones regulares
y usando como alfabeto cualquier carácter ASCII. Cualquier símbolo excepto el espacio en blanco,
tabulador, cambio de línea y los caracteres especiales se escriben tal cual en las expresiones
regulares (patrones) de Flex.
Los caracteres especiales son: “ \ [ ^ - ? . * + | ( ) $ / { } % < >
Algunos de los patrones de Flex son: x
empareja el carácter ‘x’ .
cualquier carácter excepto una línea nueva [xyz]
un conjunto de caracteres; en este caso, el patrón empareja una ‘x’, una ‘y’, o una ‘z’ [abj-oZ] un
conjunto de caracteres con un rango; empareja una ‘a’, una ‘b’, cualquier letra desde la ‘j’ hasta
la ‘o’, o una ‘Z’
Emparejamiento de la entrada
Cuando el escáner generado está funcionando, este analiza su entrada buscando cadenas que
concuerden con cualquiera de sus patrones. Si encuentra más de un emparejamiento, toma el
que empareje el texto más largo. Si encuentra dos o más emparejamientos de la misma longitud,
se escoge la regla listada en primer lugar en el fichero de entrada de Flex. Una vez que se
determina el emparejamiento, el texto correspondiente al emparejamiento (denominado el
token) está disponible en el puntero de carácter global yytext, y su longitud en la variable global
entera yyleng. Entonces la acción correspondiente al patrón emparejado se ejecuta y luego la
entrada restante se analiza para otro emparejamiento. Si no se encuentra un emparejamiento,
entonces se ejecuta la regla por defecto: el siguiente carácter en la entrada se considera
reconocido y se copia a la salida estándar.
Condiciones de arranque
Variables disponibles para el usuario
Esta sección resume algunos de los diferentes valores disponibles al usuario en las acciones de
las reglas.
Al ejecutar el comando Flex nombre fichero fuente se creará un fichero en C llamado “lex.yy.c”.
Si se compila este fichero con la instrucción “gcc lex.yy.c –ll –o nombre ejecutable” (-ll indica
enlazar con la biblioteca de lex) se obtendrá como resultado un fichero ejecutable llamado
nombre ejecutable).
Una vez se ha creado el fichero ejecutable se puede ejecutar directamente. Flex esperará que se
introduzca texto por la entrada estándar (teclado) y lo analizará cada vez que se pulse el retorno
de carro. Para terminar con el análisis normalmente hay que pulsar -D. Para poder probarlo con
entradas más largas, lo más sencillo es crear archivos de texto y usar redirecciones para que el
ejecutable lea estos ficheros como entrada a analizar. Por ejemplo, si hemos creado un analizador
llamado prueba1 y un fichero de texto con datos para su análisis, llamado entrada.txt, podemos
ejecutar $prueba1 < entrada.txt.
Si se quiere ejecutar algún código al final de un análisis de Flex (para mostrar resultados, por
ejemplo) hay al menos dos opciones:
Introducción a Bison
Las declaraciones en C pueden definir tipos y variables utilizadas en las acciones. Puede también
usar comandos del preprocesador para definir macros que se utilicen ahí, y utilizar #include para
incluir archivos de cabecera que realicen cualquiera de estas cosas.
donde resultado es el símbolo no terminal que describe esta regla y componentes son los diversos
símbolos terminales y no terminales que están reunidos por esta regla. Por ejemplo,
dice que dos agrupaciones de tipo exp, con un token ‘+’ en medio, puede combinarse en una
agrupación mayor de tipo exp. Los espacios en blanco en las reglas son significativos únicamente
para separar símbolos. Puede añadir tantos espacios en blanco extra como desee. Distribuidas en
medio de los componentes pueden haber acciones que determinan la semántica de la regla. Una
acción tiene el siguiente aspecto:
{sentencias en C}
Normalmente hay una única acción que sigue a los componentes. Se pueden escribir por
separado varias reglas para el mismo resultado o pueden unirse con el carácter de barra vertical
‘|’ así:
Estas aún se consideran reglas distintas incluso cuando se unen de esa manera. Si los
componentes en una regla están vacíos, significa que resultado puede concordar con la cadena
vacía (en notación formal sería ε). Por ejemplo, aquí aparece cómo definir una secuencia
separada por comas de cero o más agrupaciones exp:
Es habitual escribir el comentario ‘/* vacío */’ en cada regla sin componentes.
Una regla se dice recursiva cuando su no-terminal resultado aparezca también en su lado
derecho. Casi todas las gramáticas de Bison hacen uso de la recursión, ya que es la única manera
de definir una secuencia de cualquier número de cosas. Considere esta definición recursiva de
una secuencia de una o más expresiones:
Puesto que en el uso recursivo de expseq1 este es el símbolo situado más a la izquierda del lado
derecho, llamaremos a esto recursión por la izquierda. Por contraste, aquí se define la misma
construcción utilizando recursión por la derecha:
Cualquier tipo de secuencia se puede definir utilizando ya sea la recursión por la izquierda o
recursión por la derecha, pero debería utilizar siempre recursión por la izquierda, porque puede
analizar una secuencia de elementos sin ocupar espacio de pila (es decir, de forma mucho más
eficiente en memoria). La recursión indirecta o mutua sucede cuando el resultado de la regla no
aparece directamente en su lado derecho, pero aparece en las reglas de otros no terminales que
aparecen en su lado derecho.
Por ejemplo:
Declaraciones en Bison
La sección de declaraciones de Bison de una gramática de Bison define los símbolos utilizados en
la formulación de la gramática y los tipos de datos de los valores semánticos. Todos los nombres
de tokens (pero no los tokens de carácter literal simple tal como ‘+’ y ‘*’) se deben declarar. Los
símbolos no terminales deben ser declarados si necesita especificar el tipo de dato a utilizar para
los valores semánticos. 14 Introducción a Flex y Bison La primera regla en el fichero también
especifica el símbolo inicial, por defecto.
Si desea que otro símbolo sea el símbolo de arranque, lo debe declarar explícitamente.
Precedencia de operadores
Use las declaraciones %left, %right o %nonassoc para declarar un token y especificar su
precedencia y asociatividad, todo a la vez. Estas se llaman declaraciones de precedencia.
La sintaxis de una declaración de precedencia es la misma que la de %token: bien %left símbolos...
o %left símbolos...
Y realmente cualquiera de estas declaraciones sirve para los mismos propósitos que %token. Pero
además, estos especifican la asociatividad y precedencia relativa para todos los símbolos:
/* Ejemplo para una pequeña calculadora que permite trabajar con números enteros y reales
con las operaciones básicas de suma, resta, producto, división y trigonométricas como el seno y
el coseno */
#include <stdio.h>
#include <stdlib.h>
int nlines=0;
%}
DIGITO [0-9]
ID [a-zA-Z][a-zA-Z0-9_]*
%%
{DIGITO}+ {printf("Encontrado TKN_NUM_ENTERO: %d",atoi(yytext));}
{DIGITO}+"."{DIGITO}+ {printf("Encontrado TKN_NUM_REAL: %f",atof(yytext));}
"=" {printf("Encontrado TKN_ASIGN: %s",yytext);}
";" {printf("Encontrado TKN_PTOCOMA: %s",yytext);}
"*" {printf("Encontrado TKN_MULT: %s",yytext);}
"/" {printf("Encontrado TKN_DIV: %s",yytext);}
"+" {printf("Encontrado TKN_MAS: %s",yytext);}
"-" {printf("Encontrado TKN_MENOS: %s",yytext);}
"(" {printf("Encontrado TKN_PAA: %s",yytext);}
")" {printf("Encontrado TKN_PAC: %s",yytext);}
"cos" {printf("Encontrado TKN_COS: %s",yytext);}
"sen" {printf("Encontrado TKN_SEN: %s",yytext);}
{ID} {printf("Encontrado TKN_ID: %s",yytext);}
"\n" {nlines++;}
.LABORATORIO DE COMPILADORES
%% void main(int argc,char **argv)
{
if (argc>1)
yyin=fopen(argv[1],"rt");
else
yyin=stdin;
yylex();
printf("\nNumero lineas analizadas: %d\n", nlines);
}
/* para compilar
flex lexico.l
cc lex.yy.c -o milex -lfl -lm
%%
/********
Para el lexico solo
void main(int argc,char **argv)
{
if (argc>1)
yyin=fopen(argv[1],"rt");
else
yyin=stdin;
yylex();
printf("\nNumero lineas analizadas: %d\n", nlines);
}
******/
/* para compilar
flex lexico.l
cc lex.yy.c -o milex -lfl -lm
*/LABORATORIO DE COMPILADORES
Fichero sintactico.y (Bison)
%{
/* Ejemplo para una pequeña calculadora que permite trabajar con números enteros y reales
con las operaciones básicas de suma, resta, producto, división y trigonométricas como el seno y
el coseno */
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
extern int yylex(void);
extern char *yytext;
extern int nlines;
extern FILE *yyin;
void yyerror(char *s);
%}
%union
{
float real;
}
%start Calculadora
%token <real> TKN_NUM
%token TKN_ASIGN
%token TKN_PTOCOMA
%token TKN_MULT
%token TKN_DIV
%token TKN_MAS
%token TKN_MENOS
%token TKN_PAA
%token TKN_PACLABORATORIO DE COMPILADORES
%token TKN_COS
%token TKN_SEN
%token <real> TKN_ID
%type Calculadora
%type <real> Expresion
%left TKN_MAS TKN_MENOS
%left TKN_MULT TKN_DIV
%%
/* para compilar
bison -d sintactico.y
flex lexico.l
cc lex.yy.c sintactico.tab.c -o analizador -lfl -lm
*/