Content-type: text/html Manpage of FLEX

FLEX

Section: User Commands (1)
Updated: Avril 1995
Index Return to Main Contents
 

NOM

flex - générateur d'analyseurs lexicaux rapides  

SYNOPSIS

flex [-bcdfhilnpstvwBFILTV78+? -C[aefFmr] -osortie -Ppréfixe -Ssquelette] [--help --version] [nom_fichier ...]  

RÉSUMÉ

Ce manuel décrit flex, un outil de génération de programmes qui effectuent de la reconnaissance de motifs sur du texte. Le manuel inclut à la fois un tutorial et des sections de référence :

    Description
        un bref résumé de l'outil

    Quelques Exemples Simples

    Format du Fichier d'Entrée

    Motifs
        les expressions rationnelles étendues utilisées par flex

    Comment l'Entrée est Reconnue
        les règles pour déterminer ce qui a été identifié

    Actions
        comment spécifier ce qu'il faut faire quand un motif est
           reconnu

    L'Analyseur Généré
        détails à propos de l'analyseur produit par flex ;
        comment contrôler la source d'entrée

    Conditions de Démarrage
        introduction de contexte dans vos analyseurs, et 
        gestion de « mini-analyseurs »

    Tampons d'Entrée Multiples
        comment manipuler de multiples sources d'entrée ;
        comment analyser à partir de chaînes de caractères 
        plutôt qu'à partir de fichiers

    Règles de Fin-De-Fichier (End-Of-File, EOF)
        règles spéciales pour la fin de l'entrée

    Macros Diverses
        un résumé des macros disponibles dans les actions

    Valeurs Disponibles pour l'Utilisateur
        une résumé des valeurs disponibles dans les actions

    Interface avec Yacc
        connecter des analyseur lexicaux flex à des analyseurs
        syntaxiques yacc

    Options
        options de ligne de commandes de flex, et la directive 
        « %option »

    Considérations de Performance
        comment rendre votre analyseur le plus rapide possible

    Générer des Analyseurs C++
        La fonctionnalité (expérimentale) de génération de classes
        d'analyseurs C++

    Incompatibilités avec Lex et POSIX
        comment flex diffère du lex de AT&T et du lex POSIX 
        standard

    Diagnostics
        les messages d'erreur produits par flex (ou les analyseurs
        lexicaux qu'il génère) dont la signification pourrait ne pas 
        être évidente

    Fichiers
        fichiers utilisés par flex

    Défectuosités / Bogues
        problèmes connus de flex

    Voir Aussi
        autre documentation, outils apparentés

    Auteur
        inclut une information de contact

 

DESCRIPTION

flex est un outil dédié à la génération d'analyseurs lexicaux, c.-à-d. des programmes qui reconnaissent des motifs lexicaux dans du texte. flex lit les fichiers d'entrée donnés, ou son entrée standard si aucun nom de fichier n'est fourni, pour obtenir une description de l'analyseur à générer. La description est effectuée sous la forme de paires d'expressions rationnelles et de code C, appelées règles. flex génère en sortie un fichier source C, lex.yy.c, qui définit une routine yylex(). Ce fichier est compilé et lié avec la bibliothèque -lfl pour produire un exécutable. Quand l'exécutable est lancé, il analyse son entrée pour détecter les occurrences des expressions rationnelles. Chaque fois qu'il en trouve une, il exécute le code C correspondant.  

QUELQUES EXEMPLES SIMPLES

Tout d'abord, voici quelques exemples simples, pour avoir une idée de la façon d'utiliser flex. L'entrée flex suivante spécifie un analyseur qui, à chaque fois qu'il rencontre la chaîne de caractères « nomUtilisateur », la remplace par le nom de connexion de l'utilisateur :


    %%
    nomUtilisateur    printf( "%s", getlogin() );

Par défaut, tout le texte non reconnu par un analyseur flex est copié sur la sortie, et ainsi l'effet de bord de cet analyseur est de copier son fichier d'entrée sur sa sortie où chaque occurrence de « nomUtilisateur » est développée. Dans cette entrée, il n'y a qu'une seule règle. « nomUtilisateur » est le motif et « printf » est l' action. Le « %% » marque le début des règles.

Voici un autre exemple simple :


            int nombre_lignes = 0, nombre_cars = 0;

    %%
    \n      ++nombre_lignes; ++nombre_cars;
    .       ++nombre_cars;

    %%
    main()
            {
            yylex();
            printf( "# de lignes = %d, # de caractères = %d\n",
                    nombre_lignes, nombre_cars );
            }

Cet analyseur compte le nombre de caractères et le nombre de lignes de son entrée (il ne produit pas d'autre sortie que le rapport final d'occurrences). La première ligne déclare deux variables globales, « nombre_lignes » et « nombre_cars », qui sont accessibles à la fois à l'intérieur de yylex() et de la routine main() déclarée après le second « %% ». Il y a deux règles, l'une qui reconnaît le retour à la ligne (« \n ») et qui incrémente à la fois le nombre de lignes et le nombre de caractères, et l'autre qui reconnaît tous les autres caractères (ce qui est indiqué par l'expression rationnelle « . »).

Un exemple un peu plus compliqué :


    /* analyseur pour un langage de type Pascal */

    %{
    /* besoin de ceci pour l'appel à atof() plus bas */
    #include <math.h>
    %}

    CHIFFRE  [0-9]
    ID       [a-z][a-z0-9]*

    %%

    {CHIFFRE}+  {
                printf( "Un entier : %s (%d)\n", yytext, atoi(yytext));
                }

    {CHIFFRE}+"."{CHIFFRE}* {
                printf( "Un nombre flottant : %s (%g)\n", yytext, 
                atof(yytext));
                }

    if|then|begin|end|procedure|function        {
                printf( "Un mot-clé : %s\n", yytext );
                }

    {ID}        printf( "Un identificateur : %s\n", yytext );

    "+"|"-"|"*"|"/"   printf( "Un opérateur : %s\n", yytext );

    "{"[^}\n]*"}"     /* manger les commentaires d'une ligne */

    [ \t\n]+          /* manger les blancs */

    .           printf( "Caractère non reconnu : %s\n", yytext );

    %%

    main( argc, argv )
    int argc;
    char **argv;
        {
        ++argv, --argc;  /* passer le nom du programme */
        if ( argc > 0 )
                yyin = fopen( argv[0], "r" );
        else
                yyin = stdin;
        
        yylex();
        }

C'est l'ébauche d'un simple analyseur pour un langage comme Pascal. Il identifie différents types d'éléments lexicaux (tokens) et rapporte ce qu'il a vu.

Les détails de cet exemple seront expliqués dans les sections suivantes.  

FORMAT DU FICHIER D'ENTRÉE

Le fichier d'entrée de flex est constitué de trois sections, séparées à chaque fois par une ligne composée uniquement de %% :

    définitions
    %%
    règles
    %% 
    code utilisateur

La section de définitions contient les déclarations de simples définition de noms qui servent à simplifier la spécification de l'analyseur, et les déclarations de conditions de démarrage, qui sont expliquées dans une section ultérieure.

Les définitions de noms ont la forme :


    nom définition

Le « nom » est un mot commençant par une lettre ou un caractère de soulignement (« _ ») suivi de 0 à n lettres, chiffres, « _ » ou « - » (tiret). La définition est supposée débuter au premier caractère non d'espacement suivant le nom et continue jusqu'à la fin de la ligne. La définition peut être référencée plus tard en utilisant « {nom} », qui sera développé en « (définition) ». Par exemple,

    CHIFFRE  [0-9]
    ID       [a-z][a-z0-9]*

définit « CHIFFRE » comme étant une expression rationnelle correspondant à un chiffre unique, et « ID » comme étant une expression correspondant à une lettre suivie de zéro-ou-plusieurs lettres-ou-chiffres. Une référence ultérieure à

    {CHIFFRE}+"."{CHIFFRE}*

sera identique à

    ([0-9])+"."([0-9])*

et correspond à un-ou-plusieurs chiffres suivis d'un point, et de zéro-ou-plusieurs chiffres.

La section de règles dans l'entrée de flex contient une série de règles de la forme :


    motif   action

où le motif ne peut pas être indenté, et où l'action doit commencer sur la même ligne.

Voyez plus bas pour une description plus précise des motifs et des actions.

Finalement, la section du code utilisateur est simplement copiée telle quelle dans lex.yy.c. Elle est utilisée pour des routines d'accompagnement qui appellent ou sont appelées par l'analyseur. La présence de cette section est optionnelle ; si elle est manquante, le deuxième %% du fichier d'entrée peut également être omis.

Dans les sections de définitions et de règles, tout texte indenté ou compris entre %{ et %} est copié tel quel dans la sortie (sans les %{}). Les %{} ne peuvent eux-mêmes être indentés sur leur ligne.

Dans la section de règles, tout texte indenté ou entre %{} qui apparaît avant la première règle peut être utilisé pour déclarer des variables qui sont locales à la routine d'analyse lexicale, et (après les déclarations) au code qui doit être exécuté à chaque fois que l'on entre dans cette même routine. Un autre texte indenté ou entre %{} présent dans la section de règles est toujours copié sur la sortie, mais sa signification n'est pas bien définie et pourrait provoquer des erreurs à la compilation (cette caractéristique est présente pour la conformité POSIX ; voyez plus bas pour d'autres caractéristiques similaires).

Dans la section de définitions (mais pas dans la section de règles), un commentaire non indenté (c.-à-d. une ligne commençant par « /* ») est également copiée telle quelle dans la sortie jusqu'au « */ » suivant.  

MOTIFS

Les motifs en entrée sont écrits en utilisant un ensemble étendu d'expressions rationnelles. Ce sont :

    x          correspond au caractère 'x'
    .          n'importe quel caractère (octet) sauf le retour 
               à la ligne
    [xyz]      une « classe de caractères » ; dans ce cas, le motif
               convient pour un 'x', un 'y', ou un 'z'
    [abj-oZ]   une « classe de caractères » contenant un intervalle ; 
               convient pour un 'a', un 'b', n'importe quelle lettre 
               allant de 'j' à 'o', ou un 'Z'
    [^A-Z]     une « classe de caractères niée », c.-à-d. tout caractère 
               sauf ceux dans la classe. Dans cet exemple, tout caractère 
               SAUF une lettre majuscule.
    [^A-Z\n]   tout caractère SAUF une lettre majuscule ou un retour 
               à la ligne
    r*         zéro ou plusieurs r, où r est une expression rationnelle
               quelconque
    r+         un ou plusieurs r
    r?         zéro ou un r (c.-à-d. un r optionnel)
    r{2,5}     entre deux et cinq r
    r{2,}      deux r ou plus
    r{4}       exactement 4 r
    {nom}      le développement de la définition de « nom » 
               (voir au dessus)
    "[xyz]\"foo"
               la chaîne de caractères littérale : [xyz]"foo
    \X         si X est 'a', 'b', 'f', 'n', 'r', 't' ou 'v', alors la
               représentation C ANSI de \x. Sinon, un 'X' littéral 
               (utilisé pour protéger des opérateurs comme '*')
    \0         un caractère NUL (de code ASCII 0)
    \123       le caractère de valeur octale 123
    \x2a       le caractère de valeur hexadécimale 2a
    (r)        reconnaît un r ; les parenthèses sont utilisées pour 
               surcharger la priorité (voir plus bas)

    rs         l'expression rationnelle r suivie de l'expression
                        rationnelle s ; appelé « concaténation »

    r|s        un r ou un s

    r/s        un r mais seulement s'il est suivi par un s. 
               Le texte reconnu par s est inclus quand on détermine si 
               cette règle est la « correspondance la plus longue », mais 
               est ensuite renvoyé sur l'entrée avant que l'action ne 
               soit exécutée. Ainsi, l'action ne voit que le texte auquel
               correspond r. Ce type de motif est appelé « contexte de
               queue ». (trailing context) (Il y a certaines combinaisons
               de r/s que flex ne peut détecter correctement ; voyez les
               notes consacrées aux contextes de queue dangereux dans la
               section Défectuosités/Bogues.)

    ^r         un r, mais uniquement au début d'une ligne (c.-à-d. au 
               début de l'analyse, ou juste après qu'un saut de 
               ligne ait été détecté).

    r$         un r, mais seulement à la fin d'une ligne (c.-à-d. juste 
               avant un saut de ligne). Équivalent à « r/\n ».

               Notez que la notion qu'a flex d'un « saut de ligne » 
               est exactement celle dont le compilateur C utilisé pour 
               compiler flex interprète '\n' ; en particulier, sur 
               certains systèmes DOS, vous devez soit filtrer vous-même
               les \r de l'entrée vous-même, soit utiliser explicitement 
               r/\r\n pour « r$ ».


    <s>r       un r, mais seulement dans la condition de démarrage s 
               (voyez en dessous pour une discussion sur les conditions 
               de démarrage)
    <s1,s2,s3>r
               idem, mais dans une des conditions de démarrage s1, s2 
               ou s3
    <*>r       un r dans n'importe quelle condition de démarrage, même 
               une exclusive.


    <<EOF>>    un end-of-file (fin de fichier)
    <s1,s2><<EOF>>
               un end-of-file quand on se trouve dans la condition de 
               démarrage s1 ou s2

Notez qu'à l'intérieur d'une classe de caractères, tous les opérateurs d'expressions rationnelles perdent leur signification spéciale sauf l'échappement ('\') et les opérateurs de classes de caractères, « - », « ] » et, au début de la classe, « ^ ».

Les expressions rationnelles listées plus haut sont groupées en fonction de leur priorité, allant de la plus haute au sommet à la plus basse en bas. Celles regroupées ensemble ont une priorité égale. Par exemple,


    foo|bar*

est identique à

    (foo)|(ba(r*))

puisque l'opérateur « * » a une plus grande priorité que la concaténation, et que la concaténation a une plus grande priorité que l'alternative ('|'). Ce motif convient par conséquent soit à la chaîne de caractères « foo », soit à la chaîne de caractères « ba » suivie de zéro-ou-plusieurs r. Pour reconnaître « foo » ou zéro-ou-plusieurs « bar », utilisez :

    foo|(bar)*

et pour reconnaître zéro-ou-plusieurs « foo ou bar » :

    (foo|bar)*

