

_Parse contiene el algoritmo de
análisis LR genérico. En esta sección
nos concentraremos en la forma en la que se ha implantado en
yapp la recuperación de errores.
1 sub _Parse {
2 my($self)=shift;
3 ...
4 $$errstatus=0; $$nberror=0;
La variable $$errstatus nos indica la situación con respecto a la recuperación
de errores. La variable $$nberror contiene el número total de errores.
5 ($$token,$$value)=(undef,undef);
6 @$stack=( [ 0, undef ] ); $$check='';
7 while(1) {
8 my($actions,$act,$stateno);
9 $stateno=$$stack[-1][0];
10 $actions=$$states[$stateno];
11
12 if (exists($$actions{ACTIONS})) {
13 defined($$token) or do { ($$token,$$value)=&$lex($self); };
14 ...
15 }
16 else { $act=$$actions{DEFAULT}; }
Si $act no esta definida es que ha ocurrido un
error. En tal caso no se entra a estudiar
si la acción es de desplazamiento o reducción.
17 defined($act) and do {
18 $act > 0 and do { #shift
19 $$errstatus and do { --$$errstatus; };
20 ...
21 next;
22 };
23 #reduce
24 ....
25 $$check eq 'ERROR' or do {
26 push(@$stack, [ $$states[$$stack[-1][0]]{GOTOS}{$lhs}, $semval ]);
27 $$check='';
28 next;
29 };
30 $$check='';
31 };
Si $$errstatus es cero es que estamos
ante un nuevo error:
32 #Error
33 $$errstatus or do {
34 $$errstatus = 1;
35 &$error($self);
36 $$errstatus # if 0, then YYErrok has been called
37 or next; # so continue parsing
38 ++$$nberror;
39 };
Como el error es ``nuevo'' se llama a la subrutina de
tratamiento de errores &$error. Obsérvese que no
se volverá a llamar a la rutina de manejo de errores
hasta que $$errstatus vuelva a alcanzar
el valor cero. Puesto que &$error ha sido
escrita por el usuario, es posible que este haya llamado
al método YYErrok. Si ese es el caso, es que el
programador prefiere que el análisis continúe como
si la recuperación de errores se hubiera completado.
Ahora se pone $$errstatus a 3:
47 $$errstatus=3;Cada vez que se logre un desplazamiento con éxito
$$errstatus
será decrementado (línea 19).
A continuación se retiran estados de la pila hasta
que se encuentre alguno que pueda transitar ante el
terminale especial error:
48 while(@$stack
49 and (not exists($$states[$$stack[-1][0]]{ACTIONS})
50 or not exists($$states[$$stack[-1][0]]{ACTIONS}{error})
51 or $$states[$$stack[-1][0]]{ACTIONS}{error} <= 0)) {
52 pop(@$stack);
53 }
54 @$stack or do {
55 return(undef);
56 };
Si la pila quedó vacía se devuelve undef. En caso contrario
es que el programador escribió alguna regla para la
recuperación de errores. En ese caso, se transita al estado
correspondiente:
57 #shift the error token
58 push(@$stack, [ $$states[$$stack[-1][0]]{ACTIONS}{error}, undef ]);
59 }
60 #never reached
61 croak("Error in driver logic. Please, report it as a BUG");
62 }#_Parse
Un poco antes tenemos el siguiente código:
41 $$errstatus == 3 #The next token is not valid: discard it
42 and do {
43 $$token eq '' # End of input: no hope
44 and do { return(undef); };
45 $$token=$$value=undef;
46 };
Si hemos alcanzado el final de la entrada en una situación de error
se abandona devolviendo undef.
$$errstatus es 3, el último
terminal no ha producido un desplazamiento correcto. ¿Porqué?
sub YYErrok {
my($self)=shift;
${$$self{ERRST}}=0;
undef;
}
El método YYErrok cambia el valor referenciado por $errstatus.
De esta forma se le da al programador yapp la oportunidad de anunciar
que es muy probable que la fase de recuperación de errores se haya completado.
Los dos siguientes métodos devuelven el número de errores hasta el momento
(YYNberr)
y si nos encontramos o no en fase de recuperación de errores (YYRecovering):
sub YYNberr {
my($self)=shift;
${$$self{NBERR}};
}
sub YYRecovering {
my($self)=shift;
${$$self{ERRST}} != 0;
}

