« Les expressions correctes sont constituées d'un nombre quelconque mais non nul de 'a', suivi d'un 'b', suivi d'un nombre quelconque éventuellement nul de 'c'.»
Exercice : Faire le même travail que précédemment.
<expression> ::= <groupe a> «b» <liste c>
<groupe a> ::= «a» <suite a>
<suite a> ::= <groupe a>
::=
<liste c> ::= «c» <liste c>
::=
Cet analyseur syntaxique pur répond par « Bon » ou « Mauvais » .
#include <stdio.h>
char s[512];
char **ss;
int expression(){
if (groupea()==1)
if (*ss == 'b'){
ss++;
return listec();
}
return 0;
}
int groupea(){
if (*ss == 'a'){
s++;
return suitea();
}
return 0;
}
int suitea(){
if (groupea() == 0)
return 1;
return 1;
}
int listec(){
if (*ss == '\0')
return 1;
else
if (*ss == 'c'){
ss++;
listec();
} else
return 0;
}
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");
}
int groupea(){
if (*ss == 'a'){
s++;
return suitea();
} else {
printf("L'expression doit commencer par a \n");
return 0;
}
}
int suitea(){
if (*ss == 'a'){
ss++;
return suitea();
}
return 1;
}
int main(){
printf("Une expression a analyser ? \n");
scanf("%s",s);
ss = s;
if (expression()==1)
if (*ss == '\0')
printf("Bon \n");
else
printf("Il n'y a que des c après le b\n");
}
int expression(){
if (groupea()==1)
if (*ss=='b'){
ss++;
return listec();
} else {
printf("Apres le(s) a il doit y avoir un b");
}
return 0;
}
#include <stdio.h> // déclaration des fonctions et structures char s[512]; char *ss; struct Expression{ int zero; int un; }; struct Expression expression(){ struct Expression a; a.zero = groupe0(); if (a.zero !=0) a.un = groupe1(); return a; }; 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; } int groupe1(){ if (*ss == '1'){ ss++; return 1+suite1(); } return 0; } int suite1(){ if (*ss == '1'){ ss++; return 1+suite1(); } return 0; } // programme principal int main(){ printf("Une expression a analyser ? \n"); scanf("%s",s); ss = s; struct Expression E = expression(); if (*ss == '\0'){ printf("Bon \n"); printf("Nombre de 0 :%d, de 1 :%d\n",E.zero, E.un);} else if (*ss == '0') printf("Un 0 ne peut suivre des 1."); else printf("Caractère interdit : %c\n",*ss); }
Les expressions algébriques à analyser sont constituées :
Exemple :
a+b+c*(d+e*(f+g))*((h+i)*j+k*l)+(m+n+o)*(p+q)*r
On souhaite ici faire le même travail que précédemment, tout en sachant que la grammmaire devra tenir compte des deux opérateurs hiérarchisés.
Les règles sont les suivantes :
<somme> ::= <terme> <finSomme>
<finSomme> ::= «+» <somme>
::=
<terme> ::= <facteur> <finProduit>
<finProduit> ::= «*» <terme>
::=
<facteur> ::= <variable>
::= «(» <somme> «)»
<variable> ::= «a» ... «z» (notation non BNF)
Exercice : Écrire l'analyseur syntaxique pur.
On pensera à inclure la bibliothèque ctype, permettant la reconnaissance de caractères.
#include <stdio.h>
#include <ctype.h>
char s[512];
char *ss;
int somme(){
if (terme() == 1)
return finSomme();
return 0;
}
int finSomme(){
if (*ss == '+'){
ss++;
return somme();
}
return 1;
}
int terme(){
if (facteur() == 1;
return finProduit();
return 0;
}
int finProduit(){
if (*ss == '*'){
ss++;
return terme();
}
return 1;
}
int facteur(){
if (variable ==1)
return 1;
if (*ss == '('){
ss++;
if (somme() == 1)
if (*ss ==')'){
ss++;
return 1;
}
return 0;
}
}
int variable(){
if (isalpha(*ss)){
ss++;
return 1;
}
return 0;
}
int main(){
printf("\n Une expression à analyser ? ");
scanf("%s»,s);
ss=s;
if (somme() == 1)
if (*ss == '\0')
printf("Bon\n");
else
printf("Mauvais\n");
else
printf("Mauvais\n")
Exercice : Adaptez la grammaire pour qu'elle gère la soustraction et la division.
<somme> ::= <terme> <finSomme>
<finSomme> ::= <operateurAdditif> <somme>
::=
<operateurAdditif> ::= «+»
::= «-»
<terme> ::= <facteur> <finProduit>
<finProduit> ::= <operateurMultiplicatif> <terme>
::=
<operateurMultiplicatif> ::= «*»
::= «/»
<facteur> ::= <variable>
::= «(» <somme> «)»
<variable> ::= «a» ... «z»
#include <stdio.h>
#include <ctype.h>
char s[512];
char *ss;
int somme(){
if (terme() == 1)
return finSomme();
return 0;
}
int finSomme(){
if (*ss == '+' || *ss == '-'){
ss++;
return somme();
}
return 1;
}
int terme(){
if (facteur() == 1;
return finProduit();
return 0;
}
int finProduit(){
if (*ss == '*' || *ss == '/'){
ss++;
return terme();
}
return 1;
}
int facteur(){
if (*ss == '('){
if (*ss == '-' || *ss == '+') ss++;
ss++;
if (somme() == 1)
if (*ss ==')'){
ss++;
return 1;
}else{
printf("Il manque une parenthèse fermante.\n");
return 0;
}
}else{
if (*ss == '-' || *ss == '+') ss++;
if (isalpha(*ss)){
ss++;
return 1;
}else{
printf("\n\n ATTENTION :\n");
if (*ss == '+' || *ss == '-' || *ss == '/' || *ss == '*')
printf("Deux opérateurs ne peuvent pas se suivre");
else
if (*ss == '\0')
printf("L'expression ne peut pas se terminer par un opérateur");
else
printf("Ce caractere ne fait pas partie de la grammaire : %c \n.", *ss);
return 0;
}
}
}
int variable(){
if (isalpha(*ss)){
ss++;
return 1;
}
return 0;
}
int main(){
printf("\n Une expression à analyser ?");
scanf("%s",s);
ss=s;
if (*ss == '*' || *ss '/'){
printf("Ne peut pas commencer par un opérateur binaire");
return c;
}
if (somme() == 1)
if (*ss == '\0')
printf("Bon\n");
else{
printf("Attention\n");
if (isalpha(*ss))
printf("Deux variables ne peuvent se suivre.\n");
else if (*ss == '(' || *ss == ')')
printf("Caractère mal placé : %c", *ss);
else
printf("Ce caractère ne fait pas parti de la grammaire : %c \n",*ss);
}
}
printf("\n");
return 1;
}
On utilise les arbres : a+b+c devient
+
/ \
+ c
/ \
a b
et -a+b :
+
/ \
- b
|
a
On est en effet obligé d'associer à gauche pour la soustraction (sinon on change l'expression).
Exercice : Programmez cette structure d'arbre, puis la version sémantique.
typedef enum {FEUILLE, UNAIRE, BINAIRE} Tnoeud;
typedef struct arbre{
Tnoeur selecteur;
union{
char var;
struct{
char operateur;
struct arbre* fs;
} un;
struct{
char operateur;
struct arbre* fd;
struct arbre* fg;
} bin;
} type;
} arbre;
#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>
#define amalloc (arbre*)malloc (sizeof(arbre))
#define ANULL (arbre*) NULL
arbre* feuille(char var){
arbre* a = ANULL;
if ((a = amalloc) != ANULL{
a->selecteur = FEUILLE;
a->t.var = var;
}
return a;
}
arbre* unaire(char op, arbre* fs){
arbre* a = ANULL;
if (fs != ANULL)
if ((a=amalloc) != ANULL){
a->sel.UNAIRE;
a->t.un.op=op;
a->t.un.fs=fs;
}
return a;
}
arbre* binaire(char op, arbre* fg, arbre* fd){
arbre* a = ANULL;
if (fg != ANULL && fd != ANULL)
if ((a=amalloc) != ANULL){
a->sel.BINAIRE;
a->t.un.op=op;
a->t.un.fg=fg;
a->t.un.fd=fd;
}
return a;
}
void printa(arbre* a){
switch (a->sel){
case FEUILLE : printf("%c",a->t.var);
break;
case UNAIRE : printf("%c(",a->t.un.op);
printa(a->t.un.fs);
printf(")");
break;
case BINAIRE : printf("%c(",a->t.bin.op);
printa(a->t.bin.fg);
printf(",");
printa(a->t.bin.fd);
printf(")");
break;
}
}
void print_arbre(arbre* a){
if (a != NULL)
printa (a);
else
printf("Arbre vide\n");
printf("\n");
}
#define cmalloc(n) (char*)malloc((n)*sizeof(char))
#define crealloc(ptr,n) (char*)realloc(ptr,(n)*sizeof(char))
#define CNULL (char*)NULL
#define BLOC 16
char* lire_chaine(){
char *s, *ss, *ssmax;
int taille, c;
if ((s = cmalloc(BLOC))==CNULL){
fprintf(stderr,»Memoire saturee\n");
exit(43);
}
ss=s;
taille = BLOC;
smax = s + taille;
while ((c=getchar()) != '\n'){
*ss++ = (char)c;
if (ss == smax){
if ((s=crealloc(c,taille+BLOC))==CNULL){
fprintf(stderr,»Memoire saturee\n")
exit(44);
}
ss=s+taille;
taille += BLOC;
smax = s+taille;
}
}
*ss++ = '\0';
return crealloc(s,ss-s);
}
On souhaite rajouter le - unaire ; son analyse intervient au niveau du facteur :