Provided by: manpages-pl_20060617-1_all bug

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 skanerw:  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ą  reguami.   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 tokenw 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  warunkw
       pocztkowych, 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ś  wcity
       (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  wyraenia.   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]" bdzie 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  regua
       domylna:  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 wskanik 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 odgazieniem; 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
              pocztek 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  wczajce  warunki
       początkowe,  a  druga wykluczajce.  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 wczajcy , to reguły bez warunków  początkowych  będą
       również  aktywne.   Jeśli  jest  wykluczajcy, 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 wczajcy (%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 pominite). 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 nastpny
              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 moesz 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 rwnowanoci, 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ć pene 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-rwnowanoci,  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 kadej 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
       krtkie 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 peny 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++ s 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.