Analyse Syntaxique ascendante |
- Définition d'analyse ascendante LR
- Définition de poignée et préfixe viable
- Structure d'un analyseur ascendant
- Définition et construction des Items LR(k)
- Définition et construction des tables LR(k)
- Un exemple LR(0)
- Un exemple LR(1)
- Construction des tables SLR
- Construction des tables LALR(1)
Analyse ascendante
Cette méthode cherche a construire une dérivation droite en la reconstituant à
l'envers à partir de la chaîne de terminaux vers le symbole initial, ou de façon
équivalente, cherche à construire un arbre de dérivation a partir des feuilles
selon un parcours inverse d'un parcours en profondeur d'abord de gauche a
droite.
Pour savoir simplement quand l'analyse s' arrête avec succès pour une
grammaire G, on travaille toujours sur une grammaire dite augmentés
G' qui est G avec un nouveau symbole S' comme symbole de départ, un
nouveau symbole terminal $ et une transition supplémentaire
Terminologie
- des lettres majuscules
- indiquent des symboles non-terminaux
- des lettres grecques
- indiquent des séquences de symboles terminaux ou non-terminaux
- des lettres minuscules
- indiquent des symboles terminaux
- des lettres minuscules
- indiquent des séquences de symboles terminaux
Définitions techniques
On notera une étape de dérivation droite entre et , et on notera une série (même vide) d'étapes de dérivation droite entre et .
On parle de protophrase droite (resp. gauche) lorsque cette séquence peut apparaître dans une dérivation droite (resp. gauche) de G.
Définitions techniques, suite
, et que cette production doit être appliquée à en position n pour construire la protophrase précédente dans une dérivation droite á partir de S vers avec la grammaire G.
, où
est une protophrase droite de G est est une poignée dans cette protophrase.
Autrement dit, un préfixe viable est un préfixe d'une protophrase , mais qui ne s' étend pas plus à droite d'une poignée de .
Un exemple
Sur la grammaire augmentée
l'unique dérivation droite pour est la suivante:
Chaque ligne est une protophrase droite, en surligné les symboles produits, et en souligne les poignées.
Les préfixes viables sont les préfixes qui ne s'étendent pas plus loin qu'une poignée. Par exemple, sur la protophrase ce sont .
Définitions techniques, fin
contient les préfixes de longueur k des séquences de non terminaux de longueur au moins kdérivables à partir de dans G, et les séquences de non terminaux de longueur inférieur à k dérivables depuis .
Exemples
Pour la grammaire:
on a:
- FIRST2(S)
- =
- EFF2(S)
- =
Grammaires LR(k)
On peut maintenant donner la définition formelle de la classe des grammaires LR(k).
- FIRSTk(w) = FIRSTk(y)
Structure de l'analyseur
Les grammaires LR(k) sont celles dont le langage est reconnu par un analyseur déterministe LR(k). Cet analyseur utilise une pile et le flot d'entrée, qui décrivent une configuration de l'analyseur, notée
où les X sont de symboles, terminaux ou non terminaux, stoqués sur la pile, alors que les a sont seulement des symboles terminaux, et correspondent aux terminaux non encore lus sur le flot d'entrée.
L'analyseur travaille en effectuant quatre actions possibles:
- shift (décalage)
- on transfert le terminal ai du flot d'entrée vers la pile
- reduce (réduction)
- on reconnaît sur le sommet de la pile une partie droite d'une production , on l'enlève et on la remplace par sa partie gauche Y
- erreur
- l'analyseur s'arrête et signale un erreur
- accept
- l'analyseur s'arrête et signale que la phrase a été reconnue
Un exemple
Sur la grammaire augmentée
Une possible séquence de reconnaissance pour pour un analyseur ascendant serait:
Remarques Importantes
Configurations et protophrases:
la concatenation de la partie gauche
et droite d'une configuration d'un analyseur ascendant pour une grammaire Gest toujours une protophrase droite de G (si l'analyse se termine avec
succès).
Préfixes viables:
un préfixe viable peut toujours se compléter en
une protophrase droite. En d'autre termes, il n'y a pas d'erreur au cours de
l'analyse tant que l'on a sur la pile un préfixe viable.
Un autre exemple, avec look-ahead
Sur la grammaire augmentée
Une possible séquence de reconnaissance pour pour un analyseur ascendant serait:
Analyseurs LR
Un analyseur LR est composé de- une pile et un flot d'entrée
- comme vu dans les exemples
- une table d'analyse
- qui décrit un automate à états finis
augmenté avec des actions á effectuer éventuellement sur
la pile (shift, reduce, accept, error)
Fonctionnement d'un analyseur LR
Sur un état d'analyseur le fonctionnement de l'analyseur LR est le suivant:- exécuter l'automate à partir de l'état initial s1 sur la pile , ce qui nous laisse sur un état sk
- exécuter l'action décrite dans la table d'analyse associée au symbole
terminal x en entrée pour l'état sk
- shift
- (noté s) déplacer le symbole d'entrée x sur la pile,
- reduce n
- (noté rn) sur le sommet de la pile il y a la partie gauche de la règle numéro n, disons ; dépiler et empiler X
- accept
- (noté a) arrêter avec succès
- error
- (noté par une case vide) arrêter sur erreur
- recommencer avec le nouvel état d'analyseur
Exemple d'exécution avec une table d'analyse LALR(1)
\end{displaymath} -->
Ici on a marqué en bas les états de l'automate après lecture de chaque symbole sur la pile.
Analyseur avec états sur la pile
Si on garde les états sur la pile, en modifiant la notion de configuration pour que chaque symbole soit suivi par un état, on peut éviter de relire toute la pile à chaque fois: sur une configuration d'analyseur le fonctionnement de l'analyseur LR est le suivant:- exécuter l'action décrite dans la table d'analyse associée au symbole
terminal x en entrée pour l'état sk
- shift k
- déplacer le symbole d'entrée x sur la pile, et empiler l'état numéro k
- reduce n
- sur le sommet de la pile il y a la partie gauche de la règle numéro n, disons ; dépiler et tous les états associés, en découvrant l'état s'; empiler X et l'état s'' contenu dans la table á la ligne s', colonne X
- accept
- arrêter avec succès
- error
- arrêter sur erreur
- recommencer avec le nouvel état d'analyseur
Comment produire une table d'analyse?
Il faut savoir reconnaître les préfixes viables, et savoir déterminer quelles productions utiliser pour les réductions, éventuellement en utilisant k tokens en entrée pour aider dans la décision.Pour reconnaître les préfixes viables, on définit d'abord
de G plus une position j dans
et une
séquence w de longueur .
Cela est noté, si
avec jla longueur de
sauf dans le cas LR(0) pour lequel on écrit simplement
Reconnaître les préfixes viables: la fermeture
Si on a , i.e. on a a déjà vu en entrée le préfixe et on attende une séquence dérivable à partir de , on est aussi en condition d'attendre une séquence dérivable depuis X, suivie d'une séquence dérivable depuis . C'est cela que capture la notion suivante de fermeture
Fermeture(I) =
répéter tant que I grandit
pour tout item dans I
pour toute production pour tout retourner I
Reconnaître les préfixes viables: GOTO
Supposons d'avoir
,
pour un symbole
terminal ou non terminal X: on a donc déjà vu en entrée le préfixe
et on attende une
séquence dérivable à partir de .
Si maintenant l'on reconnaît X,
alors on a vu
et on attende une séquence dérivable à partir de .
C'est cela que capture la notion suivante de GOTO
Goto(I,X) =
pour tout item dans I
retourner Fermeture(J)
L'automate qui reconnaît les préfixes viables
Soit G un grammaire augmentée, et soit la collection d'ensembles d'ITEMS LR(k) attaignables depuis la fermeture de l'item par la fonction GOTO.On peut alors construire l'automate à état fini suivant:
- états
- avec s0 état initial et comme états finaux ceux qui contiennent au moins un ITEM LR(k) avec le point au fond à droite (i.e. de la forme )
- transitions
- on a une transition de l'état si vers l'état sj sur le symbole X si GOTO(si,X)=sj
La construction de la table LR(k)
Soit G un grammaire augmentée, pour laquelle on a construit l'automate.La table d'analyse a une ligne par état et un colonne par séquence de symboles terminaux de longueur (le look-ahead) et une colonne par symbole terminal et non-terminal, que l'on remplit de la façon suivante:
Pour tout état ,
- on met dans la case si, u si et est la production numéro
- on met accept dans la case si
- on met shift dans la case si, u si et
- on laisse vide (i.e. on signale erreur) autrement
Comment l'analyseur peut-il choisir l'action à effectuer?
Un analyseur LR dispose de plus d'information qu'un analyseur LL pour déterminer la prochaine action.
Imaginons d'avoir en entrée une chaîne uvw, et d'avoir déjà lu u.
Pour déterminer la production à appliquer
- un analyseur LL(k) connaît u et FIRSTk(vw)
- un analyseur LR(k) connaît uv (en effet, il connaît un
préfixe viable
obtenu à partir de uv) et FIRSTk(w)
Un exemple LR(0)
La grammaire suivante est LR(0)Voici la construction complète de la table LR(0) de
États et transitions (2,4,6,7,9 sont terminaux)
- 1.
-
- 2.
- 3.
-
- 4.
- 5.
-
- 6.
- 7.
- 8.
-
- 9.
La table d'analyse LR(0)
Pour remplir la table LR(0), on écrit la table de transition de l'automate et on introduit les actions de décalage (s pour shift) comme décrit plus en haut.Pour les réductions (rk pour reduce avec la production k), n'ayant pas de look-ahead dans les états, on met rk dans toute la ligne action de l'état j si l'état j contient un ITEM LR(0) , et que est la production numéro j.
La table d'analyse LR(0), versions compacte
Remarque
Les générateurs d'analyseurs, comme Yacc, fusionnent les colonnes des actions et des transitions pour les terminaux: plutôt que d'avoir une case case (1,x) qui contient s(hift) pour les actions, et une case (1,x) qui contient 2pour les transitions, on préfère avoir une seule case (1,x) qui contient s2, pourl'action est un shift et la transition est vers l'état </FONT></FONT></FONT>2<FONT SIZE="+2"><FONT SIZE="+2"><FONT SIZE="+1">''.
<BR>
Dans ce cas, on écrit souvent </FONT></FONT></FONT><I>gk</I><FONT SIZE="+2"><FONT SIZE="+2"><FONT SIZE="+1"> dans les colonnes transitions restantes
(celles des non-terminaux). C'est une abréviation pour
goto k'',
plus lisible que juste k.
Un exemple LR(1) non LR(0)
La grammaire
est une grammaire LR(1) mais pas LR(0).
En effet, dans l'automate on a un problème pour l'état
.
Un exemple LR(1) non LR(0) (suite)
Donc dans la table d'analyse LR(0) on trouve un conflit shift/reduce dans la case 3,+
Les états LR(1) de
Regardons alors la construction LR(1), qui garde trace des look-aheads dans les états...États et transitions (2, 5 et 6 sont terminaux)
Comparons les automates LR(0) et LR(1) pour
La table d'analyse LR(1) de
On garde trace des look-ahead pour introduire dans la table les actions reduce, donc il y en a moins et le conflit disparaît!
La table d'analyse LR(1) de , version compacte
Comparons les tables LR(0) et LR(1) pour
Trouver sa place parmi les LR(k)
Comme nous venons de voir, la classe d'analyseurs LR(0) est trop faible pour traiter les langages de programmations: même le simple langage des expressions pose problème.
Les classes LR(2), LR(3), ... ont par contre une table d'analyse trop
grosse en pratique en raison du nombre de colonnes pour le ``look-ahead'': un
analyseur moderne utilise plusieurs dizaines de tokens, et une colonne pour
chaque séquence de token de longueur inférieur ou égale à k, pour est déraisonnable.
Exercice: combien de séquences de longueur inférieure ou égale à ky-at-il si on se donne n tokens différents?
Trouver sa place entre LR(0) et LR(1)
Heureusement, la classe LR(1) est largement suffisante pour les langages modernes, et la table n'a qu'une colonne par token.Mais là, c'est le nombre d'états qui grandit trop, en raisons de la présence de look-ahead dans les états qui départage des états qui sont très peu différents.
C'est pour cela que dans la pratique on utilise deux types d'analyseurs dont la
puissance est comprise entre celle de LR(0) et celle de LR(1): SLR et LALR(1)
Analyseurs SLR
- SLR
- (Simple LR) est un analyseur dont l'automate est celui de
LR(0), donc la partie transition est la même que LR(0), et les
actions de décalage aussi, mais la table d'analyse est construite de
une façon plus fine: on pallie à l'absence de look-ahead dans les
état avec l'information contenue dans les ensembles FOLLOW construits
à partir de la grammaire.
La règle de placement des réductions devient alors:
si l'état j contient un ITEM LR(0) , et que est la production numéro , on met rk dans toutes les cases (j,t) telles que .
SLR pour la grammaire G 2
L'automate SLR étant le même que celui LR(0), on ne le montrera pas à nouveau, mais maintenant la table SLR contiendra des entrées reduce seulement sur certains nonterminaux, pas tous! En particulier, on pourra éviter le conflit shift/reduce dans l'état .
Dans ce cas précis, SLR fait aussi bien que LR(1), avec bien moins d'effort.
Analyseurs LALR(1)
- LALR(1)
- (Look-Ahead LR(1)) est une classe d'analyseurs dont l'automate est obtenu de l'automate LR(1) en fusionnant les états qui diffèrent seulement par leur look-ahead.
On dit aussi que l'on fusionne les états ayant le même coeur, le
coeur d'un état étant l'ensemble des parties gauches des ITEMS LR(1) qu'il
contient, i.e. sans le look-ahead, i.e. des ITEMS LR(0).
Donc un analyseur LALR(1) a autant d'états qu'un LR(0) ou SLR.
Les analyseurs LALR(1) sont les plus utilisés parce que, même s'ils ont moins
d'états qu'un analyseur LR(1), il est très rare qu'on retrouve un conflit
dans la table LALR(1) quand il n'y en a pas dans la table LR(1).
En particulier, on peut prouver que si un analyseur LR(1) n'a pas de conflits
shift/reduce, l'analyseur LALR(1) n'en a pas non plus.
Par contre, on peut introduire des conflits reduce/reduce.
LALR(1) pour
Dans le cas précis de cette grammaire, l'automate LR(1) pour n'ayant pas d'états différents avec le même coeur, la table d'analyse LALR(1) de est la même que celle LR(1).
Mais la grammaire suivante, qui capture un sous-ensemble des expressions du langage C, est un exemple de grammaire LALR(1) qui n'est pas SLR et pour laquelle l'automate LALR(1) est plus petit que l'automate LR(1). |
LR préfère l'associativité à gauche
Contrairement à ce qui se passe dans le cas des analyseurs LL, dans l'analyse ascendante on a plutôt intérêt à utiliser des grammaires récursives à gauche.Considérons les analyseurs LR pour la grammaire récursive à droite
vus en cours: pour reconnaître
, ils empilent toute la suite
de symboles (en réduisant id sur T à chaque coup) avant de faire la prémière reduction
non triviale.
Par contre, la grammaire recursive à gauche
mantiendra la dimension de la pile à un minimum.
Utilisation de grammaires ambiguës
Une grammaire ambiguë n'est jamais LR(k), quelque soit k.Pourtant, on a intérêt à essayer d'utiliser une grammaire ambiguë, quitte à trafiquer l'automate LR(k), si on peut.
- efficacité
- dans une grammaire obténue par désambiguation, l'analyseur passe beaucoup de temps à reduire des productions triviales (comme dans l'exemple précédent), dont le seul but était d'expliciter dans la grammaire les priorités entre opérateurs et leur associativité droite ou gauche.
- praticité
- si on peut décrire de façon concise ces priorités entre opérateurs et leur associativité droite ou gauche, sans toucher à la grammaire, on obtient une description plus modulaire du langage qui nous intéresse.
Exemple
Considérons la grammaire (ambiguë) suivante:\end{displaymath} -->
et sa table d'analyse SLR.
Voyons comment les nombreux conflits apparents peuvent s'expliquer
en terme d'associativité et précedence d'opérateurs, que l'on
peut résoudre en travaillant directement sur les entrées de la table...
(fait au tableau, pas dans les notes... si un ame gentille veut tout taper...)
About this document ...
This document was generated using the LaTeX2HTML translator Version 98.1p1 release (March 2nd, 1998)
Copyright © 1993, 1994, 1995, 1996, 1997, Nikos Drakos, Computer Based Learning Unit, University of Leeds.
The command line arguments were:
latex2html -split 0 Slides01.
The translation was initiated by Roberto Di Cosmo on 1999-11-24
Roberto Di Cosmo
1999-11-24