Está en la página 1de 23

GUÍA DE ESTUDIO

INTRODUCCIÓN AL USO DE FLEX – BISON PARA LA RESOLUCIÓN DE TAREA ESPECIAL

Sintaxis y Semántica de Lenguajes

Dpto. de Ing. en Sistemas

SSIINNTTAAXXIISS YY SSEEMMÁÁNNTTIICCAA DDEE LLEENNGGUUAAJJEESS

Departamento de Ingeniería en Sistemas Universidad Tecnológica Nacional - Facultad Regional Mendoza

ÍNDICE

1.

INTRODUCCIÓN

3

2.

EL PROBLEMA

4

3.

FLEX

5

3.1

¿Qué es Flex?

5

3.2

¿Cómo se estructura un programa Flex?

6

3.2.1 Definiciones

 

6

3.2.2 Reglas

8

3.2.3 Código de Usuario

9

3.3

Ejemplo de código Flex

9

4.

BISON

11

4.1

¿Qué es BISON?

11

4.2

¿Cómo se estructura un programa BISON?

12

4.2.1 Declaraciones

 

12

4.2.2 Reglas de Producción de la Gramática

14

4.2.3 Código C adicional

4.2.3 Código C adicional 17

17

4.3

Ejemplo de código Bison

19

5.

INTEGRACIÓN FLEX + BISON

20

6.

INSTALACIÓN DE LAS HERRAMIENTA

22

7.

PASOS PARA COMPILAR NUESTRO CÓDIGO FLEX + BISON

23

SSIINNTTAAXXIISS YY SSEEMMÁÁNNTTIICCAA DDEE LLEENNGGUUAAJJEESS

Departamento de Ingeniería en Sistemas Universidad Tecnológica Nacional - Facultad Regional Mendoza

1. INTRODUCCIÓN

La presente Guía de Estudio, pretende orientar al alumno en el desarrollo de la

Tarea Especial planteada por la cátedra. La misma constituye una guía sobre los contenidos teórico-prácticos que el alumno debe comprender para poder abordar el problema (¿Qué debo saber?) y sobre el uso de las herramientas que debe manejar para poder resolverlo (¿Cómo?). No obstante, no debe esperarse de esta guía un tratamiento detallado de todos los contenidos necesarios para la resolución del problema. La misma pretende sólo organizar aspectos básicos para facilitar al alumno al abordaje inicial del problema.

Importante: El propósito de esta Guía, no es reemplazar la bibliografía y documentos sugeridos para resolver la Tarea Especial. NO ES UN RESÚMEN DE CONTENIDOS, NI UN TUTORIAL SOBRE FLEX o BISON. Su objetivo es orientar para el estudio e indicar los primeros pasos a dar en la búsqueda de la solución.

los primeros pas os a dar en la búsqueda de la solución. Se recomienda al alumno

Se recomienda al alumno leer de manera completa la Guía antes de comenzar a trabajar.

SSIINNTTAAXXIISS YY SSEEMMÁÁNNTTIICCAA DDEE LLEENNGGUUAAJJEESS

Departamento de Ingeniería en Sistemas Universidad Tecnológica Nacional - Facultad Regional Mendoza

2. EL PROBLEMA

Con independencia del tema o caso escogido, la Tarea Especial consiste en

desarrollar, para un lenguaje de programación en particular 1 (o un conjunto de estructuras de programación en un lenguaje particular) un Analizador Léxico (Scanner) y un Analizador Sintáctico (Parser) e integrarlos conformando un compilador para dicho Lenguaje. Para resolver este problema deberán utilizarse conjuntamente dos herramientas: Flex y Bison, para generar analizadores Léxicos y Sintácticos respectivamente.

Herramientas

Analizador Léxico (SCANNER)

FLEX

Herramientas Analizador Léxico (SCANNER) FLEX Analizador Sintáctico (PARSER) BISON Nota: Otras

Analizador Sintáctico (PARSER) BISON

Nota: Otras herramientas que podrían utilizarse para desarrollar Parsers y

Scanners son Lex, Yacc, etc.

