Problème : Analyseur lexical d'un langage

Avant son exécution , tous code source d'un programme informatique doit être traduit en un autre code appelé code machine , cette traduction est réalisée par un compilateur dont le rôle est transformer le code source  en une suite d'instructions élémentaire directement exécutable par le processeur .
Ainsi le rôle de l'analyse lexical est identifier puis supprimer les caractères superflus du code source ( commentaires , espaces ... ) de connaître les mots clés , les identificateurs , les opérations ... qui sont définit par des règles .

en plus l'analyseur lexical signale les éventuelles erreurs de syntaxe et associe a chaque erreur le numéro de la ligne dans laquelle elle intervient . Dans le problème qui suit, en se propose de mettre en oeuvre un Analyseur lexical .

Mise en ouvre un analyseur lexical :

il s’agit d’implémenter des fonctions en langage C pour un analyseur lexical d'un pseudo langage noté PL .

Rappels :
Les fonctions suivantes sont définies en langage C peuvent être appelées au besoin sans être définies :
Fonctions définies dans le fichier stdio.h
  • int printf( const char * format , .... ) : afficher sur l’écran la chaîne en paramètres .
  • FILE *fopen( char * nom , char * m ) : ouvre le fichier nom avec le mode d'ouverture m ( m = "r" : mode lecture , m = "w" : mode écriture ) .
  • char fgetc( FILE * f ) : lit un caractère a partir du fichier f et le retourne .
  • char * fgets( char * ligne , int Max , FILE * f ) : lit une ligne du fichier texte f et la mis dans une chaîne ligne , Max est le nombre maximum de caractères de la ligne. fgets retourne l'adresse de la ligne lue ou NULL a la fin du fichier .
Fonctions définies dans le fichier string.h
  • int strlen( const char * ) : retourne la langueur du la chaîne en paramètres .
  • int strcmp( const char * , const char * ) : compare les 2 chaînes en paramètres et retourne 0 si elle sont identique .

A : Gestion des commentaires et des espaces .


Question A - 1 : test d'un commentaire
Un commentaire et une instruction qui n'est pas traduite par le compilateur. pour ce pseudo langage PL, un commentaire est une chaîne de caractère qui doit obligatoirement commencer par les deux caractères /* ( slash étoile ) et se termine par */ ( étoile slash ) .

Ecrire le code de la fonction d'entête int comment(char instr[] ) qui retourne 1 si la chaîne instr est un commentaire, retourne 0 sinon .
Exemple : - soit la chaîne de caractère instr="/* explication */" alors la fonction comment(instr) retourne 1 .
- si la chaîne de caractère instr="explication" alors la fonction comment(instr) retourne 0 .

Question A - 2 : Suppression des espaces multiples dans une instruction
Écrire une fonction d'entête void supprime_espaces(char instr[] ) qui supprime les espaces multiples consécutifs entre les caractères de la chaîne instr en paramètres , S'il y a N espaces ( 1 < N ) au début , ou a la fin , ou entre deux caractères de la chaine  instr en ne laissera qu'un seul espace .
Exemple : - soit la chaîne de caractère instr="     repeter     (max < 10)      max++     ", alors après l’appelle de la fonction supprime_espaces(instr) la chaîne instr devient " repeter (max < 10) max++ " .

B : Reconnaissance des mots clés et des identificateurs


    Tous langage de programmation définit un ensemble de mots clés qui sont des chaines de caractères ayant des significations et des utilisation spécifique dans ce langage .
Pour le pseudo langage PL on suppose avoir déclaré et défini N et tmotscles tel que :
  • N : est une constante entière strictement positif contenant le nombre des mots clés dans le langage PL.
  • tmotscles : est un tableau de N chaines de caractères contenant tous les mots clés de PL .

Question B - 1 : Vérification d'un mot clé
Écrire une fonction d'entête int motcle(char mot[] ) qui retourne 1 si la mot ( paramètre de la fonction ) est un mot clé de PL et retourne 0 sinon , le paramètre mot est un mot clé de PL si c'est un élément du tableau tmotscles ( supposé déclaré et définie ) . 
Exemple : pour N = 5 Si tmotscles[N][80] = { "entier","reel","repeter","si","sinon" } Alors l'appel de motcle("si") retourne 1 , et l'appel de motcle("pour") retourne 0 .

Question B - 2 : Validité d'un identificateur
    Un identificateur est une chaîne de caractères qui permet de déclarer et d'identifier de éléments du programme ( les constantes , les variables ... ) , Il doit respecter un ensemble des règles particuliers pour chaque langage .
    Un identificateur pour le langage PL est valide s'il respecte les quatre conditions suivantes :
1 - Sa langueur est inférieure strictement a 80 .
2 - Ne contient aucun espace .
3 - Ne commence pas par un chiffre ( 0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 ) .
4 - Ne doit pas être un mot clé dans le langage PL .

Écrire une fonction d'entête int identificateur(char id[] ) qui retourne 1 si son paramètre id est un identificateur valide , retourne 0 sinon .

C : Implémentation de l'analyseur lexical


    On suppose maintenant que les mots clés du pseudo langage PL sont mis dans un fichier texte de nom physique "c\fmotscles.txt" supposé existant , chaque ligne de ce fichier texte correspond a un mot clé de PL .

Question C - 1 : Analyse d'une instruction
    Une instruction de ce pseudo langage est correct si elle vérifie l'une des deux conditions suivantes :
1 - l'instruction est un commentaire du pseudo langage .
2 - l'instruction commence obligatoirement par un mots clé de PL suivie par un espace et se termine par le caractère ; ( point virgule )

