Está en la página 1de 36

Universidad Nacional Jorge Basadre Grohmann

Facultad de Ciencias

LOS GENERADORES LEX Y YACC

Mgr. Luis Andres Amaya Caldern

Tacna Per
2003

LOS GENERADORES LEX Y YACC


Contenido

Presentacin
1

Introduccin a los generadores Lex y Yacc

Las expresiones regulares Lex

El programa generado por Lex

19

Forma de una especificacin Lex

23

Seleccin de patrones

29

Generacin de analizadores lexicogrficos

37

El programa generado por Yacc

41

Forma de una especificacin Yacc

43

Generacin de analizadores sintcticos

51

Utilidad de Lex y Yacc

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

INTRODUCCIN A LOS GENERADORES LEX Y YACC


Generalidades
Un generador de analizadores es un programa que acepta como entrada la especificacin
de las caractersticas de un lenguaje L y produce como salida un analizador para L. La
especificacin de entrada puede referirse a la lexicografa, la sintaxis o la semntica; el
analizador resultante servir para analizar las caractersticas especificadas.

Generador

E Especificacin de las caractersticas del lenguaje L


A Analizador para L
Los generadores Lex y Yacc sirven, respectivamente, para generar analizadores
lexicogrficos y analizadores sintcticos para su aprovechamiento como partes de los
compiladores de los lenguajes de programacin; estos usos de Lex y Yacc no son los
nicos, aunque s son los que aqu se consideran principalmente.

Para entender cabalmente el funcionamiento de los generadores de analizadores,


hay que conocer la teora de compiladores relacionada con las tareas de anlisis de
lenguajes.
Cuando se emplea el trmino Lex, se mencionan dos posibles significados:
a) una notacin para especificar las caractersticas lexicogrficas de un lenguaje de
programacin,
b) un traductor de especificaciones lexicogrficas.
Esta misma dualidad tambin es de aplicacin al trmino Yacc.
Esquema de uso
El esquema de la pgina siguiente ilustra la manera de usar los generadores Lex y Yacc
para obtener un analizador lxico-sintctico de un lenguaje de programacin L, y de
ejecutar el analizador obtenido. Los nombres que aparecen en el esquema significan:
eLexic.l
es la especificacin de las caractersticas lexicogrficas del lenguaje L, escrita en Lex
eSint.y
es la especificacin de las caractersticas sintcticas del lenguaje L, escrita en Yacc
lex.yy.c
es el analizador lexicogrfico de L generado por Lex; est constituido, en su parte
principal, por una funcin escrita en C que realiza las tareas de anlisis lexicogrfico
basndose en autmatas regulares reconocedores de la forma de las piezas sintcticas
de L
libl
es una librera asociada a Lex que contiene estructuras de datos y funciones a las que
se puede hacer referencia desde el cdigo generado
liby
es una librera asociada a Yacc con la misma utilidad que la anterior
y.tab.c
es el analizador sintctico generado por Yacc; est constituido, en su parte principal,
por una funcin escrita en C que realiza las tareas de anlisis sintctico segn el
mtodo ascendente LALR(1), basado en tablas
anLeSi
es el analizador generado; analiza las caractersticas lexicogrficas y sintcticas
especificadas del lenguaje L; acepta como entrada un programa escrito en L y
comprueba si est codificado segn las especificaciones dadas
P
programa escrito en el lenguaje L
No es preciso que los nombres de los ficheros de entrada para Lex y Yacc tengan una
extensin determinada; los nombres de los ficheros generados por Lex y Yacc son
siempre los indicados, con independencia de cul sea el nombre de los ficheros de
entrada.
Obtencin y ejecucin del analizador

El analizador lxico-sintctico se obtiene tras la realizacin de los siguientes pasos:


