• Aucun résultat trouvé

Analyse Syntaxique

6.6 Analyse ascendante

else

Analyse := false;

end;

6.6 Analyse ascendante

Les algorithmes d'analyse ascendante sont souvent plus compliques que ceux de l'ana-lyse descendante. Ils s'appliquent toutefois a un beaucoup plus grand nombre de gram-maires. C'est pour cette raison qu'ils sont tres souvent utilises. Ils sont ainsi a la base du systeme yacc qui sert a ecrire des compilateurs sous le systeme Unix. Rappelons que l'analyse ascendante consiste a retrouver la derivation

S0!u1 !u2:::un;1 !un=f

en commencant parun;1 puisun;2et ainsi de suite jusqu'a remonter a l'axiomeS0. On eectue ainsi ce que l'on appelle des reductions car il s'agit de remplacer un membre droit d'une regle par le membre gauche correspondant, celui-ci est en general plus court.

Un exemple de langage qui n'admet pas d'analyse syntaxique descendante simple, mais sur lequel on peut eectuer une analyse ascendante est le langage des systemes de parentheses. Rappelons sa grammaire:

S !aSbS S!aSb S !abS S !ab

On voit bien que les regles S ! aSbS et S ! aSbpeuvent engendrer des mots ayant un facteur gauche commun arbitrairement long, ce qui interdit tout algorithme de type LL(k). Cependant, nous allons donner un algorithme simple d'analyse ascendante d'un mot f.

Partons de f et commencons par tenter de retrouver la derniere derivation, celle qui a donnef =un a partir d'un motun;1. Necessairementun;1 contenait unS qui a ete remplace par ab pour donnerf. L'operation inverse consiste donc a remplacer un ab parS, mais ceci ne peut pas ^etre eectue n'importe ou dans le mot, ainsi si on a

f =ababab il y a trois remplacements possibles donnant

Sabab; abSab; ababS

Les deux premiers ne permettent pas de poursuivre l'analyse. En revanche, a partir du troisieme, on retrouve abSet nalementS. D'une maniere generale on remplaceabpar S chaque fois qu'il est suivi de b ou qu'il est situe en n de mot. Les autres regles de grammaires s'inversent aussi pour donner des regles d'analyse syntaxique. Ainsi:

ReduireaSben S s'il est suivi deb ou s'il est situe en n de mot.

Reduireaben S s'il est suivi deb ou s'il est situe en n de mot.

ReduireabS en S quelle que soit sa position.

ReduireaSbS en S quelle que soit sa position.

On a un algorithme du m^eme type pour l'analyse des expressions arithmetiques inxes engendrees par la grammaire:

E !T T !F F !a

E !E+T T !T F F !(E)

E !E;T

Cet algorithme tient compte pour eectuer une reduction de la premiere lettre qui suit le facteur que l'on envisage de reduire (et de ce qui se trouve a gauche de ce facteur). On dit que la grammaire est LR(1). La theorie complete de ces grammaires meriterait un plus long developpement. Nous nous contentons de donner ici ce qu'on appelle l'automate LR(1) qui eectue l'analyse syntaxique de la grammaire, recursive a gauche, des expressions inxes. Noter que l'on a introduit l'operateur de soustraction qui n'est pas associatif. Ainsi la technique d'analyse decrite au debut du paragraphe 6.4 ne peut ^etre appliquee ici.

On lit le mot a analyser de gauche a droite et on eectue les reductions suivantes des qu'elles sont possibles:

Reduireaen F quelle que soit sa position.

Reduire (E) en F quelle que soit sa position.

ReduireF en T s'il n'est pas precede de.

ReduireT F enT quelle que soit sa position.

ReduireT en E s'il n'est pas precede de + et s'il n'est pas suivi de.

ReduireE+T en E s'il n'est pas suivi de.

ReduireE;T en E s'il n'est pas suivi de.