En plus des caractères et des intervalles de caractères, les classes de caractères peuvent également contenir des expressions de classes de caractères. Ce sont des expressions enfermées dans des délimiteurs [: et :] (qui doivent elles-mêmes apparaître entre le '[' et le ']' de la classe de caractères ; d'autres éléments peuvent également être présents dans la classe de caractères). Les expressions valides sont :


    [:alnum:] [:alpha:] [:blank:]
    [:cntrl:] [:digit:] [:graph:]
    [:lower:] [:print:] [:punct:]
    [:space:] [:upper:] [:xdigit:]

Ces expressions désignent toutes un groupe de caractères équivalent à la fonction C standard correspondante isXXX. Par exemple, [:alnum:] désigne les caractères pour lesquels isalnum() renvoie vrai - c.-à-d. tout caractère alphabétique ou numérique. Certains systèmes ne fournissent pas isblank(), et flex définit donc [:blank:] comme étant un blanc ou une tabulation.

Par exemple, les classes de caractères suivantes sont toutes équivalentes :


    [[:alnum:]]
    [[:alpha:][:digit:]
    [[:alpha:]0-9]
    [a-zA-Z0-9]

Si votre analyseur est insensible à la casse (option -i), alors [:upper:] et [:lower:] sont équivalents à [:alpha:].

Quelques notes sur les motifs :

-
Une classe de caractères niée comme l'exemple « [^A-Z] » au-dessus reconnaîtra un saut de ligne à moins que le caractère '\n' (ou une séquence d'échappement équivalente) ne soit explicitement présent dans la classe de caractères niée (p.ex. « [^A-Z\n] »). C'est différent de la façon dont de nombreux autres outils opérant sur les expressions rationnelles traitent les classes de caractères niées, mais malheureusement ce désaccord est historiquement acté. Reconnaître les sauts de ligne signifie qu'un motif comme [^"]* peut reconnaître l'entrée entière à moins qu'il n'y ait d'autres guillemets dans l'entrée.
-
Une règle peut avoir au plus une instance de contexte de queue (l'opérateur « / » ou l'opérateur « $ »). La condition de démarrage, « ^ », et les motifs « <<EOF>> » ne peuvent apparaître qu'au début d'un motif et, comme pour « / » et « $ », ne peuvent être groupés entre parenthèses. Un « ^ » qui n'est pas présent pas au début d'une règle, ou un « $ » qui n'est pas présent à la fin d'une règle, perdent leur propriété spéciale et sont traités comme des caractères normaux.
Le code suivant est illégal :

    foo/bar$
    <sc1>foo<sc2>bar

Notez que la première ligne peut être écrite « foo/bar\n ».
Le code suivant fera en sorte qu'un « $ » ou « ^ » soit traité comme un caractère normal :

    foo|(bar$)
    foo|^bar

Si ce qu'on veut est un « foo » ou un bar-suivi-par-un-saut-de-ligne, on pourrait utiliser ceci (l'action spéciale '|' est expliquée plus bas) :

    foo      |
    bar$     /* l'action vient ici */

Un truc similaire fonctionnera pour la détection d'un foo ou d'un bar-au-début-d-une-ligne.
 

COMMENT L'ENTRÉE EST RECONNUE

Quand l'analyseur généré est lancé, il analyse son entrée en recherchant des chaînes de caractères qui correspondent à l'un de ses motifs. S'il trouve plus d'une correspondance, il utilise celle correspondant au plus de texte (pour les règles de contexte de queue, cela inclut la longueur de partie de queue, même si elle est renvoyée sur l'entrée). S'il trouve deux correspondances ou plus de la même longueur, la règle listée en premier dans le fichier d'entrée de flex est choisie.

Une fois que la correspondance est déterminée, le texte correspondant (appelé élément lexical) est mis à disposition dans le pointeur de caractère global yytext, et sa longueur dans l'entier global yyleng. L'action correspondant au motif reconnu est ensuite exécutée (une description plus détaillée des actions suit), et ensuite l'entrée restante est analysée afin de trouver une autre correspondance.

Si aucune correspondance n'est trouvée, alors la règle par défaut est exécutée : le caractère suivant dans l'entrée est considéré comme reconnu et est copié sur la sortie standard. Ainsi, l'entrée légale la plus simple pour flex est :


    %%

qui génère un analyseur qui copie simplement son entrée (un caractère à la fois) sur sa sortie.

Notez que yytext peut être défini de deux façons différentes : soit comme un pointeur de caractère, soit comme un tableau de caractères. Vous pouvez contrôler quelle définition utilise flex en incluant une des directives spéciales %pointer ou %array dans la première section (définitions) de votre entrée flex. Le défaut est %pointer, à moins que vous n'utilisiez l'option de compatibilité de lex -l , auquel cas yytext sera un tableau. L'avantage à utiliser %pointer est une analyse substantiellement plus rapide et aucun débordement de tampon lors de la reconnaissance de très gros éléments lexicaux (à moins que vous ne tombiez en manque de mémoire dynamique). Le désavantage est que vous êtes limités dans la façon dont vos actions peuvent modifier yytext (voyez la section suivante), et que les appels à la fonction unput() détruisent le contenu actuel de yytext, ce qui peut être un casse-tête de portage important lors d'une migration entre différentes versions de lex.

L'avantage de %array est que vous pouvez ensuite modifier yytext comme vous le souhaitez, et que les appels à unput() ne détruisent pas yytext (voir plus bas). De plus, les programmes lex existants accèdent parfois à yytext de façon externe en utilisant des déclarations de la forme :

    extern char yytext[];
Cette définition est erronée quand elle est utilisée avec %pointer, mais correcte pour %array.

%array définit yytext comme étant un tableau de YYLMAX caractères, qui est une valeur assez grande par défaut. Vous pouvez modifier la taille en #define-issant YYLMAX à une valeur différente dans la première section de votre entrée flex. Comme mentionné plus haut, avec %pointer, yytext grandit dynamiquement pour pouvoir traiter les grands éléments lexicaux. Bien que cela signifie que votre analyseur %pointer puisse traiter les très grands éléments lexicaux (comme la reconnaissance de blocs entiers de commentaires), gardez à l'esprit qu'à chaque fois que l'analyseur doit redimensionner yytext, il doit également réexaminer l'élément lexical complet à partir du début, et la reconnaissance de tels éléments lexicaux peut être lente. yytext ne grandit actuellement pas dynamiquement si un appel à unput() résulte en trop de texte repoussé ; à la place, une erreur à l'exécution en résulte.

Notez également que vous ne pouvez pas utiliser %array avec des classes d'analyseurs C++ (l'option c++; voir plus bas).  

ACTIONS

Chaque motif dans une règle possède une action correspondante, qui peut être n'importe quelle instruction C arbitraire. Le motif se termine au premier caractère d'espacement non protégé ; le restant de la ligne constitue son action. Si l'action est vide, alors quand le motif est reconnu, l'élément lexical d'entrée est simplement écarté. Par exemple, voici la spécification d'un programme qui efface toutes les occurrences de « effacez-moi » de son entrée :

    %%
    « effacez-moi »

(Cela copiera tous les autres caractères de l'entrée en sortie puisqu'il seront reconnus par la règle par défaut.)

Voici un programme qui compresse de multiples blancs et tabulations en une simple espace blanche, et jette les caractères d'espacement trouvés à la fin d'une ligne :


    %%
    [ \t]+        putchar( ' ' );
    [ \t]+$       /* ignorer cet élément lexical */

Si l'action contient un « { », alors l'action continue jusqu'à ce que la « } » équilibrante soit trouvée, et l'action peut recouvrir plusieurs lignes. flex connaît les chaînes de caractères C et les commentaires, et ne sera pas dupé par des accolades trouvées à l'intérieur d'entre eux, mais permet également aux actions de commencer par %{ et considérera que l'action sera constituée de tout le texte allant jusqu'au %} suivant (qu'il y ait des accolades ordinaires à l'intérieur de l'action ou non).

Une action consistant uniquement en une barre verticale ('|') signifie « la même chose que l'action pour la règle suivante. » Voyez plus bas pour une illustration.

Les actions peuvent inclure du code C arbitraire, ce qui inclut les instructions return pour renvoyer une valeur à la routine qui a appelé yylex(). Chaque fois que yylex() est appelée, elle continue le traitement des éléments lexicaux à partir d'où elle s'était arrêtée en dernier lieu jusqu'à ce qu'elle atteigne la fin du fichier, ou jusqu'à ce qu'elle exécute un return.

Les actions sont libres de modifier yytext sauf pour l'allonger (ajouter des caractères à sa fin -- ceux-ci écraseront les caractères venant par après dans le flux d'entrée). Cela ne s'applique néanmoins pas lors de l'utilisation de %array (voir au-dessus); dans ce cas, yytext peut être librement modifié de n'importe quelle façon.

Les actions sont libres de modifier yyleng mis à part qu'elles ne devraient pas faire cela si l'action inclut également l'utilisation de yymore() (voir plus bas).

Il y a un certain nombre de directives spéciales qui peuvent être incluses à l'intérieur d'une action :

-
ECHO copie yytext dans la sortie de l'analyseur.
-
BEGIN suivi du nom d'une condition de démarrage place l'analyseur dans la condition de démarrage correspondante (voir plus bas).
-
REJECT ordonne à l'analyseur de poursuivre avec la « deuxième meilleure » règle qui convenait à l'entrée (ou un préfixe de l'entrée). La règle est choisie comme décrit plus haut dans « Comment l'Entrée est Reconnue », et yytext et yyleng sont réglés de manière appropriée. Elle peut être soit une règle qui reconnaît autant de texte que la règle choisie à l'origine mais qui venait plus tard dans le fichier d'entrée de flex, soit une règle qui reconnaît moins de texte. Par exemple, le code suivant comptera à la fois le nombre de mots en entrée, et appellera la routine special() à chaque fois que « frob » est rencontré :

            int nombre_mots = 0;
    %%

    frob        special(); REJECT;
    [^ \t\n]+   ++nombre_mots;

Sans le REJECT, tout « frob » dans l'entrée ne sera pas compté comme mot, car l'analyseur n'exécute normalement qu'une action par élément lexical. Des REJECT multiples sont permis, chacun trouvant le meilleur choix suivant la règle active à ce moment. Par exemple, quand l'analyseur suivant détecte l'élément lexical « abcd », il écrira « abcdabcaba » sur la sortie :

    %%
    a        |
    ab       |
    abc      |
    abcd     ECHO; REJECT;
    .|\n     /* manger tous les caractères 
                non reconnus */

(Les trois premières règles partagent l'action de la quatrième car elles utilisent l'action spéciale '|'.) REJECT est une fonctionnalité particulièrement coûteuse en terme de performance de l'analyseur ; si elles est utilisée dans toutes les actions de l'analyseur, elles ralentira toutes les correspondances effectuées par l'analyseur. De plus, REJECT ne peut être utilisé avec les options -Cf ou -CF (voir plus bas).
Notez également qu'à la différence des autres actions spéciales, REJECT est un branchement; le code le suivant immédiatement dans l'action ne sera pas exécuté.
-
yymore() indique à l'analyseur que la prochaine fois qu'il reconnaît une règle, l'élément lexical correspondant devra être concaténé à la valeur actuelle de yytext au lieu de le remplacer. Par exemple, étant donné l'entrée « mega-kludge », le code suivant écrira « mega-mega-kludge » en sortie :

    %%
    mega-    ECHO; yymore();
    kludge   ECHO;

Le premier « mega- » est détecté et transmis sur la sortie. Ensuite « kludge » est détecté, mais le « mega- » précédent traîne toujours au début de yytext, et donc l'ECHO pour la règle « kludge » sera en fait « mega-kludge ».

Deux notes concernant l'utilisation de yymore(). Primo, yymore() dépend du fait que yyleng reflète correctement la taille de l'élément lexical courant, et vous ne devriez donc pas modifier yyleng si vous utilisez yymore(). Secundo, la présence de yymore() dans l'action de l'analyseur engendre une pénalité de performance mineure dans la vitesse de reconnaissance de l'analyseur.

-
yyless(n) renvoie tout sauf les n premiers caractères de l'élément lexical courant dans le flux d'entrée, où ils seront réexaminés quand l'analyseur recherchera la correspondance suivante. yytext et yyleng sont ajustés adéquatement (p.ex., yyleng sera maintenant égale à n). Par exemple, pour l'entrée « foobar », le code suivant écrira « foobarbar »:

    %%
    foobar    ECHO; yyless(3);
    [a-z]+    ECHO;

Un argument de 0 pour yyless provoquera le réexamen entier de la chaîne de caractères d'entrée courante. À moins que vous n'ayez modifié la façon dont l'analyseur va traiter ultérieurement son entrée (en utilisant BEGIN par exemple), cela résultera en une boucle sans fin.

Notez que yyless est une macro et ne peut être utilisée que dans le fichier d'entrée de flex, et pas depuis d'autres fichiers source.

-
unput(c) replace le caractère c sur le flux d'entrée. Il sera le prochain caractère à être examiné. L'action suivante prendra l'élément lexical courant et le fera réexaminer enfermé dans des parenthèses.

    {
    int i;
    /* Copier yytext car unput() détériore yytext */
    char *yycopy = strdup( yytext );
    unput( ')' );
    for ( i = yyleng - 1; i >= 0; --i )
        unput( yycopy[i] );
    unput( '(' );
    free( yycopy );
    }

Notez que puisque chaque unput() repousse le caractère donné au début du flux d'entrée, le repoussage de chaîne de caractères doit être fait de la fin vers le début.

Un problème potentiel important lors de l'utilisation de unput() est que si vous utilisez %pointer (le défaut), un appel à unput() détruit le contenu de yytext, en commençant par son caractère le plus à droite, et en dévorant un caractère sur la gauche à chaque appel. Si vous avez besoin que la valeur de yytext soit préservée après un appel à unput() (comme dans l'exemple ci-dessus), vous devez soit la copier ailleurs, soit construire votre analyseur en utilisant %array à la place (voir Comment l'Entrée est Reconnue).

Finalement, notez que vous ne pouvez pas repousser EOF pour essayer de marquer le flux d'entrée avec un end-of-file.

-
input() lit le caractère suivant du flux d'entrée. Par exemple, le code suivant est une façon de manger les commentaires C :

    %%
    "/*"       
                register int c;

                for ( ; ; )
                    {
                    while ( (c = input()) != '*' &&
                            c != EOF )
                        ;    /* manger le texte du commentaire */

                    if ( c == '*' )
                        {
                        while ( (c = input()) == '*' )
                            ;
                        if ( c == '/' )
                            break;    /* fin trouvée */
                        }

                    if ( c == EOF )
                        {
                        error( "EOF dans un commentaire" );
                        break;
                        }
                    }
                }

(Notez que si l'analyseur a été compilé en utilisant C++, alors input() est référencé sous le nom de yyinput(), afin d'éviter un conflit de noms avec le flux C++ de nom input.)
-
YY_FLUSH_BUFFER vide le tampon interne de l'analyseur, de sorte que la prochaine fois que l'analyseur essaiera de reconnaître un élément lexical, il devra d'abord remplir le tampon en utilisant YY_INPUT (voyez L'Analyseur Généré, plus bas). Cette action est un cas spécial de la fonction plus générale yy_flush_buffer() , décrite plus bas dans la section Tampons d'Entrée Multiples.
-
yyterminate() peut être utilisée au lieu d'une instruction return dans une action. Elle arrête l'analyseur et renvoie 0 à l'appelant de l'analyseur, en indiquant « all done » (terminé). Par défaut, yyterminate() est également appelée quand un end-of-file est rencontré. C'est une macro et elle peut être redéfinie.
 

