Sie sind auf Seite 1von 34

rad

or

UNIVERSIDAD NACIONAL DEL ALTIPLANO

FACULTAD

DE I NGENIERA

M ECNICA E LCTRICA E LECTRNICA


Departamento de Ingeniera de Sistemas

S ISTEMAS

MANUAL UNIVERSITARIO

HERRAMIENTAS PARA CONSTRUCCIN DE COMPILADORES

Autor: : Mg. Oliver Amadeo Vilca Huayta

Bor

Profesor de la Facultad de Ingeniera FIMEES.

R Todos los Derechos Reservados.


Hecho en LATEX 2

Puno - Per
Octubre - 2015

rad
or

ndice

.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.

5
6
6
6
6
7
9
10
11
11
12
14
15
16

2. El generador YACC
2.1. Introduccin . . . . . . . . . . . . . . . . . . . . . . . .
2.2. Instalacin . . . . . . . . . . . . . . . . . . . . . . . . .
2.2.1. Acciones Semnticas . . . . . . . . . . . . . . .
2.2.2. El Archivo de Entrada . . . . . . . . . . . . . .
2.3. Compilar el Cdigo de Entrada para el YACC Bison
2.4. Una calculadora . . . . . . . . . . . . . . . . . . . . . .
2.5. Con gramtica ambigua . . . . . . . . . . . . . . . . .
2.6. Bison genera cdigo ANSI . . . . . . . . . . . . . . . .
2.7. Calculadora con funciones . . . . . . . . . . . . . . . .
2.8. Ejercicios propuestos . . . . . . . . . . . . . . . . . . .

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

18
19
19
19
20
21
21
23
25
27
31

Bor

1. El generador de analizadores lxicos LEX


1.1. El LEX . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.2. Uso . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.3. Instalacin . . . . . . . . . . . . . . . . . . . . . . . . .
1.3.1. Instalacin en Linux . . . . . . . . . . . . . . .
1.3.2. Instalacin en Microsoft Windows . . . . . . .
1.4. Formato del archivo de entrada del LEX . . . . . . . .
1.5. Compilar el Cdigo de Entrada para el FLEX . . . . .
1.6. Mi primer ejemplo en el LEX . . . . . . . . . . . . . .
1.7. Conjunto extendido de expresiones regulares del Lex
1.8. Analizador lxico para un sublenguaje de Pascal . . .
1.9. Analizador lxico para el sublenguaje C . . . . . . . .
1.10.Ejercicios resueltos . . . . . . . . . . . . . . . . . . . .
1.11.Ejercicios Propuestos . . . . . . . . . . . . . . . . . . .

Bibliografa

33

rad
or

Cdigo Fuente

Cuenta nmero de caracteres y lneas: ejm1.y . . . . . . . .


Analizador Lxico del Sublenguaje Pascal: ejm2.y . . . . .
Funcin main() para leer desde archivo . . . . . . . . . . .
Analizador lxico para el sublenguaje C y sus comentarios
Reduce espacios en blanco redundates . . . . . . . . . . . .
Contador de lneas palabras y caracteres . . . . . . . . . .
Entrada para el Bisn: ejm1.y . . . . . . . . . . . . . . . . .
Entrada para el Bisn: ejm2.y . . . . . . . . . . . . . . . . .
Calculadora - Notacin Infija: ejm3.y . . . . . . . . . . . . .
Calculadora - Avanzado: ejm4.y . . . . . . . . . . . . . . . .
Calculadora - Avanzado: calc.h . . . . . . . . . . . . . . . .

Bor

1.1.
1.2.
1.3.
1.4.
1.5.
1.6.
2.1.
2.2.
2.3.
2.4.
2.5.

.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.

11
12
13
14
15
16
22
23
25
27
30

Pg. 3

rad
or

Oliver A. Vilca H.

PRLOGO

En el mercado existe varios textos de compiladores, gran parte de ellos se limitan a las
teoras y principios de diseo compiladores con algunos ejemplos prcticos. Otros se orientan
en organizar cdigo fuente de un compilador para un lenguaje de programacin real un sublenguaje reducido. Un compilador escrito a mano (sin utilizar generadores) involucra muchas
lneas de cdigo y conocimiento terico para ser comprendido.

El objetivo la presente obra es explicar el uso de herramientas para la construccin de


compiladores, sin la exigencia de profusa teora que puede resultar abrumador para el estudiante, en particular cuando la construccin de un compilador es hecho a mano sin el uso de
un generador. Se inicia con ejemplos sencillos para un rpido entendimiento. Este enfoque no
pretende mensocabar la teora que debe prestarse en un curso de compiladores, mas bin, es
un complemento prctico. Tiene las siguientes ventajas:

Ahorro de tiempo en el diseo y construccin de un compilador.


No requiere extensa teora.

Bor

Acepta ms tipos de gramticas libres del contexto que los mtodos ms sencillos de construccin de compiladores.

La moyoria de generadores produce cdigo estndar (ejemplo ANSI C).

Por otro lado la gran mayoria de manuales de LEX y YACC estn escritas en ingles y no

existen, o al menos no hay versiones mnimas en espaol. Se recomienda al lector los manuales
que vienen con los instaladores del F lex y el Bison, que estn disponibles slo en ingles por lo

menos a la fecha de publicacin de la presente.

El Software que se utilizar es de distribucin libre. En particular se estudiarn dos he-

rramientas el Lex para el anlisis lxico y Y acc para el anlisis sintactico y semntico. Las

versiones que se pueden utilizar para Windows son el F lex y el Bison respectivamente, que
estn disponibles en internet.

Lenguaje de desarrollo de compiladores ser el lenguaje C/C++. El entorno de desarrollo

puede ser el Dev-C++ (ltima versin disponible en Internet) u otro que se disponga para el

Pg. 4

rad
or

Oliver A. Vilca H.

Windows, en caso de Unix o Linux puede utilizarse el gcc/g++. DevC++1 es un ambiente de


