IFT313 Introduction aux langages formels Froduald Kabanza Département d’informatique Université de Sherbrooke planiart.usherbrooke.ca/kabanza/cours/ift313 Analyseurs LR (1) et LALR(1)
Sujets Éléments LR(1) AFD LR(1). Table d’analyse LR(1). AFD LALR(1) Table d’analyse LALR(1). Hiérarchie des grammaires hors-contexte IFT313 © Froduald Kabanza
Objectifs Pouvoir définir ce qu’un élément LR(1). Pouvoir calculer l’AFD LR(1). Pouvoir construire la table d’analyse LR(1). Pouvoir reconnaître des grammaires LR(1). Pouvoir calculer l’AFD LALR(1) Pouvoir construire la table d’analyse LALR(1). Pouvoir reconnaître des grammaires LALR(1). Comprendre les forces relatives des analyses LR(0), SLR(1), LR(1) et LALR(1). Connaître l’hiérarchie entre les différentes classes de grammaires hors-contexte : LL(0), LL(1), LR(0), LR(1), SLR(1), LALR(1). IFT313 © Froduald Kabanza
Références [1] Sudkamp, T. A.. Languages and Machines. Third Edition Edition. Addison-Wesley, 2005. 20.5 [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.7 IFT313 © Froduald Kabanza
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. IFT313 © Froduald Kabanza
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®a { pop 2x|a| 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®a ; if action[i,a] = accept return true; error(); IFT313 © Froduald Kabanza
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 . ab, 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 ® a . est dans Ii , avec A différent de S’, ajoute reduce A ® 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, $]. IFT313 © Froduald Kabanza
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). IFT313 © Froduald Kabanza
Exemple Grammaire illustrant l’accès au contenu d’une adresse donnée par un pointeur dans le langage C : 0. S’® S 1. S ® L = R 2. S ® R 3. L ® * R 4. L ® id 5. 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. En C, la “l-value” désigne une valeur qui peut être à droite du signe d’égalité (=): “l” pour “memory locator”. Souvent aussi “left value” justement parce que ça peut être à gauche du signe d’égalité; la r-value est ce qui peut être à droite du signe d’égalité. Les l-values sont souvent des identificateurs (de variables), mais ça peut être aussi des entrées de tableaux, des accès à des champs de structures de données, etc. Voir http://msdn.microsoft.com/en-us/library/bkbs2cds.aspx ou http://msdn.microsoft.com/en-us/library/f90831hc(v=vs.80).aspx IFT313 © Froduald Kabanza
Table SLR(1) AFD LR(0) conflit shift/reduce * = $ S R L s5 id 1 2 4 3 1 2 4 3 7 5 9 8 6 actions / goto goto r4 r3 r1 r2 s6 r5 acc s4 r5 conflit shift/reduce S’® .S S® .L=R S® . R L®.*R L® .id R® .L I6 S® L=.R 4 L®*.R 2 S® L.=R R® L. 9 S® L=R. I7 L®*R. 5 L® id. I1 S’® S. 8 3 S® R. S R L = id * 1. S ® L = R 2. S ® R 3. L ® * R 4. L ® id 5. R ® L IFT313 © Froduald Kabanza
Simulation Conflit * = $ S R L s5 id 1 2 4 3 7 5 9 8 6 actions / goto 1 2 4 3 7 5 9 8 6 actions / goto goto r4 r3 r1 r2 s6 r5 acc s4 r5 1. S ® L = R 2. S ® R 3. L ® * R 4. L ® id 5. R ® L (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 $) conflit shift/reduce shift reduce (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 Nous avons vu que pour un automate LR, pour qu’une exécution mène à l’acceptation, il faut qu’ à tout moment, le contenu de la pile concaténé avec le reste de la chaîne à lire, soit une forme sententielle pour la dérivation la plus à droite. Avec le conflit shift/reduce précédent, la réduction viole la propriété précédente : “R = id” n’est pas une forme sententielle droite; on ne peut pas avoir : * * S => R = id => * id = id Par contre, le shift est en accord avec la propriété : “L = id” est une forme sententielle droite; on peut avoir, S => L = id => * id = id 2 S® L.=R R® L. Conflit IFT313 © Froduald Kabanza
De SLR(1) à LR(1) Dans SLR(1) lorsque l’état au sommet de la pile contient l’élément A ® a . et que le prochain token est dans Follow(A), on fait une réduction de a 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 ® a pour laquelle on a l’élément A ® a . On ne devrait pas regarder juste A, mais le préfixe viable sur la pile (dA) pour s’assurer que dA 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. IFT313 © Froduald Kabanza
É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 . b , a ]. Comme pour les éléments LR(0), cela signifie que nous avons a au sommet de la pile, et on s’attend à lire un préfixe du reste de l’entrée dérivé de b. En plus, le “a” signifie que le token “a” peut suivre A dans une forme sentientielle droite, obtenue en utilisant la production A ® a b : S => xAy => xa b y et a est dans First(y). * * IFT313 © Froduald Kabanza
AFD LR(1) En d’autres mots, un élément [A ® a . b , a ] signifie que : Le reste de l’entrée débute par un préfixe dérivable de ba Après la réduction de ab 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). IFT313 © Froduald Kabanza
Algorithme Closure Algorithm Closure(I) do { pour chaque élément [A ® a . Xb , a ] dans I chaque production X ®g dans la grammaire et chaque terminal b dans First(ba) tel que [X ® . g , b ] n’est pas dans I ajouter [X ® . g , b ] à I // b pourrait suivre X dans une dérivation droite } while (des éléments nouveaux sont ajoutés dans I) return I IFT313 © Froduald Kabanza
Algorithmes goto et etats Algorithm goto(I,X) soit J l’ensemble d’éléments [A ® aX . b , a ] tel que [A ® a X . b , 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 IFT313 © Froduald Kabanza
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 . ab, b] for a terminal a, is in Ii and goto(I , a) = Ij , set action[i, a] to ‘shift j’ b. If an item [A ® a . , a] is in Ii , where A is different from S’, set action[i, a] to ‘reduce A ® a ’ c. If item [S’ ® S . , $] is in Ii , set action[i, $] to ‘accept ’. i IFT313 © Froduald Kabanza
Générer la table d’analyse LR(1), suite 3. If goto(Ii, a) = Ij for a nonterminal A set goto[i, A] = j; 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). . IFT313 © Froduald Kabanza
Rappel : AFD LR(0) Table SLR(1) * = $ S R L s5 id 1 2 4 3 7 5 9 8 6 actions / goto goto r4 r3 r1 r2 s6 r5 acc s4 r5 conflit shift/reduce S’® .S S® .L=R S® . R L®.*R L® .id R® .L I6 S® L=.R 4 L®*.R 2 S® L.=R R® L. 9 S® L=R. I7 L®*R. 5 L® id. I1 S’® S. 8 3 S® R. S R L = id * 1. S ® L = R 2. S ® R 3. L ® * R 4. L ® id 5. R ® L IFT313 © Froduald Kabanza
Exemple 1 : AFD LR(1) Le conflit dans l’état 2 a disparu 2 S® L.=R, $ 2. S ® R 3. L ® * R 4. L ® id 5. R ® L 1 S’® S. , $ = 3 S® L=.R, $ R® .L, $ L®.*R, $ L® .id, $ L S L 4 S® R., $ S’® .S, $ S® .L=R, $ S® . R, $ L®.*R, $/= L® .id, $/= R® .L , $ R id R * 6 R® L., $ 8 S® L=R., $ id L 7 L® id., $/= * 12 L®*.R, $ R® .L, $ L®.*R, $ L® .id, $ * id 10 L® id., $ * id 5 L®*.R, $/= R® .L, $/= L®.*R, $/= L® .id, $/= Le conflit dans l’état 2 a disparu L R 11 R® L., $/= R 13 L®*R., $ 9 L®*R., $/= IFT313 © Froduald Kabanza
Table LR(1) S’® .S, $ S® .L=R, $ S® . R, $ L®.*R, $/= L® .id, $/= S’® .S, $ S® .L=R, $ S® . R, $ L®.*R, $/= L® .id, $/= R® .L , $ 3 S® L=.R, $ R® .L, $ L®.*R, $ L® .id, $ 5 L®*.R, $/= R® .L, $/= L®.*R, $/= L® .id, $/= 2 S® L.=R, $ R® L., $ 13 L®*R., $ 7 L® id., $/= 1 S’® S. , $ 6 R® L., $ S R L = id * 12 L®*.R, $ R® .L, $ L®.*R, $ L® .id, $ 4 S® R., $ 10 L® id., $ 11 R® L., $/= * = $ S R L s7 id 1 2 4 3 7 5 9 8 6 actions / goto goto r1 11 r5 s3 acc r4 s5 s10 s12 r3 r2 10 12 13 1 2 3 4 5 8 S® L=R., $ 9 L®*R., $/= IFT313 © Froduald Kabanza
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) IFT313 © Froduald Kabanza
Exemple 2/2 1. S® CC 2. C® cC 3. C® d d $ S C s3 c 1 2 4 3 7 5 9 8 6 1 2 4 3 7 5 9 8 6 actions goto r3 r1 s6 acc s7 r2 s4 S’® .S, $ S® .CC, $ C® . cC, c/d C®.d, c/d 1 S’® S., $ S 5 S® CC., $ C 2 S® C.C, $ C® . cC, $ C®.d, $ d c C c c 6 C® c.C, $ C® . cC, $ C®.d, $ d c d 3 C® c.C, c/d C® . cC, c/d C®.d, c/d 7 C® d., $ C C d 9 C® cC., $ 8 C® cC., c/d 1. S® CC 2. C® cC 3. C® d 4 C® d., c/d IFT313 © Froduald Kabanza
Analyse LALR (1) IFT313 © Froduald Kabanza
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. IFT313 © Froduald Kabanza
Exemple 1 : AFD LR(1) S’® .S, $ S® .L=R, $ S® . R, $ L®.*R, $/= S’® .S, $ S® .L=R, $ S® . R, $ L®.*R, $/= L® .id, $/= R® .L , $ 3 S® L=.R, $ R® .L, $ L®.*R, $ L® .id, $ 5 L®*.R, $/= R® .L, $/= L®.*R, $/= L® .id, $/= 2 S® L.=R, $ R® L., $ 8 S® L=R., $ 13 L®*R., $ 7 L® id., $/= 1 S’® S. , $ 6 R® L., $ S R L = id * 12 L®*.R, $ R® .L, $ L®.*R, $ L® .id, $ 4 S® R., $ 10 L® id., $ 11 R® L., $/= 9 L®*R., $/= 1. S ® L = R 2. S ® R 3. L ® * R 4. L ® id 5. R ® L IFT313 © Froduald Kabanza
AFD LALR(1) et Table d’analyse S’® .S, $ S® .L=R, $ S® . R, $ L®.*R, $/= L® .id, $/= R® .L , $ 3 S® L=.R, $ R® .L, $ L®.*R, $ L® .id, $ 5 L®*.R, $/= R® .L, $/= L®.*R, $/= L® .id, $/= 2 S® L.=R, $ R® L., $ 8 S® L=R., $ 7 L® id., $/= 1 S’® S. , $ 6 R® L., $/= S L = R id * 4 S® R., $ 9 L®*R., $/= * = $ S R L s7 id 1 2 4 3 7 5 9 8 6 actions / goto goto r5 r1 s3 acc r4 s5 r3 r2 IFT313 © Froduald Kabanza
Exemple 2 1. S® CC 2. C® cC 3. C® d d $ S C s3 c 1 2 4 3 7 5 9 8 6 1 2 4 3 7 5 9 8 6 actions goto r3 r1 s6 acc s7 r2 s4 S’® .S, $ S® .CC, $ C® . cC, c/d C®.d, c/d 1 S’® S., $ S 5 S® CC., $ C 2 S® C.C, $ C® . cC, $ C®.d, $ c c C 6 C® c.C, $ C® . cC, $ C®.d, $ c d c d 3 C® c.C, c/d C® . cC, c/d C®.d, c/d 7 C® d., $ d C C 9 C® cC., $ d 8 C® cC., c/d 1. S® CC 2. C® cC 3. C® d 4 C® d., c/d IFT313 © Froduald Kabanza
Exemple 2 1. S® CC 2. C® cC 3. C® d d $ S C s3 c 1 2 4 3 5 6 actions 1 2 4 3 5 6 actions goto r3 r1 acc s4 r2 S’® .S, $ S® .CC, $ C® . cC, c/d C®.d, c/d 2 S® C.C, $ C® . cC, $ C®.d, $ 3 C® c.C, c/d/$ C® . cC, c/d/$ C®.d, c/d/$ 4 C® d., c/d/$ 1 S’® S., $ 5 S® CC., $ 6 C® cC., c/d/$ S C c d 1. S® CC 2. C® cC 3. C® d IFT313 © Froduald Kabanza
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. . IFT313 © Froduald Kabanza
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. . IFT313 © Froduald Kabanza
Hiérarchie des grammaires hors-contexte Grammaires non ambigües Grammaires ambigües LL(0) LR(0) SLR (1) LALR (1) LL(k) LL(1) LR(1) LR(k) IFT313 © Froduald Kabanza