1 En adelante y de modo simbólico, nos referiremos a este lenguaje particular como Lenguaje XX.

SSIINNTTAAXXIISS YY SSEEMMÁÁNNTTIICCAA DDEE LLEENNGGUUAAJJEESS

Departamento de Ingeniería en Sistemas Universidad Tecnológica Nacional - Facultad Regional Mendoza

3. FLEX

3.1 ¿QUÉ ES FLEX?

Flex es un generador de analizadores léxicos de propósito general.

Esta herramienta trabaja sobre un conjunto de expresiones regulares, que recibe como entrada. A partir de ellas, Flex genera un programa en C/C++ que implementa un Scanner para reconocer el lenguaje regular descripto por dichas expresiones. Luego, al compilar dicho código en C/C++ se obtiene un programa ejecutable. El aplicativo ejecutable busca concordancias entre un fichero de entrada (código fuente en el lenguaje que se esta analizando – Lenguaje XX) y las expresiones regulares que se definieron; y ejecuta acciones asociadas en consecuencia. La Figura 1, esquematiza el proceso de generación de un Scanner con Flex.

CODIGO FUENTE FLEX (miscanner.l) (Conjunto de expresiones regulares asociadas a una G3) COMPILADOR FLEX
CODIGO FUENTE FLEX (miscanner.l)
(Conjunto de expresiones regulares asociadas a una G3)
COMPILADOR FLEX

CODIGO FUENTE EN C - lex.yy.c (Programa que implementa el reconocimiento de cadenas y ejecuta acciones en consecuencia)

de cadenas y ejecuta acciones en consecuencia) COMPILADOR C (gcc) Código Fuente Lenguaje XX PROGRAMA

COMPILADOR C (gcc)

Código Fuente Lenguaje XX

consecuencia) COMPILADOR C (gcc) Código Fuente Lenguaje XX PROGRAMA EJECUTABLE (SCANNER) Figura 1 Resultados del
consecuencia) COMPILADOR C (gcc) Código Fuente Lenguaje XX PROGRAMA EJECUTABLE (SCANNER) Figura 1 Resultados del

PROGRAMA EJECUTABLE

(SCANNER)

Figura 1

Fuente Lenguaje XX PROGRAMA EJECUTABLE (SCANNER) Figura 1 Resultados del Análisis Sintaxis y Semántica de

Resultados del Análisis

3.2

¿CÓMO SE ESTRUCTURA UN PROGRAMA FLEX?

Cómo se dijo previamente, Flex recibe como entrada un conjunto de expresiones

regulares que describen los componentes léxicos de un lenguaje (Lenguaje XX). Esta

entrada (miscanner.l), debe proporcionarse en un archivo de texto que se ajuste a la siguiente sintaxis:

% {declaraciones en C %} definiciones %% reglas %% código de usuario

Figura 2

Como se observa en la Figura 2, un archivo de entrada en Flex está compuesto por

tres secciones, separadas por una línea %%: La sección de definiciones, la sección de

reglas y la sección de código de usuario.

3.2.1 Definiciones La sección de Definiciones

está constituida por una secuencia de parejas de la

forma nombre-definición, conocidas como definiciones de nombre.

donde:

%% nombre definición nombre definición

de nombre. donde: %% nombre definición nombre definición nombre : nombre del token definición: expresión regular

nombre: nombre del token definición: expresión regular (patrón)

Figura 3

Definiciones de nombre Pareja: nombre-definición

SSIINNTTAAXXIISS YY SSEEMMÁÁNNTTIICCAA DDEE LLEENNGGUUAAJJEESS

Departamento de Ingeniería en Sistemas Universidad Tecnológica Nacional - Facultad Regional Mendoza

Cada definición de nombre, permite asociar un nombre a una expresión regular.

El nombre, identifica un token que se está definiendo para el Lenguaje XX.

La expresión regular describe el patrón al que deberán ajustarse las palabras o Lexemas que concuerden con dicho token.

Luego, cada vez que se haga referencia al token utilizando {nombre}, la definición se expandirá a su expresión regular asociada.

Ejemplo:

DIGITO

[0-9]

ID

