Zlecenia.przez.net



NAZWA

       flex - szybki generator analizatora leksykalnego


SKŁADNIA

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


WPROWADZENIE

       Podręcznik ten opisuje narzędzie flex.  Jest ono przeznaczone  do  gen-
       erowania  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

           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  gen-
       erowanego  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 kom-
       pilowany i konsolidowany z  biblioteką  -lfl.   Po  uruchomieniu  pliku
       wykonywalnego,  program  analizuje wejście w poszukiwaniu wyrażeń regu-
       larnych. Gdy tylko takie się znajdzie, wykonywany jest odpowiedni frag-
       ment 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 );
                   }

           /* 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ł.


       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  pod-
       kreślenia  ('_').   Pozostałe  znaki  mogą  być literami, cyframi, pod-
       kreśleniami lub myślnikami.  Definicja jest pobierana od momentu pojaw-
       ienia  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 dokle-
       jonymi 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 ewen-
       tualnie 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 pomoc-
       niczych, 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  (inden-
       towany) tekst lub tekst ujęty w %{ i %}, to jest on kopiowany dosłownie
       na wyjście (po usunięciu %{}).  Znaki %{} muszą pojawić się  samodziel-
       nie w liniach bez wcięć.

       W  sekcji reguł, tekst wcięty lub tekst %{}, znajdujący się przed pier-
       wszą regułą może służyć deklarowaniu zmiennych lokalnych dla  procedury
       Wzorce wejściowe są pisane z użyciem rozszerzonego zestawu wyrażeń reg-
       ularnych. 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,

           <*>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ń  regu-
       larnych  tracą  swoje  znaczenie  specjalne  (nie licząc cytowania '\',
       znaków klasy '-',

       Wymienione wyżej wyrażenia regularne są pogrupowane zgodnie z prioryte-
       tami,  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ć spec-
       jalne 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 stan-
       dardowi funkcji isXXX języka C. Przykładowo [:alnum:] oznacza wszystkie
       znaki,  dla których isalnum(3) zwraca prawdę - tj. wszelkie znaki alfa-
       betyczne 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]
              nia zanegowanych klas znaków przez inne narzędzia  operujące  na
              wyrażeniach  regularnych,  lecz niestety niespójność jest ugrun-
              towana historycznie.   Dopasowywanie  nowej  linii  oznacza,  że
              wzorzec  w  rodzaju  [^"]* może dopasować się do całego wejścia,
              chyba że istnieje w nim drugi cudzysłów.

       -      Reguła może mieć najwyżej jedną instancję dowiązanego  kontekstu
              (operatory  się  tylko  na początku wzorca i dodatkowo, podobnie
              jak '/' i '$', nie mogą być grupowane w nawiasy. Znak '^', który
              nie  pojawia się na początku reguły, lub '$', nie znajdujący się
              na końcu traci swoje specjalne znaczenie.

              Następujące wzorce są niedozwolone:

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

              Zauważ, że pierwszy z nich może być zapisany jako "foo/bar\n".

              Następujące wzorce powodują, że '$' lub '^'  są  traktowane  jak
              zwykłe znaki:

                  foo|(bar$)
                  foo|^bar

              Jeśli oczekiwaną wartością jest "foo" lub "bar-z-nową-linią", to
              użyć  można  następującego  wzorca  (akcja  specjalna   |   jest
              wyjaśniona niżej):

                  foo      |
                  bar$     /* tu rozpoczyna się akcja */

              Podobna  sztuczka  powinna  zadziałać  dla dopasowywania foo lub
              bar-na-początku-linii.


