On souhaite créer un programme qui reconnaît le langage suivant : « Les expressions correctes sont constituées d'un nombre quelconque, mais non nul, de 0, suivi d'un nombre quelconque, mais non nul, de 1. »
On suivra cette démarche :
Voici la grammaire du langage à reconnaître :
<expression> ::= <groupe0> <groupe1> <groupe0> ::= «0» <suite0> <suite0> ::= <groupe0> ::= <groupe1> ::= «1» <suite1> <suite1> ::= <groupe1> ::=
Pour la rédiger, on a suivi les règles suivantes :
Voici le code de l'analyseur pur : il répond par « bon » ou « mauvais », c'est-à-dire 1 ou 0.
#include <stdio.h> char s[512]; char *ss; int expression(){ if (groupe0()==1) return groupe1(); return 0; } int groupe0(){ if (*ss == '0'){ ss++; return suite0(); } return 0; } int suite0(){ if (groupe0() == 0) return 1; return 1; }
Il faut constater le lien très fort entre ce code et la grammaire ci-dessus. On n'a à réfléchir qu'au moment de la rédaction de la grammaire, le code est alors une conséquence directe de cette dernière. A un point tel qu'on verra qu'il est possible de générer automatiquement le code à partir de la grammaire, à l'aide d'outils prévus à cet effet.
Notons qu'une fonction prévue pour analyser une sous-expression ne connaît pas ce qui précède, et ne s'occupe pas de ce qui suit.
Passons au programme principal :
int main(){ printf("Une expression à analyser ? \n"); scanf("%s",s); ss = s; if (expression()==1) if (*ss == '\0') printf("Bon \n"); else printf("Mauvais\n"); else printf("Mauvais\n"); }
Remarque : Toujours commencer par l'analyseur syntaxique pur.
On souhaite dorénavant que les retours de notre analyseur soient plus parlant. On va donc renvoyer du texte, des messages d'erreurs...
int groupe0(){ if (*ss == '0'){ ss++; return suite0(); } printf("L'expression doit commencer par 0\n"); return 0; } int suite0(){ if (*ss == '0'){ ss++; return suite0(); } return 1; }
Le programme principal devient alors :
int main(){ printf("Une expression à analyser ? \n"); scanf("%s",s); ss = s; if (expression()==1) if (*ss == '\0') printf("Bon \n"); else if (*ss == '0') printf("Pas de 0 apres le(s) un(s).\n"); else printf("Caractère interdit : %c\n",*ss); }
int groupe0(){ if (*ss == '0'){ ss++; return 1+suite0(); } printf("L'expression doit commencer par 0\n") return 0; } int suite0(){ if (*ss == '0'){ ss++; return 1+suite0(); } return 0; }
Le programme principal devient alors :
int main(){
printf("Une expression a analyser ? \n");
scanf("%s»,s);
ss = s;
Expression = expression()
if (*ss == '\0')
printf("Bon \n");
else if (*ss == '0')
printf("Nombre de 0 : d",expression.zero, expression.un);
else
printf("Caractère interdit : %c\n",*ss);
}
où la structure Expression et la fonction expression() sont ainsi définis :
struct Expression{ int zero; int un; } struct Expression expression(){ struct Expression a; a.zero = groupe0(); if (a.zero !=0) a.un = groupe1(); return a; }
Cet exemple, et d'autres, seront (re)vus en TP.