desarrollo integrado (IDE2 ) que utiliza la versin MinGW de C++.

Mayor informacin se pude encontrar en los libros citados en bibliografa: [Aho et al., 2006],
[Gries, 1971], [Wilhelm and Maurer, 1995],

[Lewis et al., 1976] [Louden, 1997] y [Lewis and Papadimitriou, 1981].

Para obtener buenos resultados, se considera que el estudiante previamente desarroll


cursos de programacin, y que esta familiarizado con los leguajes de programacin (C/C++).
Se asume tambin que desarroll cursos de estructura de datos y conceptos sobre teora de
lenguajes y autmatas, aspectos que se dan en un curso inicial de introduccin a la ciencia
de la computacin en el primer semestre. Incluso no es necesario conocer mucha teora de

Bor

compiladores.

DevC++ viene con todo lo que se necesita para compilar y enlazar en modo consola y programas GUI que se
ejecutan en Windows. Dev-C++ es Software de distribucin libre bajo la licencia GNU (General Public License).
2
Un IDE es un programa que le permite editar, compilar, y depurar programas en C++ sin usar el comando de
lnea.

rad
or

Captulo 1

El generador de analizadores lxicos

Bor

LEX

1.1. El LEX

Pg. 6

rad
or

Oliver A. Vilca H.

En esta seccin se explicar la implementacin de un analizador lxico utilizando un generador de cdigo, especficamente un generador de analizadores lxicos conocido como LEX.
Esto a partir de la descripcin de tokens de un lenguaje de programacin, por lo general expresiones regulares.

Las versiones ms populares de LEX se conoce como FLEX (Fast Lex en ingles), es una
herramienta para generar programas que efecta reconocimiento de patrones en texto. Hay
muchas aplicaciones para el Flex, incluido la escritura de compiladores en conjunto con el GNU
Bison. Se distribuye como parte del paquete compilador GNU que produce la Free Software
Foundation, y tambin se puede obtener gratuitamente en diferentes sitios de Internet. En los
lugares de descarga de los instaladores por lo general tambin se encuentran sus manuales en
ingles.

El Flex o Lex toma como entrada un archivo de texto (por lo general con extensin y) que
contiene expresiones regulares acompaado de acciones que se ejecutan cuando se iguale cada
expresin. Como salida crea un archivo con cdigo fuente generado en lenguaje C (tambin
hay generadores para otros lenguajes de programacin). En el cdigo fuente generado se crea
una funcin denominado yylex, que es la implementacin de la tabla de un autmata finito
determinista correspondiente a las expresiones regulares del archivo de entrada. El nombre
del archivo de salida del Lex es por lo general lex.yy.c lexyy.c, este se compila junto con un

Bor

programa principal para obtener el programa.

1.2. Uso

El programa se ejecuta en la lnea de comandos, y as debera ser ejecutado desde la lnea

de comandos (ventana DOS: command.com or cmd.exe).

1.3. Instalacin

1.3.1. Instalacin en Linux

La instalacin en Linux y en particular en el Ubuntu (versin 14.4) es sencilla y general

para versiones posteriores, se ingresa al Centro de Software de Ubuntu, luego se escribe


flex inmediatamente aparece en la primera coincidencia Un generador rpido de anlisis

Pg. 7

rad
or

Oliver A. Vilca H.

lxico como se muestra en la figura 1.1, finalmente se procede a instalar (debe tener conexin
a Internet). En otras versiones de Linux la instalacin debe ser similar, en caso contrario se
puede consultar su instalacin en Internet.

Figura 1.1: Flex disponible en el Centro de Software de Ubuntu.

1.3.2. Instalacin en Microsoft Windows

Requerimientos: Todos los paquetes GnuWin requieren MS Windows 2000 / XP / 2003 /


Vista / 2008 / 7 / 8.

Descargar el instalador del Flex de la direccin web de preferencia, en seguida instalarla.


Una versin del Flex de libre distribucin se encuentra en:

http://gnuwin32.sourceforge.net/packages/flex.htm, descargue el paquete completo (Complete package, except sources), luego siga las instrucciones. Tambin estan disponibles los c-

Bor

digos fuente .

Figura 1.2: Instalacin de Flex en Windows.

1.3.2.1.

Pg. 8

rad
or

Oliver A. Vilca H.
Agregar al path del sistema

Si se desea que Flex ( Bison) se integre al conjunto de variables del entorno (esto permite
llamar a Flex desde cualquier ubicacin en la lnea de comandos), debe configuar lo siguiente:
Ingresar a Propiedades del sistema. Seleccionar la pestaa Opciones avanzadas, la

Bor

ventana debera ser similar a la figura 1.3 (las figuras han sido capturadas desde Windows 8).

Figura 1.3: Formulario: Propiedades del sistema - Windows 8.

Luego ingrese a la opcin (botn) Variables de entorno y aparece una ventana como

ilustra en la figura 1.4.

Pg. 9

rad
or

Oliver A. Vilca H.

Figura 1.4: Formulario: Variables del sistema en Windows 8.

Luego, en la ventana Variables del sistema seleccione la variable path y edtelo seleccionando la opcin Editar como se observa la figura 1.4, En seguida aparece una ventana (ver
figura 1.5) donde sin borrar las direcciones desplazar el cursor al final y agregar un punto y
coma ; y finalmente pegar (o escribir) la direccin de los ejecutables (binarios) del Flex por

Bor

ejemplo: C:\Program Files (x86)\GnuWin32\bin.

Figura 1.5: Formulario: Editar la variable del sistema en Windows 8.

1.4. Formato del archivo de entrada del LEX


La entrada del LEX consiste en tres partes, separados por una lnea con % %.

Pg. 10

rad
or

Oliver A. Vilca H.
DEFINICIONES.
%%

REGLAS.
Definicin de la regla 1
Definicin de la regla 2
...
Definicin de la regla n
%%

