Provided by: manpages-pl_0.7-1_all bug

NAZWA

       flex - szybki generator analizatora leksykalnego

SKŁADNIA

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

WPROWADZENIE

        Uwaga! To tłumaczenie może być nieaktualne!

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

           Opis
            krótki przegląd możliwości narzędzia

           Proste Przykłady

           Format Pliku Wejściowego

           Wzorce
            rozszerzone wyrażenia regularne używane przez flex

           Sposób Dopasowywania Wejścia
            reguły określania, co dopasowano

           Akcje
            jak podawać, co robić po dopasowaniu wzorca

           Generowany Skaner
            szczegóły o skanerze, tworzonym przez fleksa; jak kontrolować źródło
            wejściowe

           Warunki Startowe
               wprowadzanie do skanerów kontekstu i obsługa "mini-skanerów"

           Wielokrotne Bufory Wejściowe
            jak obsługiwać wiele źródeł wejściowych; jak skanować z łańcuchów
            zamiast z plików

           Reguły Końca Pliku
            specjalne reguły dopasowywane do końca wejścia

           Różne Makra
            ogół makr dostępnych z poziomu akcji

           Wartości Dostępne Użytkownikowi
            ogół wartości dostępnych z poziomu akcji

           Łączenie z Yacc
            łączenie skanerów flex z analizatorami yacc

           Opcje
            opcje linii poleceń fleksa i dyrektywa "%option"

           Kwestie wydajnościowe
            jak przyspieszać skanery

           Generowanie Skanerów C++
            eksperymentalna właściwość generowania klas skanerów C++

           Niezgodności z Lex i POSIX
            czym flex różni się od standardów AT&T lex i POSIX lex

           Diagnostyka
            objaśnienie komunikatów o błędach, generowanych przez flex (lub
            skanery)

           Pliki
            pliki używane przez flex

           Niedostatki / Błędy
            znane problemy fleksa

           Zobacz Także
            pozostała dokumentacja i związane z fleksem narzędzia

           Autor
            informacja kontaktu z autorem

OPIS

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

PROSTE PRZYKŁADY

       Przedstawmy  teraz  trochę prostych przykładów aby obyć się z używaniem flex.  Następujący
       plik wejściowy flex określa skaner, który za każdym razem gdy napotka łańcuch  "username",
       podmieni go nazwą użytkownika:

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

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

       Oto kolejny prosty przykład:

                   int num_lines = 0, num_chars = 0;

           %%
           \n      ++num_lines; ++num_chars;
           .       ++num_chars;

           %%
           main()
                   {
                   yylex();
                   printf( "# of lines = %d, # of chars = %d\n",
                           num_lines, num_chars );
                   }

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

       A oto trochę bardziej skomplikowany przykład:

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

           %{
           /* potrzebujemy tego do wywołania atof() */
           #include <math.h>
           %}

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

           %%

           {DIGIT}+    {
                       printf( "Liczba całkowita: %s (%d)\n", yytext,
                               atoi( yytext ) );
                       }

           {DIGIT}+"."{DIGIT}*        {
                       printf( "Liczba zmiennoprzecinkowa: %s (%g)\n", yytext,
                               atof( yytext ) );
                       }

           if|then|begin|end|procedure|function        {
                       printf( "Słowo kluczowe: %s\n", yytext );
                       }

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

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

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

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

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

           %%

           main( argc, argv )
           int argc;
           char **argv;
               {
               ++argv, --argc;  /* pomiń nazwę programu */
               if ( argc > 0 )
                       yyin = fopen( argv[0], "r" );
               else
                       yyin = stdin;

               yylex();
               }

       Są  to  początki  prostego skanera dla języka podobnego do Pascala. Rozróżnia poszczególne
       rodzaje tokenów i informuje co zobaczył.

       Szczegóły tego przykładu zostaną wyjaśnione w następnych sekcjach.

FORMAT PLIKU WEJŚCIOWEGO

       Plik wejściowy fleksa składa się z trzech sekcji, rozdzielanych liniami z łańcuchem %%:

           definicje
           %%
           reguły
           %%
           kod użytkownika

       Sekcja definicji zawiera definicje prostych  nazw,  upraszczających  później  specyfikację
       skanera. Zawiera też deklaracje warunków początkowych, które objaśniono w dalszej sekcji.

       Definicje nazw mają postać:

           nazwa definicja

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

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

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

           {DIGIT}+"."{DIGIT}*

       jest równoważne

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

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

       Sekcja reguł wejścia fleksa zawiera szereg reguł w postaci:

           wzorzec   akcja

       Przed wzorcem nie może wystąpić wcięcie, a akcja musi rozpoczynać się w tej samej linii.

       Dla dalszego opisu akcji patrz dalej.

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

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

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

       W sekcji definicji na wyjście kopiowane są  również  nie-wcięte  bloki  komentarza,  ujęte
       między znaki "/*" i "*/".

