_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 }#_ParseUn 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; }