CDIGO DEL USUARIO.

Cuadro 1.1: Archivo de entrada del LEX.

La parte de def iniciones contiene declaraciones de definiciones y declaraciones de condiciones de inicio.

1.5. Compilar el Cdigo de Entrada para el FLEX

Bor

Para compilar la entrada para el Flex, en la lnea de comandos se debe escribir:


$ flex -l ejm1.y
bien :

$ flex -l -o ejm1.c ejm1.y


Donde :

-l : Es la opcin para que no genere la directiva #line.


ejm1.y : Es el archivo de entrada para el Lex (ver la siguiente seccin).
-o ejm1.c : Es la opcin para indicar el archivo de salida, en el ejemplo es ejm1.c (nombre

del archivo de salida que sigue a la opcin -o)

En el primer ejemplo el nombre archivo de salida generado por defecto es lex.yy.c.

Oliver A. Vilca H.

Pg. 11

rad
or

1.6. Mi primer ejemplo en el LEX

Este analizador lxico cuenta el nmero de caracteres y nmero de lneas de su entrada


(retornos de carro). En la lnea siete se declara e inicializa dos variables num_l y num_c. Son
accesibles desde yylex() y main(). Hay dos reglas, una que empareja con nueva lnea (\n) e incrementa los contadores de lnea y caracteres, y otra que empareja cualquier caracter diferente
a nueva lnea (denotada por la expresin regular punto .).

Programa 1.1: Cuenta nmero de caracteres y lneas: ejm1.y

1
2

/* No se utiliza yywrap cuando lee EOF */


%option noyywrap

3
4

%{

// Variables para contar numero de lineas

// y numero de caracteres

int num_l = 0, num_c = 0;

%}

9
10

%%

11
12

\n

13

{ ++num_l; ++num_c; }

++num_c; // Si es un caracter incrementar el contador

14
15

%%

16
17

main()

18

19

Bor

yylex();

20
21

printf("\nNro de Lineas: %d, Nro de caracteres: %d\n", num_l,num_c);

Luego de haber generado el archivo de salida (codigo en C por ejemplo archivo lex.yy.c),

se prosigue con la compilacin y ejecucin, en ese momento escribir caracteres y tambin puede
presionar la tecla enter para obtener nuevas lneas, cuando se termina y desea obtener el conteo
de lneas y caracteres se debe presiona Control+D.

1.7. Conjunto extendido de expresiones regulares del Lex


En la entrada se especifica con la ayuda de un conjunto extendido de expresiones regulares

que utilizan los siguientes operadores entre los ms utilizados. Sean r y s expresiones regulares.

Pg. 12

rad
or

Oliver A. Vilca H.
x
.
[xy]

[xyz]

[w-z]

[x]
[A-Z]

r*
r+
r?

r{4}
r{2,5}
r{3,}
{XX}
rs

r|s

Representa el caracter x.
Cualquier caracter (byte) excepto nueva lnea.
Clase de caracteres, en este caso, el caracter x
y (pero no ambas).
Clase de caracteres, en este caso, el caracter
x, y z (uno slo).
Clase de caracteres con rango, en este caso,
cualquier letra desde la w hasta la z (estos
son: w,x,y, y z).
Cualquier caracter excepto x.
Complemento de una clase de caracteres, es
decir, cualquier caracter excepto los de la clase. En este caso, cualquier caracter excepto
una letra en maysculas.
Cero o ms repeticiones de r, donde r es una
expresin regular.
Una o ms repeticiones de r.
Cero o una repeticin de r, es decir, una r opcional.
Exctamente cuatro repeticiones de r.
De dos hasta cinco repeticiones r.
De tres a ms repeticiones de r.
La traduccin de XX de la seccin de deficiones.
La expresin regular r seguida de la expresin
regular s, denominado concatenacin.
Una r una s, unin.

Bor

Cuadro 1.2: Conjunto extendido de expresiones regulares del Lex.

1.8. Analizador lxico para un sublenguaje de Pascal


Este es un ejemplo ms elaborado sirve de inicio para la construccin de un analizador

lxico simple para un lenguaje reducido de Pascal (sublenguaje). Identifica diferentes tipos de
T okens.

Programa 1.2: Analizador Lxico del Sublenguaje Pascal: ejm2.y

/* Analizador Lxico para un Sub lenguaje del Pascal */

2
3
4

/* No se utiliza yywrap cuando EOF */


%option noyywrap

5
6

%{

#include <math.h>

%}

9
10

DIGIT [0-9]

11

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

12
13

%%

14
15

{DIGIT}+

Pg. 13

rad
or

Oliver A. Vilca H.

16

printf( "NUMERO: %s ( %d)\n", yytext,atoi( yytext ) );

17

18
19

