FLEX(1)                                                   FLEX(1)


NAZWA
       flex - szybki generator analizatora leksykalnego

SKŁADNIA
       flex  [-bcdfhilnpstvwBFILTV78+? -C[aefFmr] -ooutput -Ppre­
       fix -Sskeleton] [--help --version] [filename ...]

WPROWADZENIE
       Podręcznik ten opisuje narzędzie flex.  Jest ono przeznac­
       zone  do generowania programów, dokonywujących dopasowywa­
       nia 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"



Wersja 2.5                Kwiecień 1995                         1





FLEX(1)                                                   FLEX(1)


           Kwestie wydajnościowe
            jak przyspieszać skanery

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

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

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

           Pliki
            pliki używane przez flex

           Niedostatki / Błędy
            znane problemy fleksa

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

           Autor
            informacja kontaktu z autorem


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

PROSTE PRZYKŁADY
       Przedstawmy teraz trochę prostych przykładów aby obyć  się
       z używaniem flex.  Następujący plik wejściowy flex określa
       skaner, który za każdym razem gdy napotka  łańcuch  "user­
       name", 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 pojaw­
       ienia "username".  W tym przykładzie  wejścia  mamy  tylko
       jedną  regułę.  Wzorcem  jest  "username",  a  akcją  jest



Wersja 2.5                Kwiecień 1995                         2





FLEX(1)                                                   FLEX(1)


       "printf".  Znaki "%%" oznaczają początek reguł.

       Oto kolejny prosty przykład:

                   int num_lines = 0, num_chars = 0;

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

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

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

       A oto trochę bardziej skomplikowany przykład:

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

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

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

           %%

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

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

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



Wersja 2.5                Kwiecień 1995                         3





FLEX(1)                                                   FLEX(1)


                       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  infor­
       muje co zobaczył.

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

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

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

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

       Definicje nazw mają postać:

           nazwa definicja




Wersja 2.5                Kwiecień 1995                         4





FLEX(1)                                                   FLEX(1)


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

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

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

           {DIGIT}+"."{DIGIT}*

       jest równoważne

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

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

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

           wzorzec   akcja

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

       Dla dalszego opisu akcji patrz dalej.

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

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

       W sekcji reguł, tekst wcięty lub tekst %{}, znajdujący się
       przed pierwszą regułą może służyć  deklarowaniu  zmiennych
       lokalnych  dla procedury skanującej oraz (po deklaracjach)
       kodu, który ma  być  wywoływany  za  każdym  uruchomieniem



Wersja 2.5                Kwiecień 1995                         5





FLEX(1)                                                   FLEX(1)


       procedury skanującej.  Pozostałe przypadki wciętego tekstu
       lub tekstu %{} sekcji reguł są nadal kopiowane na wyjście,
       lecz  ich znaczenie nie jest dokładnie zdefiniowane i mogą
       spowodować błędy kompilacji (właściwość ta jest obecna dla
       zgodności   z   POSIX;   zobacz   niżej   inne  tego  typu
       właściwości).

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

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

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


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


           r|s        r lub s


           r/s        r, lecz tylko jeśli za nim następuje s. Tekst dopasowywany
                  przez s jest załączany do określania czy ta reguła miała
                  "najdłuższe dopasowanie", lecz potem jest zwracany do



Wersja 2.5                Kwiecień 1995                         6