1) vi eLexic.l
edicin del fichero con las caractersticas lexicogrficas
2) vi eSint.y
edicin del fichero con las caractersticas sintcticas
3) lex eLexic.l
traduccin de las caractersticas lexicogrficas
4) yacc eSint.y
traduccin de las caractersticas sintcticas
5) cc lex.yy.c y.tab.c ll ly o anLeSi
compilacin del cdigo de los analizadores generados
(El orden de pasos citado es una posibilidad, no una necesidad; se ha supuesto el uso del
editor vi).
Los ficheros sobre los que acta el analizador lxico-sintctico generado son (salvo que
de manera explcita se indique otra cosa) los pre-definidos de entrada y de salida; as
pues, la ejecucin del analizador obtenido puede hacerse de una las siguientes formas:
anLeSi
anLeSi <Entrada
anLeSi <Entrada >Salida
segn que se haga o no re-direccionamiento de los ficheros de entrada y de salida predefinidos.
En el fichero de entrada se proporciona un programa escrito en L. La salida, que debe de
informar sobre el resultado del anlisis, puede ser ms o menos elaborada; por ahora se
considera la posibilidad ms sencilla. Si el programa analizado es correcto en lo que
respecta al lxico y a la sintaxis, no se emite indicacin alguna; si el programa no es
correcto porque tiene algn error lexicogrfico o sintctico, se emite el mensaje syntax
error (ms adelante se justificar la razn por la que se emite este mensaje, tanto si se
trata de un error lexicogrfico como si es uno sintctico).
Ejemplo 1
Con este ejemplo inicial se muestra, antes de empezar el estudio detallado, una aplicacin
de los generadores Lex y Yacc para obtener un analizador lxico-sintctico de un
lenguaje simple. Se proporciona la solucin sin explicacin alguna; lo nico que se
pretende ahora es obtener automticamente un analizador, y probar su funcionamiento.
Se trata de una clase sencilla de expresiones aritmticas en las que pueden
encontrarse:
- variables (nombres escritos en minsculas, de cualquier longitud),
- constantes (nmeros con cifras decimales de cualquier longitud),
- operadores ( +, *),
- parntesis.
La sintaxis de las expresiones se define mediante la siguiente gramtica (escrita en
notacin BNF-Ampliada):

<Expresion> ::= <Termino> { + <Termino> }


<Termino> ::= <Factor> { * <Factor> }
<Factor>

::= ( <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 ;

| pAbr Expresion pCer ;


%%
main () {
yyparse ();
}
La raya vertical puesta a la izquierda de las dos especificaciones representa la posicin de
la primera columna de las lneas de los ficheros de tipo texto.
Si el analizador obtenido se aplica a la entrada (x + y) * cota, que es una expresin
correcta, no se obtiene mensaje alguno como resultado del anlisis; si se aplica a la
entrada x+y*+z, que es una expresin incorrecta, se obtiene como salida la indicacin

syntax error; si se aplica a la entrada x + y? z, que contiene un carcter que no pertenece


al alfabeto (error lexicogrfico), tambin se obtiene el resultado syntax error.

LAS EXPRESIONES REGULARES LEX


Introduccin
En captulos posteriores se expondr con detalle que Lex es un lenguaje (y un traductor para
las especificaciones escritas en l) que sirve para asociar acciones a la presencia de palabras
(secuencias de caracteres) ledas de un fichero de tipo texto; las formas de las palabras que
se pretende detectar se especifican en Lex mediante una notacin que es una ampliacin de
las expresiones regulares.
Pero antes de esa exposicin, en este captulo se describen las expresiones regulares
Lex con una amplitud suficiente para poder realizar la especificacin de las caractersticas
lexicogrficas ms habituales de los lenguajes de programacin; no se trata de una
descripcin exhaustiva: no se han incluido algunos aspectos ms difciles y empleados con
menor frecuencia.
Aunque el captulo empieza con un recordatorio de la definicin bsica de las
expresiones regulares, para su lectura es preciso tener conocimientos sobre el papel que
tienen las expresiones regulares en la caracterizacin de los lenguajes regulares.

EL PROGRAMA GENERADO POR LEX


Anlisis de secuencias de caracteres
El problema que se pretende resolver con la ayuda de esta herramienta consiste en
encontrar en un texto grabado en un fichero secuencias de caracteres que se ajusten a
unas determinadas formas (a unos determinados modelos o patrones), y una vez
encontrada una secuencia que se ajusta a un patrn proceder a la realizacin de unas
operaciones que se tengan asociadas a ese patrn. En la figura adjunta el smbolo
representa el final de una lnea en el fichero de texto analizado.

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

Entrada y salida para el traductor Lex


El siguiente esquema muestra en qu consisten la entrada y la salida para el programa
traductor Lex (por ahora se trata de una descripcin preliminar; ms adelante se
ampliarn los detalles). La entrada a Lex es una especificacin en la que se asocian
los patrones y las acciones.

Especificacin

Programa

...
.

yylex

(eLexic.l)

lex

(lex.yy.c)

Los patrones se describen mediante expresiones regulares (ampliadas sobre su


definicin originaria). El uso de expresiones regulares est justificado por estas
razones:
- una expresin regular es un texto, una sucesin de caracteres que resultan fciles
de leer y procesar por el traductor Lex,
- suele ocurrir que la forma de las secuencias de caracteres a las que se quiere
aplicar el analizador generado se puede describir con facilidad mediante
expresiones regulares.
Las acciones se describen mediante cdigo escrito en el lenguaje C debido a que:
- el traductor Lex produce un analizador que es un programa escrito en C
- el cdigo de las acciones escrito en la especificacin de entrada se traslada de
manera literal a la salida, esto es, queda incorporado al programa escrito en C.
La salida producida por Lex es un programa cuya parte principal la constituye una
funcin, de nombre yylex, que realiza el anlisis de un texto segn los patrones
indicados en la especificacin de la entrada. El algoritmo definido por la funcin est
representado en el siguiente ciclo:
mientras quede texto por analizar

acoplar un patrn a partir del punto actual de la


entrada
avanzar en la entrada hasta sobrepasar la
secuencia acoplada
ejecutar la accin asociada al patrn acoplado

Llamadas a la funcin generada


Aunque las preguntas y los comentarios que ahora se exponen se irn concretando y
confirmando en los puntos siguientes, ya desde ahora cabe insistir en que son
cuestiones importantes que han de tenerse en cuenta.
Lex genera una funcin de nombre yylex; en relacin con esta funcin procede
hacer estas dos preguntas que siguen.

1) Para que se ejecute yylex ha de producirse una llamada, desde dnde se


