flex

Autres langues

Langue: pl

Autres versions - même langue

Version: Kwiecieñ 1995 (openSuse - 09/10/07)

Section: 1 (Commandes utilisateur)

NAZWA

flex - szybki generator analizatora leksykalnego

SK£ADNIA

flex [-bcdfhilnpstvwBFILTV78+? -C[aefFmr] -ooutput -Pprefix -Sskeleton] [--help --version] [filename ...]

WPROWADZENIE

Podrêcznik ten opisuje narzêdzie flex. Jest ono przeznaczone do generowania programów, dokonywuj±cych dopasowywania wzorców na tek¶cie. Podrêcznik zawiera zarówno sekcje przewodnikowe jak i informacyjne.



    Opis

        krótki przegl±d mo¿liwo¶ci narzêdzia



    Proste Przyk³ady



    Format Pliku Wej¶ciowego



    Wzorce

        rozszerzone wyra¿enia regularne u¿ywane przez flex



    Sposób Dopasowywania Wej¶cia

        regu³y okre¶lania, co dopasowano



    Akcje

        jak podawaæ, co robiæ po dopasowaniu wzorca



    Generowany Skaner

        szczegó³y o skanerze, tworzonym przez fleksa; jak kontrolowaæ ¼ród³o

        wej¶ciowe 



    Warunki Startowe

        wprowadzanie do skanerów kontekstu i obs³uga "mini-skanerów"



    Wielokrotne Bufory Wej¶ciowe

        jak obs³ugiwaæ wiele ¼róde³ wej¶ciowych; jak skanowaæ z ³añcuchów

        zamiast z plików



    Regu³y Koñca Pliku

        specjalne regu³y dopasowywane do koñca wej¶cia



    Ró¿ne Makra

        ogó³ makr dostêpnych z poziomu akcji



    Warto¶ci Dostêpne U¿ytkownikowi

        ogó³ warto¶ci dostêpnych z poziomu akcji



    £±czenie z Yacc

        ³±czenie skanerów flex z analizatorami yacc



    Opcje

        opcje linii poleceñ fleksa i dyrektywa "%option"



    Kwestie wydajno¶ciowe

        jak przyspieszaæ skanery



    Generowanie Skanerów C++

        eksperymentalna w³a¶ciwo¶æ generowania klas skanerów C++



    Niezgodno¶ci z Lex i POSIX

        czym flex ró¿ni siê od standardów AT&T lex i POSIX lex



    Diagnostyka

        obja¶nienie komunikatów o b³êdach, generowanych przez flex (lub

        skanery)



    Pliki

        pliki u¿ywane przez flex



    Niedostatki / B³êdy

        znane problemy fleksa



    Zobacz Tak¿e

        pozosta³a dokumentacja i zwi±zane z fleksem narzêdzia



    Autor

        informacja kontaktu z autorem



OPIS

flex jest narzêdziem przeznaczonym do generowania skanerów: programów, rozpoznaj±cych wzorce leksykalne tekstu. flex odczytuje podane pliki wej¶ciowe (lub stdin gdy nie s± podane) i pobiera z nich opis generowanego skanera. Opis sk³ada siê z par wyra¿eñ regularnych i kodu C. Pary te nazywane s± regu³ami. flex jako wyj¶cie generuje plik ¼ród³owy C o nazwie lex.yy.c. Definiuje on funkcjê yylex(). Plik ten musi kompilowany i konsolidowany z bibliotek± -lfl. Po uruchomieniu pliku wykonywalnego, program analizuje wej¶cie w poszukiwaniu wyra¿eñ regularnych. Gdy tylko takie siê znajdzie, wykonywany jest odpowiedni fragment kodu C.

PROSTE PRZYK£ADY

Przedstawmy teraz trochê prostych przyk³adów aby obyæ siê z u¿ywaniem flex. Nastêpuj±cy plik wej¶ciowy flex okre¶la skaner, który za ka¿dym razem gdy napotka ³añcuch "username", podmieni go nazw± u¿ytkownika:




    %%

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



Domy¶lnie tekst, którego flex nie mo¿e dopasowaæ jest kopiowany na wyj¶cie. Skaner bêdzie wiêc kopiowa³ swój plik wej¶ciowy na wyj¶cie, podmieniaj±c wszelkie pojawienia "username". W tym przyk³adzie wej¶cia mamy tylko jedn± regu³ê. Wzorcem jest "username", a akcj± jest "printf". Znaki "%%" oznaczaj± pocz±tek regu³.

Oto kolejny prosty przyk³ad:




            int num_lines = 0, num_chars = 0;



    %%

    \n      ++num_lines; ++num_chars;

    .       ++num_chars;



    %%

    main()

            {

            yylex();

            printf( "# of lines = %d, # of chars = %d\n",

                    num_lines, num_chars );

            }



Ten skaner zlicza liczbê znaków i liczbê linijek swojego wej¶cia (nie daje ¿adnego wyj¶cia, nie licz±c koñcowego raportu). Pierwsza linia deklaruje dwie zmienne globalne, "num_lines" i "num_chars", które s± dostêpne wewn±trz funkcji yylex() i main(), zadeklarowanej po drugim "%%". Mamy tu dwie regu³y: pierwsza dopasowuje siê do nowej linii ("\n") i inkrementuje licznik linii oraz znaków; druga dopasowuje siê do dowolnego znaku innego ni¿ nowa linia (wyra¿enie regularne ".") i zwiêksza licznik liczby znaków.

A oto trochê bardziej skomplikowany przyk³ad:




    /* skaner dla zabawkowego Pascalo-podobnego jêzyka */



    %{

    /* potrzebujemy tego do wywo³ania atof() */

    #include <math.h>

    %}



    DIGIT    [0-9]

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



    %%



    {DIGIT}+    {

                printf( "Liczba ca³kowita: %s (%d)\n", yytext,

                        atoi( yytext ) );

                }



    {DIGIT}+"."{DIGIT}*        {

                printf( "Liczba zmiennoprzecinkowa: %s (%g)\n", yytext,

                        atof( yytext ) );

                }



    if|then|begin|end|procedure|function        {

                printf( "S³owo kluczowe: %s\n", yytext );

                }



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



    "+"|"-"|"*"|"/"   printf( "Operator: %s\n", yytext );



    "{"[^}\n]*"}"     /* zjedz jednolinijkowe komentarze */



    [ \t\n]+          /* zjedz bia³e spacje */



    .           printf( "Nierozpoznany znak: %s\n", yytext );



    %%



    main( argc, argv )

    int argc;

    char **argv;

        {

        ++argv, --argc;  /* pomiñ nazwê programu */

        if ( argc > 0 )

                yyin = fopen( argv[0], "r" );

        else

                yyin = stdin;

        

        yylex();

        }



S± to pocz±tki prostego skanera dla jêzyka podobnego do Pascala. Rozró¿nia poszczególne rodzaje tokenów i informuje co zobaczy³.

Szczegó³y tego przyk³adu zostan± wyja¶nione w nastêpnych sekcjach.

FORMAT PLIKU WEJ¦CIOWEGO

Plik wej¶ciowy fleksa sk³ada siê z trzech sekcji, rozdzielanych liniami z ³añcuchem %%:



    definicje

    %%

    regu³y

    %%

    kod u¿ytkownika



Sekcja definicji zawiera definicje prostych nazw, upraszczaj±cych pó¼niej specyfikacjê skanera. Zawiera te¿ deklaracje warunków pocz±tkowych, które obja¶niono w dalszej sekcji.

Definicje nazw maj± postaæ:




    nazwa definicja



gdzie "nazwa" jest s³owem, rozpoczynaj±cym siê od litery lub podkre¶lenia ('_'). Pozosta³e znaki mog± byæ literami, cyframi, podkre¶leniami lub my¶lnikami. Definicja jest pobierana od momentu pojawienia siê pierwszego znaku, który nie jest spacj± i który znajduje siê za nazw±. Definicja rozci±ga siê do koñca linii. Do takiej definicji mo¿na siê nastêpnie odwo³ywaæ przy u¿yciu konwencji "{nazwa}", która jest automatycznie rozwijana w "(definicjê)". Na przyk³ad



    DIGIT    [0-9]

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



definiuje "DIGIT" jako wyra¿enie regularne, pasuj±ce do pojedynczej cyfry, a "ID" jako wyra¿enie regularne odpowiadaj±ce literze z doklejonymi ewentualnymi literami lub cyframi. Pó¼niejsze odniesienie do



    {DIGIT}+"."{DIGIT}*



jest równowa¿ne



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



i dopasowuje jedn± lub wiêcej cyfr, po których wystêpuje kropka i ewentualnie nastêpne cyfry.

Sekcja regu³ wej¶cia fleksa zawiera szereg regu³ w postaci:




    wzorzec   akcja



Przed wzorcem nie mo¿e wyst±piæ wciêcie, a akcja musi rozpoczynaæ siê w tej samej linii.

Dla dalszego opisu akcji patrz dalej.

W koñcu, sekcja kodu u¿ytkownika jest zwyczajnie kopiowana do lex.yy.c (bez dokonywania w niej zmian). Jest to u¿ywane do funkcji pomocniczych, które wo³aj± lub s± wo³ane przez skaner. Obecno¶æ tej sekcji jest opcjonalna; je¶li nie istnieje, to ostatni %% pliku wej¶ciowego mo¿e byæ pominiêty.

Je¶li w sekcjach definicji lub regu³ znajduje siê jaki¶ wciêty (indentowany) tekst lub tekst ujêty w %{ i %}, to jest on kopiowany dos³ownie na wyj¶cie (po usuniêciu %{}). Znaki %{} musz± pojawiæ siê samodzielnie w liniach bez wciêæ.

W sekcji regu³, tekst wciêty lub tekst %{}, znajduj±cy siê przed pierwsz± regu³± mo¿e s³u¿yæ deklarowaniu zmiennych lokalnych dla procedury skanuj±cej oraz (po deklaracjach) kodu, który ma byæ wywo³ywany za ka¿dym uruchomieniem procedury skanuj±cej. Pozosta³e przypadki wciêtego tekstu lub tekstu %{} sekcji regu³ s± nadal kopiowane na wyj¶cie, lecz ich znaczenie nie jest dok³adnie zdefiniowane i mog± spowodowaæ b³êdy kompilacji (w³a¶ciwo¶æ ta jest obecna dla zgodno¶ci z POSIX; zobacz ni¿ej inne tego typu w³a¶ciwo¶ci).

W sekcji definicji na wyj¶cie kopiowane s± równie¿ nie-wciête bloki komentarza, ujête miêdzy znaki "/*" i "*/".

WZORCE

Wzorce wej¶ciowe s± pisane z u¿yciem rozszerzonego zestawu wyra¿eñ regularnych. S± to:



    x          dopasowuje znak 'x'

    .          dowolny znak poza now± lini±

    [xyz]      "klasa znaków"; w tym przypadku wzorzec odpowiada

                 zarówno 'x', 'y' jak i 'z'

    [abj-oZ]   "klasa znaków" z zakresem; odpowiada ona

                 'a', 'b', dowolnej literze od 'j' do 'o' oraz 'Z'

    [^A-Z]     zanegowana "klasa znaków" tj. dowolny znak poza 

                 wymienionymi w klasie. W tym wypadku dowolny znak oprócz

                 du¿ych liter

    [^A-Z\n]  dowolny znak oprócz du¿ych liter lub nowej linii

    r*         zero lub wiêcej r'ów, gdzie r jest wyra¿eniem regularnym

    r+         jeden lub wiêcej r'ów

    r?         zero lub jeden r (tj. "opcjonalny r")

    r{2,5}     od dwu do piêciu r

    r{2,}      dwa lub wiêcej r

    r{4}       dok³adnie 4 r

    {nazwa}    rozwiniêcie definicji "nazwa" (patrz wy¿ej)

    "[xyz]\"foo"

               ³añcuch literalny: [xyz]"foo

    \X        Je¶li X to 'a', 'b', 'f', 'n', 'r', 't' lub 'v',

                 to nastêpuje interpretacja ANSI-C \x. W przeciwnym

                 wypadku u¿ywany jest literalny 'X' (u¿ywane do cytowania

                 operatorów--np. '*').

    \0        znak NUL (kod ASCII 0)

    \123      znak o warto¶ci ósemkowej 123

    \x2a      znak o warto¶ci szesnastkowej 2a

    (r)        dopasuj r; nawiasy s± u¿ywane do przeci±¿ania priorytetów

                 (patrz ni¿ej)





    rs         wyra¿enie regularne r, za którym nastêpuje wyra¿enie

                 regularne s; nazywa siê to "³±czeniem"





    r|s        r lub s





    r/s        r, lecz tylko je¶li za nim nastêpuje s. Tekst dopasowywany

                 przez s jest za³±czany do okre¶lania czy ta regu³a mia³a

                 "najd³u¿sze dopasowanie", lecz potem jest zwracany do

                 wej¶cia przed wykonaniem akcji. Tak wiêc akcja widzi tylko

                 tekst dopasowany przez r. Ten rodzaj wzorca jest nazywany

                 "doklejonym kontekstem". (Istniej± pewne kombinacje r/s,

                 których flex nie potrafi w³a¶ciwie dopasowaæ; zobacz uwagi

                 w dalszej sekcji Niedostatki / B³êdy w okolicach

                 "niebezpiecznego kontekstu doklejonego".)

    ^r         r, lecz tylko na pocz±tku linii (tj. zaraz po rozpoczêciu

                 skanowania, lub po wyskanowaniu nowej linii).

    r$         r, lecz tylko na koñcu linii (tj. tu¿ przed now± lini±).

                 Równowa¿ne "r/\n".



               Zauwa¿, ¿e notacja nowej linii fleksa jest dok³adnie tym,

               co by³o u¿ywane jako '\n' przez kompilator C, u¿yty do 

               kompilacji fleksa; w praktyce na niektórych systemach DOS

               musisz wyfiltrowaæ \r lub jawnie u¿ywaæ r/\r\n zamiast

               "r$".





    <s>r       r, lecz tylko dla warunku pocz±tkowego s (zobacz ni¿ej

                 dyskusjê o warunkach pocz±tkowych)

    <s1,s2,s3>r

               to samo, lecz je¶li dowolny z warunków pocz±tkowych s1,

                 s2 lub s3

    <*>r       r w dowolnym warunku pocz±tkowym, nawet wykluczaj±cym





    <<EOF>>    koniec pliku

    <s1,s2><<EOF>>

               koniec pliku w warunkach pocz±tkowych s1 lub s2