On peut gerer le mot reduit a l'aide d'une pile. Les operations de reduction consistent a supprimer des elements dans celle-ci, les tests sur ce qui precede ou ce qui suit se font tres simplement en consultant les premiers symboles de la pile. On peut construire aussi un arbre de syntaxe abstraite en utilisant une autre pile qui contient cette fois des arbres (c'est a dire des pointeurs sur des nuds). Les deux piles sont traitees en parallele, la reduction par une regle a pour eet sur la deuxieme pile de construire un nouvel arbre dont les ls se trouvent en t^ete de la pile, puis a remettre le resultat dans celle-ci.

6.7 Evaluation

Dans la plupart des algorithmes que nous avons donnes, il a ete question d'arbre de syntaxe abstraite d'une expression arithmetique. An d'illustrer l'inter^et de cet arbre, on peut examiner la simplicite de la fonction d'evaluation qui permet de calculer la valeur de l'expression analysee a partir de l'arbre de syntaxe abstraite.

function Evaluer(x: Arbre): integer;

begin

if x^.valeur = 'a' then Evaluer := x^.valeur

6.8. PROGRAMMES EN C 163

else if x^.valeur = '+' then

Evaluer := Evaluer (x^.filsG) + Evaluer (x^.filsD) else if x^.valeur = '-' then

Evaluer := Evaluer (x^.filsG) - Evaluer (x^.filsD) else if x^.valeur = '*' then

Evaluer := Evaluer (x^.filsG) * Evaluer (x^.filsD) end

Une fonction similaire, qui ne demanderait pas beaucoup de mal a ecrire, permet de creer une suite d'instructions en langage machine traduisant le calcul de l'expression.

Il faudrait remplacer les operations +, *, -, eectuees lors de la visite d'un nud de l'arbre, par la concatenation des listes d'instructions qui calculent le sous-arbre droit et le sous arbre gauche de ce nud et de faire suivre cette liste par une instruction qui opere sur les deux resultats partiels. Le programme complet qui en resulte depasse toutefois le but que nous nous xons dans ce chapitre.

6.8 Programmes en C

/* Analyse descendante simple voir page 155 */

Arbre Terme();

Arbre Facteur();

Arbre Expression() {

Arbre a, b;

a = Terme();

if (f[i] == '+') { i++;

b = Expression();

return NouvelArbre('+', a, b);

}

else return a;

}

Arbre Terme() {

Arbre a, b;

a = Facteur();

if (f[i] == '*') { i++;

b = Terme();

return NouvelArbre('*', a, b);

}

else return a;

}

Arbre Facteur() {

Arbre a;

if (f[i] == '(') { i++;

a = Expression();

if (f[i] == ')') { i++;

return a;

}

else Erreur(i);

}

else if (f[i] == 'a') { i++;

a = NouvelArbre ('a', NULL, NULL);

return a;

}

else Erreur(i);

}

/* Analyse descendante recursive, voir page 157 */

int AnalyseRecursive (Mot f, Mot u) {

int i, pos;

char x, y;

Mot v;

int b;

pos = 1;

b = 0;

while (f[pos] == u[pos]) ++pos;

if (f[pos] == '$' && u[pos] == '+') { printf("analyse re'ussie \n");

b = 1;

}

else if (Auxiliaire(y)) { i = 1;

while ( (!b) && (i <= nbregle[y -'A'])) { v = Remplacer (u, regle[y-'A'][i], pos);

b = AnalyseRecursive (v, f);

if (b)

printf ("regle %d du nonterminal %c \n", i, y);

else i++;

} }

return b;

}

Arbre ArbSyntPref() /* Analyse LL(1), voir page 159 */

{

Arbre a, b, c;

char x;

6.8. PROGRAMMES EN C 165

if (f[pos] == 'a') {

a = NouvelArbre( 'a', NULL, NULL);

pos++;

}

else if (f[pos] == '(') && ((f[pos + 1] == '+') ||

(f[pos + 1] == '*')) { x = f[pos + 1];

pos = pos +2;

b = ArbSyntPref();

c = ArbSyntPref();

a = NouvelArbre(x, b, c);

if (f[pos] == ')' ) pos++;

else Erreur(pos);

}

else Erreur(pos);

}

int Evaluer(Arbre x) /* Evaluation, voir page 162 */

{

if (x -> valeur == 'a' ) return x -> valeur;

else if (x -> valeur == '+' )

return (Evaluer(x -> filsG) + Evaluer (x -> filsD));

else if (x -> valeur == '-' )

return( Evaluer(x -> filsG) - Evaluer (x -> filsD));

else if (x^.valeur == '*')

return (Evaluer(x -> filsG) * Evaluer (x -> filsD));

else Erreur();

Modularite

Jusqu'a present, nous n'avons vu que l'ecriture de petits programmes ou de procedures susant pour apprendre les structures de donnees et les algorithmes correspondants.

La partie la plus importante de l'ecriture des vrais programmes consiste a les structurer pour les presenter comme un assemblage de briques qui s'emboitent naturellement. Ce probleme, qui peut appara^tre comme purement esthetique, se revele fondamental des que la taille des programmes devient consequente. En eet, si on ne prend pas garde au bon decoupage des programmes en modules independants, on se retrouve rapidement deborde par un grand nombre de variables, et il devient quasiment impossible de realiser un programme correct.

Dans ce chapitre, il sera question de modules, d'interfaces, de compilation separee et de reconstruction incrementale de programmes.

Documents relatifs