L'ANALYSEUR GÉNÉRÉ

La sortie de flex est le fichier lex.yy.c, qui contient la routine d'analyse yylex(), un certain nombre de tables qu'il utilise pour la reconnaissance des éléments lexicaux, et quelques routines et macros auxiliaires. Par défaut, yylex() est déclarée comme suit :

    int yylex()
        {
        ... diverses définitions et actions ici ...
        }

(Si votre environnement supporte les prototypes de fonctions, alors il sera « int yylex( void ) ».) Cette définition peut être modifiée en définissant la macro « YY_DECL ». Par exemple, vous pourriez utiliser :

    #define YY_DECL float lexscan( a, b ) float a, b;

pour donner à la routine d'analyse le nom lexscan, renvoyant un flottant, et prenant deux flottants comme arguments. Notez que si vous donnez des arguments à la routine d'analyse en utilisant une déclaration de fonction de style K&R/non-prototypée, vous devez terminer la définition par un point-virgule (;).

À chaque fois que yylex() est appelée, elle analyse les éléments lexicaux à partir du fichier d'entrée global yyin (qui vaut stdin par défaut). Elle continue jusqu'à ce qu'elle atteigne soit une fin de fichier EOF (où elle renvoie la valeur 0) ou jusqu'à ce que l'une de ses actions exécute une instruction return.

Si l'analyseur atteint un EOF, les appels ultérieurs seront non définis à moins que soit yyin pointe sur un nouveau fichier d'entrée (auquel cas l'analyse continue à partir de ce fichier), soit yyrestart() soit appelée. yyrestart() prend un argument, un pointeur FILE * (qui peut être zéro, si vous avez réglé YY_INPUT afin d'analyser à partir d'une source différente de yyin), et initialise yyin pour une analyse à partir de ce fichier. Il n'y a essentiellement aucune différence entre affecter yyin à un autre fichier d'entrée, ou utiliser yyrestart() pour faire cela ; cette dernière possibilité est disponible pour assurer la compatibilité avec des versions précédentes de flex, et parce qu'elle peut être utilisée pour changer de fichier d'entrée au milieu de l'analyse. Elle peut également être utilisée pour jeter le tampon d'entrée courant, en l'appelant avec un argument de yyin, mais il vaut mieux utiliser YY_FLUSH_BUFFER (voir au-dessus). Notez que yyrestart() ne réinitialise pas la condition de démarrage à INITIAL (voir Conditions de Démarrage, plus bas).

Si yylex() arrête l'analyse du fait de l'exécution d'une instruction return dans l'une des actions, l'analyseur peut ensuite être ré-appelé et il reprendra l'analyse là où il l'avait laissée.

Par défaut (et à des fins d'efficacité), l'analyseur utilise des lectures de blocs plutôt que de simples appels getc() pour lire des caractères à partir de yyin. La façon dont il obtient son entrée peut être contrôlée en définissant la macro YY_INPUT. La séquence d'appel de YY_INPUT est « YY_INPUT(tampon,résultat,taille_max) ». Son action est de placer jusqu'à taille_max caractères dans le tableau de caractères tampon et de renvoyer dans la variable entière résultat soit le nombre de caractères lus, soit la constante YY_NULL (0 sur les systèmes Unix) pour indiquer EOF. Le YY_INPUT par défaut lit à partir du pointeur de fichier global « yyin ».

Un exemple de définition de YY_INPUT (dans la section de définitions du fichier d'entrée) :


    %{
    #define YY_INPUT(tampon,résultat,taille_max) \
        { \
        int c = getchar(); \
        résultat = (c == EOF) ? YY_NULL : (buf[0] = c, 1); \
        }
    %}

Cette définition modifiera le traitement de l'entrée pour qu'il se produise pour un caractère à la fois.

Quand l'analyseur reçoit une indication de fin de fichier de YY_INPUT, il teste ensuite la fonction yywrap(). Si yywrap() renvoie faux (zéro), alors on suppose que la fonction a poursuivi son chemin et réglé yyin pour qu'elle pointe sur un autre fichier d'entrée, et l'analyse continue. Si elle renvoie vrai (non-zéro), alors l'analyseur se termine, en renvoyant 0 à son appelant. Notez que la condition de démarrage reste inchangée dans tous les cas ; elle ne revient pas à INITIAL.

Si vous ne fournissez pas votre propre version de yywrap(), alors vous devez soit utiliser %option noyywrap auquel cas l'analyseur se comporte comme si yywrap() renvoyait 1), soit éditer les liens avec -lfl pour obtenir la version par défaut de la routine, qui renvoie toujours 1.

Trois routines sont disponibles pour effectuer l'analyse sur des tampons présents en mémoire plutôt que sur des fichiers : yy_scan_string(), yy_scan_bytes()et yy_scan_buffer(). Voyez la discussion à leur sujet plus bas dans la section Tampons d'Entrée Multiples.

L'analyseur écrit sa sortie ECHO dans le yyout global (par défaut stdout), qui peut être redéfini par l'utilisateur simplement en l'affectant à un autre pointeur de FILE.  

CONDITIONS DE DÉMARRAGE

flex fournit un mécanisme pour activer conditionnellement des règles. Toute règle dont le motif est préfixé avec « <cdd> » ne sera active que lorsque l'analyseur est dans la condition de démarrage appelée « cdd ». Par exemple,

    <STRING>[^"]*        { 
                /* manger le corps de la chaîne de caractères ... */
                ...
                }

ne sera active que lorsque l'analyseur est dans la condition de démarrage « STRING », et

    <INITIAL,STRING,QUOTE>\.       { /* traiter un escape ... */
                ...
                }

ne sera active que lorsque la condition de démarrage courante est « INITIAL », « STRING » ou « QUOTE ».

Les conditions de démarrage sont déclarées dans la section de définitions (la première) de l'entrée en utilisant des lignes non indentées commençant soit par %s, soit par %x, suivi d'une liste de noms. %s déclare des conditions de démarrage inclusives, %x des conditions de démarrage exclusives. Une condition de démarrage est activée en utilisant l'action BEGIN. Jusqu'au moment où la prochaine section BEGIN est exécutée, les règles possédant la condition de démarrage donnée seront actives et les règles avec d'autres conditions de démarrage seront inactives. Si la condition de démarrage est inclusive, alors les règles ne possédant aucune condition de démarrage seront également actives. Si elle est exclusive, alors seules les règles qualifiées par la condition de démarrage seront actives. Un ensemble de règles contingentes à la même condition de démarrage exclusive décrit un analyseur qui est indépendant de n'importe quelle autre règle de l'entrée de flex. De ce fait, les conditions de démarrage exclusives facilitent la spécification de « mini-analyseurs » qui analysent des portions de l'entrée qui sont syntaxiquement différentes du reste (p.ex. les commentaires).

Si la distinction entre les conditions de démarrage inclusives et exclusives est toujours un peu vague pour vous, voici un exemple simple illustrant les relations entre les deux. L'ensemble de règles


    %s exemple
    %%

    <exemple>foo   faire_quelque_chose();

    bar            quelque_chose_d_autre();

est équivalent à

    %x exemple
    %%

    <exemple>foo   faire_quelque_chose();

    <INITIAL,exemple>bar    quelque_chose_d_autre();

Sans le qualificatif <INITIAL,exemple>, le motif bar du second exemple ne serait pas actif (c.-à-d. ne pourrait être reconnu) lorsqu'on se trouve dans la condition de démarrage exemple. Pourtant, si nous avions juste utilisé <exemple> pour qualifier bar, alors il ne serait actif que dans exemple et pas dans INITIAL, alors que dans le premier exemple il serait actif dans les deux, car dans le premier exemple la condition de démarrage exemple est inclusive (%s).

Notez également que le spécificateur spécial de condition de démarrage <*> convient pour n'importe quelle condition de démarrage. Ainsi, l'exemple au-dessus aurait également pu être écrit


    %x exemple
    %%

    <exemple>foo   faire_quelque_chose();

    <*>bar    quelque_chose_d_autre();

La règle par défaut (pour mettre en ECHO tout caractère non reconnu) reste active dans les conditions de démarrage. Elle est équivalente à :


    <*>.|\n     ECHO;

BEGIN(0) revient à l'état original quand seules les règles sans conditions de démarrage sont actives. Cet état peut également être référencé par la condition de démarrage « INITIAL », et BEGIN(INITIAL) est par conséquent équivalent à BEGIN(0). (Les parenthèses autour du nom de la condition de démarrage ne sont pas requises mais on estime que cela fait partie d'un bon style à adopter.)

Les actions BEGIN peuvent également être fournies en tant que code indenté au début de la section de règles. Par exemple, le code suivant forcera l'analyseur à entrer dans la condition de démarrage « SPECIAL » à chaque fois que yylex() est appelée et que la variable globale enter_special est vraie :


            int entrer_special;

    %x SPECIAL
    %%
            if ( entrer_special )
                BEGIN(SPECIAL);

    <SPECIAL>blablabla
    ...d'autres règles suivent...

Pour illustrer l'utilisation des conditions de démarrage, voici un analyseur qui fournit deux interprétations différentes d'une chaîne de caractères comme « 123.456 ». Par défaut, il la traitera comme trois éléments lexicaux, l'entier « 123 », un point (« . »), et l'entier « 456 ». Mais si la chaîne est précédée plus tôt dans la ligne par la chaîne de caractères « flottants-attendus », il la traitera comme un unique élément lexical, le nombre flottant 123.456 :


    %{
    #include <math.h>
    %}
    %s attendu

    %%
    flottants-attendus        BEGIN(attendu);

    <attendu>[0-9]+"."[0-9]+      {
                printf( "flottant trouvé, = %f\n",
                        atof( yytext ) );
                }
    <attendu>\n           {
                /* c'est la fin de la ligne, ainsi
                 * nous avons besoin d'un autre « nombre-attendu »
                 * avant que nous ne reconnaissions d'autres
                 * nombres
                 */
                BEGIN(INITIAL);
                }

    [0-9]+      {
                printf( "entier trouvé, = %d\n",
                        atoi( yytext ) );
                }

    "."         printf( "point trouvé\n" );

Voici un analyseur qui reconnaît (et élimine) les commentaires C tout en maintenant un comptage de la ligne d'entrée courante.

    %x commentaire
    %%
            int num_ligne = 1;

    "/*"         BEGIN(commentaire);

    <commentaire>[^*\n]*        /* manger tout ce qui n'est pas un '*' */
    <commentaire>"*"+[^*/\n]*   /* manger les '*' non suivis d'un '/' */
    <commentaire>\n             ++num_ligne;
    <commentaire>"*"+"/"        BEGIN(INITIAL);

Cet analyseur a du mal à reconnaître le plus de texte possible avec chaque règle. En général, lorsque vous essayez d'écrire un analyseur très rapide, essayez de détecter le plus possible dans chaque règle, puisque cela offre un grand gain.

Notez que les noms des conditions de démarrage sont réellement des valeurs entières et peuvent être stockées en tant que telles. Ainsi, le code précédent pourrait être étendu de la manière suivante :


    %x commentaire foo
    %%
            int num_ligne = 1;
            int appelant_commentaire;

    "/*"         {
                 appelant_commentaire = INITIAL;
                 BEGIN(commentaire);
                 }

    ...

    <foo>"/*"    {
                 appelant_commentaire = foo;
                 BEGIN(commentaire);
                 }

    <commentaire>[^*\n]*        /* manger tout ce qui n'est pas un '*' */
    <commentaire>"*"+[^*/\n]*   /* manger les '*' non suivis d'un '/' */
    <commentaire>\n             ++num_ligne;
    <commentaire>"*"+"/"        BEGIN(appelant_commentaire);

En outre, vous pouvez accéder à la condition de démarrage courante en utilisant la macro à valeurs entières YY_START. Par exemple, les affectations ci-dessus pour appelant_commentaire pourraient à la place être écrites

    appelant_commentaire = YY_START;

Flex fournit YYSTATE en tant qu'alias pour YY_START (puisque c'est ce qui est utilisé par le lex de AT&T).

Notez que les conditions de démarrage n'ont pas leur propre espace de noms ; les %s et %x déclarent des noms de la même manière que les #define.

Finalement, voici un exemple de la façon de détecter les chaînes de caractères protégées de style C en utilisant des conditions de démarrage exclusives, qui inclut les séquences d'échappement développées (mais pas la vérification de longueur d'une chaîne de caractères) :


    %x str

    %%
            char string_buf[MAX_STR_CONST];
            char *string_buf_ptr;


    \"      string_buf_ptr = string_buf; BEGIN(str);

    <str>\"        { /* guillemets de terminaison détectés - fini */
            BEGIN(INITIAL);
            *string_buf_ptr = '\0';
            /* renvoie le type d'élément lexical « chaîne constante » 
             * et sa valeur à l'analyseur syntaxique
             */
            }

    <str>\n        {
            /* erreur - constante chaîne de caractères non terminée */
            /* générer un message d'erreur */
            }

    <str>\\[0-7]{1,3} {
            /* séquence d'échappement octale */
            int résultat;

            (void) sscanf( yytext + 1, "%o", &résultat );

            if ( résultat > 0xff )
                    /* erreur, la constante est hors limite */

            *string_buf_ptr++ = résultat;
            }

    <str>\\[0-9]+ {
            /* générer une erreur - mauvaise séquence d'échappement ; 
             * quelque chose comme '\48' ou '\0777777'
             */
            }

    <str>\\n  *string_buf_ptr++ = '\n';
    <str>\\t  *string_buf_ptr++ = '\t';
    <str>\\r  *string_buf_ptr++ = '\r';
    <str>\\b  *string_buf_ptr++ = '\b';
    <str>\\f  *string_buf_ptr++ = '\f';

    <str>\\(.|\n)  *string_buf_ptr++ = yytext[1];

    <str>[^\\\n\"]+        {
            char *yptr = yytext;

            while ( *yptr )
                    *string_buf_ptr++ = *yptr++;
            }