{DIGIT}+"."{DIGIT}* {

20

printf( "FLOTANTE: %s ( %g)\n", yytext,atof( yytext ) );

21

22
23

if|then|begin|end|procedure|function {

24

printf( "PALABRA_RESERVADA: %s\n", yytext );

25

26
27

{ID} printf( "IDENTIFICADOR: %s\n", yytext );

28
29

"+"|"-"|"*"|"/" printf( "OPERADOR: %s\n", yytext );

30
31

"{"[^}\n]*"}"

/* Elimina comentarios */

32
33

[ \t\n]+

/* Elimina espacios en blanco, tabs y retornos de carro */

34
35

printf( "CARACTER_DESCONOCIDO: %s\n", yytext );

36
37

%%

38
39
40

main( argc, argv )

41

42

yyin = stdin;

43

yylex();

Bor
44

Si se requiere que analizador lxico cargue la entrada desde un archivo (dado como par-

metro para el ejecutable) puede modificar la funcin main como sigue:


Programa 1.3: Funcin main() para leer desde archivo

1
2

main(int argc,char** argv)


{

++argv, --argc; /* no considerar el nombre del programa */


if ( argc > 0 )

4
5

yyin = fopen( argv[0], "r" );

else

yyin = stdin;

8
9

yylex();

Los detalles de la funcin f open se puede encontrar en un manuales y textos de progra-

macin.

Oliver A. Vilca H.

Pg. 14

rad
or

1.9. Analizador lxico para el sublenguaje C

Ejemplo de un escaner del lenguaje C, en este caso se utiliza el Flex++ y un compilador de


C++ (por ejemplo el g++):

Programa 1.4: Analizador lxico para el sublenguaje C y sus comentarios

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

/*

/*

/*

Analizador Lxico para el sublenguaje C


- Elimina los comentarios del C.

*/
*/

Por: Oliver V. Basado en el manual Lex


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

%option noyywrap

%{

#include<iostream>

using namespace std;

10
11

int num_lineas = 0;
%}

12
13

cadena \"[^\n"]+\"

14

esptab [ \t]+

15

letra

16

digito [0-9]

17

id

18

num1

[-+]?{digito}+\.?([eE][-+]?{digito}+)?

19

num2

[-+]?{digito}*\.{digito}+([eE][-+]?{digito}+)?

20

numero {num1}|{num2}

[A-Za-z]

{letra}({letra}|{digito}|[_.\-])*

21
22

%%

23

{esptab} /* saltar espacios en blanco y tabs */

Bor

24
25
26

"/*" {

/* eliminar comentacion del C */

27

int c;

28

while((c = yyinput()) != 0)

29

30

if(c == \n)

31

++num_lineas;

32

else if(c == *)

33

34

if((c = yyinput()) == /)

35

break;

36

else

37

unput(c);

38

39
40

41
42

{numero}

cout<<"Numero "<<YYText()<<endl;

43
44

\n

num_lineas++;

45
46
47

if|while|for|int|float|char|main|printf|"{"|"}"|"("|")"|";" {
cout<<"Palabra reservada "<<YYText()<<endl; }

48
49

{id} cout<<"Identificador "<<YYText()<<endl;

Pg. 15

50
51

{cadena} cout<< "Cadena "<<YYText()<<endl;

52
53

"+"|"-"|"*"|"/"|" %"|"="

54
55

%%

58
59
60

int main( int argc, char** argv )


{

61

FlexLexer* aLexico = new yyFlexLexer;


while( aLexico->yylex() != 0 )

62
63

64
65

cout<<"Operador "<<YYText()<<endl;

. cout<< "Caracter desconocido "<<YYText()<<endl;

56
57

rad
or

Oliver A. Vilca H.

return 0;
}

Este archivo de entrada para el Flex++ se puede ejecutar de la siguiente manera: Flex++
e4.y. Esto generar el archivo lex.yy.cc, por lo tanto se puede generar el archivo ejecutable con:
g++ lex.yy.cc. Obtenindose finalmente el archivo requerido.

1.10. Ejercicios resueltos

1. Implemente una entrada para el Lex que copie archivos, sustituyendo cada secuencia no

Bor

nula de espacios en blanco por un solo espacio en blanco.

Programa 1.5: Reduce espacios en blanco redundates

/* Reduce espacios en blanco redundates */

2
3
4

/* No se utiliza yywrap cuando EOF */


%option noyywrap

5
6

%%

7
8

[ ]+

printf( " " );

/* Espacios en blanco */

10
11

printf( " %s", yytext );

12
13

%%

14
15

main( argc, argv )

16

17

yyin = stdin;

18
19

yylex();

Pg. 16

rad
or

Oliver A. Vilca H.

2. Los sistemas Unix (tambin los sistemas Unix-ish incluyendo Linux y los BSD) vienen
con un programa de conteo de palabras, el cual lee desde un archivo e informa del nmero
de lneas, palabras y caracteres que contiene dicho archivo. Flex nos permite escribir el
wc (word counter) en poco mas de una docena de lneas, que se muestra en la siguiente
entrada para el Lex/Flex:

Programa 1.6: Contador de lneas palabras y caracteres

1
2

/* Contador de lneas, palabras y caracteres (parecido al wc) */


/* Por: Oliver Vilca
*/

%{

int caracteres = 0;

int palabras

int lineas = 0;

%}

8
9

%%

10

= 0;

11

[a-zA-Z]+

{ palabras++; caracteres += strlen(yytext); }

12

\n

{ caracteres++; lineas++; }

13

{ caracteres++; }

14
15

%%

16
17
18

main(int argc, char **argv)


{

19

yylex();

20

printf(" %d %d %d\n", lineas, palabras, caracteres);


}

Bor

21

1.11. Ejercicios Propuestos

1. Implemente una entrada para el Lex (o Flex) que reconozca los Tokens para un sublenguaje del lenguaje Java.

2. Implemente una entrada para el Lex/Flex que reconozca los Tokens para un sublenguaje
del siguiente conjunto de lenguajes de propsito general:
(a) C

(b) C++
(c) C#

(d) Php

(e) Python
(f) Basic

(g) ML

(h) Scheme
(i) SQL

Pg. 17

rad
or

Oliver A. Vilca H.

(j) R (estadstico)

Debe eliminar los comentarios de lenguaje.

3. Implemente una entrada para el Lex (o Flex) que reconozca Tokens para un sublenguaje
del siguiente conjunto de lenguajes orientados a la matemtica:
(a) Mxima
(b) Octave
(c) Maple
(d) Matemtica
(e) MathLab

Bor

Debe eliminar los comentarios de lenguaje.

rad
or

Captulo 2

Bor

El generador YACC

Oliver A. Vilca H.

Pg. 19

rad
or

2.1. Introduccin

En esta seccin se muestra como un generador de analizadores sictcticos puede facilitar


la construccin de un compilador (el FRONT END). Se utilizara el YACC (Yet Another Compiler
Compiler) o herramienta afin, los cuales estan disponibles en Internet. En 1970 fue creado la
primera versin del YACC por S. C. Johnson.

Bison es un generador de propsito general que convierte una descripcin de una gramtica (gramtica libre del contexto LALR(1)) en un programa en C, para analizar la gramtica.
Con esta herramienta es posible desarrollar una amplia gama de analizadores de lenguajes,
desde una calculadora simple de escritorio hasta un complejo lenguaje de programacin. Se
requiere que el usuario domine el lenguaje C.

2.2. Instalacin

La instalacin en Linux y en particular en el Ubuntu (versin 14.4) es sencilla y general


para versiones posteriores, se ingresa al Centro de Software de Ubuntu, luego se escribe Bison inmediatamente aparece en la primera coincidencia YACC-compatible parser generator,
finalmente se procede a instalar (debe tener conexin a Internet). En otras versiones de Linux
la instalacin debe ser similar, en caso contrario se puede consultar su instalacin en Internet.

Bor

Tambin puede instalar Bison++.

La instalacin de Bison en Windows es similar a la instalacin del Flex. Para ubicar el

sitio de decarga basta con consultar al amigo Google con la palabra Bison for Windows.

2.2.1. Acciones Semnticas

Con la finalidad de que las aplicaciones sean ms tiles, se debe hacer ms que nicamen-

te analizar la entrada. Se puede producir una salida basada en la entrada. De este modo las
gramticas pueden ir acompaadas de acciones (semnticas). Por ejemplo la siguiente gramtica libre del contexto puede implicar acciones semnticas.
exp exp + exp

En muchas aplicaciones, el propsito de las acciones es calcular valores semnticos. Por

ejemplo, si se tiene una regla que indica que una expresin es la suma de dos subexpresiones,
cada una de estas expresiones tienen un valor semntico. El valor del no terminal del lado

Pg. 20

rad
or

Oliver A. Vilca H.

izquierdo de la regla se representa por $$, y el lado derecho de la regla se representa considerando el orden de izquierda a derecha por $1, $2, ..., $n, donde n es el nmero de terminales y no
terminales del lado derecho de la regla. Por ejemplo para gramtica en cuestin:
exp : exp + exp { $$ = $1 + $3; }
|{z}

|{z}

|{z}

$$

$1

$3

En este caso $2 representara el segundo terminal + del lado derecho de la regla, el cual no
es necesario y no tendra sentido que tenga valor (porque no le corresponde un valor semntico).

2.2.2. El Archivo de Entrada

La entrada principal con la cual trabaja son las gramticas libres del contexto (reglas).
Los archivos de entrada comunmente tienen la extensin .y. El formato general del archivo de
entrada para un analizador es como sigue:

%{
CDIGO INICIAL.
%}

DECLARACIONES.
Definicin de Tokens y tipos

Bor

%%
GRAMTICAS.
Definicin de la regla 1
Definicin de la regla 2
...
Definicin de la regla n
%%

RUTINAS DEL USUARIO.

Cuadro 2.1: Archivo de entrada del YACC.

En la seccin CDIGO INICIAL se puede: incluir librerias (con la directiva #include),

definir macros, declarar funciones y variables del analizador. Esta seccin inicia y termina con
los delimitadores %{ y %}. Se puede tener ms que una de estas secciones, intercaladas con la

seccin de DECLARACIONES

En la seccin de DECLARACIONES se declara los tokens (componentes lxicos). Se define

smbolos terminales y no terminales, se especifica precedencia, etc. En aplicaciones simples no


es necesario declaraciones.

Pg. 21

rad
or

Oliver A. Vilca H.

En la seccin de GRAMTICAS, se especifican una o ms gramticas (formateadas para


el YACC).

La ltima seccin de RUTINAS DEL USUARIO, el cdigo fuente de esta parte se copia
sin cambio al final del cdigo generado del YACC. Por ejemplo las definiciones de las funciones
yylex y yyerror. Debido a que las funciones del C requieren ser declaradas antes de ser
utilizadas, puede ser necesario que las declare en la seccin de CDIGO INICIAL.

Debido a que el YACC contiene varios macros e identificadores que inician con yy e YY,
no es buena idea utilizar estos prefijos. El archivo fuente puede contener comentarios que se
utilizan en C.

2.3. Compilar el Cdigo de Entrada para el YACC Bison


Para compilar la entrada para el Bison, en la lnea de comandos se debe escribir:
$ bison ejm2.y
bien :

Bor

$ bison -l ejm2.y -o ejm2.c


Donde :

-l : Es la opcin para que no genere la directiva #line ejm2.y : Es el archivo de entrada

para el Bison (ver la siguiente seccin).

-o : Es la opcin para indicar el archivo de salida. ejm2.c : Es el nombre del archivo de

salida (requerido por la opcin -o)

2.4. Una calculadora

Se construir un acalculadora que lee expresiones aritmticas, las evalua e imprime su va-

lor numrico. Se inicia con la siguiente GLC para expresiones aritmticas, la gramtica puede
ser recursiva por la izquierda:

Pg. 22

rad
or

Oliver A. Vilca H.

E E + T | E T | T

(2.1)

T F | T /F | F

(2.2)

numero | (E)

(2.3)

Programa 2.1: Entrada para el Bisn: ejm1.y

/* COMPILADORES : Laboratorio N 1

/* GENERADORES DE ANALIZADORES

/* Calculadora - Expresiones Aritmeticas.

/* Entrada : Gramtica no Ambigua

*/
*/

/* Por: Ing. Oliver A. VIlca Huayta

*/

%{

*/
*/

/* Tipo de dato para los valores semnticos */


#define YYSTYPE double

#include <math.h>

10

#include <stdio.h>

11

int yylex (void);

12
13

void yyerror (char const *);


%}

14
15
16

/* Declaraciones */
%token NUMERO

17
18

20

% % /* Gramtica */
Entrada: /* epsilon */ | Entrada Linea
;

21

Linea: \n

19

/* Muestra el resultado */
| Exp \n { printf ("
Resultado: %g \n", $1); }

Bor

22
23
24

Exp:

Exp + Term { $$ = $1 + $3; }

25

| Exp - Term { $$ = $1 - $3; }

26

| Term { $$ = $1; }

27
28
29

Term: Term * Fact { $$ = $1 * $3; }


| Term / Fact { $$ = $1 / $3; }

30

| Fact { $$ = $1; }

31
32

Fact: NUMERO { $$ = $1; }

33

| ( Exp ) { $$ = $2; }

34

35
36
37

% % /* Funciones del Usuario*/


int yylex (void)

38

39

int c;

40

/* Saltar espacios en blanco y tabulaciones */


while ((c = getchar ()) == || c == \t) ;

41
42
43
44

/* Procesa nmeros */
if (c == . || isdigit (c))

45

46

ungetc (c, stdin);

47

scanf (" %lf", &yylval);

48

return NUMERO;

49

50
51

/* Retorna Fin de entrada */


if (c == EOF) return 0;

52
53
54

/* Retorna un solo caracter */


return c;

55
56

57
58

Pg. 23

rad
or

Oliver A. Vilca H.

59

/* Llamado por yyparse cuando ocurre un error */


void yyerror (char const *s)

60

61
62

fprintf (stderr, " %s\n", s);


}

63
64

int main (void)

65

66
67

return yyparse ();


}

El archivo de salida se compila (por ejemplo con el gcc u otro) y se obtiene un ejecutable el
cual es la calculadora requerida:

1+2+3+8/2
Resultado: 10

Bor

1+2*3

Resultado: 7

(1+2)*3

Resultado: 9

10/3

Resultado: 3.33333

5.5+3.5*2-5/2

Resultado: 10

^D

Fin de entrada presionando: Ctrl+D

2.5. Con gramtica ambigua

Programa 2.2: Entrada para el Bisn: ejm2.y

/* COMPILADORES : Laboratorio N 2

/* GENERADORES DE ANALIZADORES

3
4
5

*/

*/
/* Calculadora - Notacin Infija. */
/* Gramtica Ambigua => precedencia de operadores */
%{

Pg. 24

rad
or

Oliver A. Vilca H.

/* Tipo de dato para los valores semnticos */


#define YYSTYPE double

#include <math.h>

#include <stdio.h>

10

int yylex (void);

11
12

void yyerror (char const *);


%}

13
14
15

/* Declaraciones */
%token NUM

16

%left - +

17

%left * /
%left NEG /* negacin -- menos unario */
%right ^ /* exponenciacin */

18
19
20
21
22

% % /* Gramtica */
input: /* epsilon */

23

| input line

24
25

;
line: \n

26

| exp \n { printf ("\t %.10g\n", $1); }

27
28

;
exp: NUM { $$ = $1; }

29

| exp + exp { $$ = $1 + $3; }

30

| exp - exp { $$ = $1 - $3; }

31
32

| exp * exp { $$ = $1 * $3; }


| exp / exp { $$ = $1 / $3; }

33

| - exp %prec NEG { $$ = -$2; }

34

| exp ^ exp { $$ = pow ($1, $3); }

35

| ( exp ) { $$ = $2; }

36

37
38

% % /* Funciones del Usuario*/


int yylex (void)

40

Bor
39

41

int c;

42
43

/* Saltar espacios en blanco */


while ((c = getchar ()) == || c == \t)

44

45
46
47

/* Procesa nmeros */
if (c == . || isdigit (c))

48

49

ungetc (c, stdin);

50

scanf (" %lf", &yylval);

51

return NUM;

52

53
54

/* Retorna Fin de entrada */


if (c == EOF) return 0;

55
56
57

/* Retorna un solo caracter */


return c;

58
59

60
61
62

/* Llamado por yyparse cuando ocurre un error */


void yyerror (char const *s)

63

64
65

fprintf (stderr, " %s\n", s);


}

66
67

int main (void)

68

69
70

Pg. 25

rad
or

Oliver A. Vilca H.

return yyparse ();


}

Nota: La instruccin: gcc ejm2.c -o ejm2. Puede generar el siguiente mensaje de error:
referencia a pow sin definir. Esto es debido a que la funcin pow es parte de la librera math.h.
Por lo tanto, La invocacin del compilador debe ser como sigue:

gcc ejm2.c -o ejm2 -lm

Dnde -lm es la instruccin para enlazar su cdigo con la libreria math.h.

2.6. Bison genera cdigo ANSI

Utilizando el Turbo C++ versin 3.0 para Windows (o el Borland C++ versin 3.1) se debe considerar que el cdigo generado respeta las normas ANSI, por lo que se debe indicar al

Bor

compilador del C/C++ que debe compilar cdigo ANSI, esto se puede indicar de la siguiente
manera:

Ir al men Option. Seleccionar el submen Compiler. Ir al men Source, aqu se debe

habilitar la casilla ANSI.

Finalmente se puede compilar y generar el ejecutable.


Programa 2.3: Calculadora - Notacin Infija: ejm3.y

/* COMPILADORES : Laboratorio N 3

/* GENERADORES DE ANALIZADORES

/* Calculadora - Notacin Infija.

/* Con Recuperacin de Errores

*/
*/
*/
*/

5
6

%{

7
8

/* Tipo de dato para los valores semnticos */


#define YYSTYPE double

#include <math.h>

10

#include <stdio.h>

11

int yylex (void);

12
13

void yyerror (char const *);


%}

14
15
16

/* Declaraciones */
%token NUM

17

%left - +

18

%left * /

19

%left NEG

20

/* negacin -- menos unario */


%right ^ /* exponenciacin */

21
22
23

% % /* Gramtica */
input: /* epsilon */

24

| input line

25
26

Pg. 26

rad
or

Oliver A. Vilca H.

;
line: \n

27

| exp \n { printf ("\t %.10g\n", $1); }

28

| error \n { yyerrok; }

29
30

;
exp: NUM { $$ = $1; }

31

| exp + exp { $$ = $1 + $3; }

32

| exp - exp { $$ = $1 - $3; }

33
34

| exp * exp { $$ = $1 * $3; }


| exp / exp

35

36

if ($3)

37

$$ = $1 / $3;

38

else {

39

$$ = 1;

40

fprintf (stderr, " %d. %d- %d. %d: division por cero",

41

@3.first_line, @3.first_column,

42

@3.last_line, @3.last_column);

43

}
}