Zauwa¿, ¿e w obrêbie klasy znaków wszystkie operatory wyra¿eñ regularnych trac± swoje znaczenie specjalne (nie licz±c cytowania '\', znaków klasy '-',

Wymienione wy¿ej wyra¿enia regularne s± pogrupowane zgodnie z priorytetami, licz±c od najwy¿szego do najni¿szego (z góry na dó³). Te, które zgrupowano razem maj± jednakowy priorytet. Na przyk³ad,




    foo|bar*



jest równowa¿ne



    (foo)|(ba(r*))



poniewa¿ operator '*' ma wy¿szy priorytet ni¿ ³±czenie, a ³±czenie ma wy¿szy priorytet ni¿ alternatywa ('|'). Wzorzec ten pasuje wiêc albo do ³añcucha "foo" albo do "ba", po którym mo¿e nast±piæ zero lub wiêcej r. W celu dopasowania "foo" lub zero lub wiêcej "bar"'ów, u¿yj:



    foo|(bar)*



a ¿eby dopasowaæ zero lub wiêcej "foo"-lub-"bar"'ów:



    (foo|bar)*



Poza znakami i zakresami znaków, klasy znaków mog± te¿ zawieraæ specjalne wyra¿enia. Wyra¿enia te s± ujmowane w ograniczniki [: i :] (które musz± dodatkowo pojawiaæ siê wewn±trz '[' i ']' klasy znaków; inne elementy w klasie znaków te¿ mog± siê pojawiæ). Prawid³owymi wyra¿eniami s±:




    [:alnum:] [:alpha:] [:blank:]

    [:cntrl:] [:digit:] [:graph:]

    [:lower:] [:print:] [:punct:]

    [:space:] [:upper:] [:xdigit:]



Wyra¿enia te oznaczaj± zestaw znaków, odpowiadaj±cy równowa¿nemu standardowi funkcji isXXX jêzyka C. Przyk³adowo [:alnum:] oznacza wszystkie znaki, dla których isalnum(3) zwraca prawdê - tj. wszelkie znaki alfabetyczne lub numeryczne. Niektóre systemy nie udostêpniaj± isblank(3). Flex definiuje [:blank:] jako spacjê lub tabulacjê.

Na przyk³ad nastêpuj±ce klasy s± sobie równowa¿ne:




    [[:alnum:]]

    [[:alpha:][:digit:]

    [[:alpha:]0-9]

    [a-zA-Z0-9]



Je¶li twój skaner jest niewra¿liwy na wielko¶æ znaków (flaga (flaga -i), to [:upper:] i [:lower:] s± równowa¿ne [:alpha:].

Trochê uwag o wzorcach:

-
Zanegowana klasa znaków, taka jak wy¿ej wymienione przyk³adowe "[^A-Z]" bêdzie pasowaæ do nowej linii, chyba ¿e "\n" (lub równowa¿na sekwencja specjalna) jest jednym z jawnie obecnych w klasie znaków (np. "[^A-Z\n]"). Odbiega to od sposobu traktowania zanegowanych klas znaków przez inne narzêdzia operuj±ce na wyra¿eniach regularnych, lecz niestety niespójno¶æ jest ugruntowana historycznie. Dopasowywanie nowej linii oznacza, ¿e wzorzec w rodzaju [^"]* mo¿e dopasowaæ siê do ca³ego wej¶cia, chyba ¿e istnieje w nim drugi cudzys³ów.
-
Regu³a mo¿e mieæ najwy¿ej jedn± instancjê dowi±zanego kontekstu (operatory siê tylko na pocz±tku wzorca i dodatkowo, podobnie jak '/' i '$', nie mog± byæ grupowane w nawiasy. Znak '^', który nie pojawia siê na pocz±tku regu³y, lub '$', nie znajduj±cy siê na koñcu traci swoje specjalne znaczenie.
Nastêpuj±ce wzorce s± niedozwolone:



    foo/bar$

    <sc1>foo<sc2>bar



Zauwa¿, ¿e pierwszy z nich mo¿e byæ zapisany jako "foo/bar\n".
Nastêpuj±ce wzorce powoduj±, ¿e '$' lub '^' s± traktowane jak zwyk³e znaki:



    foo|(bar$)

    foo|^bar



Je¶li oczekiwan± warto¶ci± jest "foo" lub "bar-z-now±-lini±", to u¿yæ mo¿na nastêpuj±cego wzorca (akcja specjalna | jest wyja¶niona ni¿ej):



    foo      |

    bar$     /* tu rozpoczyna siê akcja */



Podobna sztuczka powinna zadzia³aæ dla dopasowywania foo lub bar-na-pocz±tku-linii.

JAK DOPASOWYWANE JEST WEJ¦CIE

Po uruchomieniu skanera, analizuje on swoje wej¶cie w poszukiwaniu ³añcuchów odpowiadaj±cych któremu¶ z jego wzorców. Je¶li znajdzie wiêcej ni¿ jeden pasuj±cy wzorzec, wybiera ten, który pasuje do najwiêkszej ilo¶ci tekstu (w regu³ach z dowi±zanym kontekstem oznacza to te¿ d³ugo¶æ czê¶ci dowi±zanej, mimo faktu, ¿e zostanie ona zwrócona na wej¶cie. Je¶li znajdzie dwa lub wiêcej dopasowañ o tej samej d³ugo¶ci, to wybierana jest pierwsza regu³a.

Po okre¶leniu dopasowania, tekst dopasowania (zwany dalej tokenem) jest udostêpniany we wska¼nikowej zmiennej globalnej yytext, a jego d³ugo¶æ w globalnej zmiennej ca³kowitej yyleng. Wykonywana jest te¿ odpowiadaj±ca wzorcowi akcja (szczegó³owy opis akcji jest dalej), a nastêpnie pozosta³a czê¶æ wej¶cia jest dopasowywana do kolejnego wzorca.

Je¶li dopasowanie nie zostanie znalezione, wykonana zostanie regu³a domy¶lna: nastêpny znak wej¶cia jest uwa¿any za dopasowany i kopiowany na stdout. Tak wiêc najprostszym poprawnym plikiem wej¶ciowym fleksa jest:




    %%



Generuje to skaner, który po prostu kopiuje swoje wej¶cie (jeden znak naraz) na wyj¶cie.

Zauwa¿, ¿e yytext mo¿e byæ definiowane na dwa sposoby: jako wska¼nik do znaków lub jako tablica znaków. U¿ywanie konkretnej definicji mo¿na kontrolowaæ, w³±czaj±c do pliku wej¶ciowego w pierwszej sekcji specjalne dyrektywy %pointer lub %array. Domy¶lnie u¿ywana jest dyrektywa %pointer, chyba ¿e u¿ywa siê opcji -l zgodno¶ci z leksem i wtedy yytext staje siê tablic±. Korzy¶ci± z u¿ywania %pointer jest zwiêkszenie szybko¶ci skanowania i zlikwidowanie przepe³nieñ bufora przy dopasowywaniu du¿ych tokenów (chyba ¿e zabraknie pamiêci dynamicznej). Wad± jest ograniczenie sposobu modyfikowania przez akcje zmiennej yytext (zobacz nastêpn± sekcjê) i to, ¿e wywo³ania funkcji unput() niszcz± aktualn± zawarto¶æ yytext, co mo¿e przyprawiaæ o ból g³owy podczas portowania skanerów miêdzy ró¿nymi wersjami lex.

Zalet± %array jest mo¿liwo¶æ modyfikowania yytext i to, ¿e wo³anie unput() nie niszczy yytext. Poza tym, istniej±ce programy lex czasami zewnêtrznie zagl±daj± do yytext przy u¿yciu deklaracji w postaci:


    extern char yytext[];

Definicja ta jest b³êdna przy u¿yciu z %pointer, lecz prawid³owa dla %array.

%array definiuje yytext jako tablicê YYLMAX znaków, co domy¶lnie jest do¶æ du¿± warto¶ci±. Mo¿esz zmieniaæ rozmiar przez proste #definiowanie YYLMAX na inn± warto¶æ w pierwszej sekcji wej¶ciowego pliku fleksa. Jak wspomniano wy¿ej, dla %pointer yytext wzrasta dynamicznie, by przechowywaæ du¿e tokeny. Chocia¿ oznacza to, ¿e skaner %pointer mo¿e zbieraæ du¿e tokeny (jak np. ca³e bloki komentarzy), to zakop sobie w pamiêci, ¿e za ka¿dym razem gdy skaner zmienia rozmiar yytext to musi równie¿ reskanowaæ ca³y token od pocz±tku, wiêc mo¿e siê to okazaæ powolne. yytext w chwili obecnej nie zwiêksza dynamicznie rozmiaru je¶li wywo³anie unput() powoduje wepchniêcie z powrotem zbyt du¿ego bloku tekstu. Zamiast tego pojawia siê b³±d wykonania.

Zauwa¿ te¿, ¿e postaci %array nie mo¿na u¿ywaæ z klasami skanerów C++ (zobacz opcjê c++ poni¿ej).

AKCJE

Ka¿dy wzorzec regu³y ma odpowiadaj±c± mu akcjê, która mo¿e byæ dowoln± instrukcj± jêzyka C. Wzorzec koñczy siê na pierwszym niecytowanym znaku bia³ej spacji; reszta linijki jest akcj±. Je¶li akcja jest pusta, to token wej¶ciowy jest zwyczajnie odrzucany. Na przyk³ad oto program, kasuj±cy wszystkie pojawienia ³añcucha "wytnij mnie":



    %%

    "wytnij mnie"



(Wszystkie pozosta³e znaki wej¶cia zostan± skopiowane na wyj¶cie, gdy¿ dopasuj± siê do regu³y domy¶lnej.)

Oto program, który kompresuje wielokrotne spacje i tabulacje do pojedynczej spacji. Program wycina te¿ wszystkie bia³e spacje z koñca linii:




    %%

    [ \t]+        putchar( ' ' );

    [ \t]+$       /* ignoruj ten token */



Je¶li akcja zawiera znak '{', to rozci±ga siê ona a¿ do zamykaj±cego '}', nawet na przestrzeni wielu linii. flex ma pewne wiadomo¶ci o ³añcuchach C i komentarzach, wiêc nie zostanie og³upione przez klamry, które mog± siê w nich znajdowaæ. Poza tym dozwolone s± te¿ akcje, które zaczynaj± siê od %{ i zawieraj± tekst akcji a¿ do nastêpnego %} (niezale¿nie od zwyczajnych klamer wewn±trz akcji).

Akcja sk³adaj±ca siê wy³±cznie z pionowej kreski ('|') oznacza "taka sama, jak akcja nastêpnej regu³y". Dla zobrazowania patrz ni¿ej.

Akcje mog± zawieraæ kod C, w³±czaj±c w to instrukcje return, przeznaczone do zwracania warto¶ci do procedury, która wywo³a³a yylex(). Przy ka¿dym wywo³aniu yylex() kontynuuje przetwarzanie tokenów od miejsca, w którym ostatnio przerwa³ a¿ do osi±gniêcia koñca pliku lub wywo³ania return.

Akcje mog± spokojnie modyfikowaæ zmienn± yytext; nie mog± jej jednak wyd³u¿aæ (dodawanie znaków do jej koñca nadpisze dalsze znaki strumienia wej¶ciowego). Odmiennie jest natomiast przy u¿ywaniu %array (patrz wy¿ej); wtedy yytext mo¿na spokojnie modyfikowaæ w dowolny sposób.

Podobnie do powy¿szej zmiennej, mo¿na spokojnie modyfikowaæ yyleng, lecz nale¿y uwa¿aæ by nie robiæ tego je¶li akcja u¿ywa yymore() (patrz ni¿ej).

Istnieje wiele dyrektyw specjalnych, które mo¿na zawrzeæ w akcji:

-
ECHO kopiuje wej¶cie yytext na wyj¶cie skanera.
-
BEGIN z doklejon± nazw± warunku pocz±tkowego umieszcza skaner w odpowiednim warunku pocz±tkowym (patrz ni¿ej).
-
REJECT Kieruje skaner na dzia³anie w "drugiej najlepszej" regule, która zosta³a dopasowana do wzorca wej¶ciowego (lub prefiksu wej¶cia). Regu³a jest wybierana wed³ug zasad opisanych w "Jak dopasowywane jest wej¶cie", po czym nastêpuje odpowiednie ustawienie yytext oraz yyleng. Mo¿e to byæ albo ta regu³a, która dopasowa³a siê do takiej samej ilo¶ci tekstu, jak poprzednia, lecz wyst±pi³a pó¼niej w pliku wej¶ciowym fleksa, albo taka, która dopasowa³a siê do mniejszej ilo¶ci tekstu. Na przyk³ad, nastêpuj±cy przyk³ad bêdzie liczy³ s³owa wej¶ciowe i wo³a³ funkcjê special() dla ka¿dego "frob":



            int word_count = 0;

    %%



    frob        special(); REJECT;

    [^ \t\n]+   ++word_count;



Bez dyrektywy REJECT, s³owa "frob" wej¶cia nie by³yby zliczane jako s³owa, gdy¿ skaner normalnie wykonuje tylko jedn± akcjê na token. Dozwolonych jest wiele komend REJECT, z których ka¿da wyszukuje najbardziej pasuj±cego nastêpcê. Na przyk³ad poni¿szy skaner skanuj±c token "abcd" zapisze na wyj¶ciu "abcdabcaba":



    %%

    a        |

    ab       |

    abc      |

    abcd     ECHO; REJECT;

    .|\n     /* zjedz nietrafione znaki */



(Pierwsze trzy regu³y maj± wspóln± akcjê z czwart±, gdy¿ u¿ywaj± akcji specjalnej '|'.) REJECT jest do¶æ kosztown± w³a¶ciwo¶ci± je¶li chodzi o wydajno¶æ skanera; je¶li jest u¿ywane w której¶ z akcji skanera, to spowolni wszystkie dopasowania skanera. Co wiêcej, REJECT nie mo¿e byæ u¿ywany z opcjami -Cf i -CF (zobacz ni¿ej).
Zauwa¿ te¿, ¿e, w przeciwieñstwie do innych akcji specjalnych, REJECT jest odga³êzieniem; kod akcji wystêpuj±cy bezpo¶rednio po nim nie zostanie wykonany.
-
yymore() mówi skanerowi, ¿e przy nastêpnym dopasowaniu regu³y, odpowiadaj±cy token powinien byæ doklejony do bie¿±cej warto¶ci yytext. Na przyk³ad, przy wej¶ciu "mega-kludge", poni¿szy przyk³ad na wyj¶ciu wypisze "mega-mega-kludge":



    %%

    mega-    ECHO; yymore();

    kludge   ECHO;



Pierwsze "mega-" jest dopasowane i wydrukowane na wyj¶cie. Nastêpnie dopasowane jest "kludge", lecz poprzednie "mega-" wci±¿ znajduje siê na pocz±tku yytext i komenda ECHO dla "kludge" wydrukuje w rzeczywisto¶ci "mega-kludge".

Dwie uwagi na temat yymore(). Po pierwsze, yymore() zale¿y od warto¶ci yyleng, odzwierciedlaj±cej rozmiar bie¿±cego tokenu. Zatem je¶li u¿ywasz yymore(), nie modyfikuj tej zmiennej. Po drugie, obecno¶æ yymore() w akcji skanera wp³ywa na pewne pogorszenie wydajno¶ci w szybko¶ci dokonywania przez skaner dopasowañ.

-
yyless(n) zwraca wszystkie poza pierwszymi n znakami bie¿±cego tokenu z powrotem do strumienia wej¶ciowego, sk±d zostan± one powtórnie przeskanowane przy dopasowywaniu nastêpnego wzorca. yytext i yyleng s± odpowiednio dostrajane (tj. yyleng bêdzie teraz równe n). Na przyk³ad, przy wej¶ciu "foobar", nastêpuj±cy kod wypisze "foobarbar":



    %%

    foobar    ECHO; yyless(3);

    [a-z]+    ECHO;



Podanie yyless argumentu zerowego powoduje reskanowanie ca³ego obecnego ³añcucha wej¶ciowego. O ile nie zmienisz sposobu kolejnego przetwarzania przez skaner wej¶cia (przy u¿yciu np. BEGIN), spowoduje to nieskoñczon± pêtlê.

Zwróæ uwagê, ¿e yyless jest makrem i mo¿e byæ u¿ywane tylko z pliku wej¶ciowego fleksa, a nie z innych plików ¼ród³owych.

-
unput(c) wstawia znak c z powrotem do strumienia wej¶ciowego. Bêdzie to nastêpny skanowany znak. Poni¿sza akcja pobierze bie¿±cy token i spowoduje, ¿e zostanie reskanowany po ujêciu w nawiasy.



    {

    int i;

    /* Kopiuj yytext, gdy¿ unput() niszczy jego zawarto¶æ */

    char *yycopy = strdup( yytext );

    unput( ')' );

    for ( i = yyleng - 1; i >= 0; --i )

        unput( yycopy[i] );

    unput( '(' );

    free( yycopy );

    }



Zwróæ uwagê, ¿e skoro ka¿dy unput() wstawia dany znak na pocz±tek strumienia, to wstawianie znaków musi odbywaæ siê ty³em-na-przód.

Wa¿nym potencjalnym problemem u¿ywania unput() jest fakt, ¿e je¶li u¿ywasz dyrektywy %pointer (domy¶lne), wywo³anie unput() niszczy zawarto¶æ yytext, poczynaj±c od znaku najbardziej z prawej, id±c w lewo za ka¿dym wywo³aniem. Je¶li potrzebujesz zachowaæ warto¶æ yytext po u¿yciu tej funkcji, (jak w powy¿szym przyk³adzie), musisz skopiowaæ jej zawarto¶æ gdzie indziej lub zbudowaæ skaner z u¿yciem %array.

Na koniec, zauwa¿ te¿, ¿e nie mo¿esz wstawiaæ tak znaków EOF. Nie mo¿na t± metod± zaznaczaæ koñca pliku w strumieniu.

-
input() odczytuje nastêpny znak ze strumienia wej¶ciowego. Na przyk³ad, poni¿sze jest jednym ze sposobów po¿erania komentarzy jêzyka C:



    %%

    "/*"        {

                register int c;



                for ( ; ; )

                    {

                    while ( (c = input()) != '*' &&

                            c != EOF )

                        ;    /* ze¿ryj tekst komentarza */



                    if ( c == '*' )

                        {

                        while ( (c = input()) == '*' )

                            ;

                        if ( c == '/' )

                            break;    /* znalaz³em koniec */

                        }



                    if ( c == EOF )

                        {

                        error( "EOF w komentarzu" );

                        break;

                        }

                    }

                }



(Zauwa¿, ¿e je¶li skaner jest skompilowany z u¿yciem C++, to input() nazywa siê yyinput(). Jest tak w celu zapobie¿enia zderzeniu nazwy ze strumieniem C++ poprzez nazwê input.)
-
YY_FLUSH_BUFFER wypró¿nia wewnêtrzny bufor skanera. Przy nastêpnym razie gdy skaner bêdzie dopasowywa³ siê do tokenu, najpierw nape³ni na nowo bufor z u¿yciem YY_INPUT (zobacz ni¿ej Generowany Skaner). Akcja ta jest szczególnym przypadkiem bardziej ogólnej funkcji yy_flush_buffer(), opisanej ni¿ej w sekcji Wielokrotne Bufory Wej¶ciowe.
-
yyterminate() mo¿e byæ u¿ywane zamiast instrukcji return akcji. Koñczy dzia³anie skanera i zwraca 0 do wywo³uj±cego skaner, wskazuj±c, ¿e "wszystko zrobione". Domy¶lnie, yyterminate() jest wywo³ywane równie¿ po napotkaniu koñca pliku. Jest to makro i mo¿e byæ redefiniowane.

GENEROWANY SKANER

Wynikiem dzia³ania fleksa jest plik lex.yy.c, zawieraj±cy procedurê skanuj±c± yylex() oraz zestaw tablic, u¿ywanych przez niego do dopasowywania tokenów i parê procedur i makr. Domy¶lnie yylex() jest deklarowany jako



    int yylex()

        {

        ... tu ró¿ne definicje i akcje ...

        }



(Je¶li twoje ¶rodowisko obs³uguje prototypy funkcji, to bêdzie to "int yylex( void )".) Definicjê tê mo¿na zmieniæ definiuj±c makro "YY_DECL". Na przyk³ad



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



informuje fleksa, by nadaæ procedurze skanuj±cej nazwê lexscan i ¿e procedura ta ma zwracaæ typ float i pobieraæ dwa argumenty (te¿ typu float). Zwróæ uwagê, ¿e je¶li podajesz argumenty procedurze skanuj±cej, u¿ywaj±c deklaracji w niezaprototypowanym stylu K&R, musisz zakoñczyæ definicjê ¶rednikiem (;).

Przy ka¿dym wywo³aniu yylex(), nastêpuje skanowanie tokenów z globalnego pliku wej¶ciowego yyin (który domy¶lnie wskazuje na stdin). Wczytywanie trwa a¿ do osi±gniêcia koñca pliku, lub a¿ do napotkania w której¶ z akcji instrukcji return.

Je¶li skaner osi±ga koniec pliku, to kolejne wywo³ania s± niezdefiniowane. Sposobem na skorygowanie tego jest przekierowanie yyin na nowy plik wej¶ciowy (w tym wypadku skanowanie nastêpuje z nowego pliku) lub wywo³anie yyrestart(). yyrestart() pobiera jeden argument: wska¼nik FILE * (który mo¿e byæ nil, je¶li ustawi³e¶ YY_INPUT na skanowanie ze ¼ród³a innego ni¿ yyin), i inicjalizuje yyin na pocz±tek tego pliku. W zasadzie nie ma ró¿nicy miêdzy zwyk³ym przypisaniem yyin do nowego pliku i u¿yciem yyrestart(); Procedura ta jest dostêpna z uwagi na kompatybilno¶æ z poprzednimi wersjami flex, a tak¿e dlatego, ¿e mo¿e byæ u¿ywana do prze³±czania plików wej¶ciowych w ¶rodku skanowania. Mo¿e byæ te¿ u¿ywana do porzucania bie¿±cego bufora wej¶ciowego poprzez wywo³anie z argumentem yyin; lepszym rozwi±zaniem jest jednak u¿ycie YY_FLUSH_BUFFER (patrz wy¿ej). Zauwa¿, ¿e yyrestart() nie resetuje warunku pocz±tkowego na INITIAL (zobacz ni¿ej Warunki Pocz±tkowe).

Je¶li yylex() koñczy skanowanie z powodu wywo³ania instrukcji return w jednej z akcji, skaner mo¿e byæ wo³any ponownie i wznowi dzia³anie tam, gdzie skoñczy³.

Domy¶lnie (i dla celów wydajno¶ci) skaner zamiast pojedynczych getc() wykonuje odczyty blokowe z yyin. Sposób pobierania wej¶cia mo¿e byæ kontrolowany przez definiowanie makra YY_INPUT. Sekwencja wywo³uj±ca YY_INPUT to "YY_INPUT(buf,wynik,max_rozmiar)". Jej wynikiem jest umieszczenie co najwy¿ej max_rozmiar znaków w tablicy znakowej buf i zwrócenie w zmiennej ca³kowitej wynik albo liczby wczytanych znaków albo sta³ej YY_NULL (0 w systemach uniksowych), okre¶laj±cej EOF. Domy¶lnie, YY_INPUT czyta z globalnego wska¼nika "yyin".

Przyk³adowa definicja YY_INPUT (w sekcji definicji pliku wej¶ciowego):




    %{

    #define YY_INPUT(buf,wynik,max_rozmiar) \

        { \

        int c = getchar(); \

        wynik = (c == EOF) ? YY_NULL : (buf[0] = c, 1); \

        }

    %}



Definicja ta zmieni przetwarzanie wej¶cia tak, by naraz pojawia³ siê tylko jeden znak.

W momencie, gdy skaner uzyska od YY_INPUT warunek koñca pliku, to wo³a funkcjê yywrap(). Je¶li yywrap() zwróci zero, to zak³ada, ¿e funkcja posz³a dalej i skonfigurowa³a yyin do wskazywania na nowy plik, a skanowanie trwa dalej. Je¶li zwróci warto¶æ niezerow±, skaner koñczy dzia³anie, zwracaj±c 0 do funkcji wywo³uj±cej. Zauwa¿, ¿e w ka¿dym przypadku warunek pocz±tkowy pozostaje niezmieniony; nie przechodzi on w INITIAL.

Je¶li nie chcesz podawaæ w³asnej wersji yywrap(), to musisz albo u¿yæ opcji %option noyywrap (wtedy skaner zachowuje siê, jakby yywrap() zwraca³o 1), albo konsolidowaæ z -lfl, uzyskuj±c tak domy¶ln± wersjê funkcji, zawsze zwracaj±cej 1.

Do skanowania z buforów pamiêciowych (a nie z plików) przeznaczone s± trzy procedury: yy_scan_string(), yy_scan_bytes() oraz yy_scan_buffer(). Zobacz ni¿ej dyskusjê w sekcji Wielokrotne Bufory Wej¶ciowe.

Swoje wyj¶cie ECHO skaner zapisuje do globalnego strumienia yyout (domy¶lnie stdout), który mo¿na przedefiniowaæ dziêki zwyk³emu przypisaniu tej zmiennej do innego wska¼nika FILE.

WARUNKI POCZ¡TKOWE

flex daje mechanizm warunkowej aktywacji regu³. Regu³y rozpoczynaj±ce siê od "<sc>" w³±cz± siê tylko je¶li skaner znajduje siê w warunku pocz±tkowym "sc". Na przyk³ad,



    <STRING>[^"]*        { /* zjedz cia³o ³añcucha ... */

                ...

                }



bêdzie aktywne tylko je¶li skaner jest w warunku pocz±tkowym "STRING", a



    <INITIAL,STRING,QUOTE>\.        { /* obs³u¿ cytowanie ... */

                ...

                }



bêdzie aktywne tylko je¶li obecnym warunkiem pocz±tkowym jest albo "INITIAL", albo "STRING" albo "QUOTE".

Warunki pocz±tkowe s± deklarowane w sekcji definicji wej¶cia przy u¿yciu niewciêtych linii, zaczynaj±cych siê od %s lub %x, za którymi nastêpuje lista nazw. Pierwsza postaæ deklaruje w³±czaj±ce warunki pocz±tkowe, a druga wykluczaj±ce. Warunek pocz±tkowy w³±cza siê przy u¿yciu akcji BEGIN. Regu³y u¿ywaj±ce danego warunku pocz±tkowego bêd± aktywne a¿ do wywo³ania nastêpnej akcji BEGIN. Je¶li warunek pocz±tkowy jest w³±czaj±cy , to regu³y bez warunków pocz±tkowych bêd± równie¿ aktywne. Je¶li jest wykluczaj±cy, to wykonywane bêd± tylko regu³y odpowiadaj±ce warunkowi pocz±tkowemu. Zestaw regu³ opieraj±cych siê na tym samym wykluczaj±cym warunku pocz±tkowym, opisuje skaner, który jest niezale¿ny od wszelkich innych regu³ wej¶cia fleksa. Z uwagi na to, warunki wykluczaj±ce u³atwiaj± tworzenie "mini-skanerów", które skanuj± czê¶ci wej¶cia, odmienne syntaktycznie od reszty (np. komentarze).

W rozró¿nieniu warunków w³±czaj±cych i wykluczaj±cych istnieje wci±¿ pewna niejasno¶æ: oto przyk³ad, ilustruj±cy ich powi±zanie. Zestaw regu³:




    %s przyklad

    %%



    <przyklad>foo  rob_cos();



    bar            cos_innego();



jest równowa¿ny



    %x przyklad

    %%



    <przyklad>foo   rob_cos();



    <INITIAL,przyklad>bar    cos_innego();



Bez u¿ycia kwalifikatora <INITIAL,przyklad>, wzorzec bar w drugim przyk³adzie nie by³by aktywny (tj. nie dopasowa³by siê) w warunku pocz±tkowym przyklad. Je¶li u¿yliby¶my do kwalifikowania bar tylko <przyklad>, to by³oby aktywny tylko w warunku pocz±tkowym przyklad, ale nie w INITIAL, podczas gdy w pierwszym przyk³adzie jest aktywny w obydwu, gdy¿ warunek pocz±tkowy przyklad jest w nim w³±czaj±cy (%s).

Zauwa¿ te¿, ¿e specjalny specyfikator <*> pasuje do dowolnego warunku pocz±tkowego. Tak wiêc, powy¿sze mo¿na zapisaæ równie¿ nastêpuj±co:




    %x przyklad

    %%



    <przyklad>foo   rob_cos();



    <*>bar    cos_innego();



Regu³a domy¶lna (wykonywania ECHO na ka¿dym niedopasowanym znaku) pozostaje aktywna w warunkach pocz±tkowych. Jest to w sumie równowa¿ne:




    <*>.|\n     ECHO;



BEGIN(0) zwraca do stanu oryginalnego, w którym aktywne s± tylko regu³y bez warunku pocz±tkowego. Stan ten jest oznaczany jako warunek pocz±tkowy "INITIAL", wiêc mo¿na go ustawiæ równie¿ poprzez BEGIN(INITIAL). (Nawiasy wokó³ nazwy warunku pocz±tkowego nie s± wymagane, lecz s± w dobrym tonie.)

Akcje BEGIN mog± byæ podawane jako kod wciêty na pocz±tku sekcji regu³. Na przyk³ad, nastêpuj±cy kod spowoduje, ¿e skaner wejdzie w warunek pocz±tkowy "SPECIAL" za ka¿dym razem, gdy wywo³ane zostanie yylex() a zmienna globalna enter_special bêdzie ustawiona na prawdê:




            int enter_special;



    %x SPECIAL

    %%

            if ( enter_special )

                BEGIN(SPECIAL);



    <SPECIAL>blahblahblah

    ...i kolejne rugu³y...



Dla zilustrowania wykorzystania warunków pocz±tkowych, oto skaner, który daje dwie ró¿ne interpretacje ³añcucha "123.456". Domy¶lnie bêdzie traktowa³ go jako 3 elementy, liczbê ca³kowit± 123, kropkê i liczbê ca³kowit± "456". Je¶li jednak ³añcuch zostanie poprzedzony lini± z napisem "expect-floats", to bêdzie go traktowa³ jako pojedynczy element zmiennoprzecinkowy (123.456).




    %{

    #include <math.h>

    %}

    %s expect



    %%

    expect-floats        BEGIN(expect);



    <expect>[0-9]+"."[0-9]+      {

                printf( "znalaz³em zmiennoprzecinkow±, = %f\n",

                        atof( yytext ) );

                }

    <expect>\n           {

                /* jest to koniec linii, wiêc

                 * potrzebujemy kolejnego "expect-number"

                 * przed rozpoznawaniem dalszych liczb

                 */

                BEGIN(INITIAL);

                }



    [0-9]+      {

                printf( "znalaz³em ca³kowit±, = %d\n",

                        atoi( yytext ) );

                }



    "."         printf( "znalaz³em kropkê\n" );



Oto skaner, który rozpoznaje komentarze C podczas zliczania linii.



    %x comment

    %%

            int line_num = 1;



    "/*"         BEGIN(comment);



    <comment>[^*\n]*        /* zjedz wszystko, co nie jest '*'     */

    <comment>"*"+[^*/\n]*   /* zjedz '*'-ki, po których nie ma '/' */

    <comment>\n             ++line_num;

    <comment>"*"+"/"        BEGIN(INITIAL);



Skaner ten mo¿e mieæ problemy z dopasowaniem maksymalnej ilo¶ci tekstu w ka¿dej z regu³. Ogólnie, przy pisaniu szybkich skanerów, próbuj dopasowywaæ w ka¿dej regule tyle, ile siê da.

Zauwa¿, ¿e nazwy warunków pocz±tkowych s± tak naprawdê warto¶ciami ca³kowitymi i mog± byæ tak przechowywane. Tak wiêc powy¿sze mo¿na rozwin±æ w nastêpuj±cym stylu:




    %x comment foo

    %%

            int line_num = 1;

            int comment_caller;



    "/*"         {

                 comment_caller = INITIAL;

                 BEGIN(comment);

                 }



    ...



    <foo>"/*"    {

                 comment_caller = foo;

                 BEGIN(comment);

                 }



    <comment>[^*\n]*        /* zjedz wszystko co nie jest '*'   */

    <comment>"*"+[^*/\n]*   /* zjedz '*', po których nie ma '/' */

    <comment>\n             ++line_num;

    <comment>"*"+"/"        BEGIN(comment_caller);



Co wiêcej, mo¿esz mieæ dostêp do bie¿±cego warunku pocz±tkowego poprzez makro YY_START (o warto¶ci ca³kowitej). Na przyk³ad, powy¿sze przypisania do comment_caller mo¿na by zapisaæ jako



    comment_caller = YY_START;



Flex jako alias do YY_START daje YYSTATE (gdy¿ jest to nazwa, u¿ywana przez AT&T lex).

Zauwa¿, ¿e warunki pocz±tkowe nie maj± w³asnej przestrzeni nazw; %s i %x-y deklaruj± nazwy podobnie jak #define.

Na deser, oto przyk³ad dopasowywania cytowanych w stylu C napisów przy u¿yciu wykluczaj±cych warunków pocz±tkowych, w³±cznie z rozwijanymi sekwencjami specjalnymi (lecz bez sprawdzania czy ³añcuch nie jest za d³ugi):




    %x str



    %%

            char string_buf[MAX_STR_CONST];

            char *string_buf_ptr;





    \"      string_buf_ptr = string_buf; BEGIN(str);



    <str>\"        { /* zobaczy³em zamykaj±cy cytat - gotowe */

            BEGIN(INITIAL);

            *string_buf_ptr = '\0';

            /* zwróæ typ i warto¶æ tokenu sta³ej ³añcuchowej do

             * analizatora

             */

            }



    <str>\n        {

            /* b³±d - niezakoñczona sta³a ³añcuchowa */

            /* generuj komunikat o b³êdzie */

            }



    <str>\\[0-7]{1,3} {

            /* ósemkowa sekwencja specjalna */

            int result;



            (void) sscanf( yytext + 1, "%o", &result );



            if ( result > 0xff )

                    /* b³±d, sta³a poza zakresem */



            *string_buf_ptr++ = result;

            }



    <str>\\[0-9]+ {

            /* generuj b³±d - z³a sekwencja specjalna; co¶ jak

             * '\48' lub '\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++;

            }



Czêsto, np. w niektórych przyk³adach powy¿ej mo¿na skoñczyæ pisz±c grupê regu³, rozpoczynaj±cych siê od tych samych warunków pocz±tkowych. Flex u³atwia ca³o¶æ wprowadzaj±c pojêcie zakresu warunku pocz±tkowego. Zakres rozpoczyna siê od:




    <SCs>{



gdzie SCs jest list± jednego lub wiêcej warunków pocz±tkowych. Wewn±trz zakresu warunku pocz±tkowego ka¿da regu³a dostaje automatycznie przedrostek <SCs> a¿ do napotkania '}', który odpowiada startowemu '{'. W ten sposób na przyk³ad



    <ESC>{

        "\\n"   return '\n';

        "\\r"   return '\r';

        "\\f"   return '\f';

        "\\0"   return '\0';

    }



jest równowa¿ne:



    <ESC>"\\n"  return '\n';

    <ESC>"\\r"  return '\r';

    <ESC>"\\f"  return '\f';

    <ESC>"\\0"  return '\0';



Zakresy warunków pocz±tkowych mog± byæ zagnie¿d¿ane.

Do obs³ugi stosów warunków pocz±tkowych s± przeznaczone trzy procedury:

void yy_push_state(int new_state)
wrzuca bie¿±cy warunek pocz±tkowy na stos warunków pocz±tkowych i prze³±cza siê w stan new_state, zupe³nie jak po u¿yciu BEGIN new_state (pamiêtaj, ¿e nazwy warunków pocz±tkowych s± równie¿ liczbami ca³kowitymi).
void yy_pop_state()
zdejmuje warto¶æ ze stosu i prze³±cza siê na ni± przez BEGIN.
int yy_top_state()
zwraca wierzcho³ek stosu bez zmiany zawarto¶ci stosu.

Stos warunków pocz±tkowych ro¶nie dynamicznie i nie ma ¿adnych wbudowanych ograniczeñ. Po wyczerpaniu pamiêci, wykonywanie programu jest przerywane.

Aby korzystaæ ze stosów warunków pocz±tkowych, skaner musi zawieraæ dyrektywê %option stack (zobacz ni¿ej rozdzia³ Opcje).

WIELOKROTNE BUFORY WEJ¦CIOWE

Niektóre skanery (te, obs³uguj±ce pliki do³±czane "include") wymagaj± odczytu z wielu strumieni wej¶ciowych. Poniewa¿ skanery flex wykonuj± sporo buforowania, nie mo¿na jednoznacznie zdecydowaæ sk±d bêdzie wykonywany nastêpny odczyt przez proste napisanie YY_INPUT, które jest wra¿liwe na kontekst skanowania. YY_INPUT wywo³ywane jest tylko gdy skaner osi±ga koniec swojego bufora, który mo¿e byæ daleko po wyskanowaniu instrukcji takiej jak "include", wymagaj±cej prze³±czenia ¼ród³a wej¶cia.

Aby za³atwiæ niektóre z tych problemów, flex daje mechanizm tworzenia i prze³±czania miêdzy wielokrotnymi buforami wej¶ciowymi. Bufor wej¶ciowy jest tworzony z u¿yciem funkcji




    YY_BUFFER_STATE yy_create_buffer( FILE *file, int size )



która pobiera wska¼nik FILE i rozmiar size, a nastêpnie tworzy bufor zwi±zany z danym plikiem, którego wielko¶æ (w znakach) jest okre¶lona parametrem rozmiaru. (w razie w±tpliwo¶ci u¿yj YY_BUF_SIZE jako rozmiaru). Funkcja zwraca uchwyt YY_BUFFER_STATE, który mo¿e byæ potem przekazywany do innych procedur (zobacz ni¿ej). Typ YY_BUFFER_STATE jest wska¼nikiem do struktury struct yy_buffer_state wiêc mo¿na bezpiecznie inicjalizowaæ zmienne YY_BUFFER_STATE na ((YY_BUFFER_STATE) 0) i odnosiæ siê do struktury w celu poprawnego zadeklarowania buforów wej¶ciowych w plikach ¼ród³owych innych ni¿ ten od twojego skanera. Zauwa¿, ¿e wska¼nik FILE w wywo³aniu yy_create_buffer jest u¿ywany tylko jako warto¶æ yyin widzianego przez YY_INPUT; je¶li redefiniujesz YY_INPUT tak, ¿eby nie u¿ywa³o yyin, to mo¿esz spokojnie przekazaæ tu zerowy wska¼nik FILE. Zadany bufor do skanowania wybiera siê za pomoc±:



    void yy_switch_to_buffer( YY_BUFFER_STATE new_buffer )



co prze³±cza bufor wej¶ciowy skanera tak, ¿e kolejne tokeny bêd± pochodzi³y z bufora new_buffer. Zauwa¿, ¿e yy_switch_to_buffer() mo¿e byæ u¿ywane przez yywrap() do zestawiania ró¿nych rzeczy we wznowionym skanowaniu zamiast otwierania nowego pliku i ustawiania na nim yyin. Zauwa¿ te¿, ¿e prze³±czanie ¼róde³ wej¶ciowych przez yy_switch_to_buffer() lub yywrap() nie zmienia warunku pocz±tkowego.



    void yy_delete_buffer( YY_BUFFER_STATE buffer )



u¿ywane jest do odzyskania miejsca zwi±zanego z buforem ( buffer mo¿e byæ warto¶ci± nil, ale wtedy funkcja ta nic nie robi.) Mo¿na te¿ czy¶ciæ bie¿±c± zawarto¶æ bufora, stosuj±c:



    void yy_flush_buffer( YY_BUFFER_STATE buffer )



Funkcja ta niszczy zawarto¶æ bufora, wiêc przy nastêpnej próbie dopasowania tokenu z bufora, skaner najpierw wype³ni bufor na nowo u¿ywaj±c YY_INPUT.

yy_new_buffer() jest synonimem yy_create_buffer(), udostêpnionym dla zgodno¶ci z C++ narzêdziami new i delete, s³u¿±cymi do tworzenia i niszczenia obiektów dynamicznych.

Na koniec makro YY_CURRENT_BUFFER zwraca uchwyt YY_BUFFER_STATE do bie¿±cego bufora.

A oto przyk³ad u¿ywania tych w³a¶ciwo¶ci w skanerze, rozwijaj±cym pliki za³±czane (w³a¶ciwo¶æ <<EOF>> jest opisywana ni¿ej):




    /* stan "incl" jest u¿ywany do wybierania nazwy za³±czanego pliku

     */

    %x incl



    %{

    #define MAX_INCLUDE_DEPTH 10

    YY_BUFFER_STATE include_stack[MAX_INCLUDE_DEPTH];

    int include_stack_ptr = 0;

    %}



    %%

    include             BEGIN(incl);



    [a-z]+              ECHO;

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



    <incl>[ \t]*      /* zjedz bia³± spacjê */

    <incl>[^ \t\n]+   { /* mam nazwê pliku za³±cznika */

            if ( include_stack_ptr >= MAX_INCLUDE_DEPTH )

                {

                fprintf( stderr, "Zbyt zagnie¿d¿one za³±czniki" );

                exit( 1 );

                }



            include_stack[include_stack_ptr++] =

                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 ( --include_stack_ptr < 0 )

                {

                yyterminate();

                }



            else

                {

                yy_delete_buffer( YY_CURRENT_BUFFER );

                yy_switch_to_buffer(

                     include_stack[include_stack_ptr] );

                }

            }



Do zestawiania buforów wej¶ciowych dla skanowania ³añcuchów z pamiêci zamiast plików istniej± trzy procedury. Ka¿da z nich tworzy nowy bufor wej¶ciowy do skanowania ³añcucha i zwraca odpowiadaj±cy uchwyt YY_BUFFER_STATE (który powiniene¶ skasowaæ stosuj±c yy_delete_buffer() po zakoñczeniu dzia³ania). Prze³±czaj± one te¿ przetwarzanie na nowy bufor przy u¿yciu yy_switch_to_buffer(), wiêc nastêpne wywo³anie yylex() rozpocznie skanowanie ³añcucha.
yy_scan_string(const char *str)
skanuje ³añcuch zakoñczony zerem.
yy_scan_bytes(const char *bytes, int len)
skanuje len bajtów (dopuszczalne zera w ¶rodku) pocz±wszy od pozycji bytes.

Zauwa¿, ¿e obydwie funkcje tworz± i skanuj± kopie oryginalnych danych. (Jest to po¿±dane, gdy¿ yylex() modyfikuje zawarto¶æ skanowanego bufora.) Kopiowania mo¿na unikn±æ, stosuj±c:

yy_scan_buffer(char *base, yy_size_t size)
które skanuje bufor na miejscu, zaczynaj±c od base, a w d³ugo¶ci size bajtów, z których dwa bajty musz± byæ znakami YY_END_OF_BUFFER_CHAR (ASCII NUL). Ostatnie dwa bajty nie s± skanowane; tak wiêc skanowanie przebiega od base[0] do base[size-2] w³±cznie.
Je¶li nie ustawisz odpowiednio base to yy_scan_buffer() zwraca wska¼nik nil zamiast tworzyæ nowy bufor wej¶ciowy.
Typ yy_size_t jest typem ca³kowitym, na który rzutuje siê wyra¿enie ca³kowite, okre¶laj±ce rozmiar bufora.

REGU£Y END-OF-FILE

Specjalna regu³a "<<EOF>>" okre¶la akcje, które nale¿y wykonaæ po osi±gniêciu koñca pliku i gdy yywrap() zwraca zero (tj. wskazuje brak dalszych plików do przetworzenia). Akcja musi siê zakoñczyæ zrobieniem jednej z czterech rzeczy:
-
przypisaniem yyin do nowego pliku wej¶ciowego (w poprzednich wersjach fleksa po dokonaniu przypisania nale¿a³o wywo³aæ specjaln± akcjê YY_NEW_FILE; nie jest to ju¿ wymagane);
-
wywo³aniem instrukcji return;
-
wywo³aniem specjalnej akcji yyterminate();
-
prze³±czeniem na nowy bufor za pomoc± yy_switch_to_buffer().

Regu³y <<EOF>> nie mog± byæ u¿ywane z innymi wzorcami; mog± one byæ kwalifikowane jedynie list± warunków pocz±tkowych. Je¶li podana jest niekwalifikowana regu³a <<EOF>>, to dotyczy ona wszystkich warunków pocz±tkowych, które nie maj± jeszcze akcji <<EOF>>. Aby podaæ regu³ê <<EOF>> tylko dla pocz±tkowego warunku pocz±tkowego u¿yj




    <INITIAL><<EOF>>



Te regu³y przydatne s± do ³apania rzeczy takich, jak niezamkniête cytaty. Przyk³ad:




    %x quote

    %%



    ...inne regu³y cytatowe...



    <quote><<EOF>>   {

             error( "nie zamkniêty cytat" );

             yyterminate();

             }

    <<EOF>>  {

             if ( *++filelist )

                 yyin = fopen( *filelist, "r" );

             else

                yyterminate();

             }



RÓ¯NE MAKRA

Mo¿na zdefiniowaæ makro YY_USER_ACTION, które s³u¿y do podania akcji wykonywanej zawsze przed akcj± dopasowanej regu³y. Na przyk³ad mo¿e byæ #definiowane do wywo³ywania procedury konwertuj±cej yytext na ma³e litery. Gdy wywo³ywane jest YY_USER_ACTION, zmienna yy_act okre¶la numer dopasowanej regu³y (regu³y s± numerowane od 1). Za³ó¿my, ¿e chcesz wyprofilowaæ jak czêsto jest u¿ywana ka¿da z regu³. Rozwi±zaniem jest nastêpuj±cy kawa³ek kodu:



    #define YY_USER_ACTION ++ctr[yy_act]



gdzie ctr jest tablic± przechowuj±c± zawarto¶æ ró¿nych regu³. Zauwa¿, ¿e makro YY_NUM_RULES daje ogóln± liczbê regu³ (³±cznie z regu³± domy¶ln±, nawet je¶li u¿ywasz -s), wiêc poprawn± deklaracj± ctr jest:



    int ctr[YY_NUM_RULES];



Makro YY_USER_INIT s³u¿y do podania akcji, która bêdzie wykonywana zawsze przed pierwszym skanem (i przed wewnêtrznymi inicjalizacjami skanera). Na przyk³ad mo¿na to wykorzystaæ do wo³ania procedury czytaj±cej tablice danych lub otwieraj±cej plik raportowy.

Makro yy_set_interactive(is_interactive) mo¿e byæ u¿ywane do sterowania czy bie¿±cy bufor jest uwa¿any za interaktywny. Bufor interaktywny jest przetwarzany wolniej, lecz musi byæ u¿ywany gdy wej¶cie rzeczywi¶cie jest interaktywne. Zapobiega to problemom zwi±zanym z oczekiwaniem na wype³nienie buforów (zobacz ni¿ej dyskusjê flagi -I). Warto¶æ niezerowa w wywo³aniu makra zaznacza bufor jako interaktywny, a zero to wy³±cza. Zauwa¿, ¿e u¿ycie tego makra przes³ania %option always-interactiv lub %option never-interactive (zobacz ni¿ej Opcje). Przed rozpoczêciem skanowania bufora, który jest (lub nie jest) interaktywny, nale¿y wywo³aæ funkcjê yy_set_interactive().

Makro yy_set_bol(at_bol) mo¿e byæ wykorzystywane do sterowania czy bie¿±cy kontekst skanuj±cy bufora dla nastêpnego dopasowania tokena jest dokonywany jak gdyby od pocz±tku linii. Niezerowa warto¶æ argumentu powoduje, ¿e regu³y zakotwiczone w '^' staj± siê aktywne, a warto¶æ zerowa je dezaktywuje.

Makro YY_AT_BOL() zwraca prawdê je¶li nastêpny token skanowany z bie¿±cego bufora bêdzie mia³ aktywne regu³y '^'. W przeciwnym wypadku zwraca fa³sz.

W niektórych generowanych skanerach akcje s± zebrane wszystkie w jedn± wielk± instrukcjê switch i s± rozdzielone makrem YY_BREAK, które mo¿na redefiniowaæ. Domy¶lnie jest to po prostu "break". Redefiniowanie YY_BREAK umo¿liwia u¿ytkownikom C++ zadeklarowanie, by makro nie robi³o niczego (uwa¿aj±c przy tym szczególnie, by ka¿da regu³a koñczy³a siê instrukcj± "break" lub "return"!). Mo¿na tak zapobiec cierpieniom spowodowanym ostrze¿eniami o tym, ¿e przez zakoñczenie akcji regu³y instrukcj± return, YY_BREAK jest nieosi±galne.

WARTO¦CI DOSTÊPNE DLA U¯YTKOWNIKA

Sekcja ta zestawia ró¿ne warto¶ci dostêpne dla u¿ytkownika w akcjach regu³owych.
-
char *yytext zawiera bie¿±cy tekst tokenu. Mo¿e byæ modyfikowany, lecz nie mo¿e byæ wyd³u¿any (nie mo¿na doklejaæ dodatkowych znaków na koñcu).
Je¶li w pierwszej sekcji opisu skanera pojawi siê dyrektywa specjalna %array to yytext zostanie zadeklarowane jako charyytext[YYLMAX], gdzie YYLMAX jest makrodefinicj±, któr± mo¿na przedefiniowaæ w pierwszej sekcji (warto¶æ domy¶lna to ogólnie 8KB). U¿ywanie %array daje wolniejsze skanery, lecz warto¶æ yytext staje siê odporna na wywo³ania input() i unput(), które potencjalnie niszcz± jego warto¶æ kiedy yytext jest wska¼nikiem znakowym. Przeciwn± dyrektyw± do %array jest %pointer, która jest dyrektyw± domy¶ln±.
Dyrektywy %array nie mo¿na u¿ywaæ do generowania klas skanera C++ (flaga -+).
-
int yyleng przechowuje d³ugo¶æ bie¿±cego tokenu.
-
FILE *yyin jest plikiem, z którego flex domy¶lnie odczytuje wej¶cie. Mo¿e byæ redefiniowany, lecz taki zabieg ma sens tylko nim rozpocznie siê skanowanie lub po napotkaniu EOF. Zmienianie tej warto¶ci w ¶rodku skanowania mo¿e daæ nieoczekiwane rezultaty spowodowane buforowaniem wej¶cia. Zamiast tego u¿yj wtedy yyrestart(). Po zakoñczeniu skanowania przez napotkanie koñca pliku, mo¿na przypisaæ warto¶æ yyin do nowego pliku wej¶ciowego i wywo³aæ ponownie skaner by dokoñczy³ skanowanie.
-
void yyrestart( FILE *new_file ) mo¿e byæ wo³ane do wskazywania yyin na nowy plik wej¶ciowy. Prze³±czenie na nowy plik jest natychmiastowe (wszelkie poprzednio buforowane wej¶cie jest tracone). Zauwa¿, ¿e wo³anie yyrestart() z argumentem yyin porzuca bie¿±cy bufor wej¶ciowy i kontynuuje skanowanie tego samego pliku wej¶ciowego.
-
FILE *yyout jest plikiem, do którego kierowane jest wyj¶cie akcji ECHO. U¿ytkownik mo¿e mu przypisaæ inn± warto¶æ.
-
YY_CURRENT_BUFFER zwraca uchwyt YY_BUFFER_STATE do bie¿±cego bufora.
-
YY_START zwraca warto¶æ ca³kowit±, odpowiadaj±c± bie¿±cemu warunkowi pocz±tkowemu. Warto¶ci tej mo¿na u¿ywaæ dalej z BEGIN do powrotu do tego warunku.

£¡CZENIE Z YACC

Jednym z podstawowych zastosowañ fleksa jest wspó³towarzyszenie generatorowi analizatorów yacc. Analizatory sk³adni yacc oczekuj± wywo³ania procedury o nazwie yylex() celem znalezienia kolejnego tokenu wej¶ciowego. Procedura powinna zwróciæ typ nastêpnego tokenu oraz wstawiæ zwi±zan± z nim warto¶æ do globalnej zmiennej yylval. Aby u¿ywaæ fleksa z yaccem, nale¿y yaccowi przekazaæ opcjê -d, co ka¿e mu generowaæ plik y.tab.h zawieraj±cy definicje wszystkich %tokenów(%tokens) pojawiaj±cych siê w wej¶ciu yacc. Plik ten jest nastêpnie za³±czany do skanera fleksowego. Na przyk³ad je¶li jednym z tokenów jest "TOK_NUMBER", to czê¶æ skanera mo¿e wygl±daæ tak:



    %{

    #include "y.tab.h"

    %}



    %%



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



OPCJE

flex ma nastêpuj±ce opcje:
-b
Generuje informacje zapasowe do lex.backup. Oto lista stanów skanera, które wymagaj± kopii zapasowych oraz znaki wej¶ciowe dla których to zachodzi. Dodaj±c regu³y mo¿na usun±æ stany zapasowe. Je¶li wyeliminowane zostan± wszystkie stany zapasowe, a u¿yte bêdzie -Cf lub -CF, wygenerowany skaner bêdzie dzia³a³ szybciej (zobacz flagê -p). Opcj± to powinni siê martwiæ jedynie u¿ytkownicy wyciskaj±cy ostatnie poty ze swoich skanerów. (Zobacz sekcjê o Rozwa¿aniach nad Wydajno¶ci±.)
-c
nieu¿ywana i niezalecana opcja dla zgodno¶ci z POSIX-em.
-d
powoduje, ¿e generowany skaner dzia³a w trybie debug. Za ka¿dym razem po rozpoznaniu wzorca, gdy globalna zmienna yy_flex_debug jest niezerowa (co jest domy¶lne), skaner zapisze na stderr liniê w postaci:



    --accepting rule at line 53 ("dopasowany tekst")



Numer linii odnosi siê do po³o¿enia regu³y w pliku definiuj±cym skaner (tj. w pliku, potraktowanym fleksem). Komunikaty s± równie¿ generowane gdy skaner robi kopie zapasowe, przyjmuje domy¶ln± regu³ê, dochodzi do koñca bufora (lub napotyka NUL; w tym momencie obydwa [zdarzenia] wygl±daj± jednakowo z punktu widzenia skanera) lub osi±ga koniec pliku.
-f
okre¶la szybki skaner. Nie dokonywana jest kompresja tabel i pomijane jest stdio. W efekcie kod jest du¿y, lecz szybki. Opcja ta jest równowa¿na -Cfr (zobacz ni¿ej).
-h
generuje zestawienie "pomocy" opcji fleksa na stdout i koñczy dzia³anie. -? i --help s± równowa¿nikami -h.
-i
nakazuje fleksowi generowania skanera niewra¿liwego na wielko¶æ znaków. Wielko¶æ liter we wzorcach zostanie zignorowany, a tokeny wej¶cia bêd± dopasowywane niezale¿nie od wielko¶ci. Dopasowany tekst znajduj±cy siê w yytext bêdzie mia³ zachowan± oryginaln± wielko¶æ liter.
-l
w³±cza maksymaln± zgodno¶æ z oryginaln± implementacj± leksa z AT&T. Zauwa¿, ¿e nie oznacza to pe³nej zgodno¶ci. U¿ycie tej opcji kosztuje sporo wydajno¶ci i eliminuje z u¿ycia opcje -+,-f,-F,-Cf lub -CF. Dla szczegó³ów o zapewnianej zgodno¶ci, zobacz ni¿ej sekcjê o niezgodno¶ciach miêdzy Leksem i POSIX-em. Opcja ta powoduje te¿ z#definiowanie nazwy YY_FLEX_LEX_COMPAT w generowanym skanerze.
-n
kolejna ignorowana opcja dodana dla zgodno¶ci z POSIX-em.
-p
generuje raport o wydajno¶ci na stderr. Raport sk³ada siê z komentarzy o w³a¶ciwo¶ciach pliku wej¶ciowego fleksa, wiêc powoduje znaczn± utratê wydajno¶ci skanera. Je¶li podasz tê flagê dwukrotnie, uzyskasz te¿ komentarze o w³a¶ciwo¶ciach, które doprowadzi³y do drugorzêdnych utrat wydajno¶ci.
Zauwa¿, ¿e u¿ycie REJECT, %option yylineno, i zmiennego wisz±cego kontekstu (variable trailing context) (zobacz ni¿ej sekcjê o Niedostatkach / B³êdach) powoduje znaczn± utratê wydajno¶ci; u¿ywanie yymore(), operatora ^ i flagi -I powoduje pomniejsze utraty wydajno¶ci.
-s
powoduje, ¿e domy¶lna regu³a (powoduj±ca echo niedopasowanego wej¶cia skanera na stdout) nie jest wykonywana. Je¶li skaner napotka wej¶cie, którego nie mo¿e dopasowaæ do regu³, przerywa dzia³anie z b³êdem. Opcja ta jest przydatna do znajdowania dziur w zbiorze regu³ skanera.
-t
nakazuje fleksowi zapisanie wygenerowanego skanera na standardowe wyj¶cie zamiast do pliku lex.yy.c.
-v
nakazuje fleksowi pisanie na stderr zestawienia statystyk dotycz±cych generowanego skanera. Wiêkszo¶æ statystyk jest pozbawiona znaczenia dla typowego u¿ytkownika, lecz pierwsza z linijek wskazuje wersjê fleksa (to samo co zg³asza opcja -V), a nastêpna linia flagi u¿yte do generowania skanera, z domy¶lnymi w³±cznie.
-w
powstrzymuje komunikaty o ostrze¿eniach.
-B
nakazuje fleksowi generowanie skanera wsadowego, czyli odwrotno¶æ skanerów interaktywnych, generowanych przez -I (zobacz ni¿ej). Ogólnie, opcji -B u¿ywa siê maj±c pewno¶æ, ¿e skaner nigdy nie bêdzie u¿ywany interaktywnie i chc±c wycisn±æ jeszcze troszeczkê wiêcej wydajno¶ci. Je¶li chcesz zyskaæ wiêcej wydajno¶ci, powiniene¶ u¿yæ opcji -Cf lub -CF (opisanych ni¿ej), które w³±czaj± -B i tak automatycznie.
-F
mówi, ¿e nale¿y u¿yæ reprezentacji tablicy szybkiego skanera (i stdio ma byæ pominiête). Reprezentacja ta jest mniej wiêcej tak szybka jak reprezentacja pe³nej tablicy (-f), i dla niektórych zestawów wzorców bêdzie znacznie mniejsza (a dla innych wiêksza). Ogólnie, je¶li wzorzec zawiera zarówno "s³owa kluczowe" jak i ³api±c±-wszystko regu³ê "identyfikatora", tak jak poni¿szy zestaw:



    "case"    return TOK_CASE;

    "switch"  return TOK_SWITCH;

    ...

    "default" return TOK_DEFAULT;

    [a-z]+    return TOK_ID;



to lepiej u¿yæ reprezentacji pe³nej tablicy. Je¶li obecna jest tylko regu³a "identyfikatora" i u¿ywasz potem hasza lub podobnej rzeczy do wykrywania s³ów kluczowych, to lepiej u¿yæ opcji -F.
Opcja ta odpowiada -CFr (zobacz ni¿ej). Nie mo¿na jej u¿ywaæ z -+.
-I
nakazuje fleksowi generowanie skanera interaktywnego. Skaner interaktywny patrzy naprzód do wyboru dopasowania jedynie je¶li musi. Okazuje siê, ¿e patrzenie o jeden dodatkowy znak dalej, nawet je¶li skaner ma ju¿ do¶æ do dopasowania tokenu jest trochê szybsze ni¿ wersja minimalna. Lecz skanery patrz±ce naprzód daj± dziadowsk± wydajno¶æ interaktywn±; na przyk³ad gdy u¿ytkownik wpisze now± liniê, to nie jest ona rozpoznawana jako token nowej linii dopóki nie wprowadzony zostanie nastêpny token, co oznacza czêsto wpisanie ca³ej kolejnej linii.
Skanery fleksa s± domy¶lnie interaktywne, chyba ¿e u¿yjesz opcji kompresji tablicy -Cf lub -CF (zobacz ni¿ej). Jest tak dlatego, ¿e je¶li oczekujesz wysokiej wydajno¶ci, to powiniene¶ u¿yæ jednej z tych opcji, a je¶li tego nie zrobi³e¶, flex zak³ada, ¿e jeste¶ gotów po¶wiêciæ trochê wydajno¶ci na rzecz intuicyjnego zachowania interaktywnego. Zauwa¿ te¿, ¿e nie mo¿esz u¿yæ -I w po³±czeniu z -Cf lub -CF. Z tej przyczyny opcja ta nie jest w rzeczywisto¶ci wymagana; jest domy¶lnie w³±czona dla tych przypadków, dla których jest dopuszczalna.
Opcj± -B mo¿esz wymusiæ by skaner nie by³ interaktywny (zobacz powy¿ej).
-L
nakazuje fleksowi nie generowaæ dyrektyw #line. Bez tej opcji flex przyprawia generowany skaner dyrektywami #line, wiêc komunikaty o b³êdach w akcjach bêd± poprawnie po³o¿one wzglêdem oryginalnego pliku wej¶ciowego fleksa (je¶li b³êdy wynikaj± z kodu w pliku wej¶ciowym) lub [wzglêdem] lex.yy.c (je¶li b³êdy s± win± fleksa -- powiniene¶ zg³osiæ takie b³êdy pod adres e-mail podany poni¿ej.)
-T
powoduje, ¿e flex dzia³a w trybie ¶ledzenia. Bêdzie generowa³ na stderr wiele komunikatów o postaci wej¶cia i wynikaj±cych zeñ niedeterministycznych i deterministycznych automatach skoñczonych. Opcja ta jest u¿ywana zwykle w opiece nad fleksem.
-V
drukuje numer wersji na stdout i koñczy dzia³anie. --version jest synonimem -V.
-7
nakazuje fleksowi generowanie skanera 7-bitowego, tj. takiego który mo¿e rozpoznawaæ w swoim wej¶ciu tylko znaki 7-bitowe. Zalet± u¿ywania -7 jest to, ¿e tablice skanera bêd± o po³owê mniejsze ni¿ wygenerowane opcj± -8 (zobacz ni¿ej). Wad± jest to, ¿e skanery takie czêsto siê zawieszaj± lub za³amuj± je¶li na ich wej¶ciu znajdzie siê znak 8-bitowy.
Zauwa¿ jednak, ¿e je¶li generujesz skaner z u¿yciem opcji kompresji tablic -Cf lub -CF, to u¿ycie -7 zachowa jedynie niewielki rozmiar przestrzeni tablic, a spowoduje, ¿e skaner bêdzie znacz±co mniej przeno¶ny. Domy¶lnym zachowaniem fleksa jest generowanie skanerów 8-bitowych, chyba ¿e u¿yto opcji -Cf lub -CF, i wtedy flex generuje domy¶lnie skaner 7-bitowy, chyba ¿e twoja maszyna zawsze by³a skonfigurowana na generowanie skanerów 8-bitowych (co czêsto siê zdarza poza USA). To, czy flex wygenerowa³ skaner 7 czy 8 bitowy, mo¿na okre¶liæ, sprawdzaj±c zestawienie flag w wyj¶ciu -v, co opisano wy¿ej.
Zauwa¿, ¿e je¶li u¿ywasz -Cfe lub -CFe, flex wci±¿ domy¶lnie generuje skaner 8-bitowy, gdy¿ po kompresji pe³ne tablice 8-bitowe nie s± wiele wiêksze od 7-bitowych.
-8
nakazuje fleksowi generowanie skanera 8-bitowego, tj. takiego, który rozpoznaje znaki 8-bitowe. Flaga ta jest wymagana jedynie dla skanerów wygenerowanych z u¿yciem -Cf lub -CF, gdy¿ w innych wypadkach jest ona przyjmowana jako domy¶lna.
-+
okre¶la, ¿e chcesz by fleks wygenerowa³ klasê skanera w C++. Zobacz sekcjê o generowaniu skanerów C++.
-C[aefFmr]
steruje poziomem kompresji tablic, balansuj±c miêdzy ma³ymi a szybkimi skanerami.
-Ca ("wyrównaj") nakazuje fleksowi po¶wiêciæ rozmiar tablic w wygenerowanych skanerach na rzecz szybko¶ci, gdy¿ elementy tablic mog± byæ lepiej wyrównane pod k±tem dostêpu do pamiêci i obliczeñ. Na niektórych architekturach RISC pobieranie i operowanie na d³ugich s³owach jest efektywniejsze ni¿ na mniejszych jednostkach, takich jak krótkie s³owa. Opcja ta mo¿e podwoiæ rozmiar tablic u¿ywanych przez twój skaner.
-Ce Nakazuje fleksowi budowanie klas równowa¿no¶ci, tj. zestawów znaków o identycznych w³a¶ciwo¶ciach leksykalnych (np. je¶li jedynym wyst±pieniem cyfr w pliku wej¶ciowym fleksa jest klasa znaków "[0-9]", to cyfry z przedzia³y od 0 do 9 zostan± wstawione do tej samej klasy równowa¿no¶ci. Klasy takie zwykle znacznie redukuj± ostateczne rozmiary tablic/obiektów (zwykle 2-5 razy) i s± ca³kiem tanie od strony wydajno¶ciowej (jedno podgl±dniêcie w tablicy na skanowany znak).
-Cf okre¶la, ¿e nale¿y generowaæ pe³ne tablice skanera - flex nie ma ich kompresowaæ poprzez branie korzy¶ci z podobnych funkcji przej¶æ dla ró¿nych stanów.
-CF okre¶la, ¿e nale¿y u¿yæ alternatywnej, szybkiej reprezentacji skanera (opisanej pod flag± -F). Opcja ta nie mo¿e byæ u¿ywana z -+.
-Cm nakazuje fleksowi budowanie klas meta-równowa¿no¶ci, które s± zbiorami klas równowa¿no¶ci (lub znaków, je¶li klasy równowa¿no¶ci nie s± u¿ywane), które s± czêsto u¿ywane wspólnie. Klasy takie s± czêsto dobr± rzecz± podczas u¿ywania skompresowanych tablic, lecz maj± one ju¿ umiarkowany wp³yw na wydajno¶æ (dwa lub jeden test "if" i jedno podgl±dniêcie tablicy na skanowany znak).
-Cr powoduje, ¿e generowany skaner omija u¿ycie standardowej biblioteki I/O dla wej¶cia. Zamiast wo³aæ fread() lub getc(), skaner bêdzie u¿ywaæ wywo³ania systemowego read(), zyskuj±c tak trochê na wydajno¶ci (w skali zale¿nej od systemu). W rzeczywisto¶ci jest to bez znaczenia, chyba ¿e u¿ywasz te¿ -Cf lub -CF. Wykorzystanie -Cr mo¿e te¿ spowodowaæ dziwne zachowanie je¶li np. odczytasz z yyin z pomoc± stdio przed wywo³aniem skanera (skaner pominie tekst pozostawiony przez twoje odczyty w buforze wej¶ciowym stdio).
-Cr nie dzia³a je¶li zdefiniujesz YY_INPUT (zobacz wy¿ej Generowany Skaner).
Samotne -C okre¶la, ¿e tablice skanera powinny byæ kompresowane, lecz nie nale¿y u¿ywaæ klas równowa¿no¶ci i klas metarównowa¿no¶ci.
Opcje -Cf lub -CF i -Cm nie maj± sensu razem - nie ma sytuacji dla klas metarównowa¿no¶ci je¶li tablica nie jest kompresowana. Poza tym opcje mo¿na swobodnie ³±czyæ.
Domy¶lnym ustawieniem jest -Cem, które okre¶la, ¿e flex powinien generowaæ klasy równowa¿no¶ci i metarównowa¿no¶ci. Ustawienie to daje najwy¿szy stopieñ kompresji tablic. Kosztem wiêkszych tablic mo¿na uzyskaæ szybciej wykonuj±ce siê skanery. Nastêpuj±ce zestawienie jest mniej wiêcej prawdziwe:



    najwolniejsze i najmniejsze

          -Cem

          -Cm

          -Ce

          -C

          -C{f,F}e

          -C{f,F}

          -C{f,F}a

    najszybsze i najwiêksze



Zauwa¿, ¿e skanery z najmniejszymi tablicami s± zwykle najszybciej generowane i kompilowane, wiêc podczas prac rozwojowych prawdopodobnie najchêtniej u¿yjesz domy¶lnej, maksymalnej kompresji.
-Cfe jest czêsto dobrym kompromisem miêdzy szybko¶ci± a rozmiarem dla skanerów gotowych do wdro¿enia (production scanners).
-ooutput
nakazuje fleksowi zapisanie skanera do pliku output zamiast do lex.yy.c. Je¶li po³±czysz -o z opcj± -t, to skaner jest zapisywany na stdout, lecz jego dyrektywy #line (zobacz wy¿ej opcjê \-L), odnosz± siê do pliku output.
-Pprefiks
zmienia domy¶lny przedrostek yy u¿ywany przez fleksa dla wszystkich zmiennych i funkcji globalnych na prefiks. Na przyk³ad -Pfoo zmienia nazwê yytext na footext. Zmienia to te¿ nazwê domy¶lnego pliku wyj¶ciowego z lex.yy.c na lex.foo.c. A oto wszystkie nazwy, których dotyczy takie zachowanie:



    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



(Je¶li u¿ywasz skanera C++, to dotyczyæ to bêdzie tylko yywrap i yyFlexLexer.) Wewn±trz samego skanera mo¿na wci±¿ u¿ywaæ jednej i drugiej konwencji nazywania; jednak z zewn±trz dozwolone s± tylko nazwy zmodyfikowane.
Opcja ta umo¿liwia ³atwe ³±czenie w ca³o¶æ ró¿nych programów fleksa w jeden plik wykonywalny. Zauwa¿ jednak, ¿e u¿ywanie tej opcji zmienia te¿ nazwê yywrap(), wiêc musisz teraz albo udostêpniæ w³asn± wersjê tej procedury dla swojego skanera, albo u¿yæ %option noyywrap, gdy¿ konsolidacja z -lfl nie daje ju¿ funkcji domy¶lnej.
-Sskeleton_file
przes³ania domy¶lny plik szkieletowy, na podstawie którego flex buduje swoje skanery. Nie bêdziesz u¿ywaæ tej opcji, chyba ¿e zajmujesz siê rozwojem fleksa.

flex daje te¿ mechanizm kontrolowania opcji z samej specyfikacji skanera, zamiast linii poleceñ. Dzia³a to przez w³±czanie dyrektyw %option w pierwszej sekcji specyfikacji skanera. W jednej dyrektywie %option mo¿na podawaæ wiele opcji, a w samej pierwszej sekcji pliku wej¶ciowego fleksa mo¿na u¿ywaæ wielu dyrektyw.

Wiêkszo¶æ opcji jest podawana po prostu jako nazwy, poprzedzone opcjonalnie s³owem "no" (bez bia³ych spacji w ¶rodku), które neguje ich znaczenie. Czê¶æ jest równowa¿na flagom fleksa lub ich negacjom:




    7bit            -7

    8bit            -8

    align           -Ca

    backup          -b

    batch           -B

    c++             -+



    caseful lub

    case-sensitive  przeciwne do -i (domy¶lne)



    case-insensitive lub

    caseless        -i



    debug           -d

    default         przeciwne do -s

    ecs             -Ce

    fast            -F

    full            -f

    interactive     -I

    lex-compat      -l

    meta-ecs        -Cm

    perf-report     -p

    read            -Cr

    stdout          -t

    verbose         -v

    warn            przeciwne do -w

                    (dla -w u¿yj "%option nowarn")



    array           równowa¿ne "%array"

    pointer         równowa¿ne "%pointer" (domy¶lne)



Niektóre %opcje daj± w³a¶ciwo¶ci niedostêpne gdzie indziej:
always-interactive
nakazuje fleksowi generowanie skanera, który zawsze uwa¿a swoje wej¶cie za "interaktywne". Normalnie przy ka¿dym pliku wej¶ciowym skaner wo³a isatty() do okre¶lenia czy wej¶cie skanera jest interaktywne i powinno byæ czytane po znaku. Po u¿yciu tej opcji wywo³anie takie nie jest robione.
main
nakazuje fleksowi udostêpniæ domy¶lny program main() dla skanera, który po prostu wo³a yylex(). Opcja ta implikuje noyywrap (zobacz ni¿ej).
never-interactive
nakazuje fleksowi generowanie skanera, który zawsze uwa¿a swoje wej¶cie za "nieinteraktywne" (znów, nie jest wo³ane isatty()). Opcja ta jest przeciwna do always-interactive.
stack
w³±cza u¿ywanie stosów warunków pocz±tkowych (zobacz wy¿ej Warunki Pocz±tkowe).
stdinit
je¶li jest ustawione (np. %option stdinit) to zachodzi inicjalizacja yyin i yyout na stdin i stdout, zamiast domy¶lnych nil. Niektóre istniej±ce programy lex zale¿± od tego zachowania, nawet je¶li nie jest ono zgodne z ANSI C, które nie wymagaj± sta³ych czasu kompilacji stdin i stdout.
yylineno
nakazuje fleksowi generowanie skanera, który przechowuje liczbê obecnie odczytanych linii w zmiennej globalnej yylineno. Opcja ta jest wymuszana przez %option lex-compat.
yywrap
je¶li nie jest ustawione (np. %option noyywrap), to skaner nie wo³a yywrap() na koñcu pliku, lecz po prostu przyjmuje, ¿e nie ma ju¿ plików do skanowania (dopóki u¿ytkownik nie wska¿e yyin na nowy plik i nie wywo³a yylex() ponownie).

flex skanuje akcje regu³ w celu okre¶lenia czy u¿ywasz w³a¶ciwo¶ci REJECT lub yymore(). Opcje reject i yymore mog± przes³oniæ jego decyzjê na tak±, jak± ustawisz przy u¿yciu opcji, zarówno ustawiaj±c je (np. %option reject) do wskazania, ¿e w³a¶ciwo¶æ jest rzeczywi¶cie u¿ywana, lub wy³±czaj±c je, wskazuj±c, ¿e w³a¶ciwo¶æ nie jest u¿ywana (np. %option noyymore).

Trzy opcje pobieraj± warto¶ci ³añcuchowe, offsetowane znakiem '=':




    %option outfile="ABC"



jest równowa¿ne -oABC, a



    %option prefix="XYZ"



jest równowa¿ne -PXYZ. Poza tym,



    %option yyclass="foo"



dotyczy tylko skanerów C++ (opcja -+). Mówi to fleksowi, ¿e foo jest wyprowadzone jako podklasa yyFlexLexer, wiêc flex bêdzie umieszcza³ twoje akcje w funkcji sk³adowej foo::yylex() zamiast w yyFlexLexer::yylex(). Powoduje to te¿ generowanie funkcji sk³adowej yyFlexLexer::yylex(), emituj±cej po wywo³aniu b³±d dzia³ania (przez wywo³anie yyFlexLexer::LexerError()). Dla dalszych informacji zobacz te¿ ni¿ej Generowanie Skanerów C++.

Istniej± opcje dla purystów, nie chc±cych widzieæ w swoich skanerach niepotrzebnych procedur. Ka¿da z nastêpuj±cych opcji (np. (np., %option nounput), powoduje, ¿e dana procedura nie pojawia siê w wygenerowanym skanerze:




    input, unput

    yy_push_state, yy_pop_state, yy_top_state

    yy_scan_buffer, yy_scan_bytes, yy_scan_string



(chocia¿ yy_push_state() i podobne i tak nie pojawi± siê dopóki nie u¿yjesz %optionstack).

ROZWA¯ANIA NAD WYDAJNO¦CI¡

Podstawowym zadaniem przy projektowaniu fleksa by³o zapewnienie, ¿e bêdzie generowa³ wydajne skanery. Zosta³ zoptymalizowany do dobrej wspó³pracy z wielkimi zestawami regu³. Poza omawianymi ju¿ wp³ywami opcji kompresji -C, istnieje jeszcze kilka akcji/opcji wp³ywaj±cych na wydajno¶æ. S± to, od najkosztowniejszej do najmniej kosztownej:



    REJECT

    %option yylineno

    arbitralny wisz±cy kontekst



    zestawy wzorców, wymagaj±ce cofania

    %array

    %option interactive

    %option always-interactive



    '^' operator rozpoczêcia linii

    yymore()



z których pierwsze trzy s± bardzo kosztowne, a ostatnie dwa w miarê tanie. Zauwa¿ te¿, ¿e unput() jest implementowane jako wywo³anie procedurowe, które prawdopodobnie wykonuje sporo pracy, podczas gdy yyless() jest tanim makrem; wiêc je¶li wstawiasz z powrotem nadmiarowy wyskanowany tekst, u¿yj yyless().

REJECT powinno byæ unikane za wszelk± cenê z punktu widzenia wydajno¶ci. Jest to szczególnie kosztowna opcja.

Pozbycie siê cofania jest trudne i mo¿e czêsto prowadziæ do b³êdów w skomplikowanych skanerach. W praktyce zaczyna siê od u¿ycia flagi -b do wygenerowania pliku lex.backup. Na przyk³ad dla wej¶cia




    %%

    foo        return TOK_KEYWORD;

    foobar     return TOK_KEYWORD;



plik ten wygl±da tak:



    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.



Pierwszych kilka linii mówi, ¿e istnieje stan skanera, w którym mo¿e on przyj±æ 'o', lecz nie mo¿e przyj±æ innego znaku i ¿e w tym stanie aktualnie skanowany tekst nie pasuje do ¿adnej regu³y. Stan ten pojawia siê podczas próby dopasowania regu³ z linijek 2 i 3 pliku wej¶ciowego. Je¶li skaner jest w tym stanie i odczyta cokolwiek innego ni¿ 'o', to bêdzie musia³ siê cofn±æ i okre¶liæ, która regu³a pasuje. Po chwili skrobania siê w g³owê mo¿na zauwa¿yæ, ¿e musi to byæ stan, gdy skaner zobaczy³ "fo". W tej sytuacji otrzymanie czegokolwiek innego ni¿ 'o' spowoduje cofniêcie do prostego dopasowania 'f' (regu³a domy¶lna).

Komentarz odno¶nie stanu #8 mówi, ¿e istnieje problem przy skanowaniu "foob". Rzeczywi¶cie, je¶li pojawi siê dowolny znak inny ni¿ 'a', to skaner bêdzie musia³ siê cofn±æ do przyjmowania "foo". Podobnie sprawa ma siê ze stanem #9, mówi±cym o "fooba", po którym nie nastêpuje 'r'.

Ostatni komentarz przypomina nam, ¿e usuwanie cofania nie ma sensu je¶li nie u¿ywamy -Cf lub -CF, gdy¿ nie daje to ¿adnego zysku wydajno¶ci na skanerach kompresowanych.

Sposobem usuwania cofania jest dodawanie regu³ dla "b³êdów":




    %%

    foo         return TOK_KEYWORD;

    foobar      return TOK_KEYWORD;



    fooba       |

    foob        |

    fo          {

                /* fa³szywy alarm, nie jest to s³owo kluczowe */

                return TOK_ID;

                }



Eliminowanie cofania mo¿na przeprowadziæ równie¿ przy u¿yciu regu³y "³ap-wszystko":




    %%

    foo         return TOK_KEYWORD;

    foobar      return TOK_KEYWORD;



    [a-z]+      return TOK_ID;



Jest to, tam gdzie mo¿na je zastosowaæ, najlepsze rozwi±zanie.

Komunikaty cofania czêsto uk³adaj± siê w kaskady. W skomplikowanych zbiorach regu³ mo¿na dostaæ setki komunikatów. Mimo to, je¶li mo¿na je zdeszyfrowaæ, to ich usuwanie wymaga tylko tuzina regu³ (³atwo siê jednak pomyliæ i spowodowaæ, ¿e regu³a obs³ugi b³êdu bêdzie pasowaæ do prawid³owego tokena. Mo¿liwe, ¿e przysz³e implementacje fleksa bêd± automatycznie zajmowa³y siê usuwaniem cofania).

Wa¿ne jest pamiêtanie, ¿e korzy¶ci z eliminacji tego problemu zyskujesz dopiero po zlikwidowaniu ka¿dej instancji cofania. Pozostawienie choæ jednej oznacza, ¿e nie zyskujesz niczego.

Zmienny wisz±cy kontekst (gdzie zarówno prowadz±ca jak i koñcz±ca czê¶æ nie maj± ustalonej d³ugo¶ci) wprowadza utratê wydajno¶ci zbli¿on± do REJECT (tzn. znaczn±). Dlatego gdy tylko mo¿na, to zapisz tak± regu³ê:




    %%

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



jako:



    %%

    mouse/cat|dog         run();

    rat/cat|dog           run();



lub jako



    %%

    mouse|rat/cat         run();

    mouse|rat/dog         run();



zwróæ uwagê, ¿e specjalna akcja '|' nie powoduje ¿adnych oszczêdno¶ci, a wrêcz mo¿e pogorszyæ sprawê (zobacz ni¿ej Niedostatki / B³êdy).

Innym obszarem, gdzie u¿ytkownik mo¿e zwiêkszaæ wydajno¶æ skanera jest to, ¿e im d³u¿sze s± dopasowywane tokeny, tym szybciej dzia³a skaner. Jest tak dlatego, ¿e przetwarzanie d³ugich tokenów wiêkszo¶ci znaków wej¶ciowych zachodzi w wewnêtrznej (krótkiej) pêtli skanuj±cej i rzadko musi przechodziæ przez dodatkow± pracê zwi±zan± z ustawianiem ¶rodowiska skanuj±cego (np. yytext) dla akcji. Przypomnij sobie skaner komentarzy C:




    %x comment

    %%

            int line_num = 1;



    "/*"         BEGIN(comment);



    <comment>[^*\n]*

    <comment>"*"+[^*/\n]*

    <comment>\n             ++line_num;

    <comment>"*"+"/"        BEGIN(INITIAL);



Mo¿na to przyspieszyæ nastêpuj±co:



    %x comment

    %%

            int line_num = 1;



    "/*"         BEGIN(comment);



    <comment>[^*\n]*

    <comment>[^*\n]*\n      ++line_num;

    <comment>"*"+[^*/\n]*

    <comment>"*"+[^*/\n]*\n ++line_num;

    <comment>"*"+"/"        BEGIN(INITIAL);



Teraz zamiast sytuacji, gdzie nowa linia wymaga przetwarzania nastêpnej akcji, rozpoznawanie nowych linii jest "rozrzucone" na inne regu³y. Umo¿liwia to zachowanie jak najd³u¿szego dopasowania. Zauwa¿, ¿e dodawanie regu³ nie spowalnia skanera! Jego szybko¶æ jest niezale¿na od liczby regu³ i (w porównaniu do rozwa¿añ z pocz±tku sekcji) ich stopnia skomplikowania (z zastrze¿eniem do operatorów takich jak '*' i '|').

Ostateczny przyk³ad przyspieszania skanera: za³ó¿my, ¿e chcesz skanowaæ plik zawieraj±cy identyfikatory i s³owa kluczowe w liczbie jednego na liniê, bez ¿adnych obcych znaków i chcesz rozpoznawaæ wszystkie s³owa kluczowe. Naturalnym odruchem pocz±tkowym jest:




    %%

    asm      |

    auto     |

    break    |

    ... etc ...

    volatile |

    while    /* to jest s³owo kluczowe */



    .|\n     /* a to nie... */



Aby wyeliminowaæ ¶ledzenie wstecz, wprowad¼ regu³ê ³ap-wszystko:



    %%

    asm      |

    auto     |

    break    |

    ... etc ...

    volatile |

    while    /* to s³owo kluczowe */



    [a-z]+   |

    .|\n     /* a to nie... */



Obecnie, je¶li mamy zagwarantowane, ¿e mamy dok³adnie jedno s³owo w linii, mo¿emy zredukowaæ ca³kowit± liczbê dopasowañ o po³owê przez w³±czanie w rozpoznawanie tokenów ³apanie nowych linii.



    %%

    asm\n    |

    auto\n   |

    break\n  |

    ... etc ...

    volatile\n |

    while\n  /* to s³owo kluczowe */



    [a-z]+\n |

    .|\n     /* a to nie... */



Trzeba byæ tu ostro¿nym, gdy¿ w³a¶nie wprowadzili¶my do skanera cofanie. W szczególno¶ci, je¶li my wiemy, ¿e w wej¶ciu nie bêdzie nigdy znaków innych ni¿ litery i nowe linie, to flex nie mo¿e tego wiedzieæ i bêdzie planowa³ ewentualno¶æ cofania podczas skanowania tokenu w rodzaju "auto", po którym nie nast±pi nowa linia lub litera. W poprzednim wypadku nast±pi³oby po prostu dopasowanie regu³y "auto", lecz teraz nie ma "auto", ale "auto\n". Aby wyeliminowaæ mo¿liwo¶æ cofania, mo¿emy albo zduplikowaæ wszystkie regu³y bez koñcowych nowych linii albo, je¶li nie spodziewamy siê takiego wej¶cia i nie [interesuje nas] jego klasyfikacja, mo¿emy wprowadziæ regu³ê ³ap-wszystko, która nie zawiera nowej linii.



    %%

    asm\n    |

    auto\n   |

    break\n  |

    ... etc ...

    volatile\n |

    while\n  /* to s³owo kluczowe */



    [a-z]+\n |

    [a-z]+   |

    .|\n     /* a to nie... */



Po kompilacji z -Cf, jest to prawie tak szybkie, jak tylko mo¿liwe dla fleksa dla tego problemu.

Ostatnia uwaga: flex jest wolny przy dopasowywaniu NUL-ów, szczególnie je¶li token zawiera ich wiele. Najlepiej pisaæ regu³y, dopasowuj±ce krótkie fragmenty takich tekstów.

Kolejna ostatnia uwaga o wydajno¶ci: jak wspomniano wy¿ej w sekcji Jak Dopasowywane jest Wej¶cie, dynamiczne zmiany rozmiarów yytext do przyjmowania du¿ych tokenów jest powolne, gdy¿ obecnie wymaga by taki token by³ reskanowany od pocz±tku. Tak wiêc je¶li wydajno¶æ jest istotna, to powiniene¶ dopasowywaæ "du¿e" fragmenty tekstu, lecz nie "olbrzymie". Granic± miêdzy tymi pojêciami jest oko³o 8K znaków/token.

GENEROWANIE SKANERÓW C++

flex daje dwie drogi tworzenia skanerów przeznaczonych dla C++. Pierwsz± z nich jest proste skompilowanie fleksowego skanera kompilatorem C++ zamiast kompilatora C. Nie powiniene¶ napotkaæ ¿adnych b³êdów kompilacji (je¶li siê pojawi±, to zg³o¶ to pod adres wskazany ni¿ej, w sekcji o autorze). Mo¿esz wówczas w akcjach swoich regu³ u¿ywaæ kodu C++ zamiast C. Zauwa¿, ¿e domy¶lnym ¼ród³em dla skanera pozostaje yyin, a domy¶lnym echem jest wci±¿ yyout. Obydwa urz±dzenia s± zmiennymi FILE *, a nie strumieniami C++.

Mo¿na te¿ u¿yæ fleksa do generowania klasy skanera C++. S³u¿y do tego opcja -+ (lub, równowa¿nie %option c++), co jest przyjmowane automatycznie je¶li nazwa pliku wykonywalnego fleksa koñczy siê plusem, jak np. flex++. Przy u¿yciu tej opcji, flex generuje skaner do pliku lex.yy.cc zamiast lex.yy.c. Generowany skaner zawiera plik nag³ówkowy FlexLexer.h, który definiuje interfejsy do dwóch klas C++.

Pierwsza klasa, FlexLexer, daje abstrakcyjn± klasê bazow±, definiuj±c± ogólny interfejs klasy skanera. Daje nastêpuj±ce funkcje sk³adowe:

const char* YYText()
zwraca tekst ostatnio dopasowanego tokenu, równowa¿nik yytext.
int YYLeng()
zwraca d³ugo¶æ ostatnio dopasowanego tokenu, równowa¿nik yyleng.
int lineno() const
zwraca numer aktualnej linii wej¶ciowej (zobacz %option yylineno), lub 1 je¶li %option yylineno nie zosta³o u¿yte.
void set_debug( int flag )
ustawia flagê debugguj±c± dla skanera, równowa¿nik przypisania do yy_flex_debug (zobacz wy¿ej sekcjê o opcjach). Zauwa¿, ¿e aby w³±czaæ w skanerze informacje diagnostyczne, musisz skompilowaæ go z u¿yciem %option debug.
int debug() const
zwraca bie¿±ce ustawienie flagi debugguj±cej.

Udostêpniane s± te¿ funkcje sk³adowe równowa¿ne yy_switch_to_buffer(), yy_create_buffer() (chocia¿ pierwszym argumentem jest wska¼nik istream*, a nie FILE*), yy_flush_buffer(), yy_delete_buffer() i yyrestart() (i znowu, pierwszym argumentem jest wska¼nik istream*).

Kolejn± klas± zdefiniowan± w FlexLexer.h jest yyFlexLexer, który jest klas± pochodn± FlexLexer. Zaiwera nastêpuj±ce dodatkowe funkcje sk³adowe:

yyFlexLexer( istream* arg_yyin = 0, ostream* arg_yyout = 0 )
buduje obiekt yyFlexLexer stosuj±c podane strumienie jako wej¶cie i wyj¶cie. Je¶li nie zostan± podane, to strumienie bêd± odpowiada³y odpowiednio cin i cout.
virtual int yylex()
odgrywa tê sam± rolê co yylex() dla normalnych skanerów fleksa: skanuje strumieñ wej¶ciowy, konsumuje tokeny a¿ akcja regu³y nie zwróci warto¶ci. Je¶li z yyFlexLexer wyprowadzisz podklasê S i zechcesz dostaæ siê do funkcji i zmiennych sk³adowych S z wnêtrza yylex(), to musisz u¿yæ %option yyclass=S by poinformowaæ fleksa, ¿e bêdziesz u¿ywaæ podklasy zamiast yyFlexLexer. W tym wypadku zamiast generowaæ yyFlexLexer::yylex(), flex generuje S::yylex() (oraz generuje prosty yyFlexLexer::yylex(), który wo³a yyFlexLexer::LexerError() po wywo³aniu).
virtual void switch_streams(istream* new_in = 0,
ostream* new_out = 0) przypisuje yyin do new_in (je¶li jest nie-nil) oraz yyout do new_out (ditto), kasuj±c poprzedni bufor wej¶ciowy je¶li przypisywana jest nowa warto¶æ yyin .
int yylex( istream* new_in, ostream* new_out = 0 )
najpierw prze³±cza strumienie wej¶ciowe poprzez switch_streams( new_in, new_out ), a nastêpnie zwraca warto¶æ yylex().

Poza tym, yyFlexLexer definiuje nastêpuj±ce chronione (protected) funkcje wirtualne, które mo¿na przedefiniowaæ w klasach pochodnych, by dostosowaæ skaner:

virtual int LexerInput( char* buf, int max_size )
odczytuje maksymalnie max_size znaków do buf i zwraca liczbê odczytanych znaków. Aby wskazaæ koniec wej¶cia zwracane jest 0 znaków. Zauwa¿, ¿e skanery "interaktywne" (zobacz flagi -B oraz -I) definiuj± makro YY_INTERACTIVE. Je¶li redefiniujesz LexerInput() i potrzebujesz braæ ró¿ne akcje, zale¿nie od tego czy skaner skanuje ¼ród³o interaktywne czy nie, to mo¿esz sprawdzaæ obecno¶æ tej nazwy poprzez #ifdef.
virtual void LexerOutput( const char* buf, int size )
zapisuje size znaków z bufora buf który, o ile jest zakoñczony zerem, mo¿e zawieraæ te¿ "wewnêtrzne" zera je¶li regu³y skanera mog± ³apaæ tekst z wewnêtrznymi zerami.
virtual void LexerError( const char* msg )
zg³asza komunikat b³êdu krytycznego. Domy¶lna wersja tej funkcji zapisuje komunikat do strumienia cerr i koñczy dzia³anie programu.

Zauwa¿, ¿e obiekt yyFlexLexer zawiera swój pe³ny stan skanowania. Tak wiêc mo¿na u¿ywaæ takich obiektów do tworzenia wielobie¿nych (reentrant) skanerów. Mo¿esz u¿ywaæ wielu instancji tej samej klasy yyFlexLexer, jak równie¿ mo¿esz w jednym programie ³±czyæ wiele klas skanerów w ca³o¶æ, u¿ywaj±c opisanej wy¿ej opcji -P .

Dla skanerów C++ nie jest dostêpna w³a¶ciwo¶æ %array, trzeba wiêc u¿ywaæ %pointer (tj. warto¶ci domy¶lnej).

Oto przyk³ad prostego skanera C++:




        // Przyk³ad u¿ycia klasy skanera C++



    %{

    int mylineno = 0;

    %}



    string  \"[^\n"]+\"



    ws      [ \t]+



    alpha   [A-Za-z]

    dig     [0-9]

    name    ({alpha}|{dig}|\$)({alpha}|{dig}|[_.\-/$])*

    num1    [-+]?{dig}+\.?([eE][-+]?{dig}+)?

    num2    [-+]?{dig}*\.{dig}+([eE][-+]?{dig}+)?

    number  {num1}|{num2}



    %%



    {ws}    /* pomiñ spacje i tabulacje */



    "/*"    {

            int c;



            while((c = yyinput()) != 0)

                {

                if(c == '\n')

                    ++mylineno;



                else if(c == '*')

                    {

                    if((c = yyinput()) == '/')

                        break;

                    else

                        unput(c);

                    }

                }

            }



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



    \n        mylineno++;



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



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



    %%



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

        {

        FlexLexer* lexer = new yyFlexLexer;

        while(lexer->yylex() != 0)

            ;

        return 0;

        }

Je¶li chcesz tworzyæ wiele (ró¿nych) klas leksera, powiniene¶ u¿yæ flagi -P (lub opcji prefiks=) do zmiany nazwy ka¿dego yyFlexLexer na inny xxFlexLexer. Nastêpnie mo¿esz za³±czaæ <FlexLexer.h> do swoich innych ¼róde³, raz na klasê leksera, zmieniaj±c najpierw nazwê yyFlexLexer w nastêpuj±cy sposób:



    #undef yyFlexLexer

    #define yyFlexLexer xxFlexLexer

    #include <FlexLexer.h>



    #undef yyFlexLexer

    #define yyFlexLexer zzFlexLexer

    #include <FlexLexer.h>



o ile (na przyk³ad) u¿yjesz opcji %option prefix=xx dla jednego ze swoich skanerów, a %option prefix=zz dla drugiego.

WA¯NE: obecna postaæ klasy skanuj±cej jest eksperymentalna i mo¿e zmieniaæ siê miêdzy g³ównymi wydaniami.

NIEZGODNO¦CI Z LEX I POSIX

flex jest przeróbk± narzêdzia lex z AT&T Unix (jednak¿e obie te implementacje nie maj± wspólnego kodu). Posiada pewne rozszerzenia i niezgodno¶ci, które s± istotne dla tych, którzy chc± pisaæ skanery dzia³aj±ce z oboma. Flex jest w pe³ni zgodny ze specyfikacj± POSIX lex poza szczegó³em, ¿e gdy u¿ywa %pointer (domy¶lne), to wywo³anie unput() niszczy zawarto¶æ yytext, co jest niezgodne ze specyfikacj± POSIX.

W sekcji tej omówimy wszystkie znane obszary niezgodno¶ci fleksa z AT&T lex i specyfikacj± POSIX.

fleksowa opcja -l w³±cza maksymaln± zgodno¶æ z oryginalnym AT&T lex, okupuj±c to jednak znacznymi stratami wydajno¶ci generowanego skanera. Ni¿ej zaznaczymy, które niezgodno¶ci mo¿na pokonaæ u¿ywaj±c opcji -l.

flex jest w pe³ni zgodny z leksem poza nastêpuj±cymi wyj±tkami:

-
Nieudokumentowana zmienna wewnêtrzna skanera lex o nazwie yylineno nie jest obs³ugiwana bez -l lub %option yylineno.
yylineno powinno byæ obs³ugiwane na poziomie buforowym, a nie na skanerowym (pojedyncza zmienna globalna).
yylineno nie jest czê¶ci± specyfikacji POSIX.
-
Procedura input() nie jest redefiniowalna chocia¿ mo¿e byæ wo³ana do czytania znaków nastêpuj±cym po tym, co dopasowano do regu³y. Je¶li input() napotka koniec pliku, to wykonywane jest normalne przetwarzanie yywrap(). ``Prawdziwy'' koniec pliku jest sygnalizowany przez input() zwróceniem warto¶ci EOF.
Wej¶cie jest natomiast sterowane przez definiowanie makra YY_INPUT.
Ograniczenie fleksa, ¿e input() nie mo¿e byæ redefiniowany jest zgodne ze specyfikacj± POSIX, która po prostu nie okre¶la innego ¿adnego sposobu sterowania wej¶ciem skanera ni¿ poprzez dokonanie pocz±tkowego przypisania do yyin.
-
Procedura unput() nie jest redefiniowalna. Ograniczenie to jest zgodne z POSIX.
-
Skanery fleksa nie s± tak wielobie¿ne (reentrant) jak skanery lex. W szczególno¶ci, je¶li masz interaktywny skaner i obs³ugê przerwañ, która robi d³ugi skok ze skanera, a skaner jest nastêpnie wo³any ponownie, to mo¿esz uzyskaæ nastêpuj±cy komunikat:



    fatal flex scanner internal error--end of buffer missed



Aby wej¶æ na nowo do skanera, u¿yj najpierw



    yyrestart( yyin );



Zauwa¿, ¿e wywo³anie to wyrzuci wszelkie buforowane wej¶cie; zwykle jednak nie jest to problem przy skanerach interaktywnych.
Zauwa¿ te¿, ¿e klasy skanerów C++ wielobie¿ne (reentrant), wiêc u¿ywaj±c opcji C++ powiniene¶ ich u¿ywaæ. Zobacz sekcjê o generowaniu skanerów C++.
-
output() nie jest obs³ugiwany. Wyj¶cie makra ECHO jest wykonywane do wska¼nika plikowego yyout (domy¶lnie stdout).
output() nie jest czê¶ci± specyfikacji POSIX.
-
lex nie obs³uguje wykluczaj±cych warunków pocz±tkowych (%x), choæ znajduj± siê one w specyfikacji POSIX.
-
Przy rozwijaniu definicji, flex ujmuje je w nawiasy. W leksie, nastêpuj±ce:



    NAME    [A-Z][A-Z0-9]*

    %%

    foo{NAME}?      printf( "Znalaz³em\n" );

    %%



nie dopasuje siê do ³añcucha "foo", gdy¿ makro jest rozwijane tak, ¿e regu³a odpowiada "foo[A-Z][A-Z0-9]*?", a pierwszeñstwo jest takie, ¿e '?' jest wi±zany z "[A-Z0-9]*". We fleksie regu³a zosta³aby rozwiniêta do "foo([A-Z][A-Z0-9]*)?" i ³añcuch "foo" zosta³by dopasowany.
Zauwa¿, ¿e je¶li definicja rozpoczyna siê od ^ lub koñczy siê na $ to nie jest rozwijana w nawiasach, aby umo¿liwiæ tym operatorom pojawienie siê w definicjach bez utraty ich znaczenia. Ale operatory <s>, / i <<EOF>> nie mog± byæ u¿ywane w definicji fleksa.
U¿ywanie -l skutkuje leksowym zachowaniem braku nawiasów wokó³ definicji.
POSIX nakazuje ujmowanie definicji w nawiasy.
-
Niektóre implementacje leksa umo¿liwiaj± rozpoczynanie akcji regu³ w osobnej linii je¶li wzorzec regu³y ma doklejon± bia³± spacjê:



    %%

    foo|bar<tu spacja>

      { foobar_action(); }



flex nie obs³uguje tej w³a¶ciwo¶ci.
-
Leksowe %r (generuj skaner Ratfor) nie jest obs³ugiwane. Nie jest czê¶ci± specyfikacji POSIX.
-
Po wywo³aniu unput(), yytext jest niezdefiniowane a¿ do dopasowania nastêpnego tokenu, chyba ¿e skaner u¿ywa %array. Inaczej ma siê sprawa z leksem lub specyfikacj± POSIX. Opcja -l za³atwia tê niezgodno¶æ.
-
Pierwszeñstwo operatora {} (zakresu numerycznego) jest inne. lex interpretuje "abc{1,3}" jako "dopasuj 1, 2 lub 3 pojawienia 'abc'", a flex interpretuje to jako "dopasuj 'ab' z doklejonym jednym, dwoma lub trzema znakami 'c'". Interpretacja fleksowa jest zgodna ze specyfikacj± POSIX.
-
Pierwszeñstwo operatora ^ jest inne. lex interpretuje "^foo|bar" jako "dopasuj albo 'foo' z pocz±tku linii albo 'bar' gdziekolwiek", podczas gdy flex rozumie to jako "dopasuj 'foo' lub 'bar' je¶li pojawi± siê na pocz±tku linii". To drugie jest zgodne ze specyfikacj± POSIX.
-
Specjalne deklaracje rozmiaru-tablicy, takie jak %a, obs³ugiwane przez lex nie s± wymagane przez skanery fleksa; flex je ignoruje.
-
Nazwa FLEX_SCANNER jest #definiowana, wiêc skanery mog± byæ pisane z przeznaczeniem do u¿ycia z fleksem lub leksem. Skanery zawieraj± równie¿ YY_FLEX_MAJOR_VERSION i YY_FLEX_MINOR_VERSION wskazuj±c na wersjê fleksa, która wygenerowa³a skaner (na przyk³ad dla wydania 2.5 definiowane s± odpowiednio liczby 2 i 5).

Nastêpuj±ce w³a¶ciwo¶ci fleksa nie s± zawarte w specyfikacjach lex ani POSIX:




    Skanery C++

    %option

    zakresy warunków pocz±tkowych

    stosy warunków pocz±tkowych

    skanery interaktywne/nieinteraktywne

    yy_scan_string() i koledzy

    yyterminate()

    yy_set_interactive()

    yy_set_bol()

    YY_AT_BOL()

    <<EOF>>

    <*>

    YY_DECL

    YY_START

    YY_USER_ACTION

    YY_USER_INIT

    dyrektywy #line

    %{} wokó³ akcji

    wiele akcji w linii



plus prawie wszystkie flagi fleksa. Ostatnia w³a¶ciwo¶æ listy odnosi siê do faktu, ¿e we fleksie mo¿na wstawiaæ wiele akcji do jednej linii, rozdzielaj±c je ¶rednikami, podczas gdy w leksie, nastêpuj±ca instrukcja



    foo    handle_foo(); ++num_foos_seen;



jest (raczej niespodziewanie) obcinana do



    foo    handle_foo();



flex nie obcina akcji. Akcje które nie s± objête klamrami koñcz± siê zwyczajnie na koñcu linii.

DIAGNOSTYKA

warning, rule cannot be matched (ostrze¿enie, regu³a nie mo¿e byæ dopasowana) wskazuje, ¿e podana regu³a nie mo¿e byæ dopasowana gdy¿ wystêpuje za innymi regu³ami, które zawsze dopasuj± jej tekst. Na przyk³ad nastêpuj±ce foo nie mo¿e byæ dopasowane, gdy¿ pojawia siê po regule ³ap-wszystko:




    [a-z]+    got_identifier();

    foo       got_foo();



U¿ycie w skanerze REJECT powstrzyma to ostrze¿enie.

warning, -s option given but default rule can be matched (ostrze¿enie, podano opcjê -s, lecz dopasowana mo¿e byæ regu³a domy¶lna) oznacza, ¿e mo¿liwe jest (przypuszczalnie tylko w konkretnym warunku pocz±tkowym), ¿e regu³a domy¶lna (dopasowania dowolnego znaku) jest jedyn±, która dopasuje siê do konkretnego wej¶cia. Poniewa¿ podano -s, zak³ada siê, ¿e nie jest to celowe.

reject_used_but_not_detected undefined lub yymore_used_but_not_detected undefined (niezdefiniowana fraza pierwsza lub druga) - te b³êdy pojawiaj± siê podczas kompilacji. Wskazuj± one, ¿e skaner u¿ywa REJECT lub yymore(), lecz flex nie poinformowa³ o tym fakcie. Znaczy to, ¿e flex przeskanowa³ pierwsze dwie sekcji w poszukiwaniu pojawienia siê tych akcji, ale ich nie znalaz³, bo jako¶ je przemyci³e¶ (np. przez plik #include). U¿yj %option reject lub %option yymore do wskazania fleksowi, ¿e naprawdê u¿ywasz tych w³a¶ciwo¶ci.

flex scanner jammed - skaner skompilowany z -s napotka³ ³añcuch wej¶ciowy, który nie zosta³ dopasowany do ¿adnej z jego regu³. B³±d ten mo¿e siê pojawiæ te¿ z powodu problemów wewnêtrznych.

token too large, exceeds YYLMAX (token zbyt du¿y, przekracza YYLMAX) - twój skaner u¿ywa %array a jedna z jego regu³ dopasowa³a siê do ³añcucha d³u¿szego ni¿ sta³a YYLMAX (domy¶lnie 8K). Mo¿esz zwiêkszyæ tê warto¶æ zwiêkszaj±c #definicjê sta³ej YYLMAX w sekcji definicji swojego wej¶cia fleksa.

scanner requires -8 flag to use the character 'x' (skaner wymaga flagi -8 do u¿ywania znaku 'x') - specyfikacja twojego skanera zawiera rozpoznawanie znaku 8-bitowego 'x', a nie podana zosta³a flaga -8, w wyniku czego skaner u¿y³ 7-bit z powodu wykorzystania opcji kompresji tablic -Cf lub -CF. Dla szczegó³ów zobacz dyskusjê flagi -7.

flex scanner push-back overflow - u¿y³e¶ unput() do wepchniêcia z powrotem tak d³ugiego tekstu, ¿e bufor skanera nie potrafi³ przetrzymaæ wepchniêtego tekstu i bie¿±cego tokena w yytext. Idealny skaner powinien dynamicznie zmieniæ rozmiar bufora, lecz obecnie tak siê nie dzieje.

input buffer overflow, can't enlarge buffer because scanner uses REJECT (przekroczenie bufora wej¶ciowego nie mo¿e powiêkszyæ bufora gdy¿ skaner u¿ywa REJECT) - skaner pracowa³ nad dopasowaniem bardzo du¿ego tokenu i potrzebowa³ rozszerzyæ bufor wej¶ciowy. Nie dzia³a to ze skanerami, u¿ywaj±cymi REJECT.

fatal flex scanner internal error--end of buffer missed (krytyczny b³±d wewnêtrzny skanera flex -- rozminiêto siê z koñcem bufora) - Mo¿e siê to pojawiæ w skanerze, który jest uruchomiony po d³ugim skoku z ramki aktywacji skanera. Przed powrotem do skanera u¿yj:




    yyrestart( yyin );



albo, jak wspomniano wy¿ej, prze³±cz siê na u¿ywanie skanerów C++.

too many start conditions in <> construct! (zbyt wiele warunków pocz±tkowych w konstrukcji <>) - w konstrukcji <> pojawi³o siê wiêcej warunków pocz±tkowych ni¿ istnieje w rzeczywisto¶ci (wiêc przynajmniej jeden z nich pojawi³ siê dwukrotnie).

PLIKI

-lfl
biblioteka, z któr± musz± byæ ³±czone skanery.
lex.yy.c
generowany skaner (nazywany na niektórych systemach lexyy.c).
lex.yy.cc
generowana klasa skanera C++, po u¿yciu -+.
<FlexLexer.h>
plik nag³ówkowy definiuj±cy klasê bazow± skanera C++, FlexLexer i klasê pochodn±, yyFlexLexer.
flex.skl
skaner szkieletowy. Plik ten jest u¿ywany tylko przy budowaniu fleksa, nie przy jego uruchamianiu.
lex.backup
informacje wspieraj±ce (backing-up) dla flagi -b (nazywany jest mianem lex.bck na niektórych systemach).

NIEDOSTATKI / B£ÊDY

Niektóre wzorce wisz±cego kontekstu nie mog± byæ poprawnie dopasowane i generuj± komunikaty ostrzegawcze ("dangerous trailing context") (niebezpieczny wisz±cy kontekst). S± to wzorce, gdzie zakoñczenie pierwszej czê¶ci regu³y dopasowuje siê do pocz±tku drugiej czê¶ci, takie jak "zx*/xy*", gdzie 'x*' dopasowuje 'x' na pocz±tku wisz±cego kontekstu. (Zauwa¿, ¿e projekt POSIX-a okre¶la, ¿e dopasowany w takich wzorcach tekst jest niezdefiniowany.)

Dla niektórych regu³ wisz±cego kontekstu, czê¶ci które s± w rzeczywisto¶ci okre¶lonej d³ugo¶ci nie s± tak rozpoznawane. Prowadzi to do wspomnianej wy¿ej straty wydajno¶ci. W szczególno¶ci, czê¶ci u¿ywaj±ce '|' lub {n} (takie jak "foo{3}") zawsze s± uwa¿ane za zmienno-d³ugo¶ciowe.

£±czenie wisz±cego kontekstu z akcj± specjaln± '|' mo¿e spowodowaæ, ¿e ustalony (fixed) wisz±cy kontekst zostanie zmieniony w bardziej kosztowny, zmienny wisz±cy kontekst. Na przyk³ad nastêpuj±ce:




    %%

    abc      |

    xyz/def



U¿ywanie unput() uszkadza yytext i yyleng, chyba ¿e u¿yto dyrektywy %array lub opcji -l.

Dopasowywanie wzorców NUL-i jest znacznie wolniejsze ni¿ dopasowywanie innych znaków.

Dynamiczne zmiany rozmiaru bufora s± wolne i wymagaj± reskanowania ca³ego tekstu dopasowanego dot±d przez bie¿±cy (zwykle du¿y) token.

Z powodu buforowania wej¶cia i czytania z wyprzedzeniem, nie mo¿na ³±czyæ z regu³ami fleksa wywo³añ <stdio.h>, np. getchar(). Zamiast tego wo³aj input().

Wpisy ca³ej tablicy (total table entries) wymieniane przez flagê -v nie zawieraj± niektórych wpisów, potrzebnych do okre¶lania, która regu³a zosta³a dopasowana. Liczba wpisów je¶li skaner nie u¿ywa REJECT jest równa liczbie stanów DFA, a w przeciwnym wypadku jest trochê wiêksza.

REJECT nie mo¿e byæ u¿ywany z opcjami -f lub -F.

Wewnêtrzne algorytmy fleksa wymagaj± udokumentowania.

ZOBACZ TAK¯E

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

John Levine, Tony Mason, and Doug Brown, Lex & Yacc, O'Reilly and Associates. Upewnij siê, ¿e bierzesz 2-gie wydanie.

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). Opisuje techniki dopasowywania wzorców u¿ywane przez fleksa (deterministyczne automaty skoñczone).

AUTOR

Vern Paxson, z pomoc± wielu pomys³ów i inspiracji od Vana Jacobsona. Oryginaln± wersjê napisa³ Jef Poskanzer. Reprezentacja szybkiej tablicy jest czê¶ciow± implementacj± projektu Vana Jacobsona. Implementacja zosta³a wykonana przez Kevina Gonga and Verna Paxsona.

Podziêkowania dla wielu beta testerów, komentatorów i kontrybutorów fleksa, z których szczególnie zas³u¿one s± nastêpuj±ce osoby: 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, oraz ci, których nazwiska wylecia³y z moich zdolno¶ci archiwizowania poczty, lecz których wk³ad jest równie wa¿ny.

Keith Bostic, Jon Forrest, Noah Friedman, John Gilmore, Craig Leres, John Levine, Bob Mulcahy, G.T. Nicol, Francois Pinard, Rich Salz i Richard Stallman pomogli z ró¿nymi problemami dystrybucji.

Esmond Pitt and Earle Horton pomóg³ z wsparciem 8-bit; Benson Margulies i Fred Burke pomogli z wsparciem C++; Kent Williams i Tom Epperly pomogli z wsparciem klas C++; Ove Ewerlid pomóg³ z wsparciem NUL-ów; Eric Hughes pomóg³ z wielokrotnymi buforami.

Praca ta by³a pocz±tkowo wykonywana gdy by³em z Real Time Systems Group w Lawrence Berkeley Laboratory w Berkeley, CA. Wielkie dziêki do wszystkich za wsparcie, które uzyska³em.

Komentarze ¶lij do vern@ee.lbl.gov.