puede realizar esa llamada? Desde distintos lugares:
- desde la funcin que haga las veces de analizador sintctico, en el caso
de que la funcin generada yylex sea un analizador lexicogrfico,
- desde la funcin principal main,
- desde otra funcin cualquiera.
En todo caso, la funcin llamante puede estar incorporada o no a la propia
especificacin.
2) Qu tarea se pretende realizar cuando se ejecuta una llamada a yylex? La
tarea de anlisis llevada a cabo por yylex se ha esbozado anteriormente, pero
restan detalles por concretar:
- se lee todo el fichero en una nica llamada o hay que realizar varias
llamadas para completar la lectura,
- se devuelve algn valor o se actualizan variables globales,
- se obtienen o no resultados globales relativos a todo el texto grabado en
el fichero de entrada.
Como se aprecia en el algoritmo que se ejecuta cada vez que se llama a la funcin
yylex, se trata de un ciclo repetido reiteradamente hasta que el fichero de entrada
queda analizado por completo. En efecto, en general, con una nica llamada a yylex
es suficiente para analizar por completo el fichero de entrada; pero tambin es posible
que las acciones asociadas a los patrones lleven incorporadas sentencias return. La
presencia de una sentencia return hace que se abandone la ejecucin del ciclo y se
devuelva el control a la rutina llamante, sin proseguir con la lectura del texto de
entrada; la lectura ese texto se completar en sucesivas llamadas a yylex.
Lo habitual es que el anlisis del texto de entrada comience con la primera
llamada a yylex.

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

mientras quede texto

mientras quede texto

return

con el programa generado por Lex.


Si se vuelve sobre el ejemplo 1 expuesto en el captulo inicial, se observa que en las
acciones asociadas a todas las expresiones regulares, excepto en una, est incluida la
sentencia return; en el prximo captulo se ver un ejemplo en el que ninguna de
las acciones de la especificacin tiene incluida esa sentencia de devolucin del
control. As pues, la cantidad de patrones que se aplican en cada llamada a la funcin
generada y, por lo tanto, la cantidad de texto del fichero de entrada que se analiza
durante la ejecucin de esa llamada depende de las acciones que se asocien a los
patrones.

