Grammaires algébriques
Notes inspirées du cours de David Janin.
Exemple des expressions arithmétiques
Une grammaire engendrant les expressions arithmétiques peut s'écrire avec les règles suivantes :
1. exp -> ID
2. exp -> CSTE
3. exp -> exp + exp
4. exp -> exp * exp
5. exp -> (exp)
Ces 5 règles sont appelées les règles de grammaire ou règles de production. Dans ces règles, on distingue :
- les terminaux : ce sont les unités lexicales (aussi appelées tokens) données en entrée de l'analyseur. L'entrée est donc une suite (un mot) de terminaux, élément de ;
- les non-terminaux : ce sont les ensembles de mots de terminaux qu'on souhaite construire ou définir ;
- les règles de production, qui peuvent être lues comme des inclusions ensemblistes entre parties de (par exemple ).
Théorème : ce système d'inéquations admet une plus petite solution.
Idée de preuve : soit , et plus généralement pour , avec le nombre de non-terminaux. Le constat est que est croissante, c'est-à-dire que si , alors . Il est alors facile de vérifier que
En posant (et comme commute avec les unions de suites croissantes), on a : on dit que est un point fixe de , et c'est le plus petit de ces points fixes. En effet, si est un point fixe de , on a
mais comme , on a, par récurrence, et donc .
Exemple d'utilisation de cette grammaire
Analyse syntaxique de : l'analyseur lexical reconnaît 2 comme
une constante, comme *, comme un ID, comme + et comme
un ID. Comment vérifier que cette expression appartient bien au langage ?
Vérifions que CSTE * ID + ID est bien une entrée syntaxiquement correcte selon
la grammaire.
Suite de dérivations (gauche) de
exp => exp + exp (3)
=> exp * exp + exp (4)
=> CSTE * exp + exp (2)
=> CSTE * ID + exp (1)
=> CSTE * ID + ID (1)
Remarques
- Dès qu'un terminal est engendré par une règle, on ne peut plus l'effacer.
- Il coupe implicitement l'entrée entre ce qui se trouve à sa gauche et ce qui se trouve à sa droite.
En compilation, on peut avoir l'impression que cette suite de dérivations
induit syntaxiquement une « priorité » sur les opérateurs binaires + et * :
Est-ce satisfaisant ? Presque... Expliciter la syntaxe, c'est bien. Mais cette
grammaire est ambiguë : il y a au moins deux structures syntaxiques possibles
pour CSTE * ID + ID.
exp => exp * exp (4)
=> CSTE * exp (2)
=> CSTE * exp + exp (3)
=> CSTE * ID + exp (1)
=> CSTE * ID + ID (1)
C'est toujours une suite de dérivations gauche, mais la structure syntaxique induite est plutôt .
Définition : une grammaire est dite ambiguë lorsqu'il existe deux suites de
dérivations gauches distinctes, à partir de l'axiome (start symbol), qui
produisent la même entrée.
En compilation, on ne veut pas cela ! En effet, on va faire de la traduction dirigée par la syntaxe : deux syntaxes possibles donneront deux codes différents. Exemple : . En compilation, on n'utilise que des grammaires non ambiguës.
Arbre de dérivation
Sur le même exemple .
Un arbre de dérivation est aussi appelé arbre de syntaxe concrète. C'est un arbre dont :
- les sommets sont étiquetés sur ;
- les embranchements sont définis par des règles de production ;
- les feuilles sont étiquetées sur ;
- la racine est étiquetée par l'axiome (start symbol).
Remarque
Il y a une bijection entre arbres de dérivation et suites de dérivations gauches. Les suites de dérivations gauches correspondent exactement aux parcours en profondeur, de gauche à droite et préfixes, des arbres de dérivation.
Construire un arbre de dérivation pour ID ID + est impossible :
.
Définition : une grammaire est ambiguë lorsqu'il existe deux arbres de
dérivation pour une même entrée (un même mot de terminaux).
La grammaire ci-dessus, quoique ambiguë, a tout de même le mérite de définir correctement les expressions arithmétiques. Cependant, ces expressions peuvent-elles être définies par une grammaire non ambiguë ?
Une première grammaire non ambiguë
Idée : distinguer dans les expressions arithmétiques les termes qui composent les sommes et les facteurs qui composent les produits. Autrement dit, nous allons utiliser 3 non-terminaux :
- : expressions arithmétiques ;
- : termes dans une somme ;
- : facteurs dans un produit.
On a donc , avec comme axiome. Les règles de cette grammaire :
1. exp -> term
2. exp -> exp + term
3. term -> fact
4. term -> term * fact
5. fact -> ID
6. fact -> CSTE
7. fact -> (exp)
Pas d'ambiguïté ? Nous voulons toujours dériver , c'est-à-dire
CSTE * ID + ID.
L'arbre obtenu est le seul arbre de dérivation possible ! Le parenthésage induit par cet arbre est bien . Nous avons donc notre grammaire pour la compilation : elle s'appelle la grammaire ETF. Une autre grammaire, obtenue en éliminant la récursivité gauche (adaptée à l'analyse descendante LL(1)) :
- (mot vide)
- (mot vide)
On peut vérifier que cette grammaire engendre les mêmes expressions arithmétiques et qu'elle n'est pas ambiguë.