Souvent, comme dans certains des exemples plus haut, vous finissez par écrire un tas de règles qui sont toutes précédées par la(les) même(s) condition(s) de démarrage. Flex rend cela plus facile et plus propre en introduisant une notion de portée de condition de démarrage. Une portée de condition de démarrage commence par :


    <CDDs>{

CDDs est une liste d'une ou de plusieurs conditions de démarrage. À l'intérieur de la portée de la condition de démarrage, chaque règle se voit automatiquement appliquer le préfixe <CDDs> , jusqu'à un '}' qui correspond au '{' initial. Ainsi, par exemple,

    <ESC>{
        "\\n"   return '\n';
        "\\r"   return '\r';
        "\\f"   return '\f';
        "\\0"   return '\0';
    }

est équivalent à :

    <ESC>"\\n"  return '\n';
    <ESC>"\\r"  return '\r';
    <ESC>"\\f"  return '\f';
    <ESC>"\\0"  return '\0';

Les portées de conditions de démarrage peuvent être imbriquées.

Trois routines sont disponibles pour la manipulation de conditions de démarrage :

void yy_push_state(int nouvel_état)
empile la condition de démarrage actuelle au sommet de la pile de conditions de démarrage, et passe dans le nouvel_état comme si vous aviez utilisé BEGIN nouvel_état (rappelez-vous que les noms de conditions de démarrage sont également des entiers).
void yy_pop_state()
dépile le sommet de la pile et y va via BEGIN.
int yy_top_state()
renvoie le sommet de la pile sans altérer le contenu de celle-ci.

La pile de conditions de démarrage grandit dynamiquement et ne souffre ainsi aucune limitation de taille intégrée. Si la mémoire est épuisée, l'exécution du programme échoue.

Pour utiliser des piles de conditions de démarrage, votre analyseur doit inclure une directive %option stack (voir Options plus bas).

 

TAMPONS D'ENTRÉE MULTIPLES

Certains analyseurs (comme ceux qui supportent les fichiers « include ») requièrent la lecture à partir de plusieurs flux d'entrée. Comme les analyseurs flex font beaucoup de mise en mémoire tampon (buffering), on ne peut contrôler d'où la prochaine entrée sera lue en écrivant simplement un YY_INPUT qui est sensible au contexte d'analyse. YY_INPUT n'est appelé que lorsque l'analyseur atteint la fin de son tampon, ce qui peut se produire longtemps après la détection d'une instruction comme un « include » qui requiert le changement de source d'entrée.

Pour régler ces types de problèmes, flex fournit un mécanisme pour la création et la commutation entre de multiples tampons d'entrée. Un tampon d'entrée est créé en utilisant :


    YY_BUFFER_STATE yy_create_buffer( FILE *fichier, int taille )

qui prend un pointeur vers un FILE et une taille et crée un tampon associé au fichier donné et assez grand pour contenir taille caractères (en cas de doute, utilisez YY_BUF_SIZE pour la taille). Il renvoie un gestionnaire YY_BUFFER_STATE, qui peut ensuite être passé à d'autres routines (voir plus bas). Le type YY_BUFFER_STATE est un pointeur vers une structure opaque struct yy_buffer_state, et vous pouvez ainsi initialiser sans danger les variables YY_BUFFER_STATE à ((YY_BUFFER_STATE) 0) si vous le souhaitez, et également vous référer à la structure opaque afin de déclarer correctement les tampons d'entrée présents dans des fichiers sources différents de ceux de votre analyseur. Notez que le pointeur de FILE dans l'appel à yy_create_buffer n'est utilisé qu'en tant que valeur pour yyin vue par YY_INPUT ; si vous redéfinissez YY_INPUT de sorte qu'elle n'utilise plus yyin, alors vous pouvez passer sans danger un pointeur de FILE nul à yy_create_buffer. Vous pouvez sélectionner un tampon particulier à examiner en utilisant :

    void yy_switch_to_buffer( YY_BUFFER_STATE nouveau_tampon )

substitue le tampon d'entrée de l'analyseur de sorte que les éléments lexicaux suivants proviendront du nouveau_tampon. Notez que yy_switch_to_buffer() peut être utilisée par yywrap() pour tout régler pour une analyse ininterrompue, au lieu d'ouvrir un nouveau fichier et d'y faire pointer yyin. Notez également que la commutation de sources d'entrée via soit yy_switch_to_buffer(), soit yywrap(), ne change pas la condition de démarrage.

    void yy_delete_buffer( YY_BUFFER_STATE tampon )

est utilisée pour réclamer l'espace de stockage associé à un tampon ( tampon peut être nul, auquel cas la routine ne fait rien). Vous pouvez également effacer le contenu actuel d'un tampon en utilisant :

    void yy_flush_buffer( YY_BUFFER_STATE tampon )

Cette fonction élimine le contenu du tampon, de sorte que la prochaine fois que l'analyseur essaiera de reconnaître un élément lexical à partir du tampon, il remplira d'abord le tampon en utilisant YY_INPUT.

yy_new_buffer() est un alias pour yy_create_buffer(), fourni pour la compatibilité avec l'utilisation C++ de new et de delete pour la création et la destruction d'objets dynamiques.

Finalement, la macro YY_CURRENT_BUFFER renvoie un gestionnaire YY_BUFFER_STATE vers le tampon actuel.

Voici un exemple de l'utilisation de ces fonctionnalités pour l'écriture d'un analyseur qui développe les fichiers d'inclusion (la caractéristique <<EOF>> est traitée plus bas) :


    /* l'état « incl  » est utilisé pour prendre le nom
     * d'un fichier d'inclusion
     */
    %x incl

    %{
    #define PROFONDEUR_INCLUSION_MAX 10
    YY_BUFFER_STATE pile_inclusion[PROFONDEUR_INCLUSION_MAX];
    int ptr_pile_inclusion = 0;
    %}

    %%
    include             BEGIN(incl);

    [a-z]+              ECHO;
    [^a-z\n]*\n?        ECHO;

    <incl>[ \t]*      /* manger les blancs */
    <incl>[^ \t\n]+   { /* nom du fichier include obtenu */
            if ( ptr_pile_inclusion >= PROFONDEUR_INCLUSION_MAX )
                {
                fprintf( stderr, "Includes imbriqués trop profondément");
                exit( 1 );
                }

            pile_inclusion[ptr_pile_inclusion++] =
                YY_CURRENT_BUFFER;

            yyin = fopen( yytext, "r" );

            if ( ! yyin )
                error( ... );

            yy_switch_to_buffer(
                yy_create_buffer( yyin, YY_BUF_SIZE ) );

            BEGIN(INITIAL);
            }

    <<EOF>> {
            if ( --ptr_pile_inclusion < 0 )
                {
                yyterminate();
                }

            else
                {
                yy_delete_buffer( YY_CURRENT_BUFFER );
                yy_switch_to_buffer(
                     pile_inclusion[ptr_pile_inclusion] );
                }
            }

Trois routines sont disponibles pour faire en sorte que les tampons d'entrée examinent des chaînes de caractères présentes en mémoire plutôt que des fichiers. Elle créent toutes un nouveau tampon d'entrée pour l'examen de la chaîne de caractères, et renvoie un gestionnaire YY_BUFFER_STATE correspondant (que vous devriez effacer avec yy_delete_buffer() quand vous en avez terminé avec lui). Elles passent également dans le nouveau tampon en utilisant yy_switch_to_buffer(), de sorte que le prochain appel à yylex() débutera l'examen de la chaîne de caractères.
yy_scan_string(const char *str)
analyse une chaîne de caractères terminée par un NUL.
yy_scan_bytes(const char *octets, int longueur)
analyse longueur octets (ce qui peut inclure des NUL) débutant à l'emplacement octet.

Notez que ces deux fonctions créent et examinent une copie de la chaîne de caractères ou des octets. (Cela peut être préférable, puisque yylex() modifie le contenu du tampon qu'il examine). Vous pouvez éviter la copie en utilisant

yy_scan_buffer(char *base, yy_size_t taille)
qui examine à la place le tampon commençant à base, constitué de taille octets, ses deux derniers octets devant être YY_END_OF_BUFFER_CHAR (NUL ASCII). Ces deux derniers octets ne sont pas examinés ; ainsi, l'examen est effectuée de base[0] à base[taille-2], inclusivement.
Si vous n'arrivez pas à définir base de cette manière (c.-à-d. si vous oubliez les deux octets YY_END_OF_BUFFER_CHAR finaux), alors yy_scan_buffer() renvoie un pointeur nul au lieu de créer un nouveau tampon d'entrée.
Le type yy_size_t est un type intégral vers lequel vous pouvez transtyper une expression entière reflétant la taille du tampon.

 

RÈGLES DE FIN-DE-FICHIER

La règle spéciale « <<EOF>> » indique les actions qui doivent être entreprises quand une fin de fichier est rencontrée et que yywrap() renvoie autre chose que zéro (c.-à-d. n'indique aucun autre fichier à traiter). L'action doit se terminer en faisant l'une des quatre choses suivantes :
-
affecter yyin à un nouveau fichier d'entrée (dans des versions précédentes de flex, après avoir effectué l'affectation vous aviez à appeler l'action spéciale YY_NEW_FILE; cela n'est plus nécessaire);
-
exécuter une instruction return;
-
exécuter l'action spéciale yyterminate();
-
ou passer dans un nouveau tampon en utilisant yy_switch_to_buffer() comme montré dans l'exemple au-dessus.

Les règles <<EOF>> ne peuvent être utilisées avec d'autres motifs ; elles ne peuvent être qualifiées qu'avec une liste de conditions de démarrage. Si une règle <<EOF>> non qualifiée est fournie, elle s'applique à toutes les conditions de démarrage qui ne possèdent pas encore d'actions <<EOF>>. Pour spécifier une règle <<EOF>> uniquement pour la condition de démarrage initiale, utilisez


    <INITIAL><<EOF>>

Ces règles sont utiles pour capturer des choses comme des commentaires non fermés. Un exemple:


    %x quote
    %%

    ...d'autres règles pour le traitement des guillemets...

    <quote><<EOF>>   {
             error( "guillemet non terminé" );
             yyterminate();
             }
    <<EOF>>  {
             if ( *++listefichiers )
                 yyin = fopen( *listefichiers, "r" );
             else
                yyterminate();
             }

 

MACROS DIVERSES

La macro YY_USER_ACTION peut être définie pour fournir une action qui est toujours exécutée avant l'action de la règle reconnue. Par exemple, elle pourrait être définie pour appeler une routine qui convertit yytext en minuscule. Quand YY_USER_ACTION est invoquée, la variable yy_act donne le numéro de la règle reconnue (les règles sont numérotées à partir de 1). Supposez que vous vouliez effectuer un profilage de la fréquence de reconnaissance de chacune de vos règles. Le code suivant fera l'affaire :

    #define YY_USER_ACTION ++ctr[yy_act]

ctr est un tableau qui conserve le nombre d'occurrences des différentes règles. Notez que la macro YY_NUM_RULES fournit le nombre total de règles (incluant la règle par défaut, même si vous utilisez -s), et ainsi une déclaration correcte pour ctr est :

    int ctr[YY_NUM_RULES];

La macro YY_USER_INIT peut être définie pour fournir une action qui est toujours exécutée avant la première analyse (et avant que les initialisations internes de l'analyseur ne soient effectuées). Par exemple, elle pourrait être utilisée pour appeler une routine afin de lire à partir d'une table de données, ou ouvrir un fichier journal.

La macro yy_set_interactive(est_interactif) peut être utilisée pour contrôler si le tampon actuel est considéré interactif. Un tampon interactif est traité plus lentement, mais doit être utilisé lorsque la source d'entrée de l'analyseur est effectivement interactive pour éviter des problèmes dus à l'attente pour remplir les tampons (voyez la discussion sur l'attribut -I plus bas). Une valeur non nulle dans l'invocation de macro marque le tampon comme étant interactif, une valeur nulle non interactif. Notez que l'utilisation de cette macro surcharge %option always-interactive et %option never-interactive (voir Options en dessous). yy_set_interactive() doit être invoquée avant de commencer à examiner le tampon qui doit (ou pas) être considéré interactif.

La macro yy_set_bol(at_bol) peut être utilisée pour contrôler si le contexte d'analyse du tampon courant pour la prochaine mise en correspondance d'un élément lexical est effectuée comme si l'on était au début d'une ligne. Un argument de macro non nul ancre les règles avec '^' actif, alors qu'un argument rend les règles '^' inactives.

La macro YY_AT_BOL() renvoie vrai si le prochain élément lexical détecté à partir du tampon courant a les règles '^' actives, ou faux sinon.

Dans l'analyseur généré, les actions sont toutes regroupées dans une grande instruction switch, et séparées en utilisant YY_BREAK, qui peut être redéfinie. Par défaut, c'est simplement un « break », pour séparer chaque action de règle des règles suivantes. Redéfinir YY_BREAK permet, par exemple, aux utilisateurs C++ de faire en sorte que des #define YY_BREAK ne fassent rien (tout en étant très attentif au fait que chaque règle se termine par un « break » ou un « return » !) pour éviter d'avoir à souffrir d'avertissements dus à des instructions hors d'atteinte lorsqu'une action de règle se termine par un « return », le YY_BREAK est inaccessible.  

VALEURS DISPONIBLES POUR L'UTILISATEUR

Cette section résume les différentes valeurs disponibles pour l'utilisateur dans les actions des règles.
-
char *yytext conserve le texte de l'élément lexical courant. Il peut être modifié mais non allongé (vous ne pouvez lui ajouter des caractères à la fin).
Si la directive spéciale %array apparaît dans la première section de la description de l'analyseur, alors yytext est déclaré au lieu de cela sous la forme charyytext[YYLMAX],YYLMAX est une définition de macro que vous pouvez redéfinir dans la première section si vous n'aimez pas la valeur par défaut (généralement 8 Ko). Utiliser %array résulte en des analyseurs un peu plus lents, mais la valeur de yytext est alors immunisée contre les appels à input() et unput(), qui peuvent détruire sa valeur quand yytext est un pointeur de caractère. L'opposé de %array est %pointer, qui est le défaut.
Vous ne pouvez pas utiliser %array lors de la génération de classes d'analyseurs C++ (le drapeau -+).
-
int yyleng conserve la taille de l'élément lexical courant.
-
FILE *yyin est le fichier à partir duquel lit flex par défaut. Il peut être redéfini mais cela n'a de sens qu'avant que l'analyse ne commence, ou qu'après qu'un EOF a été rencontré. Le changer au beau milieu de l'analyse donnera des résultats inattendus car flex place son entrée dans une mémoire tampon ; utilisez yyrestart() à la place. Une fois que l'analyse se termine suite à la détection d'un EOF, vous pouvez affecter yyin au nouveau fichier d'entrée, et ensuite ré-appeler l'analyseur pour continuer l'analyse.
-
void yyrestart( FILE *nouveau_fichier ) peut être appelé pour faire pointer yyin vers le nouveau fichier d'entrée. Le passage au nouveau fichier est immédiat (toute entrée précédemment mise en tampon est perdue). Notez qu'appeler yyrestart() avec yyin comme argument jette par conséquent le contenu du tampon d'entrée courant et continue à examiner le même fichier d'entrée.
-
FILE *yyout est le fichier dans lequel les actions ECHO sont effectuées. Il peut être réaffecté par l'utilisateur.
-
YY_CURRENT_BUFFER renvoie un gestionnaire YY_BUFFER_STATE vers le tampon courant.
-
YY_START renvoie une valeur entière correspondant à la condition de démarrage courante. Vous pouvez ensuite utiliser cette valeur avec BEGIN pour revenir à cette condition de démarrage.
 

INTERFACE AVEC YACC

Une des utilisations principales de flex est de servir de compagnon au générateur d'analyseurs syntaxiques yacc. Les analyseurs syntaxiques yacc s'attendent à appeler une routine appelée yylex() pour trouver le prochain élément lexical d'entrée. La routine est supposé renvoyer le type de l'élément lexical suivant en plus de placer toute valeur associée dans la variable globale yylval. Pour utiliser flex avec yacc, il faut spécifier l'option -d de yacc pour lui indiquer de générer le fichier y.tab.h qui contient les définitions de tous les %tokens apparaissant dans l'entrée de yacc. Ce fichier est ensuite inclus dans l'analyseur flex. Par exemple, si l'un des éléments lexicaux est « TOK_NUMBER », une partie de l'analyseur lexical pourrait ressembler à ceci :

    %{
    #include "y.tab.h"
    %}

    %%

    [0-9]+        yylval = atoi( yytext ); return TOK_NUMBER;

 

OPTIONS

flex possède les options suivantes :
-b
Générer de l'information de sauvegarde dans lex.backup. C'est une liste d'états de l'analyseur qui requiert une sauvegarde et les caractères d'entrée pour lesquels ils la font. En ajoutant des règles, on peut supprimer les états de sauvegarde. Si tous les états de sauvegarde sont éliminés et que -Cf ou -CF est utilisé, l'analyseur généré s'exécutera plus rapidement (voyez l'attribut -p). Seuls les utilisateurs qui veulent empêcher l'utilisation inutile du moindre cycle processeur par leurs analyseurs peuvent se soucier de cette option. (Voyez la section sur les Considérations De Performance plus bas.)
-c
est une option dépréciée, qui ne fait rien du tout, et qui est incluse pour la conformité POSIX.
-d
fait tourner l'analyseur généré en mode de débogage. À chaque fois qu'un motif est reconnu et que la variable globale yy_flex_debug est non nulle (ce qui est le défaut), l'analyseur écrira sur stderr une ligne de la forme :

    --accepting rule at line 53 ("le texte reconnu")

Le numéro de ligne se réfère à l'emplacement de la règle dans le fichier définissant l'analyseur (c.-à-d. le fichier qui nourrissait flex). Les messages sont également générés quand l'analyseur revient en arrière, accepte la règle par défaut, atteint la fin de son tampon d'entrée (ou rencontre un NUL ; à ce moment, les deux se ressemblent du point de vue de l'analyseur), ou atteint un EOF.
-f
spécifie un analyseur rapide. Aucune compression de table n'est effectuée et stdio est évité. Cela résulte en un analyseur gros mais rapide. Cette option est équivalente à -Cfr (voir plus bas).
-h
génère un résumé d'« aide » relatif aux options de flex sur stdout et se termine ensuite. -? et --help sont des synonymes pour -h.
-i
indique à flex de générer un analyseur insensible à la casse. La casse des lettres données dans les motifs d'entrée de flex sera ignorée, et les éléments lexicaux d'entrée seront reconnus sans faire attention à la casse. Le texte détecté fourni dans yytext conservera sa casse (c.-à-d. qu'il ne sera pas plié).
-l
active la compatibilité maximale avec l'implémentation originale de lex par AT&T. Notez que cela ne signifie pas une compatibilité totale. L'utilisation de cette option coûte beaucoup en terme de performance, et elle ne peut être utilisée avec les options -+,-f,-F,-Cf ou -CF. Pour des détails sur les différentes compatibilités fournies, voyez la section « Incompatibilités avec Lex et POSIX » plus bas. Cette option résulte également en la définition du nom YY_FLEX_LEX_COMPAT via un #define dans l'analyseur généré.
-n
est une autre option dépréciée, qui ne fait rien du tout, incluse uniquement pour la conformité POSIX.
-p
génère un rapport de performances sur stderr. Le rapport consiste en des commentaires concernant les caractéristiques du fichier d'entrée de flex qui provoqueront une perte importante de performance dans l'analyseur résultant. Si vous fournissez deux fois le drapeau, vous obtiendrez également des commentaires sur les caractéristiques qui mènent à des pertes de performance mineures.
Notez que l'utilisation de REJECT, %option yylineno, et d'un contexte de queue variable (voir la section Défectuosités / Bogues plus bas) occasionne une pénalité de performance substantielle ; l'utilisation de yymore(), l'opérateur ^ , et le drapeau -I occasionnent des pénalités de performance mineures.
-s
provoque la suppression de la règle par défaut (cette entrée de l'analyseur non reconnue est transmise à stdout). Si l'analyseur rencontre une entrée qui ne correspond à aucune de ses règles, il avorte avec une erreur. Cette option est utile pour trouver des tours dans un ensemble de règles d'analyseur.
-t
indique à flex d'écrire l'analyseur qu'il génère sur la sortie standard plutôt que dans lex.yy.c.
-v
spécifie que flex devrait écrire sur stderr un résumé de statistiques concernant l'analyseur qu'il génère. La plupart des statistiques n'ont aucune signification pour l'utilisateur accidentel de flex, mais la première ligne identifie la version de flex (la même que rapportée par -V), et la ligne suivante les drapeaux utilisés lors de la génération de l'analyseur, incluant ceux qui sont utilisés par défaut.
-w
supprime les messages d'avertissement.
-B
indique à flex de générer un analyseur batch , l'opposé des analyseurs interactifs générés avec -I (voir plus bas). En général, vous utilisez -B quand vous êtes certain que votre analyseur ne sera jamais utilisé de façon interactive, et que vous voulez obtenir un petit peu plus de performance. Si votre but est plutôt d'obtenir beaucoup plus de performance, vous devriez utiliser les options -Cf ou -CF (discutées plus bas), qui activent de toute façon -B automatiquement.
-F
spécifie que la représentation de table d'analyse rapide devrait être utilisée (et stdio évité). Cette représentation est presque aussi rapide que la représentation complète de la table (-f), et pour certains jeux de motifs sera considérablement réduite (et pour d'autres accrue). En général, si le jeu de motifs contient à la fois des « mots-clés » et une règle attrape-tout d'« identificateur », comme dans :

    "case"    return TOK_CASE;
    "switch"  return TOK_SWITCH;
    ...
    "default" return TOK_DEFAULT;
    [a-z]+    return TOK_ID;

alors vous devriez plutôt utiliser la représentation complète de la table. Si seule la règle « identificateur » est présente et que vous utilisez ensuite une table de hachage ou quelque chose du genre pour détecter les mots-clés, vous devriez plutôt utiliser -F.
Cette option est équivalente à -CFr (voir plus bas). Elle ne peut être utilisée avec -+.
-I
indique à flex de générer un analyseur interactif. Un analyseur interactif est un analyseur qui ne regarde en avant pour décider quel élément lexical a été détecté que si c'est absolument nécessaire. Il se trouve que toujours regarder un caractère supplémentaire en avant, même si l'analyseur a déjà vu assez de texte pour désambiguïser l'élément lexical courant, est un peu plus rapide que de regarder en avant uniquement lorsque c'est nécessaire. Mais les analyseurs qui regardent toujours en avant donnent des performances interactives affreuses ; par exemple, quand un utilisateur tape un saut de ligne, il n'est pas reconnu comme tel à moins que l'utilisateur n'entre un autre élément lexical, ce qui signifie souvent taper une autre ligne complète.
Les analyseurs Flex sont interactifs par défaut à moins que vous utilisiez les options -Cf ou -CF de compression de table (voir plus bas). C'est parce que si vous recherchez des hautes performances, vous devriez utiliser l'une de ces options, et que donc si vous ne l'avez pas fait, flex suppose que vous préférez échanger un peu de performance à l'exécution contre un comportement interactif intuitif. Notez également que vous ne pouvez pas utiliser -I conjointement avec -Cf ou -CF. Cette option n'est donc pas réellement nécessaire ; elle est activée par défaut pour tous les cas où elle est permise.
Vous pouvez forcer un analyseur à ne pas être interactif en utilisant -B (voir plus haut).
-L
indique à flex de ne pas générer de directives #line. Sans cette option, flex saupoudre l'analyseur généré de directives #line de sorte que les messages d'erreur dans les actions soient correctement situés en ce qui concerne soit le fichier d'entrée original de flex (si les erreurs sont dues à du code du fichier d'entrée), soit lex.yy.c (si les erreurs sont imputables à flex , vous devriez rapporter ce type d'erreurs à l'adresse électronique fournie plus bas).
-T
fait tourner flex en mode trace. Il générera beaucoup de messages sur stderr concernant la forme de l'entrée et les automates finis non-déterministes et déterministes résultants. Cette option est principalement utilisée pour la maintenance de flex.
-V
affiche le numéro de version de stdout et se termine normalement. --version est un synonyme pour -V.
-7
indique à flex de générer un analyseur 7 bits, c.-à-d. qui ne reconnaît que les caractères de 7 bits en entrée. L'avantage de l'utilisation de -7 est que les tables d'analyse peuvent coûter jusqu'à moitié moins que celles générées en utilisant l'option -8 (voir plus bas). Le désavantage est que des tels analyseurs se bloquent ou se crashent souvent quand leur entrée contient un caractère 8 bits.
Notez, néanmoins, qu'à moins que vous ne génériez votre analyseur en utilisant les options de compression de table -Cf ou -CF , l'utilisation de -7 n'économisera qu'une petite partie seulement de l'espace de table, et rendra votre analyseur beaucoup moins portable. Le comportement par défaut de flex est de générer un analyseur 8 bits à moins que vous n'utilisiez -Cf ou -CF, auquel cas flex générera par défaut des analyseurs 7 bits sauf si votre site a toujours été configuré pour générer des analyseurs 8 bits (ce qui sera souvent le cas avec des sites non situés aux USA). Vous pouvez savoir si flex a généré un analyseur 7 ou 8 bits en inspectant le résumé des drapeaux dans la sortie de -v comme décrite plus haut.
Notez que si vous utilisez -Cfe ou -CFe (ces options de compression de table, mais également l'utilisation de classes d'équivalence comme discuté, voir en-dessous), flex génère toujours par défaut un analyseur 8 bits, puisqu'avec ces options de compression, les tables complètes 8 bits ne sont pas beaucoup plus coûteuses que les tables 7 bits.
-8
indique à flex de générer un analyseur 8 bits, c.-à-d. qui peut reconnaître les caractères 8 bits. Ce drapeau n'est nécessaire que pour les analyseurs générés en utilisant -Cf ou -CF, puisque flex génère de toute façon un analyseur 8 bits par défaut.
Voyez la discussion sur -7 au-dessus pour une explication sur le comportement par défaut de flex, et les compromis entre les analyseurs 7 et 8 bits.
-+
spécifie que vous voulez que flex génère une classe d'analyseur C++. Voyez la section sur Générer des Analyseurs C++ plus bas pour les détails.
-C[aefFmr]
contrôle le degré de compression des tables et, plus généralement, les compromis entre analyseurs petits et analyseurs rapides.
-Ca (« align ») indique à flex de subir des tables plus grandes dans l'analyseur généré afin d'obtenir des performances plus rapides car les éléments des tables sont mieux alignés pour les accès mémoire et le calcul. Sur certaines architectures RISC, le chargement et la manipulation de mots longs est plus efficace que pour des unités de plus petite taille comme des mots courts. Cette option peut doubler la taille des tables utilisées par votre analyseur.
-Ce ordonne à flex de construire des classes d'équivalence, c.-à-d. des groupes de caractères qui ont des propriétés lexicales identiques (par exemple, si la seule apparition de chiffres dans l'entrée de flex est dans la classe de caractères « [0-9] », alors les chiffres '0', '1', ..., '9' seront tous placés dans la même classe d'équivalence. Les classes d'équivalence offrent habituellement des réductions dramatiques dans la taille finale des fichiers de tables/objets (typiquement d'un facteur 2 à 5) et sont assez peu coûteuses en terme de performances (une recherche dans un tableau par caractère examiné).
-Cf spécifie que les tables d'analyse complètes devraient être générées - flex ne devrait pas compresser les table en tirant avantage de fonctions de transition similaires pour différents états.
-CF spécifie que la représentation alternative d'analyseur rapide (décrite au-dessous sous le drapeau -F ) devrait être utilisée. Cette option ne peut être utilisée avec -+.
-Cm ordonne à flex de construire des méta-classes d'équivalence, qui sont des ensembles de classes d'équivalence (ou de caractères, si les classes d'équivalence ne sont pas utilisées) communément utilisées de concert. Les méta-classes d'équivalence offrent souvent un gros gain lors de l'utilisation de tables compressées, mais elles ont un impact modéré sur les performances (un ou deux tests « if » et une recherche dans un tableau par caractère examiné).
-Cr fait éviter à l'analyseur généré l'utilisation de la bibliothèque d'E/S standard (stdio) pour l'entrée. Au lieu d'appeler fread() ou getc(), l'analyseur utilisera l'appel système read(), ce qui résulte en un gain de performance qui varie de système en système, mais qui est en général négligeable à moins que vous n'utilisiez également -Cf ou -CF. Utiliser -Cr peut engendrer un comportement bizarre si, par exemple, vous lisez à partir de yyin en utilisant stdio avant d'appeler l'analyseur (car celui-ci ratera le texte que vos lectures précédentes ont laissé dans le tampon d'entrée de stdio).
-Cr n'a aucun effet si vous définissez YY_INPUT (voyez L'Analyseur Généré plus haut).
Un -C seul spécifie que les tables de l'analyseur doivent être compressées, mais que ni les classes d'équivalence, ni les méta-classes d'équivalence ne doivent être utilisées.
Il n'y a aucun sens à combiner les options -Cf ou -CF et -Cm ensemble - il n'y a aucune possibilité pour les méta-classes d'équivalence si la table n'est pas compressée. À part cela, les options peuvent être librement mélangées, et sont cumulatives.
Le réglage par défaut est -Cem, qui spécifie que flex devrait générer des classes d'équivalence et des méta-classes d'équivalence. Ce réglage fournit le plus haut degré de compression de table. Vous pouvez utiliser des analyseurs s'exécutant plus rapidement au prix de plus grandes tables, où ce qui suit est généralement vrai :

    plus lent et plus court
          -Cem
          -Cm
          -Ce
          -C
          -C{f,F}e
          -C{f,F}
          -C{f,F}a
    plus rapide et plus gros

Notez que les analyseurs avec les plus petites tables sont habituellement générés et compilés le plus rapidement, et que, durant le développement, vous devriez utiliser la compression maximale (par défaut).
-Cfe constitue habituellement un bon compromis entre taille et vitesse pour les analyseurs de production.
-osortie
ordonne à flex d'écrire l'analyseur dans le fichier sortie au lieu de lex.yy.c. Si vous combinez -o avec l'option -t, alors l'analyseur est écrit dans stdout mais ses directives #line (voyez l'option \-L au-dessus) se réfèrent au fichier sortie.
-Ppréfixe
remplace le préfixe par défaut yy utilisé par flex pour tous les noms de variables et de fonctions visibles globalement par préfixe. Par exemple, -Pfoo change le nom yytext en footext. Il substitue également le nom du fichier de sortie par défaut de lex.yy.c par lex.foo.c. Voici tous les noms affectés :

    yy_create_buffer
    yy_delete_buffer
    yy_flex_debug
    yy_init_buffer
    yy_flush_buffer
    yy_load_buffer_state
    yy_switch_to_buffer
    yyin
    yyleng
    yylex
    yylineno
    yyout
    yyrestart
    yytext
    yywrap

(Si vous utilisez un analyseur C++, alors seuls yywrap et yyFlexLexer sont affectés.) À l'intérieur de votre analyseur lui-même, vous pouvez toujours vous référer aux variables et fonctions globales en utilisant n'importe quelle version de leur nom ; mais vu de l'extérieur, elles possèdent le nom modifié.
Cette option vous permet de lier facilement de multiples programmes flex ensemble dans le même exécutable. Notez néanmoins que l'utilisation de cette option renomme également yywrap(), et vous devrez donc soit fournir votre propre version (nommée de façon appropriée) de la routine pour votre analyseur, soit utiliser %option noyywrap, puisque la liaison avec -lfl n'en fournit plus une pour vous par défaut.
-Sfichier_squelette
surcharge le fichier de squelette par défaut à partir duquel flex construit ses analyseurs. Vous n'aurez jamais besoin de cette option à moins que vous ne fassiez de la maintenance ou du développement de flex.

flex fournit également un mécanisme pour contrôler les options à l'intérieur même de la spécification de l'analyseur, plutôt qu'à partir de la ligne de commandes de flex. C'est fait en incluant des directives %option dans la première section de la spécification de l'analyseur. Vous pouvez spécifier de multiples options à l'intérieur d'une même directive %option, et de multiples répertoires dans la première section de votre fichier d'entrée de flex.

La plupart des options sont fournies simplement comme des noms, précédés facultativement du mot « no » (sans caractères d'espacement entre les deux) pour nier leur signification. Un nombre est équivalent à un drapeau de flex, ou à sa négation :


    7bit            option -7 
    8bit            option -8 
    align           option -Ca
    backup          option -b 
    batch           option -B 
    c++             option -+ 

    caseful ou
    case-sensitive  opposé de -i (défaut)

    case-insensitive ou
    caseless        option -i

    debug           option -d
    default         opposé de l'option -s
    ecs             option -Ce
    fast            option -F 
    full            option -f 
    interactive     option -I 
    lex-compat      option -l 
    meta-ecs        option -Cm
    perf-report     option -p 
    read            option -Cr
    stdout          option -t 
    verbose         option -v 
    warn            opposé de l'option -w
                    (utilisez "%option nowarn" pour -w)

    array           équivalent à "%array"
    pointer         équivalent  "%pointer" (défaut)

Certaines %option fournissent des fonctionnalités habituellement non disponibles :
always-interactive
indique à flex de générer un analyseur qui considère que son entrée est toujours « interactive ». Normalement, l'analyseur appelle isatty() sur chaque nouveau fichier d'entrée pour essayer de déterminer si la source d'entrée de l'analyseur est interactive et doit donc être lue un caractère à la fois. Néanmoins, quand cette option est utilisée, aucun appel de ce type n'est effectué.
main
ordonne à flex de fournir un programme main() par défaut pour l'analyseur, qui appelle simplement yylex(). Cette option implique noyywrap (voir au-dessous).
never-interactive
indique à flex de générer un analyseur qui considère que son entrée n'est jamais « interactive » (à nouveau, aucun appel n'est fait à isatty()). C'est l'opposé de always-interactive.
stack
permet l'utilisation de piles de conditions de démarrage (voir Conditions De Démarrage au-dessus).
stdinit
si elle est définie (c.-à-d. %optionstdinit), initialise yyin et yyout à stdin et stdout respectivement, au lieu du nil par défaut. Certains programmes lex existants dépendent de ce comportement, même s'il n'est pas conforme au C ANSI, qui ne requiert pas que stdin et stdout soient des constantes du moment de la compilation.
yylineno
enjoint flex de générer un analyseur qui conserve le numéro de la ligne courante lue à partir de son entrée dans la variable globale yylineno. Cette option est impliquée par %option lex-compat.
yywrap
si non défini (c.-à-d. %option noyywrap), fait que l'analyseur n'appelle pas yywrap() quand il tombe sur une fin de fichier (EOF), mais suppose simplement qu'il n'y a plus de fichiers à examiner (à moins que l'utilisateur ne fasse pointer yyin vers un nouveau fichier et appelle à nouveau yylex()).

flex examine vos actions de règles pour déterminer si vous utilisez les fonctionnalités REJECT ou yymore(). Les options reject et yymore sont disponibles pour surcharger sa décision d'utilisation ou non des options, soit en les définissant (p.ex. %option reject ) pour indiquer que la fonctionnalité est effectivement utilisée, soit en les in-définissant pour indiquer qu'elle n'est en fait pas utilisée (p.ex. %option noyymore).

Trois options prennent des valeurs délimitées par une chaîne de caractères, décalée avec '=':


    %option outfile="ABC"

est équivalent à -oABC, et

    %option prefix="XYZ"

est équivalent à -PXYZ. Finalement,

    %option yyclass="foo"

ne s'applique que lors de la génération d'un analyseur C++ (option -+). Elle informe flex que vous avez dérivé foo comme une sous-classe de yyFlexLexer, de sorte que flex placera vos actions dans la fonction membre foo::yylex() au lieu de yyFlexLexer::yylex(). Elle génère également une fonction membre yyFlexLexer::yylex() qui émet une erreur à l'exécution (en invoquant yyFlexLexer::LexerError()) si elle est appelée. Voyez Générer des Analyseurs C++, plus bas, pour des informations additionnelles.

Certaines options sont disponibles pour les puristes de lint qui veulent supprimer l'apparition de routines non nécessaires dans l'analyseur généré. Chacune des options suivantes, si elle est in-définie (p.ex., %option nounput), résulte en la non-apparition de la routine correspondante dans l'analyseur généré :


    input, unput
    yy_push_state, yy_pop_state, yy_top_state
    yy_scan_buffer, yy_scan_bytes, yy_scan_string

(bien que yy_push_state() et ses amis n'apparaîtront de toute façon pas à moins que vous n'utilisiez %option stack).

 

CONSIDÉRATIONS DE PERFORMANCE

L'objectif de conception principal de flex est de générer des analyseurs à haute performance. Il a été optimisé pour bien traiter de grands ensembles de règles. En plus des effets sur la vitesse de l'analyseur des options de compression de table -C esquissées au-dessus, il y a certaines options/actions qui dégradent les performances. Celles-ci sont, de la plus à la moins coûteuse :

    REJECT
    %option yylineno
    contexte de queue arbitraire

    jeux de motifs qui requièrent une sauvegarde
    %array
    %option interactive
    %option always-interactive

    '^' opérateur début-de-ligne
    yymore()

les trois premières étant toutes assez chères et les deux dernières étant assez bon marché. Notez également que unput() est implémenté en tant qu'appel de routine qui fait potentiellement beaucoup de travail, alors que yyless() est une macro assez bon marché ; ainsi, si vous repoussez simplement un peu de texte en excès que vous avez analysé, utilisez yyless().

REJECT devrait être évité à tout prix quand la performance est importante. C'est une option particulièrement coûteuse.

Se débarrasser de la sauvegarde est une saleté et peut représenter une grosse charge de travail pour un analyseur compliqué. En principe, on commence par utiliser le drapeau -b pour générer un fichier lex.backup. Par exemple, sur l'entrée


    %%
    foo        return TOK_KEYWORD;
    foobar     return TOK_KEYWORD;

le fichier ressemble à ceci :

    State #6 is non-accepting -
     associated rule line numbers:
           2       3
     out-transitions: [ o ]
     jam-transitions: EOF [ \001-n  p-\177 ]

    State #8 is non-accepting -
     associated rule line numbers:
           3
     out-transitions: [ a ]
     jam-transitions: EOF [ \001-`  b-\177 ]

    State #9 is non-accepting -
     associated rule line numbers:
           3
     out-transitions: [ r ]
     jam-transitions: EOF [ \001-q  s-\177 ]

    Compressed tables always back up.

Les toutes premières lignes nous disent qu'il y a un état de l'analyseur dans lequel il peut faire une transition sur un 'o' mais pas sur aucun autre caractère, et que dans cet état, le texte actuellement examiné ne correspond à aucune règle. L'état se produit quand on essaie de reconnaître les règles trouvées aux lignes 2 et 3 du fichier d'entrée. Si l'analyseur est dans cet état et lit ensuite quelque chose d'autre qu'un 'o', il devra revenir en arrière pour trouver une règle qui correspond. Avec un peu de prise de tête, on peut voir que cela doit être l'état dans lequel il est quand il a vu « fo ». Quand cela s'est passé, si quelque chose d'autre qu'un 'o' est vu, l'analyseur devra revenir en arrière pour reconnaître simplement le 'f' (par la règle par défaut).

Le commentaire concernant State #8 indique qu'il y a une problème quand « foob » a été détecté. En effet, pour chaque caractère différent d'un 'a', l'analyseur devra revenir en arrière pour accepter « foo ». De la même façon, le commentaire pour State #9 (état 9) concerne le moment où « fooba » a été détecté et qu'un 'r' ne suit pas.

Le commentaire final nous rappelle que il n'est pas nécessaire de passer par la difficulté de la suppression du retour en arrière des règles à moins d'utiliser -Cf ou -CF, puisqu'il n'y a pas de gain de performance à faire ceci avec des analyseurs compressés.

Le moyen de supprimer le retour arrière est d'ajouter des règles « erreur » :


    %%
    foo         return TOK_KEYWORD;
    foobar      return TOK_KEYWORD;

    fooba       |
    foob        |
    fo          {
                /* fausse alarme, pas réellement un mot-clé */
                return TOK_ID;
                }

Éliminer le retour arrière parmi une liste de mots-clés peut également être effectué en utilisant une règle « attrape-tout » :


    %%
    foo         return TOK_KEYWORD;
    foobar      return TOK_KEYWORD;

    [a-z]+      return TOK_ID;

C'est habituellement la meilleure solution quand c'est approprié.

Les messages de retour arrière ont tendance à s'amonceler. Avec un jeu de règles compliqué, il n'est pas rare d'obtenir des centaines de messages. Si quelqu'un parvient à les déchiffrer, il ne faut qu'une douzaine de règles pour éliminer le retour arrière (bien qu'il soit facile de commettre une faute et d'avoir une règle d'erreur convenant accidentellement à un élément lexical valide. Une fonctionnalité future possible de flex sera d'ajouter automatiquement des règles pour éliminer le retour arrière).

Il est important de garder à l'esprit que vous ne bénéficierez des avantages de l'élimination du retour arrière que si vous éliminez chaque instance de retour arrière. En laisser ne serait-ce qu'une seule ne vous fait rien gagner.

Un contexte de queue variable (où à la fois les parties de tête et de queue n'ont pas une taille fixe) occasionne presque la même perte de performance que REJECT (c.-à-d. substantielle). Dès lors, quand c'est possible, une règle comme :


    %%
    mouse|rat/(cat|dog)   run();

est mieux écrite :

    %%
    mouse/cat|dog         run();
    rat/cat|dog           run();

ou comme

    %%
    mouse|rat/cat         run();
    mouse|rat/dog         run();

Notez qu'ici l'action spéciale '|' ne fournit aucune économie, et peut même aggraver la situation (voyez Défectuosités / Bogues plus bas).

Un autre endroit où l'utilisateur peut améliorer la performance d'un analyseur (et qui est le plus facile à implémenter) provient du fait que le plus long les éléments lexicaux correspondent, le plus vite tournera l'analyseur. C'est parce qu'avec les longs éléments lexicaux, le traitement de la plupart des caractères d'entrée a lieu dans la boucle (courte) d'analyse interne, et ne doit pas souvent effectuer le travail additionnel de réglage de l'environnement d'analyse lexicale (p.ex. yytext) pour l'action. Rappelez-vous l'analyseur pour les commentaires C :


    %x commentaire
    %%
            int num_ligne = 1;

    "/*"         BEGIN(commentaire);

    <commentaire>[^*\n]*
    <commentaire>"*"+[^*/\n]*
    <commentaire>\n             ++num_ligne;
    <commentaire>"*"+"/"        BEGIN(INITIAL);

Il pourrait être accéléré en l'écrivant comme ceci :

    %x commentaire
    %%
            int num_ligne = 1;

    "/*"         BEGIN(commentaire);

    <commentaire>[^*\n]*
    <commentaire>[^*\n]*\n      ++num_ligne;
    <commentaire>"*"+[^*/\n]*
    <commentaire>"*"+[^*/\n]*\n ++num_ligne;
    <commentaire>"*"+"/"        BEGIN(INITIAL);

Maintenant, au lieu que chaque saut de ligne requière le traitement d'une autre action, la reconnaissance des sauts de ligne est « distribuée » sur les autres règles pour que le texte correspondant reste aussi long que possible. Notez que l'ajout de règles ne ralentit pas l'analyseur ! La vitesse de l'analyseur est indépendante du nombre de règles ou (modulo les considérations formulées au début de cette section) de la complexité des règles en ce qui concerne les opérateurs comme '*' et '|'.

Un exemple final de l'accélération d'un analyseur : supposez que vous vouliez examiner un fichier contenant des identificateurs et des mots-clés, un par ligne avec aucun autre caractère supplémentaire, et reconnaître tous les mots-clés. Une première approche naturelle est :


    %%
    asm      |
    auto     |
    break    |
    ... etc ...
    volatile |
    while    /* c'est un mot-clé */

    .|\n     /* ce n'est pas un mot-clé */

Pour éliminer le mouvement « à contre-courant », introduisez une règle attrape-tout :

    %%
    asm      |
    auto     |
    break    |
    ... etc ...
    volatile |
    while    /* c'est un mot-clé */

    [a-z]+   |
    .|\n     /* ce n'est pas un mot-clé */

Maintenant, s'il est garanti qu'il y a exactement un mot par ligne, alors nous pouvons réduire le nombre total de correspondances de moitié en fusionnant la reconnaissance des sauts de ligne avec celle des autres éléments lexicaux :

    %%
    asm\n    |
    auto\n   |
    break\n  |
    ... etc ...
    volatile\n |
    while\n  /* c'est un mot-clé */

    [a-z]+\n |
    .|\n     /* ce n'est pas un mot-clé */

Il faut faire très attention ici, car nous avons maintenant réintroduit du retour arrière dans l'analyseur. En particulier, alors que nous savons qu'il n'y aura jamais de caractères dans le flux d'entrée autres que des lettres ou des sauts de ligne, flex ne peut s'en rendre compte, et il planifiera le besoin éventuel de retour en arrière quand il a examiné un élément lexical comme « auto » et qu'ensuite le caractère suivant est quelque chose d'autre qu'un retour à la ligne ou une lettre. Précédemment, il ne reconnaîtrait que la règle « auto » et aurait fini, mais maintenant il n'a pas de règle « auto », seulement une règle « auto\n ». Pour éliminer la possibilité de retour arrière, nous devrions soit dupliquer toutes les règles mais sans les sauts de ligne finaux, soit, puisque nous ne nous attendons pas à rencontrer une telle entrée et par conséquent ne savons pas comment elle est classifiée, introduire une règle attrape-tout de plus, celle-ci n'incluant pas de saut de ligne :

    %%
    asm\n    |
    auto\n   |
    break\n  |
    ... etc ...
    volatile\n |
    while\n  /* c'est un mot-clé */

    [a-z]+\n |
    [a-z]+   |
    .|\n     /* ce n'est pas un mot-clé */

Compilé avec -Cf, c'est à peu près le plus (rapide) que l'on peut obtenir d'un analyseur flex pour traiter ce problème particulier.

Une note finale : flex est lent lors de la détection des NUL, en particulier quand un élément lexical contient de multiples NUL. Il vaut mieux écrire des règles qui détectent des petites petites quantités de texte si l'on s'attend à ce que le texte inclue souvent des NUL.

Une autre note finale concernant les performances : comme mentionné en haut de la section Comment l'Entrée est Reconnue, le redimensionnement dynamique de yytext pour s'adapter aux immenses éléments lexicaux est un processus lent car il requiert à l'heure actuelle que l'élément lexical (immense) soit réexaminé à partir du début. Par conséquent, si la performance est vitale, vous devriez essayer de détecter de « grandes » quantités de texte, mais pas de quantités « immenses », où la frontière entre les deux se situe environ à 8000 caractères/élément lexical.  

GÉNÉRER DES ANALYSEURS C++

flex fournit deux façons différentes de générer des analyseurs utilisables avec le C++. La première façon est de simplement compiler un analyseur généré par flex en utilisant un compilateur C++ au lieu d'un compilateur C. Vous ne devriez pas rencontrer d'erreurs de compilation (rapportez toutes celles que vous trouvez à l'adresse électronique donnée dans la section Auteur plus bas). Vous pouvez ensuite utiliser du code C++ dans vos actions de règles au lieu de code C. Notez que la source d'entrée par défaut pour votre analyseur reste yyin, et l'écho par défaut est toujours effectué dans yyout. Tous les deux restent des variables FILE * et pas des flux C++.

Vous pouvez également utiliser flex pour générer une classe analyseur C++, en utilisant l'option -+ (ou, de façon équivalente, %option c++), qui est spécifiée automatiquement si le nom de l'exécutable flex se termine par un '+', comme flex++. Quand vous utilisez cette option, flex génère par défaut l'analyseur dans le fichier lex.yy.cc au lieu de lex.yy.c. L'analyseur généré inclut le fichier d'en-tête FlexLexer.h, qui définit l'interface de deux classes C++.

La première classe, FlexLexer, fournit une classe de base abstraite définissant l'interface générale de classe d'analyseur. Elle fournit les fonctions membres suivantes :

const char* YYText()
renvoie le texte de l'élément lexical le plus récemment reconnu, l'équivalent de yytext.
int YYLeng()
renvoie la longueur de l'élément lexical le plus récemment reconnu, l'équivalent de yyleng.
int lineno() const
renvoie le numéro de la ligne d'entrée courante (voyez %option yylineno), ou 1 si %option yylineno n'était pas utilisé.
void set_debug( int drapeau )
Indique l'attribut de débogage pour l'analyseur, équivalent à l'affecter à yy_flex_debug (voir la section Options au-dessus). Notez que vous devez construire l'analyseur en utilisant %option debug pour y inclure des informations de débogage.
int debug() const
renvoie le réglage courant de l'attribut de débogage.

D'autres fonctions également fournies sont les fonctions membres équivalentes à yy_switch_to_buffer(), yy_create_buffer() (bien que le premier argument soit un pointeur d'objet istream* et pas un FILE*), yy_flush_buffer(), yy_delete_buffer(), et yyrestart() (à nouveau, le premier argument est un pointeur d'objet istream*).

La seconde classe définie dans FlexLexer.h est yyFlexLexer, qui est dérivée de FlexLexer. Elle définit les fonctions membres additionnelles suivantes :

yyFlexLexer( istream* arg_yyin = 0, ostream* arg_yyout = 0 )
construit un objet yyFlexLexer en utilisant les flux donnés pour l'entrée et la sortie. S'ils ne sont pas spécifiés, les flux valent cin et cout par défaut, respectivement.
virtual int yylex()
occupe le même rôle que celui de yylex() pour les analyseurs flex ordinaires : elle analyse le flux d'entrée, consomme les éléments lexicaux, jusqu'à ce qu'une action de règle renvoie une valeur. Si vous dérivez une sous-classe S de yyFlexLexer et voulez accéder aux fonctions membres et aux variables de S à l'intérieur de yylex(), alors vous devrez utiliser %option yyclass=S pour indiquer à flex que vous utiliserez cette sous-classe au lieu de yyFlexLexer. Dans ce cas, plutôt que de générer yyFlexLexer::yylex(), flex génère S::yylex() (et génère également yyFlexLexer::yylex() trivial qui appelle yyFlexLexer::LexerError() si elle est appelée).
virtual void switch_streams(istream* nouveau_in = 0,
ostream* nouveau_out = 0) réaffecte yyin à nouveau_in (si non-nil) et yyout à nouveau_out (dito), effaçant le tampon d'entrée précédent si yyin est réaffecté.
int yylex( istream* nouveau_in, ostream* nouveau_out = 0 )
substitue d'abord les flux d'entrée via switch_streams( nouveau_in, nouveau_out ) et renvoie ensuite la valeur de yylex().

En plus, yyFlexLexer définit les fonctions virtuelles protégées suivantes que vous pouvez redéfinir dans des classes dérivées pour configurer finement l'analyseur :

virtual int LexerInput( char* tampon, int taille_max )
lit jusqu'à taille_max caractères dans tampon et renvoie le nombre de caractères lus. Pour indiquer la fin de l'entrée, renvoie 0 caractères. Notez que les analyseurs « interactifs » (voir les drapeaux -B et -I ) définissent la macro YY_INTERACTIVE. Si vous redéfinissez LexerInput() et devez entreprendre différentes actions en fonction du caractère interactif ou non de la source d'entrée que l'analyseur pourrait être en train d'analyser, vous pouvez tester la présence de ce nom via un #ifdef.
virtual void LexerOutput( const char* tampon, int taille )
écrit taille caractères à partir du tampon, qui, bien que terminé par NUL, peut également contenir des NUL « internes » si les règles de l'analyseur peuvent reconnaître du texte comportant des NUL.
virtual void LexerError( const char* msg )
rapporte un message d'erreur fatal. La version par défaut de cette fonction écrit le message sur le flux cerr et se termine.

Notez qu'un objet yyFlexLexer contient son état d'analyse entier. Par conséquent, vous pouvez utiliser de tels objets pour créer des analyseurs réentrants. Vous pouvez instancier de multiples instances de la même classe yyFlexLexer, et vous pouvez également combiner de multiples classes d'analyseurs C++ dans le même programme en utilisant l'option -P discutée plus haut.

Finalement, notez que la fonctionnalité %array n'est pas disponible pour les classes d'analyseurs C++ ; vous devez utiliser %pointer (le défaut).

Voici un exemple d'un simple analyseur C++ :


        // Un exemple de l'utilisation de la classe d'analyseurs C++ 
        // de flex.

    %{
    int mon_no_ligne = 0;
    %}

    chaîne  \"[^\n"]+\"

    espbl   [ \t]+

    alpha   [A-Za-z]
    chiffre [0-9]
    nom    ({alpha}|{chiffre}|\$)({alpha}|{chiffre}|[_.\-/$])*
    num1    [-+]?{chiffre}+\.?([eE][-+]?{chiffre}+)?
    num2    [-+]?{chiffre}*\.{chiffre}+([eE][-+]?{chiffre}+)?
    nombre  {num1}|{num2}

    %%

    {espbl}    /* passer les blancs et les tabulations */

    "/*"    {
            int c;

            while((c = yyinput()) != 0)
                {
                if(c == '\n')
                    ++mon_no_ligne;

                else if(c == '*')
                    {
                    if((c = yyinput()) == '/')
                        break;
                    else
                        unput(c);
                    }
                }
            }

    {nombre}  cout << "nombre " << YYText() << '\n';

    \n       mon_no_ligne++;

    {nom}     cout << "nom " << YYText() << '\n';

    {chaine}  cout << "chaîne " << YYText() << '\n';

    %%

    int main( int /* argc */, char** /* argv */ )
        {
        FlexLexer* lexer = new yyFlexLexer;
        while(lexer->yylex() != 0)
            ;
        return 0;
        }
Si vous voulez créer de multiples classes (différentes) de lexer, vous devez utilisez l'attribut -P (ou l'option prefix=) pour renommer chaque yyFlexLexer en un autre xxFlexLexer. Vous pouvez ensuite inclure <FlexLexer.h> dans vos autres sources une fois par classe de lexer, en renommant d'abord yyFlexLexer comme ceci :

    #undef yyFlexLexer
    #define yyFlexLexer xxFlexLexer
    #include <FlexLexer.h>

    #undef yyFlexLexer
    #define yyFlexLexer zzFlexLexer
    #include <FlexLexer.h>

si, par exemple, vous avez utilisé %option prefix=xx pour l'un de vos analyseurs et %option prefix=zz pour l'autre.

IMPORTANT : la forme actuelle de la classe d'analyse est expérimentale et peut changer considérablement d'une version majeure de flex à l'autre.  

INCOMPATIBILITÉS AVEC LEX ET POSIX

flex est une réécriture de l'outil lex Unix de AT&T (les deux implémentations n'ont par contre aucun code en commun), avec certaines extensions et incompatibilités, qui concernent tous ceux qui veulent écrire des analyseurs acceptables dans n'importe laquelle des implémentations. Flex est entièrement conforme à la spécification POSIX de lex, mis à part que lors de l'utilisation de %pointer (le défaut), un appel à unput() détruit le contenu de yytext, ce qui est contraire à la spécification POSIX.

Dans cette section, nous discutons de toutes les zones d'incompatibilité connues entre flex, lex AT&T, et la spécification POSIX.

L'option -l de flex active la compatibilité maximale avec l'implémentation originale de lex par AT&T, au prix d'une perte majeure dans la performance de l'analyseur généré. Nous notons ci-dessous quelles incompatibilités peuvent se produire lors de l'utilisation de l'option -l.

flex est entièrement compatible avec lex avec les exceptions suivantes :

-
La variable interne non documentée yylineno de l'analyseur lex n'est pas supportée à moins que -l ou %option yylineno ne soit utilisé.
yylineno devrait être maintenue sur une base d'un tampon, plutôt que sur une base d'un analyseur (une seule variable globale).
yylineno ne fait pas partie de la spécification POSIX.
-
La routine input() n'est pas redéfinissable, bien qu'elle puisse être appelée pour lire des caractères suivant ce qui a été reconnu par une règle. Si input() rencontre une fin-de-fichier, le traitement yywrap() normal est effectué. Une fin-de-fichier « réelle » est renvoyée par input() en tant que EOF.
L'entrée est plutôt contrôlée en définissant la macro YY_INPUT.
La restriction flex disant que input() ne peut être redéfinie est en accord avec la spécification POSIX, qui ne spécifie tout simplement aucune façon de contrôler l'entrée de l'analyseur autrement qu'en effectuant une affectation initiale de yyin.
-
La routine unput() n'est pas redéfinissable. Cette restriction est en accord avec POSIX.
-
Les analyseurs flex ne sont pas aussi réentrants que les analyseurs lex. En particulier, si vous avez un analyseur interactif et un gestionnaire d'interruptions qui effectue un saut éloigné à partir de l'analyseur, et que l'analyseur est ultérieurement ré-appelé, vous pourriez obtenir le message suivant :

    fatal flex analyseur internal error--end of buffer missed

(erreur fatale interne à flex -- fin du tampon manquée) Pour rendre l'analyseur réentrant, utilisez d'abord

    yyrestart( yyin );

Notez que cet appel jettera toute entrée mise en mémoire tampon ; habituellement ce n'est pas un problème avec un analyseur interactif.
Notez également que les classes d'analyseurs C++ de flex sont réentrantes, et donc si l'utilisation du C++ est une possibilité pour vous, vous devriez les utiliser à la place. Voyez « Générer des Analyseurs C++ » ci-dessus pour les détails.
-
output() n'est pas supportée. La sortie de la macro ECHO est effectuée sur le pointeur de fichier yyout (par défaut stdout).
output() ne fait pas partie de la spécification POSIX.
-
lex ne supporte pas les conditions de démarrage exclusives (%x), bien qu'elles fassent partie de la spécification POSIX.
-
Quand les définitions sont développées, flex les enferme dans des parenthèses. Avec lex, le code suivant :

    NOM    [A-Z][A-Z0-9]*
    %%
    foo{NOM}?      printf( "Trouvé\n" );
    %%

ne reconnaîtra pas la chaîne de caractères « foo » car, quand la macro est développée, la règle est équivalente à « foo[A-Z][A-Z0-9]*? » et la priorité est telle que le '?' est associé avec « [A-Z0-9]* ». Avec flex, la règle sera développée en « foo([A-Z][A-Z0-9]*)? » et ainsi la chaîne de caractères « foo » conviendra.
Notez que si la définition commence par ^ ou se termine par $ alors elle n'est pas développée avec des parenthèses, afin de permettre à ces opérateurs d'apparaître dans des définitions sans perdre leur signification spéciale. Mais les opérateurs <s>,/, et <<EOF>> ne peuvent être utilisés dans une définition flex.
Utiliser -l résulte dans l'utilisation du comportement de lex qui ne place aucune parenthèse autour de la définition.
La spécification POSIX dit que la définition doit être comprise entre des parenthèses.
-
Certaines implémentations de lex permettent à l'action d'une règle de commencer sur une ligne séparée, si le motif de la règle possède des espaces finaux :

    %%
    foo|bar<espace ici>
      { foobar_action(); }

flex ne supporte pas cette fonctionnalité.
-
L'option lex %r (générer un analyseur Ratfor) n'est pas supportée. Elle ne fait pas partie de la spécification POSIX.
-
Après un appel à unput(), yytext n'est pas défini avant que l'élément lexical suivant ne soit reconnu, à moins que l'analyseur ait été construit en utilisant %array. Ce n'est pas le cas avec lex ou la spécification POSIX. L'option -l supprime cette incompatibilité.
-
La priorité de l'opérateur {} (intervalle numérique) est différente. lex interprète « abc{1,3} » comme « reconnaître une, deux ou trois occurrences de 'abc' », alors que flex l'interprète comme « reconnaître 'ab' suivi d'une, de deux ou de trois occurrences de 'c' ». Ce dernier comportement est en accord avec la spécification POSIX.
-
La priorité de l'opérateur ^ est différente. lex interprète « ^foo|bar » comme « reconnaître soit 'foo' au début d'une ligne, ou 'bar' n'importe où », alors que flex l'interprète comme « reconnaître soit 'foo' soit 'bar' s'ils apparaissent au début d'une ligne ». Ce dernier comportement est en accord avec la spécification POSIX.
-
Les déclarations spéciales de taille de table comme %a supportées par lex ne sont pas requises par les analyseurs flex ; flex les ignore.
-
Le nom FLEX_SCANNER est défini de sorte que des analyseurs peuvent être écrits pour être utilisés avec soit flex soit lex. Les analyseurs incluent aussi YY_FLEX_MAJOR_VERSION et YY_FLEX_MINOR_VERSION qui indiquent quelle version de flex a généré l'analyseur (par exemple, pour la version 2.5, ces #defines vaudraient 2 et 5 respectivement).

Les fonctionnalités suivantes de flex ne sont pas incluses dans lex ou la spécification POSIX:


    analyseurs C++
    %option
    portée de condition de démarrage
    piles de conditions de démarrage
    analyseurs interactifs/non-interactifs
    yy_scan_string() et amies
    yyterminate()
    yy_set_interactive()
    yy_set_bol()
    YY_AT_BOL()
    <<EOF>>
    <*>
    YY_DECL
    YY_START
    YY_USER_ACTION
    YY_USER_INIT
    directives #line
    %{}'s autour des actions
    de multiples actions sur une ligne

plus presque tous les drapeaux de flex. La dernière fonctionnalité de la liste se réfère au fait qu'avec flex, vous pouvez placer de multiples actions sur la même ligne, séparées par des points-virgules, alors qu'avec lex, le code suivant

    foo    handle_foo(); ++nombre_de_foos_vus;

est (de façon plutôt surprenante) tronqué en

    foo    handle_foo();

flex ne tronque pas l'action. Les actions qui ne sont pas enfermées dans des accolades sont simplement terminées à la fin de la ligne.

 

DIAGNOSTICS

warning, rule cannot be matched indique que la règle donnée n'a pas pu être reconnue car elle suit d'autres règles qui reconnaîtront toujours le même texte. Par exemple, dans le code suivant, « foo » ne peut être reconnu car il vient après une règle « attrape-tout » d'identificateur :


    [a-z]+    got_identifier();
    foo       got_foo();

Utiliser REJECT dans un analyseur supprime cet avertissement.

warning, -s option given but default rule can be matched signifie qu'il est possible (peut-être seulement dans une condition de démarrage particulière) que la règle par défaut (reconnaître tout caractère unique) soit la seule qui corresponde à une entrée particulière. Puisque -s a été donné, ce n'est probablement pas ce qui était prévu.

reject_used_but_not_detected undefined ou yymore_used_but_not_detected undefined - Ces erreurs peuvent se produire au moment de la compilation. Elles indiquent que l'analyseur utilise REJECT ou yymore() mais que flex n'a pas réussi à remarquer cela, ce qui signifie que flex a recherché des occurrences de ces actions dans les deux premières sections, mais n'a pas réussi à en trouver, mais d'une façon ou d'une autre vous en ayez glissées quelques unes (via un fichier #include, par exemple). Utilisez %option reject ou %option yymore pour indiquer à flex que vous utilisez réellement ces fonctionnalités.

flex analyseur jammed - Un analyseur compilé avec -s a rencontré une chaîne de caractères d'entrée qui n'a été reconnue par aucune de ses règles. Cette erreur peut également se produire à cause de problèmes internes.

token too large, exceeds YYLMAX - votre analyseur utilise %array et une de ses règles a reconnu une chaîne de caractères plus longue que la constante YYLMAX (8 Ko octets par défaut). Vous pouvez augmenter la valeur en définissant via un #define la macro YYLMAX dans la section de définitions de votre entrée flex.

scanner requires -8 flag to use the character 'x' - La spécification de votre analyseur inclut la reconnaissance du caractère 8 bits 'x' et vous n'avez pas spécifié le drapeau -8, et votre analyseur utilisait les caractères 7 bits par défaut car vous avez utilisé les options de compression de table -Cf ou -CF. Voyez la discussion sur le drapeau -7 pour les détails.

flex analyseur push-back overflow - vous avez utilisé unput() pour repousser en entrée tellement de texte que le tampon de l'analyseur ne pouvait pas contenir à la fois le texte repoussé et l'élément lexical courant dans yytext. Idéalement, l'analyseur devrait redimensionner dynamiquement le tampon dans ce cas, mais actuellement il ne le fait pas.

input buffer overflow, can't enlarge buffer because parser uses REJECT - l'analyseur travaillait sur la mise en correspondance d'un élément lexical extrêmement grand et a eu besoin d'étendre le tampon d'entrée. Cela ne fonctionne pas avec les analyseurs qui utilisent REJECT.

fatal flex analyseur internal error--end of buffer missed Cela peut se produire dans le cas d'un analyseur où on réentre après qu'un saut lointain ait sauté en dehors du cadre d'activation de l'analyseur. Avant de réentrer dans l'analyseur, utilisez :


    yyrestart( yyin );

ou, comme mentionné au-dessus, utilisez la classe d'analyseurs C++.

too many start conditions in <> construct ! - vous avez listé plus de conditions de démarrage dans une construction <> qu'il n'en existe (vous devez donc avoir listé deux fois au moins l'une d'entre elles).  

FICHIERS

-lfl
bibliothèque avec laquelle les analyseurs doivent être liés.
lex.yy.c
analyseur généré (appelé lexyy.c sur certains systèmes).
lex.yy.cc
classe d'analyseur C++ générée, lors de l'utilisation de -+.
<FlexLexer.h>
fichier d'en-tête définissant la classe de base de l'analyseur C++, FlexLexer, et sa classe dérivée, yyFlexLexer.
flex.skl
analyseur squelette. Ce fichier n'est utilisé que lors de la construction de flex, et pas quand flex s'exécute.
lex.backup
information de retour arrière pour le drapeau -b (appelé lex.bck sur certains systèmes).
 

DÉFECTUOSITÉS / BOGUES

Certains motifs de contexte de queue ne peuvent être proprement reconnus et génèrent des messages d'avertissement ("dangerous trailing context"). Ce sont des motifs où la fin de la première partie de la règle correspond au début de la seconde partie, comme « zx*/xy* », où le 'x*' correspond au texte reconnu par de tels motifs n'est pas défini.)

Pour certaines règles de contexte de queue, les parties qui sont réellement de longueur fixe ne sont pas reconnues en tant que telles, ce qui mène à la baisse de performance mentionnée au-dessus. En particulier, les parties utilisant '|' ou {n} (comme « foo{3} ») sont toujours considérées comme ayant une longueur variable.

Combiner le contexte de queue avec l'action spéciale « | » peut résulter en ce qu'un contexte de queue fixe soit converti en un contexte de queue variable plus coûteux, comme par exemple dans le code suivant :


    %%
    abc      |
    xyz/def

L'utilisation de unput() invalide yytext et yyleng, à moins que la directive %array ou l'option -l n'ait été utilisée.

La reconnaissance des motifs NUL est substantiellement plus lente que la reconnaissance d'autres caractères.

Le redimensionnement du tampon d'entrée est lent, car il occasionne le réexamen de tout le texte reconnu jusqu'ici par l'élément lexical courant (généralement immense).

À cause à la fois de la mise en mémoire tampon de l'entrée, et de la lecture en avance, vous ne pouvez pas mélanger des appels à des routines de <stdio.h>, comme par exemple getchar(), avec les règles flex et vous attendre à ce que cela fonctionne. Appelez input() à la place.

L'ensemble des entrées de table listées par le drapeau -v exclut le nombre d'entrées de table nécessaire pour déterminer quelle règle a été reconnue. Le nombre d'entrées est égal au nombre d'états DFA si l'analyseur n'utilise pas REJECT, et un peu plus que le nombre d'état s'il le fait.

REJECT ne peut pas être utilisé avec les options -f ou -F.

Les algorithmes internes de flex ont besoin de documentation.  

VOIR AUSSI

lex(1), yacc(1), sed(1), awk(1).

John Levine, Tony Mason, and Doug Brown, Lex & Yacc, O'Reilly and Associates. Assurez-vous d'avoir la 2ème édition.

M. E. Lesk and E. Schmidt, LEX - Lexical Analyzer Generator.

Alfred Aho, Ravi Sethi and Jeffrey Ullman, Compilers: Principles, Techniques and Tools, Addison-Wesley (1986). Décrit les techniques de reconnaissance de motifs utilisées par flex (automates finis déterministes).  

AUTEUR

Vern Paxson, avec l'aide de beaucoup d'idées et d'inspiration de Van Jacobson. Version originale par Jef Poskanzer. La représentation rapide de la table est une implémentation partielle d'une conception de Van Jacobson. L'implémentation a été effectuée par Kevin Gong et Vern Paxson.

Merci aux nombreux bêta-testeurs, aux gens qui ont fourni une rétroaction, et aux contributeurs de flex , en particulier à Francois Pinard, Casey Leedom, Robert Abramovitz, Stan Adermann, Terry Allen, David Barker-Plummer, John Basrai, Neal Becker, Nelson H.F. Beebe, benson@odi.com, Karl Berry, Peter A. Bigot, Simon Blanchard, Keith Bostic, Frederic Brehm, Ian Brockbank, Kin Cho, Nick Christopher, Brian Clapper, J.T. Conklin, Jason Coughlin, Bill Cox, Nick Cropper, Dave Curtis, Scott David Daniels, Chris G. Demetriou, Theo Deraadt, Mike Donahue, Chuck Doucette, Tom Epperly, Leo Eskin, Chris Faylor, Chris Flatters, Jon Forrest, Jeffrey Friedl, Joe Gayda, Kaveh R. Ghazi, Wolfgang Glunz, Eric Goldman, Christopher M. Gould, Ulrich Grepel, Peer Griebel, Jan Hajic, Charles Hemphill, NORO Hideo, Jarkko Hietaniemi, Scott Hofmann, Jeff Honig, Dana Hudes, Eric Hughes, John Interrante, Ceriel Jacobs, Michal Jaegermann, Sakari Jalovaara, Jeffrey R. Jones, Henry Juengst, Klaus Kaempf, Jonathan I. Kamens, Terrence O Kane, Amir Katz, ken@ken.hilco.com, Kevin B. Kenny, Steve Kirsch, Winfried Koenig, Marq Kole, Ronald Lamprecht, Greg Lee, Rohan Lenard, Craig Leres, John Levine, Steve Liddle, David Loffredo, Mike Long, Mohamed el Lozy, Brian Madsen, Malte, Joe Marshall, Bengt Martensson, Chris Metcalf, Luke Mewburn, Jim Meyering, R. Alexander Milowski, Erik Naggum, G.T. Nicol, Landon Noll, James Nordby, Marc Nozell, Richard Ohnemus, Karsten Pahnke, Sven Panne, Roland Pesch, Walter Pelissero, Gaumond Pierre, Esmond Pitt, Jef Poskanzer, Joe Rahmeh, Jarmo Raiha, Frederic Raimbault, Pat Rankin, Rick Richardson, Kevin Rodgers, Kai Uwe Rommel, Jim Roskind, Alberto Santini, Andreas Scherer, Darrell Schiebel, Raf Schietekat, Doug Schmidt, Philippe Schnoebelen, Andreas Schwab, Larry Schwimmer, Alex Siegel, Eckehard Stolz, Jan-Erik Strvmquist, Mike Stump, Paul Stuart, Dave Tallman, Ian Lance Taylor, Chris Thewalt, Richard M. Timoney, Jodi Tsai, Paul Tuinenga, Gary Weik, Frank Whaley, Gerhard Wilhelms, Kent Williams, Ken Yap, Ron Zellar, Nathan Zelle, David Zuhn, et ceux dont le nom a échappé à mes facultés marginales d'archivage de mails mais dont les contributions ont été appréciées de la même manière.

Merci à Keith Bostic, Jon Forrest, Noah Friedman, John Gilmore, Craig Leres, John Levine, Bob Mulcahy, G.T. Nicol, Francois Pinard, Rich Salz, et Richard Stallman pour leur aide relative à divers maux de tête dus à des problèmes de distribution.

Merci à Esmond Pitt et Earle Horton pour le support des caractères 8 bits ; à Benson Margulies et Fred Burke pour le support de C++ ; à Kent Williams et Tom Epperly pour le support de classes C++ ; à Ove Ewerlid pour le support des NUL ; et à Eric Hughes pour le support des tampons multiples.

Ce travail a été principalement effectué lorsque je faisais partie du Real Time Systems Group au Lawrence Berkeley Laboratory à Berkeley, CA. Merci beaucoup à tous ceux-ci pour leur aide.

Envoyez vos commentaires à vern@ee.lbl.gov.

 

TRADUCTION

Frédéric Delanoy <delanoy_f at yahoo.com>, 2001.


 

Index

NOM
SYNOPSIS
RÉSUMÉ
DESCRIPTION
QUELQUES EXEMPLES SIMPLES
FORMAT DU FICHIER D'ENTRÉE
MOTIFS
COMMENT L'ENTRÉE EST RECONNUE
ACTIONS
L'ANALYSEUR GÉNÉRÉ
CONDITIONS DE DÉMARRAGE
TAMPONS D'ENTRÉE MULTIPLES
RÈGLES DE FIN-DE-FICHIER
MACROS DIVERSES
VALEURS DISPONIBLES POUR L'UTILISATEUR
INTERFACE AVEC YACC
OPTIONS
CONSIDÉRATIONS DE PERFORMANCE
GÉNÉRER DES ANALYSEURS C++
INCOMPATIBILITÉS AVEC LEX ET POSIX
DIAGNOSTICS
FICHIERS
DÉFECTUOSITÉS / BOGUES
VOIR AUSSI
AUTEUR
TRADUCTION

This document was created by man2html, using the manual pages.
Time: 20:41:55 GMT, July 10, 2005