Les analyseurs LR ne peuvent pas gérer les règles de grammaire ambiguës, de par leur conception. (Rendu la théorie plus facile dans les années 1970, lorsque les idées étaient en cours d'élaboration).
C et C ++ autorisent tous les deux l'instruction suivante:
x * y ;
Il a deux analyses différentes:
- Cela peut être la déclaration de y, comme pointeur vers le type x
- Cela peut être une multiplication de x et y, jetant la réponse.
Maintenant, vous pourriez penser que ce dernier est stupide et devrait être ignoré. La plupart seraient d'accord avec vous; cependant, il y a des cas où cela peut avoir un effet secondaire (par exemple, si la multiplication est surchargée). mais ce n'est pas le but. Le fait est qu'il y a deux analyses différentes, et donc un programme peut signifier des choses différentes selon la façon dont cela aurait dû être analysé.
Le compilateur doit accepter celle qui convient dans les circonstances appropriées, et en l'absence de toute autre information (par exemple, la connaissance du type de x) doit collecter les deux afin de décider plus tard quoi faire. Une grammaire doit donc le permettre. Et cela rend la grammaire ambiguë.
Ainsi, l'analyse pure LR ne peut pas gérer cela. De nombreux autres générateurs d'analyseurs largement disponibles, tels que Antlr, JavaCC, YACC, ou Bison traditionnel, ou même des analyseurs de type PEG, ne peuvent pas non plus être utilisés de manière «pure».
Il y a beaucoup de cas plus compliqués (la syntaxe du modèle d'analyse nécessite une anticipation arbitraire, alors que LALR (k) peut anticiper la plupart des k jetons), mais il suffit d'un seul contre-exemple pour abattre l'analyse LR pure (ou les autres).
La plupart des vrais analyseurs C / C ++ gèrent cet exemple en utilisant une sorte d'analyseur déterministe avec un hack supplémentaire: ils entrelacent l'analyse avec la collection de tables de symboles ... de sorte qu'au moment où "x" est rencontré, l'analyseur sait si x est un type ou pas, et peut ainsi choisir entre les deux analyses potentielles. Mais un analyseur qui fait cela n'est pas sans contexte, et les analyseurs LR (les purs, etc.) sont (au mieux) sans contexte.
On peut tricher et ajouter des vérifications sémantiques par règle de réduction du temps dans les analyseurs de LR pour faire cette désambiguïsation. (Ce code n'est souvent pas simple). La plupart des autres types d'analyseurs ont des moyens pour ajouter des vérifications sémantiques à divers points de l'analyse, qui peuvent être utilisés pour ce faire.
Et si vous trichez suffisamment, vous pouvez faire fonctionner les analyseurs LR pour C et C ++. Les gars de GCC l'ont fait pendant un certain temps, mais l'ont abandonné pour une analyse codée à la main, je pense parce qu'ils voulaient de meilleurs diagnostics d'erreur.
Il existe une autre approche, cependant, qui est agréable et propre et analyse parfaitement C et C ++ sans aucun piratage de table de symboles: les analyseurs GLR . Ce sont des analyseurs sans contexte complets (ayant effectivement une anticipation infinie). Les analyseurs GLR acceptent simplement les deux analyses, produisant un "arbre" (en fait un graphe acyclique dirigé qui ressemble principalement à un arbre) qui représente l'analyse ambiguë. Une passe post-analyse peut résoudre les ambiguïtés.
Nous utilisons cette technique dans les frontaux C et C ++ pour notre Tookit de réingénierie logicielle DMS (à partir de juin 2017, ils gèrent le C ++ 17 complet dans les dialectes MS et GNU). Ils ont été utilisés pour traiter des millions de lignes de grands systèmes C et C ++, avec des analyses complètes et précises produisant des AST avec des détails complets sur le code source. (Voir l'AST pour l'analyse la plus épineuse de C ++. )