45

| - exp %prec NEG { $$ = -$2; }

46

| exp ^ exp { $$ = pow ($1, $3); }

47

| ( exp ) { $$ = $2; }

48

Bor

44

49
50
51

% % /* Funciones del Usuario*/


int yylex (void)

52

53

int c;

54

/* Saltar espacios en blanco */


while ((c = getchar ()) == || c == \t)

55
56

++yylloc.last_column;

57
58

60

/* Actualiza el lugar donde se inicia la lectura. */


/* first_line y first_column: posicion de inicio (token). */
/* last_line y last_column: para conteo de filas-columnas. */

61

yylloc.first_line = yylloc.last_line;

62

yylloc.first_column = yylloc.last_column;

59

63
64
65

/* Procesar nmeros. */
if (isdigit (c))

66

67

yylval = c - 0;

68

++yylloc.last_column;

69

while (isdigit (c = getchar ()))

70

71

++yylloc.last_column;

72

Pg. 27

rad
or

Oliver A. Vilca H.

yylval = yylval * 10 + c - 0;

73

74

ungetc (c, stdin);

75

return NUM;

76

77
78
79

/* Retorna Fin de entrada */


if (c == EOF)

80

return 0;

81
82
83

/* Retorna un solo caracter, y actualiza localizacin. */


