Analyse Syntaxique ascendante LR Parsing Techniques 11 Sujets • Préfixes viables. • Éléments LR(0). • AFD LR(0) pour reconnaître les préfixes viables. • Analyseur LR(0). 17/05/2016 2 Objectifs • Pouvoir calculer l’AFD qui reconnait les préfixes viables. • Pouvoir générer une table d’analyse LR(0). • Pouvoir décrire le driver LR(0) et le simuler. 17/05/2016 3 Introduction(1) ! Analyseurs descendants ! ! ! 4 commence la construction de l'arbre d'analyse à partir du la racine de l'arbre et se déplacer vers le bas (vers les feuilles). Facile à mettre en œuvre à la main, mais travailler avec les grammaires restreintes. exemple: analyseur prédictifs LL(1) Introduction(2) ! Analyseurs ascendants (Bottom-up) ! ! On cherche à construire un arbre de dérivation à partir des feuilles selon un parcours inverse d’un parcours en profondeur d’abord de gauche a droite. Les étapes de réduction tracent une dérivation à droite à l'envers. Suitable pour un générateur d'analyseur syntaxique automatique Peut gérer une grande classe de grammaires. ! exemples: shift-reduce parser (or LR (k) parsers) ! ! Grammaire S → aABe A → Abc | b B →d 5 input string : abbcde. 10/5/20 Introduction(3) Bottom-Up Parser Example Shift a INPUT: Production S → aABe A → Abc A→b B→d 6 a b b c d e $ Bottom-Up Parsing Program OUTPUT: Introduction(4) Bottom-Up Parser Example Shift b Reduce from b to A INPUT: Production S → aABe A → Abc A→b B→d 7 a b b c d e $ Bottom-Up Parsing Program OUTPUT: A b Introduction(5) Bottom-Up Parser Example Shift A INPUT: Production S → aABe A → Abc A→b B→d 8 a A b c d e $ Bottom-Up Parsing Program OUTPUT: A b Introduction(6) Bottom-Up Parser Example Shift b INPUT: Production S → aABe A → Abc A→b B→d 9 a A b c d e $ Bottom-Up Parsing Program OUTPUT: A b Introduction(7) Bottom-Up Parser Example Shift c Reduce from Abc to A INPUT: Production S → aABe A → Abc A→b B→d 10 a A b c d e $ Bottom-Up Parsing Program OUTPUT: A A b b c Introduction(8) Bottom-Up Parser Example Shift A INPUT: Production S → aABe A → Abc A→b B→d 11 a A d e $ Bottom-Up Parsing Program OUTPUT: A A b b c Introduction(9) Bottom-Up Parser Example Shift d Reduce from d to B INPUT: Production S → aABe A → Abc A→b B→d 12 a A d e $ Bottom-Up Parsing Program OUTPUT: A b A B b c d Introduction(10) Bottom-Up Parser Example Shift B INPUT: Production S → aABe A → Abc A→b B→d 13 a A B e $ Bottom-Up Parsing Program OUTPUT: A b A B b c d Introduction(11) Bottom-Up Parser Example Shift e Reduce from aABe to S INPUT: a A B e $ OUTPUT: S Production S → aABe A → Abc A→b B→d 14 Bottom-Up Parsing Program a A B A b c d b e Introduction(12) Bottom-Up Parser Example Shift S Hit the target $ INPUT: S $ OUTPUT: S Production S → aABe A → Abc A→b B→d Bottom-Up Parsing Program a A B A b c d e b Cet analyseur est connu comme un analyseur LR parce il balaye l'entrée de gauche à droite, et il construit une dérivation plus à droite dans l'ordre inverse. 15 Introduction(13) ! Conclusion ! Le balayage des productions pour faire correspondre avec des poignées dans la chaîne d'entrée ! Le Backtracking rend la procédure utilisé dans l'exemple précédent très inefficace. Pouvons-nous faire mieux? Discuter plus tard !!! Architecture précedente 16 Renouveler l'Architecture Shift-Reduce Parsers(1) ! Shift-Reduce (bottom-up) parser is known as an LR Parser ! Il scanne l'entrée de gauche à droite ! Dérivation droite dans l'ordre inverse ! Types d'analyseurs LR ! LR(k) : Le plus puissant analyseur ascendants déterministes en utilisant k symboles d'anticipations ! SLR(k) ! LALR(k) - Dérivation droite dans l'ordre inverse - Le mécanisme pour effectuer l'analyse ascendant est la machine à états finis en manipulant des "poignées" ! Composants ! Parse stack ! Shift-reduce driver ! Action table ! Goto table 17 Shift-Reduce Parsers(2) ! Parse stack ! ! ! 18 Iinitialement vide, contient des symboles déjà analysés Les éléments de la pile sont des symboles terminaux ou non terminaux La pile d'analyse enchaîné avec l'entrée restante représente toujours une bonne forme sententielles Shift-Reduce Parsers(3) ! Shift-Reduce driver ! Shift -- Quand le sommet de la pile ne contient pas une poignée de la forme sententielles ! ! Push le jeton d'entrée (avec informations contextuelles) dans la pile Reduce -- Quand le sommet de la pile contient une poignée pop la poignée ! 19 push le non-terminal réduite (avec des informations contextuelles) Rappel : Algorithme LRDriver Algorithm LRDriver variables : stack, handle (sous-chaîne au sommet de la pile), a (token), in (entrée) initialement la pile est vide ($) et l’entrée est w$ (une chaîne w). while (true) { if (symbol on top of stack is S’ ) return (a = = $); //accepte handle = stack.findHandle(); //trouver une poignée. Étape cruciale ! if handle != void { // void si la poignée n’est pas trouvée soit ‘A → handle’ la règle correspondante // reduce (nondeterminisme si plusieurs règles) pop handle from the stack; push A on the stack; print out the production ‘A → handle’; // pour imprimer la séquence de dérivation } else { a = in.nextToken(); // shift if a = $ exit with error(); // erreur si ni reduce ni shift ne sont possibles push a on the stack; continue; } } 17/05/2016 20 Exemple Grammaire augmentée Grammaire G = (V, A, R, E), avec V= {E, F, T} A = {(, ), +, *, num} R= { E E T T F F 17/05/2016 → E+T → T → T*F → F → ( E) → num} G = (V, A, R, E) : V= {E’, E, F, T} A = {(, ), +, *, num} R= { E’ → Ε E E T T F F → E+T → T → T*F → F → ( E) → num} 21 Exemple, suite Productions 1. E’ → Ε 2. E → E + T 3. E → T 4. T → T * F 5. T → F 6. F → ( E) 7. F → num Simulation du driver LR concaténé avec les tokens 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. restant à lire (w) est une Dérivation correspondante : Observation Le contenu de la pile (γ ) forme sententielle pour une dérivation la plus à droite : ($, ($(, ($(num, ($(F, ($(T, ($(E, ($(E), ($F, ($T, ($E, ($E’, (num)$) num)$) )$) )$) )$) )$) $) $) $) $) $) Shift Shift Reduce Reduce Reduce Shift Reduce Reduce Reduce Reduce Accepte 7 5 3 6 5 3 1 (num) <= (num) <= (num) <= (F) <= (T) <= (E) <= (E) <= F <= T <= E <= E’ S => * γω R 17/05/2016 22 Le problème de trouver la poignée − Pour une version déterministe, le défi est de trouver la poignée (méthode ‘stack.findHandle()’). − Pour certaines classes de GHC, on peut trouver la poignée sans examiner tout le contenu la pile. Cela découle de la propriété suivante sur les automates à pile LR : ➢ S’il est possible de reconnaître une poignée seulement à partir du contenu de la pile, alors il existe un AFD, qui en lisant les symboles de la grammaire à partir du sommet de la pile, détermine quel poignée, s’il y en a, est au sommet. − On va démontrer cette propriété en montrant comment construire l’AFD. − Ensuite on va voir comment utiliser l’AFD pour implémenter stack.findHandle(). − Ceci nous conduira à deux techniques d’analyse LR (SLR(1) et LALR(1)), résultant d’une combinaison ingénieuse de l’automate à pile LR et de l’AFD pour reconnaître les poignées. 17/05/2016 23 Que doit être l’alphabet de l’AFD pour reconnaître les poignées ? − Pour répondre à cette question, il faut garder à l’esprit que l’objectif est d’avoir un AFD qui lit le contenu de la pile (à partir du sommet) pour indiquer s’il y a une poignée au sommet de la pile. − Étant donné que la pile contient des symboles de la grammaire (terminaux et non-terminaux), l’alphabet de l’AFD doit être les symboles de la grammaire. − En d’autre mots, les transitions de l’AFD seront étiquetées par les symboles de la grammaires (terminaux et non terminaux). − La question qui reste est de savoir quels devraient être les états de l’AFD et quelles devraient être les transitions entre ces états. 17/05/2016 24 Préfixes viables − Pour répondre à la question précédente, rappelons d’abord que le contenu de la pile (γ) concaténé avec le reste des tokens (α) est une forme sententielle pour la dérivation la plus à droite. − Par conséquent, le contenu de la pile est toujours un préfixe d’une forme sententielle pour la dérivation la plus à droite. − De tels préfixes de formes sententielles de dérivation à droite, pouvant apparaître sur la pile, sont appelées des préfixes viables. − On va voir comment construire un AFD pour reconnaître des préfixes viables. − Ensuite on va expliquer comment l’utiliser pour reconnaître des poignées. 17/05/2016 25 AFN pour reconnaître les préfixes viables − Les transitions de l’AFN vont être étiquetées par des symboles dont la séquence forme un préfixe viable. − En d’autres mots, on aura les transitions préfixe viable. X Y Z si XYZ est un − Un état de l’AFN sera une production de la grammaire avec un point (« . ») à une position particulière dans la partie droite. − Ainsi, à partir de la production A → XYZ, on va générer 4 états de l’AFN : A → . XYZ • A → X . YZ • A → XY . Z • A → XYZ . • − Pour la production A → ε, on va générer un seul état : A → . 17/05/2016 26 Que signifient les états de l’AFN? − Intuitivement, un état de l’AFN indique la portion de la partie droite d’une production, reconnue au sommet de la pile, à un moment donnée durant l’analyse syntaxique LR. ▪ Par exemple l’état A → . XYZ indique qu’aucune portion n’est encore reconnue sur la pile, et on espère que le reste des tokens à lire commence par un préfixe dérivable de XYZ. ▪ L’état A → X.YZ indique qu’on vient juste de lire une sous-chaîne de tokens dérivable de X (c.-à-d., le préfixe viable X est au sommet de la pile) et on s’attend à lire ensuite une suite de tokens formant un sous-chaîne dérivable de YZ. − Un état de l’AFN est appelé un élement LR(0) (item LR(0) en anglais; ou élément tout court) de la grammaire. ▪ On peut implémenter un élément LR(0) par une paire d’entiers, le premier indiquant le numéro de la production, le deuxième indiquant la position du point. − On peut maintenant expliquer la technique pour obtenir l’AFN de la grammaire. 17/05/2016 27 AFN pour reconnaître les préfixes viables 1. L’état initial est S’ → . S 2. Pour chaque paire d’états A → α . X β et A → αX . β, ajouter une transition du premier état vers le deuxième, étiquetée par X. Il faut garder à l’esprit que A → α . Xβ signifie : à un moment donné durant l’analyse syntaxique, on vient juste de scanner un sous-chaîne dérivable de α et on s’attend à scanner une sous-chaîne dérivable de X, ensuite une sous-chaîne dérivable de β. Ainsi, la transition de A → α . X β à A → αX . β, étiquetée X, signifie qu’on vient juste de lire une sous-chaîne dérivable de X et on s’attend maintenant à en lire une dérivable de β. 3. Pour chaque paire d’états A → α . X β et X → . γ, ajouter une transition du premier vers le deuxième, étiquetée ε. Intuitivement, si on s’attend à scanner une sous-chaîne dérivable de X et si X peut dériver γ, on peut s’attendre aussi bien à scanner une chaîne dérivable de γ. 17/05/2016 28 Éléments canoniques (de base) et l’opération de fermeture (closure) 1. 2. L’état initial est S’ → . S Pour chaque paire d’états A → α . X β et A → αX . β, ajouter une transition du premier état vers le deuxième, étiquetée par X. Pour chaque paire d’états A → α . X β et X → .γ, ajouter une transition du premier vers le deuxième, étiquetée ε. 3. Les éléments impliqués dans les deux premières opérations sont appelés des éléments canoniques (c-à-d., des éléments du noyau, ou des éléments de base ou kernels en anglais). − ▪ Il s’agit de l’élément S’ → . S et des éléments dont le point ne commence pas la partie droite de la production. − Les éléments dont le point apparaît au début de la partie droite de la production sont dits non canoniques. − La troisième opération est une fermeture ε. 17/05/2016 29 Exemple 1/3 Grammaire Grammaire augmentée G = (V, A, R, E) : V= {E, F, T} A = {(, ), +, *, num} R= { G = (V, A, R, E) : V= {E’, E, F, T} A = {(, ), +, *, num} R= { } E E T T F F → E+T → T → T*F → F → ( E) → num E’ → Ε } 17/05/2016 E E T T F F → E+T → T → T*F → F → ( E) → num 30 Exemple 2/3 AFN pour les préfixes viables ε Grammaire E’→ Ε E→ E+T E→ Τ E’→.Ε ε E→.E+T Ε E E’→Ε. E→E.+T E→.Τ + T→F. E→Τ. F ε E→E+.T T→T*F ε T→.F ε T→ F F→ (E) T E→E+T. T→.T*F F→(E.) T→T*.F T→T.*F F→num T→T*F. ε F→ .num F→.(E) F→num. F→(.E) F→(E). Seulement quelques transitions sont présentes ! Compléter les autres comme exercice. 17/05/2016 31 Exemple 3/3 E’ Grammaire E’→ Ε E→ E+T E→ Τ T→T*F T→ F F→ (E) F→num 1. E’→ .Ε 2. E’→ Ε. 3. E→ .E+T 4. E→ E.+T 5. E→ E+.T 6. E→ E+T. 7. E→ .Τ 8. E→ Τ. 9. T→.T*F 10. T→T.*F 11. T→T*.F E 2 T F ( ) + * num ε 3,7 Même AFN, avec table de transition. Seulement deux entrées complétées. Les autres sont laissées comme exercice. 12. T→T*F. 13. T→ .F 14. T→ F. 15. F→ .(E) 16. F→ (E) 17. F→ (E) 18. F→num 19. F→num 17/05/2016 32 AFD pour reconnaître les préfixes viables − On obtient l’AFD pour les préfixes viables en déterminisant l’AFN (par la méthode subset construction). I0 E’→ .Ε E→ .E+T E→ .Τ T→.T*F T→ .F F→ .(E) F→.num I1 E’→ Ε. E→ E.+T I2 E→ Τ. T→T.*F I3 T→ F. I4 F→ (.E) E→ .E+T E→ .Τ T→.T*F T→ .F F→ .(E) F→ .num I7 T→T*.F F→ .(E) F→.num I5 F→ num. I9 E→ E+T. T→T.*F I6 E→ E+.T T→ .T*F T→ .F F→ .(E) F→.num E I0 + I1 T I6 I9 F ( I8 F→ (E.) E→ E.+T F * I2 to I4 to I5 F I7 I10 ( I3 ( I0 T→T*F. I11 F→ (E). num E ) I8 T num I5 to I4 to I5 ( I4 num to I7 to I3 num T * F I11 + to I6 to I2 to I3 17/05/2016 33 AFD pour reconnaître les préfixes viables − L’AFD avec une table de transition. I0 E’→ .Ε E→ .E+T E→ .Τ T→.T*F T→ .F F→ .(E) F→.num I1 E’→ Ε. E→ E.+T I2 E→ Τ. T→T.*F I3 T→ F. I4 F→ (.E) E→ .E+T E→ .Τ T→.T*F T→ .F F→ .(E) F→ .num I7 T→T*.F F→ .(E) F→.num I5 F→ num. I9 E→ E+T. T→T.*F I6 E→ E+.T T→ .T*F T→ .F F→ .(E) F→.num I8 F→ (E.) E→ E.+T E’ I0 I1 E T F ( I1 I2 I3 I4 ) + * num I5 I6 I2 I7 I3 I4 I5 I0 T→T*F. I6 I11 F→ (E). I8 I7 I9 I8 I2 I9 I3 I4 I5 I3 I4 I5 I10 I4 I5 I11 I6 I7 I10 I1 17/05/2016 34 AFD LR(0) pour reconnaître les préfixes viables − Les états de l’AFD sont donc des ensembles d’états de l’AFN, c’est-à-dire, des ensembles d’éléments LR(0). − La fonction de transition de l’AFD est appelée la fonction goto de l’analyseur syntaxique: Étant donné un état I de l’AFD et un symbole de grammaire X, goto(I,X) est le successeur de I pour la transition étiqueté par X. 17/05/2016 35 Méthode efficace de construction de l’AFD pour les préfixes viables − Une méthode efficace de construction de l’AFD pour les préfixes viables est d’appliquer la méthode de déterminisation (subset construction method), à la volée, sans construire l’AFN au préalable. − Pour ce faire, nous avons d’abord besoin de définir deux fonctions auxiliaires: closer(I) et goto(I,X). − closure(I) implémente la fermeture ε, sous-jacente à méthode déterminisation, en éliminant notamment les transitions ε. − goto(I,X) implémente les transitions entre les éléments canoniques. 17/05/2016 36 Définition informelle de closure − Soit I un ensemble d’éléments LR(0) d’une grammaire. − closure(I) est l’ensemble obtenu de I en appliquant les opérations suivantes : • Initialement, chaque élément de I est mis dans closure(I). • Ensuite on applique l’opération suivante, jusqu’à ce qu’aucun nouvel élément ne peut plus être ajouté : Si A → α . Xβ est dans closure(I) et X → γ est une production, ajouter l’élément X → .γ à closure(I). 17/05/2016 37 Définition formelle de closure(I) Algorithm closure(I) variables : I (ensemble d’éléments LR(0) : donnée d’entrée) G (grammaire : donnée comme variable globale) J (ensemble d’éléments LR(0): contient le résultat) J = I; // initialisation do { for (each item A → α . Xβ in J and each production X → γ of G such that X → .γ is not in J) add X → .γ to J } while (J is modified) return J; 17/05/2016 38 Fonction goto − Soit − I un état de l’AFD (c.-à-d., un ensemble d’items LR(0)) et − X un symbole de la grammaire. − Notons I’ l’ensemble formé de tous les éléments A → αX . β pour lesquels il existe un élément A → α . Xβ dans I. − goto(I,X) = closure(I’). − En d’autre mots, goto(I,X) est défini comme étant la fermeture (closure) de l’ensemble formé de tous les éléments A → αX . β pour lesquels il existe un élément A → α . Xβ dans I. − Rappelons que les états de l’AFD sont aussi appelés des éléments (items) canoniques LR(0). − Comme on connaît la fonction goto, pour définir l’AFD il suffit d’itérer avec la fonction goto à partir de l’état initial closure({S’→ . S}). 17/05/2016 39 Algorithme pour les états de l’AFD LR(0) Algorithm Items(G) variables : G (entrée : grammaire augmentée avec symbole de départ S) et C (résultat : ensembles des états de l’AFD.) Output : C et la fonction goto. C = { closure({S’→ . S}) }; // état initial do { for (each state I in C and each grammar symbol X such that goto(I,X) is not empty and not in C) add goto(I,X) to C } while (C is modified) return C; 17/05/2016 40 Éléments valides − Un élément A → β1 . β2 est dit valide pour un préfixe viable α β1 s’il existe une dérivation la plus à droite telle que : S’ =>* αΑw =>*α β1 β2 w . R R − On peut démontrer que l’ensemble des éléments valides pour un préfixe viable γ est précisément l’ensemble des éléments atteignables à partir de l’état initial de l’AFD des préfixes viables, en suivant un chemin étiqueté par γ. − La validité de A → β1 . β2 pour un préfixe α β1 nous indique s’il faut faire shift ou alors reduce dès que α β1 est au sommet de la pile. − En d’autre mots, ça nous indique si αβ1 est une poignée ou non. 17/05/2016 41 Quand faire shift, quand faire reduce ? − Supposons que α β1 est au sommet de la pile et que l’élément A → β1 . β2 est valide pour α β1. Alors : • Si β2 = ε , cela veut dire qu’au sommet de la pile nous avons la partie droite de la production A → β1. C.-à-d., β1 est une poignée, on peut donc réduire. • Sinon, si β2 ≠ε , cela veut dire qu’on doit faire shift des tokens de la sous-chaîne β2 avant d’obtenir β1 β2 . C-à-d., on n’a pas encore une poignée sur la pile, on doit continuer de faire shift. − De ces observations, on déduit que si on simule l’AFD pour les préfixes viables en parallèle avec le driver LR (automate à pile LR), on sera capable de déterminer quand faire shift et quand faire reduce. 17/05/2016 42 Idée de base − L’idée de base pour simuler en parallèle l’automate à pile LR et l’AFD pour les préfixes viables est la suivante. − Initialement, l’automate à pile est vide et l’AFD est dans son état initial. − Chaque fois qu’on shift un token sur la pile, l’AFD change son état en suivant la transition étiquetée par le token. − Ainsi, lorsque le sommet de la pile va contenir β1 l’AFD sera dans un état contenant l’élément A → β1 . , ce qui indique que nous devons faire une réduction avec la production A → β1. − En même temps que nous appliquons la réduction (en remplaçant β1 par A sur la pile), on doit revenir en arrière dans l’AFD (backtracking) le long du chemin étiqueté β1 jusqu’à l’état I qui est la racine du chemin étiqueté β1 . − Puisque A est le nouveau symbole au sommet de la pile, on doit faire une transition correspondante dans l’AFD; c-à-d., l’état courant de l’AFD doit devenir l’état retourné par goto(I,A) (c.-à-d., le successeur de I sous la transition A dans l’AFD). 17/05/2016 43 De l’idée à l’implémentation − Pour implémenter cette idée, on met les états de l’AFD sur la pile de sorte à garder une trace de l’exécution de l’AFD. − Plus précisément, sur la pile on va alterner un état de l’AFD avec un symbole de la grammaire (terminal ou non-terminal), en commençant avec l’état initial de l’AFD. − Donc une configuration du driver LR est dorénavant de la forme (I0X1I1 … XmIm , ti … tn$), tel que Ij sont des états de l’AFD, Xj des symboles de la grammaire, et ti … tn le reste des tokens à lire. − Nous n’avons plus besoin de mettre le $ pour reconnaître le fond de la pile. − L’état initial de l’AFD va remplir ce rôle. 17/05/2016 44 De l’idée à l’implémentation − Une configuration (I0X1I1 … XmIm, ti … tn$) signifie que: − l’AFD est actuellement dans l’état Im , − le long d’un chemin (trace) étiqueté X1 … Xm à partir de l’état initial I0, − et le reste de l’entrée à scanner est ti… tn. − Ainsi, si Xj … Xm, est une poignée, pour un j quelconque : − Il doit y avoir un élément « A→ Xj … Xm . » dans l’état Im. − Dans ce cas, on fait une réduction en remplaçant Xj … Xm par A sur la pile. − Plus précisément, on enlève Xj Ij … XmIm de la pile, et on ajoute AI’ tel que I’ est l’état retourné par goto(Ij-1, A). − La suppression de XjIj … XmIm le long du chemin Xj … Xm. 17/05/2016 de la pile revient à un retour arrière (backtracking) dans l’AFD, 45 Analyseur LR(0) (1/2) − Un analyseur (syntaxique) LR(0) n’a pas besoin d’anticiper le prochain token (lookahead) pour prendre une décision shift ou reduce. − Il décide de l’action appropriée (shift ou reduce) seulement en fonction de l’état courant de l’AFD pour les préfixes viables. − Ce comportement est reflété par la version suivante de l’analyseur LR(0). Algorithm LR0Parser Input: stream of tokens Output: a derivation sequence Variables: stack (pile), a (prochain token), in (entrée : flux de tokens) AFD pour préfixes viables, avec état initial I0 et fonction de transition goto Initialement la pile contient I0 17/05/2016 46 Analyseur LR(0) (2/2) while (true) { if (la pile contient I0S’ et l’entrée est $ (vide)) return true; if (l’état au sommet de la pile contient un élément A → α . ) { reduce with the production A → α; // α est une poignée et A → α la production correspondante c.-à-d. : Supprimer 2⋅|α| symboles de la pile (|α| états et |α| symboles de grammaires). Soit I le nouvel état au sommet de la pile. Ajouter A au sommet de la pile. Ajouter l’état retourné par goto(I,A) au sommet de la pile. print out the production A → α; continue; } if (l’état au sommet de la pile contient un élément A → α . aβ tel que a est un token) { shift; c.-à-d. : Soit I l’état au sommet de la pile. Lire le prochain token a. Ajouter a au sommet de la pile. Ajouter l’état retourné par goto(I,a) au sommet de la pile. continue; } error(); } 17/05/2016 47 Exemple I0 E’→ .Ε E→ .E+T E→ .Τ T→.T*F T→ .F F→ .(E) F→.num I1 E’→ Ε. E→ E.+T I2 E→ Τ. T→ T.*F I3 T→ F. I4 F→ (.E) E→ .E+T E→ .Τ T→.T*F T→ .F F→ .(E) F→ .num I7 T→T*.F F→ .(E) F→.num I5 F→ num. I9 E→ E+T. T→T.*F I6 E→ E+.T T→ .T*F T→ .F F→ .(E) F→.num E I0 T I6 I9 F ( F * I2 I10 ( I3 ( I0 T→T*F. num E ) I8 T num I5 to I4 to I5 ( I4 num to I7 to I4 to I5 F I7 * to I3 num T I8 F→ (E.) E→ E.+T I11 F→ (E). + I1 F I11 + to I6 to I2 to I3 17/05/2016 48 Exemple 1 E I0 + I1 T I6 I9 F ( F * I2 to I4 to I5 F I7 I10 ( I3 ( num num E T num I5 ) I8 F Trace 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. (I0, (I0(I4, (I0(I4numI5, (I0(I4FI3, (I0(I4TI2, (I0(I4EI8, (I0(I4EI8)I11, (I0FI3, (I0TI2, (I0EI1, (I0E’, (num)$) num)$) )$) )$) )$) )$) $) $) $) $) $) Shift T→ .(E) Shift T→ .num Reduce F→ num. Reduce T→ F. Reduce E→ T. Shift F→ (E.) Reduce F→ (E). Reduce T→ F. Reduce E→ T. Reduce E’→ E. Accept I11 + to I2 to I3 17/05/2016 to I4 to I5 ( I4 to I7 to I3 num T * to I6 Dérivation correspondante : (num) <= (num) <= (num) <= (F) <= (T) <= (E) <= (E) <= F <= T <= E <= E’ 49 Exemple 2 Même entrée, mais à l’étape 9, on résoud le conflit en faveur de Shift E I0 + I1 T I6 I9 F ( F * I2 to I4 to I5 F I7 I10 ( I3 ( num E ) I8 T num I5 to I4 Trace 1. 2. 3. 4. 5. 6. (I0, (I0(I4, (I0(I4numI5, (I0(I4FI3, (I0(I4TI2, (I0(I4TI2), (num)$) num)$) )$) )$) )$) $) Shift T→ .(E) Shift T→ .num Reduce F→ num. Reduce T→ F. Shift T→ T.*F Error goto(I2, ‘)’) non défini. to I5 ( I4 num to I7 to I3 num T * F I11 + to I6 to I2 to I3 17/05/2016 50 Exemple 3 Une entrée différente E I0 + I1 T I6 I9 F ( F * I2 to I4 to I5 F I7 I10 ( I3 ( num num ( I4 E T num I5 ) I8 F to I7 to I3 num T * Trace : 1. 2. 3. 4. 5. (I0, (I0(I4, (I0(I4numI5, (I0(I4FI3, (I0(I4TI2, (num*num)$) num*num)$) *num)$) *num)$) *num)$) Shift T→ .(E) Shift T→ .num Reduce F→num. Reduce T→ F. … to I4 Poursuivre comme exercice … to I5 Remarquer le conflit Shift/Reduce à l’étape 5. L’état I2 contient élément Shift (T → T.*F) et un élément Reduce (E → T.). I11 + to I2 to I6 Contrairement à l’exemple précédent, cette fois-ci le conflit devrait être réglé en faveur de Shift. Pourquoi? to I3 17/05/2016 51 Analyseurs SRL(1) 17/05/2016 52 Sujets • Algorithme générique pour l’analyse LR (Driver LR) • Table d’analyse SLR(1). 17/05/2016 53 Objectifs • Pouvoir définir et simuler l’algorithme d’analyse LR (Driver LR) • Pouvoir générer une table d’analyse SLR(1) pour une grammaire donnée. • Pouvoir reconnaître les grammaires SLR(1). 17/05/2016 54 Rappel : AFD LR(0) I0 E’→ .Ε E→ .E+T E→ .Τ T→.T*F T→ .F F→ .(E) F→.num I1 E’→ Ε. E→ E.+T I2 E→ Τ. T→T.*F I3 T→ F. I4 F→ (.E) E→ .E+T E→ .Τ T→.T*F T→ .F F→ .(E) F→ .num I5 F→ num. I6 E→ E+.T T→ .T*F T→ .F F→ .(E) F→.num I7 T→T*.F F→ .(E) F→.num E I0 T I6 I9 F ( F * I2 I10 ( I9 E→ E+T. T→T.*F I3 ( I0 T→T*F. num E ) I8 T num I5 to I4 to I5 ( I4 num to I7 to I4 to I5 F I7 * to I3 num T I8 F→ (E.) E→ E.+T I11 F→ (E). + I1 F I11 + to I6 to I2 to I3 17/05/2016 55 Rappel : Analyseur LR(0) − Un analyseur (syntaxique) LR(0) n’a pas besoin d’anticiper le prochain token (lookahead) pour prendre une décision shift ou reduce. − Il décide de l’action appropriée (shift ou reduce) seulement en fonction de l’état courant de l’AFD pour les préfixes viables. − Ce comportement est reflété par la version suivante de l’analyseur LR(0). Algorithm LR0Parser. Entrée : flux de tokens Sortie : dérivation de l’entrée ou erreur. Variables: stack (pile), a (prochain token), in (entrée : flux de tokens) AFD pour préfixes viables, avec état initial I0 et fonction de transition goto Initialement la pile contient I0 17/05/2016 56 Rappel : Analyseur LR(0) while (true) { if (la pile contient I0S’ et l’entrée est $ (vide)) return true; if (l’état au sommet de la pile contient un élément A → α . ) { reduce with the production A → α; // α est une poignée et A → α la production correspondante c.-à-d. : Supprimer 2⋅|α| symboles de la pile (|α| états et |α| symboles de grammaires). Soit I le nouvel état au sommet de la pile. Ajouter A au sommet de la pile. Ajouter l’état retourné par goto(I,A) au sommet de la pile. print out the production A → α; continue; } if (l’état au sommet de la pile contient un élément A → α . aβ tel que a est un token) { shift; c.-à-d. : Soit I l’état au sommet de la pile. Lire le prochain token a. Ajouter a au sommet de la pile. Ajouter l’état retourné par goto(I,a) au sommet de la pile. continue; } error(); } 17/05/2016 57 Rappel : Grammaires LR(0) − Un élément shift (shift item) est un élément de la forme « A → α . aβ »€ tel que a est un terminal. − Un élément reduce (reduce item) est un élément de la forme « A → α . » − Si l’AFD LR(0) a un état contenant à la fois un élément shift et un élément reduce, ou plus d’un élément reduce, alors la grammaire correspondante n’est pas LR(0). Par exemple, la grammaire précédente n’est pas LR(0) parce que les états I1, I2 et I9 contiennent des conflits Shift/Reduce. − Un analyseur LR(0) fonctionne correctement uniquement pour des grammaires LR(0). 17/05/2016 58 Exemple de grammaire LR(0) S’→ S 1. S → E; 2. E → E + T 3. E → T 4. T → (E) 5. T → num Terminaux : num, ‘+’ et‘;’ (habituellement non numérotée; production interne) − La terminaison de l’entrée ‘;’ fait partie des terminaux. Sans elle, la grammaire n’est plus LR(0). − Pour montrer que la grammaire est LR(0), on construit l’AFD pour les préfixes viables et on vérifie qu’aucun état ne contient à la fois d’éléments shift et d’élément reduce, ou plus d’un élément reduce. − Ci-après on donne aussi une simulation de l’analyseur LR(0) pour l’entrée ‘3 + 6;’, ce qui correspond au flot de tokens ‘num + num;’ 17/05/2016 59 AFD LR(0) I0 S’→ .S S→ .Ε; E→ .E+T E→ .Τ T→ .(E) T→ .num I1 S→ Ε .; E→ E .+T I2 S→ E;. 17/05/2016 I3 E→ E+.T T→ .(E) T→ .num I4 E→ E+T. I7 T→ (E .) E→ E .+T I9 num E I5 S I10 num I8 F→ (E). I5 T→ num. I6 T→ (.E) E→ .E+T E→ .Τ T→ .(E) T→ .num T I0 I1 ; I3 T I2 I9 E→T. + ( num ( + T ( I6 E I7 ) I4 I8 I10 S’→ S. 60 AFD + simulation de l’analyseur LR(0) avec une entrée correcte I0 S’→ . S S→ .Ε; E→ .E+T E→ .Τ T→ .(E) T→ .num I1 S→ Ε .; E→ E .+T I6 T→ (.E) E→ .E+T E→ .Τ T→ .(E) T→ .num I2 S→ E;. I7 T→ (E .) E→ E .+T I3 E→ E+.T T→ .(E) T→ .num I4 E→ E+T. 17/05/2016 T I5 T→ num. I8 F→ (E). I9 E→T. I9 I0 num E I5 S I10 num I1 ; + I3 T I2 ( num ( + T ( I6 E 1. (I0, num+num;$) 2. (I0numI5, +num;$) 3. (I0TI9, +num;$) 4. (I0EI1, +num;$) 5. (I0EI1+I3, num;$) 6. (I0EI1+I3numI5, ;$) 7. (I0EI1+I3TI4, ;$) 8. (I0EI1, ;$) 9. (I0EI1;I2, $) 10. (I0SI10, $) Shift Reduce Reduce Shift Shift Reduce Reduce Shift Reduce Accept. I7 ) I4 I8 Dérivation correspondante : num + num; <= num+ num; <= T + num; <= E + num <= E + num; <= E + num; <= E + T; <= E; <= E; <= E’ I10 S’→ S. 61 AFD + simulation de l’analyseur LR(0) avec une entrée erronée I0 S’→ . S S→ .Ε; E→ .E+T E→ .Τ T→ .(E) T→ .num I5 T→ num. I1 S→ Ε .; E→ E .+T I2 S→ E;. I7 T→ (E .) E→ E .+T I4 E→ E+T. 17/05/2016 I9 I0 num I6 T→ (.E) E→ .E+T E→ .Τ T→ .(E) T→ .num I3 E→ E+.T T→ .(E) T→ .num T I8 F→ (E). E I5 S I10 num I1 ; + I3 T I2 ( num ( + T ( I6 E 1. 2. 3. 4. 5. 6. (I0, num++num;$) Shift (I0numI5, ++num;$) Reduce (I0TI9, ++num;$) Reduce (I0EI1, ++num;$) Shift (I0EI1+I3, +num;$) Shift (I0EI1+I3+, ;$) Error during reduction : goto(I3,+) is not defined. I7 ) I4 I8 I9 E→T. I10 S’→ S. 62 Vers un pilote pour Analyseur LR (1) − L’exemple précédent montre que lorsqu’on a une entrée erronée : − L’analyseur LR(0) peut empiler (shift) le prochain token même s’il est erroné. − Mais il va se rendre compte que le token est erroné plus tard, durant la réduction. − En fait, en général, l’erreur peut être détectée après avoir empilé quelques tokens de plus au-delà du token fautif. − Nous allons voir une variante de l’analyseur LR(0) qui fait shift uniquement si le prochain token a un élément shift correspondant dans l’état courant de l’AFD. − Techniquement parlant, ceci n’est pas un analyseur LR(0), puisqu’il consulte le prochain token avant de décider de faire shift. C’est un anaylseur LR(1). − Néanmoins, cette version est équivalente à l’analyseur LR(0), mis à part le fait qu’elle permet de détecter des erreurs plus tôt. Avec l’exemple précédent, l’erreur serait détectée à l’étape 5 lorsqu’on essaie d’empiler (shift) le ‘+’. − Nous décrivons cette variante parce qu’elle permet une bonne transition vers l’analyseur LR(1) général qui nous intéresse le plus. 17/05/2016 63 Analyseur LR(1) équivalent à l’analyseur LR(0) while (true) { // Version 2 if (la pile contient I0S’ et l’entrée est $ (vide)) return true; if (l’état au sommet de la pile contient un élément A → α . ) { reduce with the production A → α; // α est une poignée et A → α la production correspondante c.-à-d. : Supprimer 2⋅|α| symboles de la pile (|α| états et |α| symboles de grammaires). Soit I le nouvel état au sommet de la pile. Ajouter A au sommet de la pile. Ajouter l’état retourné par goto(I,A) au sommet de la pile. print out the production A → α; continue; } if (l’état au sommet de la pile contient un élément A → α . aβ tel que a est le prochain token) { shift; c.-à-d. : Soit I l’état au sommet de la pile. Lire le prochain token a. Ajouter a au sommet de la pile. Ajouter l’état retourné par goto(I,a) au sommet de la pile. continue; } error(); } 17/05/2016 64 Exemple revisité avec le nouvel algorithme I0 S’→ . S S→ .Ε; E→ .E+T E→ .Τ T→ .(E) T→ .num I5 T→ num. I1 S→ Ε .; E→ E .+T I2 S→ E;. I7 T→ (E .) E→ E .+T I4 E→ E+T. 17/05/2016 I8 F→ (E). E I5 S I10 num I1 ; + I3 T I2 1. 2. 3. 4. 5. I9 I0 num I6 T→ (.E) E→ .E+T E→ .Τ T→ .(E) T→ .num I3 E→ E+.T T→ .(E) T→ .num T ( num ( + T ( I6 (I0, num++num;$) Shift (I0numI5, ++num;$) Reduce (I0TI9, ++num;$) Reduce (I0EI1, ++num;$) Shift (I0EI1+I3, +num;$) Error during reduction : cannot shift ‘+’ in state I3. E I7 ) I4 I8 I9 E→T. I10 S’→ S. 65 Tables d’analyse LR(0) 1/2 – On peut améliorer les deux ébauches d’analyseurs LR(0) précédents, en utilisant une table pour implémenter l’AFD pour les préfixes viables. – L’idée est que l’action shift aussi bien que l’action reduce dépend seulement de l’état de l’AFD au sommet de la pile. Tout ce dont on a besoin de savoir pour cet état c’est s’il contient : • (a) un élément de la forme A → α . , et dans ce cas on fait reduce; • (b) ou un élément de la forme A → α . aβ , et dans ce cas on fait shift; – On peut mettre ces information dans une table annexée avec la fonction de transition de l’AFD, goto. – De cette façon, on peut utiliser un algorithme d’analyse LR universel, piloté par la table d’analyse. 17/05/2016 66 Tables d’analyse LR(0) avec lookahead – Avec lookhead, la table est indexée par l’état de l’AFD et le prochain token. – L’entrée dans la table est un symbole désignant l’action (shift ou reduce) et/ou l’état successeur pour la fonction goto. – Nous utilisons les conventions suivantes pour la spécification des tables : • si signifie ‘Fait shift ensuite empile l’état i’ (c.-à-d., action shif et goto état i); • ri signifie ‘Fait reduce avec la production numéro i’; • acc signifie accepte l’entrée; • une entrée vide signifie que l’analyseur doit signaler une erreur. 17/05/2016 67 Construire la table d’analyse LR(0) avec lookahead 1/2 Algorithm construire la table d’analyse LR(0) avec lookahead Entrée : Une grammaire augmentée G, avec le symbole de départ S’; Sortie : Table d’analyse LR(0) avec lookahead; Méthode : 1. Construire l’AFD pour les préfixes viables de G; 2. Nous notons i, l’état Ii. Les actions d’analyse pour l’état i sont déterminées comme suit : a. Si un élément A → α . aβ, tel que a est un terminal, est dans Ii, avec goto(Ii , a) = j, ajoute shift j dans action[i, a]; b. Si un élément A → α . est dans Ii , avec A différent de S’, ajoute reduce A → α dans action[i, a], pour chaque terminal a; c. Si l’élément S’ → S . est dans Ii , ajoute ‘accept’ dans action[i, $]. 17/05/2016 68 Construire la table d’analyse LR(0) avec lookahead 2/2 3. Si goto(Ii, A) = Ij pour un non terminal A goto[i, A] devient j ; 4. Toutes les entrées vides sont considérées comme ayant la valeur “erreur”. 5. L’état initial, 0, est celui construit à partir de l’état de l’AFD contenant l’élément S’ → . S Si la table générée par cette algorithme contient des conflits, c.-à-d., des entrées avec actions multiples, la grammaire correspondante n’est pas LR(0). Sinon, elle est LR(0). 17/05/2016 69 Exemple 1/2 1 2 3 4 5 I0 S’→ . S S→ .Ε; E→ .E+T E→ .Τ T→ .(E) T→ .num I5 T→ num. I1 S→ Ε .; E→ E .+T I6 T→ (.E) E→ .E+T E→ .Τ T→ .(E) T→ .num I2 S→ E;. I7 T→ (E .) E→ E .+T I3 E→ E+.T T→ .(E) T→ .num I4 E→ E+T. 17/05/2016 T I9 I0 I8 T→ (E). num E I5 S I10 num I1 ; + I3 T I2 ( num ( + T ( I6 E I7 ) I4 I8 I9 E→T. I10 S’→ S. 70 Exemple 2/2 action/goto T I9 I0 num E I5 S I10 num I1 + ; I3 T I2 ( num ( + num 0 T ( I6 E I7 ) I4 I8 r1 3 s5 4 5 r2 r5 6 s5 ) r4 r3 ; $ s6 r1 S 10 E T 1 9 s2 r1 r1 r1 r1 s6 r2 r5 r2 r5 4 r2 r5 r2 r5 r2 r5 s6 s3 7 10 ( s3 State 2 9 17/05/2016 s5 1 8 + goto r4 r3 7 9 s8 r4 r3 r4 r3 r4 r3 r4 r3 acc 71 Algorithme d’analyse LR (1) 1/2 Algorithm LR1Parser Entrée : flux de tokens Sortie : dérivation de l’entrée si elle est dans L(G), sinon erreur. Variables : stack (pile), a (prochain token), in (entrée : flux de tokens) i (sommet de la pile) Table d’analyse LR(0) avec lookahead, ou SLR(1), ou LALR(1). Méthode : Initialement la pile contient l’état 0. Ensuite il exécute le code suivant. 17/05/2016 72 Algorithme d’analyse LR (1) 2/ 2 a = in.getNextToken(); while (true) { i = state on top of stack; if action[i,a] = shift j { push a on top of the stack; push j on top of the stack; a = get.NextToken(); continue; } if action[i,a] = reduce A→α { pop 2x|α| symbols off the stack; i = the state now on top of the stack; push A on top of stack; push goto[i,A] on top of the stack; printout the production A→α ; continue; } if action[i,a] = accept return true; error(); } 17/05/2016 73 Exemple (Simulation de l’analyseur LR(1)) action/goto num 0 s5 State 2 r1 3 s5 4 5 r2 r5 6 s5 S’→ S 1. S → Ε; 2. E → E+T 3. E → Τ 4. T → (E) 5. T → num 9 10 17/05/2016 ) r4 r3 ; $ s6 r1 S 10 E T 1 9 s2 r1 r1 r1 r1 s6 r2 r5 r2 r5 4 r2 r5 r2 r5 r2 r5 s6 s3 7 8 ( s3 1 Grammar + goto r4 r3 7 r4 r3 Shift Reduce 5 Reduce 3 Shift Shift Reduce 5 Reduce 2 Shift Reduce 1 Accept. 9 Corresponding derivation sequence: s8 r4 r3 1. (0, num+num;$) 2. (0 num 5, +num;$) 3. (0 T 9, +num;$) 4. (0 E 1, +num;$) 5. (0 E 1 + 3, num;$) 6. (0 E 1 + 3 num 5, ;$) 7. (0 E 1 + 3 T 4, ;$) 8. (0 E 1, ;$) 9. (0 E 1 ; 2, $) 10. (0 S 10, $) r4 r3 r4 r3 acc num + num; <= num+ num; <= T + num; <= E + num <= E + num; <= E + num; <= E + T; <= E; <= E; <= S 74 Forces et limites de l’analyse LR(0) – On peut montrer que si un langage hors-contexte a un symbole spécifique qui termine tous les mots du langage (comme le point virgule dans l’exemple précédent), alors il existe une grammaire LR(0) équivalente. – Remarquez que les langages de programmation sont en pratique des langages hors-contexte et terminent par une fin de fichier. Donc, théoriquement, on pourrait les analyser par un analyseur LR(0). – Cependant, la grammaire LR(0) en question pourrait être très large, difficile à lire et peu efficace. Mais il en existe une. – En pratique on préfère des parsers LR qui utilise un lookahead, parce qu’ils exigent des grammaires plus simples. 17/05/2016 75 Exemple de grammaire non LR(0) − Un grand nombre de grammaires intéressantes ne sont pas LR(0) même si elles ne sont pas ambiguës. − Par exemple, la grammaire suivante pour les expressions arithmétiques n’est pas LR(0): E’ → E 1. E → E+T 2. E → T 3. T → T*F 4. T →F 5. F → (E) 6. F→ num (non numéroté parce que c’est une production interne) − Pour montrer que la grammaire n’est pas LR(0), on montre que l’AFD correspondant pour les préfixes viables a au moins un état contenant à la fois un élément shift et un élément reduce ou plus d’un élément reduce. Conséquemment la table d’analyse correspondante aurait des entrées multiples. 17/05/2016 76 AFD pour préfixes viables I0 E’→ .Ε 1 E→ .E+T 2 E→ .Τ 3 T→.T*F 4 T→ .F 5 F→ .(E) 6 F→.num I4 F→ (.E) E→ .E+T E→ .Τ T→.T*F T→ .F F→ .(E) F→ .num I7 T→T*.F F→ .(E) F→.num I1 E’→ Ε. E→ E.+T I5 F→ num. I9 E→ E+T. T→T.*F I2 E→ Τ. T→T.*F I3 T→ F. I6 E→ E+.T T→ .T*F T→ .F F→ .(E) F→.num E I0 + I1 T I6 I9 F ( I8 F→ (E.) E→ E.+T F * I2 to I4 to I5 F I7 I10 ( I3 ( I0 T→T*F. I11 F→ (E). num E ) I8 T num I5 to I4 to I5 ( I4 num to I7 to I3 num T * F I11 + to I6 to I2 to I3 17/05/2016 77 Table d’analyse LR(0) E I0 + I1 T I6 I9 F ( F * I2 ( I10 num num to I4 4 5 6 E ) I8 T num I5 0 1 3 to I5 ( I4 num State 2 ( I3 to I7 to I4 to I5 F I7 actions / goto to I3 num T * F + to I2 to I6 9 11 * s5 r2 r4 s5 r6 s5 s5 8 10 to I3 17/05/2016 7 I11 + r1 r3 r5 ( goto ) $ T F 1 2 3 s4 E s6 r2 r4 r6 s6 r1 r3 r5 s7 r2 r4 r6 s7 r1 r3 r5 r2 r4 s4 r6 s4 s4 r1 r3 r5 r2 r4 acc r2 r4 8 r6 2 3 9 3 r6 10 s11 r1 r1 r3 r3 r5 r5 78 Analyse LR(1) – Puisque les analyseurs LR(0) n’utilisent pas de lookahead, ils ne peuvent pas analyser efficacement les langages pratiques en programmation. – Les analyseurs LR(1) généralisent les analyseurs LR(0) en introduisant un lookahead dans les tables d’analyse, un peu comme dans la version de l’analyseur LR(0) avec lookahead. – La différence est que dans le cas d’un analyseur LR(1), le lookahead est non seulement impliqué dans la décision de faire l’action shift, mais aussi dans la décision de faire l’action reduce. 17/05/2016 79 Analyse LR(1) – Les analyseurs LR(1) les plus populaires sont SLR(1) et LALR(1). – SLR(1) signifie “simple” LR(1). Techniquement, c’est une simple modification de l’analyseur LR(0) avec lookahead. ➢ La modification est très simple mais significative : on obtient un analyseur presque aussi puissant qu’un analyseur LR(1) complet. – LALR(1) signifie “Look Ahead LR(1)”. – Il est légèrement plus puissant que SLR(1). – L’appellation “LALR” peut paraître inappropriée vu que SLR(1), aussi bien que tout autre analyseur LR(1), utilise aussi un lookahead. – En fait, en disant “Look Ahead LR(1),” le “Look Ahead” réfère à l’AFD pour les préfixes viables : dans un analyseur LALR(1), l’AFD est composé des éléments qui sont associés avec un lookahead, alors que ce n’est pas le cas ni dans l’analyse SLR(1) ni dans l’analyse LR(0) avec lookahead. 17/05/2016 80 Construire la table d’analyse SLR(1) 1/2 Algorithm construire la table d’analyse SLR(1) Entrée : Une grammaire augmentée G, avec le symbole de départ S’; Sortie : Table d’analyse SLR(1); Méthode : 1. Construire l’AFD pour les préfixes viables de G; 2. Nous notons i, l’état Ii. Les actions d’analyse pour l’état i sont déterminées comme suit : a. Si un élément A → α . aβ, tel que a est un terminal, est dans Ii et goto(Ii , a) est défini, ajoute shift dans action[i, a]; b. Si un élément A → α . est dans Ii , avec A différent de S’, ajoute reduce A → α dans action[i, a], pour chaque terminal a dans Follow(A); c. Si l’élément S’ → S . est dans Ii , ajoute ‘accept’ dans action[i, $]. 17/05/2016 81 Construire la table d’analyse SLR(1) 2/2 3. Si goto(Ii, A) = Ij pour un non terminal A goto[i, A] devient j ; 4. Toutes les entrées vides sont considérées comme ayant la valeur “erreur”. 5. L’état initial, 0, est celui construit à partir de l’état de l’AFD contenant l’élément S’ → . S − Si la table générée par cette algorithme contient des conflits, c.-à-d., des entrées avec des actions multiples, la grammaire correspondante n’est pas SLR(1). − L’algorithme d’analyse demeure le même que le précédent (seule la table change). − Un analyseur SLR(1) est un analyseur qui utilise une table SLR(1). Dans ce cas on omet en général le suffixe (1) puisqu’on ne s’intéresse pratiquement pas à des analyseurs SLR(k) avec k>1. 17/05/2016 82 Exemple I0 E’→ .Ε 1 E→ .E+T 2 E→ .Τ 3 T→.T*F 4 T→ .F 5 F→ .(E) 6 F→.num I4 F→ (.E) E→ .E+T E→ .Τ T→.T*F T→ .F F→ .(E) F→ .num I7 T→T*.F F→ .(E) F→.num I1 E’→ Ε. E→ E.+T I5 F→ num. I9 E→ E+T. T→T.*F I2 E→ Τ. T→T.*F I3 T→ F. I6 E→ E+.T T→ .T*F T→ .F F→ .(E) F→.num E I0 + I1 T I6 I9 F ( I8 F→ (E.) E→ E.+T F * I2 to I4 to I5 F I7 I10 ( I3 ( I0 T→T*F. I11 F→ (E). num E ) I8 T num I5 to I4 to I5 ( I4 num to I7 to I3 num T * F I11 + to I6 to I2 to I3 17/05/2016 83 Table d’analyse SLR(1) E I0 + I1 T I6 I9 F ( F * I2 to I4 to I5 F I7 I3 ( to I4 4 5 6 E ) I8 T num I5 0 1 F 7 I11 + to I2 to I3 to I6 8 9 10 11 17/05/2016 + * s5 ( goto ) $ E T F 1 2 3 s4 s6 r2 r4 3 to I5 ( I4 num num num State 2 I10 ( actions / goto to I7 to I3 num T * s7 r4 s5 r2 r4 acc r2 r4 s4 r6 r6 s5 s5 8 r6 s7 r3 r5 3 9 3 r6 s4 s4 s6 r1 r3 r5 2 10 s11 r1 r1 r3 r3 r5 r5 84 Simulation actions / goto num 0 1 7 8 9 10 11 ( ) $ s7 r4 s5 r2 r4 T F 1 2 3 acc r2 r4 s4 r6 r6 s5 s5 8 r6 s7 r3 r5 17/05/2016 2 3 r6 s4 s4 s6 r1 r3 r5 Exemple d’exécution : E s4 s6 r2 r4 3 6 * s5 2 4 5 + goto 9 (0, (0 ( 4, (0 ( 4 num 5, (0 ( 4 F 3, (0 ( 4 T 2, (0 ( 4 E 8, (0 ( 4 E 8 ) 11, (0 F 3, (0 T 2, (0 E 1, (num)$) num)$) )$) )$) )$) )$) $) $) $) $) s4 s5 r6: F→ num. r4: T→ F. r2: E→ T. s11 r5: F→ (E). r4: T→ F. r2: E→ T. accept 3 10 s11 r1 r1 r3 r3 r5 r5 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. Dérivation correspondante (num) <= (num) <= (num) <= (F) <= (T) <= (E) <= (E) <= F <= T <= E 85 Optionnel ALGORITHME D’ANALYSE LR(0) SANS LOOKAHEAD 17/05/2016 86 Tables d’analyse LR(0) sans Lookahead – En principle, pour l’analyse LR(0), on a pas besoin de lookahead. On aurait pu donc formule l’analyse LR(0) sans lookahead. – Sans lookahead, la table d’analyse est indexée seulement par l’état de l’AFD. – Nous utilisons les conventions suivantes pour la spécification des tables : • s signifie ‘Fait shift’ (utilisé seulement dans les tables sans lookahead); On n’a plus besoin de si c.à`d. ‘Fait shift ensuite empile l’état i’ (c.-à-d., action shif et goto état i); • ri signifie encore ‘Fait reduce avec la production numéro i’; • acc signifie accepte l’entrée; • une entrée vide signifie que l’analyseur doit signaler une erreur. 17/05/2016 87 Construire la table d’analyse LR(0) sans lookahead 1/2 Algorithm construire la table d’analyse LR(0) sans lookahead Entrée : Une grammaire augmentée G, avec le symbole de départ S’; Sortie : Table d’analyse LR(0) sans lookahead; Méthode : 1. Construire l’AFD pour les préfixes viables de G; 2. Nous notons i, l’état Ii. Les actions d’analyse pour l’état i sont déterminées comme suit : a. Si un élément A → α . aβ, tel que a est un terminal, est dans Ii et goto(Ii , a) est défini, ajoute shift dans action[i]; b. Si un élément A → α . est dans Ii , avec A différent de S’, ajoute reduce A → α dans action[i]; c. Si l’élément S’ → S . est dans Ii , ajoute ‘accept’ dans action[i]. 17/05/2016 88 Construire la table d’analyse LR(0) sans lookahead) 2/2 3. Si goto(Ii, A) = Ij pour un non terminal A goto[i, A] devient j ; 4. Toutes les entrées vides sont considérées comme ayant la valeur “erreur”. 5. L’état initial, 0, est celui construit à partir de l’état de l’AFD contenant l’élément S’ → . S Si la table générée par cette algorithme contient des conflits, c.-à-d., des entrées avec des actions multiples, la grammaire correspondante n’est pas LR(0). Sinon, elle est LR(0). 17/05/2016 89 Exemple 1/2 1 2 3 4 5 I0 S’→ . S S→ .Ε; E→ .E+T E→ .Τ T→ .(E) T→ .num I5 T→ num. I1 S→ Ε .; E→ E .+T I2 S→ E;. I7 T→ (E .) E→ E .+T I4 E→ E+T. 17/05/2016 I9 I0 num I6 T→ (.E) E→ .E+T E→ .Τ T→ .(E) T→ .num I3 E→ E+.T T→ .(E) T→ .num T I8 T→ (E). E I5 S I10 num I1 ; + I3 T I2 ( num ( + T ( I6 E I7 ) I4 I8 I9 E→T. I10 S’→ S. 90 Exemple 2/2 T I9 I0 num E I5 S I10 num I1 + ; I3 T I2 ( num ( + num T 0 ( I6 E 1 State 2 3 I7 4 ) I4 I8 5 6 7 8 9 10 17/05/2016 goto action s s r1 s r2 r5 s s r4 r3 acc + 5 ( ) ; 6 10 3 E T 1 9 2 5 6 5 6 3 S 4 7 9 8 91 Algorithme d’analyse LR(0) avec table d’analyse sans lookahead (1/2) Algorithm LR0ParserWithTable Entrée : flux de tokens Sortie : dérivation de l’entrée si elle est dans L(G), sinon erreur. Variables : stack (pile), a (prochain token), in (entrée : flux de tokens) i (sommet de la pile) Table d’analyse LR(0) sans lookahead. Méthode : Initialement la pile contient 0 (c-.à-d., état 0). Ensuite il exécute le code suivant. 17/05/2016 92 Algorithme d’analyse LR(0) avec table d’analyse sans lookahead (2/2) while (true) { i = state on top of stack; if action[i] = shift { a = in.getNextToken(); // Ceci avance la tête de lecture. push a on top of the stack; push goto[i,a] on top of the stack; continue; } if action[I] = reduce A→α { pop 2x|α | symbols off the stack; i = the state now on top of the stack; push A on top of stack; push goto[i,A] on top of the stack; printout the production A→α ; continue; } if action[i] = accept and the input is empty return true; error(); } 17/05/2016 93 Exemple (simulation de l’analyseur LR(0)) action num 0 1 State 2 3 4 Grammar S’→ S 1. S → Ε; 2. E → E+T 3. E → Τ 4. T → (E) 5. T → num 5 6 7 8 9 10 s s r1 s r2 r5 s s r4 r3 acc + 5 ( ) ; 6 10 3 E T 1 9 2 5 6 5 6 3 S 4 8 8 9 1. (0, num+num;$) 2. (0 num 5, +num;$) 3. (0 T 9, +num;$) 4. (0 E 1, +num;$) 5. (0 E 1 + 3, num;$) 6. (0 E 1 + 3 num 5, ;$) 7. (0 E 1 + 3 T 4, ;$) 8. (0 E 1, ;$) 9. (0 E 1 ; 2, $) 10. (0 S 10, $) Shift Reduce 5 Reduce 3 Shift Shift Reduce 5 Reduce 2 Shift Reduce 1 Accept. Dérivation correspondante : num + num; <= num+ num; <= T + num; <= E + num <= E + num; <= E + num; <= E + T; <= E; <= E; <= S 17/05/2016 94 Rappel : Algorithme d’analyse LR (1) 1/2 Algorithm LR1Parser Entrée : flux de tokens Sortie : dérivation de l’entrée si elle est dans L(G), sinon erreur. Variables : stack (pile), a (prochain token), in (entrée : flux de tokens) i (sommet de la pile) Table d’analyse LR(0) avec lookahead, ou SLR(1), ou LALR(1). Méthode : Initialement la pile contient l’état 0. Ensuite il exécute le code suivant. 95 Rappel : Algorithme d’analyse LR (1) 2/ 2 a = in.getNextToken(); while (true) { i = state on top of stack; if action[i,a] = shift j { push a on top of the stack; push j on top of the stack; a = get.NextToken(); continue; } if action[i,a] = reduce A→α { pop 2x|α| symbols off the stack; i = the state now on top of the stack; push A on top of stack; push goto[i,A] on top of the stack; printout the production A→α ; continue; } if action[i,a] = accept return true; error(); } 96 Rappel : Table d’analyse SLR(1) Algorithm construire la table d’analyse SLR(1) Entrée : Une grammaire augmentée G, avec le symbole de départ S’; Sortie : Table d’analyse SLR(1); Méthode : 1. Construire l’AFD LR(0) de G; 2. Nous notons i, l’état Ii. Les actions d’analyse pour l’état i sont déterminées comme suit : a. Si un élément A → α . aβ, tel que a est un terminal, est dans Ii et goto(Ii , a) = Ij, ajoute ‘shift j’ dans action[i, a]; b. Si un élément A → α . est dans Ii , avec A différent de S’, ajoute reduce A → α dans action[i, a], pour chaque terminal a dans Follow(A); c. Si l’élément S’ → S . est dans Ii , ajoute ‘accept’ dans action[i, $]. 97 Rappel : Table d’analyse SLR(1) 3. Si goto(Ii, A) = Ij pour un non terminal A goto[i, A] devient j ; 4. Toutes les entrées vides sont considérées comme ayant la valeur “erreur”. 5. L’état initial, 0, est celui construit à partir de l’état de l’AFD contenant l’élément S’ → . S − Si la table générée par cette algorithme contient des conflits, c.-à-d., des entrées avec actions multiples, la grammaire correspondante n’est pas SLR(1). − Un analyseur SLR(1) est un analyseur qui utilise une table SLR(1). 98 Exemple – Grammaire illustrant l’accès au contenu d’une adresse donnée par un pointeur dans le langage C : 0. 1. 2. 3. 4. 5. S’→ S S→L=R S→R L→*R L → id R→L Les terminaux sont : id, = et *. – On peut interpréter les symboles L et R, respectivement comme étant la « L-value » et la « R-value »; et * l’opérateur “contenu de”. Exemple: *x = y. 99 Table SLR(1) AFD LR(0) actions / goto conflit shift/reduce 9 S→ L=R. R L R S I1 S’→ S. I7 L→*R. 0 S’→ .S S→ .L=R S→ . R L→.*R L→ .id R→ .L = 2 S→ L.=R R→ L. 3 S→ R. id id 5 L→ id. L id * R * I6 S→ L=.R R→ .L L→.*R L→ .id 4 L→*.R R→ .L L→.*R L→ .id * L id * 0 1 2 3 s5 s4 4 5 6 7 8 9 s5 s4 = $ goto S R L 1 3 2 acc s6 r5 r5 r2 r4 8 R→ L. 1. 2. 3. 4. 5. 8 9 8 r4 s5 s4 r3 r5 7 r3 r5 r1 S→L=R S→R L→*R L → id R→L 100 Simulation actions / goto id * 0 1 2 3 s5 s4 4 5 6 7 8 9 s5 s4 = $ goto S R L 1 3 (0, *id = id $) (0 * 4, id = id $) (0 * 4 id 5, = id $) (0 * 4 L 8, = id $) (0 * 4 R 7, = id $) (0 L 2, = id $) 2 acc s6 r5 r5 r2 7 r4 r4 s5 s4 Conflit r3 r5 r1 2 S→ L.=R R→ L. S→L=R S→R L→*R L → id R→L conflit shift/reduce shift reduce 9 r3 r5 8 1. 2. 3. 4. 5. 8 (0 R 3, = id $) Error: no entry [3, =] in table (0 L 2 = 6, id $) (0 L 2 = 6 id 5, $) (0 L 2 = 6 L 8, $) (0 L 2 = 6 R 9, $) (0 S 1, $) Accepte 101 De SLR(1) à LR(1) – Dans SLR(1) lorsque l’état au sommet de la pile contient l’élément A → α . et que le prochain token est dans Follow(A), on fait une réduction de α sur la pile. – Cette décision est trop approximative pour deux raisons : – Follow(A) est l’union de tous les tokens qui peuvent suivre A, en tenant compte de toutes les productions qui commencent par A, pas juste la production A → α pour laquelle on a l’élément A → α . – On ne devrait pas regarder juste A, mais le préfixe viable sur la pile (δΑ) pour s’assurer que δΑ dérive effectivement une chaîne pouvant être suivi du prochain token. – Cette approximation cause des conflits. Les éléments LR(1) font une meilleure approximation, donnant lieu à moins de conflits. 102 Éléments LR(1) – Tout comme les éléments LR(0), un élément LR(1) consiste d’une production et d’un point. En plus, un élément LR(1) a un symbole lookahead (le prochain token attendu). – Plus précisément, un élément LR(1) est de la forme [A → α . β , a ]. – Comme pour les éléments LR(0), cela signifie que nous avons α au sommet de la pile, et on s’attend à lire un préfixe du reste de l’entrée dérivé de β. – En plus, le “a” signifie que le token “a” peut suivre A dans une forme sentientielle droite, obtenue en utilisant la production A → α β : * * S => xAy => xα β y et a est dans First(y). 103 AFD LR(1) – En d’autres mots, un élément [A → α . β , a ] signifie que : • Le reste de l’entrée débute par un préfixe dérivable de βa • Après la réduction de αβ par A, on va faire shift de a. Ainsi, A sera suivi de a sur la pile. – Un état LR(1) est un ensemble d’éléments LR(1), obtenues en utilisant la fonction etats(G) suivante. – Cette version utilise des fonctions closure et goto qui sont des mises à jour des fonctions similaires pour l’AFD LR(0). 104 Algorithme Closure Algorithm Closure(I) do { pour chaque élément [A → α . Xβ , a ] dans I chaque production X →γ dans la grammaire et chaque terminal b dans First(βa) tel que [X → . γ , b ] n’est pas dans I ajouter [X → . γ , b ] à I // b pourrait suivre X dans une dérivation droite } while (des éléments nouveaux sont ajoutés dans I) return I 105 Algorithmes goto et etats Algorithm goto(I,X) soit J l’ensemble d’éléments [A → αX . β , a ] tel que [A → α X . β , a ] est dans I; return closure(I) Algorithm etats(Augmented grammar G) C = { closure({[S’ → . S , $]}) }; // C est l’ensemble des états do pour chaque état I dans C et chaque non terminal X tel que goto(I,X) est non vide et n’est pas encore dans C ajouter goto(I,X) dans C while (un nouvel état est ajouté dans C) return C 106 Générer la table d’analyse LR(1) Algorithm Constructing LR(1) Parsing Table Input: An augmented grammar G, with start symbol S’; Output: LR(1) parsing table; Method: 1. Construct the LR(1) DFA for G; 2. State i corresponds to Ii . The parsing actions for state i are determined as follows: a. If an item [A → α . aβ, b] for a terminal a, is in Ii and goto(I , a) =i Ij , set action[i, a] to ‘shift j’ b. If an item [A → α . , a] is in Ii , where A is different from S’, set action[i, a] to ‘reduce A → α ’ c. If item [S’ → S . , $] is in Ii , set action[i, $] to ‘accept ’. 107 Générer la table d’analyse LR(1), suite 3. If goto(Ii, a) = Ij set goto[i, A] = j; for a nonterminal A 4. All entries not defined by the rules 2 and 3 are made “error” . 5. The initial state ‘0’ is the one constructed from the set of items containing [S’ → S , $] − Si la table d’analyse contient des entrées multiples, la grammaire n’est pas LR(1). − L’algorithme d’analyse demeure le même que pour l’analyse SLR(1) (LR1Driver). − Un analyseur utilisant une table LR(1) est un analyseur LR(1). 108 Table SLR(1) Rappel : AFD LR(0) actions / goto conflit shift/reduce 9 S→ L=R. R L R S I1 S’→ S. I7 L→*R. 0 S’→ .S S→ .L=R S→ . R L→.*R L→ .id R→ .L = 2 S→ L.=R R→ L. 3 S→ R. id id 5 L→ id. L id * R * I6 S→ L=.R R→ .L L→.*R L→ .id 4 L→*.R R→ .L L→.*R L→ .id * L id * 0 1 2 3 s5 s4 4 5 6 7 8 9 s5 s4 = $ goto S R L 1 3 2 acc s6 r5 r5 r2 r4 8 R→ L. 1. 2. 3. 4. 5. 8 9 8 r4 s5 s4 r3 r5 7 r3 r5 r1 S→L=R S→R L→*R L → id R→L 109 Exemple 1 : AFD LR(1) 1 S’→ S. , $ S 2 S→ L.=R, $ R→ L., $ L 0 S’→ .S, $ S→ .L=R, $ S→ . R, L→.*R, L→ .id, R→ .L , * * $ $/= $/= $ R id id 5 L→*.R, $/= R→ .L, $/= L→.*R, $/= L→ .id, $/= 4 S→ R., $ R 3 S→ L=.R, R→ .L, L→.*R, L→ .id, L 6 R→ L., $ 7 L→ id., $/= 10 $ 11 R→ L., $/= 9 L→*R., $/= id L id 1. 2. 3. 4. 5. $ $ $ $ S→L=R S→R L→*R L → id R→L R * L→ id., L = 8 S→ L=R., $ 12 L→*.R, R→ .L, L→.*R, L→ .id, R $ $ $ $ * Le conflit dans l’état 2 a disparu 13 L→*R., $ 110 Table LR(1) 1 S’→ S. , $ S 2 S→ L.=R, $ R→ L., $ L 0 S’→ .S, $ 1 2 S→ .L=R, $ $ 3 S→ . R, 4 L→.*R, $/= 5 L→ .id, $/= R→ .L , $ * * R id id 5 L→*.R, $/= R→ .L, $/= L→.*R, $/= L→ .id, $/= 4 S→ R., $ R 3 S→ L=.R, R→ .L, L→.*R, L→ .id, L 6 R→ L., $ 7 L→ id., $/= 10 $ 11 R→ L., $/= 9 L→*R., $/= id L id actions / goto $ $ $ $ R * L→ id., L = 8 S→ L=R., $ 12 L→*.R, R→ .L, L→.*R, L→ .id, R $ $ $ $ 13 L→*R., $ id 0 1 2 3 4 5 6 7 8 9 * = $ s7 s5 goto S R L 1 4 2 acc s3 r5 s10 s12 8 6 9 11 r2 s7 s5 10 11 12 s10 s12 13 r5 r4 r4 r1 r3 r3 r4 r5 r5 13 6 r3 111 Exemple 1/ 2 Soit la grammaire 1. S→ CC 2. C→ cC 3. C→ d Le symbole de départ est S. Les terminaux sont: c, d Construire la table LR(1) 112 Exemple 2/2 0 S’→ .S, $ S→ .CC, $ C→ . cC, c/d C→.d, c/d S 1 S’→ S., $ 2 S→ C.C, $ C→ . cC, $ C→.d, $ C c d d 4 C→ d., c/d c c 6 d c 3 C→ c.C, c/d C→ . cC, c/d C→.d, c/d c 5 S→ CC., $ C d C→ c.C, $ C→ . cC, $ 7 C→.d, $ C→ d., $ C actions C 0 1 2 3 4 5 6 7 8 9 d goto $ s3 s4 S C 1 2 acc s6 s7 s3 s4 r3 r3 5 8 r1 s6 s7 9 r3 r2 r2 r2 9 8 C→ cC., c/d C→ cC., $ 1. S→ CC 2. C→ cC 3. C→ d 113 ANALYSE LALR (1) 114 AFD LALR(1) et Table d’analyse LALR(1) – Les tables d’analyse LR(1) peuvent devenir très larges. On ne les utilisent pas en pratique. – La décomposition de l’ensemble Follow en lookahead associé à chaque élément LR(0) est la source de la puissance de l’analyse LR(1). – Mais on n’a pas besoin de cette décomposition dans chaque état. Cette observation mène à une approximation de l’AFD LR(1) beaucoup plus efficace (moins d’états), mais légèrement moins expressive: l’AFD LALR(1). – Un AFD LALR(1) est obtenu de l’AFD LR(1) en fusionnant les états identiques pour les éléments LR(0) (seuls les composantes lookahead diffèrent). – Une table d’analyse LALR(1) est obtenue de l’AFD LALR(1) de la même façon que la table d’analyse LR(1) est obtenue de l’AFD LR(1). – Il peu arriver que la table d’analyse LALR(1) contient des conflits alors que la table d’analyse LR(1) correspondant n’en a pas. Mais c’est rare. 115 Exemple 1 : AFD LR(1) 1 S’→ S. , $ S 2 S→ L.=R, $ R→ L., $ L 0 S’→ .S, $ S→ .L=R, $ S→ . R, L→.*R, L→ .id, R→ .L , * * $ $/= $/= $ R id id 5 L→*.R, $/= R→ .L, $/= L→.*R, $/= L→ .id, $/= 4 S→ R., $ R 3 S→ L=.R, R→ .L, L→.*R, L→ .id, L 6 R→ L., $ 7 L→ id., $/= 10 $ 11 R→ L., $/= 9 L→*R., $/= id L id 1. 2. 3. 4. 5. $ $ $ $ S→L=R S→R L→*R L → id R→L R * L→ id., L = 8 S→ L=R., $ 12 L→*.R, R→ .L, L→.*R, L→ .id, R $ $ $ $ * 13 L→*R., $ 116 AFD LALR(1) et Table d’analyse 1 S’→ S. , $ S 2 S→ L.=R, $ R→ L., $ L 0 1 2 3 4 5 S’→ .S, $ S→ .L=R, $ S→ . R, L→.*R, L→ .id, R→ .L , * * $ $/= $/= $ L R id id 4 S→ R., $ 6 R→ L., $/= $/= $ $ $ $ R 7 L→ id., $/= L→*.R, $/= R→ .L, L 3 S→ L=.R, R→ .L, L→.*R, L→ .id, id * 5 = 8 S→ L=R., $ actions / goto id 0 1 2 3 4 5 6 7 8 9 * = $ s7 s5 goto S R L 1 9 acc s3 r5 s7 s5 8 6 9 6 r2 s7 s5 r5 r4 r3 r5 r4 r1 r3 R L→.*R, $/= L→ .id, $/= 6 9 L→*R., $/= 117 Exemple 2 0 S’→ .S, $ S→ .CC, $ C→ . cC, c/d C→.d, c/d S 1 S’→ S., $ 2 S→ C.C, $ C→ . cC, $ C→.d, $ C c c d d 4 C→ d., c/d c 6 d c 3 C→ c.C, c/d C→ . cC, c/d C→.d, c/d c 5 S→ CC., $ C d C→ c.C, $ C→ . cC, $ 7 C→.d, $ C→ d., $ actions C 0 1 2 3 4 5 6 7 8 9 d goto $ s3 s4 S C 1 2 acc s6 s7 s3 s4 r3 r3 5 8 r1 s6 s7 9 r3 r2 r2 r2 C 9 8 C→ cC., c/d C→ cC., $ 1. S→ CC 2. C→ cC 3. C→ d 118 Exemple 2 0 S’→ .S, $ S→ .CC, $ C→ . cC, c/d C→.d, c/d S 1 S’→ S., $ 5 S→ CC., $ C C c c 2 S→ C.C, $ C→ . cC, $ C→.d, $ c d actions c 0 1 2 3 d goto $ s3 s4 S C 1 2 acc s3 s4 s3 s4 r3 r3 r3 r3 r1 r2 4 5 6 r2 5 6 3 C→ c.C, c/d/$ C→ . cC, c/d/$ C→.d, c/d/$ d d 4 C→ d., c/d/$ C 6 C→ cC., c/d/$ 1. S→ CC 2. C→ cC 3. C→ d 119 Génération efficace des tables d’analyse LALR(1) – Il existe une méthode efficace pour générer l’AFD LALR(1) et la table d’analyse LALR(1) sans passer directement par l’AFD LR(1). – Cette méthode calcule les états de l’AFD LALR(1) à la volée en utilisant . une technique de propagation des lookahead. Voir Aho-Sethi-Ullman. Compilers: Principles, Techniques and Tools, 1988 :Pages 240 – 244. – Dans le même livre : représentation compacte des tables LALR(1): Pages 244-245. 120 Commentaires sur l’analyse LALR(1) – Les états de l’AFD LR(1) sont toujours des versions dupliquées des états de l’AFD LR(0), mis à part les lookahead qui diffèrent. – Donc l’AFD LALR(1) a le même nombre d’états que l’AFD LR(0) et les mêmes transitions. – . Une des raisons pour lesquelles l’analyse LALR(1) fonctionne si bien et que la fusion des états de l’AFD LR(1) ne peut pas introduire des conflits shift-reduce. S’il y en a dans l’AFD LALR(1) c’est qu’elles étaient dans l’AFD LR(1) d’origine. Seuls des conflits reduce-reduce peuvent être introduites par la fusion des états. 121 Hiérarchie des grammaires hors-contexte Grammaires non ambigües LR(k) LL(k) LL(1) Grammaires ambigües LR(1) LALR (1) SLR (1) LL(0) LR(0) 122