Professional Documents
Culture Documents
Facultad de Ciencias
Tacna Per
2003
Presentacin
1
19
23
Seleccin de patrones
29
37
41
43
51
61
10
Presentacin
Lex y Yacc son las ms conocidas y usadas de todas las herramientas que se emplean en
la generacin automtica de (partes de) compiladores de los lenguajes de programacin.
Son utilidades del sistema operativo Unix y los programas que producen estn escritos en
lenguaje C.
En este trabajo se hace una descripcin de los aspectos bsicos de las dos herramientas; con ello se pretende aportar los conocimientos necesarios para la realizacin de
las prcticas de la asignatura Compiladores e Intrpretes de esta Escuela Universitaria; en
concreto, se trata de su aplicacin para la obtencin de los analizadores lexicogrfico y
sintctico de los traductores de lenguajes.
Para entender cabalmente algunas cuestiones relativas a Yacc es preciso conocer los mtodos de anlisis sintctico ascendente debido a que el analizador generado pertenece a
esa categora; Yacc tambin sirve de apoyo para la implementacin de las tareas de
anlisis semntico y de generacin de codigo. Estas cuestiones ms avanzadas no se
tratan aqu.
En algunos libros dedicados al sistema operativo Unix se incluyen captulos en los que se
describen las utilidades Lex y Yacc. Un estudio ms completo se encuentra en el libro
Lex & Yacc , de John R. Levine, Tony Mason y Doug Brown (Editorial OReally, 2 edicin, 1990).
No todas las versiones de Lex y Yacc son idnticas; aunque son muy parecidas, no son
por completo compatibles; los ejemplos descritos se han probado con las versiones incorporadas al sistema operativo Unix-OSF(v.3.2). Estos ejemplos se pueden conseguir a travs del servidor del Centro de Clculo de esta Escuela, en la direccin:
ftp://ftp.eui.upm.es/eui_practicas/ci/lexyacc
Generador
::= ( <Expresion> )
| id
| cte
La entrada a Lex (la especificacin lexicogrfica), si se emplean los nombres pId, pCte,
pSum, pMul, pAbr y pCer para representar las distintas piezas sintcticas del lenguaje, es:
%{
#define pId 1
#define pCte 2
#define pSum 3
#define pMul 4
#define pAbr 5
#define pCer 6
#define Error 999
%}
%%
[a-z]+ { return pId; }
[0-9]+ { return pCte; }
"+"
{ return pSum; }
"*"
{ return pMul; }
"("
{ return pAbr; }
")"
{ return pCer; }
[\ \t\n] { ;
}
.
{ return Error; }
La entrada a Yacc (la especificacin sintctica) es, considerada a partir de una gramtica
equivalente a la dada, pero escrita en notacin BNF-No ampliada, es:
%token pId 1
%token pCte 2
%token pSum 3
%token pMul 4
%token pAbr 5
%token pCer 6
%start Expresion
%%
Expresion : Termino RestoExpr ;
RestoExpr : pSum Termino RestoExpr ;
| ;
Termino : Factor RestoTerm ;
RestoTerm : pMul Factor RestoTerm ;
| ;
Factor : pId ;
| pCte ;
14424443
. . . . . . . . . .
Patrn [ forma
Accin [ operaciones ]
En la prctica habitual suelen tenerse varios patrones, cada uno de ellos con sus
correspondientes operaciones (tareas) asociadas; el siguiente esquema ilustra esta
variedad de patrones en una nica especificacin Lex:
. . . . . . . .
A1
P1
P2
Pq
. . . . . . . . . .
. . . . . .
Por ejemplo, si se quisieran contar las palabras en minsculas (cada palabra es una
secuencia de una o ms letras minsculas grabadas de forma consecutiva) y los
nmeros (cada nmero es una secuencia de cifras decimales grabadas de forma
consecutiva) que hay en un fichero de tipo texto, se tendra:
P1 patrn indicativo de la forma de una palabra en minsculas
P2 patrn indicativo de la forma de un nmero
A1 incrementar en una unidad el contador de palabras
A2 incrementar en una unidad el contador de nmeros
A2
Aq
Especificacin
Programa
...
.
yylex
(eLexic.l)
lex
(lex.yy.c)
Los siguientes esquemas ilustran las dos maneras de funcionamiento; ambas son
factibles y tiles: el uso de una u otra depende del problema que se quiera resolver
return
Seleccin de patrones
Criterio de seleccin
...........
.
%%
...........
.
..............
.
Ai
..............
.
Pi
Para determinar el patrn que se elige, en cada momento, para acoplar una secuencia
de caracteres de la entrada hay que aplicar el criterio que se explica en lo que sigue.
El texto de entrada estar ya analizado hasta cierto punto de la lnea actual; se
considera como carcter actual el primer carcter todava no analizado (no acoplado).
carcter
actual
ya
mayor
longitud
aplicacin
de
[0-9]+
[0-9]+\.[0-9]+
{ return pCteEnt; }
{ return pCteDec; }
{ return prEnd; }
{ return prWhile; }
{ return pId; }
%{
%}
%%
%%
Cdigo de
Definiciones
Seccin
de
Definiciones
Definiciones
para los
smbolos
Seccin
de
Reglas
Seccin
de
Rutinas
y.tab.c
C1
Especificacin escrita en
Yacc
%{
C(D)
C1
P1
%}
D
C2
%%
P2
P3
%%
P4
C2
yyparse
%start
prEnteros
1
prCaracteres 2
prTabla
3
prDe
4
pId
5
pNum
6
pPuntero
7
pAbrir
8
pCerrar
9
pSeparador
10
Tipo
%token pAbrir 8
se transforma en
#define pAbrir 8
Seccin de reglas
Es la parte fundamental de una especificacin Yacc; en ella se pone la gramtica que
define la sintaxis del lenguaje que se pretende analizar con el programa generado.
Las reglas de la gramtica se escriben con una notacin parecida a la notacin
BNF-No Ampliada; a continuacin se describe la notacin de Yacc, con comentarios
comparativos con la bien conocida notacin BNF.
- Nombres de los smbolos de la gramtica.
En la notacin de Yacc no se emplean los caracteres < y > para delimitar los
nombres de los smbolos no terminales; ya se ha comentado antes cmo se escriben y
cmo se distinguen entre s los smbolos terminales y no terminales.
- Separacin entre las dos partes de una regla.
En Yacc la separacin entre la parte izquierda y la parte derecha de una regla se
indica mediante el carcter dos puntos; en la notacin BNF esta separacin est
representada mediante la secuencia de tres caracteres ::=.
- Reglas con la misma parte izquierda.
Para indicar que varias reglas consecutivas tienen la misma parte izquierda, tanto en
Yacc como en BNF se emplea el carcter |.
- Parte derecha que es la palabra vaca.
En la notacin de Yacc la palabra vaca se representa mediante la ausencia de la parte
derecha; esto es, para indicar la presencia de la palabra vaca no se pone smbolo
alguno (precisamente la ausencia de smbolo es la representacin de la palabra vaca);
en la notacin BNF la palabra vaca suele representarse con la letra griega .
- Separacin entre los smbolos de la parte derecha.
Para indicar la separacin entre dos smbolos consecutivos de la parte derecha de una
regla, en la notacin de Yacc ha de ponerse al menos un espacio en blanco (o un
tabulador, o incluso un final de lnea); se puede decir que as ocurre tambin en la
notacin BNF.
- Marca de final de regla.
En una especificacin Yacc, no es preciso indicar explcitamente el punto donde
termina una regla (detrs del ltimo smbolo de la parte derecha); no obstante, para
favorecer la legibilidad de la gramtica, en el estilo habitual de escritura se pone un
punto y coma para indicar el final de una regla, o bien un punto y coma detrs de la
ltima regla de una secuencia de reglas consecutivas que tienen la misma parte
izquierda. En la notacin BNF no se marca el final de las reglas.
As pues, el carcter punto y coma es un metasmbolo de la notacin Yacc.
%token pId 7
%token pComa 15
%%
Lista
: pId RestoLista ;
RestoLista : pComa pId RestoLista ;
|
;
<Lista> ::= id { , id }
tiene una estructura que tambin puede expresarse en notacin BNF No-Ampliada
mediante las producciones recursivas por la izquierda siguientes:
<Lista> ::= id
| <Lista> , id
:
|
pId ;
Lista pComa
pId
%%
Tipo : TipoSimple ;
| pPuntero pId ;
TipoSimple : prEnteros ;
| prCaracteres ;
prDe
Lista
RestoLista
Lista
:
|
;
:
;
:
|
;
pId
Lista
pId
RestoLista
pComa
pId
Tipo : TipoSimple
| pPuntero pId
TipoSimple : prEnteros ;
| prCaracteres ;
prDe
Seccin de rutinas
En esta seccin se coloca cdigo escrito en C, que se traslada literalmente al fichero
generado. Usualmente se ponen aqu rutinas de apoyo para el tratamiento semntico;
son funciones a las que se llama desde las acciones que pueden asociarse a las reglas
de la gramtica (esta posibilidad de asociacin no se estudia aqu).
Tambin se puede aprovechar esta seccin para incorporar la funcin
principal main desde la que se produce la llamada al analizador sintctico generado:
la funcin yyparse; as puede verse en el Ejemplo 1 expuesto en el captulo inicial.
En la figura que ilustra la relacin entre la entrada y la salida del traductor
Yacc, el cdigo de esta seccin est representado mediante C2.
En esa misma figura las partes de la salida indicadas mediante P1, P2, P3 y P4
no interesan en lo que aqu se expone.
Ejemplo 5
Se pretende obtener un programa que ponga en maysculas las palabras reservadas
encontradas en un programa escrito en Pascal; las palabras reservadas del lenguaje (en
la versin original) son:
program
and
div
file
in
of
array
do
for
label
or
begin
case
downto
else
function
goto
mod
nil
packed
record
type
repeat
until
set
var
then
while
const
end
if
not
procedure
to
with
[aA][nN][dD]
}
[aA][rR][rR][aA][yY]
}
[bB][eE][gG][iI][nN]
}
[cC][aA][sS][eE]
}
[cC][oO][nN][sS][tT]
}
{ printf ("AND");
{ printf ("ARRRAY");
{ printf ("BEGIN");
{ printf ("CASE");
{ printf ("CONST");
[dD][iI][vV]
}
[dD][oO]
}
[dD][oO][wW][nN][tT][oO]
}
[eE][lL][sS][eE]
}
[eE][nN][dD]
}
[fF][iI][lL][eE]
}
[fF][oO][rR]
}
[fF][uU][nN][cC][tT][iI][oO][nN]
("FUNCTION"); }
[gG][oO][tT][oO]
}
[iI][fF]
}
[iI][nN]
}
[lL][aA][bB][eE][lL]
}
[mM][oO][dD]
}
[nN][iI][lL]
}
[nN][oO][tT]
}
[oO][fF]
}
[oO][rR]
}
[pP][aA][cC][kK][eE][dD]
}
[pP][rR][oO][cC][eE][dD][uU][rR][eE]
("PROCEDURE"); }
[pP][rR][oO][gG][rR][aA][mM]
("PROGRAM");
}
[rR][eE][cC][oO][rR][dD]
}
[rR][eE][pP][eE][aA][tT]
}
[sS][eE][tT]
}
[tT][hH][eE][nN]
}
[tT][oO]
}
[tT][yY][pP][eE]
}
[uU][nN][tT][iI][lL]
}
[vV][aA][rR]
}
{ printf ("DIV");
{ printf ("DO");
{ printf ("DOWNTO");
{ printf ("ELSE");
{ printf ("END");
{ printf ("FILE");
{ printf ("FOR");
{ printf
{ printf ("GOTO");
{ printf ("IF");
{ printf ("IN");
{ printf ("LABEL");
{ printf ("MOD");
{ printf ("NIL");
{ printf ("NOT");
{ printf ("OF");
{ printf ("OR");
{ printf ("PACKED");
{ printf
{ printf
{ printf ("RECORD");
{ printf ("REPEAT");
{ printf ("SET");
{ printf ("THEN");
{ printf ("TO");
{ printf ("TYPE");
{ printf ("UNTIL");
{ printf ("VAR");
[wW][hH][iI][lL][eE]
}
[wW][iI][tT][hH]
}
[a-zA-Z][a-zA-Z0-9]*
.|\n
%%
main () {
yylex ();
}
{ printf ("WHILE");
{ printf ("WITH");
|
{ printf ("%s", yytext); }
Ejemplo 6
En el lenguaje Pascal el smbolo punto y coma tiene el papel de separador de
sentencias; por ello, si una sentencia es la ltima de un bloque, no es preciso poner
detrs de ella un punto y coma.
Se pretende obtener un programa que suprima en un texto escrito en Pascal
los smbolos punto y coma que precedan a la palabra reservada end; la supresin se
realiza sustituyendo el carcter punto y coma por un espacio en blanco. Ha de
considerarse la posibilidad de que entre el punto y coma y la palabra reservada end
puedan estar presentes cero o ms espacios en blanco (o tabuladores o finales de
lnea).
Por ejemplo, para el texto de entrada
i:= i + 1;
end;
while
;/[\ \t\n]*[eE][nN][dD]
%%
main () {
yylex ();
}
se obtendr la salida:
i := i + 1
end
end;
while
Ejemplo 7
En un fichero se tiene grabada una relacin de nombres con una calificacin asociada;
en cada lnea del fichero se tiene, en este orden:
- primero y segundo apellido
- un smbolo punto y coma
- nombre propio
- calificacin
El nombre propio y cada uno de los apellidos puede estar formado por una o ms
palabras; las palabras estn constituidas por letras minsculas o maysculas. La
calificacin puede ser un nmero entero o un nmero decimal; en el caso de ser un
nmero decimal, la parte entera y la parte decimal estn separadas por una coma.
Entre cualquier par de componentes consecutivos de una lnea hay uno o ms
espacios en blanco. Una lnea puede tener, antes de la primera palabra, uno o ms
espacios en blanco. El fichero tiene al menos una lnea. Un ejemplo de fichero
grabado segn estas condiciones es:
de la Calle
Cepero
;
Enriqueta
8
del Rio de la Plaza;Jose Maria
Antonio
Cerezo del Peral ; Margarita
8,00
Iniesta
Zurbaran;
Leopoldo 7,5
5,003
5,003
El programa pedido deber detectar, por ejemplo, estos errores en los datos de una
lnea: que falte la calificacin, que falte el nombre, que en la parte de los apellidos
haya menos de dos palabras, que falte el punto y coma (o que en su lugar haya otro
carcter), que en una palabra haya algn carcter que no sea una letra, que el nmero
decimal tenga un punto en vez de una coma.
La especificacin de entrada a Lex es:
%{
#include "y.tab.h"
espacio [\ ]+
palabra [a-zA-Z]+
numEnt
[0-9]+
numDec
{numEnt}\,{numEnt}
separa
[\ ]*;[\ ]*
%%
{palabra}({espacio}{palabra})+/{separa} {
return apellidos;
}
{palabra}({espacio}{palabra})*/{espacio} {
return nombre;
}
{separa}
{ return ptocoma; }
{numEnt}
{ printf (" >> %s,0\n", yytext);
return nota; }
{numDec}
{ printf (" >> %s\n", yytext); return
nota; }
\n
{ return limite; }
{espacio}
{ ; }
.
{ return yytext [0]; }
%start Lista
%%
| Linea
;
%%
main () {
yyparse ();
}
pId;
pCte;
pSum;
pMul;
pAbr;
pCer;
}
}
}
}
}
}
}
Error; }
%token pId 1
%token pCte 2
%token pSum 3
%token pMul 4
%token pAbr 5
%token pCer 6
%start Expresion
%%
Expresion : Termino RestoExpr ;
RestoExpr : pSum Termino RestoExpr ;
| ;
Termino : Factor RestoTerm ;
RestoTerm : pMul Factor RestoTerm ;
| ;
Factor : pId ;
| pCte ;
| pAbr Expresion pCer ;
%%
main () {
yyparse ();
}
Ejemplo 2
%{
int nPal = 0, nNum = 0;
%}
letra
cifra
[a-z]
[0-9]
%%
{letra}+
{cifra}+
\n
.
{
{
{
{
++nPal; }
++nNum; }
; }
; }
%%
main () {
yylex ();
printf ("Palabras: %d\n", nPal);
printf ("Numeros : %d\n", nNum);
}
Ejemplo 3
%{
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
prEnteros 1
prCaracteres 2
prTabla 3
prDe 4
pId 5
pNum 6
pPuntero 7
pAbrir 8
pCerrar 9
pSeparador 10
Error 999
%}
letra
digito
[a-zA-Z]
[0-9]
%%
ENTEROS
CARACTERES
TABLA
DE
{
{
{
{
return
return
return
return
prEnteros; }
prCaracteres; }
prTabla; }
prDe; }
{letra}({letra}|{digito})*
{digito}+
"^"
"["
]
".."
{
{
{
{
return
return
return
return
[\ \t\n]
.
%token
%token
%token
%token
%token
%token
%token
%token
%token
%token
{ return pId; }
{ return pNum; }
pPuntero; }
pAbrir; }
pCerrar; }
pSeparador; }
{ ; }
{ return Error; }
prEnteros 1
prCaracteres 2
prTabla 3
prDe 4
pId 5
pNum 6
pPuntero 7
pAbrir 8
pCerrar 9
pSeparador 10
%start Tipo
%%
Tipo : TipoSimple ;
| pPuntero pId ;
| prTabla pAbrir TipoSimple pCerrar prDe Tipo ;
TipoSimple : prEnteros ;
| prCaracteres ;
| pNum pSeparador pNum ;
%%
main () {
if ( yyparse () == 0 )
printf ("No se ha encontrado error alguno\n");
}
Ejemplo 4
%{
#include "y.tab.h"
%}
Letra [a-zA-Z]
Digito [0-9]
Exp
[eE][-+]?{Digito}+
%%
AND
NOT
OR
DIV
MOD
{
{
{
{
{
return
return
return
return
return
prAnd;
prNot;
prOr;
prDiv;
prMod;
}
}
}
}
}
{Letra}({Letra}|{Digito})*
{Digito}+
{Digito}+"."{Digito}+
{Digito}+("."{Digito}+)?{Exp}
"<"=
>=
"<">
:=
[\ \t\n]+
.
{
{
{
{
{
{
return
return
return
return
; }
return
{
{
{
{
return
return
return
return
pMei; }
pMai; }
pDis; }
pAsig; }
yytext [0]; }
%{
extern yytext [], yylineno;
%}
%token
%token
%token
%token
%token
%token
%token
%token
%token
%token
%token
%token
%token
prAnd
prOr
prNot
prDiv
prMod
pId
pCteEnt
pCteDec
pCteExp
pMei
pMai
pDis
pAsig
%start Asignacion
%%
pId; }
pCteEnt; }
pCteDec; }
pCteExp; }
'<' ;
'=' ;
'>' ;
pMei ;
pMai ;
pDis ;
'+' ;
'-' ;
prOr ;
'*' ;
'/' ;
prMod ;
prDiv ;
prAnd ;
%%
main () {
if ( yyparse () == 0 ) {
printf ("\nAnalisis Lexico-Sintactico terminado.\n");
printf ("No se ha encontrado error alguno.\n\n");
}
}
yyerror (char *mensaje) {
printf ("\nMensaje proporcionado:\n");
printf (">>> %s\n\n", mensaje);
printf ("Linea actual : %d\n", yylineno);
printf ("Lexema actual: %s\n\n", yytext);
printf ("Analisis suspendido.\n\n");
}
Ejemplo 5
%%
[aA][nN][dD]
[aA][rR][rR][aA][yY]
[bB][eE][gG][iI][nN]
[cC][aA][sS][eE]
[cC][oO][nN][sS][tT]
[dD][iI][vV]
[dD][oO]
[dD][oO][wW][nN][tT][oO]
[eE][lL][sS][eE]
[eE][nN][dD]
[fF][iI][lL][eE]
[fF][oO][rR]
[fF][uU][nN][cC][tT][iI][oO][nN]
[gG][oO][tT][oO]
[iI][fF]
[iI][nN]
[lL][aA][bB][eE][lL]
[mM][oO][dD]
[nN][iI][lL]
[nN][oO][tT]
[oO][fF]
[oO][rR]
[pP][aA][cC][kK][eE][dD]
[pP][rR][oO][cC][eE][dD][uU][rR][eE]
[pP][rR][oO][gG][rR][aA][mM]
[rR][eE][cC][oO][rR][dD]
[rR][eE][pP][eE][aA][tT]
[sS][eE][tT]
[tT][hH][eE][nN]
[tT][oO]
[tT][yY][pP][eE]
[uU][nN][tT][iI][lL]
[vV][aA][rR]
[wW][hH][iI][lL][eE]
[wW][iI][tT][hH]
[a-zA-Z][a-zA-Z0-9]*
.|\n
{
{
{
{
{
{
{
{
{
{
{
{
{
{
{
{
{
{
{
{
{
{
{
{
{
{
{
{
{
{
{
{
{
{
{
printf
printf
printf
printf
printf
printf
printf
printf
printf
printf
printf
printf
printf
printf
printf
printf
printf
printf
printf
printf
printf
printf
printf
printf
printf
printf
printf
printf
printf
printf
printf
printf
printf
printf
printf
|
{ printf ("%s", yytext); }
%%
main () {
yylex ();
}
Ejemplo 6
%%
;/[\ \t\n]*[eE][nN][dD]
%%
main () {
yylex ();
}
("AND");
("ARRRAY");
("BEGIN");
("CASE");
("CONST");
("DIV");
("DO");
("DOWNTO");
("ELSE");
("END");
("FILE");
("FOR");
("FUNCTION");
("GOTO");
("IF");
("IN");
("LABEL");
("MOD");
("NIL");
("NOT");
("OF");
("OR");
("PACKED");
("PROCEDURE");
("PROGRAM");
("RECORD");
("REPEAT");
("SET");
("THEN");
("TO");
("TYPE");
("UNTIL");
("VAR");
("WHILE");
("WITH");
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
Ejemplo 7
%{
#include "y.tab.h"
char * losApell [50];
%}
espacio
palabra
numEnt
numDec
separa
[\ ]+
[a-zA-Z]+
[0-9]+
{numEnt}\,{numEnt}
[\ ]*;[\ ]*
%%
{palabra}({espacio}{palabra})+/{separa}
{
strcpy (losApell, yytext);
return apellidos;
}
{palabra}({espacio}{palabra})*/{espacio}
{
printf ("%s %s", yytext, losApell);
return nombre;
}
{separa}
{ return ptocoma; }
{numEnt}
{ printf (" >> %s,0\n", yytext); return nota; }
{numDec}
{ printf (" >> %s\n", yytext); return nota; }
\n
{ return limite; }
{espacio}
{ ; }
.
{ return yytext [0]; }
%{
extern yytext [];
%}
%token
%token
%token
%token
%token
nombre
apellidos
ptocoma
nota
limite
%start Lista
%%
Lista : Lista Linea ;
| Linea
;
Linea : apellidos ptocoma nombre nota limite ;
%%
main () {
yyparse ();
}
yyerror (char *mensaje) {
printf ("\n\n\nMensaje proporcionado:\n");
printf (">>> %s\n\n", mensaje);
printf ("Analisis suspendido.\n\n");
printf ("lexema: %s\n", yytext);
}