if (c == \n)

84

85

++yylloc.last_line;

86

yylloc.last_column = 0;

87

88

else

89

++yylloc.last_column;

90
91

return c;
}

92
93
94

/* Llamado por yyparse cuando ocurre un error */


void yyerror (char const *s)

95

96
97

fprintf (stderr, " %s\n", s);


}

98
99
100

int main (void)


{

101

yylloc.first_line = yylloc.last_line = 1;

102

yylloc.first_column = yylloc.last_column = 0;

103

return yyparse ();


}

Bor

104

2.7. Calculadora con funciones

Se examin ejemplos bsicos, es hora de pasar a ejemplos ms elaborados.


Programa 2.4: Calculadora - Avanzado: ejm4.y

/* COMPILADORES : Laboratorio N 4

/* GENERADORES DE ANALIZADORES

3
4

/* Calculadora - Notacin Infija. */


/* Multifunciones Avanzado */

%{

*/

*/

#include <math.h> /* Para funciones matemticas, cos(), etc. */


#include <stdio.h>

7
8

#include "calc.h" /* Contiene definiciones de symrec. */


int yylex (void);

10

void yyerror (char const *);

11

%}

12

%union {

13
14
15
16

Pg. 28

rad
or

Oliver A. Vilca H.

double val; /* Para retornar nmeros. */


symrec *tptr; /* Para retornar punteros de la tabla de smbolos. */
}

