59 votes

Entrée de la chaîne de caractères dans le lexer flex

Je veux créer une boucle de lecture-évaluation-impression en utilisant le parseur flex/bison. Le problème est que le lexer généré par flex veut une entrée de type FILE* et je voudrais qu'elle soit de type char*. Existe-t-il un moyen de faire cela ?

Une suggestion a été de créer un tube, de lui fournir la chaîne de caractères, d'ouvrir le descripteur de fichier et de l'envoyer au lexer. C'est assez simple, mais cela semble alambiqué et pas très indépendant de la plate-forme. Existe-t-il une meilleure solution ?

58voto

dfa Points 54490

Les routines suivantes sont disponibles pour configurer les tampons d'entrée afin d'analyser les chaînes en mémoire au lieu des fichiers (comme le fait yy_create_buffer) :

  • YY_BUFFER_STATE yy_scan_string(const char *str) : scanne une chaîne de caractères terminée par NUL``.
  • YY_BUFFER_STATE yy_scan_bytes(const char *bytes, int len) : parcourt len octets (y compris éventuellement les NULs) en commençant par les octets d'emplacement.

Notez que ces deux fonctions créent, renvoient un handle YY_BUFFER_STATE correspondant (que vous devez supprimer avec yy_delete_buffer() quand vous en avez fini avec lui) afin que yylex() scanne une copie de la chaîne ou des octets. Ce comportement peut être souhaitable puisque yylex() modifie le contenu du tampon qu'il analyse).

Si vous voulez éviter la copie (et yy_delete_buffer) en utilisant :

  • YY_BUFFER_STATE yy_scan_buffer(char *base, yy_size_t size)

échantillon principal :

int main() {
    yy_scan_buffer("a test string");
    yylex();
}

0 votes

Hey dfa (qui est approprié vu qu'il s'agit de flex) pourriez-vous ajouter quelque chose à propos de l'exigence du double-null ?

3 votes

L'échantillon principal échouera probablement, car yy_scan_buffer nécessite un tampon accessible en écriture (il modifie temporairement le tampon, en insérant des NULs pour terminer yytext et restaure ensuite les caractères d'origine), ET il nécessite DEUX octets NUL de fin.

0 votes

Au fait, dans mon programme C++, j'avais besoin de déclarer yy_scan_bytes avec un size_t len pour éviter les erreurs de liaison.

21voto

unwind Points 181987

Ver cet article du manuel Flex pour obtenir des informations sur la manière d'analyser les tampons en mémoire, tels que les chaînes de caractères.

15voto

sevko Points 129

flex peut analyser char * en utilisant l'une des trois fonctions suivantes : yy_scan_string() , yy_scan_buffer() y yy_scan_bytes() (voir le documentation ). Voici un exemple du premier :

typedef struct yy_buffer_state * YY_BUFFER_STATE;
extern int yyparse();
extern YY_BUFFER_STATE yy_scan_string(char * str);
extern void yy_delete_buffer(YY_BUFFER_STATE buffer);

int main(){
    char string[] = "String to be parsed.";
    YY_BUFFER_STATE buffer = yy_scan_string(string);
    yyparse();
    yy_delete_buffer(buffer);
    return 0;
}

Les déclarations équivalentes pour yy_scan_buffer() (qui nécessite une chaîne de caractères à double terminaison nulle) :

char string[] = "String to be parsed.\0";
YY_BUFFER_STATE buffer = yy_scan_buffer(string, sizeof(string));

Ma réponse reprend certaines des informations fournies par @dfa et @jlholland, mais le code d'aucune de leurs réponses ne semble fonctionner pour moi.

1 votes

Comment va-t-il reconnaître ce qu'est la structure yy_buffer_state ?

0 votes