Seleccin de patrones
Criterio de seleccin

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

%%

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

En la seccin de reglas se pone, como ya se ha descrito, la relacin de los patrones y


las acciones a ellos asociadas.

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

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

El criterio de seleccin del patrn es:


1) A partir del carcter actual se intentan aplicar de manera simultnea todos los
patrones de la especificacin; habr patrones que se puedan acoplar a la parte
actual de la entrada, y otros que no se puedan.
2) De todos los patrones que se acoplan a la parte actual de la entrada, se
selecciona el que se acopla a la secuencia de caracteres de mayor longitud (a
partir del carcter actual).
3) Si ocurre que varios patrones se acoplan sobre la misma secuencia de longitud
mxima, se selecciona de entre ellos el que est situado antes en la relacin de
patrones de la seccin de reglas de la especificacin.
Segn este criterio, ocurre que, en general, no es indiferente el orden de colocacin de
los patrones en la seccin de reglas.
Si, por ejemplo, en una especificacin de reglas se tienen, en el orden indicado a
continuacin, los patrones representativos de una constante entera y de una constante
decimal



[0-9]+
[0-9]+\.[0-9]+

{ return pCteEnt; }
{ return pCteDec; }

y en la entrada la secuencia actual es 47.015+, se acoplar la secuencia de


longitud 6 que representa la constante decimal.
Cuando, como ocurre en muchos lenguajes de programacin, las palabras reservadas
tienen una forma que es un caso particular de la forma de los identificadores, al
escribir una especificacin Lex para detectar palabras reservadas e identificadores,
hay que tener el cuidado de colocar el patrn de los identificadores detrs de todos los
patrones de las palabras reservadas; as, en el caso de Pascal (lenguaje en el que no se
distingue entre minsculas y maysculas) la colocacin de los patrones debera de
ser:

[eE][nN][dD]

[wW][hH][iI][lL][eE]

[a-zA-Z][a-zA-Z0-9]*

{ return prEnd; }
{ return prWhile; }
{ return pId; }

de esta manera, cuando en la entrada se encontrase la secuencia de caracteres


end;, se considerar como palabra reservada y no como identificador.

Forma de una especificacin Yacc


Esquema general
Una especificacin Yacc est formada por 3 secciones, tal y como se ilustra en el
siguiente esquema; la raya vertical de la izquierda representa el comienzo de las
lneas del fichero texto de entrada, es decir la posicin de la primera columna de cada
lnea. Puede observarse que es por completo anloga a la forma de una especificacin
Lex.


%{

%}

%%

%%

Cdigo de
Definiciones

Seccin
de
Definiciones
Definiciones
para los
smbolos

Seccin
de
Reglas

Seccin
de
Rutinas

En el primer captulo ya se ha mostrado un ejemplo inicial de especificacin escrita


en Yacc, que se puede tomar como referencia para las explicaciones de este captulo.
En lo que sigue se explica la correspondencia que hay entre las distintas partes de una
especificacin de entrada y la estructura del correspondiente fichero de salida; en esas
explicaciones se hace mencin de la siguiente figura que refleja la relacin entre la
entrada y la salida del traductor Yacc.

y.tab.c
C1
Especificacin escrita en
Yacc
%{

C(D)
C1

P1
%}

D
C2
%%

P2
P3

%%

P4

C2
yyparse

La codificacin de una especificacin Yacc no es tan rgida como la de una


especificacin Lex: podra decirse que la entrada a Yacc se escribe con un formato
libre; no obstante, la costumbre y el estilo tpico de las especificaciones Yacc indican
que:
- el separador de secciones %% y los delimitadores %{ y %} se coloquen en la
primera columna de una lnea,
- los smbolos de la parte izquierda de las producciones se empiecen a escribir a
partir de la primera columna de una lnea.
Una especificacin Yacc ha de tener al menos una produccin en la seccin de
reglas. Las secciones de definiciones y de rutinas pueden estar vacas; en caso de
ausencia de la seccin de rutinas puede suprimirse el separador %% que marca el final
de la seccin de reglas; el separador %% entre las secciones de definiciones y de reglas
siempre ha de estar presente, aunque no exista la seccin de definiciones.