17

%token <val> NUM /* Nmero de doble presicin. */


%token <tptr> VAR FNCT /* Variables y Funciones. */

18

%type <val> exp

19

%right =

20

%left - +

21

%left * /
%left NEG /* negacin -- menos unario */
%right ^ /* exponenciacin */

22
23
24
25
26

% % /* Gramtica */

27
28
29

input: /* empty */
| input line

30
31

;
line: \n

32

| exp \n { printf ("\t %.10g\n", $1); }

33

| error \n { yyerrok; }

34

35
36

exp: NUM { $$ = $1; }

37

| VAR { $$ = $1->value.var; }

38

| VAR = exp { $$ = $3; $1->value.var = $3; }

39

| FNCT ( exp ) { $$ = (*($1->value.fnctptr))($3); }

40

| exp + exp { $$ = $1 + $3; }

41

| exp - exp { $$ = $1 - $3; }

42
43

| exp * exp { $$ = $1 * $3; }


| exp / exp

44

45

Bor

if ($3)

46

$$ = $1 / $3;

47

else {

48

$$ = 1;

49

fprintf (stderr, " %d. %d- %d. %d: division por cero",

50

@3.first_line, @3.first_column,

51

@3.last_line, @3.last_column);

52

53

54

| - exp %prec NEG { $$ = -$2; }

55

| exp ^ exp { $$ = pow ($1, $3); }

56

| ( exp ) { $$ = $2; }

57
58

/* Fin de la Gramtica. */

59
60
61

% % /* Funciones del Usuario. */


int yylex (void)

62

63

int c;

64

/* Saltar espacios en blanco */


while ((c = getchar ()) == || c == \t)

65
66

++yylloc.last_column;

67
68
69

/* Retorna Fin de entrada */


if (c == EOF)

70

return 0;

71

Pg. 29

rad
or

Oliver A. Vilca H.

72

yylloc.first_line = yylloc.last_line;

73

yylloc.first_column = yylloc.last_column;

74
75

77

/* Procesar nmeros. */
/* Si inicia con un dgito => NUM */
if (c == . || isdigit (c))

78

76

79

yylval.val = c - 0;

80

++yylloc.last_column;

81

while (isdigit (c = getchar ()))

82

83

++yylloc.last_column;

84

yylval.val = yylval.val * 10 + c - 0;

85

86

ungetc (c, stdin);

87
88

return NUM;
}

89
90
91

/* Si inicia con un smbolo-alfabeto => leer el nombre. */


if (isalpha (c))

92

93

95

symrec *s;
static char *symbuf = 0;
static int length = 0;

96

int i;

97

/* Inicializar el tamao del buffer (nombre de smbolo = 40). */


