Os analisadores LR não podem lidar com regras gramaticais ambíguas, por design. (Facilitou a teoria na década de 1970, quando as idéias estavam sendo elaboradas).
C e C ++ permitem a seguinte instrução:
x * y ;
Ele tem duas análises diferentes:
- Pode ser a declaração de y, como ponteiro para o tipo x
- Pode ser uma multiplicação de xey, jogando fora a resposta.
Agora, você pode pensar que o último é estúpido e deve ser ignorado. A maioria concordaria com você; no entanto, há casos em que isso pode ter um efeito colateral (por exemplo, se a multiplicação estiver sobrecarregada). mas esse não é o ponto. O ponto é que existem duas análises diferentes e, portanto, um programa pode significar coisas diferentes, dependendo de como isso deveria ter sido analisado.
O compilador deve aceitar o apropriado nas circunstâncias apropriadas e, na ausência de qualquer outra informação (por exemplo, conhecimento do tipo de x), deve coletar os dois para decidir posteriormente o que fazer. Assim, uma gramática deve permitir isso. E isso torna a gramática ambígua.
Portanto, a análise de LR pura não pode lidar com isso. Muitos outros geradores de analisadores amplamente disponíveis, como Antlr, JavaCC, YACC ou Bison tradicional, ou mesmo analisadores de estilo PEG, também não podem ser usados de maneira "pura".
Existem muitos casos mais complicados (a sintaxe do modelo de análise requer um olhar arbitrário, enquanto o LALR (k) pode olhar para a frente na maioria dos k tokens), mas apenas é necessário um contraexemplo para eliminar a análise de LR pura (ou outras).
A maioria dos analisadores C / C ++ reais manipula esse exemplo usando algum tipo de analisador determinístico com um hack extra: eles entrelaçam a análise com a coleção de tabelas de símbolos ... para que, quando "x" for encontrado, o analisador saiba se x é um tipo ou não e, portanto, pode escolher entre as duas análises em potencial. Mas um analisador que faz isso não é livre de contexto, e os analisadores LR (os puros etc.) são (na melhor das hipóteses) livres de contexto.
É possível trapacear e adicionar verificações semânticas por tempo de redução por regra nos analisadores LR para fazer essa desambiguação. (Esse código geralmente não é simples). A maioria dos outros tipos de analisadores possui alguns meios para adicionar verificações semânticas em vários pontos da análise, que podem ser usadas para fazer isso.
E se você trapacear o suficiente, poderá fazer com que os analisadores LR funcionem para C e C ++. Os caras do GCC fizeram isso por um tempo, mas desistiram da análise codificada manualmente, acho que porque queriam um diagnóstico de erro melhor.
Há outra abordagem, porém, que é legal e limpa e analisa C e C ++ sem problemas, sem nenhum truque de tabela de símbolos: analisadores GLR . Esses são analisadores livres de contexto completo (com cabeçalho efetivamente infinito). Os analisadores GLR simplesmente aceitam ambos os analisadores, produzindo uma "árvore" (na verdade, um gráfico acíclico direcionado que é principalmente semelhante a uma árvore) que representa a análise ambígua. Um passe de pós-análise pode resolver as ambiguidades.
Usamos essa técnica nos front-ends C e C ++ para o nosso DMS Software Reengineering Tookit (em junho de 2017, eles lidam com C ++ 17 completo em dialetos MS e GNU). Eles foram usados para processar milhões de linhas de grandes sistemas C e C ++, com análises completas e precisas, produzindo ASTs com detalhes completos do código-fonte. (Consulte o AST para a análise mais irritante do C ++. )