JAK DOPASOWYWANE JEST WEJŚCIE

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

       Po określeniu dopasowania, tekst dopasowania (zwany dalej tokenem) jest
       udostępniany  we wskaźnikowej zmiennej globalnej yytext, a jego długość
       w  globalnej  zmiennej  całkowitej   yyleng.    Wykonywana   jest   też
       odpowiadająca  wzorcowi  akcja  (szczegółowy  opis akcji jest dalej), a
       następnie  pozostała  część  wejścia  jest  dopasowywana  do  kolejnego
       wzorca.

       Jeśli  dopasowanie  nie  zostanie  znalezione, wykonana zostanie reguła
       domyślna: następny znak wejścia jest uważany za dopasowany i  kopiowany
       na  stdout.   Tak więc najprostszym poprawnym plikiem wejściowym fleksa
       jest:
       staje się tablicą.  Korzyścią z używania %pointer jest zwiększenie szy-
       bkoś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 prze-
       chowywać  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 poje-
       dynczej 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,
       return.

       Akcje  mogą  spokojnie  modyfikować zmienną yytext; nie mogą jej jednak
       wydłużać (dodawanie znaków do jej końca  nadpisze  dalsze  znaki  stru-
       mienia  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"  reg-
              ule,  która  została  dopasowana do wzorca wejściowego (lub pre-
              fiksu 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 poprzed-
              nia, 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).
                  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 szy-
       bkoś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  kole-
              jnego  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

                  %%
                  "/*"        {
                              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  dopa-
       sowywania  tokenów  i  parę  procedur  i  makr.  Domyślnie yylex() jest
       deklarowany jako

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


       Przy każdym wywołaniu yylex(), następuje skanowanie tokenów  z  global-
       nego  pliku wejściowego yyin (który domyślnie wskazuje na stdin). Wczy-
       tywanie 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ą niezdefin-
       iowane.  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 kom-
       patybilność  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.

       (domyślnie  stdout),  który można przedefiniować dzięki zwykłemu przyp-
       isaniu 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();


           %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(INI-
       TIAL).  (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",
                       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 dopa-
       sowywać 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  przyp-
       isania do comment_caller można by zapisać jako

           comment_caller = YY_START;

           %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++;
                   }

       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  wbu-
       dowanych  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 )

       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  dopa-
       sowania 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 )
                       {

                   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

       -      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 czy-
       tającej tablice danych lub otwierającej plik raportowy.

       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ść argu-
       mentu 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ć mody-
              fikowany, 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  charyy-
              text[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 rezul-
              taty 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 tra-
              cone). Zauważ, że wołanie yyrestart() z argumentem yyin  porzuca
              do powrotu do tego warunku.


ŁĄCZENIE Z YACC

       Jednym z podstawowych zastosowań fleksa jest współtowarzyszenie genera-
       torowi  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 zapa-
              sowe. 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).

              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ę wyda-
              jności; używanie yymore(), operatora ^ i flagi -I powoduje  pom-
              niejsze 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 standard-
              owe 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 kluc-
              zowe" jak i łapiącą-wszystko regułę  "identyfikatora",  tak  jak
              poniższy zestaw:

                  "case"    return TOK_CASE;

       -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ć jed-
              nej  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 przy-
              padkó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  komu-
              nikaty o błędach w akcjach będą poprawnie położone względem ory-
              ginalnego 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 kom-
              presji  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


       -+     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  oper-
              owanie  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ą  wstaw-
              ione  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  reprezen-
              tacji  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 skompre-
              sowanych tablic, lecz mają one już umiarkowany  wpływ  na  wyda-
              jność  (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 rzeczy-
              wistoś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 skan-
              era  (skaner  pominie  tekst  pozostawiony przez twoje odczyty w
              buforze wejściowym stdio).

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

              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 najszyb-
              ciej generowane i kompilowane,  więc  podczas  prac  rozwojowych
              prawdopodobnie  najchętniej  użyjesz domyślnej, maksymalnej kom-
              presji.

              -Cfe jest często dobrym kompromisem między szybkością  a  rozmi-
              arem 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 zapisy-
              wany  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 wszyst-
              kich  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.


       flex daje też mechanizm kontrolowania opcji z samej specyfikacji  skan-
       era,  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 opcjon-
       alnie  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  skan-
              era  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  skan-
              era,  który po prostu woła yylex().  Opcja ta implikuje noyywrap
              (zobacz niżej).

              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 wygen-
       erowanym 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
           %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  pro-
       cedurowe,  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 wyda-
       jnoś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 aktu-
       alnie 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).

           %%
           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ę jed-
       nak  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).
                   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 */

           ... 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 poprzed-
       nim 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 przyj-
       mowania  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++.  Pier-
       wszą  z  nich jest proste skompilowanie fleksowego skanera kompilatorem
       C++ zamiast kompilatora C. Nie powinieneś napotkać żadnych błędów  kom-
       pilacji  (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++.

              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  yyli-
              neno), 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 poinfor-
              mować 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:
              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 pro-
              gramu.

       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 (reen-
       trant)  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);

           %%

           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 imple-
       mentacje nie mają wspólnego kodu). Posiada pewne rozszerzenia  i  niez-
       godnoś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 yyli-
              neno 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.

              żadnego sposobu sterowania wejściem skanera  niż  poprzez  doko-
              nanie 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 komu-
              nikat:

                  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  wykony-
              wane 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  opera-
              torom 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.
              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 dopa-
              sowania  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

       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ć dopa-
       sowana)  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  pojaw-
       iają  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 flek-
       sowi, ż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

       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 pier-
       wszej 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 kontek-
       stu.  (Zauważ, że projekt POSIX-a określa, że dopasowany w takich wzor-
       cach tekst jest niezdefiniowany.)

       Dla  niektórych  reguł  wiszącego  kontekstu, części które są w rzeczy-
       wistoś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  kosz-
       towny, 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.
       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 Asso-
       ciates.  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, Tech-
       niques  and Tools, Addison-Wesley (1986).  Opisuje techniki dopasowywa-
       nia 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  tabl-
       icy 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, ben-
       son@odi.com, Karl Berry, Peter A. Bigot, Simon Blanchard, Keith Bostic,
       Frederic  Brehm,  Ian Brockbank, Kin Cho, Nick Christopher, Brian Clap-
       per, 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 Mew-
       burn,  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  Raim-
       bault,  Pat Rankin, Rick Richardson, Kevin Rodgers, Kai Uwe Rommel, Jim
       Roskind, Alberto Santini, Andreas Scherer, Darrell Schiebel, Raf  Schi-
       etekat,  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,
       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 wszys-
       tkich za wsparcie, które uzyskałem.

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





R E K L A M A Znudzony swoją pracą? Zleć ją innym www.zlecenia.przez.net