You are on page 1of 25

1

Apndice 3

Introduccin a la estructura y operacin


de analizadores lxicos.
A3.1. Estructura de un lenguaje de programacin.
Un lenguaje est basado en un vocabulario, o lxico, el que est compuesto por palabras, o
ms precisamente por smbolos.
Ciertas secuencias de palabras son reconocidas como sintcticamente bien formadas o correctas.
La gramtica o sintaxis o estructura del lenguaje queda descrita por una serie de reglas o
frmulas que definen si una secuencia de smbolos es una sentencia correcta. La estructura de
las sentencias establece el significado o semntica de sta.
Ejemplo.
<sentencia> ::= <sujeto><predicado>
<sujeto>::= rboles|arbustos
<predicado>::=grandes|pequeos
La semntica de las lneas anteriores es la siguiente:
Una sentencia est formada por un sujeto seguido de un predicado. Un sujeto es la palabra
rboles o arbustos. Un predicado consiste de una sola palabra, la cual puede ser grandes o
pequeos.
El lenguaje anterior, genera cuatro sentencias correctas.
Una sentencia bien formada puede ser derivada a partir del smbolo de partida, <sentencia> en
el caso del ejemplo, por la repetida aplicacin de reglas de reemplazo o producciones (reglas
sintcticas). Se denominan smbolos no terminales, o categoras sintcticas, a las sentencias
definidas entre parntesis de ngulo, que figuran a la derecha en las producciones; los smbolos
terminales (vocabulario) figuran a la derecha de las producciones y se representan a s mismos.
Estas reglas para definir lenguajes se denomina formulismo de Backus-Nauer. Los parntesis
de ngulo, y los smbolos ::= (que se lee: puede ser reemplazado por) y | (que se lee como: o
excluyente) son denominados metasmbolos.
Las producciones son libres al contexto, si en stas figura a la izquierda un solo smbolo no
terminal S, que puede ser reemplazado en funcin de smbolos terminales s, no importando el
contexto en el que ocurra S.

Profesor Leopoldo Silva Bijit

20-01-2010

Estructuras de Datos y Algoritmos

La produccin: ASB::=AsB define que S puede ser reemplazado por s, siempre que ocurra
entre A y B; por lo cual es sensible al contexto.
El uso de la recursin al definir producciones, permite generar un infinito nmero de sentencias
a partir de un nmero finito de producciones.
S::=aA
A::=b|cA
La categora A, est definida en trminos de s misma. En el ejemplo, los smbolos terminales se
representan con minsculas, los no terminales con maysculas.
A partir de S, se generan: ab, acb, accb, acccb, ..

A3.2. Analizador lxico. (parser)


El diseo de un reconocedor de sentencias correctas est basado en encontrar algoritmos que
sean de complejidad n, donde n es el largo de la sentencia a analizar. Es decir en cada paso del
algoritmo se depende solamente del estado actual y del siguiente smbolo; y no es necesario
volver atrs. Obviamente la estructura del lenguaje debe permitir esta forma de anlisis.
El mtodo jerrquico o top-down, de anlisis de sentencias (parsing) consiste en reconstruir el
rbol de derivacin desde el smbolo de partida hasta la sentencia final.
Ejemplo: Se da la sentencia: rboles grandes, y se desea determinar si pertenece al lenguaje
definido en un ejemplo anterior.
Se parte del smbolo de partida, <sentencia> y se lee el primer smbolo del texto que se desea
analizar: rboles.
Se reemplaza <sentencia> por <sujeto><predicado>, se ve si es posible reemplazar <sujeto>; se
verifica que puede ser reemplazado por rboles. En este momento, puede avanzarse al siguiente
smbolo de la secuencia de entrada, que en el caso del ejemplo es grandes. Al mismo tiempo se
avanza al siguiente de los smbolos no terminales. Ahora la tarea restante es verificar si
<predicado> puede generar el smbolo grandes. Como esto es as, se avanza en la secuencia de
entrada, y se observa que no quedan ms smbolos. Con lo cual el anlisis termina reconociendo
cmo vlida la construccin.
Cada paso del anlisis est basado solamente en el siguiente smbolo de la secuencia de
smbolos no terminales que se est analizando.
Puede ilustrarse el algoritmo mediante la siguiente tabla. La columna a la izquierda representa la
tarea de reconocimiento pendiente y la de la derecha la secuencia de smbolos terminales que
an no se leen. Se desea validar accb como perteneciente a S.
S::=aA
A::=b|cA

Profesor Leopoldo Silva Bijit

20-01-2010

Introduccin a analizadores lxicos


S
accb
aA
accb
A
ccb
cA
ccb
A
cb
cA
cb
A
b
b
b
-

Inicio. Se lee a
Se reemplaza S. Se reconoce a.
Se acepta a. Se lee c
Se reemplaza A. Se reconoce c.
Se acepta c. Se lee c.
Se reemplaza A. Se reconoce c.
Se acepta c. Se lee b.
Se reemplaza A. Se reconoce b.
Se acepta la frase como correcta.

