He leído varios artículos, artículos y la sección 4.1.4, capítulo 4 de Compiladores: Principios, Técnicas y Herramientas (2ª Edición) (también conocido como "El Libro del Dragón") que tratan sobre el tema de la recuperación de errores del compilador sintáctico. Sin embargo, después de experimentar con varios compiladores modernos, he visto que también se recuperan de errores semánticos , así como de errores sintácticos.
Entiendo bastante bien los algoritmos y técnicas detrás de los compiladores que se recuperan de errores relacionados sintácticamente, sin embargo, no entiendo exactamente cómo un compilador puede recuperarse de un error semántico.
Actualmente estoy usando una ligera variación del patrón de visitante para generar código a partir de mi árbol de sintaxis abstracta. Considere mi compilador compilando las siguientes expresiones:
1 / (2 * (3 + "4"))
El compilador generaría el siguiente árbol de sintaxis abstracta:
op(/)
|
-------
/ \
int(1) op(*)
|
-------
/ \
int(2) op(+)
|
-------
/ \
int(3) str(4)
La fase de generación de código luego usaría el patrón de visitante para recorrer recursivamente el árbol de sintaxis abstracta y realizar la verificación de tipo. El árbol de sintaxis abstracta se atravesaría hasta que el compilador llegara a la parte más interna de la expresión; (3 + "4")
. Luego, el compilador verifica cada lado de las expresiones y ve que no son semánticamente equivalentes. El compilador genera un error de tipo. Aquí es donde radica el problema. ¿Qué debería hacer ahora el compilador ?
Para que el compilador se recupere de este error y continúe verificando el tipo de las partes externas de las expresiones, tendría que devolver algún tipo ( int
o str
) de la evaluación de la parte más interna de la expresión, a la siguiente parte más interna de la expresión. Pero simplemente no tiene un tipo para devolver . Como se produjo un error de tipo, no se dedujo ningún tipo.
Una posible solución que he postulado es que si se produce un error de tipo, se debe generar un error, y un valor especial que significa que se produjo un error de tipo, debe devolverse a las llamadas transversales de árbol de sintaxis abstracta anteriores. Si las llamadas transversales anteriores encuentran este valor, saben que se produjo un error de tipo más profundo en el árbol de sintaxis abstracta y deben evitar intentar deducir un tipo. Si bien este método parece funcionar, parece ser muy ineficiente. Si la parte más interna de una expresión está profunda en el árbol de sintaxis abstracta, entonces el compilador tendrá que hacer muchas llamadas recursivas solo para darse cuenta de que no se puede hacer un trabajo real, y simplemente regresar de cada una.
Se utiliza el método que describí anteriormente (lo dudo). Si es así, ¿no es eficiente? Si no, ¿cuáles son exactamente los métodos utilizados cuando los compiladores se recuperan de los errores semánticos?