[a-z][a-z0-9]*

CARACTER

(\\[^\n]|[^"\\\n])

%%

Dadas estas definiciones de nombre, nuestro Scanner asociará cualquier número dígito (0,1,2,…,9) al token DIGITO, cualquier secuencia de una letra seguida cero o más letras o dígitos al token ID, y cualquier carácter ASCII empatará con el token CARÁCTER.

ier carácter ASCII empa tará con el token CARÁCTER. El “nombre” es un identificador que puede

El “nombre” es un identificador que puede comenzar con una letra o guión bajo, seguido por cero o más letras, dígitos, ‘_’, o ‘-’ (guión).

La definición comienza en el primer carácter que no sea un espacio en blanco siguiendo al nombre y continuando hasta el final de la línea. La sintaxis para escribir expresiones regulares en Flex puede consultarse en la sección 07. Patterns de Vern Paxon (1995) – Flex 2.5. Universidad de California 2 .

2 Tutorial de Flex provisto con la instalación de Flex ver.2.5.4a

SSIINNTTAAXXIISS YY SSEEMMÁÁNNTTIICCAA DDEE LLEENNGGUUAAJJEESS

Departamento de Ingeniería en Sistemas Universidad Tecnológica Nacional - Facultad Regional Mendoza

3.2.2 Reglas La sección de Reglas contiene una serie de declaraciones de la forma: patrón-

acción donde el patrón debe estar sin sangrar y la acción debe comenzar en la misma

línea.

%%

patrón1 {acción1}

patrón2 {acción2}

donde:

patrón: expresión regular

acción: código C con las acciones a ejecutar cuando se encuentra concordancia del patrón con el texto de entrada.

Figura 4

concordancia del patrón co n el texto de entrada. Figura 4 El patrón es una expresión

El patrón es una expresión regular que busca concordancias en el fichero de entrada del Lenguaje XX; y la acción es código escrito en C que se ejecutará cuando una

declaración en el archivo de entrada en Lenguaje XX concuerde con el patrón asociado.

Ejemplo

%%

{DIGITO}+

{printf(“Número: %s”, yytext);}

\"{CARACTER}*\"

{printf("Cadena: %s",yytext);}

En la primera regla del ejemplo, el patrón empareja con cualquier secuencia de 1 o más

dígitos; y la acción indica que se imprima el contenido de la variable yytext.

En la segunda regla, el patrón emparejará con una cadena conformada por 0 o más

caracteres, aceptando como CARÁCTER a aquellos que se definieron en la sección de

definiciones de nombre.

SSIINNTTAAXXIISS YY SSEEMMÁÁNNTTIICCAA DDEE LLEENNGGUUAAJJEESS

Departamento de Ingeniería en Sistemas Universidad Tecnológica Nacional - Facultad Regional Mendoza

Variables yytext y yyleng

Cuando se halla concordancia entre una declaración en el archivo de entrada en

Lenguaje XX y un patrón, entonces el texto de entrada queda apuntado por el puntero

global a char yytext; y la longitud de esta cadena se guarda en la variable global yylen.

Luego, se ejecuta la acción asociada al patrón emparejado.

Para más información sobre la forma de describir las reglas patrón-acción Ver las

secciones 0.8 How the input is matched y 09. Actions de: Vern Paxon (1995) – Flex 2.5.

Universidad de California.

3.2.3 Código de Usuario La sección de Código de Usuario es opcional y puede incluir funciones en C que llaman

al Scanner o son llamadas por él (por ejemplo: desde las acciones incluidas en la sección

de reglas). El código que aquí se incluya se copia literalmente en el archivo ‘lex.yy.c’

incluya se copi a literalmente en el archivo ‘lex.yy.c’ generado por Flex al compilar las decla

generado por Flex al compilar las declaraciones incluidas en miscanner.l (Ver Figura 1).

3.3 EJEMPLO DE CÓDIGO FLEX

El siguiente ejemplo en Flex, describe un Scanner que aceptará constantes enteras,

identificadores, operadores de adición (‘+’), operadores de asignación (‘:=’) y paréntesis

como componentes léxicos de un lenguaje algebraico.

%{

#include <stdio.h> #include "instrucciones.tab.h" %}

([ \t])+

separador

letra

[a-zA-Z]

digito

[0-9]

identificador {letra}({letra}|{digito})* constEntera {digito}({digito})* %%

SSIINNTTAAXXIISS YY SSEEMMÁÁNNTTIICCAA DDEE LLEENNGGUUAAJJEESS

Departamento de Ingeniería en Sistemas Universidad Tecnológica Nacional - Facultad Regional Mendoza

{separador}

{constEntera}

{/* omitir */}

{return (CONSTENTERA);}

":="

{return (OPAS);}

"+"

{return (MAS);}

{identificador} {return (IDENTIFICADOR);}

"("

{return (APAR);}

")"

{return (CPAR);}

\n

{return (NL);}

.

ECHO;

%%

(CPAR);} \n {return (NL);} . ECHO; %% Sintaxis y Semántica de Lenguajes: resolución problemas de

SSIINNTTAAXXIISS YY SSEEMMÁÁNNTTIICCAA DDEE LLEENNGGUUAAJJEESS

Departamento de Ingeniería en Sistemas Universidad Tecnológica Nacional - Facultad Regional Mendoza

4. BISON

4.1 ¿QUÉ ES BISON?

Bison es un generador de analizadores sintácticos de propósito general, que

convierte una descripción gramatical para una gramática independiente del contexto LALR(1) en un programa en C que analice esa gramática (Parser).

Un fuente de Bison (normalmente un fichero con extensión .y) describe una gramática independiente del contexto (G2). El ejecutable que se genera indica si un fichero de entrada dado pertenece o no al lenguaje descripto por esa gramática (Lenguaje XX). La Figura 5 esquematiza el proceso de construcción de un Parser con Bison.

CODIGO FUENTE BISON (miparser.y) (Conjunto de reglas gramaticales G2) COMPILADOR BISON
CODIGO FUENTE BISON (miparser.y)
(Conjunto de reglas gramaticales G2)
COMPILADOR BISON

CODIGO FUENTE EN C (miparser.tab.c) (Programa que implementa el reconocimiento de las estructuras sintácticas y ejecuta acciones en consecuencia – implementa la semántica del Lenguaje)

en consecuencia – im plementa la semántica del Lenguaje) COMPILADOR C (gcc) Código Fuente Lenguaje XX

COMPILADOR C (gcc)

Código Fuente Lenguaje XX

del Lenguaje) COMPILADOR C (gcc) Código Fuente Lenguaje XX PROGRAMA EJECUTABLE ( PARSER ) Figura 5
del Lenguaje) COMPILADOR C (gcc) Código Fuente Lenguaje XX PROGRAMA EJECUTABLE ( PARSER ) Figura 5