Écrire la fonction d'entête int analyserInstruction(char instr[] ) qui retourne 1 si la la chaine instr ( paramètre de la fonction ) est une instruction correct du PL et retourne 0 sinon.
Exemple : l'appel de la fonction analyserInstruction("reel x=5 , y ;") retourn 1 , et analyserInstruction("var x=5;") retourn 0 .

Question C - 2 : Analyse d'un fichier source
    On suppose avoir écrit un programme avec se pseudo langage PL , le code source de se programme est enregistré dans un fichier texte .
chaque ligne de se fichier correspond a une instruction PL .

Écrire une fonction d'entête int analyserSource(char source[] ) qui analyse le fichier source dont le nom est en paramètre de la fonction . cette fonction affiche sur l’écran les numéros de toutes les lignes qui correspond a une instruction incorrect ou affiche sur l’écran le mot "Succès" dans le cas de toutes les lignes du fichier source sont correct .
Exemple : Soit le fichier de nom "SourcePL" contenant les 6 lignes suivantes du code source d'un programme écrit en pseudo langage PL :

        reel x=0,y=1;
        tantque (x<10) x=x+1;
        /* instruction */
        si (y==0) alors afficher(NUL);
        sinon afficher(Non NUL)
        fin;

Après l'appel de la fonction analyserSource(SourcePL) on aura sur l’écran : 2 , 5 , 6 .

Solution du Problème

// QA1
int comment(char instr[]){
 if ( instr[0] == '/' && instr[1] == '*' && instr[strlen(instr)-1] == '/' && instr[strlen(instr)-2] == '*' ) return 1;
 else return 0;
}

// QA2
void suprime_espace(char T[]){
        if( T[0] != '\0' ) { 
  int j,i=0;
  while( ( T[i] != ' ' || T[i+1] != ' ' ) && T[i] != '\0' ) i++;
  j=i;
  while( T[i] != '\0' ) {
   T[i] = T[i+1];
   i++;
  }
  suprime_espace(&T[j]);
 }
}

// QB1
int moscle( char mot[] ) {
 int i;
 for( i=0 ; i < N ; i++ ) if( strcmp(mot,tmoscles[i]) == 0 ) return 1;
 return 0;
}

// QB2
// pour cette question on va cree une autre fontion int espace(char s[]) qui return 1 si la chaine s contien un espace , et 0 sinon 

int espace(char s[]){
 int i=0;
 while( s[i] != '\0' ){
  if( s[i] == ' ') return 1;
  i++;
 }
 return 0;
}
int identificateur( char id[] ){
 if( strlen(id) <= 80 && ( id[0] < '0' || id[0] > '9' ) && moscle(id) == 0 && espace(id) == 0 ) return 1;
 else return 0;
}

// QC1

int analyserInstruction(char instr[]){
 if ( comment(instr) == 1 ) return 1;
 else {
  int i=0,j=0;
  char mos[80],id[80];
  while( instr[i] != ' ' && instr[i] != '\0' ){
   mos[i] = instr[i];
   i++;
  }
  mos[i] = '\0';
  i++;
  while( instr[i+j] != '=' && instr[i+j] != '\0' ){
   id[j] = instr[i+j];
   j++;
  }
  id[j] = '\0';
 
  if( moscle(mos) == 1 && identificateur(id) == 1 && instr[strlen(instr)-1] == ';' ) return 1;
  else return 0;
 }
}
// QC2
// on cree d'abord une fonction qui calcule le nombre des lignes dans une chaine ( nbre de ligne = nbre de \n + 1 )
int nbreLigne(char chaine[]){
 int i=0,nbr=1;
 while( chaine[i] != '\0' ) {
  if ( chaine[i] == '\n' ) nbr++;
  i++;
 }
 return i;
}

int analyserSource(char source[]){

 int k;

 // declaration de la chaine copiée de source
 char* sourceCopy = NULL;
 sourceCopy = ( char * ) malloc( sizeof(char) * strlen(source) );
 
 // des parametres qu'on va les utilisés
 int nbrL = nbreLigne(source),i=0,j=1;

 // declaration et initialisation du tableau ou on va noté les lignes d'erreurs
 int* erreurs = NULL;
 erreurs = ( int* ) malloc( sizeof(int) * nbrL );
 for(k=0; k < nbrL ; k++ ) erreurs[k] = 1;

 // declaration du tableau de pointeurs sur les ligne;
 char** ligne = NULL;
 ligne = ( char ** )malloc( sizeof(char*) * nbrL );

 // le copiage et le chainage
 ligne[0] = sourceCopy;
 while( source[i] != '\0' ){ 
  sourceCopy[i] = source[i];
  if( sourceCopy[i] == '\n' ) { sourceCopy[i] = '\0'; ligne[j] = sourceCopy + i + 1; j++; }
  i++;
 }
 sourceCopy[i] = '\0';
 
 // analyse ligne par ligne
 for(k=0; k < nbrL ; k++ ) if( analyserInstruction(ligne[k]) == 0 ) erreurs[k] = 0;
 
 // ecriture des lignes d'erreur
 i=0;
 for(k=0; k < nbrL ; k++ ) if ( erreurs[k] == 0 ) { printf("%d",k+1); i++; }

 // succée
 if ( i == 0 ) { printf("succee"); return 1; }
 else return 0;
 
}
Partager avec vos amis :

Aucun commentaire :

Enregistrer un commentaire

Formulaire de contact

Nom

E-mail *

Message *

MedAnassSDK. Fourni par Blogger.