WZORCE

       Wzorce wejściowe są pisane z użyciem rozszerzonego zestawu wyrażeń regularnych. Są to:

           x          dopasowuje znak 'x'
           .          dowolny znak poza nową linią
           [xyz]      "klasa znaków"; w tym przypadku wzorzec odpowiada
                        zarówno 'x', 'y' jak i 'z'
           [abj-oZ]   "klasa znaków" z zakresem; odpowiada ona
                        'a', 'b', dowolnej literze od 'j' do 'o' oraz 'Z'
           [^A-Z]     zanegowana "klasa znaków" tj. dowolny znak poza
                        wymienionymi w klasie. W tym wypadku dowolny znak oprócz
                  dużych liter
           [^A-Z\n]  dowolny znak oprócz dużych liter lub nowej linii
           r*         zero lub więcej r'ów, gdzie r jest wyrażeniem regularnym
           r+         jeden lub więcej r'ów
           r?         zero lub jeden r (tj. "opcjonalny r")
           r{2,5}     od dwu do pięciu r
           r{2,}      dwa lub więcej r
           r{4}       dokładnie 4 r
           {nazwa}    rozwinięcie definicji "nazwa" (patrz wyżej)
           "[xyz]\"foo"
                      łańcuch literalny: [xyz]"foo
           \X        Jeśli X to 'a', 'b', 'f', 'n', 'r', 't' lub 'v',
                  to następuje interpretacja ANSI-C \x. W przeciwnym
                  wypadku używany jest literalny 'X' (używane do cytowania
                  operatorów--np. '*').
           \0        znak NUL (kod ASCII 0)
           \123      znak o wartości ósemkowej 123
           \x2a      znak o wartości szesnastkowej 2a
           (r)        dopasuj r; nawiasy są używane do przeciążania priorytetów
                     (patrz niżej)

           rs         wyrażenie regularne r, za którym następuje wyrażenie
                  regularne s; nazywa się to "łączeniem"

           r|s        r lub s

           r/s        r, lecz tylko jeśli za nim następuje s. Tekst dopasowywany
                  przez s jest załączany do określania czy ta reguła miała
                  "najdłuższe dopasowanie", lecz potem jest zwracany do
                  wejścia przed wykonaniem akcji. Tak więc akcja widzi tylko
                  tekst dopasowany przez r. Ten rodzaj wzorca jest nazywany
                  "doklejonym kontekstem". (Istnieją pewne kombinacje r/s,
                  których flex nie potrafi właściwie dopasować; zobacz uwagi
                  w dalszej sekcji Niedostatki / Błędy w okolicach
                  "niebezpiecznego kontekstu doklejonego".)
           ^r         r, lecz tylko na początku linii (tj. zaraz po rozpoczęciu
                  skanowania, lub po wyskanowaniu nowej linii).
           r$         r, lecz tylko na końcu linii (tj. tuż przed nową linią).
                  Równoważne "r/\n".

                   Zauważ, że notacja nowej linii fleksa jest dokładnie tym,
                   co było używane jako '\n' przez kompilator C, użyty do
                   kompilacji fleksa; w praktyce na niektórych systemach DOS
                   musisz wyfiltrować \r lub jawnie używać r/\r\n zamiast
                   "r$".

           <s>r       r, lecz tylko dla warunku początkowego s (zobacz niżej
                  dyskusję o warunkach początkowych)
           <s1,s2,s3>r
                      to samo, lecz jeśli dowolny z warunków początkowych s1,
                        s2 lub s3
           <*>r       r w dowolnym warunku początkowym, nawet wykluczającym

           <<EOF>>    koniec pliku
           <s1,s2><<EOF>>
                      koniec pliku w warunkach początkowych s1 lub s2

       Zauważ,  że  w  obrębie  klasy  znaków wszystkie operatory wyrażeń regularnych tracą swoje
       znaczenie specjalne (nie licząc cytowania '\', znaków klasy '-', ']' 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  wyrażenia.
       Wyrażenia  te  są  ujmowane  w  ograniczniki  [:  i :] (które muszą dodatkowo pojawiać się
       wewnątrz '[' i ']' klasy znaków; inne elementy w klasie  znaków  też  mogą  się  pojawić).
       Prawidłowymi wyrażeniami są:

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

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

       Na przykład następujące klasy są sobie równoważne:

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

       Jeśli twój skaner jest niewrażliwy na wielkość znaków (flaga (flaga -i),  to  [:upper:]  i
       [:lower:] są równoważne [:alpha:].

       Trochę uwag o wzorcach:

       -      Zanegowana  klasa  znaków,  taka  jak  wyżej wymienione przykładowe "[^A-Z]" będzie
              pasować do nowej linii, chyba że "\n" (lub  równoważna  sekwencja  specjalna)  jest
              jednym  z  jawnie  obecnych w klasie znaków (np. "[^A-Z\n]"). Odbiega to od sposobu
              traktowania zanegowanych klas znaków przez inne narzędzia operujące na  wyrażeniach
              regularnych,    lecz    niestety   niespójność   jest   ugruntowana   historycznie.
              Dopasowywanie nowej linii oznacza, że wzorzec w rodzaju [^"]* może dopasować się do
              całego wejścia, chyba że istnieje w nim drugi cudzysłów.

       -      Reguła  może mieć najwyżej jedną instancję dowiązanego kontekstu (operatory '/' 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  reguła  domyślna:  następny
       znak  wejścia  jest  uważany  za  dopasowany i kopiowany na stdout.  Tak więc najprostszym
       poprawnym plikiem wejściowym fleksa jest:

           %%

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

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

       Zaletą  %array  jest  możliwość  modyfikowania yytext i to, że wołanie unput() nie niszczy
       yytext.  Poza tym, istniejące programy lex czasami zewnętrznie zaglądają  do  yytext  przy
       użyciu deklaracji w postaci:
           extern char yytext[];
       Definicja ta jest błędna przy użyciu z %pointer, lecz prawidłowa dla %array.

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

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

AKCJE

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

           %%
           "wytnij mnie"

       (Wszystkie  pozostałe  znaki  wejścia  zostaną skopiowane na wyjście, gdyż dopasują się do
       reguły domyślnej.)

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

           %%
           [ \t]+        putchar( ' ' );
           [ \t]+$       /* ignoruj ten token */

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

       Akcja  składająca  się  wyłącznie  z  pionowej  kreski ('|') oznacza "taka sama, jak akcja
       następnej reguły". Dla zobrazowania patrz niżej.

       Akcje mogą zawierać kod C, włączając w to instrukcje  return,  przeznaczone  do  zwracania
       wartości  do  procedury, która wywołała yylex().  Przy każdym wywołaniu yylex() kontynuuje
       przetwarzanie tokenów od miejsca, w którym ostatnio przerwał aż do osiągnięcia końca pliku
       lub wywołania return.

       Akcje  mogą  spokojnie modyfikować zmienną yytext; nie mogą jej jednak wydłużać (dodawanie
       znaków do  jej  końca  nadpisze  dalsze  znaki  strumienia  wejściowego).  Odmiennie  jest
       natomiast  przy  używaniu %array (patrz wyżej); wtedy yytext można spokojnie modyfikować w
       dowolny sposób.

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

       Istnieje wiele dyrektyw specjalnych, które można zawrzeć w akcji:

       -      ECHO kopiuje wejście yytext na wyjście skanera.

       -      BEGIN z doklejoną nazwą warunku początkowego umieszcza skaner w odpowiednim warunku
              początkowym (patrz niżej).

       -      REJECT Kieruje skaner na działanie w "drugiej  najlepszej"  regule,  która  została
              dopasowana  do  wzorca  wejściowego  (lub  prefiksu wejścia). Reguła jest wybierana
              według zasad opisanych  w  "Jak  dopasowywane  jest  wejście",  po  czym  następuje
              odpowiednie  ustawienie  yytext  oraz  yyleng.   Może  to być albo ta reguła, która
              dopasowała się do takiej  samej  ilości  tekstu,  jak  poprzednia,  lecz  wystąpiła
              później  w  pliku  wejściowym  fleksa, albo taka, która dopasowała się do mniejszej
              ilości tekstu.  Na przykład, następujący przykład będzie liczył słowa  wejściowe  i
              wołał funkcję special() dla każdego "frob":

                          int word_count = 0;
                  %%

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

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

                  %%
                  a        |
                  ab       |
                  abc      |
                  abcd     ECHO; REJECT;
                  .|\n     /* zjedz nietrafione znaki */

              (Pierwsze trzy reguły mają wspólną akcję z czwartą, gdyż używają  akcji  specjalnej
              '|'.)   REJECT  jest  dość kosztowną właściwością jeśli chodzi o wydajność skanera;
              jeśli jest używane w którejś z akcji skanera,  to  spowolni  wszystkie  dopasowania
              skanera. Co więcej, REJECT nie może być używany z opcjami -Cf i -CF (zobacz niżej).

              Zauważ  też,  że,  w  przeciwieństwie  do  innych  akcji  specjalnych,  REJECT jest
              odgałęzieniem; kod akcji występujący bezpośrednio po nim nie zostanie wykonany.

       -      yymore() mówi skanerowi, że przy następnym dopasowaniu reguły, odpowiadający  token
              powinien  być  doklejony  do  bieżącej  wartości yytext.  Na przykład, przy wejściu
              "mega-kludge", poniższy przykład na wyjściu wypisze "mega-mega-kludge":

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

              Pierwsze "mega-" jest dopasowane i wydrukowane  na  wyjście.  Następnie  dopasowane
              jest  "kludge",  lecz  poprzednie  "mega-"  wciąż znajduje się na początku yytext i
              komenda ECHO dla "kludge" wydrukuje w rzeczywistości "mega-kludge".

       Dwie  uwagi  na  temat  yymore().   Po  pierwsze,  yymore()  zależy  od  wartości  yyleng,
       odzwierciedlającej  rozmiar  bieżącego tokenu. Zatem jeśli używasz yymore(), nie modyfikuj
       tej zmiennej.  Po drugie, obecność yymore() w akcji skanera wpływa  na  pewne  pogorszenie
       wydajności w szybkości dokonywania przez skaner dopasowań.

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

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

              Podanie  yyless  argumentu  zerowego powoduje reskanowanie całego obecnego łańcucha
              wejściowego. O ile  nie  zmienisz  sposobu  kolejnego  przetwarzania  przez  skaner
              wejścia (przy użyciu np.  BEGIN), spowoduje to nieskończoną pętlę.

       Zwróć  uwagę, że yyless jest makrem i może być używane tylko z pliku wejściowego fleksa, a
       nie z innych plików źródłowych.

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

                  {
                  int i;
                  /* Kopiuj yytext, gdyż unput() niszczy jego zawartość */
                  char *yycopy = strdup( yytext );
                  unput( ')' );
                  for ( i = yyleng - 1; i >= 0; --i )
                      unput( yycopy[i] );
                  unput( '(' );
                  free( yycopy );
                  }

              Zwróć uwagę, że skoro każdy unput() wstawia dany znak na  początek  strumienia,  to
              wstawianie znaków musi odbywać się tyłem-na-przód.

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

       Na  koniec,  zauważ  też,  że  nie  możesz  wstawiać  tak znaków EOF.  Nie można tą metodą
       zaznaczać końca pliku w strumieniu.

       -      input() odczytuje następny znak ze strumienia wejściowego.  Na  przykład,  poniższe
              jest jednym ze sposobów pożerania komentarzy języka C:

                  %%
                  "/*"        {
                              register int c;

                              for ( ; ; )
                                  {
                                  while ( (c = input()) != '*' &&
                                          c != EOF )
                                      ;    /* zeżryj tekst komentarza */

                                  if ( c == '*' )
                                      {
                                      while ( (c = input()) == '*' )
                                          ;
                                      if ( c == '/' )
                                          break;    /* znalazłem koniec */
                                      }

                                  if ( c == EOF )
                                      {
                                      error( "EOF w komentarzu" );
                                      break;
                                      }
                                  }
                              }

              (Zauważ,  że  jeśli  skaner  jest skompilowany z użyciem C++, to input() nazywa się
              yyinput().  Jest tak w celu zapobieżenia zderzeniu nazwy ze strumieniem C++ poprzez
              nazwę input.)

       -      YY_FLUSH_BUFFER wypróżnia wewnętrzny bufor skanera. Przy następnym razie gdy skaner
              będzie dopasowywał się do tokenu, najpierw napełni na nowo bufor z użyciem YY_INPUT
              (zobacz  niżej  Generowany Skaner).  Akcja ta jest szczególnym przypadkiem bardziej
              ogólnej funkcji yy_flush_buffer(),  opisanej  niżej  w  sekcji  Wielokrotne  Bufory
              Wejściowe.

       -      yyterminate()  może  być  używane zamiast instrukcji return akcji. Kończy działanie
              skanera i zwraca 0 do  wywołującego  skaner,  wskazując,  że  "wszystko  zrobione".
              Domyślnie, yyterminate() jest wywoływane również po napotkaniu końca pliku. Jest to
              makro i może być redefiniowane.

GENEROWANY SKANER

       Wynikiem działania fleksa jest plik lex.yy.c, zawierający procedurę skanującą yylex() oraz
       zestaw  tablic,  używanych  przez  niego  do dopasowywania tokenów i parę procedur i makr.
       Domyślnie yylex() jest deklarowany jako

           int yylex()
               {
               ... tu różne definicje i akcje ...
               }

       (Jeśli twoje środowisko obsługuje prototypy funkcji, to będzie to "int  yylex(  void  )".)
       Definicję tę można zmienić definiując makro "YY_DECL". Na przykład

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

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

       Przy każdym wywołaniu yylex(), następuje skanowanie tokenów z globalnego pliku wejściowego
       yyin  (który domyślnie wskazuje na stdin). Wczytywanie trwa aż do osiągnięcia końca pliku,
       lub aż do napotkania w którejś z akcji instrukcji return.

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

       Jeśli  yylex()  kończy  skanowanie  z powodu wywołania instrukcji return w jednej z akcji,
       skaner może być wołany ponownie i wznowi działanie tam, gdzie skończył.

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

       Przykładowa definicja YY_INPUT (w sekcji definicji pliku wejściowego):

           %{
           #define YY_INPUT(buf,wynik,max_rozmiar) \
               { \
               int c = getchar(); \
               wynik = (c == EOF) ? YY_NULL : (buf[0] = c, 1); \
               }
           %}

       Definicja ta zmieni przetwarzanie wejścia tak, by naraz pojawiał się tylko jeden znak.

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

       Jeśli nie chcesz podawać własnej wersji  yywrap(),  to  musisz  albo  użyć  opcji  %option
       noyywrap  (wtedy  skaner  zachowuje  się,  jakby yywrap() zwracało 1), albo konsolidować z
       -lfl, uzyskując tak domyślną wersję funkcji, zawsze zwracającej 1.

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

       Swoje wyjście ECHO skaner zapisuje do  globalnego  strumienia  yyout  (domyślnie  stdout),
       który  można  przedefiniować  dzięki zwykłemu przypisaniu tej zmiennej do innego wskaźnika
       FILE.

WARUNKI POCZĄTKOWE

       flex daje mechanizm warunkowej aktywacji reguł. Reguły rozpoczynające się od "<sc>" włączą
       się tylko jeśli skaner znajduje się w warunku początkowym "sc". Na przykład,

           <STRING>[^"]*        { /* zjedz ciało łańcucha ... */
                       ...
                       }

       będzie aktywne tylko jeśli skaner jest w warunku początkowym "STRING", a

           <INITIAL,STRING,QUOTE>\.        { /* obsłuż cytowanie ... */
                       ...
                       }

       będzie  aktywne  tylko  jeśli  obecnym  warunkiem  początkowym  jest  albo "INITIAL", albo
       "STRING" albo "QUOTE".

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

       W rozróżnieniu warunków włączających i wykluczających istnieje wciąż pewna niejasność: oto
       przykład, ilustrujący ich powiązanie. Zestaw reguł:

           %s przyklad
           %%

           <przyklad>foo  rob_cos();

           bar            cos_innego();

       jest równoważny

           %x przyklad
           %%

           <przyklad>foo   rob_cos();

           <INITIAL,przyklad>bar    cos_innego();

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

       Zauważ  też,  że  specjalny specyfikator <*> pasuje do dowolnego warunku początkowego. Tak
       więc, powyższe można zapisać również następująco:

           %x przyklad
           %%

           <przyklad>foo   rob_cos();

           <*>bar    cos_innego();

       Reguła domyślna (wykonywania ECHO na każdym  niedopasowanym  znaku)  pozostaje  aktywna  w
       warunkach początkowych.  Jest to w sumie równoważne:

           <*>.|\n     ECHO;

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

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

                   int enter_special;

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

           <SPECIAL>blahblahblah
           ...i kolejne ruguły...

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

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

           %%
           expect-floats        BEGIN(expect);

           <expect>[0-9]+"."[0-9]+      {
                       printf( "znalazłem zmiennoprzecinkową, = %f\n",
                               atof( yytext ) );
                       }
           <expect>\n           {
                       /* jest to koniec linii, więc
                        * potrzebujemy kolejnego "expect-number"
                        * przed rozpoznawaniem dalszych liczb
                        */
                       BEGIN(INITIAL);
                       }

           [0-9]+      {
                       printf( "znalazłem całkowitą, = %d\n",
                               atoi( yytext ) );
                       }

           "."         printf( "znalazłem kropkę\n" );

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

           %x comment
           %%
                   int line_num = 1;

           "/*"         BEGIN(comment);

           <comment>[^*\n]*        /* zjedz wszystko, co nie jest '*'     */
           <comment>"*"+[^*/\n]*   /* zjedz '*'-ki, po których nie ma '/' */
           <comment>\n             ++line_num;
           <comment>"*"+"/"        BEGIN(INITIAL);

       Skaner  ten  może mieć problemy z dopasowaniem maksymalnej ilości tekstu w każdej z reguł.
       Ogólnie, przy pisaniu szybkich skanerów, próbuj dopasowywać w każdej regule tyle, ile  się
       da.

       Zauważ,  że nazwy warunków początkowych są tak naprawdę wartościami całkowitymi i mogą być
       tak przechowywane. Tak więc powyższe można rozwinąć w następującym stylu:

           %x comment foo
           %%
                   int line_num = 1;
                   int comment_caller;

           "/*"         {
                        comment_caller = INITIAL;
                        BEGIN(comment);
                        }

           ...

           <foo>"/*"    {
                        comment_caller = foo;
                        BEGIN(comment);
                        }

           <comment>[^*\n]*        /* zjedz wszystko co nie jest '*'   */
           <comment>"*"+[^*/\n]*   /* zjedz '*', po których nie ma '/' */
           <comment>\n             ++line_num;
           <comment>"*"+"/"        BEGIN(comment_caller);

       Co więcej, możesz mieć dostęp do bieżącego warunku początkowego poprzez makro YY_START  (o
       wartości  całkowitej).   Na  przykład,  powyższe  przypisania  do  comment_caller można by
       zapisać jako

           comment_caller = YY_START;

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

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

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

           %x str

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

           \"      string_buf_ptr = string_buf; BEGIN(str);

           <str>\"        { /* zobaczyłem zamykający cytat - gotowe */
                   BEGIN(INITIAL);
                   *string_buf_ptr = '\0';
                   /* zwróć typ i wartość tokenu stałej łańcuchowej do
                    * analizatora
                    */
                   }

           <str>\n        {
                   /* błąd - niezakończona stała łańcuchowa */
                   /* generuj komunikat o błędzie */
                   }

           <str>\\[0-7]{1,3} {
                   /* ósemkowa sekwencja specjalna */
                   int result;

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

                   if ( result > 0xff )
                           /* błąd, stała poza zakresem */

                   *string_buf_ptr++ = result;
                   }

           <str>\\[0-9]+ {
                   /* generuj błąd - zła sekwencja specjalna; coś jak
                    * '\48' lub '\0777777'
                    */
                   }

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

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

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

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

       Często,   np.  w  niektórych  przykładach  powyżej  można  skończyć  pisząc  grupę  reguł,
       rozpoczynających  się  od  tych  samych  warunków  początkowych.   Flex   ułatwia   całość
       wprowadzając pojęcie zakresu warunku początkowego.  Zakres rozpoczyna się od:

           <SCs>{

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

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

       jest równoważne:

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

       Zakresy warunków początkowych mogą być zagnieżdżane.

       Do obsługi stosów warunków początkowych są przeznaczone trzy procedury:

       void yy_push_state(int new_state)
              wrzuca  bieżący  warunek początkowy na stos warunków początkowych i przełącza się w
              stan new_state, zupełnie jak po użyciu BEGIN new_state (pamiętaj, że nazwy warunków
              początkowych są również liczbami całkowitymi).

       void yy_pop_state()
              zdejmuje wartość ze stosu i przełącza się na nią przez BEGIN.

       int yy_top_state()
              zwraca wierzchołek stosu bez zmiany zawartości stosu.

       Stos  warunków początkowych rośnie dynamicznie i nie ma żadnych wbudowanych ograniczeń. Po
       wyczerpaniu pamięci, wykonywanie programu jest przerywane.

       Aby korzystać ze stosów warunków początkowych,  skaner  musi  zawierać  dyrektywę  %option
       stack (zobacz niżej rozdział Opcje).

WIELOKROTNE BUFORY WEJŚCIOWE

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

       Aby załatwić niektóre z tych problemów,  flex  daje  mechanizm  tworzenia  i  przełączania
       między wielokrotnymi buforami wejściowymi. Bufor wejściowy jest tworzony z użyciem funkcji

           YY_BUFFER_STATE yy_create_buffer( FILE *file, int size )

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

           void yy_switch_to_buffer( YY_BUFFER_STATE new_buffer )

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

           void yy_delete_buffer( YY_BUFFER_STATE buffer )

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

           void yy_flush_buffer( YY_BUFFER_STATE buffer )

       Funkcja ta niszczy zawartość bufora, więc  przy  następnej  próbie  dopasowania  tokenu  z
       bufora, skaner najpierw wypełni bufor na nowo używając YY_INPUT.

       yy_new_buffer()  jest  synonimem  yy_create_buffer(),  udostępnionym  dla  zgodności z C++
       narzędziami new i delete, służącymi do tworzenia i niszczenia obiektów dynamicznych.

       Na koniec makro YY_CURRENT_BUFFER zwraca uchwyt YY_BUFFER_STATE do bieżącego bufora.

       A oto  przykład  używania  tych  właściwości  w  skanerze,  rozwijającym  pliki  załączane
       (właściwość <<EOF>> jest opisywana niżej):

           /* stan "incl" jest używany do wybierania nazwy załączanego pliku
            */
           %x incl

           %{
           #define MAX_INCLUDE_DEPTH 10
           YY_BUFFER_STATE include_stack[MAX_INCLUDE_DEPTH];
           int include_stack_ptr = 0;
           %}

           %%
           include             BEGIN(incl);

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

           <incl>[ \t]*      /* zjedz białą spację */
           <incl>[^ \t\n]+   { /* mam nazwę pliku załącznika */
                   if ( include_stack_ptr >= MAX_INCLUDE_DEPTH )
                       {
                       fprintf( stderr, "Zbyt zagnieżdżone załączniki" );
                       exit( 1 );
                       }

                   include_stack[include_stack_ptr++] =
                       YY_CURRENT_BUFFER;

                   yyin = fopen( yytext, "r" );

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

                   yy_switch_to_buffer(
                       yy_create_buffer( yyin, YY_BUF_SIZE ) );

                   BEGIN(INITIAL);
                   }

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

                   else
                       {
                       yy_delete_buffer( YY_CURRENT_BUFFER );
                       yy_switch_to_buffer(
                            include_stack[include_stack_ptr] );
                       }
                   }

       Do  zestawiania  buforów  wejściowych  dla  skanowania  łańcuchów z pamięci zamiast plików
       istnieją trzy procedury. Każda z nich tworzy nowy bufor wejściowy do skanowania łańcucha i
       zwraca   odpowiadający   uchwyt   YY_BUFFER_STATE   (który  powinieneś  skasować  stosując
       yy_delete_buffer() po zakończeniu działania). Przełączają one też  przetwarzanie  na  nowy
       bufor  przy  użyciu  yy_switch_to_buffer(),  więc  następne  wywołanie  yylex() rozpocznie
       skanowanie łańcucha.

       yy_scan_string(const char *str)
              skanuje łańcuch zakończony zerem.

       yy_scan_bytes(const char *bytes, int len)
              skanuje len bajtów (dopuszczalne zera w środku) począwszy od pozycji bytes.

       Zauważ, że obydwie funkcje tworzą i skanują kopie oryginalnych danych. (Jest to  pożądane,
       gdyż yylex() modyfikuje zawartość skanowanego bufora.) Kopiowania można uniknąć, stosując:

       yy_scan_buffer(char *base, yy_size_t size)
              które  skanuje  bufor  na  miejscu, zaczynając od base, a w długości size bajtów, z
              których dwa bajty muszą być znakami YY_END_OF_BUFFER_CHAR  (ASCII  NUL).   Ostatnie
              dwa   bajty  nie  są  skanowane;  tak  więc  skanowanie  przebiega  od  base[0]  do
              base[size-2] włącznie.

              Jeśli nie ustawisz odpowiednio base to yy_scan_buffer() zwraca wskaźnik nil zamiast
              tworzyć nowy bufor wejściowy.

              Typ  yy_size_t  jest  typem  całkowitym,  na który rzutuje się wyrażenie całkowite,
              określające rozmiar bufora.

REGUŁY END-OF-FILE

       Specjalna reguła "<<EOF>>" określa akcje, które należy wykonać po osiągnięciu końca  pliku
       i  gdy  yywrap()  zwraca  zero (tj. wskazuje brak dalszych plików do przetworzenia). Akcja
       musi się zakończyć zrobieniem jednej z czterech rzeczy:

       -      przypisaniem yyin do nowego pliku wejściowego (w  poprzednich  wersjach  fleksa  po
              dokonaniu przypisania należało wywołać specjalną akcję YY_NEW_FILE; nie jest to już
              wymagane);

       -      wywołaniem instrukcji return;

       -      wywołaniem specjalnej akcji yyterminate();

       -      przełączeniem na nowy bufor za pomocą yy_switch_to_buffer().

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

           <INITIAL><<EOF>>

       Te reguły przydatne są do łapania rzeczy takich, jak niezamknięte cytaty. Przykład:

           %x quote
           %%

           ...inne reguły cytatowe...

           <quote><<EOF>>   {
                    error( "nie zamknięty cytat" );
                    yyterminate();
                    }
           <<EOF>>  {
                    if ( *++filelist )
                        yyin = fopen( *filelist, "r" );
                    else
                       yyterminate();
                    }

RÓŻNE MAKRA

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

           #define YY_USER_ACTION ++ctr[yy_act]

       gdzie  ctr  jest  tablicą  przechowującą  zawartość  różnych  reguł.  Zauważ,   że   makro
       YY_NUM_RULES daje ogólną liczbę reguł (łącznie z regułą domyślną, nawet jeśli używasz -s),
       więc poprawną deklaracją ctr jest:

           int ctr[YY_NUM_RULES];

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

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

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

       Makro YY_AT_BOL() zwraca prawdę jeśli następny token skanowany z bieżącego  bufora  będzie
       miał aktywne reguły '^'. W przeciwnym wypadku zwraca fałsz.

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

WARTOŚCI DOSTĘPNE DLA UŻYTKOWNIKA

       Sekcja ta zestawia różne wartości dostępne dla użytkownika w akcjach regułowych.

       -      char *yytext zawiera bieżący tekst tokenu. Może być modyfikowany, lecz nie może być
              wydłużany (nie można doklejać dodatkowych znaków na końcu).

              Jeśli w pierwszej sekcji opisu skanera pojawi się  dyrektywa  specjalna  %array  to
              yytext   zostanie   zadeklarowane   jako   charyytext[YYLMAX],  gdzie  YYLMAX  jest
              makrodefinicją, którą można przedefiniować w pierwszej sekcji (wartość domyślna  to
              ogólnie  8KB).  Używanie  %array daje wolniejsze skanery, lecz wartość yytext staje
              się odporna na wywołania input() i unput(), które potencjalnie niszczą jego wartość
              kiedy  yytext  jest  wskaźnikiem  znakowym.  Przeciwną  dyrektywą  do  %array  jest
              %pointer, która jest dyrektywą domyślną.

              Dyrektywy %array nie można używać do generowania klas skanera C++ (flaga -+).

       -      int yyleng przechowuje długość bieżącego tokenu.

       -      FILE *yyin jest plikiem, z którego  flex  domyślnie  odczytuje  wejście.  Może  być
              redefiniowany,  lecz taki zabieg ma sens tylko nim rozpocznie się skanowanie lub po
              napotkaniu EOF. Zmienianie tej wartości w środku skanowania może dać  nieoczekiwane
              rezultaty  spowodowane  buforowaniem  wejścia. Zamiast tego użyj wtedy yyrestart().
              Po zakończeniu skanowania przez napotkanie końca  pliku,  można  przypisać  wartość
              yyin do nowego pliku wejściowego i wywołać ponownie skaner by dokończył skanowanie.

       -      void  yyrestart(  FILE *new_file ) może być wołane do wskazywania yyin na nowy plik
              wejściowy. Przełączenie na  nowy  plik  jest  natychmiastowe  (wszelkie  poprzednio
              buforowane  wejście jest tracone). Zauważ, że wołanie yyrestart() z argumentem yyin
              porzuca  bieżący  bufor  wejściowy  i  kontynuuje  skanowanie  tego  samego   pliku
              wejściowego.

       -      FILE *yyout jest plikiem, do którego kierowane jest wyjście akcji ECHO.  Użytkownik
              może mu przypisać inną wartość.

       -      YY_CURRENT_BUFFER zwraca uchwyt YY_BUFFER_STATE do bieżącego bufora.

       -      YY_START zwraca wartość całkowitą, odpowiadającą bieżącemu warunkowi  początkowemu.
              Wartości tej można używać dalej z BEGIN do powrotu do tego warunku.

ŁĄCZENIE Z YACC

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

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

           %%

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

OPCJE

       flex ma następujące opcje:

       -b     Generuje informacje zapasowe  do  lex.backup.   Oto  lista  stanów  skanera,  które
              wymagają  kopii  zapasowych  oraz  znaki wejściowe dla których to zachodzi. Dodając
              reguły można usunąć stany zapasowe. Jeśli  wyeliminowane  zostaną  wszystkie  stany
              zapasowe,  a  użyte będzie -Cf lub -CF, wygenerowany skaner będzie działał szybciej
              (zobacz flagę -p).  Opcją to powinni się martwić  jedynie  użytkownicy  wyciskający
              ostatnie poty ze swoich skanerów. (Zobacz sekcję o Rozważaniach nad Wydajnością.)

       -c     nieużywana i niezalecana opcja dla zgodności z POSIX-em.

       -d     powoduje,  że  generowany  skaner  działa  w  trybie  debug.   Za  każdym  razem po
              rozpoznaniu wzorca, gdy globalna zmienna  yy_flex_debug  jest  niezerowa  (co  jest
              domyślne), skaner zapisze na stderr linię w postaci:

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

              Numer  linii  odnosi  się  do  położenia reguły w pliku definiującym skaner (tj.  w
              pliku, potraktowanym fleksem). Komunikaty są również  generowane  gdy  skaner  robi
              kopie  zapasowe,  przyjmuje domyślną regułę, dochodzi do końca bufora (lub napotyka
              NUL; w tym momencie  obydwa  [zdarzenia]  wyglądają  jednakowo  z  punktu  widzenia
              skanera) lub osiąga koniec pliku.

       -f     określa  szybki skaner.  Nie dokonywana jest kompresja tabel i pomijane jest stdio.
              W efekcie kod jest duży, lecz szybki. Opcja ta jest równoważna -Cfr (zobacz niżej).

       -h     generuje zestawienie "pomocy" opcji fleksa na stdout i  kończy  działanie.   -?   i
              --help są równoważnikami -h.

       -i     nakazuje  fleksowi  generowania skanera niewrażliwego na wielkość znaków.  Wielkość
              liter  we  wzorcach  zostanie  zignorowany,  a  tokeny  wejścia  będą  dopasowywane
              niezależnie  od  wielkości.  Dopasowany  tekst  znajdujący się w yytext będzie miał
              zachowaną oryginalną wielkość liter.

       -l     włącza maksymalną zgodność z oryginalną implementacją leksa z AT&T. Zauważ, że  nie
              oznacza to pełnej zgodności. Użycie tej opcji kosztuje sporo wydajności i eliminuje
              z użycia opcje -+,-f,-F,-Cf lub  -CF.   Dla  szczegółów  o  zapewnianej  zgodności,
              zobacz  niżej  sekcję o niezgodnościach między Leksem i POSIX-em. Opcja ta powoduje
              też z#definiowanie nazwy YY_FLEX_LEX_COMPAT w generowanym skanerze.

       -n     kolejna ignorowana opcja dodana dla zgodności z POSIX-em.

       -p     generuje  raport  o  wydajności  na  stderr.  Raport  składa  się  z  komentarzy  o
              właściwościach  pliku  wejściowego  fleksa, więc powoduje znaczną utratę wydajności
              skanera.  Jeśli  podasz  tę   flagę   dwukrotnie,   uzyskasz   też   komentarze   o
              właściwościach, które doprowadziły do drugorzędnych utrat wydajności.

              Zauważ,  że  użycie  REJECT,  %option  yylineno,  i  zmiennego  wiszącego kontekstu
              (variable trailing context)  (zobacz   niżej  sekcję  o  Niedostatkach  /  Błędach)
              powoduje  znaczną  utratę  wydajności;  używanie  yymore(),  operatora ^ i flagi -I
              powoduje pomniejsze utraty wydajności.

       -s     powoduje, że domyślna reguła (powodująca echo niedopasowanego  wejścia  skanera  na
              stdout)  nie  jest  wykonywana.  Jeśli  skaner  napotka  wejście,  którego nie może
              dopasować do reguł, przerywa  działanie  z  błędem.  Opcja  ta  jest  przydatna  do
              znajdowania dziur w zbiorze reguł skanera.

       -t     nakazuje  fleksowi  zapisanie wygenerowanego skanera na standardowe wyjście zamiast
              do pliku lex.yy.c.

       -v     nakazuje fleksowi pisanie na stderr zestawienia statystyk dotyczących  generowanego
              skanera.   Większość  statystyk jest pozbawiona znaczenia dla typowego użytkownika,
              lecz pierwsza z linijek wskazuje wersję fleksa (to samo co  zgłasza  opcja  -V),  a
              następna linia flagi użyte do generowania skanera, z domyślnymi włącznie.

       -w     powstrzymuje komunikaty o ostrzeżeniach.

       -B     nakazuje   fleksowi   generowanie  skanera  wsadowego,  czyli  odwrotność  skanerów
              interaktywnych, generowanych przez -I (zobacz niżej). Ogólnie, opcji -B  używa  się
              mając  pewność,  że  skaner nigdy nie będzie używany interaktywnie i chcąc wycisnąć
              jeszcze troszeczkę  więcej  wydajności.  Jeśli  chcesz  zyskać  więcej  wydajności,
              powinieneś  użyć  opcji  -Cf  lub  -CF  (opisanych  niżej), które włączają -B i tak
              automatycznie.

       -F     mówi, że należy użyć reprezentacji  tablicy  szybkiego  skanera  (i  stdio  ma  być
              pominięte).  Reprezentacja ta jest mniej więcej tak szybka jak reprezentacja pełnej
              tablicy (-f), i dla niektórych zestawów wzorców będzie  znacznie  mniejsza  (a  dla
              innych  większa).  Ogólnie,  jeśli  wzorzec  zawiera zarówno "słowa kluczowe" jak i
              łapiącą-wszystko regułę "identyfikatora", tak jak poniższy zestaw:

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

              to lepiej użyć  reprezentacji  pełnej  tablicy.  Jeśli  obecna  jest  tylko  reguła
              "identyfikatora"  i  używasz  potem  hasza  lub  podobnej rzeczy do wykrywania słów
              kluczowych, to lepiej użyć opcji -F.

              Opcja ta odpowiada -CFr (zobacz niżej).  Nie można jej używać z -+.

       -I     nakazuje fleksowi generowanie skanera interaktywnego.  Skaner  interaktywny  patrzy
              naprzód  do  wyboru  dopasowania  jedynie  jeśli musi.  Okazuje się, że patrzenie o
              jeden dodatkowy znak dalej, nawet jeśli skaner ma już dość  do  dopasowania  tokenu
              jest  trochę  szybsze  niż  wersja  minimalna.   Lecz skanery patrzące naprzód dają
              dziadowską wydajność interaktywną; na przykład gdy użytkownik wpisze nową linię, to
              nie  jest  ona  rozpoznawana jako token nowej linii dopóki nie wprowadzony zostanie
              następny token, co oznacza często wpisanie całej kolejnej linii.

              Skanery fleksa są domyślnie interaktywne, chyba że użyjesz opcji kompresji  tablicy
              -Cf  lub  -CF  (zobacz  niżej).   Jest  tak  dlatego,  że jeśli oczekujesz wysokiej
              wydajności, to powinieneś użyć jednej z tych opcji, a jeśli tego nie zrobiłeś, flex
              zakłada,  że  jesteś  gotów  poświęcić  trochę  wydajności  na  rzecz  intuicyjnego
              zachowania interaktywnego. Zauważ też, że nie możesz użyć -I w połączeniu z -Cf lub
              -CF.   Z  tej przyczyny opcja ta nie jest w rzeczywistości wymagana; jest domyślnie
              włączona dla tych przypadków, dla których jest dopuszczalna.

              Opcją -B możesz wymusić by skaner nie był interaktywny (zobacz powyżej).

       -L     nakazuje fleksowi nie generować dyrektyw #line.   Bez  tej  opcji  flex  przyprawia
              generowany  skaner  dyrektywami  #line,  więc  komunikaty  o błędach w akcjach będą
              poprawnie położone względem oryginalnego  pliku  wejściowego  fleksa  (jeśli  błędy
              wynikają  z  kodu  w pliku wejściowym) lub [względem] lex.yy.c (jeśli błędy są winą
              fleksa -- powinieneś zgłosić takie błędy pod adres e-mail podany poniżej.)

       -T     powoduje, że flex działa w trybie śledzenia.   Będzie  generował  na  stderr  wiele
              komunikatów   o   postaci   wejścia  i  wynikających  zeń  niedeterministycznych  i
              deterministycznych automatach skończonych. Opcja ta jest używana  zwykle  w  opiece
              nad fleksem.

       -V     drukuje numer wersji na stdout i kończy działanie.  --version jest synonimem -V.

       -7     nakazuje   fleksowi   generowanie   skanera  7-bitowego,  tj.  takiego  który  może
              rozpoznawać w swoim wejściu tylko znaki 7-bitowe. Zaletą używania -7  jest  to,  że
              tablice  skanera  będą  o połowę mniejsze niż wygenerowane opcją -8 (zobacz niżej).
              Wadą jest to, że skanery takie często się zawieszają  lub  załamują  jeśli  na  ich
              wejściu znajdzie się znak 8-bitowy.

              Zauważ  jednak, że jeśli generujesz skaner z użyciem opcji kompresji tablic -Cf lub
              -CF,  to  użycie  -7  zachowa  jedynie  niewielki  rozmiar  przestrzeni  tablic,  a
              spowoduje, że skaner będzie znacząco mniej przenośny.  Domyślnym zachowaniem fleksa
              jest generowanie skanerów 8-bitowych, chyba że użyto opcji -Cf  lub  -CF,  i  wtedy
              flex  generuje  domyślnie  skaner  7-bitowy,  chyba  że  twoja  maszyna zawsze była
              skonfigurowana na generowanie skanerów 8-bitowych (co często się zdarza poza  USA).
              To,  czy  flex  wygenerował  skaner  7  czy  8  bitowy, można określić, sprawdzając
              zestawienie flag w wyjściu -v, co opisano wyżej.

              Zauważ, że jeśli używasz -Cfe  lub  -CFe,  flex  wciąż  domyślnie  generuje  skaner
              8-bitowy,  gdyż  po  kompresji  pełne  tablice  8-bitowe  nie  są  wiele większe od
              7-bitowych.

       -8     nakazuje fleksowi generowanie skanera 8-bitowego,  tj.  takiego,  który  rozpoznaje
              znaki  8-bitowe.  Flaga  ta  jest  wymagana  jedynie  dla skanerów wygenerowanych z
              użyciem -Cf lub -CF, gdyż w innych wypadkach jest ona przyjmowana jako domyślna.

       -+     określa, że chcesz by fleks wygenerował  klasę  skanera  w  C++.  Zobacz  sekcję  o
              generowaniu skanerów C++.

       -C[aefFmr]
              steruje poziomem kompresji tablic, balansując między małymi a szybkimi skanerami.

              -Ca  ("wyrównaj")  nakazuje  fleksowi  poświęcić  rozmiar  tablic  w wygenerowanych
              skanerach na rzecz szybkości, gdyż elementy tablic mogą być  lepiej  wyrównane  pod
              kątem dostępu do pamięci i obliczeń. Na niektórych architekturach RISC pobieranie i
              operowanie na długich słowach jest efektywniejsze niż  na  mniejszych  jednostkach,
              takich jak krótkie słowa. Opcja ta może podwoić rozmiar tablic używanych przez twój
              skaner.

              -Ce  Nakazuje  fleksowi  budowanie  klas  równoważności,  tj.  zestawów  znaków   o
              identycznych  właściwościach  leksykalnych  (np.  jeśli jedynym wystąpieniem cyfr w
              pliku wejściowym fleksa jest klasa znaków "[0-9]", to cyfry z przedziały od 0 do  9
              zostaną  wstawione  do  tej  samej klasy równoważności. Klasy takie zwykle znacznie
              redukują ostateczne rozmiary tablic/obiektów (zwykle 2-5 razy) i są  całkiem  tanie
              od strony wydajnościowej (jedno podglądnięcie w tablicy na skanowany znak).

              -Cf  określa,  że  należy  generować  pełne  tablice  skanera  -  flex  nie  ma ich
              kompresować poprzez branie korzyści z podobnych funkcji przejść dla różnych stanów.

              -CF określa, że należy użyć alternatywnej, szybkiej reprezentacji skanera (opisanej
              pod flagą -F).  Opcja ta nie może być używana z -+.

              -Cm  nakazuje  fleksowi  budowanie  klas meta-równoważności, które są zbiorami klas
              równoważności (lub znaków, jeśli klasy równoważności  nie  są  używane),  które  są
              często  używane  wspólnie.  Klasy  takie  są  często  dobrą rzeczą podczas używania
              skompresowanych tablic, lecz mają one już umiarkowany wpływ na wydajność  (dwa  lub
              jeden test "if" i jedno podglądnięcie tablicy na skanowany znak).

              -Cr  powoduje,  że  generowany  skaner omija użycie standardowej biblioteki I/O dla
              wejścia.  Zamiast  wołać  fread()  lub  getc(),  skaner  będzie  używać   wywołania
              systemowego  read(),  zyskując  tak  trochę  na  wydajności  (w  skali  zależnej od
              systemu). W rzeczywistości jest to bez znaczenia, chyba że używasz też -Cf lub -CF.
              Wykorzystanie  -Cr może też spowodować dziwne zachowanie jeśli np. odczytasz z yyin
              z pomocą stdio przed wywołaniem skanera (skaner pominie  tekst  pozostawiony  przez
              twoje odczyty w buforze wejściowym stdio).

              -Cr nie działa jeśli zdefiniujesz YY_INPUT (zobacz wyżej Generowany Skaner).

              Samotne  -C  określa,  że tablice skanera powinny być kompresowane, lecz nie należy
              używać klas równoważności i klas metarównoważności.

              Opcje -Cf lub -CF  i  -Cm  nie  mają  sensu  razem  -  nie  ma  sytuacji  dla  klas
              metarównoważności  jeśli  tablica  nie  jest  kompresowana.  Poza  tym  opcje można
              swobodnie łączyć.

              Domyślnym ustawieniem jest -Cem, które określa, że flex  powinien  generować  klasy
              równoważności  i  metarównoważności. Ustawienie to daje najwyższy stopień kompresji
              tablic. Kosztem większych tablic można uzyskać  szybciej  wykonujące  się  skanery.
              Następujące zestawienie jest mniej więcej prawdziwe:

                  najwolniejsze i najmniejsze
                        -Cem
                        -Cm
                        -Ce
                        -C
                        -C{f,F}e
                        -C{f,F}
                        -C{f,F}a
                  najszybsze i największe

              Zauważ,  że  skanery  z  najmniejszymi tablicami są zwykle najszybciej generowane i
              kompilowane, więc  podczas  prac  rozwojowych  prawdopodobnie  najchętniej  użyjesz
              domyślnej, maksymalnej kompresji.

              -Cfe  jest  często  dobrym  kompromisem  między szybkością a rozmiarem dla skanerów
              gotowych do wdrożenia (production scanners).

       -ooutput
              nakazuje fleksowi zapisanie skanera do pliku output  zamiast  do  lex.yy.c.   Jeśli
              połączysz  -o  z opcją -t, to skaner jest zapisywany na stdout, lecz jego dyrektywy
              #line (zobacz wyżej opcję -L), odnoszą się do pliku output.

       -Pprefiks
              zmienia domyślny przedrostek yy używany przez fleksa  dla  wszystkich  zmiennych  i
              funkcji  globalnych na prefiks.  Na przykład -Pfoo zmienia nazwę yytext na footext.
              Zmienia to też nazwę domyślnego pliku wyjściowego z lex.yy.c na lex.foo.c.   A  oto
              wszystkie nazwy, których dotyczy takie zachowanie:

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

              (Jeśli  używasz  skanera  C++,  to  dotyczyć to będzie tylko yywrap i yyFlexLexer.)
              Wewnątrz samego skanera można wciąż używać jednej i  drugiej  konwencji  nazywania;
              jednak z zewnątrz dozwolone są tylko nazwy zmodyfikowane.

              Opcja  ta  umożliwia  łatwe łączenie w całość różnych programów fleksa w jeden plik
              wykonywalny. Zauważ jednak, że używanie tej opcji zmienia też nazwę yywrap(),  więc
              musisz  teraz albo udostępnić własną wersję tej procedury dla swojego skanera, albo
              użyć %option noyywrap, gdyż konsolidacja z -lfl nie daje już funkcji domyślnej.

       -Sskeleton_file
              przesłania domyślny plik  szkieletowy,  na  podstawie  którego  flex  buduje  swoje
              skanery. Nie będziesz używać tej opcji, chyba że zajmujesz się rozwojem fleksa.

       flex  daje  też  mechanizm kontrolowania opcji z samej specyfikacji skanera, zamiast linii
       poleceń. Działa to przez  włączanie  dyrektyw  %option  w  pierwszej  sekcji  specyfikacji
       skanera. W jednej dyrektywie %option można podawać wiele opcji, a w samej pierwszej sekcji
       pliku wejściowego fleksa można używać wielu dyrektyw.

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

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

           caseful lub
           case-sensitive  przeciwne do -i (domyślne)

           case-insensitive lub
           caseless        -i

           debug           -d
           default         przeciwne do -s
           ecs             -Ce
           fast            -F
           full            -f
           interactive     -I
           lex-compat      -l
           meta-ecs        -Cm
           perf-report     -p
           read            -Cr
           stdout          -t
           verbose         -v
           warn            przeciwne do -w
                           (dla -w użyj "%option nowarn")

           array           równoważne "%array"
           pointer         równoważne "%pointer" (domyślne)

       Niektóre %opcje dają właściwości niedostępne gdzie indziej:

       always-interactive
              nakazuje  fleksowi  generowanie  skanera,  który  zawsze  uważa  swoje  wejście  za
              "interaktywne".  Normalnie  przy  każdym  pliku  wejściowym skaner woła isatty() do
              określenia czy wejście skanera jest interaktywne i powinno być czytane po znaku. Po
              użyciu tej opcji wywołanie takie nie jest robione.

       main   nakazuje  fleksowi  udostępnić domyślny program main() dla skanera, który po prostu
              woła yylex().  Opcja ta implikuje noyywrap (zobacz niżej).

       never-interactive
              nakazuje  fleksowi  generowanie  skanera,  który  zawsze  uważa  swoje  wejście  za
              "nieinteraktywne"  (znów,  nie  jest  wołane isatty()).  Opcja ta jest przeciwna do
              always-interactive.

       stack  włącza używanie stosów warunków początkowych (zobacz wyżej Warunki Początkowe).

       stdinit
              jeśli jest ustawione (np.  %option stdinit) to zachodzi inicjalizacja yyin i  yyout
              na stdin i stdout, zamiast domyślnych nil.  Niektóre istniejące programy lex zależą
              od tego zachowania, nawet jeśli nie jest ono zgodne z ANSI C,  które  nie  wymagają
              stałych czasu kompilacji stdin i stdout.

       yylineno
              nakazuje fleksowi generowanie skanera, który przechowuje liczbę obecnie odczytanych
              linii w zmiennej globalnej yylineno.  Opcja ta jest wymuszana  przez  %option  lex-
              compat.

       yywrap jeśli  nie  jest  ustawione (np.  %option noyywrap), to skaner nie woła yywrap() na
              końcu pliku, lecz po prostu przyjmuje, że nie ma już plików do  skanowania  (dopóki
              użytkownik nie wskaże yyin na nowy plik i nie wywoła yylex() ponownie).

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

       Trzy opcje pobierają wartości łańcuchowe, offsetowane znakiem '=':

           %option outfile="ABC"

       jest równoważne -oABC, a

           %option prefix="XYZ"

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

           %option yyclass="foo"

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

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

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

       (chociaż yy_push_state() i podobne i tak nie pojawią się dopóki nie użyjesz %optionstack).

ROZWAŻANIA NAD WYDAJNOŚCIĄ

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

           REJECT
           %option yylineno
           arbitralny wiszący kontekst

           zestawy wzorców, wymagające cofania
           %array
           %option interactive
           %option always-interactive

           '^' operator rozpoczęcia linii
           yymore()

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

       REJECT powinno być unikane  za  wszelką  cenę  z  punktu  widzenia  wydajności.   Jest  to
       szczególnie kosztowna opcja.

       Pozbycie  się  cofania  jest  trudne  i  może często prowadzić do błędów w skomplikowanych
       skanerach. W praktyce zaczyna się od użycia flagi -b do  wygenerowania  pliku  lex.backup.
       Na przykład dla wejścia

           %%
           foo        return TOK_KEYWORD;
           foobar     return TOK_KEYWORD;

       plik ten wygląda tak:

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

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

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

           Compressed tables always back up.

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

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

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

       Sposobem usuwania cofania jest dodawanie reguł dla "błędów":

           %%
           foo         return TOK_KEYWORD;
           foobar      return TOK_KEYWORD;

           fooba       |
           foob        |
           fo          {
                       /* fałszywy alarm, nie jest to słowo kluczowe */
                       return TOK_ID;
                       }

       Eliminowanie cofania można przeprowadzić również przy użyciu reguły "łap-wszystko":

           %%
           foo         return TOK_KEYWORD;
           foobar      return TOK_KEYWORD;

           [a-z]+      return TOK_ID;

       Jest to, tam gdzie można je zastosować, najlepsze rozwiązanie.

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

       Ważne jest pamiętanie, że  korzyści  z  eliminacji  tego  problemu  zyskujesz  dopiero  po
       zlikwidowaniu  każdej  instancji  cofania.  Pozostawienie  choć  jednej  oznacza,  że  nie
       zyskujesz niczego.

       Zmienny wiszący kontekst (gdzie zarówno prowadząca jak i kończąca część nie mają ustalonej
       długości) wprowadza utratę wydajności zbliżoną do REJECT (tzn. znaczną). Dlatego gdy tylko
       można, to zapisz taką regułę:

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

       jako:

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

       lub jako

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

       zwróć uwagę, że specjalna akcja '|'  nie  powoduje  żadnych  oszczędności,  a  wręcz  może
       pogorszyć sprawę (zobacz niżej Niedostatki / Błędy).

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

           %x comment
           %%
                   int line_num = 1;

           "/*"         BEGIN(comment);

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

       Można to przyspieszyć następująco:

           %x comment
           %%
                   int line_num = 1;

           "/*"         BEGIN(comment);

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

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

       Ostateczny  przykład  przyspieszania skanera: załóżmy, że chcesz skanować plik zawierający
       identyfikatory i słowa kluczowe w liczbie jednego na linię, bez żadnych  obcych  znaków  i
       chcesz rozpoznawać wszystkie słowa kluczowe.  Naturalnym odruchem początkowym jest:

           %%
           asm      |
           auto     |
           break    |
           ... etc ...
           volatile |
           while    /* to jest słowo kluczowe */

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

       Aby wyeliminować śledzenie wstecz, wprowadź regułę łap-wszystko:

           %%
           asm      |
           auto     |
           break    |
           ... etc ...
           volatile |
           while    /* to słowo kluczowe */

           [a-z]+   |
           .|\n     /* a to nie... */

       Obecnie,  jeśli  mamy  zagwarantowane,  że  mamy  dokładnie  jedno  słowo  w linii, możemy
       zredukować całkowitą liczbę dopasowań o połowę przez  włączanie  w  rozpoznawanie  tokenów
       łapanie nowych linii.

           %%
           asm\n    |
           auto\n   |
           break\n  |
           ... etc ...
           volatile\n |
           while\n  /* to słowo kluczowe */

           [a-z]+\n |
           .|\n     /* a to nie... */

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

           %%
           asm\n    |
           auto\n   |
           break\n  |
           ... etc ...
           volatile\n |
           while\n  /* to słowo kluczowe */

           [a-z]+\n |
           [a-z]+   |
           .|\n     /* a to nie... */

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

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

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

GENEROWANIE SKANERÓW C++

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

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

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

       const char* YYText()
              zwraca tekst ostatnio dopasowanego tokenu, równoważnik yytext.

       int YYLeng()
              zwraca długość ostatnio dopasowanego tokenu, równoważnik yyleng.

       int lineno() const
              zwraca numer aktualnej linii wejściowej (zobacz  %option  yylineno),  lub  1  jeśli
              %option yylineno nie zostało użyte.

       void set_debug( int flag )
              ustawia  flagę  debuggującą  dla  skanera, równoważnik przypisania do yy_flex_debug
              (zobacz wyżej sekcję o opcjach). Zauważ,  że  aby  włączać  w  skanerze  informacje
              diagnostyczne, musisz skompilować go z użyciem %option debug.

       int debug() const
              zwraca bieżące ustawienie flagi debuggującej.

       Udostępniane  są też funkcje składowe równoważne yy_switch_to_buffer(), yy_create_buffer()
       (chociaż pierwszym argumentem jest wskaźnik istream*,  a  nie  FILE*),  yy_flush_buffer(),
       yy_delete_buffer() i yyrestart() (i znowu, pierwszym argumentem jest wskaźnik istream*).

       Kolejną  klasą  zdefiniowaną  w  FlexLexer.h  jest  yyFlexLexer, który jest klasą pochodną
       FlexLexer.  Zaiwera następujące dodatkowe funkcje składowe:

       yyFlexLexer( istream* arg_yyin = 0, ostream* arg_yyout = 0 )
              buduje obiekt yyFlexLexer stosując podane strumienie jako wejście i wyjście.  Jeśli
              nie zostaną podane, to strumienie będą odpowiadały odpowiednio cin i cout.

       virtual int yylex()
              odgrywa  tę  samą  rolę co yylex() dla normalnych skanerów fleksa: skanuje strumień
              wejściowy,  konsumuje  tokeny  aż  akcja  reguły  nie  zwróci  wartości.  Jeśli   z
              yyFlexLexer  wyprowadzisz  podklasę  S i zechcesz dostać się do funkcji i zmiennych
              składowych S z wnętrza yylex(), to musisz użyć %option yyclass="S" by  poinformować
              fleksa,  że  będziesz  używać  podklasy zamiast yyFlexLexer.  W tym wypadku zamiast
              generować yyFlexLexer::yylex(), flex  generuje  S::yylex()  (oraz  generuje  prosty
              yyFlexLexer::yylex(), który woła yyFlexLexer::LexerError() po wywołaniu).

       virtual void switch_streams(istream* new_in = 0,
              ostream*  new_out = 0) przypisuje yyin do new_in (jeśli jest nie-nil) oraz yyout do
              new_out (ditto), kasując poprzedni bufor wejściowy  jeśli  przypisywana  jest  nowa
              wartość yyin .

       int yylex( istream* new_in, ostream* new_out = 0 )
              najpierw  przełącza strumienie wejściowe poprzez switch_streams( new_in, new_out ),
              a następnie zwraca wartość yylex().

       Poza tym, yyFlexLexer definiuje następujące chronione (protected) funkcje wirtualne, które
       można przedefiniować w klasach pochodnych, by dostosować skaner:

       virtual int LexerInput( char* buf, int max_size )
              odczytuje  maksymalnie  max_size  znaków do buf i zwraca liczbę odczytanych znaków.
              Aby  wskazać  koniec  wejścia  zwracane  jest  0   znaków.   Zauważ,   że   skanery
              "interaktywne"  (zobacz  flagi  -B  oraz -I) definiują makro YY_INTERACTIVE.  Jeśli
              redefiniujesz LexerInput() i potrzebujesz brać różne akcje, zależnie  od  tego  czy
              skaner  skanuje źródło interaktywne czy nie, to możesz sprawdzać obecność tej nazwy
              poprzez #ifdef.

       virtual void LexerOutput( const char* buf, int size )
              zapisuje size znaków z bufora buf który, o ile jest zakończony zerem, może zawierać
              też "wewnętrzne" zera jeśli reguły skanera mogą łapać tekst z wewnętrznymi zerami.

       virtual void LexerError( const char* msg )
              zgłasza komunikat błędu krytycznego. Domyślna wersja tej funkcji zapisuje komunikat
              do strumienia cerr i kończy działanie programu.

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

       Dla  skanerów  C++  nie  jest dostępna właściwość %array, trzeba więc używać %pointer (tj.
       wartości domyślnej).

       Oto przykład prostego skanera C++:

               // Przykład użycia klasy skanera C++

           %{
           int mylineno = 0;
           %}

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

           ws      [ \t]+

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

           %%

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

           "/*"    {
                   int c;

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

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

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

           \n        mylineno++;

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

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

           %%

           int main( int /* argc */, char** /* argv */ )
               {
               FlexLexer* lexer = new yyFlexLexer;
               while(lexer->yylex() != 0)
                   ;
               return 0;
               }
       Jeśli chcesz tworzyć wiele (różnych) klas leksera, powinieneś użyć  flagi  -P  (lub  opcji
       prefiks=)  do  zmiany  nazwy  każdego  yyFlexLexer  na inny xxFlexLexer.  Następnie możesz
       załączać <FlexLexer.h> do swoich innych źródeł, raz na klasę leksera, zmieniając  najpierw
       nazwę yyFlexLexer w następujący sposób:

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

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

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

       WAŻNE: obecna postać klasy skanującej jest eksperymentalna  i  może  zmieniać  się  między
       głównymi wydaniami.

NIEZGODNOŚCI Z LEX I POSIX

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

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

       fleksowa opcja -l włącza maksymalną zgodność z oryginalnym AT&T lex,  okupując  to  jednak
       znacznymi  stratami wydajności generowanego skanera.  Niżej zaznaczymy, które niezgodności
       można pokonać używając opcji -l.

       flex jest w pełni zgodny z leksem poza następującymi wyjątkami:

       -      Nieudokumentowana zmienna  wewnętrzna  skanera  lex  o  nazwie  yylineno  nie  jest
              obsługiwana bez -l lub %option yylineno.

              yylineno  powinno  być  obsługiwane  na  poziomie  buforowym,  a  nie na skanerowym
              (pojedyncza zmienna globalna).

              yylineno nie jest częścią specyfikacji POSIX.

       -      Procedura input() nie jest redefiniowalna  chociaż  może  być  wołana  do  czytania
              znaków  następującym  po tym, co dopasowano do reguły. Jeśli input() napotka koniec
              pliku, to wykonywane jest normalne przetwarzanie  yywrap().   ``Prawdziwy''  koniec
              pliku jest sygnalizowany przez input() zwróceniem wartości EOF.

              Wejście jest natomiast sterowane przez definiowanie makra YY_INPUT.

              Ograniczenie  fleksa,  że  input()  nie  może  być  redefiniowany  jest  zgodne  ze
              specyfikacją POSIX, która po prostu nie określa innego żadnego  sposobu  sterowania
              wejściem skanera niż poprzez dokonanie początkowego przypisania do yyin.

       -      Procedura unput() nie jest redefiniowalna. Ograniczenie to jest zgodne z POSIX.

       -      Skanery   fleksa   nie   są   tak  wielobieżne  (reentrant)  jak  skanery  lex.   W
              szczególności, jeśli masz interaktywny skaner i obsługę przerwań, która robi  długi
              skok  ze  skanera,  a  skaner  jest  następnie  wołany  ponownie, to możesz uzyskać
              następujący komunikat:

                  fatal flex scanner internal error--end of buffer missed

              Aby wejść na nowo do skanera, użyj najpierw

                  yyrestart( yyin );

              Zauważ, że wywołanie to wyrzuci wszelkie buforowane wejście; zwykle jednak nie jest
              to problem przy skanerach interaktywnych.

              Zauważ  też,  że klasy skanerów C++  wielobieżne (reentrant), więc używając opcji
              C++ powinieneś ich używać. Zobacz sekcję o generowaniu skanerów C++.

       -      output() nie jest obsługiwany. Wyjście makra  ECHO  jest  wykonywane  do  wskaźnika
              plikowego yyout (domyślnie stdout).

              output() nie jest częścią specyfikacji POSIX.

       -      lex  nie obsługuje wykluczających warunków początkowych (%x), choć znajdują się one
              w specyfikacji POSIX.

       -      Przy rozwijaniu definicji, flex ujmuje je w nawiasy.  W leksie, następujące:

                  NAME    [A-Z][A-Z0-9]*
                  %%
                  foo{NAME}?      printf( "Znalazłem\n" );
                  %%

              nie dopasuje się do łańcucha "foo",  gdyż  makro  jest  rozwijane  tak,  że  reguła
              odpowiada  "foo[A-Z][A-Z0-9]*?",  a pierwszeństwo jest takie, że '?' jest wiązany z
              "[A-Z0-9]*". We fleksie reguła zostałaby  rozwinięta  do  "foo([A-Z][A-Z0-9]*)?"  i
              łańcuch "foo" zostałby dopasowany.

              Zauważ,  że  jeśli  definicja  rozpoczyna  się od ^ lub kończy się na $ to nie jest
              rozwijana w nawiasach, aby umożliwić tym operatorom pojawienie  się  w  definicjach
              bez  utraty  ich  znaczenia.  Ale operatory <s>, / i <<EOF>> nie mogą być używane w
              definicji fleksa.

              Używanie -l skutkuje leksowym zachowaniem braku nawiasów wokół definicji.

              POSIX nakazuje ujmowanie definicji w nawiasy.

       -      Niektóre implementacje leksa umożliwiają rozpoczynanie akcji reguł w osobnej  linii
              jeśli wzorzec reguły ma doklejoną białą spację:

                  %%
                  foo|bar<tu spacja>
                    { foobar_action(); }

              flex nie obsługuje tej właściwości.

       -      Leksowe  %r  (generuj  skaner  Ratfor)  nie  jest  obsługiwane.  Nie  jest  częścią
              specyfikacji POSIX.

       -      Po wywołaniu unput(), yytext jest  niezdefiniowane  aż  do  dopasowania  następnego
              tokenu,  chyba  że  skaner  używa  %array.   Inaczej  ma  się  sprawa  z leksem lub
              specyfikacją POSIX. Opcja -l załatwia tę niezgodność.

       -      Pierwszeństwo operatora {} (zakresu  numerycznego)  jest  inne.   lex  interpretuje
              "abc{1,3}"  jako "dopasuj 1, 2 lub 3 pojawienia 'abc'", a flex interpretuje to jako
              "dopasuj 'ab' z doklejonym jednym, dwoma lub  trzema  znakami  'c'".  Interpretacja
              fleksowa jest zgodna ze specyfikacją POSIX.

       -      Pierwszeństwo  operatora  ^  jest  inne.  lex interpretuje "^foo|bar" jako "dopasuj
              albo 'foo' z początku linii albo 'bar' gdziekolwiek", podczas gdy flex  rozumie  to
              jako  "dopasuj 'foo' lub 'bar' jeśli pojawią się na początku linii". To drugie jest
              zgodne ze specyfikacją POSIX.

       -      Specjalne deklaracje rozmiaru-tablicy, takie jak %a, obsługiwane przez lex  nie  są
              wymagane przez skanery fleksa; flex je ignoruje.

       -      Nazwa FLEX_SCANNER jest #definiowana, więc skanery mogą być pisane z przeznaczeniem
              do użycia z fleksem lub leksem.  Skanery zawierają również YY_FLEX_MAJOR_VERSION  i
              YY_FLEX_MINOR_VERSION  wskazując  na  wersję  fleksa, która wygenerowała skaner (na
              przykład dla wydania 2.5 definiowane są odpowiednio liczby 2 i 5).

       Następujące właściwości fleksa nie są zawarte w specyfikacjach lex ani POSIX:

           Skanery C++
           %option
           zakresy warunków początkowych
           stosy warunków początkowych
           skanery interaktywne/nieinteraktywne
           yy_scan_string() i koledzy
           yyterminate()
           yy_set_interactive()
           yy_set_bol()
           YY_AT_BOL()
           <<EOF>>
           <*>
           YY_DECL
           YY_START
           YY_USER_ACTION
           YY_USER_INIT
           dyrektywy #line
           %{} wokół akcji
           wiele akcji w linii

       plus prawie wszystkie flagi fleksa. Ostatnia właściwość listy odnosi się do faktu,  że  we
       fleksie  można  wstawiać  wiele akcji do jednej linii, rozdzielając je średnikami, podczas
       gdy w leksie, następująca instrukcja

           foo    handle_foo(); ++num_foos_seen;

       jest (raczej niespodziewanie) obcinana do

           foo    handle_foo();

       flex nie obcina akcji. Akcje które nie są objęte klamrami kończą się zwyczajnie  na  końcu
       linii.

DIAGNOSTYKA

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

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

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

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

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

       flex scanner jammed - skaner skompilowany z  -s  napotkał  łańcuch  wejściowy,  który  nie
       został dopasowany do żadnej z jego reguł. Błąd ten może się pojawić też z powodu problemów
       wewnętrznych.

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

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

       flex scanner push-back overflow - użyłeś unput() do wepchnięcia z  powrotem  tak  długiego
       tekstu, że bufor skanera nie potrafił przetrzymać wepchniętego tekstu i bieżącego tokena w
       yytext.  Idealny skaner powinien dynamicznie zmienić rozmiar bufora, lecz obecnie tak  się
       nie dzieje.

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

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

           yyrestart( yyin );

       albo, jak wspomniano wyżej, przełącz się na używanie skanerów C++.

       too  many  start  conditions  in  <>  construct!   (zbyt  wiele  warunków  początkowych  w
       konstrukcji  <>) - w konstrukcji <> pojawiło się więcej warunków początkowych niż istnieje
       w rzeczywistości (więc przynajmniej jeden z nich pojawił się dwukrotnie).

PLIKI

       -lfl   biblioteka, z którą muszą być łączone skanery.

       lex.yy.c
              generowany skaner (nazywany na niektórych systemach lexyy.c).

       lex.yy.cc
              generowana klasa skanera C++, po użyciu -+.

       <FlexLexer.h>
              plik nagłówkowy definiujący klasę bazową skanera C++, FlexLexer i  klasę  pochodną,
              yyFlexLexer.

       flex.skl
              skaner  szkieletowy.  Plik  ten  jest używany tylko przy budowaniu fleksa, nie przy
              jego uruchamianiu.

       lex.backup
              informacje wspierające (backing-up) dla flagi -b (nazywany jest mianem  lex.bck  na
              niektórych systemach).

NIEDOSTATKI / BŁĘDY

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

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

       Łączenie  wiszącego  kontekstu  z akcją specjalną '|' może spowodować, że ustalony (fixed)
       wiszący kontekst zostanie zmieniony w bardziej kosztowny,  zmienny  wiszący  kontekst.  Na
       przykład następujące:

           %%
           abc      |
           xyz/def

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

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

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

       Z powodu buforowania wejścia i czytania z  wyprzedzeniem,  nie  można  łączyć  z  regułami
       fleksa wywołań <stdio.h>, np.  getchar().  Zamiast tego wołaj input().

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

       REJECT nie może być używany z opcjami -f lub -F.

       Wewnętrzne algorytmy fleksa wymagają udokumentowania.

ZOBACZ TAKŻE

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

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

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

       Alfred  Aho,  Ravi  Sethi and Jeffrey Ullman, Compilers: Principles, Techniques and Tools,
       Addison-Wesley (1986).   Opisuje  techniki  dopasowywania  wzorców  używane  przez  fleksa
       (deterministyczne automaty skończone).

AUTOR

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

       Podziękowania dla wielu beta testerów, komentatorów  i  kontrybutorów  fleksa,  z  których
       szczególnie  zasłużone  są  następujące  osoby:  Francois  Pinard,  Casey  Leedom,  Robert
       Abramovitz, Stan Adermann, Terry Allen, David Barker-Plummer, John  Basrai,  Neal  Becker,
       Nelson  H.F.  Beebe,  benson@odi.com,  Karl  Berry, Peter A. Bigot, Simon Blanchard, Keith
       Bostic, Frederic Brehm, Ian Brockbank, Kin Cho,  Nick  Christopher,  Brian  Clapper,  J.T.
       Conklin,  Jason  Coughlin, Bill Cox, Nick Cropper, Dave Curtis, Scott David Daniels, Chris
       G. Demetriou, Theo Deraadt, Mike Donahue, Chuck Doucette, Tom Epperly,  Leo  Eskin,  Chris
       Faylor,  Chris  Flatters, Jon Forrest, Jeffrey Friedl, Joe Gayda, Kaveh R. Ghazi, Wolfgang
       Glunz, Eric Goldman, Christopher M. Gould, Ulrich Grepel, Peer Griebel, Jan Hajic, Charles
       Hemphill,  NORO  Hideo,  Jarkko  Hietaniemi,  Scott  Hofmann, Jeff Honig, Dana Hudes, Eric
       Hughes, John Interrante, Ceriel Jacobs, Michal Jaegermann, Sakari  Jalovaara,  Jeffrey  R.
       Jones,  Henry  Juengst,  Klaus  Kaempf,  Jonathan  I.  Kamens, Terrence O Kane, Amir Katz,
       ken@ken.hilco.com, Kevin B. Kenny,  Steve  Kirsch,  Winfried  Koenig,  Marq  Kole,  Ronald
       Lamprecht, Greg Lee, Rohan Lenard, Craig Leres, John Levine, Steve Liddle, David Loffredo,
       Mike Long, Mohamed el Lozy, Brian Madsen, Malte, Joe  Marshall,  Bengt  Martensson,  Chris
       Metcalf,  Luke  Mewburn,  Jim  Meyering,  R.  Alexander Milowski, Erik Naggum, G.T. Nicol,
       Landon Noll, James Nordby, Marc Nozell,  Richard  Ohnemus,  Karsten  Pahnke,  Sven  Panne,
       Roland  Pesch,  Walter  Pelissero, Gaumond Pierre, Esmond Pitt, Jef Poskanzer, Joe Rahmeh,
       Jarmo Raiha, Frederic Raimbault, Pat Rankin,  Rick  Richardson,  Kevin  Rodgers,  Kai  Uwe
       Rommel,  Jim  Roskind, Alberto Santini, Andreas Scherer, Darrell Schiebel, Raf Schietekat,
       Doug Schmidt, Philippe Schnoebelen, Andreas Schwab, Larry Schwimmer, Alex Siegel, Eckehard
       Stolz, Jan-Erik Strvmquist, Mike Stump, Paul Stuart, Dave Tallman, Ian Lance Taylor, Chris
       Thewalt, Richard M. Timoney, Jodi Tsai, Paul Tuinenga, Gary Weik,  Frank  Whaley,  Gerhard
       Wilhelms,  Kent  Williams, Ken Yap, Ron Zellar, Nathan Zelle, David Zuhn, oraz ci, których
       nazwiska wyleciały z moich zdolności archiwizowania poczty, lecz których wkład jest równie
       ważny.

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

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

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

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

INFORMACJE O TŁUMACZENIU

       Powyższe  tłumaczenie  pochodzi  z nieistniejącego już Projektu Tłumaczenia Manuali i może
       nie być aktualne. W  razie  zauważenia  różnic  między  powyższym  opisem  a  rzeczywistym
       zachowaniem  opisywanego  programu  lub  funkcji,  prosimy  o  zapoznanie się z oryginalną
       (angielską) wersją strony podręcznika za pomocą polecenia:

              man --locale=C 1 flex

       Prosimy o pomoc w aktualizacji stron man - więcej informacji  można  znaleźć  pod  adresem
       http://sourceforge.net/projects/manpages-pl/.