Provided by: manpages-pl_20060617-2_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 '-', ']' oraz '^' na początku 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 '/' lub '$'). Wzorce warunku  początkowego  '^'  oraz
              "<<EOF>>" mogą pojawić 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.