Recuperacion de Errores: Visión Detallada

La subrutina _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.

Ejercicio 35.26.1   Explique la razón para el comentario de la línea 41. Si $$errstatus es 3, el último terminal no ha producido un desplazamiento correcto. ¿Porqué?

A continuación aparecen los códigos de los métodos implicados en la recuperación de errores:
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;
}

Casiano Rodríguez León
2013-04-23