Figura A3.1. Anlisis sin volver atrs.


No es necesario volver atrs, y el anlisis est basado en la lectura de un smbolo terminal por
adelantado.
Para que esto sea posible, los smbolos iniciales de smbolos no terminales alternativos que
figuran a la derecha en las producciones, deben ser diferentes.
El siguiente ejemplo ilustra reglas que no cumplen el principio anterior. Ya que A y B (smbolos
no terminales alternativos en S), tienen iguales smbolos iniciales, ambos son x.
S::=A|B
A::=xA|y
B::=xB|z
Si se desea analizar la secuencia xxxz, se tendr la dificultad que no es posible discernir (slo
leyendo el primer smbolo por adelantado) si S debe ser reemplazado por A o por B. Si se
eligiera al azar, reemplazar S por A, luego de unos pasos el anlisis falla, y se debera volver
atrs, e intentar reemplazar por B, en lugar de A, y volver a intentar.
Las reglas:
S::=C|xS
C::=y|z
Son equivalentes a las anteriores y cumplen el principio anterior.
En variadas construcciones de los lenguajes se acepta smbolos opcionales. Es decir la
alternativa entre un smbolo terminal y una secuencia nula de smbolos. Ejemplo de esto es la
asignacin: x= +a; el smbolo + es opcional.
Sea nula la secuencia nula.
Entonces las reglas:
S::=Ax
A::=x|nula
Si se desea reconocer x, si luego de reemplazar S por Ax, se intenta reemplazar A por x el
anlisis falla, se logra xx en la derivacin. Con lo cual puede reconocerse el primer x, y luego al

Profesor Leopoldo Silva Bijit

20-01-2010

Estructuras de Datos y Algoritmos

intentar leer el prximo, como no quedan smbolos no terminales que leer y queda pendiente un
x que derivar, se concluye que el anlisis fall. Lo que se debi realizar era reemplazar A por
nula.
Para evitar la vuelta atrs en el reconocimiento, se impone una regla adicional para las
producciones que generen la secuencia nula:
Para una secuencia A que genera la secuencia nula, los smbolos iniciales que genera A deben
se disjuntos con los smbolos que siguen a cualquier secuencia generada por A.
En el ejemplo anterior, S dice que la sentencia A tiene a x como smbolo siguiente. Y la
produccin que define A, indica que el primer smbolo que puede generar A es tambin x.
Como los iniciales generados por A son iguales a los siguientes a A, se viola la regla anterior.
La repeticin de construcciones, que tambin es muy frecuente en los lenguajes, suele definirse
empleando recursin.
Por ejemplo la repeticin de una o ms veces del elemento B, puede anotarse:
A::= B|AB
Pero el primero de B y el primero de AB no es el vaco, y no cumple la primera de las reglas.
Si se cambia la definicin de A por:
A::=nula|AB
A genera: nula, B, BB, BBB, y se tendr que el primero de A y el siguiente a A sern B,
violando la segunda regla.
Lo cual permite visualizar que no puede emplearse recursin por la izquierda.
La recursin por la derecha, cumple las reglas anteriores:
A::=nula|BA
Esta ltima produccin tambin genera la repeticin de cero, una o ms veces del elemento B.
La frecuencia de construcciones repetitivas que generen la secuencia nula lleva a definir los
siguiente metasmbolos:
A::={B}
Que genera: nula, B, BB, BBB,
Esta definicin slo simplifica la notacin, pero an es preciso revisar que se cumpla la segunda
regla, para emplear algoritmos basados en leer un smbolo por adelantado y sin volver atrs.

A3.3. Reglas de anlisis.


Debido a lo abstracto del formalismo de Backus-Nauer se ha desarrollado una representacin
grfica de las reglas. En los grafos sintticos se emplean los siguientes smbolos:

Profesor Leopoldo Silva Bijit

20-01-2010

Introduccin a analizadores lxicos

Smbolo terminal.
x

Figura A3.2. Smbolo terminal.


Corresponde a un reconocimiento del smbolo terminal x, en la produccin de la que forma
parte, y al avanzar en la lectura del siguiente smbolo en la secuencia de entrada.
Es importante asociar estos grafos a elementos de un lenguaje de programacin que
implementar el reconocedor sintctico basado en diagramas.
Para el elemento terminal, puede traducirse:
if (ch== x) ch=lee(stream); else error(n);
Donde stream es el descriptor del archivo que contiene el texto que ser analizado. La funcin
lee, trae el siguiente carcter. En este nivel los caracteres individuales del texto se consideran
smbolos terminales. La funcin de error, debera generar un mensaje asociado a la deteccin de
una sentencia mal formada.
Smbolo no terminal.
B

Figura A3.3. Smbolo no terminal.


Cuando aparece este diagrama en una produccin, corresponde a la activacin de un
reconocedor del smbolo no terminal B.
En el reconocedor, se activa una invocacin al procedimiento reconocedor de B.
B( );
Alternativa.
La produccin: A::=B1|B2||Bn
Se representa:

Profesor Leopoldo Silva Bijit

20-01-2010

Estructuras de Datos y Algoritmos