if (length == 0)

94

98
99

length = 40, symbuf = (char *)malloc (length + 1);

100

i = 0;

102

do

103

Bor

101

104
105

/* Si buffer lleno => aumentar el tamao. */


if (i == length)

106

107

length *= 2;
symbuf = (char *) realloc(symbuf,length+1);

108
109

110

/* Agregar el carcter al buffer. */


symbuf[i++] = c;

111
112

/* Obtener otro caracter. */


c = getchar ();

113
114

while (isalnum (c));

115
116

ungetc (c, stdin);

117

symbuf[i] = \0;

118
119

s = getsym (symbuf);

120

if (s == 0)

121

s = putsym (symbuf, VAR);

122

yylval.tptr = s;

123
124

return s->type;

125
126
127

/* Retorna un solo caracter, y actualiza localizacin. */


if (c == \n)

128

129

++yylloc.last_line;

130

yylloc.last_column = 0;

131

132

else

133

++yylloc.last_column;

134
135

/* Algn otro caracter es token por si mismo. */


return c;

136
137

138
139

Pg. 30

rad
or

Oliver A. Vilca H.

140

/* Llamado por yyparse cuando ocurre un error. */


void yyerror (char const *s)

141

142
143

fprintf (stderr, " %s\n", s);


}

144
145

struct init

146

147

char const *fname;


double (*fnct) (double);

148
149

};

150
151

struct init const arith_fncts[] =

152

153

"FXY", sin,

154

"cos", cos,

155

"atan", atan,

156

"ln", log,

157

"exp", exp,

158

"sqrt", sqrt,

159

0, 0

160

};

Bor

161
162
163

/* La tabla de smbolos: a chain of struct symrec. */


symrec *sym_table;

164
165
166

/* Insertar funciones aritmticas en la tabla. */


void init_table (void)

167

168

int i;

169
170

symrec *ptr;
for (i = 0; arith_fncts[i].fname != 0; i++)

171

172

ptr = putsym (arith_fncts[i].fname, FNCT);

173

ptr->value.fnctptr = arith_fncts[i].fnct;

174
175

176
177

int main (void)

178

179

yylloc.first_line = yylloc.last_line = 1;

180

yylloc.first_column = yylloc.last_column = 0;

181

init_table ();

182
183

return yyparse ();

Pg. 31

rad
or

Oliver A. Vilca H.

Programa 2.5: Calculadora - Avanzado: calc.h

typedef double (*func_t) (double);

2
3
4

/* Tipo de datos para la lista de smbolos. */


struct symrec

char *name; /* Nombre del smbolo. */


int type; /* Tipo de smbolo: VAR o FNCT. */
union

10

double var; /* Valor de VAR */


func_t fnctptr; /* Valor de FNCT */

11
12

} value;

13
14

struct symrec *next; /* Puntero al siguiente smbolo. */


};

15
16

typedef struct symrec symrec;

17

/* La tabla de smbolos: lista de struct symrec. */


extern symrec *sym_table;

18
19
20

symrec *putsym (char const *, int);


symrec *getsym (char const *);

21
22
23
24

symrec *putsym (char const *sym_name, int sym_type)


{

25

27

symrec *ptr;
ptr = (symrec *) malloc (sizeof (symrec));
ptr->name = (char *) malloc (strlen (sym_name) + 1);

28

strcpy (ptr->name,sym_name);

29

ptr->type = sym_type;

30
31

ptr->value.var = 0; /* Asignar 0. */
ptr->next = (struct symrec *)sym_table;

32

sym_table = ptr;

Bor

26

33
34

return ptr;

35
36
37

symrec *getsym (char const *sym_name)


{

38

symrec *ptr;
for (ptr = sym_table; ptr != (symrec *) 0;
ptr = (symrec *)ptr->next)

39
40
41

if (strcmp (ptr->name,sym_name) == 0)

42

return ptr;

43
44

return 0;

2.8. Ejercicios propuestos

1. Implemente un programa en YACC que indique si una cadena de ingresada es palndrome o no. Palndrome es una palabra sentencia que es igual a su inverso. Ejemplo:
sagas, alla, radar, madamimadam (Madam Im Adam, posiblemente lo primero

Pg. 32

rad
or

Oliver A. Vilca H.

que Eva oy en el Jardn del Eden).

2. Aadir otras nuevas funciones de la librera math.h a la lista de inicializacin.

3. Agregar el operador mod ( %). Es binario: a % b donde a y b son nmeros enteros, y el


resultado es el resto (o residuo) de la divisinde a entre b.

4. Disee una calculadora que soporte las siguientes caractersticas adicionales:


(a) La sentencia Exit, para salir de la calculadora.

(b) La sentencia Print, para imprimir, por ejemplo Print(2+3) que debe imprimir 5 y

Bor

Print X que debe imprimir el valor de la bariable X.

rad
or

Bibliografa

[Aho et al., 2006] Aho, A. V., Lam, M. S., Sethi, R., and Ullman, J. D. (2006). Compilers: Principles, Techniques, and Tools (2nd Edition). Addison Wesley.
[Gries, 1971] Gries, D. (1971). Compiler Construction for Digital Computers. Wiley, New York.
[Lewis and Papadimitriou, 1981] Lewis, H. R. and Papadimitriou, C. H. (1981). Elements of the
Theory of Computation. Prentice-Hall, New York.
[Lewis et al., 1976] Lewis, P. M., Rosenkrantz, D. J., and Stearns, R. E. (1976). Compiler Design
Theory. Addison-Wesley, Reading, MA.
[Louden, 1997] Louden, K. C. (1997). Compiler Construction: Principles and Practice. PWS
Publishing Co., Boston, MA, USA.

Bor

[Wilhelm and Maurer, 1995] Wilhelm, R. and Maurer, D. (1995). Compiler Design. AddisonWesley, Wokingham, England, Reading, MA.

Das könnte Ihnen auch gefallen