Eu li vários artigos, artigos e a seção 4.1.4, capítulo 4 de Compiladores: Princípios, Técnicas e Ferramentas (2ª Edição) (também conhecida como "O Livro do Dragão"), que discutem o tópico da recuperação de erro de compilador sintático. No entanto, depois de experimentar vários compiladores modernos, vi que eles também se recuperam de erros semânticos e de sintaxe.
Entendo muito bem os algoritmos e as técnicas por trás dos compiladores que se recuperam de erros sintaticamente relacionados, mas não entendo exatamente como um compilador pode se recuperar de um erro semântico.
Atualmente, estou usando uma ligeira variação do padrão de visitante para gerar código da minha árvore de sintaxe abstrata. Considere meu compilador compilando as seguintes expressões:
1 / (2 * (3 + "4"))
O compilador geraria a seguinte árvore de sintaxe abstrata:
op(/)
|
-------
/ \
int(1) op(*)
|
-------
/ \
int(2) op(+)
|
-------
/ \
int(3) str(4)
A fase de geração de código usaria o padrão de visitante para percorrer recursivamente a árvore de sintaxe abstrata e executar a verificação de tipo. A árvore de sintaxe abstrata seria percorrida até que o compilador chegasse à parte mais interna da expressão; (3 + "4")
. O compilador verifica cada lado das expressões e vê que elas não são semanticamente equivalentes. O compilador gera um erro de tipo. Aqui é onde está o problema. O que agora o compilador deve fazer ?
Para que o compilador se recupere desse erro e continue verificando as partes externas das expressões, ele precisará retornar algum tipo ( int
ou str
) da avaliação da parte mais interna da expressão para a próxima parte mais interna da expressão. Mas ele simplesmente não tem um tipo para retornar . Como ocorreu um erro de tipo, nenhum tipo foi deduzido.
Uma solução possível que eu postulei é que, se ocorrer um erro de tipo, um erro deve ser gerado e um valor especial que significa que ocorreu um erro de tipo deve ser retornado para as chamadas anteriores da árvore de sintaxe abstrata. Se chamadas transversais anteriores encontrarem esse valor, eles sabem que ocorreu um erro de tipo mais profundo na árvore de sintaxe abstrata e devem evitar tentar deduzir um tipo. Embora esse método pareça funcionar, parece ser muito ineficiente. Se a parte mais interna de uma expressão estiver profunda na árvore de sintaxe abstrata, o compilador precisará fazer muitas chamadas recursivas apenas para perceber que nenhum trabalho real pode ser feito e simplesmente retornar de cada uma.
O método que eu descrevi acima foi usado (duvido). Se sim, não é eficiente? Caso contrário, quais são exatamente os métodos usados quando os compiladores se recuperam de erros semânticos?