Ça n'arrivera pas, mais il s'en moque . Tout ce qu'on fait, c'est déclarer pointeurs opaques à une structure inconnue appelée yy_buffer_state Le compilateur sait que la structure a une largeur de 4 octets (ou la taille des pointeurs de votre système) : comme nous n'accédons jamais à aucune de ses variables membres, il n'a pas besoin de connaître la composition de cette structure. Vous pouvez remplacer le struct yy_buffer_state dans le typedef con void o int ou autre, puisque la taille du pointeur sera la même pour chacun.

8voto

jlholland Points 51

Voici ce que je devais faire :

extern yy_buffer_state;
typedef yy_buffer_state *YY_BUFFER_STATE;
extern int yyparse();
extern YY_BUFFER_STATE yy_scan_buffer(char *, size_t);

int main(int argc, char** argv) {

  char tstr[] = "line i want to parse\n\0\0";
  // note yy_scan_buffer is is looking for a double null string
  yy_scan_buffer(tstr, sizeof(tstr));
  yy_parse();
  return 0;
}

vous ne pouvez pas externaliser le typedef, ce qui est logique quand on y pense.

1 votes

Vous ne pouvez pas externaliser sans type défini [ligne 1].

1 votes

Il suffit de spécifier un seul '\0' car la seconde est implicitement présente (à cause de la chaîne littérale).

4voto

Tad Carlucci Points 51

La réponse acceptée est incorrecte. Elle entraînera des fuites de mémoire.

En interne, yy_scan_string appelle yy_scan_bytes qui, à son tour, appelle yy_scan_buffer.

yy_scan_bytes alloue de la mémoire pour une COPIE du tampon d'entrée.

yy_scan_buffer travaille directement sur le tampon fourni.

Avec ces trois formes, vous DEVEZ appeler yy_delete_buffer pour libérer les informations sur l'état de la mémoire tampon flexible (YY_BUFFER_STATE).

Cependant, avec yy_scan_buffer, vous évitez l'allocation/copie/libération du tampon interne.

Le prototype de yy_scan_buffer ne prend PAS un const char* et vous NE DEVEZ PAS vous attendre à ce que le contenu reste inchangé.

Si vous avez alloué de la mémoire pour contenir votre chaîne, vous êtes responsable de la libérer APRÈS avoir appelé yy_delete_buffer.

N'oubliez pas non plus de faire en sorte que yywrap renvoie 1 (non nul) lorsque vous analysez JUST cette chaîne.

Vous trouverez ci-dessous un exemple COMPLET.

%%

<<EOF>> return 0;

.   return 1;

%%

int yywrap()
{
    return (1);
}

int main(int argc, const char* const argv[])
{
    FILE* fileHandle = fopen(argv[1], "rb");
    if (fileHandle == NULL) {
        perror("fopen");
        return (EXIT_FAILURE);
    }

    fseek(fileHandle, 0, SEEK_END);
    long fileSize = ftell(fileHandle);
    fseek(fileHandle, 0, SEEK_SET);

    // When using yy_scan_bytes, do not add 2 here ...
    char *string = malloc(fileSize + 2);

    fread(string, fileSize, sizeof(char), fileHandle);

    fclose(fileHandle);

    // Add the two NUL terminators, required by flex.
    // Omit this for yy_scan_bytes(), which allocates, copies and
    // apends these for us.   
    string[fileSize] = '\0';
    string[fileSize + 1] = '\0';

    // Our input file may contain NULs ('\0') so we MUST use
    // yy_scan_buffer() or yy_scan_bytes(). For a normal C (NUL-
    // terminated) string, we are better off using yy_scan_string() and
    // letting flex manage making a copy of it so the original may be a
    // const char (i.e., literal) string.
    YY_BUFFER_STATE buffer = yy_scan_buffer(string, fileSize + 2);

    // This is a flex source file, for yacc/bison call yyparse()
    // here instead ...
    int token;
    do {
        token = yylex(); // MAY modify the contents of the 'string'.
    } while (token != 0);

    // After flex is done, tell it to release the memory it allocated.    
    yy_delete_buffer(buffer);

    // And now we can release our (now dirty) buffer.
    free(string);

    return (EXIT_SUCCESS);
}

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X