B1
A
B2
Bn

Figura A3.4. Alternativa.


En el reconocedor, puede implementarse, mediante la sentencia switch.
switch (ch){
case L1: B1(); break;
case L2: B2(); break;
.
case Ln: Bn(); break;
}
Donde los Li seran los conjuntos de los smbolos iniciales de los Bi.
Es preferible describir la alternativa, explicitando el primer smbolo:

b1

B1

b2

B2

bn

Bn

Figura A3.5. Alternativa, con primer smbolo explcito.


switch (ch){
case b1: {ch=lee(stream); B1(); break;}
case b2: {ch=lee(stream); B2(); break;}
.
case bn: {ch=lee(stream); Bn(); break;}
}
Concatenacin.
La produccin: A::=B1B2Bn
Se representa:

Profesor Leopoldo Silva Bijit

20-01-2010

Introduccin a analizadores lxicos

A
B1

B2

Bn

Figura A3.6. Concatenacin.


En el reconocedor: {B1( ); B2( );Bn( );}
Repeticin.
La produccin: A::={B}
Se representa:
A
B

Figura A3.7. Repeticin.


En el reconocedor, se implementa:
while( esta_en(L, ch) ) B( );
Donde la funcin esta_en retorna verdadero si ch pertenece al conjunto L de los primeros
caracteres generados por B.
Es preferible, representar, el forma explcita el primer carcter:
A
B

Figura A3.8. Repeticin, con primer smbolo explcito.


De este modo el reconocedor se implementa:
while( ch==b) {ch=lee(stream); B( );}
La repeticin, de a lo menos una vez, puede implementarse con una sentencia while.
Cada uno de los bloques anteriores puede ser reemplazado por alguna de las construcciones
anteriores. Por ejemplo: la repeticin puede ser una serie de acciones concatenadas.

Profesor Leopoldo Silva Bijit

20-01-2010

Estructuras de Datos y Algoritmos

Resumen.
Para una gramtica dada, pueden construirse grafos sintcticos a partir de las producciones
descritas en BNF, y viceversa.
Y de stas derivar el cdigo del reconocedor.
Los grafos deben cumplir las siguientes dos reglas, para que se puedan recorrer leyendo un
smbolo por adelantado y sin volver atrs.
Los primeros smbolos en las alternativas deben ser diferentes. De tal forma que la
bifurcacin solo pueda escogerse observando el siguiente smbolo de esa rama.
Si un grafo reconocedor de una sentencia A, puede generar la secuencia nula, debe
rotularse con todos los smbolos que puedan seguir a A. Ya que ingresar al lazo puede
afectar el reconocimiento de lo que viene a continuacin.
Una vez definido el lenguaje a reconocer, mediante sus grafos, debe verificarse el cumplimiento
de las dos reglas anteriores. Un sistema de grafos que cumplan las reglas anteriores se denomina
determinista y puede ser recorrido sin volver atrs y solo leyendo un smbolo por adelantado.
Esta restriccin no es una limitante en los casos prcticos.

Ejemplo A3.1. Reconocedor simple.


Generar reconocedor para sentencias que cumplan las siguientes producciones.
A::=x|(B)
B::=AC
C::={+A}
Algunas sentencias vlidas, de este lenguaje, son:
x, (x), (x+x), ((x)), (x+x+x+x+x+x+x+x+x),.
Pueden plantearse los grafos sintcticos para cada una de las producciones. Posteriormente, es
posible reducir los grafos, mediante substituciones. Luego de esto se obtiene:
A

)
A

Figura A3.9. Grafo del reconocedor.


El grafo permite revisar el cumplimiento de las dos reglas.
La bifurcacin tiene interseccin vaca de los primeros elementos de cada rama:
{ ( } { x } =

Profesor Leopoldo Silva Bijit

20-01-2010

Introduccin a analizadores lxicos

La produccin que genera la secuencia nula tiene interseccin vaca del primer elemento de la
repeticin y del smbolo que sigue a esa repeticin:
{ + } { ) } =
El siguiente programa implementa un reconocedor, para sentencias que cumplan la sintaxis
descrita por el grafo. Lo ms importante es notar que el cdigo para el reconocedor de
sentencias A, puede ser escrito a partir del diagrama anterior. Cada conjunto de reglas da origen
a un programa determinado. Se han agregado las funciones que abren y leen el archivo con el
texto que ser analizado, para ilustrar los detalles del entorno. Se da trmino a las sentencias del
archivo con un asterisco, en la primera posicin de una lnea. Cada vez que termina el anlisis
de una sentencia avisa si la encontr correcta.
Se emplea una variable global ch, para disminuir el nmero de argumentos. Se destaca que debe
leerse un smbolo por adelantado, antes de invocar al reconocedor.
Si el descriptor del archivo se deja como variable global, pueden disminuirse an ms los
argumentos de las funciones, simplificndolas.
#include <stdio.h>
void error(int e)
{ printf("Error %d\n", e);}
char lee(FILE *stream)
{ return( fgetc(stream)); }
char ch='\0';
void A(FILE *stream)
{
if (ch=='x') ch=lee(stream);
else
if (ch=='(' )
{ ch=lee(stream); A(stream);
while(ch=='+') {ch=lee(stream); A(stream);}
if ( ch==')' ) ch=lee(stream); else error(1);
}
else
error(2);
}
/* Analiza archivo de texto */
int parser(void)
{
FILE *stream;
if ((stream = fopen("inparser.txt", "r")) == NULL) {
fprintf(stderr, "No pudo abrir archivo de entrada.\n");
return 1;
Profesor Leopoldo Silva Bijit

20-01-2010

10

Estructuras de Datos y Algoritmos


}
/* lee hasta encontrar el final del stream */
while(!feof(stream))
{
ch=lee(stream); if(ch=='*') break; //lee ch por adelantado.
A(stream);
printf("%s\n","ok");
}
printf("%s\n","fin");
fclose(stream);
return 0;

}
int main(void)
{
parser();
return 0;
}

