Forum Programmation.c Problème analyseur lexical flex

Posté par . Licence CC by-sa
Tags :
0
23
fév.
2017

Bonjour,

Je viens vers vous car je débute avec flex et j'aurais bien besoin de conseils pour me débloquer …

Je cherche à créer un parser avec flex qui va récupérer les noms de fonctions dans un header afin de les réutiliser plus tard. Je désire stocker ces noms de fonctions dans un tableau que j'alloue dynamiquement car je souhaite pouvoir parser n'importe quel header avec (donc n'importe quel nombre de fonctions).

Dans les règles de production flex afin d'identifier les mots j'ai indiqué :

[a-zA-Z0-9]+    {if(yyin==hfile){
            if(strcmp(yytext,"extern")==0){
                fctON=1;
                cptWords=0;}
            else if(fctON==1){
                allocationfonctions(yytext);}
            else{ECHO;}
            }
             else if(yyin==cfile){
                ECHO;
            }
        }

qui lorsque je suis en train de parser le header hfile va identifier "extern" et faire appel à la fonction allocationsfonctions ci dessous pour les mots suivants jusqu'à rencontrer le caractère '(' qui repassera fctON à 0 (code non indiqué ici).

La fonction allocationfonctions :

int allocationfonctions(char* data){
    int i=0;
    char* word = strcat(data,"");
    printf("mot a stocker : %s\n",word);
        if(cptWords==0){
            if(n>=5){
                fonctions = realloc(fonctions, (n+2)*sizeof(chaine));
                if(fonctions==NULL){
                    printf("erreur lors de la reallocation\n");
                    return -1;}
                fonctions[n]="fctname";
                fonctions[n+1]="fctname";
                }

            fonctions[n+1]=word;

            printf("result : fonctions[%d]=%s\n",n+1,fonctions[n+1]);           
            cptWords++;
            n+=2;
            return 1;}
        else{
            if(n>=5){
                fonctions = realloc(fonctions, (n+1)*sizeof(chaine));
                if(fonctions==NULL){
                    printf("erreur lors de la reallocation\n");
                    return -1;}
                fonctions[n]="fctname";}

            fonctions[n]=word;

            printf("result : fonctions[%d]=%s\n",n,fonctions[n]);
            cptWords++;
            n+=1;
            return 1;}  
}

Le passage par un paramètre word avec strcat était une tentative d'éviter le problème que je pointe plus bas avec l'ajout d'un caractère nul, mais ça n'a rien changé

Et voici le main qui initialise tout ça :

int main(void) {

    int i;
    hfile = fopen("HelloWorld.h", "r");

    if (!hfile) {
        printf("I don't know where to read\n");
        return -1;
        }

    cfile = fopen("HelloWorld.c", "r");

    if (!cfile) {
        printf("I don't know where to read\n");
        return -1;
        }

    thatfile = fopen("result.c", "w");

    if (!thatfile) {
        printf("I don't know where to write\n");
        return -1;
    }

    chaine=malloc(8*sizeof(char));
    if(chaine==NULL){
    printf("erreur de memoire - ligne\n");
    return 0;
    }
    fonctions=malloc(5*sizeof(chaine));
    if(fonctions==NULL){
    printf("erreur de memoire - tableau\n");
    return 0;
    }
    for(i=0;i<5;i++){
        fonctions[i]="fctname";
        printf("valeur defaut fonction[%d]:%s\n",i,fonctions[i]);
        }

    yyin = hfile;
    yylex();
    printf("end of first parsing\n\n\n");

    for(i=0;i<n;i++){
        printf("fonction[%d]:%s\n",i,fonctions[i]);}

    printf("start of second parsing\n\n");
    yyin = cfile;
    yyout= thatfile;
    yylex();

    for(i=0;i<n;i++){
        printf("fonction[%d]:%s\n",i,fonctions[i]);}

    free(fonctions);
    fclose(hfile);
    fclose(cfile);
    fclose(thatfile);
    return 0;
}

Avec ce code je souhaite avoir au final par exemple :

fonctions[0]:fctname

fonctions[1]:int fonctions[2]:main fonctions[3]:fctname

fonction[4]:int fonctions[5]:calc fonction[6]:fctname etc…

avec le HelloWorld.h suivant :

#ifndef HELLOWORLD_FLEX
#define HELLOWORLD_FLEX

extern int main();
extern int calc(int nombre);
extern float testerpresent();

#endif

Or lorsque j'exécute mon code après compilation (l'instruction flex ne retourne pas de warning, gcc affiche des warning car je passe des char* dans les printf où il attends des string) j'ai le résultat suivant:

valeur defaut fonction[0]:fctname
valeur defaut fonction[1]:fctname
valeur defaut fonction[2]:fctname
valeur defaut fonction[3]:fctname
valeur defaut fonction[4]:fctname
ifndef
HELLOWORLD
FLEX
define
HELLOWORLD
FLEX
mot a stocker : int
result : fonctions[1]=int
mot a stocker : main
result : fonctions[2]=main
mot a stocker : int
result : fonctions[4]=int
mot a stocker : calc
result : fonctions[5]=calc
int
nombre
mot a stocker : float
result : fonctions[7]=float
mot a stocker : testerpresent
result : fonctions[8]=testerpresent
endif
end of first parsing

Jusqu'ici tout va bien vous me direz, les ré allocations fonctionnent et tout, oui en effet c'est ce que je croyais… mais lorsque je redemande d'afficher le contenu du tableau une fois le parsing du fichier fini c'est la douche froide :

fonction[0]:fctname
fonction[1]:int main();
extern int calc(int nombre);
extern float testerpresent();

#endif

fonction[2]:main();
extern int calc(int nombre);
extern float testerpresent();

#endif

fonction[3]:fctname
fonction[4]:int calc(int nombre);
extern float testerpresent();

#endif

fonction[5]:calc(int nombre);
extern float testerpresent();

#endif

fonction[6]:fctname
fonction[7]:float testerpresent();

#endif

fonction[8]:testerpresent();

#endif 

Que s'est il passé durant le parsing ? le seul endroit où j’écris dans le tableau fonctions[] est la fonction allocationfonctions, appelée dans un cas assez précis. J'ai l'impression que le parser a stocké dans yytext tous les caractères entre 2 appels de la fonction et les a remis dans tout le tableau (sauf les zones mémoire avec fctname). Or je croyais que ce paramètre ne contenait que le caractère courant ?

Si quelqu'un connait bien flex (ou voit une erreur dans mon code C, ce qui est possible aussi) j'aimerais comprendre ce qui s'est passé pour le corriger.

Je vous remercie d'avance pour vos réponses, toute suggestion est la bienvenue !

PS : les endif en gras sont en fait le endif du helloworld.h, j'ai eu un peu de mal avec les quotes..

  • # balise de texte vs balise de code

    Posté par . Évalué à 2.

    PS : les endif en gras sont en fait le endif du helloworld.h, j'ai eu un peu de mal avec les quotes..

    c'est corrigé,
    c'est parce que tu utilisais des balises de citations de texte >
    pour entourer une citation de code…

    il fallait continuer avec ```LANG
    j'ai mis ```sh pour tes citations.

  • # Possible raison

    Posté par . Évalué à 1. Dernière modification le 23/02/17 à 19:01.

    Bonjour,

    Si j'ai bien vu, fonctions est traîtée comme un tableau de pointeurs sur char dans votre programme et je ne pense pas que c'est que vous aviez derrière la tête ;-)
    sizeof(chaine) retourne la taille d'un pointeur et vous assignez aux éléments du tableau la variable yytext (via word et data) qui est un pointeur sur char et dont le contenu pointé est ecrasé régulièrement par yylex().

  • # possible raison et nouvelle problématique

    Posté par . Évalué à 1.

    Merci pour votre réponse,
    Votre piste me parait intéressante, J'ai défini dans les règles de flex yytext avec %array, pour que cette variable ne soit plus un pointeur sur char mais un tableau de char. Je n'ai pas encore résolu mon problème, mais maintenant au lieu d'obtenir au final le header complet dans chaque espace mémoire de fonctions j'ai le résultat suivant dans le terminal :

    valeur defaut fonction[0]:fctname
    valeur defaut fonction[1]:fctname
    valeur defaut fonction[2]:fctname
    valeur defaut fonction[3]:fctname
    valeur defaut fonction[4]:fctname
    word :ifndef
    word :HELLOWORLD
    word :FLEX
    word :define
    word :HELLOWORLD
    word :FLEX
    word :extern
    extern found
    word :int
    valeur data : int
    mot a stocker : int
    result : fonctions[1]=int
    rappel : fonctions[2]=fctname
    word :main
    valeur data : main
    mot a stocker : main
    result : fonctions[2]=main
    rappel : fonctions[2]=main
    word :extern
    extern found
    word :int
    valeur data : int
    mot a stocker : int
    result : fonctions[4]=int
    rappel : fonctions[2]=int
    word :calc
    valeur data : calc
    mot a stocker : calc
    result : fonctions[5]=calc
    rappel : fonctions[2]=calc
    word :int
    word :nombre
    word :extern
    extern found
    word :float
    valeur data : float
    mot a stocker : float
    result : fonctions[7]=float
    rappel : fonctions[2]=float
    word :testerpresent
    valeur data : testerpresent
    mot a stocker : testerpresent
    result : fonctions[8]=testerpresent
    rappel : fonctions[2]=testerpresent
    word :endif
    end of first parsing
    
    
    fonction[0]:fctname
    fonction[1]:
    fonction[2]:
    fonction[3]:fctname
    fonction[4]:
    fonction[5]:
    fonction[6]:fctname
    fonction[7]:
    fonction[8]:
    

    J'ai donc bien l'impression que toutes les cases où je mets data pointent vers la même chose et sont réactualisées à chaque changement de yytext.

    Il faut donc que je trouve comment récupérer la valeur de yytext en évitant de pointer dessus par la suite. Sachant que yytext est maintenant un tableau et non un pointeur de char mais que allocationfonctions récupère toujours un char* en vue de compléter fonctions.

    J'ai conscience que mon tableau fonctions est un tableau de pointeur, mais je n'ai pas vu d'autres façons de déclarer un tableau alloué dynamiquement et c'est un critère que je ne peux pas écarter en vue de parser n'importe quel fichier…

    Des idées pour arriver à joindre ces conditions ?

    • [^] # Re: possible raison et nouvelle problématique

      Posté par . Évalué à 1.

      Que yytext soit de type tableau ou pointeur sur un tableau n'y changera rien. En C, la valeur d'une variable de type tableau est un pointeur sur son premier élément. Et yylex() écrasera de toute façon le tableau à chaque cycle.

      Vous devez donc dupliquer yytext dans un nouvel emplacement (p.e., avec strdup() si vous conservez le tableau de pointeurs ou strcpy() si vous optez pour un tableau de chaînes). Gardez en tête qu'en C, vous ne pouvez pas assigner ou passer en paramètre un tableau.

      Aussi, la variable chaine n'est d'aucune utilisé dans votre programme.

      Vous devriez (re-)lire le chapître 5, et particulièrement la section 5.3 (Pointeurs et tableaux) du fameux livre de K&R.

Suivre le flux des commentaires

Note : les commentaires appartiennent à ceux qui les ont postés. Nous n'en sommes pas responsables.