Seccin de definiciones. Cdigo de definiciones


Se trata de cdigo escrito en C y delimitado por los smbolos %{ y %}; no es necesario
que estos delimitadores se pongan a partir de la primera columna de una lnea, pero es
la costumbre.
El cdigo C de esta seccin suele estar formado por definiciones globales de
objetos que se usan en otras partes del cdigo generado. Este cdigo se traslada
literalmente al fichero de salida, al principio de cdigo generado.
En el esquema que relaciona la especificacin y el fichero generado, este
cdigo est representado por C1.
Seccin de definiciones. Definiciones de los smbolos
En la seccin de definiciones tambin se incluyen diversas definiciones relativas a los
smbolos terminales y no terminales de la gramtica sintctica especificada con
posterioridad en la seccin de reglas.
Cada smbolo terminal se declara poniendo su nombre y un valor numrico
asociado a l; esta asociacin se define con objeto de fijar el valor empleado en la
comunicacin entre los analizadores lexicogrfico y sintctico. En esta declaracin se
emplea la palabra reservada %token; as, por ejemplo, las declaraciones
%token nombre1 n1
%token nombre2 n2

definen nombre1 y nombre2 como nombres de smbolos terminales de la gramtica


sintctica, y asocian el valor n1 al smbolo nombre1 y el valor n2 al smbolo
nombre2. Los valores asociados mediante la declaracin %token deben de
concordar con los valores que realmente devuelve el analizador lexicogrfico (valores
que se habrn definido para las piezas sintcticas en la especificacin de Lex).
Conviene insistir de nuevo en que hay otra manera ms simple y ms segura
de definir los valores asociados a los smbolos terminales de la gramtica sintctica
(piezas sintcticas en la especificacin lexicogrfica), pero su descripcin se deja para
ms adelante; la que de momento se emplea se considera ms didctica.
Los nombres de los smbolos terminales pueden declararse en cualquier
orden; los valores que se asocian a los nombres pueden ser cualesquiera, y no
necesariamente consecutivos (hay alguna excepcin: el valor numrico 0 representa
que el analizador lexicogrfico ha alcanzado el final del fichero, el valor numrico -1
tiene un significado especial para el programa generado por Yacc).

Aunque es prctica extendida poner en cada lnea una palabra reservada


%token para declarar un nico nombre de smbolo, es posible poner estas
declaraciones agrupadas de otras maneras.
Los nombres dados a los smbolos terminales pueden construirse con letras
(minsculas y maysculas), cifras decimales y el carcter guin inferior; una cifra no
puede ser el primer carcter del nombre.
El smbolo (no terminal) inicial de la gramtica se declara mediante la palabra
reservada %start; as la declaracin
%start nombre
indica que se considere nombre como el smbolo inicial de la gramtica.
Los nombres de los smbolos no terminales se construyen segn las mismas
normas que las indicadas para los smbolos terminales. La declaracin %start se
puede colocar en cualquier parte de la seccin (no es necesario que preceda o que siga
a las declaraciones %token) y su presencia no es obligada (si no se incluye, se
considera como smbolo inicial de la gramtica el smbolo de la parte izquierda de la
primera regla que se ponga en la seccin de reglas).
Ntese que los smbolos no terminales de la gramtica sintctica no se
declaran explcitamente: no estn anotados en la seccin de definiciones; en el
traductor Yacc se presupone que son smbolos no terminales todos aquellos smbolos
empleados en la escritura de la gramtica (en la seccin de reglas) y que no han sido
declarados de antemano como smbolos terminales.
Al contrario que en la notacin BNF, no se tiene una manera propia para
escribir los smbolos no terminales; en Yacc los smbolos terminales y los no
terminales se escriben segn las mismas normas citadas; se distinguen unos de otros
porque los smbolos terminales se declaran y los smbolos usados pero no declarados
se toman como no terminales.

Hay otras declaraciones relativas a los smbolos de la gramtica que no se consideran