Ejemplo A3.2. Parser BNF.


Se desea reconocer sentencias descritas por la siguiente gramtica:
produccin ::= <smbolo no terminal> = <expresin> .
expresin ::= <trmino> {, <termino>}
trmino ::= <factor> { <factor> }
factor ::= <smbolo terminal> | <smbolo no terminal> | ( <expresin> )
smbolo no terminal ::= Letra mayscula
smbolo terminal ::= Letra minscula
Los smbolos terminales requeridos por las reglas se han colocado entre comillas simple. Ntese
que cada produccin termina en el carcter punto.
La serie de producciones se termina cuando se encuentra un asterisco:
<texto de programa> ::= {produccin} *
El parser genera algunos comentarios de error, hacia la salida estndar, indicando la lnea y la
posicin del carcter que no cumple las reglas.
#include <stdio.h>
#include <stdlib.h> //malloc
#include <string.h>
#include <ctype.h>
void expresion(void); //prototipo. Factor requiere expresin.

Profesor Leopoldo Silva Bijit

20-01-2010

Introduccin a analizadores lxicos

11

char simbolo='\0';
int nl=1; //contador de lneas
int nc=0; //contador de caracteres en la lnea.
FILE *stream;
void error(int tipo)
{
putchar('\n');printf("(%d,%d): ",nl, nc+1);
switch (tipo)
{
case 1: printf("%s\n", "Esperaba smbolo no terminal");break;
case 2: printf("%s\n", "Esperaba signo igual"); break;
case 3: printf("%s\n", "Esperaba cierre parntesis"); break;
case 4: printf("%s\n", "Esperaba abre parntesis"); break;
case 5: printf("%s\n", "Esperaba punto"); break;
}
}
void getch(void)
{
if(!feof(stream))
{ simbolo = fgetc(stream); nc++;
if(smbolo == '\n') {nl++; nc=0;}
putchar(simbolo); //eco en salida estndard
}
}
void getsimbolo(void)
{
getch();
while(isspace(simbolo)) getch(); //descarta blancos
}
//factor ::= <smbolo terminal> | <smbolo no terminal> | ( <expresin> )
void factor(void)
{
if (isalpha(simbolo) ) getsimbolo();
else
if (simbolo == '(' )
{ getsimbolo(); expresion();
if(smbolo == ')' ) getsimbolo(); else error(3);
}
else error(4);
}

Profesor Leopoldo Silva Bijit

20-01-2010

12

Estructuras de Datos y Algoritmos

// trmino ::= <factor> { <factor> }


void termino(void)
{
factor();
while( (isalpha(simbolo)) || (smbolo == '(' ) ) factor();
}
Notar que la repeticin de factor es precedida por la revisin de los primeros caracteres de
factor: es decir que sea un smbolo terminal o no terminal o el comienzo de una expresin, que
debe comenzar por parntesis abierto.
//expresin ::= <trmino> {, <termino>}
void expresion(void)
{
termino();
while(smbolo == ',') {getsimbolo(); termino();}
}
// produccin ::= <smbolo no terminal> = <expresin> .
void produccion()
{
if(isupper(simbolo)) getsimbolo(); else error(1);
if (smbolo == '=') getsimbolo(); else error(2);
expresion();
if (simbolo != '.') error(5);
}
/* Lectura de archivo de texto */
int bnfparser(void)
{
/* Abre stream para lectura, en modo texto. */
if ((stream = fopen("bnfparser.txt", "r")) == NULL) {
fprintf(stderr, "No pudo abrir archivo de entrada.\n");
return 1;
}
/* lee hasta encontrar el final del stream */
while(!feof(stream))
{
getsimbolo(); if(simbolo=='*') break;
produccion();
}
printf("%s numero de lineas =%d\n","fin de archivo", nl);
fclose(stream); /* close stream */
return 0;
}

Profesor Leopoldo Silva Bijit

20-01-2010

Introduccin a analizadores lxicos

13