FLEX(1)                                                   FLEX(1)


                  wejścia przed wykonaniem akcji. Tak więc akcja widzi tylko
                  tekst dopasowany przez r. Ten rodzaj wzorca jest nazywany
                  "doklejonym kontekstem". (Istnieją pewne kombinacje r/s,
                  których flex nie potrafi właściwie dopasować; zobacz uwagi
                  w dalszej sekcji Niedostatki / Błędy w okolicach
                  "niebezpiecznego kontekstu doklejonego".)
           ^r         r, lecz tylko na początku linii (tj. zaraz po rozpoczęciu
                  skanowania, lub po wyskanowaniu nowej linii).
           r$         r, lecz tylko na końcu linii (tj. tuż przed nową linią).
                  Równoważne "r/\n".

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


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


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

       Zauważ, że w  obrębie  klasy  znaków  wszystkie  operatory
       wyrażeń  regularnych  tracą swoje znaczenie specjalne (nie
       licząc cytowania '\', znaków klasy '-',

       Wymienione wyżej wyrażenia regularne są pogrupowane  zgod­
       nie  z  priorytetami, licząc od najwyższego do najniższego
       (z góry na dół). Te, które zgrupowano razem mają jednakowy
       priorytet. Na przykład,

           foo|bar*

       jest równoważne

           (foo)|(ba(r*))

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

           foo|(bar)*

       a żeby dopasować zero lub więcej "foo"-lub-"bar"'ów:



Wersja 2.5                Kwiecień 1995                         7





FLEX(1)                                                   FLEX(1)


           (foo|bar)*


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

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

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

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

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

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

       Trochę uwag o wzorcach:

       -      Zanegowana klasa znaków, taka jak wyżej  wymienione
              przykładowe "[^A-Z]" będzie pasować do nowej linii,
              chyba że "\n" (lub równoważna sekwencja  specjalna)
              jest  jednym z jawnie obecnych w klasie znaków (np.
              "[^A-Z\n]").  Odbiega  to  od  sposobu  traktowania
              zanegowanych  klas znaków przez inne narzędzia ope­
              rujące na wyrażeniach  regularnych,  lecz  niestety
              niespójność  jest  ugruntowana historycznie.  Dopa­
              sowywanie 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



Wersja 2.5                Kwiecień 1995                         8





FLEX(1)                                                   FLEX(1)


              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  dopasowywa­
              nia 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 glob­
       alnej  yytext,  a  jego  długość  w   globalnej   zmiennej
       całkowitej  yyleng.   Wykonywana  jest  też  odpowiadająca
       wzorcowi akcja (szczegółowy  opis  akcji  jest  dalej),  a
       następnie  pozostała  część  wejścia  jest dopasowywana do
       kolejnego wzorca.

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

           %%

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




Wersja 2.5                Kwiecień 1995                         9





FLEX(1)                                                   FLEX(1)


       Zauważ, że yytext może być  definiowane  na  dwa  sposoby:
       jako wskaźnik do znaków lub jako tablica znaków.  Używanie
       konkretnej definicji można kontrolować, włączając do pliku
       wejściowego   w   pierwszej   sekcji  specjalne  dyrektywy
       %pointer lub %array.   Domyślnie  używana  jest  dyrektywa
       %pointer, chyba że używa się opcji -l zgodności z leksem i
       wtedy yytext staje  się  tablicą.   Korzyścią  z  używania
       %pointer  jest zwiększenie szybkości skanowania i zlikwid­
       owanie  przepełnień  bufora  przy   dopasowywaniu   dużych
       tokenów  (chyba  że  zabraknie pamięci dynamicznej).  Wadą
       jest ograniczenie sposobu modyfikowania przez akcje zmien­
       nej  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 wspomni­
       ano wyżej, dla %pointer  yytext  wzrasta  dynamicznie,  by
       przechowywać  duże  tokeny.  Chociaż oznacza to, że skaner
       %pointer może zbierać duże  tokeny  (jak  np.  całe  bloki
       komentarzy),  to zakop sobie w pamięci, że za każdym razem
       gdy  skaner  zmienia  rozmiar  yytext  to   musi   również
       reskanować cały token od początku, więc może się to okazać
       powolne.  yytext w chwili obecnej nie zwiększa dynamicznie
       rozmiaru  jeśli  wywołanie  unput() powoduje wepchnięcie z
       powrotem zbyt dużego bloku tekstu.  Zamiast  tego  pojawia
       się błąd wykonania.

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

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

           %%
           "wytnij mnie"

       (Wszystkie  pozostałe  znaki wejścia zostaną skopiowane na



Wersja 2.5                Kwiecień 1995                        10





FLEX(1)                                                   FLEX(1)


       wyjście, gdyż dopasują się do reguły domyślnej.)

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

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


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

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

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

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

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

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

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

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

       -      REJECT  Kieruje  skaner  na  działanie  w  "drugiej
              najlepszej"  regule,  która  została  dopasowana do
              wzorca wejściowego (lub prefiksu  wejścia).  Reguła
              jest  wybierana według zasad opisanych w "Jak dopa­
              sowywane   jest   wejście",   po   czym   następuje



Wersja 2.5                Kwiecień 1995                        11





FLEX(1)                                                   FLEX(1)


              odpowiednie ustawienie yytext oraz yyleng.  Może to
              być albo ta reguła, która dopasowała się do  takiej
              samej ilości tekstu, jak poprzednia, lecz wystąpiła
              później w pliku wejściowym fleksa, albo taka, która
              dopasowała  się  do  mniejszej  ilości  tekstu.  Na
              przykład, następujący przykład będzie liczył  słowa
              wejściowe  i  wołał  funkcję  special() dla każdego
              "frob":

                          int word_count = 0;
                  %%

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

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

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

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

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

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

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

              Pierwsze  "mega-"  jest dopasowane i wydrukowane na



Wersja 2.5                Kwiecień 1995                        12





FLEX(1)                                                   FLEX(1)


              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 mody­
       fikuj tej zmiennej.  Po drugie, obecność yymore() w  akcji
       skanera wpływa na pewne pogorszenie wydajności w szybkości
       dokonywania przez skaner dopasowań.

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

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

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

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

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

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

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



Wersja 2.5                Kwiecień 1995                        13





FLEX(1)                                                   FLEX(1)


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

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

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

                  %%
                  "/*"        {
                              register int c;

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

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

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

              (Zauważ,   że  jeśli  skaner  jest  skompilowany  z
              użyciem C++, to input() nazywa się yyinput().  Jest
              tak w celu zapobieżenia zderzeniu nazwy ze strumie­
              niem 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



Wersja 2.5                Kwiecień 1995                        14





FLEX(1)                                                   FLEX(1)


              sekcji Wielokrotne Bufory Wejściowe.

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

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

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

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

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

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

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

       Jeśli  skaner osiąga koniec pliku, to kolejne wywołania są
       niezdefiniowane.   Sposobem  na  skorygowanie  tego   jest
       przekierowanie  yyin na nowy plik wejściowy (w tym wypadku
       skanowanie  następuje  z  nowego  pliku)   lub   wywołanie
       yyrestart().  yyrestart() pobiera jeden argument: wskaźnik
       FILE * (który może być nil, jeśli  ustawiłeś  YY_INPUT  na
       skanowanie ze źródła innego niż yyin), i inicjalizuje yyin
       na początek tego pliku. W zasadzie nie ma  różnicy  między
       zwykłym  przypisaniem  yyin  do  nowego  pliku  i  użyciem
       yyrestart(); Procedura ta jest dostępna z  uwagi  na  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  argu­
       mentem  yyin;  lepszym  rozwiązaniem  jest  jednak  użycie



Wersja 2.5                Kwiecień 1995                        15





FLEX(1)                                                   FLEX(1)


       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  poje­
       dynczych  getc()  wykonuje odczyty blokowe z yyin.  Sposób
       pobierania wejścia  może  być  kontrolowany  przez  defin­
       iowanie  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 sys­
       temach uniksowych), określającej EOF. Domyślnie,  YY_INPUT
       czyta z globalnego wskaźnika "yyin".

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

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

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

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

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

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

       Swoje   wyjście   ECHO   skaner   zapisuje  do  globalnego



Wersja 2.5                Kwiecień 1995                        16





FLEX(1)                                                   FLEX(1)


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

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

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

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

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

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

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

       W rozróżnieniu warunków włączających i wykluczających ist­
       nieje  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




Wersja 2.5                Kwiecień 1995                        17





FLEX(1)                                                   FLEX(1)


           %x przyklad
           %%

           <przyklad>foo   rob_cos();

           <INITIAL,przyklad>bar    cos_innego();

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

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

           %x przyklad
           %%

           <przyklad>foo   rob_cos();

           <*>bar    cos_innego();


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

           <*>.|\n     ECHO;


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

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

                   int enter_special;

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



Wersja 2.5                Kwiecień 1995                        18





FLEX(1)                                                   FLEX(1)


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


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

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

           %%
           expect-floats        BEGIN(expect);

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

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

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

       Oto skaner, który rozpoznaje komentarze C podczas  zlicza­
       nia 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



Wersja 2.5                Kwiecień 1995                        19





FLEX(1)                                                   FLEX(1)


       ilości tekstu w każdej z reguł. Ogólnie, przy pisaniu szy­
       bkich  skanerów,  próbuj dopasowywać w każdej regule tyle,
       ile się da.

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

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

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

           ...

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

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

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

           comment_caller = YY_START;

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

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

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

           %x str

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




Wersja 2.5                Kwiecień 1995                        20





FLEX(1)                                                   FLEX(1)


           \"      string_buf_ptr = string_buf; BEGIN(str);

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

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

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

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

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

                   *string_buf_ptr++ = result;
                   }

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

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

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

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

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


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



Wersja 2.5                Kwiecień 1995                        21





FLEX(1)                                                   FLEX(1)


           <SCs>{

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

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

       jest równoważne:

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

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

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

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

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

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

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

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

WIELOKROTNE BUFORY WEJŚCIOWE
       Niektóre   skanery   (te,   obsługujące   pliki  dołączane
       "include") wymagają odczytu z wielu strumieni wejściowych.
       Ponieważ  skanery  flex  wykonują  sporo  buforowania, nie



Wersja 2.5                Kwiecień 1995                        22





FLEX(1)                                                   FLEX(1)


       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 mecha­
       nizm  tworzenia  i   przełączania   między   wielokrotnymi
       buforami  wejściowymi.  Bufor  wejściowy  jest  tworzony z
       użyciem funkcji

           YY_BUFFER_STATE yy_create_buffer( FILE *file, int size )

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

           void yy_switch_to_buffer( YY_BUFFER_STATE new_buffer )

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

           void yy_delete_buffer( YY_BUFFER_STATE buffer )

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

           void yy_flush_buffer( YY_BUFFER_STATE buffer )

       Funkcja ta niszczy zawartość bufora, więc  przy  następnej
       próbie   dopasowania  tokenu  z  bufora,  skaner  najpierw



Wersja 2.5                Kwiecień 1995                        23





FLEX(1)                                                   FLEX(1)


       wypełni bufor na nowo używając YY_INPUT.

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

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

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

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

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

           %%
           include             BEGIN(incl);

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

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

                   include_stack[include_stack_ptr++] =
                       YY_CURRENT_BUFFER;

                   yyin = fopen( yytext, "r" );

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

                   yy_switch_to_buffer(
                       yy_create_buffer( yyin, YY_BUF_SIZE ) );

                   BEGIN(INITIAL);
                   }

           <<EOF>> {
                   if ( --include_stack_ptr < 0 )



Wersja 2.5                Kwiecień 1995                        24





FLEX(1)                                                   FLEX(1)


                       {
                       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 proce­
       dury. Każda z nich tworzy nowy bufor wejściowy do skanowa­
       nia ł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 orygi­
       nalnych 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 rzu­
              tuje się wyrażenie całkowite,  określające  rozmiar
              bufora.

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



Wersja 2.5                Kwiecień 1995                        25





FLEX(1)                                                   FLEX(1)


       rzeczy:

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

       -      wywołaniem instrukcji return;

       -      wywołaniem specjalnej akcji yyterminate();

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

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

           <INITIAL><<EOF>>


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

           %x quote
           %%

           ...inne reguły cytatowe...

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


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




Wersja 2.5                Kwiecień 1995                        26





FLEX(1)                                                   FLEX(1)


           #define YY_USER_ACTION ++ctr[yy_act]

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

           int ctr[YY_NUM_RULES];


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

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

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

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

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

WARTOŚCI DOSTĘPNE DLA UŻYTKOWNIKA
       Sekcja ta zestawia różne wartości dostępne dla użytkownika
       w akcjach regułowych.



Wersja 2.5                Kwiecień 1995                        27





FLEX(1)                                                   FLEX(1)


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

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

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

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

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

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

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

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

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

ŁĄCZENIE Z YACC
       Jednym   z    podstawowych    zastosowań    fleksa    jest



Wersja 2.5                Kwiecień 1995                        28





FLEX(1)                                                   FLEX(1)


       współtowarzyszenie  generatorowi analizatorów yacc.  Anal­
       izatory 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 yyl­
       val.  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 skan­
       era 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  zapa­
              sowych   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ą  jed­
              nakowo z punktu widzenia skanera) lub osiąga koniec
              pliku.



Wersja 2.5                Kwiecień 1995                        29





FLEX(1)                                                   FLEX(1)


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

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

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

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

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

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

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

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

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

       -v     nakazuje  fleksowi  pisanie  na  stderr zestawienia



Wersja 2.5                Kwiecień 1995                        30





FLEX(1)                                                   FLEX(1)


              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,   gen­
              erowanych  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 szy­
              bkiego  skanera  (i  stdio   ma   być   pominięte).
              Reprezentacja  ta  jest mniej więcej tak szybka jak
              reprezentacja pełnej tablicy (-f), i dla niektórych
              zestawów  wzorców  będzie  znacznie mniejsza (a dla
              innych większa).  Ogólnie,  jeśli  wzorzec  zawiera
              zarówno  "słowa  kluczowe"  jak  i łapiącą-wszystko
              regułę "identyfikatora", tak jak poniższy zestaw:

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

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

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

       -I     nakazuje fleksowi  generowanie  skanera  interakty­
              wnego.    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ą wyda­
              jność  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.



Wersja 2.5                Kwiecień 1995                        31





FLEX(1)                                                   FLEX(1)


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

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

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

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

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

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

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



Wersja 2.5                Kwiecień 1995                        32





FLEX(1)                                                   FLEX(1)


              opisano wyżej.

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

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


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

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

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

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

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

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

              -Cm  nakazuje   fleksowi   budowanie   klas   meta-
              równoważności, które są zbiorami klas równoważności



Wersja 2.5                Kwiecień 1995                        33





FLEX(1)                                                   FLEX(1)


              (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 skom­
              presowanych  tablic,  lecz mają one już umiarkowany
              wpływ na wydajność (dwa lub jeden test "if" i jedno
              podglądnięcie tablicy na skanowany znak).

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

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

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

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

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

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

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




Wersja 2.5                Kwiecień 1995                        34





FLEX(1)                                                   FLEX(1)


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

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

       -Pprefiks
              zmienia  domyślny  przedrostek  yy  używany   przez
              fleksa  dla  wszystkich zmiennych i funkcji global­
              nych 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 skan­
              era  można  wciąż używać jednej i drugiej konwencji
              nazywania; jednak z  zewnątrz  dozwolone  są  tylko
              nazwy zmodyfikowane.

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

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



Wersja 2.5                Kwiecień 1995                        35





FLEX(1)                                                   FLEX(1)


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

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

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

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

           case-insensitive lub
           caseless        -i

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

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

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

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




Wersja 2.5                Kwiecień 1995                        36





FLEX(1)                                                   FLEX(1)


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

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

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

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

       yylineno
              nakazuje  fleksowi generowanie skanera, który prze­
              chowuje 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  skanowa­
              nia (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"




Wersja 2.5                Kwiecień 1995                        37





FLEX(1)                                                   FLEX(1)


       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 pro­
       cedura nie pojawia się w wygenerowanym skanerze:

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

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

ROZWAŻANIA NAD WYDAJNOŚCIĄ
       Podstawowym  zadaniem  przy  projektowaniu   fleksa   było
       zapewnienie,  że  będzie generował wydajne skanery. Został
       zoptymalizowany do dobrej współpracy z wielkimi  zestawami
       reguł.  Poza  omawianymi  już wpływami opcji kompresji -C,
       istnieje jeszcze kilka akcji/opcji wpływających  na  wyda­
       jność.  Są  to,  od  najkosztowniejszej  do najmniej kosz­
       townej:

           REJECT
           %option yylineno
           arbitralny wiszący kontekst

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

           '^' operator rozpoczęcia linii
           yymore()

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

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

       Pozbycie  się  cofania jest trudne i może często prowadzić
       do błędów w skomplikowanych skanerach. W praktyce  zaczyna



Wersja 2.5                Kwiecień 1995                        38





FLEX(1)                                                   FLEX(1)


       się  od użycia flagi -b do wygenerowania pliku lex.backup.
       Na przykład dla wejścia

           %%
           foo        return TOK_KEYWORD;
           foobar     return TOK_KEYWORD;

       plik ten wygląda tak:

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

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

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

           Compressed tables always back up.

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

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

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

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



Wersja 2.5                Kwiecień 1995                        39





FLEX(1)                                                   FLEX(1)


           %%
           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 skomp­
       likowanych  zbiorach reguł można dostać setki komunikatów.
       Mimo to, jeśli można  je  zdeszyfrować,  to  ich  usuwanie
       wymaga  tylko  tuzina  reguł  (łatwo  się jednak pomylić i
       spowodować, że reguła  obsługi  błędu  będzie  pasować  do
       prawidłowego  tokena.  Możliwe,  że przyszłe implementacje
       fleksa będą automatycznie zajmowały  się  usuwaniem  cofa­
       nia).

       Ważne jest pamiętanie, że korzyści z eliminacji tego prob­
       lemu zyskujesz dopiero po zlikwidowaniu  każdej  instancji
       cofania.  Pozostawienie choć jednej oznacza, że nie zysku­
       jesz 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ą). Dlat­
       ego gdy tylko można, to zapisz taką regułę:

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

       jako:

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

       lub jako




Wersja 2.5                Kwiecień 1995                        40





FLEX(1)                                                   FLEX(1)


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

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

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

           %x comment
           %%
                   int line_num = 1;

           "/*"         BEGIN(comment);

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

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

           %x comment
           %%
                   int line_num = 1;

           "/*"         BEGIN(comment);

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

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

       Ostateczny  przykład  przyspieszania  skanera: załóżmy, że
       chcesz skanować plik zawierający  identyfikatory  i  słowa
       kluczowe  w  liczbie  jednego na linię, bez żadnych obcych



Wersja 2.5                Kwiecień 1995                        41





FLEX(1)                                                   FLEX(1)


       znaków i  chcesz  rozpoznawać  wszystkie  słowa  kluczowe.
       Naturalnym odruchem początkowym jest:

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

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

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

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

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

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

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

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

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



Wersja 2.5                Kwiecień 1995                        42





FLEX(1)                                                   FLEX(1)


       jego klasyfikacja, możemy wprowadzić regułę  łap-wszystko,
       która nie zawiera nowej linii.

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

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

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

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

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

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

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

       Pierwsza klasa, FlexLexer, daje abstrakcyjną klasę bazową,



Wersja 2.5                Kwiecień 1995                        43





FLEX(1)                                                   FLEX(1)


       definiującą   ogólny   interfejs   klasy   skanera.   Daje
       następujące funkcje składowe:

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

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

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

       void set_debug( int flag )
              ustawia flagę debuggującą dla skanera,  równoważnik
              przypisania do yy_flex_debug (zobacz wyżej sekcję o
              opcjach). Zauważ, że aby włączać w skanerze  infor­
              macje   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ż pier­
       wszym 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 strumie­
              nie 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, kon­
              sumuje tokeny aż akcja reguły nie zwróci  wartości.
              Jeśli  z  yyFlexLexer  wyprowadzisz  podklasę  S  i
              zechcesz  dostać  się  do   funkcji   i   zmiennych
              składowych  S  z  wnętrza  yylex(),  to musisz użyć
              %option  yyclass="S"  by  poinformować  fleksa,  że
              będziesz  używać  podklasy  zamiast yyFlexLexer.  W
              tym wypadku zamiast generować yyFlexLexer::yylex(),
              flex  generuje  S::yylex()  (oraz  generuje  prosty



Wersja 2.5                Kwiecień 1995                        44





FLEX(1)                                                   FLEX(1)


              yyFlexLexer::yylex(), który woła  yyFlexLexer::Lex­
              erError() 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 przypisy­
              wana jest nowa wartość yyin .

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

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

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

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

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

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

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

       Oto przykład prostego skanera C++:

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




Wersja 2.5                Kwiecień 1995                        45





FLEX(1)                                                   FLEX(1)


           %{
           int mylineno = 0;
           %}

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

           ws      [ \t]+

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

           %%

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

           "/*"    {
                   int c;

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

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

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

           \n        mylineno++;

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

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

           %%

           int main( int /* argc */, char** /* argv */ )
               {
               FlexLexer* lexer = new yyFlexLexer;
               while(lexer->yylex() != 0)
                   ;
               return 0;
               }



Wersja 2.5                Kwiecień 1995                        46





FLEX(1)                                                   FLEX(1)


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

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

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

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

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

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

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

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

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

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

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

              yylineno nie jest częścią specyfikacji POSIX.

       -      Procedura  input()  nie jest redefiniowalna chociaż



Wersja 2.5                Kwiecień 1995                        47





FLEX(1)                                                   FLEX(1)


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

              Wejście jest natomiast sterowane przez definiowanie
              makra YY_INPUT.

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

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

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

                  fatal flex scanner internal error--end of buffer missed

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

                  yyrestart( yyin );

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

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

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

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

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

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

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



Wersja 2.5                Kwiecień 1995                        48





FLEX(1)                                                   FLEX(1)


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

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

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

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

              POSIX nakazuje ujmowanie definicji w nawiasy.

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

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

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

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

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

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

       -      Pierwszeństwo operatora ^ jest inne.  lex interpre­
              tuje "^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



Wersja 2.5                Kwiecień 1995                        49





FLEX(1)                                                   FLEX(1)


              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  flek­
              sem   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 specy­
       fikacjach lex ani POSIX:

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

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

           foo    handle_foo(); ++num_foos_seen;

       jest (raczej niespodziewanie) obcinana do

           foo    handle_foo();

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

DIAGNOSTYKA
       warning, rule cannot be matched (ostrzeżenie,  reguła  nie
       może  być  dopasowana) wskazuje, że podana reguła nie może



Wersja 2.5                Kwiecień 1995                        50





FLEX(1)                                                   FLEX(1)


       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-wszys­
       tko:

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

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

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

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

       flex scanner jammed - skaner skompilowany  z  -s  napotkał
       łańcuch wejściowy, który nie został dopasowany do żadnej z
       jego reguł. Błąd ten może się pojawić też z  powodu  prob­
       lemó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 two­
       jego 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 skan­
       era   nie   potrafił  przetrzymać  wepchniętego  tekstu  i
       bieżącego tokena w yytext.  Idealny skaner powinien dynam­
       icznie  zmienić  rozmiar  bufora, lecz obecnie tak się nie
       dzieje.



Wersja 2.5                Kwiecień 1995                        51





FLEX(1)                                                   FLEX(1)


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

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

           yyrestart( yyin );

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

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

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

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

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

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

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

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

NIEDOSTATKI / BŁĘDY
       Niektóre wzorce wiszącego kontekstu nie mogą być poprawnie
       dopasowane  i generują komunikaty ostrzegawcze ("dangerous
       trailing context") (niebezpieczny wiszący kontekst). Są to
       wzorce,  gdzie  zakończenie  pierwszej części reguły dopa­
       sowuje  się  do  początku  drugiej   części,   takie   jak
       "zx*/xy*", gdzie 'x*' dopasowuje 'x' na początku wiszącego



Wersja 2.5                Kwiecień 1995                        52





FLEX(1)                                                   FLEX(1)


       kontekstu.  (Zauważ, że projekt POSIX-a określa, że  dopa­
       sowany w takich wzorcach tekst jest niezdefiniowany.)

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

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

           %%
           abc      |
           xyz/def


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

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

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

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

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

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

       Wewnętrzne algorytmy fleksa wymagają udokumentowania.

ZOBACZ TAKŻE
       lex(1), yacc(1), sed(1), awk(1).

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

       M.  E. Lesk and E. Schmidt, LEX - Lexical Analyzer Genera­
       tor




Wersja 2.5                Kwiecień 1995                        53





FLEX(1)                                                   FLEX(1)


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

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

       Podziękowania dla wielu beta testerów, komentatorów i kon­
       trybutoró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-Plum­
       mer,  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 Clapper, J.T. Conklin, Jason  Coughlin,
       Bill  Cox, Nick Cropper, Dave Curtis, Scott David Daniels,
       Chris G. Demetriou,  Theo  Deraadt,  Mike  Donahue,  Chuck
       Doucette,  Tom  Epperly,  Leo  Eskin,  Chris Faylor, Chris
       Flatters, Jon Forrest, Jeffrey Friedl, Joe Gayda, Kaveh R.
       Ghazi, Wolfgang Glunz, Eric Goldman, Christopher M. Gould,
       Ulrich Grepel, Peer Griebel, Jan Hajic, Charles  Hemphill,
       NORO  Hideo, Jarkko Hietaniemi, Scott Hofmann, Jeff Honig,
       Dana Hudes, Eric Hughes, John Interrante,  Ceriel  Jacobs,
       Michal  Jaegermann,  Sakari  Jalovaara,  Jeffrey R. Jones,
       Henry Juengst, Klaus Kaempf, Jonathan I. Kamens,  Terrence
       O  Kane,  Amir  Katz,  ken@ken.hilco.com,  Kevin B. Kenny,
       Steve Kirsch, Winfried  Koenig,  Marq  Kole,  Ronald  Lam­
       precht,  Greg Lee, Rohan Lenard, Craig Leres, John Levine,
       Steve Liddle, David Loffredo, Mike Long, Mohamed el  Lozy,
       Brian Madsen, Malte, Joe Marshall, Bengt Martensson, Chris
       Metcalf,  Luke  Mewburn,  Jim   Meyering,   R.   Alexander
       Milowski,  Erik  Naggum,  G.T.  Nicol,  Landon Noll, James
       Nordby, Marc Nozell, Richard Ohnemus, Karsten Pahnke, Sven
       Panne,  Roland  Pesch,  Walter  Pelissero, Gaumond Pierre,
       Esmond Pitt, Jef Poskanzer, Joe Rahmeh, Jarmo Raiha, Fred­
       eric   Raimbault,   Pat  Rankin,  Rick  Richardson,  Kevin
       Rodgers, Kai Uwe Rommel,  Jim  Roskind,  Alberto  Santini,
       Andreas  Scherer,  Darrell  Schiebel, Raf Schietekat, Doug
       Schmidt,  Philippe  Schnoebelen,  Andreas  Schwab,   Larry
       Schwimmer,   Alex   Siegel,   Eckehard   Stolz,   Jan-Erik
       Strvmquist, Mike Stump, Paul  Stuart,  Dave  Tallman,  Ian
       Lance  Taylor,  Chris  Thewalt,  Richard  M. Timoney, Jodi
       Tsai, Paul Tuinenga, Gary Weik, Frank Whaley, Gerhard Wil­
       helms,  Kent  Williams, Ken Yap, Ron Zellar, Nathan Zelle,
       David Zuhn, oraz ci, których nazwiska  wyleciały  z  moich
       zdolności  archiwizowania  poczty, lecz których wkład jest
       równie ważny.

       Keith Bostic, Jon Forrest, Noah  Friedman,  John  Gilmore,



Wersja 2.5                Kwiecień 1995                        54





FLEX(1)                                                   FLEX(1)


       Craig  Leres, John Levine, Bob Mulcahy, G.T.  Nicol, Fran­
       cois Pinard,  Rich  Salz  i  Richard  Stallman  pomogli  z
       różnymi problemami dystrybucji.

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

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

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









































Wersja 2.5                Kwiecień 1995                        55