aqu; son las que emplean las palabras reservadas %left, %right y %nonassoc.
A modo de ejemplo, se expone a continuacin la declaracin de smbolos
correspondiente a la gramtica sintctica del Ejemplo 3.
%token
%token
%token
%token
%token
%token
%token
%token
%token
%token

%start

prEnteros
1
prCaracteres 2
prTabla
3
prDe
4
pId
5
pNum
6
pPuntero
7
pAbrir
8
pCerrar
9
pSeparador
10
Tipo

En la figura que representa la relacin entre la especificacin de entrada y el cdigo


de salida las declaraciones de esta seccin estn indicadas mediante D. A partir de
cada declaracin %token se obtiene una declaracin #define de cdigo C que se
traslada al fichero generado; as, por ejemplo,

%token pAbrir 8

se transforma en
#define pAbrir 8

En la figura representativa citada este cdigo est indicado mediante C(D).

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.

-Inexistencia de una notacin ampliada.


La notacin BNF tiene dos variantes: Ampliada y No-Ampliada; en la ampliada se
usan metasmbolos para simplificar la escritura. En la notacin de Yacc no son de
uso tales metasmbolos. Se podra resumir esta carencia de Yacc diciendo que una
gramtica de entrada a Yacc ha de escribirse en notacin no ampliada.
La escritura de las reglas de la gramtica es flexible (puede codificarse en formato
libre); no obstante, ciertas prcticas se han convertido en estilo habitual de codificacin.
Por ejemplo: cada regla se escribe (si cabe) en una lnea nica y el smbolo no terminal
de la parte izquierda se escribe a partir de la primera columna.
En general, el orden de colocacin de las reglas es indiferente; pero hay casos
especiales (para gramticas que tienen ciertas peculiaridades) que no se consideran
aqu en los que el orden de colocacin de las reglas influye en el funcionamiento del
analizador generado.
A continuacin se exponen algunos ejemplos. Considrese la produccin, escrita en
notacin BNF-Ampliada
<Lista> ::= id { , id }
En primer lugar se transforma esta regla para dejarla en notacin BNF No-Ampliada
<Lista>
::= id <RestoLista>
<RestoLista> ::= , id <RestoLista>
|

Las producciones transformadas se escriben en la notacin Yacc de la siguiente


manera:

%token pId 7

%token pComa 15

%%

Lista
: pId RestoLista ;
RestoLista : pComa pId RestoLista ;

|
;

Ya se ha comentado que en las especificaciones sintcticas escritas en Yacc suelen


resultar preferibles (por cuestiones de eficacia en el funcionamiento del analizador
generado) las gramticas con producciones recursivas por la izquierda. La lista de
identificadores separados entre s por comas que antes se ha especificado mediante la
regla

<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

que escritas con la notacin Yacc quedan as:


Lista

:
|

pId ;
Lista pComa

pId

Como una ilustracin ms de la forma de escribir gramticas en Yacc, a continuacin


se pone la gramtica de la especificacin sintctica del Ejemplo 3 (la declaracin de
los nombres de los smbolos terminales ya se ha expuesto anteriormente):

%%

Tipo : TipoSimple ;

| pPuntero pId ;

| prTabla pAbrir TipoSimple pCerrar


Tipo ;

TipoSimple : prEnteros ;

| prCaracteres ;

| pNum pSeparador pNum ;

prDe

En otro estilo habitual de escritura de las especificaciones Yacc el metacarcter punto


y coma slo se coloca detrs de la ltima regla del grupo que comparten la misma
parte izquierda; tambin se pueden incorporar comentarios escritos con la misma
notacin que en el lenguaje C. Segn esto, los ejemplos anteriores tambin suelen
escribirse de la siguiente manera:

Lista

RestoLista

Lista

:
|
;

:
;
:
|
;

pId
Lista

pId

RestoLista

pComa pId RestoLista


/* palabra vacia */

pComa

pId


Tipo : TipoSimple

| pPuntero pId

| prTabla pAbrir TipoSimple pCerrar


Tipo

TipoSimple : prEnteros ;

| prCaracteres ;

| pNum pSeparador pNum

prDe