PROGRAMA EJECUTABLE

(PARSER)

Figura 5

Fuente Lenguaje XX PROGRAMA EJECUTABLE ( PARSER ) Figura 5 Resultados del Análisis Sintaxis y Semántica

Resultados del Análisis

SSIINNTTAAXXIISS YY SSEEMMÁÁNNTTIICCAA DDEE LLEENNGGUUAAJJEESS

Departamento de Ingeniería en Sistemas Universidad Tecnológica Nacional - Facultad Regional Mendoza

4.2 ¿CÓMO SE ESTRUCTURA UN PROGRAMA BISON?

Como dijimos, Bison recibe como entrada un conjunto de reglas de producción que

describen una gramática independiente del contexto (G2). Dicha gramática define las

categorías sintácticas del lenguaje que se está definiendo (Lenguaje XX). Esta entrada

(miparser.y), debe proporcionarse en un archivo de texto, ajustándose a la siguiente estructura:

% {declaraciones en C %} Declaraciones Bison %% Reglas de producción de la gramática %% Código C adicional

Figura 6

de la gramática %% Código C adicional Figura 6 Como se observa en la Figura 6

Como se observa en la Figura 6, un archivo de entrada en Bison está compuesto por

tres secciones, separadas por una línea %%: La primera sección incluye declaraciones