int main(void)
{
bnfparser();
return 0;
}
Para el siguiente archivo de entrada:
A = C.
B=x,A.
B=x,A,B,CC=x(B,D.
D=(A).
*
Se genera la siguiente salida:
A = C.
B=x,A.
B=x,A,B,C(3,10): Esperaba punto
C=x(B,D.
(4,8): Esperaba cierre parntesis
D=(A).
*fin de archivo nmero de lneas =5

Ejemplo A3.3. Reconocedor de identificador.


Un identificador es una secuencia de caracteres, donde el primero debe ser letra, y los que
siguen letras o nmeros.
Un identificador puede aparecer entre espacios, tabs, retornos; o estar entre caracteres no
alfanumricos. Si ab y cd son identificadores, las siguientes lneas muestran posibles instancias
de stos:
ab
(ab + cd)
ab= cd;
ab
= cd+5;
Una alternativa al diseo de reconocedores es el diseo basado en diagramas de estados. Se
ilustra un ejemplo, basado en anlisis de lneas. Ms adelante en A3.4 se esbozan
procedimientos para leer archivos de texto por lneas.
El diagrama de estados de la Figura A3.10, muestra que deben descartarse los espacios (se
simboliza por el crculo 0), y comenzar a almacenar el identificador, cuando se encuentra una
Profesor Leopoldo Silva Bijit

20-01-2010

14

Estructuras de Datos y Algoritmos

letra; luego se siguen almacenado los caracteres del identificador (crculo 1) hasta que llegue un
carcter no alfanumrico, en que se vuelve a esperar identificadores.
Si lo nico que se desea es extraer los identificadores, si no llega una letra cuando se espera una,
puede descartrsela y continuar el anlisis.
Si es letra
Si es espacio
Si es alfanumrico

No es letra

Si no es alfanumrico

Figura A3.10. Estados de reconocedor de identificador.


Asumiendo que se tiene una lnea almacenada en buffer, la siguiente funcin forma en el string
id, el identificador.
La estructura del cdigo est basada en el diagrama anterior, y en las funciones cuyos prototipos
se encuentran en ctype.h
#define LARGOLINEA 80
#define Esperando_letra 0
#define Almacenando_id 1
getword(char *buffer, int nl)
{
char id[LARGOLINEA];
int i, j, estado;
for(i=0, estado=0, j=0; i<strlen(buffer); i++)
{
switch (estado){
case Esperando_letra:
if (isspace(buffer[i]) ) continue;
else
if (isalpha(buffer[i]))
{ estado=Almacenando_id;
id[j]=buffer[i]; j++;
}
else ; //No es letra. Descarta char.

Profesor Leopoldo Silva Bijit

20-01-2010

Introduccin a analizadores lxicos

15

break;
case Almacenando_id:
if (isalnum(buffer[i]))
{id[j]=buffer[i]; j++;} //forma id
else
if(!isalnum(buffer[i]))
{ estado = Esperando_letra;
id[j]='\0'; //termina string
j=0; //reset posicin
//printf("%s %d\n", id, nl); //muestra los identificadores y la lnea
// Aqu debera hacerse algo con el identificador
root=insert(id, root, nl); //Ejemplo: lo inserta en rbol
}
break;
}
}
}

Ejemplo A3.4. Reconocedor de una definicin.


El siguiente ejemplo es una elaboracin del anterior, y su cdigo se realiza apoyndose en el
diagrama de estados de la Figura A3.11.
Se desea analizar un texto de programa y reconocer el identificador y su definicin que figuran
en una lnea que comienza con #define.
En el siguiente diagrama de estados, se pasa al estado 3, si se reconoce el identificador define,
luego del smbolo #. A la salida del estado 4, se tiene el identificador para el cual se est
definiendo una equivalencia. A la salida del estado 6, se tiene el identificador con el valor.
Luego del estado 6 debe regresar al estado 0.
Se emplean las definiciones:
#define LARGOID
20 //mximo largo identificadores
#define EsperaID
0 //estados
#define GetId
1
#define BuscaDefine 2
#define EsperaDef
3
#define GetDef
4
#define EsperaEquiv 5
#define GetEquiv
6

Profesor Leopoldo Silva Bijit

20-01-2010

16

Estructuras de Datos y Algoritmos

alfanum
6

isspace

!alfanum

alfanum
!alfanum
id ==define

es alfanumrico

Es #

alfanum
id != define

Si es espacio

Si es letra

Si es alfanumrico

Si es espacio
0

alfanum

No es letra ni #
Si no es alfanumrico

Figura A3.11. Estados de reconocedor de definiciones.


La funcin puede escribirse, basndose en el diagrama:
FILE *streami,*streamo;
char ch;
#define p() putc(ch,streamo);
#define g() ch=getc(streami)
void parser(void)
{
char word[LARGOID];
char equiv[LARGOID];
int j, state;
pcelda p;
for(state=0, j=0;!feof(streami);g())
{
switch (state){
case EsperaID:
if (isspace(ch) ) { p(); continue;}
else if (isalpha(ch)) {state=GetId; word[j++]=ch;}
else if(ch=='#'){state=BuscaDefine; j=0;}
else p(); //se traga hasta final de lnea
break;
case GetId:
if (isalnum(ch)) {word[j++]=ch;}
else
{ if(!isalnum(ch))
Profesor Leopoldo Silva Bijit

20-01-2010

Introduccin a analizadores lxicos

17

{state=EsperaID; word[j]='\0'; j=0;


//printf("%s\n",word);
if( (p=buscar(word))!=NULL ) //busca identificador
{
//printf("%s -> %s\n", p->definicion, p->equivalencia);
fprintf(streamo, p->equivalencia); //lo reeemplaza
}
else fprintf(streamo,word);
}
p();
}
break;
case BuscaDefine:
if (isalnum(ch)) {word[j++]=ch;}
else
if(!isalnum(ch))
{word[j]='\0';j=0;
if (strcmp(word,"define")==0)
{ state=EsperaDef;
//printf("pre=%s\n",word);
}
else {state=EsperaID; putc('#', streamo); fprintf(streamo,word);p();}
}
break;
case EsperaDef:
if (isspace(ch) ) continue;
else if (isalpha(ch)) {state=GetDef; word[j++]=ch;}
break;
case GetDef:
if (isalnum(ch)) {word[j++]=ch;}
else if(!isalnum(ch))
{state=EsperaEquiv; word[j]='\0'; j=0;
//printf("Definicin =%s\n",word);
}
break;
case EsperaEquiv:
if (isspace(ch) ) continue;
else if (isgraph(ch)) {state=GetEquiv; equiv[j++]=ch;}
break;
case GetEquiv:
if (isgraph(ch)) {equiv[j++]=ch;}
else if(!isgraph(ch))
{ state=EsperaID; equiv[j]='\0';j=0;
//printf("insertar valor equivalente en tabla=%s\n", equiv);
// Aqu debera insertar la palabra word y su equivalencia.
if( (p=buscar(word))!=NULL ) descarte(word); //permite redefinicin
inserte(word, equiv);
Profesor Leopoldo Silva Bijit

20-01-2010

18

Estructuras de Datos y Algoritmos


}
break;
}
}

}
El siguiente segmento, abre los archivos de entrada y salida.
int procesa_archivos(void)
{
/* Abre stream para lectura, en modo texto. */
if ((streami = fopen("input6.c", "r")) == NULL) {
fprintf(stderr, "No pudo abrir archivo de entrada.\n");
return 1;
}
/* Abre stream para escritura, en modo texto. */
if ((streamo = fopen("output6.c", "w")) == NULL) {
fprintf(stderr, "No pudo abrir archivo de salida.\n");
return 1;
}
/* lee hasta encontrar el final del stream */
while(!feof(streami))
{
g(); //lee uno por adelantado
if(!feof(streami))
{
parser();
//putchar(ch);
}
else break;
}
fclose(streami); /* close stream */
fclose(streamo);
return 0;
}
int main(void)
{ makenull();
procesa_archivos();
return 0;
}

A3.4. Manipulacin de archivos en C.


En la etapa de prueba de los algoritmos es necesario ingresar datos. Si stos son numerosos es
recomendable efectuar esta operacin leyendo los datos desde un archivo.

Profesor Leopoldo Silva Bijit

20-01-2010

Introduccin a analizadores lxicos

19

Tambin es preciso poder extraer resultados, para posterior anlisis, escribindolos en un


archivo.
Escritura de archivos de texto, con estructura de lneas.
Consideremos primero la escritura de archivos de texto con formato.
Esto puede lograrse con un editor, siguiendo ciertas convenciones para separar los elementos de
datos. Es recomendable ingresar por lneas, y en cada lnea separar los tems mediante espacios
o tabs.
Es importante saber el nmero y tipos de datos de los tems de cada lnea, ya que debe
confeccionarse un string de formato con dicha estructura.
En caso de escritura de un archivo, mediante un programa, tambin debe tenerse en cuenta la
estructura de la lnea.
Veamos un ejemplo.
Se tiene un entero y un carcter por lnea, que configuran un dato con la siguiente estructura:
struct mystruct
{
int i;
char ch;
};
La confeccin con un editor, podra generar el siguiente texto, donde los espacios previos al
nmero pueden ser reemplazados por varios espacios o tabs. La separacin entre el nmero y el
carcter debe ser un espacio o un tab. Luego pueden existir varios espacios o tabs, seguidos de
un retorno.
11 a
22
b
333 c
4d
5e
Escritura de archivo, desde un programa.
La manipulacin de archivos, requiere la invocacin a funciones de <stdio.h>, para abrir, cerrar,
leer o escribir en el archivo.
El siguiente segmento abre para escritura el archivo testwr.txt, que debe estar ubicado en el
mismo directorio que el programa ejecutable; en caso de otra ubicacin, es preciso preceder el
nombre con el sendero de la ruta.
El modo w establece modo escritura, es decir sobreescribe si el archivo existe, y lo crea en
caso contrario.
La variable que permite manipular un archivo es de tipo FILE, y se la define segn:
Profesor Leopoldo Silva Bijit

20-01-2010

20

Estructuras de Datos y Algoritmos

FILE *stream;
Se suele proteger la apertura, en caso de falla, mediante:
if ((stream = fopen("testwr.txt", "w")) == NULL)
{
fprintf(stderr, "No puede abrir archivo de salida.\n");
return 1;
}
El stream o flujo de salida stderr suele ser la pantalla.
La siguiente instruccin escribe en el stream 4 caracteres por lnea, ms el terminador de lnea,
eol; que suele ser uno o dos caracteres. La estructura de la lnea: dd<sp>c<eol>.
fprintf(stream, "%2d %c\n", s.i, s.ch);
Una vez completada la escritura de todas las lneas, se cierra el archivo, mediante:
fclose(stream);
Lectura de archivos de texto con estructura de lneas.
Se incluye un programa para leer el archivo, ya sea generado con un editor o por un programa,
mediante la funcin fscanf. Se ha usado la funcin feof, para determinar si se lleg al final del
archivo. La accin que se realiza con los datos es simplemente desplegar en la pantalla, los
datos formateados.
/* Ejemplo con streams. Lectura de archivo de texto formateado */
#include <stdio.h>
//El archivo testwr.txt debe estar estructurado en lneas.
//Cada lnea debe estar formateada segn: <sep>entero<sep>char<eol>
//Donde <sep> pueden ser espacios o tabs.
//El entero debe ser de dos dgitos, si en el string de formato del fscanf figura como %2d.
FILE *stream;
int main(void)
{
int jj; char cc;
/* Abre stream para lectura, en modo texto. */
if ((stream = fopen("testwr.txt", "r")) == NULL){
fprintf(stderr, "No pudo abrir archivo de entrada.\n");
return 1;
}
/* lee hasta encontrar el final del stream */
Profesor Leopoldo Silva Bijit

20-01-2010

Introduccin a analizadores lxicos

21

for(;;)
{
fscanf(stream, "%d %c", &jj, &cc); //lee variables segn su tipo y estructura de lnea.
if(feof(stream)) break;
printf("%d %c\n", jj, cc); //slo muestra las lneas del archivo
}
fclose(stream); /* close stream */
return 0;
}
Si se intenta leer ms all del fin de archivo la funcin feof, retorna verdadero.
Llenar un arreglo a partir de un archivo.
El siguiente ejemplo llena un arreglo de estructuras.
/* Ejemplo con streams. Con datos de archivo se escribe un arreglo */
#include <stdio.h>
//El archivo testwr.txt debe estar estructurado en lineas.
//Cada linea debe estar formateada segn: <sep>entero<sep>char<eol>
//Donde <sep> pueden ser espacios o tabs.
//El entero debe ser de dos dgitos, si en el string de formato del fscanf figura como %2d.
struct mystruct
{
int i;
char ch;
};
#define ITEMS 20
struct mystruct arr[ITEMS];
FILE *stream;
int main(void)
{
int i, jj; char cc;
/* Abre stream para lectura, en modo texto. */
if ((stream = fopen("testwr.txt", "r")) == NULL)
{
fprintf(stderr, "No pudo abrir archivo de entrada.\n");
return 1;
}
for(i=0; i< ITEMS; i++)
{
fscanf(stream, "%d %c", &jj, &cc); //lee variables segn tipo.
if(feof(stream)) break;
arr[i].i=jj; arr[i].ch=cc; //llena items del arreglo
}
Profesor Leopoldo Silva Bijit

20-01-2010

22

Estructuras de Datos y Algoritmos


fclose(stream); /* close stream */
for(i=0;i< ITEMS;i++)
{
printf("%d %c\n", arr[i].i, arr[i].ch);// muestra el arreglo
}
return 0;

}
Escritura y lectura de archivos binarios.
El siguiente ejemplo, ilustra la escritura y lectura de archivos binarios, no de texto. Se emplean
ahora las funciones: fwrite, fseek y fread.
* Ejemplo con streams. Escritura y luego lectura de archivo binario */
#include <stdio.h>
struct mystruct
{
int i;
char ch;
};
int main(void)
{
FILE *stream;
struct mystruct s;
int j;
/* sobreescribe y lo abre para escritura o lectura en modo binario */
if ((stream = fopen("TEST.bin", "w+b")) == NULL)
{
fprintf(stderr, "No se puede crear archivo de salida.\n");
return 1;
}
for(j=0;j<20;j++)
{
s.i = j;
s.ch = 'A'+j;
fwrite(&s, sizeof(s), 1, stream); /* write struct s to file */
}
/* seek to the beginning of the file */
fseek(stream, SEEK_SET, 0);
/* lee y despliega los datos */
Profesor Leopoldo Silva Bijit

20-01-2010

Introduccin a analizadores lxicos

23

for(j=0; j<20;j++)
{
fread(&s, sizeof(s), 1, stream);
printf("%d %c\n", s.i, s.ch);
}
fclose(stream); /* close file */
return 0;
}
El archivo TEST.bin, no puede ser visualizado con un editor de texto. Para su interpretacin
debe usarse un editor binario.
Compilacin y ejecucin en ambiente UNIX.
Para programas sencillos, como los ilustrados, puede generarse el ejecutable en ambiente UNIX,
mediante el comando: make <nombre de archivo c, sin extensin>
Esto crea un ejecutable de igual nombre al programa en C.
Para su ejecucin basta escribir su nombre.
Escritura y lectura de archivos por lneas.
#define LARGOLINEA 80 //mximo largo de lnea igual a 80 caracteres
char buffer[LARGOLINEA];
int lee_archivo(void)
{
FILE *stream;
/* Abre stream para lectura, en modo texto. */
if ((stream = fopen("input.txt", "r")) == NULL) {
fprintf(stderr, "No pudo abrir archivo de entrada.\n");
return 1;
}
while(!feof(stream)) /* lee hasta encontrar el final del stream */
{
fgets(buffer, LARGOLINEA, stream); //carga buffer
if(!feof(stream))
{
// Aqu debera procesarse la lnea
//printf("%s ", buffer); //muestra las lneas del archivo de entrada
}
else break;
}
fclose(stream); /* close stream */
return 0;
Profesor Leopoldo Silva Bijit

20-01-2010

24

Estructuras de Datos y Algoritmos

}
int escribe_archivo(void)
{
FILE *stream;
/* Abre stream para escritura, en modo texto. */
if ((stream = fopen("output.txt", "w")) == NULL) {
fprintf(stderr, "No pudo crear archivo de salida.\n");
return 1;
}
// Aqu debera escribirse el el archivo..
//fputs(buffer, stream);
imprime lnea
//fprintf(stream, "%d\t", 5); salida formateada
// fputc('\n', stream);
salida caracteres
fclose(stream); /* close stream */
return 0;
}

Referencias.
Niklaus Wirth, Algorithms + Data Structures = Programs, Prentice-Hall 1975.

Profesor Leopoldo Silva Bijit

20-01-2010

Introduccin a analizadores lxicos

25

ndice general.
APNDICE 3 .............................................................................................................................................. 1
INTRODUCCIN A LA ESTRUCTURA Y OPERACIN DE ANALIZADORES LXICOS. ...... 1
A3.1. ESTRUCTURA DE UN LENGUAJE DE PROGRAMACIN. ...................................................................... 1
A3.2. ANALIZADOR LXICO. (PARSER) ..................................................................................................... 2
A3.3. REGLAS DE ANLISIS. ..................................................................................................................... 4
Smbolo terminal. ................................................................................................................................ 5
Smbolo no terminal. ........................................................................................................................... 5
Alternativa. .......................................................................................................................................... 5
Concatenacin..................................................................................................................................... 6
Repeticin. ........................................................................................................................................... 7
Resumen. ............................................................................................................................................. 8
EJEMPLO A3.1. RECONOCEDOR SIMPLE. ................................................................................................... 8
EJEMPLO A3.2. PARSER BNF. ............................................................................................................... 10
EJEMPLO A3.3. RECONOCEDOR DE IDENTIFICADOR. .............................................................................. 13
EJEMPLO A3.4. RECONOCEDOR DE UNA DEFINICIN. ............................................................................ 15
A3.4. MANIPULACIN DE ARCHIVOS EN C. ............................................................................................. 18
Escritura de archivos de texto, con estructura de lneas. .................................................................. 19
Escritura de archivo, desde un programa. ........................................................................................ 19
Lectura de archivos de texto con estructura de lneas. ..................................................................... 20
Llenar un arreglo a partir de un archivo. ......................................................................................... 21
Escritura y lectura de archivos binarios. .......................................................................................... 22
Compilacin y ejecucin en ambiente UNIX. .................................................................................... 23
Escritura y lectura de archivos por lneas. ....................................................................................... 23
REFERENCIAS. ........................................................................................................................................ 24
NDICE GENERAL. ................................................................................................................................... 25
NDICE DE FIGURAS................................................................................................................................. 25

ndice de figuras.
FIGURA A3.1. ANLISIS SIN VOLVER ATRS. ................................................................................................ 3
FIGURA A3.2. SMBOLO TERMINAL. .............................................................................................................. 5
FIGURA A3.3. SMBOLO NO TERMINAL. ........................................................................................................ 5
FIGURA A3.4. ALTERNATIVA. ....................................................................................................................... 6
FIGURA A3.5. ALTERNATIVA, CON PRIMER SMBOLO EXPLCITO. ................................................................. 6
FIGURA A3.6. CONCATENACIN. .................................................................................................................. 7
FIGURA A3.7. REPETICIN............................................................................................................................ 7
FIGURA A3.8. REPETICIN, CON PRIMER SMBOLO EXPLCITO. ..................................................................... 7
FIGURA A3.9. GRAFO DEL RECONOCEDOR. ................................................................................................... 8
FIGURA A3.10. ESTADOS DE RECONOCEDOR DE IDENTIFICADOR. ............................................................... 14
FIGURA A3.11. ESTADOS DE RECONOCEDOR DE DEFINICIONES................................................................... 16

Profesor Leopoldo Silva Bijit

20-01-2010

You might also like