Quelle est la différence entre les analyseurs syntaxiques LR, SLR et LALR? Je sais que SLR et LALR sont des types d'analyseurs syntaxiques LR, mais quelle est la différence réelle en ce qui concerne leurs tables d'analyse?
Et comment montrer si une grammaire est LR, SLR ou LALR? Pour une grammaire LL, il suffit de montrer que toute cellule de la table d'analyse ne doit pas contenir plusieurs règles de production. Des règles similaires pour LALR, SLR et LR?
Par exemple, comment pouvons-nous montrer que la grammaire
S --> Aa | bAc | dc | bda
A --> d
est LALR (1) mais pas SLR (1)?
EDIT (ybungalobill) : Je n’ai pas obtenu de réponse satisfaisante quant à la différence entre LALR et LR. Les tables de LALR sont donc de taille plus petite mais ne peuvent reconnaître qu'un sous-ensemble de grammaires LR. Quelqu'un peut-il élaborer davantage sur la différence entre LALR et LR s'il vous plaît? LALR (1) et LR (1) suffiront pour répondre. Les deux utilisent un jeton anticipé et les deux sont pilotés par des tables! Comment sont-ils différents?
Les analyseurs syntaxiques SLR, LALR et LR peuvent tous être implémentés en utilisant exactement les mêmes machines pilotées par table.
Fondamentalement, l'algorithme d'analyse collecte le prochain jeton d'entrée T et consulte l'état actuel S (et les tables d'anticipation, de GOTO et de réduction associées) pour décider quoi faire:
Alors, s'ils utilisent tous la même machine, à quoi ça sert?
La valeur supposée de SLR est sa simplicité de mise en œuvre. vous n'avez pas à parcourir les réductions possibles en vérifiant les ensembles d'anticipation car il y en a au plus une, et il s'agit de la seule action viable s'il n'y a pas de sorties SHIFT de l'état. La réduction applicable peut être attachée spécifiquement à l'État, afin que les machines d'analyse syntaxique des reflex ne soient pas obligées de la rechercher. Dans la pratique, les analyseurs L (AL) R gèrent un ensemble de langages bien plus large et utile, et impliquent si peu de travail supplémentaire à mettre en œuvre que personne ne met en œuvre le reflex sauf comme exercice académique.
La différence entre LALR et LR concerne la table générateur. Les générateurs d’analyseur LR gardent une trace de toutes les réductions possibles d’états spécifiques et de leur ensemble d’observations précises; vous vous retrouvez avec des états dans lesquels chaque réduction est associée à son ensemble exact d'anticipation depuis son contexte de gauche. Cela tend à construire des ensembles d'états assez importants. Les générateurs d'analyseurs LALR sont disposés à combiner des états si les tables et les ensembles de têtes de lecture GOTO pour les réductions sont compatibles et n'entrent pas en conflit. cela produit un nombre beaucoup plus petit d'états, au prix de ne pas être en mesure de distinguer certaines séquences de symboles que LR peut distinguer. Ainsi, les analyseurs syntaxiques LR peuvent analyser un plus grand ensemble de langues que les analyseurs syntaxiques LALR, mais ils disposent de tables d'analyse beaucoup plus volumineuses. En pratique, on peut trouver des grammaires LALR suffisamment proches des langages cibles pour que la taille de la machine à états mérite d'être optimisée. les endroits où l'analyseur LR serait meilleur sont traités par une vérification ad hoc en dehors de l'analyseur.
Donc: tous les trois utilisent les mêmes machines. Le reflex est "facile" dans le sens où vous pouvez ignorer un tout petit peu de la machinerie, mais cela ne vaut tout simplement pas la peine. LR analyse un plus grand nombre de langues mais les tables des états ont tendance à être assez volumineuses. Cela laisse LALR comme choix pratique.
Cela dit, il faut savoir que les analyseurs GLR peuvent analyser n’importe quel langage dépourvu de contexte, en utilisant des machines plus complexes mais les mêmes tableaux (y compris la version plus petite utilisée par LALR). Cela signifie que GLR est strictement plus puissant que LR, LALR et SLR; à peu près si vous pouvez écrire une grammaire BNF standard, GLR analysera en fonction de celui-ci. La différence dans les machines réside dans le fait que GLR est disposé à essayer plusieurs analyses en cas de conflit entre la table GOTO et/ou les ensembles d'anticipation. (Comment GLR fait cela efficacement est un pur génie [pas le mien] mais ne rentrera pas dans ce post SO).
Pour moi, c'est un fait extrêmement utile. Je construis des analyseurs de programme et des transformateurs de code et des analyseurs syntaxiques sont nécessaires mais "sans intérêt"; le travail intéressant correspond à ce que vous faites avec le résultat analysé et l’accent est mis sur le travail de post-analyse. Utiliser GLR signifie que je peux relativement facilement construire des grammaires qui fonctionnent, par rapport au piratage d’une grammaire pour obtenir une forme utilisable par LALR. Cela est très important lorsque vous essayez de traiter avec des langages non-académiques tels que C++ ou Fortran, où vous avez littéralement besoin de milliers de règles pour bien gérer la langue entière, et vous ne voulez pas passer votre vie à essayer de pirater les règles de grammaire à respecter les limites de LALR (ou même LR).Comme une sorte d’exemple célèbre, C++ est considéré comme extrêmement difficile à analyser ... par les gars qui analysent LALR. C++ est facile à analyser en utilisant une machine GLR en utilisant à peu près les règles fournies à la fin du manuel de référence C++. (Je possède précisément un tel analyseur, qui gère non seulement Vanilla C++, mais également divers dialectes de vendeurs. Cela n’est possible que dans la pratique car nous utilisons un analyseur GLR, IMHO).
[EDIT novembre 2011: nous avons étendu notre analyseur syntaxique à l’ensemble du langage C++ 11. GLR a rendu cela beaucoup plus facile à faire. EDIT août 2014: gestion de l’ensemble de C++ 17. Rien ne s'est cassé ou a empiré, GLR est toujours le miaou du chat.].
[EDIT November 2011: We've extended our parser to handle all of C++11. GLR made that a lot easier to do. EDIT Aug 2014: Now handling all of C++17. Nothing broke or got worse, GLR is still the cat's meow.]
Les analyseurs syntaxiques LALR fusionnent des états similaires dans une grammaire LR pour produire des tables d'état analyseur qui ont exactement la même taille que la grammaire SLR équivalente, qui sont généralement d'un ordre de grandeur inférieur aux tables d'analyse LR pur. Toutefois, pour les grammaires LR trop complexes pour être LALR, ces états fusionnés génèrent des conflits d’analyseur ou produisent un analyseur ne reconnaissant pas pleinement la grammaire LR originale.
BTW, je mentionne quelques choses à ce sujet dans mon algorithme de table d’analyse MLR (k) ici .
Addenda
La réponse courte est que les tables d’analyse LALR sont plus petites, mais que l’analyseur est le même. Une grammaire LALR donnée produira des tables d'analyse beaucoup plus grandes si tous les états LR sont générés, avec beaucoup d'états redondants (presque identiques).
Les tables LALR sont plus petites car les états similaires (redondants) sont fusionnés, ce qui élimine efficacement les informations contextuelles/anticipées que les différents états encodent. L'avantage est que vous obtenez des tables d'analyse beaucoup plus petites pour la même grammaire.
L'inconvénient est que toutes les grammaires LR ne peuvent pas être codées sous forme de tables LALR, car les grammaires plus complexes ont des types d'apparence plus compliqués, ce qui donne deux ou plusieurs états au lieu d'un seul état fusionné.
La principale différence est que l’algorithme pour produire des tables LR transporte plus d’informations entre les transitions d’un état à l’autre, contrairement à l’algorithme LALR. Donc, l'algorithme LALR ne peut pas dire si un état fusionné donné doit vraiment être laissé sous la forme de deux états séparés ou plus.
Encore une autre réponse (YAA).
Les algorithmes d’analyse pour SLR (1), LALR (1) et LR (1) sont identiques, comme le dit Ira Baxter.
Cependant, les tables d’analyse peuvent être différentes en raison de l’algorithme de génération d’analyseur.
Un générateur d’analyseur SLR crée une machine à états LR (0) et calcule les anticipations à partir de la grammaire (ensembles FIRST et FOLLOW). Cette approche est simplifiée et peut signaler des conflits qui n'existent pas vraiment dans la machine à états LR (0).
Un générateur d'analyseur syntaxique LALR crée une machine à états LR (0) et calcule les anticipations à partir de la machine à états LR (0) (via les transitions de terminaux). Cette approche est correcte, mais signale parfois des conflits qui n'existeraient pas dans une machine à états LR (1).
Un générateur d'analyseur syntaxique Canonical LR calcule une machine à états LR (1) et les anticipations font déjà partie de la machine à états LR (1). Ces tables d'analyse peuvent être très volumineuses.
Un générateur d'analyseur syntaxique LR minimal calcule une machine à états LR (1), mais fusionne des états compatibles au cours du processus, puis calcule les prévisions à partir de la machine à états LR (1) minimal. Ces tables d’analyseur ont la même taille ou sont légèrement plus grandes que les tables d’analyseur LALR, ce qui donne la meilleure solution.
LRSTAR 9.1 peut générer des analyseurs syntaxiques LR (1) et LR (*) minimaux en C++. Voir ce diagramme qui montre la différence entre les analyseurs syntaxiques LR.
[Divulgation complète: LRSTAR est mon produit]
Supposons qu'un analyseur syntaxique sans recherche préalable analyse avec bonheur les chaînes de votre grammaire.
En utilisant votre exemple donné, il trouve une chaîne dc
, que fait-il? Le réduit-il à S
, car dc
est une chaîne valide produite par cette grammaire? OR peut-être avons-nous essayé d'analyser bdc
parce que même si c'est une chaîne acceptable?
En tant qu'êtres humains, nous savons que la réponse est simple, nous devons simplement nous rappeler si nous venons d'analyser ou non b
. Mais les ordinateurs sont stupides :)
Etant donné qu'un analyseur syntaxique de reflex (1) disposait du pouvoir supplémentaire sur LR (0) d'effectuer une recherche anticipée, nous savons que toute quantité de recherche anticipée ne peut pas nous dire quoi faire dans ce cas; au lieu de cela, nous devons regarder en arrière dans notre passé. Ainsi vient l’analyseur canonique LR à la rescousse. Il se souvient du contexte passé.
La manière dont il se souvient de ce contexte est qu’elle se discipline elle-même, que chaque fois qu’elle rencontrera un b
, elle commencera à marcher sur le chemin de la lecture de bdc
, comme une possibilité. Ainsi, quand il voit un d
, il sait s'il marche déjà sur un chemin . Ainsi, un analyseur CLR (1) peut faire des choses qu'un analyseur SLR (1) ne peut pas!
Mais maintenant, puisque nous avons dû définir autant de chemins, les états de la machine deviennent très grands!
Nous fusionnons donc les mêmes chemins, mais comme prévu, cela pourrait engendrer des problèmes de confusion. Cependant, nous sommes prêts à prendre le risque de réduire cette taille.
Ceci est votre analyseur LALR (1).
Maintenant, comment le faire par algorithme.
Lorsque vous dessinez les jeux de configuration pour la langue ci-dessus, vous constaterez un conflit de réduction de décalage dans deux états. Pour les supprimer, vous pouvez envisager un reflex (1), qui prend les décisions en suivant, mais vous remarquerez qu'il ne le pourra toujours pas. Ainsi, vous dessineriez à nouveau les jeux de configuration, mais cette fois avec une restriction voulant que, chaque fois que vous calculez la fermeture, les productions supplémentaires ajoutées soient strictement suivies. Reportez-vous à n'importe quel manuel sur ce que ceux-ci devraient suivre.
Les analyseurs syntaxiques SLR reconnaissent un sous-ensemble correct de grammaires reconnaissables par les analyseurs syntaxiques LALR (1), lesquels reconnaissent à leur tour un sous-ensemble correct de grammaires identifiables par les analyseurs syntaxiques LR (1).
Chacune de celles-ci est construite comme une machine à états, chaque état représentant un ensemble de règles de production de la grammaire (et la position dans chacune d'elles) lors de l'analyse de l'entrée.
Le Dragon Book exemple d’une grammaire LALR (1) qui n’est pas un reflex est le suivant:
S → L = R | R
L → * R | id
R → L
Voici l'un des états pour cette grammaire:
S → L•= R
R → L•
Le •
indique la position de l'analyseur dans chacune des productions possibles. Il ne sait pas laquelle des productions est réellement présente jusqu'à ce qu'elle atteigne la fin et tente de réduire.
Dans ce cas, l'analyseur peut décaler un =
ou réduire R → L
.
Un analyseur syntaxique SLR (aka LR (0)) déterminerait s'il pouvait réduire le nombre de personnes en vérifiant si le prochain symbole d'entrée est dans le follow set de R
(c'est-à-dire l'ensemble des terminaux de la grammaire pouvant suivre R
). Étant donné que =
est également dans cet ensemble, l'analyseur de reflex rencontre un conflit de réduction de décalage.
Cependant, un analyseur LALR (1) utiliserait l’ensemble des terminaux pouvant suivre cette production particulière} de R, qui n’est que $
(c’est-à-dire la fin de l’entrée). Donc, pas de conflit.
Comme l'ont fait remarquer de précédents commentateurs, les analyseurs LALR (1) ont le même nombre d'états que les analyseurs SLR. Un algorithme de propagation par anticipation est utilisé pour associer des anticipations aux productions d'états de reflex des états LR (1) correspondants. L’analyseur LALR (1) résultant peut introduire des conflits de réduction-réduction qui ne figurent pas dans l’analyseur LR (1), mais il ne peut pas introduire de conflits de réduction de décalage.
Dans votre exemple, l'état suivant LALR (1) provoque un conflit de réduction de décalage dans une implémentation de reflex:
S → b d•a / $
A → d• / c
Le symbole après /
correspond à l'ensemble suivant pour chaque production dans l'analyseur LALR (1). Dans SLR, follow (A
) inclut a
, qui pourrait également être décalé.
La différence fondamentale entre les tables d’analyseur générées avec SLR vs LR est que les actions de réduction sont basées sur l’ensemble des suivis défini pour les tables SLR. Cela peut être trop restrictif, ce qui finit par provoquer un conflit - réduire les conflits.
Par contre, un analyseur syntaxique LR réduit les décisions uniquement sur l’ensemble des terminaux qui peuvent réellement suivre la réduction du terminal non terminal. Cet ensemble de terminaux est souvent un sous-ensemble approprié de l'ensemble Suivant d'un tel non-terminal, et présente donc moins de risques de conflit avec les actions de décalage.
Les analyseurs syntaxiques LR sont plus puissants pour cette raison. Les tables d'analyse syntaxique LR peuvent toutefois être extrêmement volumineuses.
Un analyseur LALR commence par l’idée de construire une table d’analyse LR, mais combine les états générés de manière à réduire considérablement la taille de la table. L'inconvénient est que peu de risques de conflits seraient introduits pour certaines grammaires qu'une table LR aurait autrement évitées.
Les analyseurs syntaxiques LALR sont légèrement moins puissants que les analyseurs syntaxiques LR, mais ils sont toujours plus puissants que les analyseurs syntaxiques SLR. YACC et d'autres générateurs d'analyseurs de ce type ont tendance à utiliser LALR pour cette raison.
P.S. Par souci de brièveté, SLR, LALR et LR ci-dessus signifient vraiment SLR (1), LALR (1) et LR (1).
Une réponse simple est que toutes les grammaires LR (1) sont des grammaires LALR (1) . Comparé à LALR (1), LR (1) a plus d’états dans la machine à états finis associée (plus du double des états). Et c’est la principale raison pour laquelle les grammaires LALR (1) nécessitent plus de code pour détecter les erreurs de syntaxe que les grammaires LR (1) ..__ Et une chose plus importante à savoir concernant ces deux grammaires est que nous pourrions en avoir dans LR (1) moins réduire/réduire les conflits. Mais dans LALR (1), il y a plus de possibilité de réduire/réduire les conflits.