en lenguaje C y declaraciones Bison. La segunda incluye las reglas de producción propiamente dichas. Finalmente, la tercera sección incluye código en C adicional. Las

dos primeras secciones son obligatorias aunque pueden ir vacías.

4.2.1 Declaraciones Las declaraciones en C pueden definir tipos y variables utilizadas luego en las

acciones 3 . Pueden también incluir comandos del preprocesador, tales como la directiva de inclusión #include para incluir archivos de cabecera que realicen cualquiera de estas cosas.

3 En general, cuando la presente guía utiliza el término “acciones”, referencia al código C que deberá ejecutarse como acción semántica vinculada al reconocimiento de un Token (en Flex) o de una categoría sintáctica (en Bison).

SSIINNTTAAXXIISS YY SSEEMMÁÁNNTTIICCAA DDEE LLEENNGGUUAAJJEESS

Departamento de Ingeniería en Sistemas Universidad Tecnológica Nacional - Facultad Regional Mendoza

Ejemplo

%{

#define YYSTYPE int

#include <math.h>

char *cadena;

int errores = 0;

%}

Las declaraciones de Bison enuncian los nombres de los símbolos terminales y no terminales de la gramática. También se puede describir aquí la precedencia de operadores y los tipos de datos de algunos símbolos (Valores Semánticos).

Todos los nombres de tokens 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.

el tipo de dato a utilizar para los valores semánticos. Símbolos terminales tokens (que obtenemos del

Símbolos terminales tokens (que obtenemos del Analizador Léxico). Por convención se escriben en mayúsculas.

Símbolos no terminales: se definen luego en las reglas de producción. Por convención se escriben en minúsculas.

Figura 7

La forma básica de declarar un nombre de token (símbolo terminal) es:

%token nombre1 nombre2 nombre3

De forma alternativa, puede utilizar %left, %right, o %nonassoc en lugar de

%token, si desea especificar precedencia y asociatividad.

SSIINNTTAAXXIISS YY SSEEMMÁÁNNTTIICCAA DDEE LLEENNGGUUAAJJEESS

Departamento de Ingeniería en Sistemas Universidad Tecnológica Nacional - Facultad Regional Mendoza

Ejemplo

%%

/* Declaraciones de BISON */

%token ENTERO

%token DIGITO %token INICIO_PROGRAMA

%token FIN_PROGRAMA

-> token declarado

%left '-' '+‘ %left '*' '/'

-> token de caracter

En este ejemplo, el Analizador Léxico (Scanner) reconocerá previamente los tokens declarados: ENTERO, DIGITO, INICIO_PROGRAMA y FIN_PROGRAMA y se los proporcionará al Parser que estamos definiendo. En las declaraciones Bison, sólo se está indicando que el Parser deberá aceptar esos tokens.

está indicando que el Parser deberá aceptar esos tokens. Los operadores aritméticos se están definiendo en

Los operadores aritméticos se están definiendo en este ejemplo como símbolos terminales de la gramática (token de carácter) en dónde el producto y la división tienen mayor prioridad que la resta y la adición (La precedencia es inversa al orden de las líneas en la declaración. Los operadores incluidos en la última línea declarada tienen mayor precedencia que los declarados en líneas anteriores.). La asociatividad se está definiendo, mediante la declaración %left, de izquierda a derecha.

4.2.2 Reglas de Producción de la Gramática

Las reglas gramaticales son las producciones de la gramática. Además se puede asociar a cada regla, un conjunto de acciones, código en C, que se ejecutan cuando el Parser encuentra concordancias con la regla correspondiente.

SSIINNTTAAXXIISS YY SSEEMMÁÁNNTTIICCAA DDEE LLEENNGGUUAAJJEESS

Departamento de Ingeniería en Sistemas Universidad Tecnológica Nacional - Facultad Regional Mendoza

Una regla gramatical de Bison tiene la siguiente forma general:

resultado: componentes-regla1

| componentes-regla2

donde:

;

{sentencias en C}

{sentencias en C}

resultado: no terminal a la izquierda de la producción.

componentes-regla: secuencia de terminales y no terminales que definen una regla.

sentencias en C: acciones que se ejecutan cuando hay correspondencia con una regla (Es opcional usarlo – Asignan valor semántico a la regla).

Figura 8

usarlo – Asignan valor semántico a la regla). Figura 8 La primera regla de producción definida

