IFT313 Introduction aux langages formels Froduald Kabanza Département d’informatique Université de Sherbrooke Analyseurs LR(0)
Sujets Préfixes viables. Éléments LR(0). AFD LR(0) pour reconnaître les préfixes viables. Analyseur LR(0). IFT313 © Froduald Kabanza
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. IFT313 © Froduald Kabanza
Références [1] Sudkamp, T. A.. Languages and Machines. Third Edition Edition. Addison-Wesley, 2005. Sections 20.1 à 20.4 [2] Appel, A. and Palsberg. J. Modern Compiler Implementation in Java. Second Edition. Cambridge, 2004. Section 3.3 [4] Aho, A., Lam, M., Sethi R., Ullman J. Compilers: Principles, Techniques, and Tools, 2nd Edition. Addison Wesley, 2007. Section 4.6 IFT313 © Froduald Kabanza
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; Remarquer que cet algorithme suppose qu’on fait reduce d’abord avant de faire shift. Comme vu précédemment, dans le cas non-déterministe, à priori on peut avoir des situations pour lesquelles shift et reduce sont simultanément possibles. Toutefois, comme cet algorithme est une ébauche pour une version déterministe qu’on verra plus tard, les grammaires qu’on va utiliser n’auront pas de tel conflits. IFT313 © Froduald Kabanza
Exemple Grammaire augmentée Grammaire G = (V, A, R, E) : V= {E’, E, F, T} A = {(, ), +, *, num} R = { E’ ® E E ® E + T E ® T T ® T * F T ® F F ® ( E) F ® num} Grammaire G = (V, A, R, E), avec V= {E, F, T} A = {(, ), +, *, num} R = { E ® E + T E ® T T ® T * F T ® F F ® ( E) F ® num} IFT313 © Froduald Kabanza
Exemple, suite Productions Observation 1. E’ ® 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 1. ($, (num)$) Shift 2. ($(, num)$) Shift 3. ($(num, )$) Reduce 7 4. ($(F, )$) Reduce 5 5. ($(T, )$) Reduce 3 6. ($(E, )$) Shift 7. ($(E), $) Reduce 6 8. ($F, $) Reduce 5 9. ($T, $) Reduce 3 10. ($E, $) Reduce 1 11. ($E’, $) Accepte Dérivation correspondante : (num) <= (num) <= (num) <= (F) <= (T) <= (E) <= (E) <= F <= T <= E <= E’ Observation Le contenu de la pile (g ) concaténé avec les tokens restant à lire (w) est une forme sententielle pour une dérivation la plus à droite : S => g w * R IFT313 © Froduald Kabanza
Rappel : Contexte LR(0) 1/2 On aurait pu formuler l’analyser LR(0) à l’aide du concept de «contexte LR(0)» plutôt que celui « poignée » (Sudkamp [1] , page 596). Nous avons introduit la « poignée » comme suit: Si S => aAw =>ab w, alors A ® b dans la position juste après a est une poignée pour ab w Étant donné une grammaire (V, Σ, P, S), la chaîne ab est un contexte LR(0) pour la règle de production A ® b s’il existe une dérivation S => aAw =>ab w, avec a et b ϵ (V U Σ)* et w ϵ Σ*. Autrement dit, ab est un contexte LR(0) pour la règle de production A ® b ssi A ® b est un poignée pour ab w, avec a et b ϵ (V U Σ)* et w ϵ Σ* . * R R * Dans le manuel du cours [1] (Sudkamp, T. A. Languages and Machines. Third Edition Edition. Addison-Wesley, 2005), on utilise le concept de “contexte LR(0)” plutôt que celui de “poignée”. Un contexte LR(0) est un préfixe d’une forme sentantielle droite (rappelons que le contenu de la pile est toujours un préfixe d’une forme sentielle droite) donnant lieu à une réduction; c-à-d., un préfixe d’une forme sententielle terminé par une poignée. Les deux concepts sont très proches et ont la même finalité : déterminer la bonne action à prendre (shift ou reduce). Les deux notions expriment relativement la même chose. Le poignée fait référence à un suffixe de la pile; le contexte LR(0) fait référence à tout le contenu de la pile. Cependant le concept de poigné est le plus fréquemment rencontré dans la littérature. R R IFT313 © Froduald Kabanza
Rappel : Contexte LR(0) 2/2 Tout comme pour un poignée, un contextes LR(0) détermine quand faire une réduction durant l’analyse LR, sinon quand faire « shift ». Si A ® b est une poignée (c.-à-d., si le contenu de la pile (ab) est un contexte LR(0) de la règle A ® b) , on réduit avec A ® b. S’il n’y a pas de poignée au sommet de la pile, mais le sommet de la pile contient un préfixe d’une poignée (c.-à-d., si le contenu de la pile (ab) est n’est pas un contexte LR(0) mais est un préfixe d’un contexte LR(0)), on met (shit) le prochain token sur la pile. Si le sommet de la pile n’est pas un préfixe pour une poignée quelconque (c.à-d., le contenu de la pile n’est pas un préfixe pour un contexte LR(0) quelconque), on rejette la chaîne d’entrée. Pour une grammaire non ambiguë, il y au plus une seule poignée à chaque étape d’une dérivation la plus à droite. De façon équivalente, il y a au plus un seul contexte LR(0). Donc, les deux premières étapes de l’algorithme donnent lieu à une seule action possible. Le contenu de la pile est un préfixe viable si et seulement si c’est un préfixe d’un contexte LR(0) (autrement dit, le contenu de la pile a un suffixe qui est une poignée). Si tel est le cas, une séquence de shift produit un contexte LR(0) (autrement dit, produit une poignée au sommet de la pile). Nous reviendrons sur le concept de préfixe viable plus tard. IFT313 © Froduald Kabanza
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. IFT313 © Froduald Kabanza
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. IFT313 © Froduald Kabanza
Préfixes viables Pour répondre à la question précédente, rappelons d’abord que le contenu de la pile (g) concaténé avec le reste des tokens (a) 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. De façon équivalente, un préfixe viable est défini comme étant un préfixe d’un contexte LR(0). 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. Dans la leçon¸on précédente, nous avons vu qu’un contexte LR(0) est un contenu de la pile donnant lieu à une réduction, autremenent dit un contenu de la pile ayant un suffixe qui est une poignée. Voir aussi la note de la slide 24 dans la lécon précédente (automates à pile LR). IFT313 © Froduald Kabanza
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 si XYZ est un préfixe viable. 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 ® e, on va générer un seul état : A ® . X Y Z IFT313 © Froduald Kabanza
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. IFT313 © Froduald Kabanza
AFN pour reconnaître les préfixes viables L’état initial est S’ ® . S Pour chaque paire d’états A ® a . X b et A ® aX . b, ajouter une transition du premier état vers le deuxième, étiquetée par X. Il faut garder à l’esprit que A ® a . Xb signifie : à un moment donné durant l’analyse syntaxique, on vient juste de scanner un sous-chaîne dérivable de a et on s’attend à scanner une sous-chaîne dérivable de X, ensuite une sous-chaîne dérivable de b. Ainsi, la transition de A ® a . X b à A ® aX . b, é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 b. Pour chaque paire d’états A ® a . X b et X ® . g, ajouter une transition du premier vers le deuxième, étiquetée e. Intuitivement, si on s’attend à scanner une sous-chaîne dérivable de X et si X peut dériver g, on peut s’attendre aussi bien à scanner une chaîne dérivable de g. IFT313 © Froduald Kabanza
Éléments canoniques (de base) et l’opération de fermeture (closure) L’état initial est S’ ® . S Pour chaque paire d’états A ® a . X b et A ® aX . b, ajouter une transition du premier état vers le deuxième, étiquetée par X. Pour chaque paire d’états A ® a . X b et X ® .g, ajouter une transition du premier vers le deuxième, étiquetée e. 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 e. IFT313 © Froduald Kabanza
Exemple 1/3 Grammaire Grammaire augmentée G = (V, A, R, E) : V= {E, F, T} A = {(, ), +, *, num} R = { E ® E + T E ® T T ® T * F T ® F F ® ( E) F ® num } Grammaire augmentée G = (V, A, R, E) : V= {E’, E, F, T} A = {(, ), +, *, num} R = { E’ ® E E ® E + T E ® T T ® T * F T ® F F ® ( E) F ® num } IFT313 © Froduald Kabanza
AFN pour les préfixes viables Exemple 2/3 AFN pour les préfixes viables e Grammaire E’® E E® E+T E® T T®T*F T® F F® (E) F®num E T T®F. E’®.E E’®E. E®.T E®T. e e F E + E®.E+T E®E.+T E®E+.T e T®.F e e F® .num E®E+T. T®.T*F T®T.*F F®.(E) F®num. F®(.E) T®T*.F F®(E.) T®T*F. F®(E). Seulement quelques transitions sont présentes ! Compléter les autres comme exercice. IFT313 © Froduald Kabanza
Exemple 3/3 e E’ E T ( F ) + * num 1. E’® .E 2. E’® E. 3. E® .E+T 9. T®.T*F 10. T®T.*F 11. T®T*.F 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 2 7. E® .T 3,7 Même AFN, avec table de transition. Seulement deux entrées complétées. Les autres sont laissées comme exercice. Grammaire E’® E E® E+T E® T T®T*F T® F F® (E) F®num IFT313 © Froduald Kabanza
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 I1 I6 I9 I2 I7 I10 I8 I11 I5 E T + F ( num * ) to I7 to I2 to I6 to I3 to I4 to I5 I4 I3 I0 E’® .E E® .E+T E® .T T®.T*F T® .F F® .(E) F®.num I1 E’® E. E® E.+T I2 E® T. T®T.*F I3 T® F. I4 F® (.E) F® .num I5 F® num. I6 E® E+.T T® .T*F I7 T®T*.F I8 F® (E.) I9 E® E+T. T®T*F. I11 F® (E). IFT313 © Froduald Kabanza
AFD pour reconnaître les préfixes viables L’AFD avec une table de transition. I0 E’® .E E® .E+T E® .T T®.T*F T® .F F® .(E) F®.num I1 E’® E. E® E.+T I2 E® T. T®T.*F I3 T® F. I4 F® (.E) F® .num I5 F® num. I6 E® E+.T T® .T*F I7 T®T*.F I8 F® (E.) I9 E® E+T. T®T*F. I11 F® (E). E T ( F ) + * num I1 E’ I0 I2 I4 I3 I7 I5 I9 I8 I10 I6 I11 IFT313 © Froduald Kabanza
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. Les états de l’AFD sont donc des ensembles d’états de l’AFN, c’est-à-dire, des ensembles d’éléments LR(0). En fait, on verra que les états de l’AFD sont les éléments canoniques LR(0) puisqu’on peut obtenir le reste d’un état par l’opération de fermeture. IFT313 © Froduald Kabanza
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 e, sous-jacente à méthode déterminisation, en éliminant notamment les transitions e. goto(I,X) implémente les transitions entre les éléments canoniques. IFT313 © Froduald Kabanza
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 ® a . Xb est dans closure(I) et X ® g est une production, ajouter l’élément X ® .g à closure(I). IFT313 © Froduald Kabanza
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 ® a . Xb in J and each production X ® g of G such that X ® .g is not in J) add X ® .g to J } while (J is modified) return J; IFT313 © Froduald Kabanza
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 ® aX . b pour lesquels il existe un élément A ® a . Xb 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 ® aX . b pour lesquels il existe un élément A ® a . Xb 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}). Ceci veut dire que si on veut représenter l’AFD de manière compacte, on peut juste stocker les éléments canoniques. Étant donné les éléments canoniques, il suffit alors de leur appliquer la fermeture pour retrouver l’état correspondant. IFT313 © Froduald Kabanza
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; IFT313 © Froduald Kabanza
Éléments valides Un élément A ® b1 . b2 est dit valide pour un préfixe viable a b1 s’il existe une dérivation la plus à droite telle que : S’ => aAw => a b1 b2 w . On peut démontrer que l’ensemble des éléments valides pour un préfixe viable g 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 g. La validité de A ® b1 . b2 pour un préfixe a b1 nous indique s’il faut faire shift ou alors reduce dès que a b1 est au sommet de la pile. En d’autre mots, ça nous indique si ab1 est une poignée ou non. * * R R IFT313 © Froduald Kabanza
Quand faire shift, quand faire reduce ? Supposons que a b1 est au sommet de la pile et que l’élément A ® b1 . b2 est valide pour a b1. Alors : Si b2 = e , cela veut dire qu’au sommet de la pile nous avons la partie droite de la production A ® b1. C.-à-d., b1 est une poignée, on peut donc réduire. Sinon, si b2 ≠e , cela veut dire qu’on doit faire shift des tokens de la sous-chaîne b2 avant d’obtenir b1 b2 . 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. betta2 = epsilon signifie que le point est à la fin d’une partie droite de la règle de production. betta2 différent de epsilon signifie que le point n’est pas à la fin d’une partie droite de la règle de production. IFT313 © Froduald Kabanza
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 b1 l’AFD sera dans un état contenant l’élément A ® b1 . , ce qui indique que nous devons faire une réduction avec la production A ® b1. En même temps que nous appliquons la réduction (en remplaçant b1 par A sur la pile), on doit revenir en arrière dans l’AFD (backtracking) le long du chemin étiqueté b1 jusqu’à l’état I qui est la racine du chemin étiqueté b1 . 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). IFT313 © Froduald Kabanza
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. IFT313 © Froduald Kabanza
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 de la pile revient à un retour arrière (backtracking) dans l’AFD, le long du chemin Xj … Xm. IFT313 © Froduald Kabanza
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 IFT313 © Froduald Kabanza
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 ® a . ) { reduce with the production A ® a; // a est une poignée et A ® a la production correspondante c.-à-d. : Supprimer 2×|a| symboles de la pile (|a| états et |a| 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 ® a; continue; } if (l’état au sommet de la pile contient un élément A ® a . ab 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. error(); IFT313 © Froduald Kabanza
Exemple to I7 to I2 to I6 to I3 to I4 to I5 IFT313 © Froduald Kabanza + F ( num * ) to I7 to I2 to I6 to I3 to I4 to I5 I4 I3 I0 E’® .E E® .E+T E® .T T®.T*F T® .F F® .(E) F®.num I1 E’® E. E® E.+T I2 E® T. T® T.*F I3 T® F. I4 F® (.E) F® .num I5 F® num. I6 E® E+.T T® .T*F I7 T®T*.F I8 F® (E.) I9 E® E+T. T®T.*F T®T*F. I11 F® (E). IFT313 © Froduald Kabanza
Exemple 1 Trace 1. (I0, (num)$) Shift T® .(E) + F ( num * ) to I7 to I2 to I6 to I3 to I4 to I5 I4 I3 Trace 1. (I0, (num)$) Shift T® .(E) 2. (I0(I4, num)$) Shift T® .num 3. (I0(I4numI5, )$) Reduce F® num. 4. (I0(I4FI3, )$) Reduce T® F. 5. (I0(I4TI2, )$) Reduce E® T. 6. (I0(I4EI8, )$) Shift F® (E.) 7. (I0(I4EI8)I11, $) Reduce F® (E). 8. (I0FI3, $) Reduce T® F. 9. (I0TI2, $) Reduce E® T. 10. (I0EI1, $) Reduce E’® E. 11. (I0E’, $) Accept Dérivation correspondante : (num) <= (num) <= (num) <= (F) <= (T) <= (E) <= (E) <= F <= T <= E <= E’ À l’étape 5, l’état I2 contient un conflit Shift (élément T -> T.*F) / Reduce (élément E -> T.). La grammaire n’est donc pas LR(0); l’analyseur LR(0) correspondant n’est pas déterministe. Le conflit devrait être résolu en fonction de reduce (intuitivement parce que, selon cette grammaire, « ) » ne peut suivre « T » dans une forme sententielle; c.-à-d., « ((T) » ne serait pas un préfixe viable.). On verra plus tard, comment résoudre ce conflit en utilisant un lookahead et l’ensemble Follow. Pour l’instant, résolvons donc le conflit en faveur de Reduce. À l’étape 10, l’état I1, contient un conflit Shift (élément E -> E.+T) / Reduce (élément E’ -> E.). De toute évidence, on est`rendu à la fin du fichier ($); il n’y a plus de token à lire (“shifter”). La seule option valide est donc Reduce. Là encore, on verra plus tard comment décider de cette option en se servant du lookahead et de l’ensemble Follow. IFT313 © Froduald Kabanza
Même entrée, mais à l’étape 9, on résoud le conflit en faveur de Shift Exemple 2 Même entrée, mais à l’étape 9, on résoud le conflit en faveur de Shift I0 I1 I6 I9 I2 I7 I10 I8 I11 I5 E T + F ( num * ) to I7 to I2 to I6 to I3 to I4 to I5 I4 I3 Trace 1. (I0, (num)$) Shift T® .(E) 2. (I0(I4, num)$) Shift T® .num 3. (I0(I4numI5, )$) Reduce F® num. 4. (I0(I4FI3, )$) Reduce T® F. 5. (I0(I4TI2, )$) Shift T® T.*F 6. (I0(I4TI2), $) Error goto(I2, ‘)’) non défini. IFT313 © Froduald Kabanza
Exemple 3 Une entrée différente Trace : + F ( num * ) to I7 to I2 to I6 to I3 to I4 to I5 I4 I3 Trace : 1. (I0, (num*num)$) Shift T® .(E) 2. (I0(I4, num*num)$) Shift T® .num 3. (I0(I4numI5, *num)$) Reduce F®num. 4. (I0(I4FI3, *num)$) Reduce T® F. 5. (I0(I4TI2, *num)$) … Poursuivre comme exercice … 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.). Contrairement à l’exemple précédent, cette fois-ci le conflit devrait être réglé en faveur de Shift. Pourquoi? Encore une fois, nous verrons plus tard comment résoudre ce conflit tenant compte du prochain token et de l’ensemble Follow. Cela nous mènera aux analyseurs SLR(1). IFT313 © Froduald Kabanza