El traductor Yacc realiza una serie de comprobaciones sobre la gramtica que se le


proporciona como entrada; as, se comprueba:
- que todos los smbolos no terminales sean tiles,
- que la gramtica sea LALR(1),
- que todas las reglas se empleen en alguna operacin de reduccin.
Si no se cumplen satisfactoriamente las condiciones comprobadas, se emiten
mensajes indicativos de las deficiencias encontradas; algunas deficiencias de la
gramtica impiden que se genere el analizador; otras veces se genera el analizador
aunque se hayan detectado deficiencias en la gramtica (en estos casos se puede decir
que el traductor resuelve mediante la aplicacin de ciertos criterios los defectos
encontrados al analizar la gramtica).
El traductor Yacc, a partir de las reglas de la gramtica, obtiene las funciones de
transicin y de operacin relativas a un autmata en el que apoya el algoritmo
reconocedor (el analizador sintctico); estas funciones, implementadas mediante
tablas, se trasladan al fichero generado.
La funcin yyparse, que realiza el anlisis del texto de entrada, es un
algoritmo nico e invariable para todos los analizadores que se generan; desde esta
funcin se consultan las tablas generadas en cada caso. Puede decirse que el
analizador sintctico generado por Yacc est formado por un algoritmo y unas
estructuras de datos; el algoritmo es el mismo para todas las gramticas, lo que
cambia segn el lenguaje que se analiza son las estructuras de datos.
En el esquema que relaciona la entrada y la salida del traductor Yacc puede
apreciarse que el cdigo de la funcin yyparse ocupa la parte final del fichero
generado.

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.

Utilidad de Lex y Yacc


Otros usos para Lex
Aunque en las explicaciones precedentes se ha hecho hincapi en la utilizacin de
Lex para la generacin de analizadores lexicogrficos de los lenguajes de
programacin, conviene insistir en que tambin pueden servir de ayuda en la solucin
de otros tipos de problemas.
La tarea bsica resuelta mediante el cdigo generado por Lex es la deteccin
en un texto de la presencia de secuencias de caracteres (consecutivos) que tienen una
determinada forma, es decir, que se ajustan a unos determinados patrones; una vez
resuelto este aspecto, el cdigo C incorporado a las especificaciones puede aplicarse
para realizar diferentes tareas, para resolver diversos problemas.
A continuacin se exponen dos ejemplos para ilustrar ciertas clases de
programas que se pueden obtener con la ayuda de Lex.

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

En la solucin que se propone a continuacin no se ha tenido en cuenta la situacin


especial que se da dentro de los comentarios y de los literales; las palabras que son
reservadas se pasan a maysculas en todo caso (cualquiera que sea el contexto en el
que se encuentran). En el texto de entrada las palabras reservadas pueden estar
escritas con letras maysculas o minsculas.
%%

