JavaCup - Control de errores
Recuperación de Errores
Uno de los aspectos más importantes en el desarrollo de parsers CUP es el soporte de la recuperación de errores sintácticos. CUP utiliza los mismos mecanismos de recuperación que YACC. En particular, soporta un símbolo especial de error, denotado simplemente como error, que simplemente juega el rol de un no terminal especial que en lugar de ser definido por producciones, reconoce una secuencia de entrada errónea.
El símbolo error solo aparece si el error de sintaxis se detecta. Si se detecta un error de sintaxis, el parser intenta reemplazar alguna porción del stream de tokens de entrada con error y luego continuar el parsing. Por ejemplo, en presencia de producciones tales como :
stmt::= expr SEMI | while_stmt SEMI | if_stmt SEMI | ... | error SEMI ;
indica que si ninguna de las producciones normales para stmt pueden ser reconocidas en la entrada, entonces debería declararse un error, y la recuperación debería llevarse a cabo ignorando los tokens erróneos (lo cual es equivalente a reconocer y rremplazarlos con error) sobre un punto en el que el parser pueda continuar con un punto y coma (y el contexto adicional que "legalmente" sigue a una sentencia). Se considera que un error se ha recobrado si y sólo si un número suficiente de tokens anteriores al símbolo error pueden ser parseados exitosamente. (El número de tokens requerido es determinado por el método del parser error_sync_size() y por defecto es 3).
Específicamente, el parser primero busca en el tope del stack el estado
más cercano que tiene una transición de salida para error. Esto, generalmente
corresponde para trabajar desde producciones que representan constructores
más detallados (tales como una clase específica de sentencias) sobre las
producciones que representan constructores más generales o englobadores,
tales como la producción general para todas las sentencias o una producción
que representa una sección de declaraciones en su totalidad, hasta arribar
a un lugar en el que se provea una producción de recuperación del error.
Una vez que el parser haya alcanzado una configuración que tiene una recuperación
de error inmediata (desapilando del stack el primero de tales estados),
el parser comienza por ignorar los tokens para encontrar un punto en el
que el parsing pueda continuar.
Luego de descartar cada token, el parser intenta parsear a la cabeza de
la entrada (sin ejecutar ninguna de las acciones semánticas incluídas).
Si el parser puede parsear exitosamente pasado el número de tokens requerido,
la entrada es retrocedida sobre el punto de recuperación y el proceso de
parsing se reanuda normalmente, ejecutando todas las acciones. Si el parsing
no puede continuar lo suficientemente lejos, entonces se descarta otro
token y el parser nuevamente intenta parsear a la cabeza. Si se llega al
final de la entrada sin lograr una recuperación exitosa (o no pudo encontrarse
un estado de recuperación del error en el stack, para comenzar) entonces
la recuperación del error falla.