La primera regla de producción definida constituye el axioma de la gramática.

Si para una regla no se definen componentes del lado derecho de la regla de producción, se entiende que la gramática acepta la cadena vacía (λ).

Ejemplo

%%

/* Gramática */

programa: INICIO_PROGRAMA exps FIN_PROGRAMA

exps: exp exps

;

| exp

exp: ENTERO

| exp exp '+'

| exp exp '-'

| exp exp '*'

| exp exp '/'

{ $$ = $1;}

{ $$ = $1 + $2;}

- $2;}

{ $$ = $1

{ $$ = $1 * $2;}

{ $$ = $1 / $2; }

SSIINNTTAAXXIISS YY SSEEMMÁÁNNTTIICCAA DDEE LLEENNGGUUAAJJEESS

Departamento de Ingeniería en Sistemas Universidad Tecnológica Nacional - Facultad Regional Mendoza

;

En este ejemplo, el axioma de la gramática es el no terminal programa. Un programa se define como un conjunto de expresiones agrupadas entre los tokens INICIO_PROGRAMA y FIN_PROGRAMA. Se debe tener presente que estos tokens deberán ser previamente reconocidos por el Analizador Léxico y proporcionados al Parser. Un conjunto de expresiones (exps), se define luego mediante una regla recursiva a derecha como una expresión (exp) seguida de un conjunto de expresiones (exps) ó simplemente como una expresión (exp). Finalmente se define una expresión (exp) como un ENTERO ó como una expresión binaria de suma, resta, producto o cociente en notación postfija. De esta forma, el ejemplo define la gramática de un lenguaje de expresiones algebraicas en notación postfija (o polaca inversa).

Asociado a cada derivación de exp, se enuncia una acción (código en C) que define la semántica asociada a dicha producción. Es decir, la acción que debe ejecutarse cuando el Parser utiliza dicha producción en el reconocimiento de una entrada.

dicha producción en el reconocimiento de una entrada. La variable $$ guarda el valor asociado al

La variable $$ guarda el valor asociado al no terminal que se está definiendo. La variable $1 guarda el valor asociado al primer componente del lado derecho de la producción, la variable $2 guarda el valor asociado al segundo componente del lado derecho y así sucesivamente. Se debe recordar que Bison genera un Parser LALR(1) que construye el árbol de análisis sintáctico desde las hojas hacia la raíz (Bottom-Up) mediante operaciones de desplazamiento-reducción (Shift-Reduce).

Así, en nuestro ejemplo, para la entrada 5 + 3, las derivaciones serían:

SSIINNTTAAXXIISS YY SSEEMMÁÁNNTTIICCAA DDEE LLEENNGGUUAAJJEESS

Departamento de Ingeniería en Sistemas Universidad Tecnológica Nacional - Facultad Regional Mendoza

Tecnológica Nacional - Facultad Regional Mendoza Figura 9 4.2.3 Código C adicional Se incluyen en esta

Figura 9

4.2.3 Código C adicional Se incluyen en esta sección declaraciones o funciones en C que adicionalmente se requieran (Por ejemplo: aquellas que son llamadas desde las acciones semánticas definidas en las reglas de producción de la gramática).

Como mínimo debe incluirse la función main() para lanzar la ejecución del

Analizador. Esta función debe invocar a la función yyparse() en la cuál se implementa

el Parser propiamente dicho.

Otras funciones que pueden incluirse son yyerror() para el tratamiento de errores

sintácticos; y yywrap() en donde se incluye el código que desee ejecutarse al finalizar

el proceso de Análisis.

SSIINNTTAAXXIISS YY SSEEMMÁÁNNTTIICCAA DDEE LLEENNGGUUAAJJEESS

Departamento de Ingeniería en Sistemas Universidad Tecnológica Nacional - Facultad Regional Mendoza

La función yyparse()

El código fuente Bison se convierte, al compilarlo, en una función en C llamada yyparse. Esta función, implementa el Analizador; es decir: lee tokens, ejecuta acciones, y por último retorna cuando se completó el análisis del código fuente en Lenguaje XX o cuando se encontró un Error de tipo sintáctico del que no puede recuperarse. El valor devuelto por yyparse es 0 si el análisis tuvo éxito ó 1 si el análisis falló.

La función yyerror()

Esta función es llamada por yyparse() cuando se detecta un error de sintaxis. Si se define esta función, se esta definiendo qué acciones debe ejecutar el Parser cuando se produce un error sintáctico.

Ejemplo

%% int main(char** argv) { yyin = fopen(argv[0],"rt"); yyparse();

}

argv) { yyin = fopen(argv[0],"rt"); yyparse(); } yyerror (char *s) { printf ("%s\n", s); ++errores;

yyerror (char *s) { printf ("%s\n", s); ++errores;

}

int yywrap() { return 1; }

La variable yyin es un puntero global al archivo de entrada (Código fuente en Lenguaje XX). Por defecto yyin siempre está asociada a stdin (dispositivo de entrada estándar, típicamente el teclado). En el ejemplo, yyin se asocia al archivo de texto provisto como primer argumento en la llamada a main().

SSIINNTTAAXXIISS YY SSEEMMÁÁNNTTIICCAA DDEE LLEENNGGUUAAJJEESS

Departamento de Ingeniería en Sistemas Universidad Tecnológica Nacional - Facultad Regional Mendoza

Cuando la función yyparse() detecta un error sintáctico, llama a yyerror() pasando como argumento la cadena con el mensaje de error. En el ejemplo se muestra en pantalla dicho mensaje y se incrementa en uno un contador de errores.

4.3 EJEMPLO DE CÓDIGO BISON

El siguiente ejemplo en Bison, describe un Parser que aceptará una lista de instrucciones de asignación. En cada instrucción una expresión algebraica de adición se asigna a un identificador.

%{ #include <stdio.h> %} %token IDENTIFICADOR OPAS CONSTENTERA NL MAS APAR CPAR %start instrucciones %% instrucciones : instrucciones instruccion | instruccion

;

instrucciones : instrucciones instruccion | instruccion ; instruccion : IDENTIFICADOR OPAS expresion NL ; expresion :

instruccion : IDENTIFICADOR OPAS expresion NL

;

expresion : termino

| expresion MAS termino

;

termino : IDENTIFICADOR

 

|

CONSTENTERA

|

APAR expresion CPAR

;

%% int yyerror (char *s) { printf ("%s\n", s);

}

main()

{

yyparse();

}

SSIINNTTAAXXIISS YY SSEEMMÁÁNNTTIICCAA DDEE LLEENNGGUUAAJJEESS

Departamento de Ingeniería en Sistemas Universidad Tecnológica Nacional - Facultad Regional Mendoza

5. INTEGRACIÓN FLEX + BISON

Utilizando Bison junto a Flex, se pueden construir compiladores de lenguajes.

El programa Flex, una vez compilado, implementa la función yylex() encargada de

implementar el Scanner. Esta función, es llamada luego (cuando se compila el

programa Bison), por la función yyparse() para obtener los tokens.

Veamos a continuación el ejemplo completo de integración Flex + Bison.

Ejemplo Completo

Código Fuente del Analizador Léxico (Scanner) en Flex

%{ #include <stdio.h>

/* y.tab.h contiene las definiciones de todos los ‘%tokens’ que aparecen el fuente Bison. De esta forma se vinculan Flex y Bison*/

#include "y.tab.h"

%}

separador

([ \t])+

#include "y.tab.h" %} separador ([ \t])+ letra [a-zA-Z] digito [0-9] identificador {letra}(

letra

[a-zA-Z]

digito

[0-9]

identificador {letra}({letra}|{digito})* constEntera {digito}({digito})* %%

{separador}

{/* omitir */}

/*se indica al Parser el reconocimiento de cada token*/

{constEntera}

{return (CONSTENTERA);}

":="

{return (OPAS);}

"+"

{return (MAS);}

{identificador} {return (IDENTIFICADOR);}

"("

{return (APAR);}

")"

{return (CPAR);}

\n

{return (NL);}

.

ECHO;

%%

SSIINNTTAAXXIISS YY SSEEMMÁÁNNTTIICCAA DDEE LLEENNGGUUAAJJEESS

Departamento de Ingeniería en Sistemas Universidad Tecnológica Nacional - Facultad Regional Mendoza

Código Fuente del Analizador Sintáctico (Parser) en Bison

%{

/* Fichero instrucciones.y */

#include <stdio.h> %}

/* Se declaran los tokens que reconocerá el Scanner*/

%token IDENTIFICADOR OPAS CONSTENTERA NL MAS APAR CPAR

/* Se indica que el axioma es instrucciones - esta línea podría obviarse*/

%start instrucciones %%

/* Se define el conjunto de reglas de producción de la gramática*/

instrucciones : instrucciones instruccion | instruccion

;

instruccion : IDENTIFICADOR OPAS expresion NL

;

expresion : termino

| expresion MAS termino

;

expresion NL ; expresion : termino | expresion MAS termino ; termino : IDENTIFICADOR | |

termino : IDENTIFICADOR

|

|

;

CONSTENTERA

APAR expresion CPAR

%%

int yyerror (char *s) /* Llamada por yyparse ante un error */

{

printf ("%s\n", s); /* Esta implementación por defecto valdrá */ /* Si no se crea esta función, habrá que enlazar con –ly en el momento de compilar para usar una implementación por defecto */

}

main()

{

/* Acciones a ejecutar antes del análisis */

yyparse();

/* Acciones a ejecutar después del análisis */

}

SSIINNTTAAXXIISS YY SSEEMMÁÁNNTTIICCAA DDEE LLEENNGGUUAAJJEESS

Departamento de Ingeniería en Sistemas Universidad Tecnológica Nacional - Facultad Regional Mendoza

6. INSTALACIÓN DE LAS HERRAMIENTA

En esta sección se enuncian brevemente los pasos a seguir para instalar Flex y

Bison en nuestra computadora.

1. Ejecutar el programa de instalación de Flex

2. Ejecutar el programa de instalación de Bison

de Flex 2. Ejecutar el programa de instalación de Bison 3. Agregar en la variable de
de Flex 2. Ejecutar el programa de instalación de Bison 3. Agregar en la variable de

3. Agregar en la variable de entorno Path, la ruta a las carpetas donde se encuentran los programas ejecutables de Flex , Bison y gcc

encuentran los programas ejecutables de Flex , Bison y gcc Path = %SystemRoot%\system32; C:\Archivos de

Path = %SystemRoot%\system32; C:\Archivos de programa\GnuWin32\bin;C:\Dev-Cpp\bin

Importante: Se supone que se ha instalado previamente Dev C++ ó simplemente

el compilador gcc "GNU Compiler Collection".

SSIINNTTAAXXIISS YY SSEEMMÁÁNNTTIICCAA DDEE LLEENNGGUUAAJJEESS

Departamento de Ingeniería en Sistemas Universidad Tecnológica Nacional - Facultad Regional Mendoza

7. PASOS PARA COMPILAR NUESTRO CÓDIGO FLEX + BISON

Una vez instaladas las herramientas y escrito el código fuente del Scanner en Flex y el

Parser en Bison para el Lenguaje XX, se deberá compilar de acuerdo a los siguientes pasos:

1. Compilar el código fuente Bison

bison –d fuente.y

2. Compilar el código fuente Flex

flex fuente.l

3. Compilar el código C generado para obtener el ejecutable

gcc –o fuente fuente.tab.c lex.yy.c -lm

el ejecutable gcc –o fuente fuente.tab.c lex.yy.c -lm Importante: Puede utilizarse cualquier procesador de

Importante: Puede utilizarse cualquier procesador de textos para escribir los

archivos de código Flex y Bison (fuente.l y fuente.y). Una sugerencia es utilizar el editor de GoldParser.