Je lisais sur les analyseurs et les générateurs d'analyseurs et j'ai trouvé cette déclaration dans l'analyse LR de wikipedia -page:
De nombreux langages de programmation peuvent être analysés en utilisant une variante d'un analyseur LR. Une exception notable est le C ++.
Pourquoi en est-il ainsi? Quelle propriété particulière de C ++ rend impossible l'analyse avec les analyseurs LR?
En utilisant google, j'ai seulement trouvé que C peut être parfaitement analysé avec LR (1) mais C ++ nécessite LR (∞).
Réponses:
Il existe un fil de discussion intéressant sur Lambda the Ultimate qui traite de la grammaire LALR pour C ++ .
Il comprend un lien vers une thèse de doctorat qui comprend une discussion sur l'analyse C ++, qui stipule que:
Il continue en donnant un certain nombre d'exemples (voir page 147 du pdf).
L'exemple est:
sens
Comparer aux:
sens
(une expression séparée par des virgules).
Les deux séquences de jetons ont la même sous-séquence initiale mais des arbres d'analyse différents, qui dépendent du dernier élément. Il peut y avoir arbitrairement de nombreux jetons avant celui qui désambiguise.
la source
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:
Il a deux analyses différentes:
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 ++. )
la source
x * y
et j'ai ri - c'est incroyable de voir à quel point on pense à de petites ambiguïtés astucieuses comme celle-ci.Le problème n'est jamais défini comme ça, alors qu'il devrait être intéressant:
quel est le plus petit ensemble de modifications de la grammaire C ++ qui serait nécessaire pour que cette nouvelle grammaire puisse être parfaitement analysée par un analyseur yacc "non-context-free"? (n'utilisant qu'un seul 'hack': l'homonymie du nom de type / identifiant, l'analyseur informant le lexer de chaque typedef / class / struct)
J'en vois quelques-uns:
Type Type;
est interdit. Un identifiant déclaré en tant que nom de type ne peut pas devenir un identifiant sans nom de type (note quistruct Type Type
n'est pas ambiguë et pourrait être encore autorisée).Il existe 3 types de
names tokens
:types
: type intégré ou à cause d'un typedef / class / structConsidérer les fonctions de modèle comme des jetons différents résout l'
func<
ambiguïté. Sifunc
est un nom de fonction de modèle, alors<
doit être le début d'une liste de paramètres de modèle, sinonfunc
est un pointeur de fonction et<
est l'opérateur de comparaison.Type a(2);
est une instanciation d'objet.Type a();
etType a(int)
sont des prototypes de fonction.int (k);
est totalement interdit, doit être écritint k;
typedef int func_type();
ettypedef int (func_type)();
sont interdits.Une fonction typedef doit être un pointeur de fonction typedef:
typedef int (*func_ptr_type)();
la récursivité du template est limitée à 1024, sinon un maximum augmenté pourrait être passé en option au compilateur.
int a,b,c[9],*d,(*f)(), (*g)()[9], h(char);
pourrait être interdit aussi, remplacé parint a,b,c[9],*d;
int (*f)();
int (*g)()[9];
int h(char);
une ligne par prototype de fonction ou déclaration de pointeur de fonction.
Une alternative hautement préférée serait de changer la terrible syntaxe du pointeur de fonction,
int (MyClass::*MethodPtr)(char*);
en cours de resyntaxe comme:
int (MyClass::*)(char*) MethodPtr;
ceci étant cohérent avec l'opérateur de distribution
(int (MyClass::*)(char*))
typedef int type, *type_ptr;
pourrait être interdit aussi: une ligne par typedef. Ainsi il deviendraittypedef int type;
typedef int *type_ptr;
sizeof int
,sizeof char
,sizeof long long
Et co. pourrait être déclaré dans chaque fichier source. Ainsi, chaque fichier source utilisant le typeint
doit commencer par#type int : signed_integer(4)
et
unsigned_integer(4)
serait interdit en dehors de cette#type
directive ce serait un grand pas dans l'sizeof int
ambiguïté stupide présente dans tant d'en-têtes C ++Le compilateur implémentant le C ++ resyntaxé, s'il rencontrait une source C ++ utilisant une syntaxe ambiguë, déplacerait
source.cpp
également unambiguous_syntax
dossier et créerait automatiquement un fichier traduit sans ambiguïtésource.cpp
avant de le compiler.Veuillez ajouter vos syntaxes C ++ ambiguës si vous en connaissez!
la source
Comme vous pouvez le voir dans ma réponse ici , C ++ contient une syntaxe qui ne peut pas être analysée de manière déterministe par un analyseur LL ou LR en raison de l'étape de résolution de type (généralement après l'analyse) modifiant l' ordre des opérations , et donc la forme fondamentale de l'AST ( généralement censé être fourni par une analyse de première étape).
la source
Je pense que vous êtes assez proche de la réponse.
LR (1) signifie que l'analyse de gauche à droite n'a besoin que d'un seul jeton pour anticiper le contexte, alors que LR (∞) signifie une anticipation infinie. Autrement dit, l'analyseur devrait savoir tout ce qui allait arriver pour savoir où il se trouve maintenant.
la source
Le problème "typedef" en C ++ peut être analysé avec un analyseur LALR (1) qui construit une table de symboles lors de l'analyse (pas un analyseur LALR pur). Le problème du «modèle» ne peut probablement pas être résolu avec cette méthode. L'avantage de ce type d'analyseur LALR (1) est que la grammaire (illustrée ci-dessous) est une grammaire LALR (1) (sans ambiguïté).
L'entrée suivante peut être analysée sans problème:
Le générateur d'analyseur LRSTAR lit la notation grammaticale ci-dessus et génère un analyseur qui gère le problème "typedef" sans ambiguïté dans l'arborescence d'analyse ou AST. (Divulgation: je suis le gars qui a créé LRSTAR.)
la source