Dérivation
Qualités des grammaires
Ambigüités
Récursivité à gauche
Factorisation à gauche
Analyseurs descendants
Principe
Analyse par descente récursive
Construction de la table d’analyse
1
Chapitre 3: Analyse syntaxique descendante
Généralités:
Le langage décrit par une grammaire régulière peut être reconnu par un automate fini,
et réciproquement.
les constructions des langages de programmation peuvent être définies par des
grammaires non contextuelles.
2
Une grammaire non contextuelle, on dit parfois grammaire BNF (pour Backus-Naur
form), est un quadruplet G = (T , V, S0, P) formé de :
S → S1S2 … Sk avec S V et Si V T
3
Exemple: grammaire des expressions arithmétiques simples:
E → E A E | (E ) | -E| id
A→+|-|*|/ 4
Dérivation
Définition : la dérivation est la processus par lequel une grammaire définit un langage
A
5
Si n on dit que se dérive en n en n étapes, et on écrit
n
n
6
Soit G = (T , V, S0, P) une grammaire non contextuelle; le langage engendré par G est
l’ensemble des chaînes de symboles terminaux qui dérivent de S 0 :
* *
L(G)={ w ∈T /S 0 ⇒ w }
Une proto-phrase dont tous les symboles sont terminaux est une phrase.
7
Par exemple, soit la grammaire :
et considérons la chaîne "60 * vitesse + 200" qui, une fois lue par l'analyseur lexical,
se présente ainsi :
On peut définir de même une dérivation droite, où à chaque étape c'est le non-terminal
le plus à droite qui est réécrit.
9
Arbre de dérivation
10
Qualités des grammaires
*
pour prouver que w L(G) il faut exhiber une dérivation S0 ===>w,
c'est-à-dire construire un arbre de dérivation dont la liste des feuilles est w.
Grammaires ambigües: Une grammaire est ambigüe s'il existe plusieurs dérivations
gauches différentes pour une même chaîne de terminaux.
Exemple:
E→E+E
E→E*E
E→F
F → nombre
F → identificateur
F → (E)
11
E→E+E
E→E*E
E→F
F → nb
F → id
F → (E)
E → E+E → E * E + E → F * E + E E → E*E → F * E → nb * E
→ nb * E + E → nb * F + E→ nb * nb + E → nb * E + E → nb * F + E→ nb * nb + E
→ nb * nb + F→ nb * nb + nb → nb * nb + F→ nb * nb + nb
12
E→E+E E→E+T
E→E*E E→T
E→F T→T*F
F → nb → T→F
F → id F → nb
F → (E) F → id
F → (E)
E → E+T → T +T → T * F + T
→ F * F + T → nb * F + T→ nb * nb + T
→ nb * nb + F→ nb * nb + nb
13
Grammaires récursives à gauche.
Une grammaire est récursive à gauche s'il existe un non-terminal A et une dérivation
de la forme
A Aα
Oùest une chaîne quelconque.
Cas particulier, on dit qu'on a une récursivité à gauche simple si la grammaire possède
une production de la forme
A Aα
14
Suppression de la récursivité à gauche
En appliquant ce procédé à :
On obtient:
15
Factorisation à gauche
On souhaite écrire des analyseurs prédictifs
Exemple:
Instr_si ---> si expr alors instr
| si expr alors instr sinon instr
Etant donnée une grammaire G = (T , V, S0, P), analyser une chaine de symboles terminaux
*
w T* revient à construire un arbre de dérivation (en préordre) prouvant que S0 ===>w.
Les grammaires des langages qu’on veut analyser par analyse descendante ont un ensemble
de propriétés dit LL(1):
i.e. qu’on peut écrire des analyseurs:
- dans lesquels un seul symbole de la chaîne source est accessible à chaque instant et
permet de choisir, lorsque c’est nécessaire, une production parmi plusieurs candidates (1)
Initialisation
au départ la pile contient l’axiome et la fenêtre montre le premier symbole terminal
de la chaîne à analyser.
Itération:
Tant que la pile n’est pas vide, répéter les opérations suivantes:
- si le symbole au sommet de la pile est un terminal , alors
si le terminal visible à la fenêtre de lecture est alors
- dépiler le symbole au sommet de la pile
- lire le prochain symbole
- sinon signaler une erreur
- si le symbole au sommet de la pile est un non terminal S alors
- s’il y a une seule production S---> S1S2…SK alors dépiler S et empiler SKSk-1…S1 avec S1 au sommet
- s’il y a plusieurs production ayant S pour membre gauche alors
d’après le terminal visible à la fenêtre, sans faire avancer ce dernier, choisir l’unique production
S---> S1S2…SK qui peut convenir et empiler SKSk-1…S1 avec S1 au sommet
(nombre * identificateur + nombre)
fenêtre pile
nombre expression
nombre terme fin_expression
nombre facteur fin_terme fin_expression
nombre nombre fin_terme fin_expression
‘’*’’ fin_terme fin_expression
‘’*’’ * facteur fin_terme fin_expression
identificateur facteur fin_terme fin_expression
identificateur identificateur fin_terme fin_expression
‘’+’’ fin_terme fin_expression
‘’+’’ fin_expression
‘’+’’ fin_expression
‘’+’’ ‘’+’’ terme fin_expression
nombre terme fin_expression
nombre facteur fin_terme fin_expression
nombre nombre fin_terme fin_expression
¶ fin_terme fin_expression
¶ fin_expression
¶ fin_expression
20
¶
reste une question d'importance : « comment choisir l’unique production
S---> S1S2…SK qui peut convenir » ?
l'idée est simple : pour décider si on réalise l'expansion X→β lorsque le premier
caractère de l'entrée est c, on va chercher à déterminer si c fait partie des premiers
caractères des mots reconnus par β
une difficulté se pose pour une production telle que X→ε, et il faut alors considérer aussi
l'ensemble des caractères qui peuvent suivre X
21
Définitions
Définition (null)
Soit α ∈(T U V )* . null(α) est vrai si et seulement si on peut dériver à partir de α i.e.
*
α
Définition premier (first)
Soit α ∈(T U V )* . first(α) est l'ensemble de tous les premiers terminaux des mots dérivés
de α, i.e.
{ a∈T / ∃w et α →aw }
Définition suivant (follow)
Soit X ∈V . follow(X) est l'ensemble de tous les terminaux qui peuvent apparaître après X
dans une dérivation, i.e.
*
{ a∈T / ∃u , w et S → uXaw }
22
pour calculer null() il suffit de déterminer null(X) pour X V
23
pour calculer les null(Xi ), on part donc de null(X1) = false, . . .,null(Xn) = false
et on applique les équations jusqu'a obtention du point fixe.
24
de même, les équations définissant first() sont mutuellement récursives
first ( X ) first ( )
X
et
first (a ) a
first ( X ) first ( X ), si NULL( X )
first ( X ) first ( X ) first ( ), si NULL ( X )
25
les équations définissant follow() sont aussi mutuellement récursives
note : il faut introduite # dans les suivants du symbole de départ (ce que l'on peut faire
directement, ou en ajoutant une règle ) S → E #
26
Application : construction de la table d'expansion
27
Analyse par descente récursive
Un analyseur par descente récursive est un type d’analyseur dans lequel le programme de
l’analyseur est étroitement liés à la grammaire analysée.
1 – chaque groupe de productions ayant le même membre gauche S donne lieu à une
fonction
void reconnaitre_S(void) ou simplement void S(void)
3 – Une séquence de symboles S1S2…Sn dans le membre droit d’une production donne lieu,
dans la fonction correspondante, à une séquence d’instructions traduisant les actions
« reconnaissance de S1 », « reconnaissance de S2 », … « reconnaissance de Sn » .
28
4- si S est un symbole non terminal, l’action « reconnaissance de S » se réduit à l’appel
de fonction
reconnaitre_S()
L’ensemble des fonctions écrites suivant les prescriptions ci-dessus forme l’analyseur du
langage considéré
29
30
remarques
- la table d'expansion n'est pas explicite : elle est dans le code de chaque fonction.
- la pile non plus n'est pas explicite : elle est réalisée par la pile d'appels.
31