[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

la salida producida deber ser:


i:= i + 1
end;
while

En la solucin que se muestra a continuacin, se define la expresin regular que


denota el carcter punto y coma, pero imponiendo que su contexto por la derecha sea
la palabra end precedida de cero o ms espacios en blanco (o tabuladores o finales de
lnea).
La especificacin Lex para obtener el programa pedido es:
%%

;/[\ \t\n]*[eE][nN][dD]

%%

main () {

yylex ();
}

{ printf (" "); }

La especificacin anterior no es completa; para todos los caracteres que no son un


punto y coma seguido de la palabra end se aplica el patrn por defecto; en este caso
se aprovecha que la tarea pretendida es reproducir todos los caracteres del texto
fuente, excepto los punto y coma que se suprimen. Tambin se graban los caracteres
de fin de lnea: as se consigue reproducir la estructura de lneas del fichero de
entrada.
En la solucin expuesta no se ha tenido en cuenta la posibilidad de que estn
presentes varios smbolos punto y coma precediendo a la palabra reservada end. Si se
desea incorporar esta posibilidad, en la especificacin habra que poner la expresin
regular:
;/[\ \t\n;]*[eE][nN][dD]

Con esta segunda solucin, para el texto de entrada


i := i + 1;
;
end;
;
end;
while

se obtendr la salida:
i := i + 1
end
end;
while

Otros usos para Yacc


En los captulos precedentes se ha considerado Yacc como una herramienta para
producir analizadores sintcticos de los lenguajes de programacin; pero no es se su
nico provecho.
Muchos programas tienen que hacer operaciones sobre un texto de entrada y tambin
comprobar que esa entrada est correctamente organizada. Cuando la estructura de un
texto de entrada puede especificarse mediante una gramtica de contexto
independiente, resulta apropiado el uso de Yacc para obtener un algoritmo que
compruebe la correccin del texto tratado.
A continuacin se da un ejemplo que pretende ilustrar la utilidad de Yacc
para facilitar la obtencin de programas que sirven para verificar la estructura de un
texto.

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

Se pretende obtener un programa que compruebe que la entrada satisface la estructura


descrita (si no la cumple deber emitirse un mensaje de error). Adems deber
reproducirse el fichero de entrada transformado para que la salida tenga la misma
estructura de lneas y la misma informacin en cada lnea, pero de manera que los
datos se pongan en cada lnea segn el siguiente orden:
- las palabras que constituyen el nombre propio; si hay varias palabras, si el
nombre est formado por varias palabras, se mantendrn entre ellas las mismas
separaciones que tienen en el fichero de entrada,
- un espacio en blanco,
- las palabras que constituyen los apellidos, manteniendo entre todas las palabras
las mismas separaciones que tienen en el fichero de entrada,
- dos espacios en blanco,
- el par de caracteres >>,
- dos espacios en blanco,
- la calificacin; si en la entrada est como nmero decimal, en la salida se pone de
la misma manera; si en la entrada est como nmero entero, en la salida se
reproduce adjuntndole por la derecha una coma y el nmero cero,
Si delante de la primera palabra de una lnea del fichero de entrada hay espacios en
blanco o tabuladores, han de suprimirse en la correspondiente lnea de salida.
Para el ejemplo dado de fichero de entrada, la salida que ha de obtenerse es:
Enriqueta de la Calle
Cepero >> 8,0
Jose Maria
Antonio del Rio de la Plaza >>
Margarita Cerezo
del Peral >> 8,00
Leopoldo Iniesta
Zurbaran >> 7,5

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"

char * losApell [50];


%}

espacio [\ ]+
palabra [a-zA-Z]+
numEnt
[0-9]+
numDec
{numEnt}\,{numEnt}
separa
[\ ]*;[\ ]*

%%

{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]; }

La especificacin de entrada a Yacc es:


%token nombre
%token apellidos
%token ptocoma
%token nota
%token 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");

En la especificacin Lex se emplea la variable losApell para dejar anotados de


manera provisional los apellidos, para proceder a su grabacin despus de la del
nombre propio.
La solucin expuesta no es la nica posible; tambin se podra aprovechar la
posibilidad (no estudiada aqu) de asociar acciones semnticas a las reglas sintcticas.
Ejemplo 1
%{
#define pId 1
#define pCte 2
#define pSum 3
#define pMul 4
#define pAbr 5
#define pCer 6
#define Error 999
%}
%%
[a-z]+
{ return
[0-9]+
{ return
"+"
{ return
"*"
{ return
"("
{ return
")"
{ return
[\ \t\n]
{ ;
.
{ return

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
%%

Asignacion : pId pAsig Expresion ';'

pId; }
pCteEnt; }
pCteDec; }
pCteExp; }

Expresion : ExprSimple RestoExpresion ;


RestoExpresion : OpRel ExprSimple ;
|
;
ExprSimple : Termino RestoSimple ;
| '+' Termino RestoSimple ;
| '-' Termino RestoSimple ;
RestoSimple : RestoSimple OpAdt Termino ;
|
;
Termino : Factor RestoTermino ;
RestoTermino : RestoTermino OpMul Factor ;
|
;
Factor : '(' Expresion ')' ;
| pId ;
| pCteEnt ;
| pCteDec ;
| pCteExp ;
| prNot Factor ;
OpRel :
|
|
|
|
|
OpAdt :
|
|
OpMul :
|
|
|
|

'<' ;
'=' ;
'>' ;
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");

{ printf (" "); }

}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}

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);
}