Академический Документы
Профессиональный Документы
Культура Документы
i=0
L
i
L
+
= U
i=1
L
i
Lgicamente: L
i
= L
i-1
L
* tiene mayor precedencia, luego concatenacin y por ltimo la |. Todas
ellas son asociativas por la izquierda.
S
a
b
B
a
a
Z
C
b
a
b
27
Llamamos conjunto regular al lenguaje asociado a una expresin regular.
Ejemplo.- Identificadores en Pascal
Su expresin regular sera: letra (letra | nmero)*
Ejemplo.-
E = T = alfabeto = {a, b}
a | b = {a, b}
a* = {c, a, aa, ...}
(a | b) (a | b) = {aa, bb, ab, ba}
(a | b)* = (a*b*)*
NOTA: Algunas abreviaturas utilizadas en la bibliografa son las siguientes:
r? = r | c
r
+
= rr*
r* = r
+
| c
(r)? = L(r) U {c}
[a, b, c] = a | b | c
[a-z] = a | b | c | ... | z
3.5 Algoritmo de Thompson.
Este algoritmo nos va a permitir convertir una expresin regular en un
autmata finito no determinista con transiciones (AFND-).
Un AFND- es un AFND al que incorporamos a Te un elemento ms, el , con
lo cual: Q x Te U {} Q. Es decir, se puede mover de estado si usar entrada.
Los pasos a seguir son los siguientes:
i) Para construiramos:
ii) a e Te construiramos:
iii) Tenemos M(s), M(t), dos AFND para 2 expresiones regulares s y t, de la
forma:
M(s)
0
f
M(t)
0
f
0 f
a
0 f
28
Entonces s|t podemos reconocerlo con un AFND- de la forma:
Entonces st podemos reconocerlo con un AFND- de la forma:
Entonces s* podemos reconocerlo con un AFND- de la forma:
Ejemplo.- Sea la expresin regular r = (a | b)* abb, crear el AFND-.
3.6 Transformacin de un AFND- en un AFD.
Primeramente definimos:
Si S es un estado:
Cerradura-E (S) = { S
k
e AFND / S
S
k
}
Siendo W un conjunto de estados:
Cerradura-E (W) = { S
k
e AFND / - S
j
e W, S
j
S
k
}
Y la funcin de movimiento (llammosle Mueve):
Mueve (W,a) = { S
k
e AFND / - S
j
e W, S
j
a
S
k
}
El mecanismo para realizar la transformacin consiste en:
M(s)
0a
fa
M(t)
0b
fb
0
0
M(s)
0
fa
M(t)
0b
f
M(s)
0a
fa
0
f
2 3
4 5
1
6
0
7 8 9 10
a b b
a
b
29
1) Calcular primero el nuevo estado A = Cerradura-E(0), es decir, la
Cerradura-E del estado 0 del AFND-.
2) Calculamos Mueve (A, a
i
), a
i
e T. Luego calculamos Cerradura-E (Mueve
(A, a
i
)), a
i
e T, es decir, para todos los terminales, y as vamos generando
los estados B, C, D...
3) Calculamos Mueve (B, a
i
), a
i
e T, y hacemos lo mismo. Esto se repite
sucesivamente hasta que no podamos inclur nuevos elementos en los
conjuntos Cerradura-E.
Algoritmo
BEGIN
Calcular Cerradura-E (0) = Estado A;
Inclur A en NuevosEstados;
WHILE no estn todos los W de NuevosEstados marcados DO BEGIN
Marcar W;
FOR cada a
i
e Te DO BEGIN
X = Cerradura-E (Mueve (W, a
i
));
IF X no est en el conjunto NuevosEstados aadirlo;
Transicin [W, a] = X;
END
END
END
Sern estados finales todos aquellos que contengan alguno de los estados
finales del autmata original.
Ejemplo.- Si tenemos el siguiente AFND- que reconoce la expresin regular r
= (a | b)* abb, convertirlo en un AFD.
Cerradura-E (0) = {0, 1, 7, 4, 2} = A
Mueve (A, a) = {3, 8}
Mueve (A, b) = {5}
Cerradura-E (Mueve(A,a)) = {3, 6, 7, 1, 2, 4, 8} = B (A con a mueve a B)
Cerradura-E (Mueve(A,b)) = {5, 6, 7, 1, 2, 4} = C (A con b mueve a C)
Mueve (B, a) = {3, 8} = Mueve (A, a) (B con a mueve a B)
Mueve (B, b) = {5, 9}
Mueve (C, a) = {3, 8} = Mueve (A, a) (C con a mueve a B)
Mueve (C, b) = {5} = Mueve (A, b) (C con b mueve a C)
2 3
4 5
1
6
0
7 8 9 10
a b b
a
b
30
Cerradura-E (Mueve(B, b)) = {5, 6, 7, 1, 2, 4, 9} = D (B con b mueve a D)
Mueve (D, a) = {3, 8} = Mueve (A, a) (D con a mueve a B)
Mueve (D, b) = {5, 10}
Cerradura-E (Mueve(D, b)) = {5, 6, 7, 1, 2, 4, 10} = E (D con b mueve a E)
Mueve (E, a) = {3, 8} = Mueve (B, a) (E con a mueve a B)
Mueve (E, b) = {5} = Mueve (C, b) (E con b mueve a C)
Tabla de transiciones:
Estado a b
A B C
B B D
C B C
D B E
E B C
Vamos a ver el autmata:
3.7 Traductores finitos (TF).
Ser un autmata finito de la forma:
TF = (Q, Te, Ts, o, q
0
, F)
Q es el conjunto de estados.
Te es el alfabeto de entrada.
Ts es el alfabeto de salida.
q
0
es el estado inicial.
F son los estados finales (un subconjunto de Q).
o: Q x {Te U {}} P(Q) x Ts*
A
C
a
b
b
a
b
B D
b
a
a
a
b
E
31
La configuracin del TF viene definida por una tupla (q, t, s), donde q es el
estado actual, t es la tira de caracteres por analizar (t e Te*) y s es la tira que
ha salido (s e Ts*).
Un movimiento es una transicin de la forma:
(q, ae, s) |---- (q, e, sz) cuando o(q, a) = (q, z)
q, q e Q
e e Te*
s e Ts*
Podemos definir el conjunto de traduccin como:
Tr(TF) = {(t, s), t e Te*, s e Ts* / (q
0
, t, ) |--*-- (q
f
, , s), q
f
e F}
Al igual que en el caso de los AF, pueden ser deterministas y no deterministas,
dependiendo de si exste una nica transicin posible o varias ante la misma
entrada y el mismo estado actual.
3.8 Implementacin de autmatas.
3.8.1 Tabla compacta.
Sea el siguiente lenguaje L = {dda$, ab
n
ca$ / n > 0} y su autmata:
La tabla de transiciones (M) correspondiente es la siguiente:
a b c d $
1 2 5
2 2 3
3 4
4 6
5 3
6
Se precisa mucha cantidad de memoria para almacenar esta matriz al
completo, entonces se utiliza la tcnica de la tabla compacta, que consiste en
definir los siguientes vectores:
1
5
$ c
a
d
2 3 4
d
a
6
b
32
iv) Valor.- En l se almacenan los elementos no nulos de la tabla.
v) Columna.- La columna en que se encuentra el elemento del vector
valor.
vi) PreFil.- Este vector tiene tantos elementos como filas tenga la tabla y
almacenaremos los nmeros de orden en el vector valor que comienzan
cada fila no nulos.
vii) NumFil.- Almacenamos el nmero de elementos no nulos que hay en
cada fila.
En nuestro ejemplo sera:
Valor Columna PreFil NumFil
2 1 1 2
5 4 3 2
2 2 5 1
3 3 6 1
4 1 7 1
6 5 0 0
3 4
El espacio ocupado viene dado por: (n de filas x 2) + (n no nulos x 2). En
nuestro caso sera (6 x 2) + (7 x 2) = 26.
En el caso de este ejemplo no parece un ahorro de espacio muy sustancial,
pero supongamos una tabla de tamao 100 x 100. Si suponemos 110 valores no
nulos tenemos que el espacio ocupado sera 10.000 sin comprimir y 420
implementando una tabla compacta.
Vamos a ver a continuacin como se realiza el direccionamiento mediante un
algoritmo.
Algoritmo
Function M (i, j: integer) : integer
VAR
num, com, k: integer;
hallado : boolean;
BEGIN
num := NUMFIL[i];
IF num = 0 THEN M := 9999;
ELSE BEGIN
com := PREFIL[i];
hallado := FALSE;
k := 0;
WHILE (k < num) AND NOT hallado DO
IF col[com+k] = j THEN hallado := TRUE
ELSE k := k + 1;
IF hallado THEN M := VALOR[com + k]
33
ELSE M := 9999;
END;
END;
En nuestro ejemplo, si queremos localizar M[2, 3] haremos:
PreFil [2] = 3, NumFil[2] = 2 Entonces puede ser Valor[3] o Valor[4]
Como columna es 3 entonces M[2, 3] = 3.
3.8.2 Autmata programado.
Otra opcin es hacer un programa para realizar las transiciones, de la
siguiente forma:
estado = 1
WHILE estado = 6 DO BEGIN
LeerCarcter
CASE estado OF
1 : IF Car="a" THEN estado = 2
ELSE IF Car="d" THEN estado = 5
...
2 : IF Car="e" THEN estado = 7
...
...
3.9 Ejemplo. Scanner para nmeros reales sin signo en Pascal.
Vamos a reconocer nmeros del tipo: 23E+12, 100E-15, ...
Reglas BNF:
<nmero> <entero> | <real>
<entero> <dgito> {<dgito>} con la { } indicamos 0 ms veces
<real> <entero> . <dgito> {<dgito>}
| <entero> . <dgito> {<dgito>} E <escala>
| <entero>
| <entero> E <escala>
<escala> <entero>
| <signo> <entero>
<signo> + | -
<dgito 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9
Esta gramtica podemos ponerla en forma de gramtica regular, que sera la
siguiente:
34
<nmero> 0 <resto_nmero>
<nmero> 1 <resto_nmero>
...
<nmero> 9 <resto_nmero>
Este tipo de reglas las resumiremos como:
<nmero> dgito <resto_nmero> (10 reglas)
<resto_nmero> dgito <resto_nmero> (10 reglas)
| . <fraccin>
| E <exponente>
|
<fraccin> dgito <resto_fraccin> (10 reglas)
<resto_fraccin> dgito <resto_fraccin> (10 reglas)
| E <exponente>
|
<exponente> signo <entero_exponente> (2 reglas, + y -)
<entero_exponente> dgito <resto_entero_exponente> (10 reglas)
<resto_entero_exponente> dgito <resto_entero_exponente> (10 reglas)
|
Ahora implementaremos el autmata. Empezamos por definir los estados:
1 nmero 2 resto_nmero 3 fraccin 4 resto_fraccin
5 exponente 6 entero_exponente 7 resto_entero_exponente
El autmata sera:
1
5
$ .
d
d
2 3 4
d
d
8
d
7
6 d
E E
d
$
s
$
35
La transicin que marcamos punteada (de 5 a 7 con dgito), nos permite
aceptar nmeros sin signo en el exponente (por ejemplo.- 2E32), tal y como
estaba originalmente tenamos que poner siempre un + o un -.
La tabla asociada es la siguiente:
ESTADO Dgito . E Signo $
1 2
2 2 3 5 8
3 4
4 4 5 8
5 7 6
6 7
7 7 8
Ejemplo.- Gramtica de las expresiones regulares
<expresin> <literal> | <alternativa> |
<secuencia> | <repeticin> | '(' <expresin> ')'
<alternativa> <expresin> '|' <expresin>
<secuencia> <expresin> <expresin>
<repetition> <expresin> ' * '
<literal> 'a' | 'b' | 'c' | ... ( 'a' | 'b' | 'c' | ... )*
Ejemplo.- Especificaciones de tiempo en UNIX
timespec = time | time date | time increment
| time date increment | time decrement
| time date decrement | nowspec
nowspec = NOW | NOW increment | NOW decrement
time = hr24clock | NOON | MIDNIGHT | TEATIME
date = month_name day_number
| month_name day_number ',' year_number
| day_of_week | TODAY | TOMORROW
| year_number '-' month_number '-' day_number
increment = '+' inc_number inc_period
decrement = '-' inc_number inc_period
hr24clock = INT 'h' INT
inc_period = MINUTE | HOUR | DAY | WEEK | MONTH | YEAR
day_of_week = SUN | MON | TUE | WED | THU | FRI | SAT
month_name = JAN | FEB | MAR | APR | MAY | JUN | JUL
| AUG | SEP | OCT | NOV | DEC
36
3.10 Acciones semnticas.
Estas acciones se pueden inclur en la tabla de transiciones. Por ejemplo, en
el caso de las casillas en blanco, que son errores, se puede informar acerca
del error en concreto.
En el ejemplo del apartado anterior, en la casilla [3, "."] podramos mostrar el
error de "Doble punto decimal" y realizar incluso una correccin automtica.
Otra accin semntica interesante en el caso anterior sera ir almacenando en
la Tabla de Smbolos el valor del nmero que se est introduciendo. Por
ejemplo, una tabla para acciones semnticas de este tipo podra ser:
ESTADO Dgito $
1 T
2 T Z
3 U
4 U Z
5 V
6 V
7 V Z
T: num = 10 * num + dgito (calculamos nmero)
U: n = n + 1 (calculamos fraccin)
num = 10 * num + dgito
V exp = 10 * exp + dgito (calculamos exponente)
El clculo final ser realizara en Z, que sera:
Z: Valor = num * 10
signo exp - n
37
3.11 Generador LEX.
Lex es una de las herramientas que pueden ser utilizadas para generar un
analizador lxico a partir de una descripcin basada en expresiones regulares.
Si tenemos un programa lex en un fuente con nombre ejemplo.l, los pasos
para convertirlo en un ejecutable son los siguiente:
l ex ej empl o. l
cc o mi ej empl o l ex. yy. c l l
/ *A veces puede ser necesar i o t ambi n l m*/
mi ej empl o < f i cher o_pr ueba. t xt
Si se utiliza Flex y Gcc (GNU) :
f l ex ej empl o. l
gcc o mi ej empl o l ex. yy. c l f l
La secuencia es la siguiente:
1. Lex crea un fichero fuente en C, lex.yy.c, desde una descripcin del
analizador escrita en lenguaje Lex.
2. Lex.yy.c se compila (es un programa C) generndose el ejecutable.
3. Este ejecutable analizar una entrada lexicamente para producir una
secuencia de tokens.
3.11.1 Partes de un programa LEX
La estructura de un programa en LEX es la siguiente:
Declaraciones
%%
Reglas de traduccin
%%
Procedimientos Auxiliares
main() /* Si se desea ejecutar algo ms despus del analizador*/
{
yylex();
}
- En la parte de Declaraciones se incluyen identificadores, expresiones
regulares, constantes, los includes, etc
38
- Las reglas de traduccin son de la forma (siendo Pi una expresin
regular):
P1 {Accin 1}
P2 {Accin 2}
... ...
Pn {Accin n}
Es importante hacer notar que el orden de las relgas es fundamental. Las
expresiones regulares ms especficas se deben de poner antes que las ms
generales (ejemplo: ., que es cualquier cosa, si se utiliza, debera de
ponerse al final ya que en otro caso siempre tomara esta expresin regular
como vlida).
Los procedimientos auxiliares son todos aquellos procedimientos en C que se
van a utilizar en las reglas.
3.11.2 Expresiones regulares.
Algunos ejemplos de definicin de expresiones regulares son los siguientes:
Si queremos reconocer nmeros del tipo: 100, 111.02, 1, 111.2, etc, la
expresin regular sera la siguiente:
([0-9]*\.)?[0-9]+
Vamos a revisarla por partes:
[0-9]+
Dgitos de 0 a 9 (los [] indican eleccin de uno de entre el rango), al aadir el
+ indicamos la repeticin 1 o ms veces de lo que lo precede.
([0-9]*\.)
El * significa repeticin al igual que el +, aunque en este caso es de 0 o ms
veces, es decir lo que precede al * puede no aparecer. El . simplemente
indicara cualquier carcter excepto salto de lnea, en este caso queremos
identificar al punto decimal, por ello aadimos el carcter de escape \.
En la siguiente tabla se muestra un resumen de la sintaxis para la definicin
de expresiones regulares:
Symbol | Meani ng
- - - - - - - - - - - - - +- - - - - - - - - - - - - - - - - - -
x | The " x" char act er
. | Any char act er except \ n
[ xyz] | Ei t her x, ei t her y, ei t her z
[ ^bz] | Any char act er , EXCEPT b and z
[ a- z] | Any char act er bet ween a and z
[ ^a- z] | Any char act er EXCEPT t hose bet ween a and z
R* | Zer o R or mor e; R can be any r egul ar expr essi on
R+ | One R or mor e
R? | One or zer o R ( t hat i s an opt i onnal R)
R{2, 5} | Two t o 5 R
39
R{2, } | Two R or mor e
R{2} | Exact l y t wo R
" [ xyz\ " f oo" | The st r i ng " [ xyz" f oo"
{NOTI ON} | Expansi on of NOTI ON, t hat as been def i ned above i n t he f i l e
\ X | I f X i s a " a" , " b" , " f " , " n" , " r " , " t " , or
| " v" , t hi s r epr esent t he ANSI - C i nt er pr et at i on of \ X
\ 0 | ASCI I 0 char act er
\ 123 | ASCI I char act er whi ch ASCI I code i s 123 I N OCTAL
\ x2A | ASCI I char act er whi ch ASCI I code i s 2A i n hexadeci mal
RS | R f ol l owed by S
R| S | R or S
R/ S | R, onl y i f f ol l owed by S
^R | R, onl y at t he begi nni ng of a l i ne
R$ | R, onl y at t he end of a l i ne
<<EOF>> | End of f i l e
3.11.3 Paso de valores en Lex.
Si queremos devolver el valor de un token podemos utilizar return. Por
ejemplo, si hemos definido el valor de NUMBER a 257 (#define NUMBER 257),
podemos incorporar en nuestro scanner lo siguiente:
[ 0- 9] + {r et ur n NUMBER; }
Existen dos variables para el paso de valores a funciones C o bien a un parser
diseado mediante la herramienta YACC. La variable externa yytext contiene
el string de caracteres que son aceptados por la expresin regular. Tambin
tenemos la variable externa yylval, definida como un INTEGER (aunque puede
ser redefinida), pensada inicialmente para el paso del token desde el
analizador lxico al parser. Para asignar el valor de yytext a yylval es
necesario realizar una conversin.
Se puede utilizar la funcin C atoi para convertir el nmero almacenado en
yytext en forma de cadena de caracteres en su valor numrico en yylval:
[ 0- 9] + { yyl val = at oi ( yyt ext ) ;
r et ur n NUMBER;
}
3.11.4 Ejemplos.
Ejemplo1. Identificador de nmeros y palabras
di gi t [ 0- 9]
l et t er [ A- Za- z]
del i m [ \ t \ n]
ws {del i m}+
%%
{ws} { / * i gnor e whi t e space */ }
{di gi t }+ { pr i nt f ( " %s" , yyt ext ) ; pr i nt f ( " = number \ n" ) ; }
{l et t er }+ { pr i nt f ( " %s" , yyt ext ) ; pr i nt f ( " = wor d\ n" ; ) }
%%
mai n( )
{
40
yyl ex( ) ;
}
Ejemplo 2. Un contador de palabras similar al wc de UNIX
%{
unsi gned char Count = 0, wor dCount = 0, l i neCount = 0;
%}
wor d [ ^ \ t \ n] +
eol \ n
%%
{wor d} {wor dCount ++; char Count += yyl eng; }
{eol } {char Count ++; l i neCount ++; }
. {char Count ++; }
%%
mai n( )
{
yyl ex( ) ;
pr i nt f ( " %d %d %d\ n" , l i neCount , wor dCount , char Count ) ;
}
Ejemplo 3. Reconocedor de comentarios en lenguaje C.
%%
( . | \ n) ? {pr i nt f ( " " ) ; }
" / *" " / " *( [ ^*/ ] | [ ^*] " / " | " *" [ ^/ ] ) *" *" *" */ " {pr i nt f ( " [ comment ] %s" , yyt ext ) ; }
%%
NOTA: Pr obar con el si gui ent e ej empl o:
/ * coment ar i os en una l i nea */
/ * coment ar i os sobr e mul t i pl es
l i neas */
coment ar i os / *i ncl ui dos dent r o de una */ l i nea
coment ar i os / **/ r ar os
/ */ Acept a coment ar i os que no l o son?
/ **_/ */ Es cor r ect o, l o acept a?
Ejemplo 4. Calculadora
%{
/ *
** l ex pr ogr amf or si mpl e dc
** i nput of t he f or m: opd opr opd
** out put of t he f or m: opd opr opd = ans
*/
#def i ne NUM 1
#def i ne PLUS 2
#def i ne MI NUS 3
#def i ne MUL 4
#def i ne DI V 5
#def i ne MOD 6
#def i ne DELI M 7
%}
ws [ \ t ] +
%%
{ws} ;
- ?[ 0- 9] + {
41
pr i nt f ( " %s" , yyt ext ) ;
r et ur n ( NUM) ;
}
" +" {
ECHO;
r et ur n ( PLUS) ;
}
" - " {
ECHO;
r et ur n ( MI NUS) ;
}
" *" {
ECHO;
r et ur n ( MUL) ;
}
" / " {
ECHO;
r et ur n ( DI V) ;
}
" %" {
ECHO;
r et ur n ( MOD) ;
}
. |
" \ n" {
ECHO;
r et ur n ( DELI M) ;
}
%%
/ * mai n. c */
# i ncl ude <st di o. h>
mai n( )
{
i nt opd1, opd2, opr ;
/ * Look f or f i r st oper and. */
i f ( yyl ex( ) ! = NUM)
{
pr i nt f ( " mi ssi ng oper and\ n" ) ;
exi t ( 1) ;
}
opd1 = at oi ( yyt ext ) ;
/ * Check f or oper at or . */
opr = yyl ex( ) ;
/ * Bad cases - no oper and - ei t her a del i mi t er f or a number . */
i f ( opr == DELI M)
{
pr i nt f ( " mi ssi ng oper at or \ n" ) ;
exi t ( 1) ;
}
el se i f ( opr == NUM)
{
i f ( ( opd2=at oi ( yyt ext ) ) >= 0)
{
pr i nt f ( " mi ssi ng oper at or \ n" ) ;
exi t ( 1) ;
}
/ * Pr obl emcase - di st i ngui sh oper at or f r omoper and. */
el se
{
opr = MI NUS;
opd2 = - opd2;
42
}
}
/ * Check f or second oper and, i f not yet f ound. */
el se i f ( yyl ex( ) ! = NUM)
{
pr i nt f ( " mi ssi ng oper and\ n" ) ;
exi t ( 1) ;
}
el se / * Must have f ound oper and 2 */
opd2 = at oi ( yyt ext ) ;
swi t ch ( opr )
{
case PLUS: {
pr i nt f ( " = %d\ n" , opd1 + opd2) ;
br eak;
}
case MI NUS: {
pr i nt f ( " = %d\ n" , opd1 - opd2) ;
br eak;
}
case MUL: {
pr i nt f ( " = %d\ n" , opd1 * opd2) ;
br eak;
}
case DI V: {
i f ( opd2 == 0)
{
pr i nt f ( " \ nERROR: at t empt t o di vi de by zer o! \ n" ) ;
exi t ( 1) ;
}
el se
{
pr i nt f ( " = %d\ n" , opd1 / opd2) ;
br eak;
}
}
case MOD: {
i f ( opd2 == 0)
{
pr i nt f ( " \ nERROR: at t empt t o di vi de by zer o! \ n" ) ;
exi t ( 1) ;
}
el se
{
pr i nt f ( " = %d\ n" , opd1 %opd2) ;
br eak;
}
}
}
}
43
4 Anlisis sintctico (Parsing).
Analizar sintcticamente una tira de tokens es encontrar para ella el rbol de
derivacin (rbol sintctico) que tenga como raz el axioma de la gramtica.
Si lo encontramos diremos que la cadena pertenece al lenguaje, pasando a la
siguiente fase, si no lo encontramos quiere decir que no est bien construda y
entrarn en funcionamiento los mensajes de error.
Tenemos dos posibilidades a la hora de realizar el parsing:
a) Parsing a la izquierda, si para obtener el rbol de derivacin empleamos
derivaciones por la izquierda.
b) Parsing a la derecha, si las derivaciones las realizamos por la derecha.
Ejemplo.-
Sea la gramtica:
1. S S + T
2. S T
3. T T * F
4. T F
5. F (S)
6. F a
7. F b
Vamos a realizar el parsing descendente de la cadena "a*(a+b)" por la
izquierda y por la derecha:
Parsing descendente por la izquierda
Parse: 2
Parse: 2 3
Parse: 2 3 4
T
F T
*
S
T
T
*
F
S
a
F
T
S
44
Parsing descendente por la derecha
T
F T
*
F
S
a
T
F T
*
F
S
a
) (
S
Parse: 2 3 4 6
Parse: 2 3 4 6 5
...
T
F T
*
F
S
a
) (
S
T S
+
Parse: 2 3 4 6 5 1
T
F T
*
F
S
a
) (
S
T S
+
F
a
T
F
b
Parse: 2 3 4 6 5 1 2 4 6 4 7
45
As sucesivamente hasta llegar al rbol final (con un parse distinto):
Adems, tambin tenemos dos posibilidades en cuando a si comenzamos
desde la metanocin o desde la cadena que queremos reconocer. Esto sera:
a) Parsing descendente, si comenzamos por la metanocin.
a) Parsing ascendente, si comenzamos por la palabra a reconocer.
Parse: 2
Parse: 2 3
Parse: 2 3 5
T
F T
*
S
T
S
T
T
*
S
F
) (
S
T
F T
*
F
S
a
) (
S
T S
+
F
a
T
F
b
Parse: 2 3 5 1 4 7 2 4 6 4 6
46
En general podemos realizar la siguiente clasificacin:
4.1 Mquinas tericas, mecanismos con retroceso.
En este tipo de algoritmos siempre tendremos el problema de buscar la
trayectoria correcta en el rbol, si seguimos una trayectoria equivocada, es
preciso volver hacia atrs, eso es precisamente el retroceso. Veremos una
serie de mquinas tericas que funcionan mediante este mecanismo.
4.1.1 Autmatas con pila (AP).
Este tipo de autmatas pueden reconocer lenguajes de contexto libre.
Esta mquina terica podemos definirla como:
G = (N, T, P, S)
P: A o ; A e N ; o e (N U T)*
AP = (Q, Te, Tp, o, q
0
, z
0
, F)
Q es el conjunto de estados
Te es el alfabeto de entrada
Tp es el alfabeto de la pila
q
0
es el estado inicial
z
0
es el carcter inicial de la pila
F es el conjunto de estados finales
o funcin Q x {Te U {}} x {Tp U {}} Q x Tp*
Para un estado, una configuracin de la pila y una entrada las imgenes son
varias en el caso de un APND y una sla imagen en un APD.
PARSING
ascendente descendente
EDT, AP
(tericas)
precedencia
operador
TP
(terica)
Herramienta
YACC
LR(K)
LR cannico SLR LALR
simple
LL(K)
47
Llamamos configuracin al estado actual del autmata y podemos definirlo
como:
(q, e, o) e Q x Te* x Tp*
q es el estado en que est el autmata (q e Q)
e es la cadena de caracteres que queda por analizar (e e Te*)
o es la cadena de caracteres que hay en la pila (o e Tp*)
Un movimiento podemos representarlo de la siguiente forma:
(q, ae, zo) |---- (q, e, |o) siendo (q, |) e o(q, a, z)
Si o(q, a, z) = {(p
1
, v
1
), (p
2
, v
2
), ... (p
n
, v
n
)} ser no determinista.
Si o(q, , z) = {(p
1
, v
1
), (p
2
, v
2
), ... (p
n
, v
n
)} ser no determinista de
transiciones .
Configuracin inicial: (q
0
, t, z
0
), t es la tira que queremos reconocer.
Existen tres posibilidades para el reconocimiento de un AP:
1. Una forma de definir un autmata con pila de forma que aceptar una
tira de entrada t si partiendo de una configuracin inicial consigue transitar a
un estado final final empleando movimientos vlidos.
(q
0
, t, z
0
) |--*-- (q
f
, , o) con q
f
e F, o e Tp*
Con esta definicin, la configuracin final ser: (q
f
, , o), q
f
e F
Lenguaje de un autmata con pila definido de esta forma:
L(AP) = { t, t e Te*/ (q
0
, t, z
0
) |--*-- (q
f
, , o)}
Aqu realizamos el reconocimiento por estado final (q
f
e F) pero o no tiene
por qu ser .
2. Hay otra forma de definirlo, de forma que aceptar una tira de entrada
t si partiendo de una configuracin inicial consigue dejar la pila vaca
empleando movimientos vlidos:
L(AP) = { t, t e Te* / (q
0
, t, z
0
)|--*-- (q, , )}
Con esta definicin, la configuracin final ser: (q' , , ), q' e Q
En este segundo caso reconocemos por pila vaca, q no tiene por qu ser un
estado final (en ese caso no definimos estados finales ya que no existen).
3. Tambin se puede definir el reconocimiento del autmata por ambas
condiciones, estado final y pila vaca. Se puede demostrar que todo lenguaje
48
aceptado por una de las dos cosas puede serlo por ambas. Los ejemplos los
veremos con este tipo de reconocimiento.
Con esta definicin, la configuracin final ser: (q
f
, , ), q
f
e F
Ejemplo.- un palndromo.
L = { t c t
r
/ t=(a | b)
+
}
T = {a, b, c}
Por ejemplo, si t = ab, entonces t
r
= ba.
Elementos vlidos del lenguaje seran: abcba, abbcbba, ababcbaba, etc
1 o(q
0
, a, z
0
) = (q
1
, az
0
)
2 o(q
0
, b, z
0
) = (q
1
, bz
0
)
3 o(q
1
, a, ) = (q
1
, a) NOTA : el significa sin mirar la pila
4 o(q
1
, b, ) = (q
1
, b)
5 o(q
1
, c, ) = (q
2
, )
6 o(q
2
, a, a) = (q
2
, ) NOTA : aqu tomamos la a de la pila
7 o(q
2
, b, b) = (q
2
, )
8 o(q
1
, $, z
0
) = (q
f
, )
Con ocho transiciones podemos reconocer el lenguaje. Lo que hacemos es
utilizar la pila para comprobar sin coinciden los caracteres de uno y otro lado.
Se trata de un autmata con pila determinista. El diagrama de estados es el
siguiente:
Ejemplo.-
L = {t t
r
/ t = (a | b)+}
T={a,b}
1 o(q
0
, a, z
0
) = (q
1
, az
0
) Tambin podramos decir:
2 o(q
0
, b, z
0
) = (q
1
, bz
0
)
3 o(q
1
, a, a) = {(q
1
, aa), (q
2
, )} o(q
1
, a, a) = (q
2
, )
4 o(q
1
, a, b) = (q
1
, ab) o(q
1
, a, ) = (q
1
, a)
5 o(q
1
, b, b) = {(q
1
, bb), (q
2
, )} o(q
1
, b, b) = (q
2
, )
6 o(q
1
, b, a) = (q
1
, ba) o(q
1
, b, ) = (q
1
, b)
7 o(q
2
, a, a) = (q
2
, )
0 1 2 f
(a,a,) (a,,a)
(b,,b)
(b,b,)
(a,z
0
,az
0
)
(b,z
0
,bz
0
)
(c,,)
($,z
0
,)
49
8 o(q
2
, b, b) = (q
2
, )
9 o(q
2
, $, z
0
) = (q
f
, )
Se trata de un autmata con pila no determinista.
Vamos a intentar reconocer la cadena abba$:
(0, abba$, z
0
) (1, bba$, az
0
) (1, ba$, baz
0
) (a) y (b)
(a) (1, a$, bbaz
0
) (1, $, abbaz
0
) No aceptamos.
(b) (2, a$, az
0
) (2, $, z
0
) (f, , ) Aceptamos.
4.1.1.1 Conversin de una GCL en un Autmata con pila.
El lenguaje expresado en forma de Gramtica de Contexto Libre puede
tambin expresarse en forma de un Autmata con pila. Veremos mediante un
ejemplo, como, dada una gramtica de contexto libre podemos construr un
autmata con pila que reconoce el mismo lenguaje. El tipo de anlisis que se
realiza es un anlisis sintctico descendente.
Es importante hacer notar que aqu daremos por reconocida la cadena
siempre que la cadena de entrada se ha terminado y la pila est vaca.
Sea la gramtica:
S S+T | T
T T*F | F
F (S) | a | b
Crearemos un autmata con pila de la siguiente forma:
AP = ({q}, {a, *, +, (, )}, Te U {S, T, F}, o, q, S, q}
Como se puede observar, solamente tenemos un estado que es inicial y final.
o(q, , S) = {(q, S+T), (q, T)} o(q, (, () = (q, )
o(q, , T) = {(q, T*F), (q, F)} o(q, ), )) = (q, )
o(q, , F) = {(q, (S)), (q, a), (q, b)} o(q, +, +) = (q, )
o(q, a, a) = (q, ) o(q, *, *) = (q, )
o(q, b, b) = (q, )
A continuacin vamos a ver el camino feliz para reconocer a+a*b:
0 1 2 f
(a,a,)
(a,b,ab)
(b,b,bb) (b,b,)
(a,z
0
,az
0
)
(b,z
0
,bz
0
)
(a,a,)
($,z
0
,)
(b,b,)
(b,a,ba)
(a,a,aa)
50
(q, a+a*b, S) (q, a+a*b, S+T) (q, a+a*b, T+T) (q, a+a*b, F+T)
(q, a+a*b, a+T) (q, +a*b, +T) (q, a*b, T) (q, a*b, T*F)
(q, a*b, F*F) (q, a*b, a*F) (q, *b, *F), (q, b, F) (q, b, b) (q, , )
Esta es una mquina terica, con este tipo de mquinas nunca podremos
resolver problemas como la ambigedad; en el tema siguiente hablaremos de
implementaciones de AP pero con restricciones para solucionar estos
problemas.
4.1.2 Esquemas de traduccin (EDT).
Con este tipo de mquinas podemos definir un anlisis sintctico descendente.
Un esquema de traduccin es una mquina terica que podemos definir de la
siguiente forma:
EDT = (N, Te, Ts, R, S)
N son los no terminales.
Te es el alfabeto de entrada.
Ts es el alfabeto de salida.
S es el axioma.
R son las reglas de la produccin de la forma:
A o, | con: o e (N U Te)*, |e (N U Ts)* y adems N (Ts U Te) = C
Lo que hace esta mquina es traducir una tira de caracteres a otro lenguaje,
el esquema podra ser el siguiente:
La gramtica de salida Gs = (N, Ts, P, S), donde P = { A | / A o,| e R},
es decir, nos quedamos con la parte derecha de las reglas R.
NOTA: Es importante hacer notar que aqu la salida es "modificable", es decir,
es una memoria. Es una tipo de autmata con pila en el cual tomamos la
salida de los valores que quedan en esa memoria.
Una forma de traduccin es un par (t, s), donde t es una combinacin de
caracteres entrada y s la salida proporcionada por el EDT, dichos caracteres
son una combinacin de terminales y no terminales. A la forma (S, S) la
llamamos forma inicial de traduccin.
Sea un par (t, s), una derivacin sera:
(vA, Ao) (voa, |o) si existe una regla A o,|
EDT
Ge Le Ls Gs
51
Representamos con * 0 o ms derivaciones y con
+
1 o ms derivaciones.
Podemos definir un conjunto de traduccin como:
Tr (EDT) = {(t, s) / (S, S)
+
(t, s), t e Te*, s e Ts*}
Llamamos traduccin regular a aquella cuyas reglas del esquema de
traduccin son regulares (es una gramtica regular).
Ejemplo1.- Notacin polaca inversa.
Ge = ({S}, {a, b, *, /, (, )}, P, S}
Reglas de P:
S (S)
S a | b
S S*S | S/S
Vamos a crear las reglas del EDT:
Reglas de P: Reglas de R:
S (S) S (S), S
S a S a, a
S b S b, b
S S*S S S*S, SS*
S S/S S S/S, SS/
Ya tenemos un EDT = (N, Te, Ts, R, S) en el que:
N = {S}
Te = {a, b, *, /, (, )}
Ts = {a, b, *, /}
Vamos a introducir en el EDT la tira "a/(a*b)" y ver su recorrido:
(S, S) (S/S, SS/) (a/S, aS/) (a/(S), aS/) (a/(S*S), aSS*/)
(a/(a*S), aaS*/) (a/(a*b), aab*/)
Ejemplo2.-
T = { a, b, c, (, ), *, + }
EDT = ( N, T, <1, 2, ..., n>, R, S>
S S + T,1ST
S T, 2T
T T * F, 3TF
T F, 4F
F (S), 5S
F a, 6
F b, 7
52
Vamos a intentar reconocer la palabra "a*(a+b)":
(S, S) (T, 2T) (T*F, 23TF) (F*F, 234FF) (a*F, 2346F)
(a*(S), 23465S) ... (a*(a+b), 23465124647)
La cadena que obtenemos nos informa del camino tomado por el parsing, es lo
que denominamos parse.
4.1.3 Traductores con pila (TP).
Un traductor con pila realiza un anlisis sintctico ascendente y tiene la
posibilidad de generar una salida, que ser el parse. Tambin se trata de una
mquina terica y podemos definirlo como:
TP = (Q, Te, Tp, Ts, o, q
0
, z
0
, F)
Q es el conjunto de estados.
Te es el alfabeto de entrada.
Tp es el alfabeto de la pila.
Ts es el alfabeto de salida.
q
0
es el estado inicial.
z
0
es el elemento inicial de la pila.
F son los estados finales (un subconjunto de Q).
o: Q x {Te U {}} x {Tp U {}} P(Q) x Tp* x Ts*
Llamamos configuracin a la siguiente tupla: (q, t, o, s), en donde q es el
estado actual, t es la tira de entrada que queda por analizar, o es el
contenido de la pila y s la cadena que est saliendo.
Un movimiento es una transicin de la forma:
(q, ax, zv, y) |---- (q, x, ov, yz) con q, q e Q, x e Te*, v e Tp*, y e Ts*
El conjunto de traduccin podemos definirlo como:
Tr(TP) = {(t,s) / (q
0
, t, z
0
, ) |--*-- (q
f
, , o, s), q
f
e F}
si realizamos la traduccin hasta que se agote la cadena de entrada y nos
encontremos en un estado final, o bien:
Tr(TP) = {(t,s) / (q
0
, t, z
0
, ) |--*-- (q, , , s)}
si realizamos la traduccin hasta que se agote la cadena de entrada y la
memoria de la pila.
Ejemplo.-
T = { a, b, (, ), *, + }
53
TP = ( {q}, T, N U T, <1, 2, ..., 7>, o, q, z
0
, q>
S S + T o(q, , S+T) = (q, S, 1)
S T o(q, , T) = (q, S, 2)
T T * F o(q, , T*F) = (q, T, 3)
T F o(q, , F) = (q, T, 4)
F (S) o(q, , (S)) = (q, F, 5)
F a o(q, , a) = (q, F, 6)
F b o(q, , b) = (q, F, 7)
Si queremos vaciar la pila aadimos la siguiente regla:
o(q, , S) = (q, , )
Siendo t un terminal, para introducirlo en la pila:
o(q, t, ) = (q, t, )
Vamos a reconocer la cadena "a*(a+b)" (veremos el camino feliz):
ENTRADA PILA SALIDA
a*(a+b) z
0
*(a+b) z
0
a
*(a+b) z
0
F 6
*(a+b) z
0
T 64
(a+b) z
0
T* 64
a+b) z
0
T*( 64
+b) z
0
T*(a 64
+b) z
0
T*(F 646
... ... ...
z
0
S 64642741532
z
0
64642741532
4.2 Algoritmos sin retroceso.
En los algoritmos vistos anteriormente tenemos el problema de buscar la
trayectoria correcta en el rbol, si es la equivocada, es preciso volver hacia
atrs y eso es precisamente el retroceso. Nos interesarn algoritmos
predictivos que, viendo los elementos que van a venir a continuacin sabrn a
que estado transitar. Esto implica que las gramticas sern ms restrictivas,
as hablaremos de gramticas LL1, LR, precedencia simple, etc, estas
gramticas siempre sern subconjuntos de las Gramticas de Contexto Libre.
54
4.2.1 Anlisis sintctico ascendente por precedencia simple.
Es un mtodo de parsing ascendente que se basa en la relaciones <, >, ,
relaciones que no son de equivalencia.
Decimos que una relacin R es una relacin de equivalencia cuando:
i) Reflexiva. x R x x e R
ii) Simtrica. x R y y R x
iii) Transitiva. x R y, y R z x R z
Ejemplo.-
R = { (x,y) / x > y } x, y e N S = { (x,y) / x = y } x, y e N
No es de equivalencia Es de equivalencia
x\y 0 1 2 3 ... x\y 0 1 2 3 ...
0 0 0 0 0 0 1 0 0 0
1 1 0 0 0 1 0 1 0 0
2 1 1 0 0 2 0 0 1 0
3 1 1 1 0 3 0 0 0 1
... ...
NOTA: No confundir la relacin = numrica, que s es de equivalencia, con
la relacin que usaremos aqu, que no es de equivalencia.
Definicin: Relacin universal
U = A x A = { (x, y) / x e A, y e A }
Definicin: Relacin traspuesta (R o R
T
)
Dada una relacin R, R
T
= { (y, x) / x R y }
Definicin: Complementario de una relacin (R
~
)
R
~
= { (x, y) / ~(x R y) } = U - R
Dadas dos relaciones R y S sobre A:
R + S = { (x, y) / (x R y) v (x S y) } (OR)
R x S = { (x, y) / (x R y) . (x S y) } (AND)
R + S = M
ij
OR M
kp
R x S = M
ij
AND M
kp
R . S = { (x, y) / - z (x R z) . (z S y) } denominado producto relativo.
A la hora de realizar la tabla de relaciones de la gramtica se puede hacer a
mano, haciendo todas las posibles derivaciones a partir del axioma de la
55
gramtica y determinar las relaciones en funcin del lugar en que nos
encontremos en el rbol.
Por ejemplo, supongamos la gramtica:
S aAa Algunos rboles seran los siguientes:
A bB
A c
B Acd
Las relaciones que podemos obtener son las siguientes:
b B
a < b
B > a
b < c
A c
Etc...
Si estn en la misma regla la relacin es , si estn en el nivel superior por la
izquierda < y si estn en el nivel superior por la derecha o cualquiera que
cuelgue de ste >.
Finalmente, la tabla sera:
S
a a
A
c
S
a a
A
B b
d A
c
c
S
a a
A
B b
d A
c
B b
d A
c
c
56
S a A B c b d
S
a
< <
A
B
>
>
c
>
>
b
< < <
d
>
>
Para que la gramtica sea de precedencia simple solamente ha de existir un
smbolo por casilla. Existen ms razones para que una gramtica no sea de
precedencia simple pero siempre que exista la misma parte derecha en dos
reglas distintas esta gramtica no lo ser.
Llamamos pivote a un conjunto de smbolos que se pueden sustituir por las
metanociones de la regla correspondiente (son smbolos situados entre < y >
con los intermedios relacionados con ). La tira completa la rodeamos por
izquierda y derecha pcon < y >. Utilizando el concepto de pivote, vamos a
realizar a continuacin el parsing de una tira, necesitamos encontrar un
pivote, si ste no existe querr decir que no pertenece al lenguaje:
TIRA PIVOTE REDUCCIN
<a<b<c>cd>a> c A c
<a<b<Acd>a> Acd B Acd
<a<bB>a> bB A bB
<aAa> aAa S aAa
<S> XITO, ACEPTADA
4.2.1.1 Clculo de la tabla por el mtodo matricial.
Dada una gramtica de contexto libre G = (N, T, P, S):
i) x < y si y slo si A oxB| e P y B
+
yv
ii) x y si y slo si A oxy| e P
iii) x > y si y slo si A oCD| con C
+
vx y D * y
El ejemplo anterior creemos que har comprender la diferencia entre < y >.
S
B A
D C
b
a
d c
Solamente A < D y A < d
pero:
C > B, C > D, C > d
y
c > B, c > D, c > d
57
Sea una GCL G = (N, T, P, S), y sea A ox| e P, definimos cabecera como:
Cab(A) = { x / A
+
x...}
Relacin PRIMERO: Diremos que A es PRIMERO de X si y slo si - A Xo|
As podemos decir que Cab(A) = { X / (A, X) e PRIMERO
+
}
A PRIMERO
+
X si y slo si - A
+
Xo|
A ULTIMO X si y slo si - A o|X
A ULTIMO
+
X si y slo si - A
+
o|X
A DENTRO x si y slo si - A oX|
A DENTRO
+
x si y slo si - A
+
oX|
Ejemplo.-
A Aa | B
B DbC | Dc
C c
D Ba
PRIMERO+ = { (A, A), (A, B), (A, D), (B, D), (B, B), (C, c), (D, B), (D, D) }
La matriz PRIMERO+ sera la siguiente:
A B C D a b c
A 1 1 0 1 0 0 0
B 0 1 0 1 0 0 0
C 0 0 0 0 0 0 1
D 0 1 0 1 0 0 0
a 0 0 0 0 0 0 0
b 0 0 0 0 0 0 0
c 0 0 0 0 0 0 0
Si una vez calculada la matriz PRIMERO la multiplicamos por sigo misma (con
AND, es decir 1+1=1, el denominado producto relativo) tantas veces como
haga falta hasta que no aparezcan ms unos (los que vayan apareciendo los
aadiremos), llegaremos a obtener PRIMERO
+
.
Algoritmo de Warshall
Nos permite hallar A
+
a partir de A; A
+
= U
i>0
A
i
a partir de la relacin R en
forma de una matriz.
B = A
I = 1
3 REPEAT
IF B(I, J) = 1 THEN
FOR K = 1 TO N DO
IF A(J, K) = 1 THEN A(I, K) = 1
UNTIL para todos los valores de J
58
I = I + 1
IF I s N THEN GOTO 3 ELSE STOP
Clculo de las matrices (), (<) y (>)
() se calcula por la propia definicin.
(<) = () (PRIMERO
+
)
(>) = (ULTIMO
+
)
T
() (I + PRIMERO
+
) , donde I es la matriz identidad.
Ejemplo 1.- Calcular las relaciones de precedencia simple para la gramtica:
S aSB |
B b
Tenemos que eliminar la regla , de forma que la gramtica nos quedara:
S S |
S aSB | aB
B b
La regla S S | podemos no tenerla en cuenta (y por lo tanto tampoco el
smbolo S), si bien al implementarlo contemplaramos la "sentencia vaca"
como parte del lenguaje. Se indican en negrita y con un asterisco los 1s que
seran 0s si no eliminramos la regla existente en la gramtica.
Vamos a calcular las matrices () y PRIMERO:
S B a b PRIMERO S B a b ULTIMO S B a b
S 0 1 0 0 S 0 0 1 0 S 0 1 0 0
B 0 0 0 0 B 0 0 0 1 B 0 0 0 1
a 1 1* 0 0 a 0 0 0 0 a 0 0 0 0
b 0 0 0 0 b 0 0 0 0 b 0 0 0 0
Por lo tanto:
0 1 0 0 0 0 1 0
() = 0 0 0 0 PRIMERO = 0 0 0 1
1 1* 0 0 0 0 0 0
0 0 0 0 0 0 0 0
Vamos a hacer el producto relativo con PRIMERO:
0 0 1 0 0 0 1 0 0 0 0 0
0 0 0 1 0 0 0 1 = 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0
59
Entonces PRIMERO
+
= PRIMERO
Vamos a hacer el producto relativo con ULTIMO:
0 1 0 0 0 1 0 0 0 0 0 1
0 0 0 1 0 0 0 1 = 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0
Como aparece un nuevo 1, volvemos a multiplicarlo por ULTIMO:
0 1 0 0 0 0 0 1 0 0 0 0
0 0 0 1 0 0 0 0 = 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0
Ahora ya tenemos las matrices PRIMERO
+
y ULTIMO
+
:
0 0 1 0 0 1 0 1
PRIMERO
+
= 0 0 0 1 ULTIMO
+
= 0 0 0 1
0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0
Ya podemos calcular (<) y (>):
() (PRIMERO
+
)
0 1 0 0 0 0 1 0 0 0 0 1
(<) = 0 0 0 0 0 0 0 1 = 0 0 0 0
1 1* 0 0 0 0 0 0 0 0 1 1*
0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 1 0 0 0 0 0 0
(ULTIMO
+
)
T
() = 1 0 0 0 0 0 0 0 = 0 1 0 0
(+) 0 0 0 0 1 1* 0 0 0 0 0 0
1 1 0 0 0 0 0 0 0 1 0 0
(+) (I+PRIMERO
+
)
0 0 0 0 1 0 1 0 0 0 0 0
(>) = 0 1 0 0 0 1 0 1 = 0 1 0 1
0 0 0 0 0 0 1 0 0 0 0 0
0 1 0 0 0 0 0 1 0 1 0 1
La tabla final de parsing ser:
60
S B a b
S
<
B > >
a
* < <*
b > >
Si en esta tabla eliminamos los smbolos * y <* (que sera la tabla resultante
si no eliminramos la regla ) no podramos resolver elementos del lenguaje
como podra ser "aabb"; al mirar las precedencias no seramos capaces de
encontrar un pivote y no aceptaramos una palabra que s es del lenguaje. En
cambio con la tabla tal cual aparece podemos reconocer todo el lenguaje
excepto el elemento "cadena vaca", que habra que tratarlo y reconocerlo
especficamente (algo, por otro lado, muy sencillo).
Ejercicio 2.-
S AB
B A
A a
S A B a PRIMERO S A B a ULTIMO S A B a
S 0 0 0 0 S 0 1 0 0 S 0 0 1 0
A 0 0 1 0 A 0 0 0 1 A 0 0 0 1
B 0 0 0 0 B 0 1 0 0 B 0 1 0 0
a 0 0 0 0 a 0 0 0 0 a 0 0 0 0
Por lo tanto:
0 0 0 0 0 1 0 0
() = 0 0 1 0 PRIMERO = 0 0 0 1
0 0 0 0 0 1 0 0
0 0 0 0 0 0 0 0
Vamos a hacer el producto relativo con PRIMERO:
0 1 0 0 0 1 0 0 0 0 0 1
0 0 0 1 0 0 0 1 = 0 0 0 0
0 1 0 0 0 1 0 0 0 0 0 1
0 0 0 0 0 0 0 0 0 0 0 0
Como aparecen dos nuevos 1, volvemos a multiplicarlo por PRIMERO:
0 1 0 0 0 0 0 1 0 0 0 0
0 0 0 1 0 0 0 0 = 0 0 0 0
0 1 0 0 0 0 0 1 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0
Vamos a hacer el producto relativo con ULTIMO:
61
0 0 1 0 0 0 1 0 0 1 0 0
0 0 0 1 0 0 0 1 = 0 0 0 0
0 1 0 0 0 1 0 0 0 0 0 1
0 0 0 0 0 0 0 0 0 0 0 0
Como aparecen nuevos 1, volvemos a multiplicarlo por ULTIMO:
0 0 1 0 0 1 0 0 0 0 0 1
0 0 0 1 0 0 0 0 = 0 0 0 0
0 1 0 0 0 0 0 1 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0
Nuevamente aparece un "1", y seguimos:
0 0 1 0 0 0 0 1 0 0 0 0
0 0 0 1 0 0 0 0 = 0 0 0 0
0 1 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0
Ahora ya tenemos las matrices PRIMERO
+
y ULTIMO
+
:
0 1 0 1 0 1 1 1
PRIMERO
+
= 0 0 0 1 ULTIMO
+
= 0 0 0 1
0 1 0 1 0 1 0 1
0 0 0 0 0 0 0 0
Ya podemos calcular (<) y (>):
() (PRIMERO
+
)
0 0 0 0 0 1 0 1 0 0 0 0
(<) = 0 0 1 0 0 0 0 1 = 0 1 0 1
0 0 0 0 0 1 0 1 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0
(ULTIMO
+
)
T
() = 1 0 1 0 0 0 1 0 = 0 0 0 0
(*) 1 0 0 0 0 0 0 0 0 0 0 0
1 1 1 0 0 0 0 0 0 0 1 0
(*) (I+PRIMERO
+
)
0 0 0 0 1 1 0 1 0 0 0 0
(>) = 0 0 0 0 0 1 0 1 = 0 0 0 0
0 0 0 0 0 1 1 1 0 0 0 0
0 0 1 0 0 0 0 1 0 1 1 1
La tabla final de parsing ser:
62
S A B a
S
A <
<
B
a > > >
Ejercicio 3.-
S CC
C cC
C d
S C c d PRIMERO S C c d ULTIMO S C c d
S 0 0 0 0 S 0 1 0 0 S 0 1 0 0
C 0 1 0 0 C 0 0 1 1 C 0 1 0 1
c 0 1 0 0 c 0 0 0 0 c 0 0 0 0
d 0 0 0 0 d 0 0 0 0 d 0 0 0 0
Por lo tanto:
0 0 0 0 0 1 0 0
() = 0 1 0 0 PRIMERO = 0 0 1 1
0 1 0 0 0 0 0 0
0 0 0 0 0 0 0 0
Vamos a hacer el producto relativo con PRIMERO:
0 1 0 0 0 1 0 0 0 0 1 1
0 0 1 1 0 0 1 1 = 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0
Como aparecen dos nuevos 1, volvemos a multiplicarlo por PRIMERO:
0 1 0 0 0 0 1 1 0 0 0 0
0 0 1 1 0 0 0 0 = 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0
Vamos a hacer el producto relativo con ULTIMO:
0 1 0 0 0 1 0 0 0 1 0 1
0 1 0 1 0 1 0 1 = 0 1 0 1
0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0
Como aparece un nuevo 1, volvemos a multiplicarlo por ULTIMO:
63
0 1 0 0 0 1 0 1 0 1 0 1
0 1 0 1 0 1 0 1 = 0 1 0 1
0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0
Ahora ya tenemos las matrices PRIMERO
+
y ULTIMO
+
:
0 1 1 1 0 1 0 1
PRIMERO
+
= 0 0 1 1 ULTIMO
+
= 0 1 0 1
0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0
Ya podemos calcular (<) y (>):
() (PRIMERO
+
)
0 0 0 0 0 1 1 1 0 0 0 0
(<) = 0 1 0 0 0 0 1 1 = 0 0 1 1
0 1 0 0 0 0 0 0 0 0 1 1
0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0
(ULTIMO
+
)
T
() = 1 1 0 0 0 1 0 0 = 0 1 0 0
(*) 0 0 0 0 0 1 0 0 0 0 0 0
1 1 0 0 0 0 0 0 0 1 0 0
(*) (I+PRIMERO
+
)
0 0 0 0 1 1 1 1 0 0 0 0
(>) = 0 1 0 0 0 1 1 1 = 0 1 1 1
0 0 0 0 0 0 1 0 0 0 0 0
0 1 0 0 0 0 0 1 0 1 1 1
La tabla final de parsing ser:
S C c d
S
C >
>
<
>
<
c
< <
d > > >
En la fila de B aparecen varias relaciones en la misma casilla, se trata de una
gramtica no implementable por precedencia simple.
64
4.2.2 Anlisis sintctico ascendente por precedencia de operadores.
Una gramtica de contexto libre G=(N, T, P, S) diremos que es una gramtica
de operador si no posee reglas y si en la parte derecha de sus reglas no
aparecen dos no terminales adyacentes. Esto es:
1) no - A
2) no - A oBC| con A, B, C e N
Sean a y b dos smbolos, trabajaremos con las siguientes precedencias: a > b,
b < a y a b.
Algoritmo
1. Obtener la tira de entrada, se ponen las reglas de precedencia entre cada
dos elementos y se delimita mediante otro smbolo, por ejemplo $. Por
ejemplo: $<id>+<id>$. NOTA: Slo existen relaciones entre terminales.
2. Analizamos la cadena de entrada de izquierda a derecha y vamos
avanzando hasta encontrar el smbolo de precedencia mayor (>), luego
iremos hacia atrs hasta encontrar el smbolo de precedencia menor (<),
con lo que todo lo encerrado entre ambos ser el pivote y el que nos
permitir realizar la reduccin.
De esta forma, los errores que pueden producirse son:
a) Ninguna relacin de precedencia entre un smbolo y el siguiente.
b) Una vez encontrada la relacin de precedencia, es decir, tenemos pivote,
no existe ninguna regla que permita reducirlo.
Algoritmo para la obtencin de las relaciones de precedencia operador
i) Si el operador u
1
tiene mayor precedencia que el u
2
u
1
> u
2
u
2
< u
1
ii) Si el operador u
1
tiene igual precedencia que el u
2
- Si son asociativos por la izquierda u
1
> u
2
u
2
> u
1
- Si son asociativos por la derecha u
1
< u
2
u
2
< u
1
iii) Hgase:
u < ( < u > ) > u ( < ( ) > )
$ < ( ) > $
u < id > u u > $ < u
( < id > ) $ < id > $
Ejemplo1.- Sea la siguiente gramtica:
S S + S asumimos que el operador + es asociativo por la izquierda
S S * S asumimos que el operador * es asociativo por la izquierda
S id
La tabla de precedencia operador ser la siguiente:
65
id + * $
id > > >
+ < > < >
* < > > >
$ < < <
Vamos a reconocer la tira $id+id*id$
TIRA PIVOTE REDUCCIN
$<id>+<id>*<id>$ id S id
$<S+<id>*<id>$ id S id
$<S+<S*<id>$ id S id
$<S+<S*S>$ S*S S S*S
$<S+S>$ S+S S S+S
$S$ XITO, ACEPTADA
Ejemplo 2.- Sea la siguiente gramtica:
S SAS | (S) | id
A + | - | * | / | |
Esta gramtica no es de precedencia operador porque en S SAS tenemos
tres no terminales consecutivos, pero si sustitumos las alternativas de A en
esa regla la podemos convertir en una gramtica de precedencia operador de
la siguiente forma:
S S + S
S S S
S S * S
S S / S
S S | S
S (S)
S id
Asumimos que:
- El operador | tiene la mayor precedencia y es asociativo por la derecha.
- Los operadores * y / tienen la siguiente mayor precedencia y son
asociativos por la izquierda.
- Los operadores + y son los de menor precedencia y son asociativos por la
izquierda.
La tabla de precedencia operador sera la siguiente:
+ - * /
|
id ( ) $
+ > > < < < < < > >
- > > < < < < < > >
* > > > > < < < > >
/ > > > > < < < > >
66
|
> > > > < < < > >
id > > > > > > >
( < < < < < < <
) > > > > > > >
$ < < < < < < <
Funciones de precedencia
Normalmente no se almacena la tabla de precedencias de operador sino que
se definen unas funciones de precedencia. Utilizando estas funciones
ahorramos memoria pues no es preciso almacenar la tabla. El mtodo se basa
en implementar dos funciones f y g que transformarn smbolos terminales en
enteros que compararemos para mirar su prioridad. Esto es:
a, b e T
f(a) < g(b) sii a < b
f(a) > g(b) sii a b
f(a) > g(b) sii a > b
Para la tabla del ejercicio 2, las funciones f y g seran las siguientes:
+ - * /
|
id ( ) $
f 2 2 4 4 4 6 0 6 0
g 1 1 3 3 5 5 5 0 0
De esta forma, ante una cadena de entrada como la siguiente:
$id+id*id$
Las precedencias las calcularamos de la siguiente forma:
f($) = 0 y g(id) = 5 entonces $ < id
f(id) = 6 y g(+) = 1 entonces id < +
f(+) = 2 y g(id) = 5 entonces + < id
f(id) = 6 y g(*) = 3 entonces id > *
f(*) = 4 y g(id) = 5 entonces * < id
f(id) = 6 y g($) = 0 entonces id > $
Con lo cual:
$<id>+<id>*<id>$
Reduciramos <id> con la regla S id y continuaramos el proceso.
Algoritmo para la construccin de las funciones de precedencia
1. Partimos de la matriz de precedencia.
2. Creamos para un grafo los nodos f
a
y g
a
a e T incluyendo $.
3. Si tienen igual precedencia los agrupamos.
67
4. Creamos un grafo dirigido cuyos nodos sean los smbolos creados en el
paso anterior.
5. Si a < b la arista va de g
b
a f
a
.
6. Si a > b la arista va de f
a
a g
b
.
7. Si el grafo as construido tiene ciclos significa que no se pueden calcular
las funciones de precedencia. En otro caso,las funciones las construiremos
asignando a f
a
la longitud del camino ms largo que podemos recorrer por
el grafo desde el nodo donde est f
a
, al igual que con g
a
.
Para la tabla del ejemplo 1 las funciones seran las siguientes (construdas a
partir del grafo):
id + * $
f 4 2 4 0
g 5 1 3 0
4.2.3 Analizadores descendentes LL(K).
Este tipo de analizadores consta de los siguientes elementos:
g
id
f
*
g
+
f
$
f
id
g
*
f
+
g
$
68
Los analizadores sintcticos LL(k) funcionan de forma descendente, sin
retroceso y son predictivos en k smbolos. Nosotros nos quedaremos con los
LL(1). La primera L significa Left, izquierda en ingls, indicando que el
anlisis de la entrada se realiza de izquierda a derecha; la segunda L indica
tambin Left y representa una derivacin por la izquierda; K, en nuestro
caso K=1, indica que utilizamos un smbolo de entrada de examen por
anticipado a cada paso para tomar decisiones de accin en el anlisis
sintctico.
Suponiendo que tenemos la tabla, el funcionamiento ser:
Si X = a = $ xito en el anlisis (X elemento de la pila, a es entrada).
Si X = a $ El analizador quita X de la pila y va a la siguiente entrada.
Si X e N Consulta la tabla en M[X,a], en donde tendremos una
produccin, por ejemplo X WZT, sustituyendo X en la pila por WZT.
Ejemplo.- Sea la gramtica:
E TE
E +TE | c
T FT
T *FT | c
F (E) | id
Y la tabla correspondiente:
id + * ( ) $
E
E TE E TE
E
E +TE E c E c
T
T FT T FT
T
T c T *FT T c T c
F
F id F (E)
Vamos a realizar un reconocimiento de id+id*id$:
Programa de
anlisis
Mij, tabla del
analizador
Pila
Tira de entrada
Resultado
69
PILA ENTRADA Reduccin
$E id+id*id$
$ET id+id*id$ E TE
$ETF id+id*id$ T FT
$ETid id+id*id$ F id
$ET +id*id$
$E +id*id$ T c
$ET+ +id*id$ E +TE
$ET id*id$
$ETF id*id$ T FT
$ETid id*id$ F id
$ET *id$
$ETF* *id$ T *FT
$ETF id$
$ETid id$ F id
$ET $
$E $ T c
$ $ E c
$ $ XITO
Si realizramos el recorrido con la cadena id**id en algn paso nos
encontraramos con una casilla en blanco, es decir, un error.
4.2.3.1 Construccin de la tabla Mij.
Clculo del conjunto PRIMERO (X):
i) X e T entonces PRIMERO(X) = {X}
ii) X c entonces c e PRIMERO(X)
iii) X e N, X X
1
X
2
... X
n
entonces a e T, a e PRIMERO(X
j
), a e
PRIMERO(X) siempre y cuando: X
1
* c, X
2
* c, ..., X
j-1
* c. Si
ocurriera que X
1
* c, X
2
* c, ..., X
n
* c entonces c e PRIMERO(X).
Clculo del conjunto SIGUIENTE(X):
i) Si X = S (smbolo inicial o axioma) entonces $ e SIGUIENTE(X).
ii) Si tenemos una produccin de la forma A oB|, con | = c, entonces
PRIMERO(|) \ {c} e SIGUIENTE(B) (nota: \ significa menos).
iii) Si tenemos producciones de la forma: A oB bien A oB| donde
PRIMERO(|) contenga c (o lo que es lo mismo, | * c) entonces
hacemos SIGUIENTE(A) c SIGUIENTE(B).
Ejemplo.- Sea la gramtica del ejemplo anterior:
E TE
E +TE | c
70
T FT
T *FT | c
F (E) | id
Si calculamos PRIMERO y SIGUIENTE quedara:
PRIMERO SIGUIENTE
E (, id ), $
E +, c ), $
T (, id +, ), $
T *, c +, ), $
F (, id *, +, ), $
En el caso de E tenemos que $ e SIGUIENTE(E) por (i), adems tenemos la
regla F (E), caso (ii), por lo que PRIMERO()) = { ) } e SIGUIENTE(E) (no est
c, en ese caso no la aadiramos), entonces tenemos ya que SIGUIENTE(E) =
{$, )}.
En el caso de E tenemos la regla E TE, que es el caso (iii), con lo cual
SIGUIENTE(E) c SIGUIENTE(E), con lo cual SIGUIENTE(E) = {), $}.
En el caso de T tenemos la regla E TE, por la regla (ii) tenemos que
PRIMERO (E)\c = {+} e SIGUIENTE(T). Adems, en la regla E +TE tenemos
la situacin (iii), con lo cual SIGUIENTE(E) c SIGUIENTE(T), por lo que
aadimos {), $}. Finalmente tenemos que SIGUIENTE(T) = {+, ), $}.
Algoritmo de confeccin de la tabla Mij
1) A o e P aplicamos (2) y (3).
2) a e T, a e PRIMERO(o), aadir A o en M[A, a].
3) Si c e PRIMERO(o), aadir A o en M[A, b] b e SIGUIENTE(A). Como
caso ms especial, incluido en el anterior, tenemos que si c est en
PRIMERO(o) y $ est en SIGUIENTE(A), adase A o a M[A, $].
4) Cualquier entrada no definida en Mij ser un error.
Es importante sealar que una gramtica ser LL(1) si en cada casilla
tenemos, como mximo, una produccin. Si la gramtica es recursiva por la
izquierda o ambigua, entonces M tendr al menos una entrada con definicin
mltiple, y por lo tanto no se podr implementar mediante LL(1).
Ejemplo.- Para la gramtica anterior vamos a calcular Mij:
71
Id + * ( ) $
E
E TE E TE
E
E +TE E c E c
T
T FT T FT
T
T c T *FT T c T c
F
F id F (E)
Con la regla E TE
En este caso PRIMERO (TE) = PRIMERO(T) porque c no est incluido en este
ltimo. Utilizando la regla (2), {(, id} e PRIMERO(T), entonces hemos aadido
E TE en M[E, (] y M[E, id}
Con la regla E +TE
Tambin tenemos que PRIMERO(+TE) = PRIMERO(+) = {+}, entonces, tambin
con la regla (2), en M[E, +] introducimos E +TE.
Con la regla E c
Como PRIMERO(c) = c y SIGUIENTE(E) = {), $}, entonces introducimos E c
en M[E, )] y M[E, $], aplicando la regla (3).
Y as continuaramos con el resto de las reglas.
Condiciones a cumplir para que una gramtica sea LL(1):
1) Ninguna gramtica ambigua o recursiva por la izquierda puede ser LL(1).
2) Puede demostrarse que una gramtica G es de tipo LL(1) si, y slo si,
cuando A o | | sean dos producciones distintas de G se cumplan las
siguientes condiciones:
3) Para ningn terminal a tanto o como | derivan a la vez cadenas que
comiencen con a.
4) A lo sumo una de o y | pude derivar la cadena vaca.
5) Si | * c, o no deriva ninguna cadena que comience con un terminal en
SIGUIENTE(A).
Ejemplo.- Implemntese un analizador LL(1) de la gramtica:
E E + T | E T | T
T T * F | T / F | F
F id | id [ E ] | ( E )
Ejemplo.- Sea la gramtica:
S { A }
A id = E
E id
Implementar un analizador LL(1).
Ejemplo.-
72
S S ; L
S L
L if expr then S else S fi
L if expr then S fi
L instr
No es LL(1), hay que transformarlo eliminando recursividad por la izquierda y
factorizacin en:
S L S
S ; L S | c
L if expr then S X fi
L instr
X else S | c
Primero calculamos los conjuntos PRIMERO y SIGUIENTE:
PRIMERO SIGUIENTE
S if expr then, instr $, else, fi
L if expr then, instr ;, $, else, fi
S ;, c $, else, fi
X c, else fi,
La tabla resultante es la que viene a continuacin:
; if expr then fi instr else $
S S L S S L S
L L if expr then S X
fi
L instr
S S ; LS S c S c S c
X X c X else S
Reconozcamos if expr then if expr then instr fi else if expr then instr fi fi$:
PILA ENTRADA SALIDA
$S
if expr then if expr then instr fi else if
expr then instr fi fi$
$SL
if expr then if expr then instr fi else if
expr then instr fi fi$
S L S
$Sfi X S then expr if
if expr then if expr then instr fi else if
expr then instr fi fi$
L if expr
then S X fi
$S fi X S
if expr then instr fi else if expr then
instr fi fi$
$S fi XSL
if expr then instr fi else if expr then
instr fi fi$
S L S
$S fi XSfi X S then expr if
if expr then instr fi else if expr then
instr fi fi$
L if expr
then S X fi
$S fi XSfi X S
instr fi else if expr then instr fi fi$
$ S fi X Sfi X S L
instr fi else if expr then instr fi fi$
S L S
$ S fi X Sfi X S instr
instr fi else if expr then instr fi fi$ L instr
$ S fi X Sfi X S
fi else if expr then instr fi fi$
73
$ S fi X Sfi X
fi else if expr then instr fi fi$ S c
$ S fi X Sfi
fi else if expr then instr fi fi$ X c
$ S fi X S
else if expr then instr fi fi$
$ S fi X
else if expr then instr fi fi$ S c
$ S fi S else
else if expr then instr fi fi$ X else S
$ S fi S
if expr then instr fi fi$
$ S fi S L
if expr then instr fi fi$ S L S
$ S fi S fi X S then expr if
if expr then instr fi fi$ L if expr
then S X fi
$ S fi S fi X S
instr fi fi$
$ S fi S fi X SL
instr fi fi$ S L S
$ S fi S fi X S instr
instr fi fi$ L instr
$ S fi S fi X S
fi fi$
$ S fi S fi X
fi fi$ S c
$ S fi S fi
fi fi$ X c
$ S fi S
fi$
$ S fi
fi$ S c
$ S
$
$
$ S c
$
$
XITO
4.2.4 Analizadores ascendentes LR(k).
Los analizadores sintcticos LR(k) funcionan de forma ascendente, sin
retroceso y son predictivos en k smbolos. Es un mecanismo que se puede
utilizar para analizar una clase ms amplia de GCL. La primera L significa
Left, izquierda en ingls, indicando que el anlisis de la entrada se realiza
de izquierda a derecha; la segunda R significa Right e indica que se
realizan las derivaciones por la derecha; k, en nuestro caso k=1, indica que
utilizamos un smbolo de entrada de examen por anticipado a cada paso para
tomar decisiones de accin en el anlisis sintctico.
A la hora de construir la tabla LR tenemos tres tcnicas:
1) SLR.- El ms sencillo y menos potente y ms restrictivo.
2) LALR.- De potencia intermedia (YACC utiliza este analizador).
3) LR-cannico.- El mtodo ms general y por lo tanto el ms potente aunque
es ms difcil de implementar.
Este tipo de analizador consta de los siguientes elementos:
74
La tabla de "accin" nos dir lo que hacer con cada entrada y la tabla "ir_a"
nos indicar las transiciones entre estados. El funcionamiento es:
1) Tomamos S
m
de la pila y a
i
de la tira de entrada.
2) Miramos en accion[S
m
, a
i
] que nos indicar una de las siguientes acciones:
a) Desplazar S
p
(S
p
es un estado).
b) Reducir A |. Miramos ir_a[S
m-r
, A] y obtendremos un nuevo estado
al que transitar S
q
(r es la longitud de |).
c) Aceptar.
d) Error.
Llamamos configuracin a un par formado por el estado de la pila y la tira de
entrada que estamos analizando.
Supongamos una configuracin inicial: (S
0
X
1
S
1
X
2
S
2
... X
m
S
m
, a
i
a
i+1
... a
n
$).
Respecto a la notacin, los S
i
son estados y los X
i
son smbolos gramaticales
(terminales y no terminales).
Vamos a ver las diferentes posibilidades:
a) Si tenemos que la accion[S
k
, a
i
] = desplazar S
p
, entonces la configuracin
variara de la siguiente forma:
(S
0
X
1
S
1
X
2
S
2
... X
m
S
m
a
i
S
p
, a
i+1
... a
n
$)
b) Sin embargo, si ocurriera que accion[S
k
, a
i
] = reducir A |, la
configuracin quedara como:
(S
0
X
1
S
1
X
2
S
2
... X
m-r
S
m-r
AS
q
, a
i
a
i+1
... a
n
$)
En donde S
q
= ir_a[S
m-r
,A] y r es la longitud de |.
c) Si accion[S
k
, a
i
] = aceptar entonces significa el fin del anlisis sintctico.
Programa de
anlisis
accin
Pila
Tira de entrada
Resultado
S
m
X
m
S
m-1
X
m-1
...
ir_a
75
d) Si accion[S
k
, a
i
] = error significar que no es una frase correcta,
posteriormente se podra lanzar una rutina que manejara los mensajes de
error o de recuperacin de errores.
En el siguiente ejemplo veremos como funciona el reconocimiento LR cuando
ya disponemos de la tabla.
Ejemplo.-
(1) E E+T (2) E T (3) T T*F (4) T F
(5) F (E) (6) F id
Suponemos que ya hemos calculado la tabla LR(1), es decir, con un smbolo de
anticipacin, que sera:
ESTADO id + * ( ) $ E T F
0 d5 d4 1 2 3
1 d6 ACEP.
2 r2 d7 r2 r2
3 r4 r4 r4 r4
4 d5 d4 8 2 3
5 r6 r6 r6 r6
6 d5 d4 9 3
7 d5 d4 10
8 d6 d11
9 r1 d7 r1 r1
10 r3 r3 r3 r3
11 r3 r3 r5 r5
ACCIN IR_A
Vamos a reconocer "id*id+id":
PILA ENTRADA Accin
0 id*id+id$ d5
0id5 *id+id$ r6 (F id)
0F3 *id+id$ r4 (T F)
0T2 *id+id$ d7
0T2*7 id+id$ d5
0T2*7id5 +id$ r6 (F id)
0T2*7F10 +id$ r3 (T T*F)
0T2 +id$ r2 (E T)
0E1 +id$ d6
0E1+6 id$ d5
0E1+6id5 $ r6 (F id)
0E1+6F3 $ r4 (T F)
0E1+6T9 $ r1 (E E+T)
0E1 $ ACEPTAR
76
Llamamos gramtica LR a aquella gramtica de contexto libre para la cual es
posible construir la tabla.
4.2.4.1 Mtodo SLR.
Es el mtodo ms sencillo pero el menos general, siendo por lo tanto el que
tiene ms restricciones para la gramtica que LALR LR-cannico.
Llamamos elemento de anlisis LR(0) a una produccin de la gramtica G con
un punto en alguna posicin del lado derecho de la regla.
Por ejemplo.-
A XYZ los elementos LR(0) son: A .XYZ
A X.YZ
A XY.Z
A XYZ.
Un caso especial sern las producciones A c, en donde A .
La base para construir la tabla de anlisis LR(0) son una serie de elementos
LR(0) asociados a una gramtica G se denomina coleccin cannica LR(0).
Para conseguir la coleccin cannica LR(0) tenemos que definir previamente
los conceptos de : gramtica aumentada, cerradura y funcin ir_a.
Dada una gramtica G se define G como una gramtica aumentada,
equivalente a G, a la que se le aade la produccin S S, siendo S la
metanocin de la gramtica.
Dado un conjunto I de elementos LR(0) de G, cerradura(I) ser el conjunto de
elementos obtenidos de la siguiente manera:
i) Todo elemento de I e cerradura(I).
ii) A o.B| e cerradura(I), B v e P, entonces B .v e cerradura(I).
iii) Se repite (2) hasta que no se puedan aadir ms elementos.
Algoritmo para calcular cerradura(I)
Funcin cerradura(I)
BEGIN
J = I
REPEAT
FOR cada elemento A o.B| e J Y cada B v e G Y B .v e J
DO aadir B .v a J
UNTIL no se puedan aadir ms a J
END.
Ejemplo.- En la gramtica aumentada:
77
(0) E E (1) E E+T (2) E T (3) T T*F
(4) T F (5) F (E) (6) F id
Vamos a calcular cerradura(E .E):
1) E .E por la regla (i)
2) E .E+T por la regla (ii) desde (1)
3) E .T por la regla (ii) desde (1)
4) T .T*F por la regla (ii) desde (3)
5) T .F por la regla (ii) desde (3)
6) F .(E) por la regla (ii) desde (5)
7) F .id por la regla (ii) desde (5)
Se llaman elementos nucleares a aquellos elementos que no tienen el punto
en el extremo izquierdo. Un caso especial es S .S, que se pone en este
grupo.
Se llaman elementos no nucleares a aquellos que tienen el punto en el
extremo izquierdo.
Definimos la funcin Ir_a(I, X) con I un conjunto LR(0) y X e (N U T):
Ir_a (I, X) = { cerradura(A oX.|) / A o.X| e I}
NOTA: Es la transicin que nos produce X en el autmata.
Ejemplo.- En la gramtica del ejemplo anterior, si tomamos:
I = { E E., E E.+T }
Ir_a(I, +) se calcula como cerradura(E E+.T), que sera:
E E+.T
T .T*F
T .F
F .(E)
F .id
Intuitivamente puede interpretarse que, si A a.Bb est en la cerradura(I),
en este punto el parsing la secuencia Bb debera ser nuestra entrada. Adems,
si tenemos que B g es otra regla, un substring prefijo de g debera ser
tambin nuestra entrada. Ir_a nos indica que si I es un conjunto de elementos
LR(0) vlidos para un posible prefijo de g, entonces Ir_a(I, X) es un conjunto
de sucesores vlidos si X viene despus de g y gX es un prefijo posible.
Obtencin de la coleccin cannica LR(0)
78
Para construir la coleccin cannica LR(0) para una gramtica aumentada
G, utilizamos el siguiente algoritmo:
Algoritmo coleccin cannica LR(0)
Procedimiento elementos_LR(0)(G)
BEGIN
C = {cerradura({[S .S]})}
REPEAT
FOR cada I, I c C Y cada X e G donde Ir_a(I, X) = C Y
Ir_a(I, X) . C, entonces aadir Ir_a(I, X) a C
UNTIL no se puedan aadir ms conjuntos a C
END.
Ejemplo.- Para la gramtica aumentada anterior:
(0) E E (1) E E+T (2) E T (3) T T*F
(4) T F (5) F (E) (6) F id
calculamos la coleccin cannica LR(0) como:
I
0
= cerradura(E .E) = E .E T .T*F F .id
E .E+T T .F
E .T F .(E)
I
1
= Ir_a(I
0
, E) = E E. I
2
= Ir_a(I
0
, T) = E T.
E E.+T T T.*F
I
3
= Ir_a(I
0
, F) = T F.
I
4
= Ir_a(I
0
, () = cerradura(F (.E)) = E (.E)
E .E+T
E .T
T .T*F
T .F
F .(E)
F .id
I
5
= Ir_a(I
0
, id) = F id. I
6
= Ir_a(I
1
, +) = cerradura(E E+.T) = E E+.T
T .T*F
T .F
F .(E)
F .id
I
7
= Ir_a(I
2
, *) = T T*.F I
8
= Ir_a(I
4
, E) = F (E.)
F .(E) E E.+T
F .id
Ir_a(I
4
, T) = I
2
79
Ir_a(I
4
, F) = I
3
Ir_a(I
4
, () = I
4
Ir_a(I
4
, id) = I
5
I
9
= Ir_a(I
6
, T) = E E+T. I
10
= Ir_a(I
7
, F) = T T*F.
T T.*F
Ir_a(I
7
, () = I
4
Ir_a(I
7
, id) = I
5
I
11
= Ir_a(I
8
, )) = F (E).
Ir_a(I
8
, +) = I
6
Cuando tenemos el autmata LR(0), tendremos diversas posibilidades en
cuanto a las reducciones posibles, es un mtodo con retroceso (si nos
equivocamos de rama tenemos que volver hacia atrs). En realidad se trata de
un Autmata finito no determinista pues existen transiciones de la forma A
I
0
E
I
1
I
3
I
7
I
9
I
6
I
4
I
3
I
8
I
4
I
5
I
2
I
10
I
7
I
4
I
5
I
11
I
5
I
6
T
F
(
id
+ T
F
(
id
* F
)
(
id
*
E
I
3
I
2
T
F
)
+
80
o.X| a A oX.| etiquetadas con "X" y transiciones c (dentro de los propios I
i
)
entre elementos de la forma A o.B| a B . .
Decimos que el elemento A |
1
.|
2
es un elemento vlido para un prefijo
variable o|
1
si existe una derivacin S * oAw * o|
1
|
2
w. En general, un
elemento ser vlido para muchos prefijos variables. El hecho de que
tengamos un elemento A |
1
.|
2
vlido para o|
1
informa sobre si desplazar
(|
2
= c) o reducir (|
2
= c) cuando se encuentre o|
1
en la pila del analizador.
Obtencin de la tabla de anlisis SLR
Para la construccin de la tabla seguimos los siguientes pasos:
i) Construir el conjunto C = { I
0
, I
1
, ..., I
n
}
ii) Los estados se construyen a partir de los conjuntos I
i
. El estado k es I
k
,
para rellenar las casillas hacemos (siendo a un terminal):
a) Si A o.a| e I
i
, Ir_a(I
i
,a) = I
j
accion[i, a] = desplazar j
b) Si A o. e I
i
accion[i, a] = reducir "A o" a e SIGUIENTE(A).
c) Si S S. e I
i
accion[i, $] = ACEPTAR
iii) Si Ir_a[I
i
, A] = I
j
, siendo A un no terminal IR_A[i, A] = j
iv) Todas las entradas no definidas constituyen los errores.
Ejemplo.- Para la gramtica aumentada ya vista:
(0) E E (1) E E+T (2) E T (3) T T*F
(4) T F (5) F (E) (6) F id
Veremos como con los terminales hallamos la tabla de accin y con los no
terminales la IR_A:
I
0
= E .E
E .E+T
E .T
T .T*F
T .F
F .(E) F .(E) e I
0
, Ir_a(I
0
,() = I
4
accion[0, (] = d4
F .id F .id e I
0
, Ir_a(I
0
,id) = I
5
accion[0, id] = d5
Adems, como:
Ir_a(I
0
,E) = I
1
IR_A[0, E] = 1
Ir_a(I
0
,T) = I
2
IR_A[0, T] = 2
Ir_a(I
0
,F) = I
3
IR_A[0, F] = 3
E E. e I
1
accion[1, $] = ACEPTAR
E E.+T e I
1
, Ir_a(I
1
, +) = I
6
accion[1, +] = d6
81
E T. e I
2
accion[2, a] = r2 (regla E T) a e SIGUIENTE(E) = { ), +, $},
con lo cual:
accion[2, )] = r2
accion[2, +] = r2
accion[2, $] = r2
T T.*F e I
2
, Ir_a(I
2
, *) = I
7
accion[2, *] = d7
T T*F. e I
10
accion[10, a] = r3 (regla T T*F) a e SIGUIENTE(T) = { *, ),
+, $}, con lo cual:
accion[10, *] = r3
accion[10, )] = r3
accion[10, +] = r3
accion[10, $] = r3
E (E.) e I
8
, Ir_a(I
8
, )) = I
11
accion[8, )] = d11
E E.+T e I
1
, Ir_a(I
8
, +) = I
6
accion[8, +] = d6
As continuaramos para todos los I
i
. Al final nos quedara la tabla ya vista
anteriormente:
ESTADO id + * ( ) $ E T F
0 d5 d4 1 2 3
1 d6 ACEP.
2 r2 d7 r2 r2
3 r4 r4 r4 r4
4 d5 d4 8 2 3
5 r6 r6 r6 r6
6 d5 d4 9 3
7 d5 d4 10
8 d6 d11
9 r1 d7 r1 r1
10 r3 r3 r3 r3
11 r5 r5 r5 r5
ACCIN IR_A
Veamos ahora el caso de una gramtica que no es SLR, si consideramos la
siguiente gramtica:
(0) S S (1) S L=E (2) S E (3) E L
(4) L *E (5) L id
Si realizamos el ejercicio completo nos quedara:
PRIMERO SIGUIENTE
S *, id $
L *, id =, $
82
E *, id =, $
Calculemos los elementos LR(0):
I
0
= {cerradura({[S .S]})} = S .S
S .L=E
S .E
L .*E
E .L
L .id
I
1
= Ir_a(I
0
, S) = S S.
Al generar la tabla el problema estara en I
2
porque obtendramos:
I
2
= Ir_a(I
0
, L) = S L.=E desplazar con =
E L. reducir EL con =
porque = e SIG(E)
Entre I
0
e I
2
la transicin se realizara por "L".
Con lo cual, mediante SLR no podramos generar la tabla, este mtodo no
sera lo suficientemente potente para esta gramtica.
I
3
= Ir_a(I
0
, E) = S E.
I
4
= Ir_a(I
0
, *) = L *.E
E .L
L .*E
L .id
I
5
= Ir_a(I
0
, id) = L id.
I
6
= Ir_a(I
2
, =) = S L=.E
E .L
L .*E
L .id
I
7
= Ir_a(I
4
, E) = L *E.
I
8
= Ir_a(I
4
, L) = E L.
Ir_a(I
4
, *) = I
4
Ir_a(I
4
, id) = I
5
I
9
= Ir_a(I
6
, E) = S L=E.
Ir_a(I
6
, L) = I
8
Ir_a(I
6
, *) = I
4
83
Ir_a(I
6
, id) = I
5
La tabla nos quedara:
ESTADO id = * $ S L E
0 d5 d4 1 2 3
1 ACEP.
2 d6/r3 r3
3 r2
4 d5 d4 8 7
5 r5 r5
6 d5 d4 8 9
7 r4 r4
8 r3 r3
9 r1
ACCIN IR_A
Como se puede ver, tenemos el conflicto en [2,=] tal y como habamos
anunciado.
Ejemplo.- Calcular la tabla de anlisis LR de la siguiente gramtica mediante
el mtodo SLR:
E E+T
E T
T TF
T F
F F*
F a
F b
Ejemplo.- Calcular la tabla de anlisis LR de la siguiente gramtica mediante
el mtodo SLR:
S E $
E T + E
E T
T ( E )
T x
Ejemplo.- Calcular la tabla de anlisis LR de la siguiente gramtica mediante
el mtodo SLR:
S S $
S ( L )
S x
L S
L L , S
84
4.2.4.2 Mtodo LR-cannico.
Es el mtodo LR ms potente, funciona para casi todas las gramticas.
Llamamos elemento de anlisis LR(1) a una produccin con un punto y un
terminal, esto es: [A o.|, a]
Si tenemos un elemento de la forma [A o.|, a], no tiene efecto, en cambio
si es [A o., a] lo que nos indica es que ese es el elemento que hay que
mirar cuando tenemos varios.
Al igual que en SLR, partimos de la gramtica aumentada G generada a partir
de G.
Algoritmo para calcular cerradura(I)
Funcin cerradura(I)
BEGIN
REPEAT
FOR cada elemento [A o.B|, a] e I
Y cada B v e G
Y b e PRIMERO(|a)
Y [B .v, b] e I
DO aadir [B .v, b] en I
UNTIL no se puedan aadir ms a I
END.
Algoritmo para calcular Ir_a(I, X)
Funcin Ir_a(I, X)
BEGIN
Sea J el conjunto [A oX.|, a] tal que [A o.X|, a] e I
Return cerradura(J)
END.
Algoritmos de coleccin de elementos LR(1)
Procedimiento elementos_LR(1)(G)
BEGIN
C = {cerradura({[S .S, $]})}
REPEAT
FOR cada I, I c C Y cada X e G donde Ir_a(I, X) = C Y
Ir_a(I, X) . C, entonces aadir Ir_a(I, X) a C
UNTIL no se puedan aadir ms conjuntos a C
END.
Ejemplo.- Sea la gramtica:
85
S CC
C cC | d
Calcular los elementos de anlisis por el mtodo LR-cannico.
Primero creamos la gramtica extendida:
S S
S CC
C cC | d
I
0
= {cerradura({[S .S, $]})} = S .S, $
S .CC, $ porque PRIMERO($) = {$}
C .cC, c porque PRIMERO(C$} = {c, d}
C .cC, d porque PRIMERO(C$} = {c, d}
C .d, c porque PRIMERO(C$} = {c, d}
C .d, d porque PRIMERO(C$} = {c, d}
Ir_a(I
0
,S) = cerradura([S S., $]) = I
1
= S S. , $
Ir_a(I
0
,C) = cerradura([S C.C, $]) = I
2
= S C.C, $
C .cC, $
C .d, $
Ir_a(I
0
,c) = cerradura([C c.C, c], [C c.C, d]) = I
3
= C c.C, c
C c.C, d
C .cC, c
C .cC, d
C .d, c
C .d, d
As continuaramos hasta llegar a conseguir el autmata siguiente:
86
Obtencin de la tabla de anlisis LR-cannico
Para la construccin de la tabla seguimos los siguientes pasos:
i) Construir el conjunto C = { I
0
, I
1
, ..., I
n
}
ii) Los estados se construyen a partir de los conjuntos I
i
. El estado k es I
k
,
para rellenar las casillas hacemos:
a) Si [A o.a|, b] e I
i
, Ir_a(I
i
,a) = I
j
accion[i, a] = desplazar j
b) Si [A o., a] e I
i
, A = S accion[i, a] = reducir "A o"
c) Si [S S., $] e I
i
accion[i, $] = ACEPTAR
iii) Si Ir_a[I
i
, A] = I
j
, siendo A un no terminal IR_A[i, A] = j
iv) Todas las entradas no definidas constituyen los errores.
Ejemplo.- Para la gramtica anterior la tabla resultante sera:
ESTADO c d $ S C
0 d3 d4 1 2
1 ACEP.
2 d6 d7 5
3 d3 d4 8
4 r3 r3
5 r1
6 d6 d7 9
7 r3
8 r2 r2
9 r2
ACCIN IR_A
S . S, $
S . CC, $
C . cC, c| d
C . d, c| d
I
0
S C. C, $
C . cC, $
C . d, $
I
2
S S. , $
I
1
C c. C, c| d
C . cC, c| d
C . d, c| d
I
3
C d. , c| d
I
4
S CC. , $
I
5
C c. C, $
C . cC, $
C . d, $
I
6
C cC. , c| d
I
8
C d. , $
I
7
C cC. , $
I
9
d
C
c
S
c
d
C
c
d
d
C
c
C
87
4.2.4.3 Mtodo LALR.
El nmero de estados que genera es equiparable al SLR, pero es ms potente,
ms general. Adems la herramienta YACC utiliza este tipo de anlisis.
Existen bastantes algoritmos para realizarlo pero vamos a ver como calcularlo
a partir del LR-cannico.
En LR-cannico tenemos, por ejemplo, un estado en el que nos encontramos:
[A o.a|, e]
[A o.a|, f]
En LALR, llamamos a "o.a|" corazn y definimos un nico estado para ambos.
Tendremos menos estados, menos errores y tambin es menos potente.
En el ejercicio realizado en LR-cannico tenamos:
I
4
= [C d., c|d]
I
7
= [C d., $]
Entonces definimos un estado nico I
47
= [C d., c|d|$].
Ocurre lo mismo con los estados 3 y 6 y con los estados 8 y 9.
Para realizar la tabla LALR hacemos los siguientes pasos:
1. Construimos el conjunto LR(1) como en el caso de LR-cannico.
2. Remplazamos los conjuntos con el mismo corazn por su unin.
3. Encontramos las acciones de la misma forma que en LR-cannico, si
aparecen conflictos es que no puede implementarse como LALR.
4. Construimos la tabla IR_A usando que: el valor del corazn de Ir_a (I
i
, X) es
el mismo para todos los I
i
con el mismo corazn.
La tabla resultante para la gramtica anterior es la siguiente:
ESTADO c D $ S C
0 d36 d47 1 2
1 ACEP.
2 d36 d47 5
36 d36 d47 89
47 r3 r3 r3
5 r1
89 r2 r2 r2
ACCIN IR_A
Ejemplo.- Sea la siguiente gramtica, ya extendida:
88
S S
S aAd | bBd | aBe | bAe
A c
B c
I
0
= {cerradura({[S .S, $]})} = S .S, $
S .aAd, $
S .bBd, $
S .aBe, $
S .bAe, $
Ir_a(I
0
,S) = cerradura([S S., $]) = S S. , $ = I
1
Ir_a(I
0
,a) = cerradura([S a.Ad, $], [S a.Be, $]) = S a.Ad, $ = I
2
S a.Be, $
A .c, d
B .c, e
Ir_a(I
0
,b) = cerradura([S b.Bd, $], [S b.Ae, $]) = S b.Bd, $ = I
3
S b.Ae, $
A .c, e
B .c, d
Ir_a(I
2
,A) = S aA.d, $ = I
4
Ir_a(I
2
,B) = S aB.e, $ = I
5
Ir_a(I
2
,c) = A c., d = I
6
B c., e
Ir_a(I
3
,A) = S bA.e, $ = I
7
Ir_a(I
3
,B) = S bB.d, $ = I
8
Ir_a(I
3
,c) = A c., e = I
9
B c., d
Ir_a(I
4
,d) = S aAd., $ = I
10
Ir_a(I
5
,e) = S aBe., $ = I
11
Ir_a(I
7
,e) = S bAe., $ = I
12
Ir_a(I
8
,d) = S bBd., $ = I
13
Si construimos los conjuntos LR(1), dos de ellos sern:
I
6
= { [A c., d], [B c., e] }
I
9
= { [A c., e], [B c., d] }
que tienen el mismo corazn y daran lugar a la unin de ambos:
I
69
= { [A c., d|e], [B c., d|e] }
89
en donde, al generar la tabla de anlisis LR, producir un conflicto, en dos
casillas aparecern dos reducciones simultneamente A c y B c. Este
sera un ejemplo de gramtica que s es LR-cannico pero no LALR.
4.2.5 Generador de analizadores sintcticos YACC.
YACC significa Yet Another Compiler-Compiler, es decir otro compilador de
compiladores ms; su primera versin fue creada por S. C. Johnson. Este
generador se encuentra disponible como una orden del sistema UNIX y se ha
utilizado para facilitar la implementacin de gran cantidad de analizadores
sintcticos, tanto para compiladores de lenguajes de programacin como con
otros fines.
Si tenemos un programa yacc en un fuente con nombre ejemplo.y, los pasos
para convertirlo en un ejecutable son los siguiente:
yacc ej empl o. y
cc o mi ej empl o y. t ab. c l y
/ *A veces puede ser necesar i o t ambi n l m*/
mi ej empl o < f i cher o_pr ueba. t xt
La secuencia es la siguiente:
4. Yacc crea un fichero fuente en C, y.tab.c, desde una descripcin del
analizador sintctico en YACC.
5. y.tab.c se compila (es un programa C) generndose el ejecutable.
6. Este ejecutable realizar la traduccin especificada.
4.2.5.1 Partes de un programa YACC
La estructura de un programa en YACC es la siguiente:
Declaraciones
%%
Reglas de traduccin
%%
Rutinas de apoyo en C
- En la parte de Declaraciones se incluyen declaraciones ordinarias de C,
delimitadas por %{ y %}, declaraciones de variables temporales usadas por
las reglas de traduccin o procedimientos auxiliares de la segunda y
tercera secciones y componentes lxicos.
- Las reglas de traduccin constan de una produccin de la gramtica y una
accin semntica asociada, de la siguiente forma:
<lado izquierdo> : <alt 1> { accin semntica 1}
| <alt 2> { accin semntica 2}
90
...
| <alt n> { accin semntica n
;
En las reglas de produccin, un carcter simple entrecomillado c se
considera como el smbolo terminal c, y las cadenas sin comillas de letras y
dgitos no declarados como componentes lxicos se consideran no terminales.
Las acciones semnticas son instrucciones C, dentro de ellas, el smbolo $$ se
refiere al valor del atributo asociado con el no terminal del lado izquierdo,
mientras que $i se refiere al valor asociado con el i-simo smbolo gramatical,
terminal o no terminal, del lado derecho.
- Las rutinas de apoyo en C es la tercera parte de un programa en YACC. Es
necesario proporcionar un anlisis lxico con nombre yylex(). Tambin
pueden aadirse rutinas e recuperacin de apoyo.
El analizador lxico yylex() produce pares formados por un componente lxico
y su valor de atributo asociado. Si se devuelve un componente lxico como
CIFRA, ste debe de declararse en la primera seccin del programa. El valor
del atributo asociado a un componente lxico se comunica al analizador
sintctico mediante la variable yylval.
Vamos a ver un ejemplo de implementacin sobre YACC de una calculadora
sencilla. La gramtica para las expresiones aritmticas ser la siguiente:
E E + T | T
T T * F | F
R (E) | dgito
El componente lxico dgito es un solo dgito entre 0 y 9. El cdigo YACC es el
siguiente:
%{
#i ncl ude <ct ype. h>
%}
%t oken CI FRA
%%
l i nea : expr \ n { pr i nt f ( %d\ n, $1) ; }
expr : expr + t er mi no { $$ = $1 + $3; }
| t er mi no
;
t er mi no : t er mi no * f act or {$$ = $1 * $3; }
| f act or
;
f act or : ( expr ) { $$ = $2; }
| CI FRA
;
%%
yyl ex( ) {
91
i nt c;
c = get char ( ) ;
i f ( i sdi gi t ( c) ) {
yyl val = c- 0;
r et ur n CI FRA;
}
r et ur n c;
}
En la parte de declaraciones se ha introducido el include <ctype.h>, que nos
permitir utilizar el yylex() la funcin isdigit. La produccin inicial (linea)
se introduce para obligar al programa a aceptar una entrada seguida de un
carcter de nueva lnea.