You are on page 1of 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 2 3 4 5 6 7 8 9 10 Introduccin a los generadores Lex y Yacc Las expresiones regulares Lex El programa generado por Lex Forma de una especificacin Lex Seleccin de patrones Generacin de analizadores lexicogrficos El programa generado por Yacc Forma de una especificacin Yacc Generacin de analizadores sintcticos Utilidad de Lex y Yacc 1 7 19 23 29 37 41 43 51 61

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: . . . . . . . .

P1 P2 . . . . . . . . . .

A1 A2

Pq

. . . . . .

Aq

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

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

... .

lex

(eLexic.l)

(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.

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

Pi .............. . .............. .

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

%%

Ai

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 aplicacin de

mayor longitud

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

%%

C2 yyparse

P4

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

: ; : | ;

pId

RestoLista ;

pComa pId RestoLista /* palabra vacia */

Lista

: | ;

pId Lista

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:
and div file in of record type array do for label or repeat until begin case downto else function goto mod nil packed set var then while const end if not procedure to with

program

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 %% {letra}+ {cifra}+ \n . %% main () { yylex (); printf ("Palabras: %d\n", nPal); printf ("Numeros : %d\n", nNum); } { { { { ++nPal; } ++nNum; } ; } ; } [a-z] [0-9]

Ejemplo 3

%{ #define #define #define #define #define #define #define #define #define #define #define %} letra digito %% ENTEROS CARACTERES TABLA DE { { { { return return return return prEnteros; } prCaracteres; } prTabla; } prDe; } [a-zA-Z] [0-9] prEnteros 1 prCaracteres 2 prTabla 3 prDe 4 pId 5 pNum 6 pPuntero 7 pAbrir 8 pCerrar 9 pSeparador 10 Error 999

{letra}({letra}|{digito})* {digito}+ "^" "[" ] ".." { { { { return return return return

{ return pId; } { return pNum; }

pPuntero; } pAbrir; } pCerrar; } pSeparador; }

[\ \t\n] .

{ ; } { return Error; }

%token %token %token %token %token %token %token %token %token %token

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; } } } } } { { { { return return return return pId; } pCteEnt; } pCteDec; } pCteExp; }

{Letra}({Letra}|{Digito})* {Digito}+ {Digito}+"."{Digito}+ {Digito}+("."{Digito}+)?{Exp} "<"= >= "<"> := [\ \t\n]+ . { { { { { { 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 ';'

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 : | | | | %% 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"); } '<' ; '=' ; '>' ; pMei ; pMai ; pDis ; '+' ; '-' ; prOr ; '*' ; '/' ; prMod ; prDiv ; prAnd ;

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 %% main () { yylex (); }

{ { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { { {

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

("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 ("%s", yytext); }

Ejemplo 6
%% ;/[\ \t\n]*[eE][nN][dD] %% main () { yylex (); }

{ printf (" "); }

Ejemplo 7
%{ #include "y.tab.h" char * losApell [50]; %} espacio palabra numEnt numDec 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]; } [\ ]+ [a-zA-Z]+ [0-9]+ {numEnt}\,{numEnt} [\ ]*;[\ ]*

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

You might also like