.\" 2000 PTM Przemek Borys
.TH FLEX 1 "Kwiecień 1995" "Wersja 2.5"
.SH NAZWA
flex \- szybki generator analizatora leksykalnego
.SH SKŁADNIA
.B flex
.B [\-bcdfhilnpstvwBFILTV78+? \-C[aefFmr] \-ooutput \-Pprefix \-Sskeleton]
.B [\-\-help \-\-version]
.I [filename ...]
.SH WPROWADZENIE
\fI Uwaga! To tłumaczenie może być nieaktualne!\fP
.PP
Podręcznik ten opisuje narzędzie
.IR flex .
Jest ono przeznaczone do generowania programów, dokonywujących dopasowywania
wzorców na tekście. Podręcznik zawiera zarówno sekcje przewodnikowe jak i
informacyjne.
.nf

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

    Proste Przykłady

    Format Pliku Wejściowego

    Wzorce
	rozszerzone wyrażenia regularne używane przez flex

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

    Akcje
	jak podawać, co robić po dopasowaniu wzorca

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

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

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

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

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

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

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

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

    Kwestie wydajnościowe
	jak przyspieszać skanery

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

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

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

    Pliki
	pliki używane przez flex

    Niedostatki / Błędy
	znane problemy fleksa

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

    Autor
	informacja kontaktu z autorem

.fi
.SH OPIS
.I flex
jest narzędziem przeznaczonym do generowania
.I skanerów:
programów, rozpoznających wzorce leksykalne tekstu.
.I 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ą
.IR regułami .
.I flex
jako wyjście generuje plik źródłowy C o nazwie
.BR lex.yy.c .
Definiuje on funkcję
.B yylex().
Plik ten musi kompilowany i konsolidowany z biblioteką
.BR \-lfl .
Po uruchomieniu pliku wykonywalnego, program analizuje wejście w
poszukiwaniu wyrażeń regularnych. Gdy tylko takie się znajdzie, wykonywany
jest odpowiedni fragment kodu C.
.SH PROSTE PRZYKŁADY
.PP
Przedstawmy teraz trochę prostych przykładów aby obyć się z używaniem
.I flex.
Następujący plik wejściowy
.I flex
określa skaner, który za każdym razem gdy napotka łańcuch
"username", podmieni go nazwą użytkownika:
.nf

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

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

            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 );
            }

.fi
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
.B yylex()
i
.BR 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.
.PP
A oto trochę bardziej skomplikowany przykład:
.nf

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

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

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

    %%

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

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

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

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

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

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

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

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

    %%

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

.fi
Są to początki prostego skanera dla języka podobnego do Pascala. Rozróżnia
poszczególne rodzaje
.I tokenów
i informuje co zobaczył.
.PP
Szczegóły tego przykładu zostaną wyjaśnione w następnych sekcjach.
.SH FORMAT PLIKU WEJŚCIOWEGO
Plik wejściowy
.I fleksa
składa się z trzech sekcji, rozdzielanych liniami z łańcuchem
.BR %% :
.nf

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

.fi
Sekcja
.I definicji
zawiera definicje prostych
.IR nazw ,
upraszczających później specyfikację skanera. Zawiera też deklaracje
.IR "warunków początkowych" ,
które objaśniono w dalszej sekcji.
.PP
Definicje nazw mają postać:
.nf

    nazwa definicja

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

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

.fi
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
.nf

    {DIGIT}+"."{DIGIT}*

.fi
jest równoważne
.nf

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

.fi
i dopasowuje jedną lub więcej cyfr, po których występuje kropka i ewentualnie
następne cyfry.
.PP
Sekcja
.I reguł
wejścia
.I fleksa
zawiera szereg reguł w postaci:
.nf

    wzorzec   akcja

.fi
Przed wzorcem nie może wystąpić wcięcie, a akcja musi rozpoczynać się w tej samej
linii.
.PP
Dla dalszego opisu akcji patrz dalej.
.PP
W końcu, sekcja kodu użytkownika jest zwyczajnie kopiowana do
.B 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
.B %%
pliku wejściowego może być pominięty.
.PP
Jeśli w sekcjach definicji lub reguł znajduje się jakiś
.I wcięty
(indentowany) tekst lub tekst ujęty w
.B %{
i
.BR %} ,
to jest on kopiowany dosłownie na wyjście (po usunięciu %{}).
Znaki %{} muszą pojawić się samodzielnie w liniach bez wcięć.
.PP
W sekcji reguł, tekst wcięty lub tekst %{}, znajdujący się przed pierwszą
regułą może służyć deklarowaniu zmiennych lokalnych dla procedury
skanującej oraz (po deklaracjach) kodu, który ma być wywoływany za każdym
uruchomieniem procedury skanującej.
Pozostałe przypadki wciętego tekstu lub tekstu %{} sekcji reguł są nadal
kopiowane na wyjście, lecz ich znaczenie nie jest dokładnie zdefiniowane i
mogą spowodować błędy kompilacji (właściwość ta jest obecna dla zgodności z
.IR POSIX ;
zobacz niżej inne tego typu właściwości).
.PP
W sekcji definicji na wyjście kopiowane są również nie-wcięte bloki 
komentarza, ujęte między znaki "/*" i "*/".
.SH WZORCE
Wzorce wejściowe są pisane z użyciem rozszerzonego zestawu wyrażeń
regularnych. Są to:
.nf

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


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


    r|s        r lub s


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

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


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


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

.fi
Zauważ, że w obrębie klasy znaków wszystkie operatory wyrażeń regularnych
tracą swoje znaczenie specjalne (nie licząc cytowania '\\', znaków klasy '-',
\&']' oraz '^' na początku klasy).
.PP
Wymienione wyżej wyrażenia regularne są pogrupowane zgodnie z priorytetami,
licząc od najwyższego do najniższego (z góry na dół). Te, które zgrupowano
razem mają jednakowy priorytet. Na przykład,
.nf

    foo|bar*

.fi
jest równoważne
.nf

    (foo)|(ba(r*))

.fi
ponieważ operator '*' ma wyższy priorytet niż łączenie, a łączenie ma wyższy
priorytet niż alternatywa ('|'). Wzorzec ten pasuje więc
.I albo
do łańcucha "foo"
.I 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:
.nf

    foo|(bar)*

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

    (foo|bar)*

.fi
.PP
Poza znakami i zakresami znaków, klasy znaków mogą też zawierać specjalne
.I wyrażenia.
Wyrażenia te są ujmowane w ograniczniki
.B [:
i
.B :]
(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ą:
.nf

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

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

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

.fi
Jeśli twój skaner jest niewrażliwy na wielkość znaków (flaga
(flaga
.BR \-i ),
to
.B [:upper:]
i
.B [:lower:]
są równoważne
.BR [:alpha:] .
.PP
Trochę uwag o wzorcach:
.IP -
Zanegowana klasa znaków, taka jak wyżej wymienione przykładowe "[^A-Z]"
.IR "będzie pasować do nowej linii" ,
chyba że "\\n" (lub równoważna sekwencja specjalna) jest jednym z jawnie
obecnych w klasie znaków (np. "[^A-Z\\n]"). Odbiega to od sposobu traktowania
zanegowanych klas znaków przez inne narzędzia operujące na wyrażeniach
regularnych, lecz niestety niespójność jest ugruntowana historycznie.
Dopasowywanie nowej linii oznacza, że wzorzec w rodzaju [^"]* może dopasować
się do całego wejścia, chyba że istnieje w nim drugi cudzysłów.
.IP -
Reguła może mieć najwyżej jedną instancję dowiązanego kontekstu (operatory
\&'/' lub '$'). Wzorce warunku początkowego '^' oraz "<<EOF>>" mogą pojawić
się tylko na początku wzorca i dodatkowo, podobnie jak '/' i '$', nie mogą
być grupowane w nawiasy. Znak '^', który nie pojawia się na początku reguły,
lub '$', nie znajdujący się na końcu traci swoje specjalne znaczenie.
.IP
Następujące wzorce są niedozwolone:
.nf

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

.fi
Zauważ, że pierwszy z nich może być zapisany jako "foo/bar\\n".
.IP
Następujące wzorce powodują, że '$' lub '^' są traktowane jak zwykłe znaki:
.nf

    foo|(bar$)
    foo|^bar

.fi
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):
.nf

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

.fi
Podobna sztuczka powinna zadziałać dla dopasowywania foo lub
bar-na-początku-linii.
.SH 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.
.PP
Po określeniu dopasowania, tekst dopasowania (zwany dalej
.IR tokenem )
jest udostępniany we wskaźnikowej zmiennej globalnej
.BR yytext ,
a jego długość w globalnej zmiennej całkowitej
.BR yyleng .
Wykonywana jest też odpowiadająca wzorcowi
.I akcja
(szczegółowy opis akcji jest dalej), a następnie pozostała część wejścia
jest dopasowywana do kolejnego wzorca.
.PP
Jeśli dopasowanie nie zostanie znalezione, wykonana zostanie
.IR "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
.I fleksa
jest:
.nf

    %%

.fi
Generuje to skaner, który po prostu kopiuje swoje wejście (jeden znak naraz)
na wyjście.
.PP
Zauważ, że
.B yytext
może być definiowane na dwa sposoby: jako 
.I wskaźnik
do znaków lub jako
.I tablica
znaków.
Używanie konkretnej definicji można kontrolować, włączając do pliku
wejściowego w pierwszej sekcji specjalne dyrektywy
.B %pointer
lub
.BR %array .
Domyślnie używana jest dyrektywa
.BR %pointer ,
chyba że używa się opcji
.B -l
zgodności z leksem i wtedy 
.B yytext
staje się tablicą.
Korzyścią z używania 
.B %pointer
jest zwiększenie szybkości skanowania i zlikwidowanie przepełnień bufora
przy dopasowywaniu dużych tokenów (chyba że zabraknie pamięci dynamicznej).
Wadą jest ograniczenie sposobu modyfikowania przez akcje zmiennej
.B yytext
(zobacz następną sekcję) i to, że wywołania funkcji
.B unput()
niszczą aktualną zawartość
.BR yytext ,
co może przyprawiać o ból głowy podczas portowania skanerów między
różnymi wersjami 
.IR lex .
.PP
Zaletą
.B %array
jest możliwość modyfikowania
.B yytext
i to, że wołanie
.B unput()
nie niszczy
.BR yytext .
Poza tym, istniejące programy
.I lex
czasami zewnętrznie zaglądają do
.B yytext
przy użyciu deklaracji w postaci:
.nf
    extern char yytext[];
.fi
Definicja ta jest błędna przy użyciu z
.BR %pointer ,
lecz prawidłowa dla
.BR %array .
.PP
.B %array
definiuje
.B yytext
jako tablicę
.B YYLMAX
znaków, co domyślnie jest dość dużą wartością. Możesz zmieniać rozmiar przez
proste #definiowanie
.B YYLMAX
na inną wartość w pierwszej sekcji wejściowego pliku
.IR fleksa .
Jak wspomniano wyżej, dla
.B %pointer
yytext wzrasta dynamicznie, by przechowywać duże tokeny. Chociaż
oznacza to, że skaner
.B %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
.B yytext
to musi również reskanować cały token od początku, więc może się to okazać
powolne.
.B yytext
w chwili obecnej
.I nie
zwiększa dynamicznie rozmiaru jeśli wywołanie
.B unput()
powoduje wepchnięcie z powrotem zbyt dużego bloku tekstu. Zamiast
tego pojawia się błąd wykonania.
.PP
Zauważ też, że postaci
.B %array
nie można używać z klasami skanerów C++
(zobacz opcję
.B c++
poniżej).
.SH 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":
.nf

    %%
    "wytnij mnie"

.fi
(Wszystkie pozostałe znaki wejścia zostaną skopiowane na wyjście, gdyż
dopasują się do reguły domyślnej.)
.PP
Oto program, który kompresuje wielokrotne spacje i tabulacje do pojedynczej
spacji. Program wycina też wszystkie białe spacje z końca linii:
.nf

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

.fi
.PP
Jeśli akcja zawiera znak '{', to rozciąga się ona aż do zamykającego '}',
nawet na przestrzeni wielu linii.
.I flex 
ma pewne wiadomości o łańcuchach C i komentarzach, więc nie zostanie
ogłupione przez klamry, które mogą się w nich znajdować. Poza tym dozwolone
są też akcje, które zaczynają się od
.B %{
i zawierają tekst akcji aż do następnego
.B %}
(niezależnie od zwyczajnych klamer wewnątrz akcji).
.PP
Akcja składająca się wyłącznie z pionowej kreski ('|') oznacza
"taka sama, jak akcja następnej reguły". Dla zobrazowania patrz niżej.
.PP
Akcje mogą zawierać kod C, włączając w to instrukcje
.BR return ,
przeznaczone do zwracania wartości do procedury, która wywołała
.BR yylex() .
Przy każdym wywołaniu
.B yylex()
kontynuuje przetwarzanie tokenów od miejsca, w którym ostatnio przerwał
aż do osiągnięcia końca pliku lub wywołania return.
.PP
Akcje mogą spokojnie modyfikować zmienną
.BR yytext ;
nie mogą jej jednak wydłużać (dodawanie znaków do jej końca nadpisze dalsze
znaki strumienia wejściowego). Odmiennie jest natomiast przy używaniu
.B %array
(patrz wyżej); wtedy
.B yytext
można spokojnie modyfikować w dowolny sposób.
.PP
Podobnie do powyższej zmiennej, można spokojnie modyfikować
.BR yyleng ,
lecz należy uważać by nie robić tego jeśli akcja używa 
.B yymore()
(patrz niżej).
.PP
Istnieje wiele dyrektyw specjalnych, które można zawrzeć w akcji:
.IP -
.B ECHO
kopiuje wejście yytext na wyjście skanera.
.IP -
.B BEGIN
z doklejoną nazwą warunku początkowego umieszcza skaner w odpowiednim
warunku początkowym (patrz niżej).
.IP -
.B REJECT
Kieruje skaner na działanie w "drugiej najlepszej" regule, która została
dopasowana do wzorca wejściowego (lub prefiksu wejścia). Reguła jest
wybierana według zasad opisanych w "Jak dopasowywane jest wejście", po czym
następuje odpowiednie ustawienie
.B yytext
oraz
.BR 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":
.nf

            int word_count = 0;
    %%

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

.fi
Bez dyrektywy
.BR 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
.BR 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":
.nf

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

.fi
(Pierwsze trzy reguły mają wspólną akcję z czwartą, gdyż używają akcji specjalnej '|'.)
.B REJECT
jest dość kosztowną właściwością jeśli chodzi o wydajność skanera; jeśli
jest używane w którejś z akcji skanera, to spowolni
.I wszystkie
dopasowania skanera. Co więcej,
.B REJECT
nie może być używany z opcjami
.I -Cf
i
.I -CF
(zobacz niżej).
.IP
Zauważ też, że, w przeciwieństwie do innych akcji specjalnych,
.B REJECT
jest
.IR odgałęzieniem ;
kod akcji występujący bezpośrednio po nim
.I nie
zostanie wykonany.
.IP -
.B yymore()
mówi skanerowi, że przy następnym dopasowaniu reguły, odpowiadający token
powinien być
.I doklejony
do bieżącej wartości
.BR yytext .
Na przykład, przy wejściu "mega-kludge", poniższy przykład na wyjściu
wypisze "mega-mega-kludge":
.nf

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

.fi
Pierwsze "mega-" jest dopasowane i wydrukowane na wyjście. Następnie
dopasowane jest "kludge", lecz poprzednie "mega-" wciąż znajduje się na
początku
.B yytext
i komenda
.B ECHO
dla "kludge" wydrukuje w rzeczywistości "mega-kludge".
.PP
Dwie uwagi na temat
.BR yymore() .
Po pierwsze,
.B yymore()
zależy od wartości
.IR yyleng ,
odzwierciedlającej rozmiar bieżącego tokenu. Zatem jeśli używasz 
.BR yymore() ,
nie modyfikuj tej zmiennej.
Po drugie, obecność
.B yymore()
w akcji skanera wpływa na pewne pogorszenie wydajności w szybkości
dokonywania przez skaner dopasowań.
.IP -
.B yyless(n)
zwraca wszystkie poza pierwszymi
.I n
znakami bieżącego tokenu z powrotem do strumienia wejściowego, skąd
zostaną one powtórnie przeskanowane przy dopasowywaniu następnego wzorca.
.B yytext
i
.B yyleng
są odpowiednio dostrajane (tj.
.B yyleng
będzie teraz równe
.IR n ).
Na przykład, przy wejściu "foobar", następujący kod wypisze "foobarbar":
.nf

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

.fi
Podanie
.B 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.
.BR BEGIN ),
spowoduje to nieskończoną pętlę.
.PP
Zwróć uwagę, że
.B yyless
jest makrem i może być używane tylko z pliku wejściowego fleksa, a nie z
innych plików źródłowych.
.IP -
.B unput(c)
wstawia znak
.I 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.
.nf

    {
    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 );
    }

.fi
Zwróć uwagę, że skoro każdy
.B unput()
wstawia dany znak na
.I początek
strumienia, to wstawianie znaków musi odbywać się tyłem-na-przód.
.PP
Ważnym potencjalnym problemem używania
.B unput()
jest fakt, że jeśli używasz dyrektywy
.B %pointer
(domyślne), wywołanie
.B unput()
.I niszczy
zawartość
.I 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
.BR %array .
.PP
Na koniec, zauważ też, że nie możesz wstawiać tak znaków
.BR EOF .
Nie można tą metodą zaznaczać końca pliku w strumieniu.
.IP -
.B 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:
.nf

    %%
    "/*"        {
                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;
                        }
                    }
                }

.fi
(Zauważ, że jeśli skaner jest skompilowany z użyciem
.BR C++ ,
to
.B input()
nazywa się
.BR yyinput() .
Jest tak w celu zapobieżenia zderzeniu nazwy ze strumieniem
.B C++
poprzez nazwę
.IR input .)
.IP -
.B 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
.B YY_INPUT
(zobacz niżej Generowany Skaner).  Akcja ta jest szczególnym przypadkiem
bardziej ogólnej funkcji
.BR yy_flush_buffer() ,
opisanej niżej w sekcji Wielokrotne Bufory Wejściowe.
.IP -
.B 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,
.B yyterminate()
jest wywoływane również po napotkaniu końca pliku. Jest to makro i może być
redefiniowane.
.SH GENEROWANY SKANER
Wynikiem działania fleksa jest plik
.BR lex.yy.c ,
zawierający procedurę skanującą
.B yylex()
oraz zestaw tablic, używanych przez niego do dopasowywania tokenów i parę
procedur i makr. Domyślnie
.B yylex()
jest deklarowany jako
.nf

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

.fi
(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
.nf

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

.fi
informuje fleksa, by nadać procedurze skanującej nazwę
.I 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 (;).
.PP
Przy każdym wywołaniu
.BR yylex() ,
następuje skanowanie tokenów z globalnego pliku wejściowego
.I 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
.IR return .
.PP
Jeśli skaner osiąga koniec pliku, to kolejne wywołania są niezdefiniowane.
Sposobem na skorygowanie tego jest przekierowanie
.I yyin
na nowy plik wejściowy (w tym wypadku skanowanie następuje z nowego pliku)
lub wywołanie
.BR yyrestart() .
.B yyrestart()
pobiera jeden argument: wskaźnik
.B FILE *
(który może być nil, jeśli ustawiłeś
.B YY_INPUT
na skanowanie ze źródła innego niż
.I yyin),
i inicjalizuje
.I yyin
na początek tego pliku. W zasadzie nie ma różnicy między zwykłym
przypisaniem
.I yyin
do nowego pliku i użyciem
.BR yyrestart() ;
Procedura ta jest dostępna z uwagi na kompatybilność z poprzednimi wersjami
.IR flex ,
a także dlatego, że może być używana do przełączania plików wejściowych w
środku skanowania.
Może być też używana do porzucania bieżącego bufora wejściowego poprzez
wywołanie z argumentem
.IR yyin ;
lepszym rozwiązaniem jest jednak użycie
.B YY_FLUSH_BUFFER
(patrz wyżej).
Zauważ, że
.B yyrestart()
.I nie
resetuje warunku początkowego na
.B INITIAL
(zobacz niżej Warunki Początkowe).
.PP
Jeśli
.B yylex()
kończy skanowanie z powodu wywołania instrukcji
.I return
w jednej z akcji, skaner może być wołany ponownie i wznowi działanie tam,
gdzie skończył.
.PP
Domyślnie (i dla celów wydajności) skaner zamiast pojedynczych
.I getc()
wykonuje odczyty blokowe z
.IR yyin .
Sposób pobierania wejścia może być kontrolowany przez definiowanie makra
.BR YY_INPUT .
Sekwencja wywołująca YY_INPUT to "YY_INPUT(buf,wynik,max_rozmiar)".  Jej
wynikiem jest umieszczenie co najwyżej
.I max_rozmiar
znaków w tablicy znakowej
.I buf
i zwrócenie w zmiennej całkowitej
.I wynik
albo liczby wczytanych znaków albo stałej YY_NULL (0 w systemach uniksowych),
określającej EOF. Domyślnie, YY_INPUT czyta z globalnego wskaźnika "yyin".
.PP
Przykładowa definicja YY_INPUT (w sekcji definicji pliku wejściowego):
.nf

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

.fi
Definicja ta zmieni przetwarzanie wejścia tak, by naraz pojawiał się tylko
jeden znak.
.PP
W momencie, gdy skaner uzyska od YY_INPUT warunek końca pliku, to woła
funkcję
.BR yywrap() .
Jeśli
.B yywrap()
zwróci zero, to zakłada, że funkcja poszła dalej i skonfigurowała
.I 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;
.I nie
przechodzi on w
.BR INITIAL .
.PP
Jeśli nie chcesz podawać własnej wersji
.BR yywrap() ,
to musisz albo użyć opcji
.B %option noyywrap
(wtedy skaner zachowuje się, jakby 
.B yywrap()
zwracało 1), albo konsolidować z
.BR \-lfl ,
uzyskując tak domyślną wersję funkcji, zawsze zwracającej 1.
.PP
Do skanowania z buforów pamięciowych (a nie z plików) przeznaczone są trzy
procedury:
.BR yy_scan_string() ,
.B yy_scan_bytes()
oraz
.BR yy_scan_buffer() .
Zobacz niżej dyskusję w sekcji Wielokrotne Bufory Wejściowe.
.PP
Swoje wyjście
.B ECHO
skaner zapisuje do globalnego strumienia
.I yyout
(domyślnie stdout), który można przedefiniować dzięki zwykłemu
przypisaniu tej zmiennej do innego wskaźnika
.BR FILE .
.SH WARUNKI POCZĄTKOWE
.I 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,
.nf

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

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

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

.fi
będzie aktywne tylko jeśli obecnym warunkiem początkowym jest albo
"INITIAL", albo "STRING" albo "QUOTE".
.PP
Warunki początkowe są deklarowane w sekcji definicji wejścia przy użyciu
niewciętych linii, zaczynających się od
.B %s
lub
.BR %x ,
za którymi następuje lista nazw.
Pierwsza postać deklaruje
.I włączające
warunki początkowe, a druga
.IR wykluczające . 
Warunek początkowy włącza się przy użyciu akcji
.BR BEGIN .
Reguły używające danego warunku początkowego będą aktywne aż do wywołania
następnej akcji
.BR BEGIN .
Jeśli warunek początkowy jest
.I włączający ,
to reguły bez warunków początkowych będą również aktywne.
Jeśli jest
.IR wykluczający ,
to wykonywane będą
.I 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).
.PP
W rozróżnieniu warunków włączających i wykluczających istnieje wciąż pewna
niejasność: oto przykład, ilustrujący ich powiązanie. Zestaw reguł:
.nf

    %s przyklad
    %%

    <przyklad>foo  rob_cos();

    bar            cos_innego();

.fi
jest równoważny
.nf

    %x przyklad
    %%

    <przyklad>foo   rob_cos();

    <INITIAL,przyklad>bar    cos_innego();

.fi
Bez użycia kwalifikatora
.BR <INITIAL,przyklad> ,
wzorzec
.I bar
w drugim przykładzie nie byłby aktywny (tj. nie dopasowałby się) w warunku
początkowym
.BR przyklad .
Jeśli użylibyśmy do kwalifikowania \fIbar\fR tylko
.BR <przyklad> ,
to byłoby aktywny tylko w warunku początkowym
.BR przyklad , 
ale nie w 
.BR INITIAL ,
podczas gdy w pierwszym przykładzie jest aktywny w obydwu, gdyż
warunek początkowy
.B przyklad
jest w nim
.I włączający
.BR (%s) .
.PP
Zauważ też, że specjalny specyfikator
.B <*>
pasuje do dowolnego warunku początkowego. Tak więc, powyższe można zapisać
również następująco:
.nf

    %x przyklad
    %%

    <przyklad>foo   rob_cos();

    <*>bar    cos_innego();

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

    <*>.|\\n     ECHO;

.fi
.PP
.B 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
.BR BEGIN(INITIAL) .
(Nawiasy wokół nazwy warunku początkowego nie są wymagane, lecz są w dobrym
tonie.)
.PP
Akcje
.B 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
.B yylex()
a zmienna globalna
.I enter_special
będzie ustawiona na prawdę:
.nf

            int enter_special;

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

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

.fi
.PP
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).
.nf

    %{
    #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" );

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

    %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);

.fi
Skaner ten może mieć problemy z dopasowaniem maksymalnej ilości tekstu w
każdej z reguł. Ogólnie, przy pisaniu szybkich skanerów, próbuj dopasowywać
w każdej regule tyle, ile się da.
.PP
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:
.nf

    %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);

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

    comment_caller = YY_START;

.fi
Flex jako alias do
.B YY_START
daje
.B YYSTATE
(gdyż jest to nazwa, używana przez AT&T
.IR lex ).
.PP
Zauważ, że warunki początkowe nie mają własnej przestrzeni nazw; %s i %x-y
deklarują nazwy podobnie jak #define.
.PP
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):
.nf

    %x str

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


    \\"      string_buf_ptr = string_buf; BEGIN(str);

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

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

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

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

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

            *string_buf_ptr++ = result;
            }

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

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

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

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

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

.fi
.PP
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
.I zakresu
warunku początkowego.
Zakres rozpoczyna się od:
.nf

    <SCs>{

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

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

.fi
jest równoważne:
.nf

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

.fi
Zakresy warunków początkowych mogą być zagnieżdżane.
.PP
Do obsługi stosów warunków początkowych są przeznaczone trzy procedury:
.TP
.B 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
.IR new_state ,
zupełnie jak po użyciu
.B BEGIN new_state
(pamiętaj, że nazwy warunków początkowych są również liczbami całkowitymi).
.TP
.B void yy_pop_state()
zdejmuje wartość ze stosu i przełącza się na nią przez
.BR BEGIN .
.TP
.B int yy_top_state()
zwraca wierzchołek stosu bez zmiany zawartości stosu.
.PP
Stos warunków początkowych rośnie dynamicznie i nie ma żadnych wbudowanych
ograniczeń. Po wyczerpaniu pamięci, wykonywanie programu jest przerywane.
.PP
Aby korzystać ze stosów warunków początkowych, skaner musi zawierać
dyrektywę
.B %option stack
(zobacz niżej rozdział Opcje).
.SH WIELOKROTNE BUFORY WEJŚCIOWE
Niektóre skanery (te, obsługujące pliki dołączane "include") wymagają
odczytu z wielu strumieni wejściowych. Ponieważ skanery
.I flex
wykonują sporo buforowania, nie można jednoznacznie zdecydować skąd będzie
wykonywany następny odczyt przez proste napisanie
.BR YY_INPUT ,
które jest wrażliwe na kontekst skanowania.
.B 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.
.PP
Aby załatwić niektóre z tych problemów,
.I flex
daje mechanizm tworzenia i przełączania między wielokrotnymi buforami
wejściowymi. Bufor wejściowy jest tworzony z użyciem funkcji
.nf

    YY_BUFFER_STATE yy_create_buffer( FILE *file, int size )

.fi
która pobiera wskaźnik
.I 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
.B YY_BUF_SIZE
jako rozmiaru). Funkcja zwraca uchwyt
.BR YY_BUFFER_STATE ,
który może być potem przekazywany do innych procedur (zobacz niżej). Typ
.B YY_BUFFER_STATE
jest wskaźnikiem do struktury
.B struct yy_buffer_state
więc można bezpiecznie inicjalizować zmienne YY_BUFFER_STATE na
.B ((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
.I FILE
w wywołaniu
.B yy_create_buffer
jest używany tylko jako wartość
.I yyin
widzianego przez
.BR YY_INPUT ;
jeśli redefiniujesz
.B YY_INPUT
tak, żeby nie używało
.IR yyin ,
to możesz spokojnie przekazać tu zerowy wskaźnik
.IR FILE .
Zadany bufor do skanowania wybiera się za pomocą:
.nf

    void yy_switch_to_buffer( YY_BUFFER_STATE new_buffer )

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

    void yy_delete_buffer( YY_BUFFER_STATE buffer )

.fi
używane jest do odzyskania miejsca związanego z buforem (
.B 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:
.nf

    void yy_flush_buffer( YY_BUFFER_STATE buffer )

.fi
Funkcja ta niszczy zawartość bufora, więc przy następnej próbie dopasowania
tokenu z bufora, skaner najpierw wypełni bufor na nowo używając
.BR YY_INPUT .
.PP
.B yy_new_buffer()
jest synonimem
.BR yy_create_buffer() ,
udostępnionym dla zgodności z C++ narzędziami
.I new
i
.IR delete ,
służącymi do tworzenia i niszczenia obiektów dynamicznych.
.PP
Na koniec makro
.B YY_CURRENT_BUFFER
zwraca uchwyt
.B YY_BUFFER_STATE
do bieżącego bufora.
.PP
A oto przykład używania tych właściwości w skanerze, rozwijającym pliki
załączane (właściwość
.B <<EOF>>
jest opisywana niżej):
.nf

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

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

    %%
    include             BEGIN(incl);

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

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

            include_stack[include_stack_ptr++] =
                YY_CURRENT_BUFFER;

            yyin = fopen( yytext, "r" );

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

            yy_switch_to_buffer(
                yy_create_buffer( yyin, YY_BUF_SIZE ) );

            BEGIN(INITIAL);
            }

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

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

.fi
Do zestawiania buforów wejściowych dla skanowania łańcuchów z pamięci
zamiast plików istnieją trzy procedury. Każda z nich tworzy nowy bufor
wejściowy do skanowania łańcucha i zwraca odpowiadający uchwyt
.B YY_BUFFER_STATE
(który powinieneś skasować stosując
.B yy_delete_buffer()
po zakończeniu działania). Przełączają one też przetwarzanie na nowy bufor
przy użyciu
.BR yy_switch_to_buffer() ,
więc następne wywołanie
.B yylex()
rozpocznie skanowanie łańcucha.
.TP
.B yy_scan_string(const char *str)
skanuje łańcuch zakończony zerem.
.TP
.B yy_scan_bytes(const char *bytes, int len)
skanuje
.I len
bajtów (dopuszczalne zera w środku) począwszy od pozycji
.I bytes.
.PP
Zauważ, że obydwie funkcje tworzą i skanują
.I kopie
oryginalnych danych. (Jest to pożądane, gdyż
.B yylex()
modyfikuje zawartość skanowanego bufora.) Kopiowania można uniknąć,
stosując:
.TP
.B yy_scan_buffer(char *base, yy_size_t size)
które skanuje bufor na miejscu, zaczynając od
.IR base ,
a w długości
.I size
bajtów, z których dwa bajty
.I muszą
być znakami
.B YY_END_OF_BUFFER_CHAR
(ASCII NUL).
Ostatnie dwa bajty nie są skanowane; tak więc skanowanie przebiega od
.B base[0]
do
.B base[size-2]
włącznie.
.IP
Jeśli nie ustawisz odpowiednio
.I base
to 
.B yy_scan_buffer()
zwraca wskaźnik nil zamiast tworzyć nowy bufor wejściowy.
.IP
Typ
.B yy_size_t
jest typem całkowitym, na który rzutuje się wyrażenie całkowite, określające
rozmiar bufora.
.SH REGUŁY END-OF-FILE
Specjalna reguła "<<EOF>>" określa akcje, które należy wykonać po
osiągnięciu końca pliku i gdy yywrap() zwraca zero (tj. wskazuje brak dalszych
plików do przetworzenia). Akcja musi się zakończyć zrobieniem jednej z
czterech rzeczy:
.IP -
przypisaniem
.I yyin
do nowego pliku wejściowego (w poprzednich wersjach fleksa po dokonaniu
przypisania należało wywołać specjalną akcję
.B YY_NEW_FILE;
nie jest to już wymagane);
.IP -
wywołaniem instrukcji
.IR return ;
.IP -
wywołaniem specjalnej akcji
.BR yyterminate() ;
.IP -
przełączeniem na nowy bufor za pomocą 
.BR yy_switch_to_buffer() .
.PP
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
.I 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
.nf

    <INITIAL><<EOF>>

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

    %x quote
    %%

    ...inne reguły cytatowe...

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

.fi
.SH RÓŻNE MAKRA
Można zdefiniować makro
.BR 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
.BR YY_USER_ACTION ,
zmienna
.I 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:
.nf

    #define YY_USER_ACTION ++ctr[yy_act]

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

    int ctr[YY_NUM_RULES];

.fi
.PP
Makro
.B 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.
.PP
Makro
.B yy_set_interactive(is_interactive)
może być używane do sterowania czy bieżący bufor jest uważany za
.IR interaktywny .
Bufor interaktywny jest przetwarzany wolniej, lecz musi być używany gdy
wejście rzeczywiście jest interaktywne. Zapobiega to problemom związanym z
oczekiwaniem na wypełnienie buforów
(zobacz niżej dyskusję flagi
.BR \-I ).
Wartość niezerowa w wywołaniu makra zaznacza bufor jako interaktywny, a zero
to wyłącza. Zauważ, że użycie tego makra przesłania
.B %option always-interactiv
lub
.B %option never-interactive
(zobacz niżej Opcje).
Przed rozpoczęciem skanowania bufora, który jest (lub nie jest)
interaktywny, należy wywołać funkcję
.BR yy_set_interactive() .
.PP
Makro
.B 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.
.PP
Makro
.B 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.
.PP
W niektórych generowanych skanerach akcje są zebrane wszystkie w jedną
wielką instrukcję switch i są rozdzielone makrem
.BR YY_BREAK ,
które można redefiniować. Domyślnie jest to po prostu "break".
Redefiniowanie
.B 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,
.B YY_BREAK
jest nieosiągalne.
.SH WARTOŚCI DOSTĘPNE DLA UŻYTKOWNIKA
Sekcja ta zestawia różne wartości dostępne dla użytkownika w akcjach
regułowych.
.IP -
.B 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).
.IP
Jeśli w pierwszej sekcji opisu skanera pojawi się dyrektywa specjalna
.B %array
to
.B yytext
zostanie zadeklarowane jako
.BR char yytext[YYLMAX] ,
gdzie
.B YYLMAX
jest makrodefinicją, którą można przedefiniować w pierwszej sekcji (wartość
domyślna to ogólnie 8KB). Używanie
.B %array
daje wolniejsze skanery, lecz wartość
.B yytext
staje się odporna na wywołania
.I input()
i
.IR unput() ,
które potencjalnie niszczą jego wartość kiedy
.B yytext
jest wskaźnikiem znakowym. Przeciwną dyrektywą do
.B %array
jest
.BR %pointer ,
która jest dyrektywą domyślną.
.IP
Dyrektywy
.B %array
nie można używać do generowania klas skanera C++
(flaga
.BR \-+ ).
.IP -
.B int yyleng
przechowuje długość bieżącego tokenu.
.IP -
.B FILE *yyin
jest plikiem, z którego
.I 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
.BR yyrestart() .
Po zakończeniu skanowania przez napotkanie końca pliku, można przypisać
wartość
.I yyin
do nowego pliku wejściowego i wywołać ponownie skaner by dokończył
skanowanie.
.IP -
.B void yyrestart( FILE *new_file )
może być wołane do wskazywania
.I yyin
na nowy plik wejściowy. Przełączenie na nowy plik jest natychmiastowe
(wszelkie poprzednio buforowane wejście jest tracone). Zauważ, że wołanie
.B yyrestart()
z argumentem
.I yyin
porzuca bieżący bufor wejściowy i kontynuuje skanowanie tego samego pliku
wejściowego.
.IP -
.B FILE *yyout
jest plikiem, do którego kierowane jest wyjście akcji
.BR ECHO .
Użytkownik może mu przypisać inną wartość.
.IP -
.B YY_CURRENT_BUFFER
zwraca uchwyt
.B YY_BUFFER_STATE
do bieżącego bufora.
.IP -
.B YY_START
zwraca wartość całkowitą, odpowiadającą bieżącemu warunkowi początkowemu.
Wartości tej można używać dalej z
.B BEGIN
do powrotu do tego warunku.
.SH ŁĄCZENIE Z YACC
Jednym z podstawowych zastosowań
.I fleksa
jest współtowarzyszenie generatorowi analizatorów
.IR yacc .
Analizatory składni
.I yacc
oczekują wywołania procedury o nazwie
.B 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
.B yylval.
Aby używać
.I fleksa
z
.I yaccem,
należy yaccowi przekazać  opcję
.BR \-d ,
co każe mu generować plik
.B y.tab.h
zawierający definicje wszystkich
.BR %tokenów (%tokens)
pojawiających się w wejściu
.IR yacc .
Plik ten jest następnie załączany do skanera
.IR fleksowego .
Na przykład jeśli jednym z tokenów jest "TOK_NUMBER", to część skanera może
wyglądać tak:
.nf

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

    %%

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

.fi
.SH OPCJE
.I flex
ma następujące opcje:
.TP
.B \-b
Generuje informacje zapasowe do
.I lex.backup.
Oto lista stanów skanera, które wymagają kopii zapasowych oraz znaki
wejściowe dla których to zachodzi. Dodając reguły można usunąć stany
zapasowe. Jeśli wyeliminowane zostaną
.I wszystkie
stany zapasowe, a użyte będzie
.B \-Cf
lub
.BR \-CF ,
wygenerowany skaner będzie działał szybciej (zobacz flagę
.BR \-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ą.)
.TP
.B \-c
nieużywana i niezalecana opcja dla zgodności z POSIX-em.
.TP
.B \-d
powoduje, że generowany skaner działa w trybie
.IR debug .
Za każdym razem po rozpoznaniu wzorca, gdy globalna zmienna
.B yy_flex_debug
jest niezerowa (co jest domyślne), skaner zapisze na stderr linię w postaci:
.nf

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

.fi
Numer linii odnosi się do położenia reguły w pliku definiującym skaner (tj.
w pliku, potraktowanym fleksem). Komunikaty są również generowane gdy skaner
robi kopie zapasowe, przyjmuje domyślną regułę, dochodzi do końca bufora
(lub napotyka NUL; w tym momencie obydwa [zdarzenia] wyglądają jednakowo z
punktu widzenia skanera) lub osiąga koniec pliku.
.TP
.B \-f
określa
.IR "szybki skaner" .
Nie dokonywana jest kompresja tabel i pomijane jest stdio. W efekcie kod
jest duży, lecz szybki. Opcja ta jest równoważna
.B \-Cfr
(zobacz niżej).
.TP
.B \-h
generuje zestawienie "pomocy" opcji fleksa na stdout i kończy działanie.
.B \-?
i
.B \-\-help
są równoważnikami
.B \-h.
.TP
.B \-i
nakazuje fleksowi generowania skanera niewrażliwego na wielkość znaków.
Wielkość liter we wzorcach zostanie zignorowany, a tokeny wejścia będą
dopasowywane niezależnie od wielkości. Dopasowany tekst znajdujący się w
.I yytext
będzie miał zachowaną oryginalną wielkość liter.
.TP
.B \-l
włącza maksymalną zgodność z oryginalną implementacją
.I 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
.BR \-+ , -f , -F , -Cf
lub
.BR -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
.B YY_FLEX_LEX_COMPAT
w generowanym skanerze.
.TP
.B \-n
kolejna ignorowana opcja dodana dla zgodności z POSIX-em.
.TP
.B \-p
generuje raport o wydajności na stderr. Raport składa się z komentarzy o
właściwościach pliku wejściowego
.IR fleksa ,
więc powoduje znaczną utratę wydajności skanera. Jeśli podasz tę flagę
dwukrotnie, uzyskasz też komentarze o właściwościach, które doprowadziły do
drugorzędnych utrat wydajności.
.IP
Zauważ, że użycie
.BR REJECT ,
.BR "%option yylineno" ,
i zmiennego wiszącego kontekstu (variable trailing context) (zobacz  niżej
sekcję o Niedostatkach / Błędach) powoduje znaczną utratę wydajności; używanie
.IR yymore() ,
operatora
.B ^
i flagi
.B \-I
powoduje pomniejsze utraty wydajności.
.TP
.B \-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.
.TP
.B \-t
nakazuje
.I fleksowi
zapisanie wygenerowanego skanera na standardowe wyjście zamiast do pliku
.BR lex.yy.c .
.TP
.B \-v
nakazuje
.I fleksowi
pisanie na 
.I stderr
zestawienia statystyk dotyczących generowanego skanera.
Większość statystyk jest pozbawiona znaczenia dla typowego użytkownika, lecz
pierwsza z linijek wskazuje wersję fleksa (to samo co zgłasza opcja \-V), a
następna linia flagi użyte do generowania skanera, z domyślnymi włącznie.
.TP
.B \-w
powstrzymuje komunikaty o ostrzeżeniach.
.TP
.B \-B
nakazuje fleksowi generowanie skanera
.IR wsadowego ,
czyli odwrotność skanerów
.IR interaktywnych ,
generowanych przez
.B \-I
(zobacz niżej). Ogólnie, opcji
.B \-B
używa się mając
.IR pewność ,
że skaner nigdy nie będzie używany interaktywnie i chcąc wycisnąć jeszcze
.I troszeczkę
więcej wydajności. Jeśli chcesz zyskać więcej wydajności, powinieneś użyć
opcji
.B \-Cf
lub
.B \-CF
(opisanych niżej), które włączają
.B \-B
i tak automatycznie.
.TP
.B \-F
mówi, że należy użyć reprezentacji tablicy
.ul
szybkiego skanera (i stdio ma być pominięte). Reprezentacja ta jest mniej
więcej tak szybka jak reprezentacja pełnej tablicy
.BR (-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:
.nf

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

.fi
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
.BR -F .
.IP
Opcja ta odpowiada
.B \-CFr
(zobacz niżej).  Nie można jej używać z
.BR \-+ .
.TP
.B \-I
nakazuje
.I fleksowi
generowanie skanera
.IR interaktywnego .
Skaner interaktywny patrzy naprzód do wyboru dopasowania jedynie jeśli musi.
Okazuje się, że patrzenie o jeden dodatkowy znak dalej, nawet jeśli skaner
ma już dość do dopasowania tokenu jest trochę szybsze niż wersja minimalna.
Lecz skanery patrzące naprzód dają dziadowską wydajność interaktywną; na
przykład gdy użytkownik wpisze nową linię, to nie jest ona rozpoznawana jako
token nowej linii dopóki nie wprowadzony zostanie 
.I następny
token, co oznacza często wpisanie całej kolejnej linii.
.IP
Skanery
.I fleksa
są domyślnie
.IR interaktywne ,
chyba że użyjesz opcji kompresji tablicy
.B \-Cf
lub
.BR \-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ś,
.I flex
zakłada, że jesteś gotów poświęcić trochę wydajności na rzecz intuicyjnego
zachowania interaktywnego. Zauważ też, że 
.I nie możesz
użyć
.B \-I
w połączeniu z
.B \-Cf
lub
.BR \-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.
.IP
Opcją
.B \-B
możesz wymusić by skaner
.I nie
był interaktywny
(zobacz powyżej).
.TP
.B \-L
nakazuje
.I fleksowi
nie generować dyrektyw
.BR #line .
Bez tej opcji
.I flex
przyprawia generowany skaner dyrektywami #line, więc komunikaty o błędach w
akcjach będą poprawnie położone względem oryginalnego pliku wejściowego
.I fleksa
(jeśli błędy wynikają z kodu w pliku wejściowym) lub [względem] 
.B lex.yy.c
(jeśli błędy są winą
.I fleksa
-- powinieneś zgłosić takie błędy pod adres e-mail podany poniżej.)
.TP
.B \-T
powoduje, że
.I flex
działa w trybie
.IR śledzenia .
Będzie generował na stderr wiele komunikatów o postaci wejścia i
wynikających zeń niedeterministycznych i deterministycznych automatach
skończonych. Opcja ta jest używana zwykle w opiece nad
.IR fleksem .
.TP
.B \-V
drukuje numer wersji na
.I stdout
i kończy działanie.
.B \-\-version
jest synonimem
.BR \-V .
.TP
.B \-7
nakazuje
.I fleksowi
generowanie skanera 7-bitowego, tj. takiego który może rozpoznawać w swoim
wejściu tylko znaki 7-bitowe. Zaletą używania
.B \-7
jest to, że tablice skanera będą o połowę mniejsze niż wygenerowane opcją
.B \-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.
.IP
Zauważ jednak, że jeśli generujesz skaner z użyciem opcji kompresji tablic
.B \-Cf
lub
.BR \-CF ,
to użycie
.B \-7
zachowa jedynie niewielki rozmiar przestrzeni tablic, a spowoduje, że skaner
będzie znacząco mniej przenośny.
Domyślnym zachowaniem
.I fleksa
jest generowanie skanerów 8-bitowych, chyba że użyto opcji
.B \-Cf
lub
.BR \-CF ,
i wtedy
.I 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
.BR \-v ,
co opisano wyżej.
.IP
Zauważ, że jeśli używasz
.B \-Cfe
lub
.BR \-CFe ,
flex wciąż domyślnie generuje skaner 8-bitowy, gdyż po kompresji pełne
tablice 8-bitowe nie są wiele większe od 7-bitowych.
.TP
.B \-8
nakazuje
.I fleksowi
generowanie skanera 8-bitowego, tj. takiego, który rozpoznaje znaki
8-bitowe. Flaga ta jest wymagana jedynie dla skanerów wygenerowanych z
użyciem
.B \-Cf
lub
.BR \-CF ,
gdyż w innych wypadkach jest ona przyjmowana jako domyślna.
.IP
.TP
.B \-+
określa, że chcesz by fleks wygenerował klasę skanera w C++. Zobacz sekcję o
generowaniu skanerów C++.
.TP 
.B \-C[aefFmr]
steruje poziomem kompresji tablic, balansując między małymi a szybkimi
skanerami.
.IP
.B \-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.
.IP
.B \-Ce
Nakazuje
.I fleksowi
budowanie klas
.IR 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).
.IP
.B \-Cf
określa, że należy generować
.I pełne
tablice skanera -
.I flex
nie ma ich kompresować poprzez branie korzyści z podobnych funkcji przejść
dla różnych stanów.
.IP
.B \-CF
określa, że należy użyć alternatywnej, szybkiej reprezentacji skanera
(opisanej pod flagą
.BR \-F ).
Opcja ta nie może być używana z
.BR \-+ .
.IP
.B \-Cm
nakazuje
.I fleksowi
budowanie klas
.I meta-równoważności,
które są zbiorami klas równoważności (lub znaków, jeśli klasy równoważności
nie są używane), które są często używane wspólnie. Klasy takie są często
dobrą rzeczą podczas używania skompresowanych tablic, lecz mają one już
umiarkowany wpływ na wydajność (dwa lub jeden test "if" i jedno
podglądnięcie tablicy na skanowany znak).
.IP
.B \-Cr
powoduje, że generowany skaner
.I omija
użycie standardowej biblioteki I/O dla wejścia. Zamiast wołać
.B fread()
lub
.BR getc() ,
skaner będzie używać wywołania systemowego
.BR 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ż
.B \-Cf
lub
.BR \-CF .
Wykorzystanie
.B \-Cr
może też spowodować dziwne zachowanie jeśli np. odczytasz z
.I yyin
z pomocą stdio przed wywołaniem skanera (skaner pominie tekst pozostawiony
przez twoje odczyty w buforze wejściowym stdio).
.IP
.B \-Cr
nie działa jeśli zdefiniujesz
.B YY_INPUT
(zobacz wyżej Generowany Skaner).
.IP
Samotne
.B \-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.
.IP
Opcje
.B \-Cf
lub
.B \-CF
i
.B \-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ć.
.IP
Domyślnym ustawieniem jest
.B \-Cem,
które określa, że
.I 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:
.nf

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

.fi
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.
.IP
.B \-Cfe
jest często dobrym kompromisem między szybkością a rozmiarem dla skanerów
gotowych do wdrożenia (production scanners).
.TP
.B \-ooutput
nakazuje fleksowi zapisanie skanera do pliku
.B output
zamiast do
.BR lex.yy.c .
Jeśli połączysz
.B \-o
z opcją
.BR \-t ,
to skaner jest zapisywany na stdout, lecz jego dyrektywy
.B #line
(zobacz wyżej opcję
.BR \\-L ),
odnoszą się do pliku
.BR output .
.TP
.B \-Pprefiks
zmienia domyślny przedrostek
.I "yy"
używany przez 
.I fleksa
dla wszystkich zmiennych i funkcji globalnych na
.IR prefiks .
Na przykład
.B \-Pfoo
zmienia nazwę
.B yytext
na
.BR footext .
Zmienia to też nazwę domyślnego pliku wyjściowego z
.B lex.yy.c
na
.BR lex.foo.c .
A oto wszystkie nazwy, których dotyczy takie zachowanie:
.nf

    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

.fi
(Jeśli używasz skanera C++, to dotyczyć to będzie tylko
.B yywrap
i
.BR yyFlexLexer .)
Wewnątrz samego skanera można wciąż używać jednej i drugiej konwencji
nazywania; jednak z zewnątrz dozwolone są tylko nazwy zmodyfikowane.
.IP
Opcja ta umożliwia łatwe łączenie w całość różnych programów
.I fleksa
w jeden plik wykonywalny. Zauważ jednak, że używanie tej opcji zmienia też
nazwę
.BR yywrap() ,
więc \fImusisz\fR teraz albo udostępnić własną wersję tej procedury dla
swojego skanera, albo użyć
.BR "%option noyywrap" ,
gdyż konsolidacja z
.B \-lfl
nie daje już funkcji domyślnej.
.TP
.B \-Sskeleton_file
przesłania domyślny plik szkieletowy, na podstawie którego
.I flex
buduje swoje skanery. Nie będziesz używać tej opcji, chyba że zajmujesz się
rozwojem fleksa.
.PP
.I flex
daje też mechanizm kontrolowania opcji z samej specyfikacji skanera, zamiast
linii poleceń. Działa to przez włączanie dyrektyw
.B %option
w pierwszej sekcji specyfikacji skanera. W jednej dyrektywie
.B %option
można podawać wiele opcji, a w samej pierwszej sekcji pliku wejściowego
fleksa można używać wielu dyrektyw.
.PP
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:
.nf

    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)

.fi
Niektóre
.B %opcje
dają właściwości niedostępne gdzie indziej:
.TP
.B 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
.B 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.
.TP
.B main
nakazuje fleksowi udostępnić domyślny program
.B main()
dla skanera, który po prostu woła
.BR yylex() .
Opcja ta implikuje
.B noyywrap
(zobacz niżej).
.TP
.B never-interactive
nakazuje fleksowi generowanie skanera, który zawsze uważa swoje wejście za
"nieinteraktywne" (znów, nie jest wołane
.BR isatty() ).
Opcja ta jest przeciwna do
.B always-interactive.
.TP
.B stack
włącza używanie stosów warunków początkowych (zobacz wyżej Warunki
Początkowe).
.TP
.B stdinit
jeśli jest ustawione (np.
.BR "%option stdinit" )
to zachodzi inicjalizacja
.I yyin
i
.I yyout
na
.I stdin
i
.IR stdout ,
zamiast domyślnych
.I nil.
Niektóre istniejące programy
.I lex
zależą od tego zachowania, nawet jeśli nie jest ono zgodne z ANSI C,
które nie wymagają stałych czasu kompilacji
.I stdin
i
.IR stdout .
.TP
.B yylineno
nakazuje
.I fleksowi
generowanie skanera, który przechowuje liczbę obecnie odczytanych linii w
zmiennej globalnej
.B yylineno.
Opcja ta jest wymuszana przez
.BR "%option lex-compat" .
.TP
.B yywrap
jeśli nie jest ustawione (np.
.BR "%option noyywrap" ),
to skaner nie woła
.B yywrap()
na końcu pliku, lecz po prostu przyjmuje, że nie ma już plików do skanowania
(dopóki użytkownik nie wskaże yyin na nowy plik i nie wywoła
.B yylex()
ponownie).
.PP
.I flex
skanuje akcje reguł w celu określenia czy używasz właściwości
.B REJECT
lub
.BR yymore() .
Opcje
.B reject
i
.B yymore
mogą przesłonić jego decyzję na taką, jaką ustawisz przy użyciu opcji,
zarówno ustawiając je (np.
.BR "%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.
.BR "%option noyymore" ).
.PP
Trzy opcje pobierają wartości łańcuchowe, offsetowane znakiem '=':
.nf

    %option outfile="ABC"

.fi
jest równoważne
.BR -oABC ,
a
.nf

    %option prefix="XYZ"

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

    %option yyclass="foo"

.fi
dotyczy tylko skanerów C++ (opcja
.BR \-+ ).
Mówi to fleksowi, że
.B foo
jest wyprowadzone jako podklasa
.B yyFlexLexer,
więc
.I flex
będzie umieszczał twoje akcje w funkcji składowej
.B foo::yylex()
zamiast w
.BR yyFlexLexer::yylex() .
Powoduje to też generowanie funkcji składowej
.BR yyFlexLexer::yylex() ,
emitującej po wywołaniu błąd działania (przez wywołanie
.BR yyFlexLexer::LexerError()) .
Dla dalszych informacji zobacz też niżej Generowanie Skanerów C++.
.PP
Istnieją opcje dla purystów, nie chcących widzieć w swoich skanerach
niepotrzebnych procedur. Każda z następujących opcji (np.
(np.,
.BR "%option nounput" ),
powoduje, że dana procedura nie pojawia się w wygenerowanym skanerze:
.nf

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

.fi
(chociaż
.B yy_push_state()
i podobne i tak nie pojawią się dopóki nie użyjesz
.BR %option stack) .
.SH ROZWAŻANIA NAD WYDAJNOŚCIĄ
Podstawowym zadaniem przy projektowaniu
.I 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
.BR \-C ,
istnieje jeszcze kilka akcji/opcji wpływających na wydajność. Są to, od
najkosztowniejszej do najmniej kosztownej:
.nf

    REJECT
    %option yylineno
    arbitralny wiszący kontekst

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

    '^' operator rozpoczęcia linii
    yymore()

.fi
z których pierwsze trzy są bardzo kosztowne, a ostatnie dwa w miarę tanie.
Zauważ też, że
.B unput()
jest implementowane jako wywołanie procedurowe, które prawdopodobnie
wykonuje sporo pracy, podczas gdy
.B yyless()
jest tanim makrem; więc jeśli wstawiasz z powrotem nadmiarowy wyskanowany
tekst, użyj
.BR yyless() .
.PP
.B REJECT
powinno być unikane za wszelką cenę z punktu widzenia wydajności.
Jest to szczególnie kosztowna opcja.
.PP
Pozbycie się cofania jest trudne i może często prowadzić do błędów w
skomplikowanych skanerach. W praktyce zaczyna się od użycia flagi
.B \-b 
do wygenerowania pliku
.IR lex.backup .
Na przykład dla wejścia
.nf

    %%
    foo        return TOK_KEYWORD;
    foobar     return TOK_KEYWORD;

.fi
plik ten wygląda tak:
.nf

    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.

.fi
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).
.PP
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'.
.PP
Ostatni komentarz przypomina nam, że usuwanie cofania nie ma sensu jeśli nie
używamy
.B \-Cf
lub
.BR \-CF ,
gdyż nie daje to żadnego zysku wydajności na skanerach kompresowanych.
.PP
Sposobem usuwania cofania jest dodawanie reguł dla "błędów":
.nf

    %%
    foo         return TOK_KEYWORD;
    foobar      return TOK_KEYWORD;

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

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

    %%
    foo         return TOK_KEYWORD;
    foobar      return TOK_KEYWORD;

    [a-z]+      return TOK_ID;

.fi
Jest to, tam gdzie można je zastosować, najlepsze rozwiązanie.
.PP
Komunikaty cofania często układają się w kaskady. W skomplikowanych zbiorach
reguł można dostać setki komunikatów. Mimo to, jeśli można je zdeszyfrować, to
ich usuwanie wymaga tylko tuzina reguł (łatwo się jednak pomylić i spowodować, że
reguła obsługi błędu będzie pasować do prawidłowego tokena. Możliwe, że przyszłe
implementacje fleksa będą automatycznie zajmowały się usuwaniem cofania).
.PP
Ważne jest pamiętanie, że korzyści z eliminacji tego problemu
zyskujesz dopiero po zlikwidowaniu
.I każdej
instancji cofania. Pozostawienie choć jednej oznacza, że nie zyskujesz
niczego.
.PP
.I 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
.B REJECT
(tzn. znaczną). Dlatego gdy tylko można, to zapisz taką regułę:
.nf

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

.fi
jako:
.nf

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

.fi
lub jako
.nf

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

.fi
zwróć uwagę, że specjalna akcja '|'
.I nie
powoduje żadnych oszczędności, a wręcz może pogorszyć sprawę (zobacz niżej
Niedostatki / Błędy).
.LP
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.
.BR yytext )
dla akcji. Przypomnij sobie skaner komentarzy C:
.nf

    %x comment
    %%
            int line_num = 1;

    "/*"         BEGIN(comment);

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

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

    %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);

.fi
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
.I dodawanie
reguł
.I 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 '|').
.PP
Ostateczny przykład przyspieszania skanera: załóżmy, że chcesz skanować plik
zawierający identyfikatory i słowa kluczowe w liczbie jednego na linię, bez
żadnych obcych znaków i chcesz rozpoznawać wszystkie słowa kluczowe.
Naturalnym odruchem początkowym jest:
.nf

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

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

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

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

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

.fi
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.
.nf

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

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

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

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

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

.fi
Po kompilacji z
.BR \-Cf ,
jest to prawie tak szybkie, jak tylko możliwe dla
.I fleksa
dla tego problemu.
.PP
Ostatnia uwaga:
.I flex
jest wolny przy dopasowywaniu NUL-ów, szczególnie jeśli token zawiera ich
wiele. 
Najlepiej pisać reguły, dopasowujące
.I krótkie
fragmenty takich tekstów.
.PP
Kolejna ostatnia uwaga o wydajności: jak wspomniano wyżej w sekcji Jak
Dopasowywane jest Wejście, dynamiczne zmiany rozmiarów
.B 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.
.SH GENEROWANIE SKANERÓW C++
.I flex
daje dwie drogi tworzenia skanerów przeznaczonych dla C++. Pierwszą z nich
jest proste skompilowanie fleksowego skanera kompilatorem C++ zamiast
kompilatora C. Nie powinieneś napotkać żadnych błędów kompilacji (jeśli się
pojawią, to zgłoś to pod adres wskazany niżej, w sekcji o autorze). Możesz
wówczas w akcjach swoich reguł używać kodu C++ zamiast C. Zauważ, że
domyślnym źródłem dla skanera pozostaje
.IR yyin ,
a domyślnym echem jest wciąż
.IR yyout .
Obydwa urządzenia są zmiennymi
.IR "FILE *" ,
a nie strumieniami C++.
.PP
Można też użyć
.I fleksa
do generowania klasy skanera C++. Służy do tego opcja
.B \-+
(lub, równoważnie
.BR "%option c++" ),
co jest przyjmowane automatycznie jeśli nazwa pliku wykonywalnego fleksa
kończy się plusem, jak np.
.IR flex++ .
Przy użyciu tej opcji, flex generuje skaner do pliku
.B lex.yy.cc
zamiast
.BR lex.yy.c .
Generowany skaner zawiera plik nagłówkowy
.IR FlexLexer.h ,
który definiuje interfejsy do dwóch klas C++.
.PP
Pierwsza klasa,
.BR FlexLexer ,
daje abstrakcyjną klasę bazową, definiującą ogólny interfejs klasy skanera.
Daje następujące funkcje składowe:
.TP
.B const char* YYText()
zwraca tekst ostatnio dopasowanego tokenu, równoważnik
.BR yytext .
.TP
.B int YYLeng()
zwraca długość ostatnio dopasowanego tokenu, równoważnik
.BR yyleng .
.TP
.B int lineno() const
zwraca numer aktualnej linii wejściowej
(zobacz
.BR "%option yylineno" ),
lub
.B 1
jeśli
.B %option yylineno
nie zostało użyte.
.TP
.B void set_debug( int flag )
ustawia flagę debuggującą dla skanera, równoważnik przypisania do
.B yy_flex_debug
(zobacz wyżej sekcję o opcjach). Zauważ, że aby włączać w skanerze
informacje diagnostyczne, musisz skompilować go z użyciem
.BR "%option debug" .
.TP
.B int debug() const
zwraca bieżące ustawienie flagi debuggującej.
.PP
Udostępniane są też funkcje składowe równoważne
.BR yy_switch_to_buffer() ,
.B yy_create_buffer()
(chociaż pierwszym argumentem jest wskaźnik
.BR istream* ,
a nie
.BR FILE* ),
.BR yy_flush_buffer() ,
.B yy_delete_buffer()
i
.B yyrestart()
(i znowu, pierwszym argumentem jest wskaźnik
.BR istream* ).
.PP
Kolejną klasą zdefiniowaną w
.I FlexLexer.h
jest
.BR yyFlexLexer ,
który jest klasą pochodną
.BR FlexLexer .
Zaiwera następujące dodatkowe funkcje składowe:
.TP
.B
yyFlexLexer( istream* arg_yyin = 0, ostream* arg_yyout = 0 )
buduje obiekt
.B yyFlexLexer
stosując podane strumienie jako wejście i wyjście. Jeśli nie zostaną
podane, to strumienie będą odpowiadały odpowiednio
.B cin
i
.BR cout .
.TP
.B virtual int yylex()
odgrywa tę samą rolę co
.B yylex()
dla normalnych skanerów fleksa: skanuje strumień wejściowy, konsumuje tokeny
aż akcja reguły nie zwróci wartości. Jeśli z
.B yyFlexLexer
wyprowadzisz podklasę
.B S
i zechcesz dostać się do funkcji i zmiennych składowych
.B S
z wnętrza
.BR yylex() ,
to musisz użyć
.B %option yyclass="S"
by poinformować
.IR fleksa ,
że będziesz używać podklasy zamiast 
.BR yyFlexLexer .
W tym wypadku zamiast generować
.BR yyFlexLexer::yylex() ,
.I flex
generuje
.B S::yylex()
(oraz generuje prosty
.BR yyFlexLexer::yylex() ,
który woła
.B yyFlexLexer::LexerError()
po wywołaniu).
.TP
.B
virtual void switch_streams(istream* new_in = 0,
.B
ostream* new_out = 0)
przypisuje
.B yyin
do
.B new_in
(jeśli jest nie-nil)
oraz
.B yyout
do
.B new_out
(ditto), kasując poprzedni bufor wejściowy jeśli przypisywana jest nowa
wartość
.B yyin .
.TP
.B
int yylex( istream* new_in, ostream* new_out = 0 )
najpierw przełącza strumienie wejściowe poprzez
.BR "switch_streams( new_in, new_out )" ,
a następnie zwraca wartość
.BR yylex() .
.PP
Poza tym,
.B yyFlexLexer
definiuje następujące chronione (protected) funkcje wirtualne, które można
przedefiniować w klasach pochodnych, by dostosować skaner:
.TP
.B
virtual int LexerInput( char* buf, int max_size )
odczytuje maksymalnie
.B max_size
znaków do
.B buf
i zwraca liczbę odczytanych znaków. Aby wskazać koniec wejścia zwracane jest
0 znaków. Zauważ, że skanery "interaktywne" (zobacz flagi
.B \-B
oraz
.BR \-I )
definiują makro
.BR YY_INTERACTIVE .
Jeśli redefiniujesz
.B LexerInput()
i potrzebujesz brać różne akcje, zależnie od tego czy skaner skanuje źródło
interaktywne czy nie, to możesz sprawdzać obecność tej nazwy poprzez
.BR #ifdef .
.TP
.B
virtual void LexerOutput( const char* buf, int size )
zapisuje
.B size
znaków z bufora
.B 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.
.TP
.B
virtual void LexerError( const char* msg )
zgłasza komunikat błędu krytycznego. Domyślna wersja tej funkcji zapisuje
komunikat do strumienia
.B cerr
i kończy działanie programu.
.PP
Zauważ, że obiekt
.B yyFlexLexer
zawiera swój
.I 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
.BR yyFlexLexer ,
jak również możesz w jednym programie łączyć wiele klas skanerów w całość,
używając opisanej wyżej opcji
.B \-P .
.PP
Dla skanerów C++ nie jest dostępna właściwość
.BR %array ,
trzeba więc używać
.B %pointer
(tj. wartości domyślnej).
.PP
Oto przykład prostego skanera C++:
.nf

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

    %{
    int mylineno = 0;
    %}

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

    ws      [ \\t]+

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

    %%

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

    "/*"    {
            int c;

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

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

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

    \\n        mylineno++;

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

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

    %%

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

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

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

.fi
o ile (na przykład) użyjesz opcji
.B %option prefix="xx"
dla jednego ze swoich skanerów, a
.B %option prefix="zz"
dla drugiego.
.PP
WAŻNE: obecna postać klasy skanującej jest
.I eksperymentalna
i może zmieniać się między głównymi wydaniami.
.SH NIEZGODNOŚCI Z LEX I POSIX
.I flex
jest przeróbką narzędzia
.I 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
.I lex
poza szczegółem, że gdy używa
.B %pointer
(domyślne), to wywołanie
.B unput()
niszczy zawartość
.BR yytext ,
co jest niezgodne ze specyfikacją POSIX.
.PP
W sekcji tej omówimy wszystkie znane obszary niezgodności fleksa z AT&T lex
i specyfikacją POSIX.
.PP
.I fleksowa opcja
.B \-l
włącza maksymalną zgodność z oryginalnym AT&T
.IR lex ,
okupując to jednak znacznymi stratami wydajności generowanego skanera.
Niżej zaznaczymy, które niezgodności można pokonać używając opcji
.BR \-l .
.PP
.I flex
jest w pełni zgodny z
.I leksem
poza następującymi wyjątkami:
.IP -
Nieudokumentowana zmienna wewnętrzna skanera
.I lex
o nazwie
.B yylineno
nie jest obsługiwana bez
.B \-l
lub
.BR "%option yylineno" .
.IP
.B yylineno
powinno być obsługiwane na poziomie buforowym, a nie na skanerowym
(pojedyncza zmienna globalna).
.IP
.B yylineno
nie jest częścią specyfikacji POSIX.
.IP -
Procedura
.B input()
nie jest redefiniowalna chociaż może być wołana do czytania znaków
następującym po tym, co dopasowano do reguły. Jeśli
.B input()
napotka koniec pliku, to wykonywane jest normalne przetwarzanie
.BR yywrap() .
``Prawdziwy'' koniec pliku jest sygnalizowany przez
.B input()
zwróceniem wartości
.IR EOF .
.IP
Wejście jest natomiast sterowane przez definiowanie makra
.BR YY_INPUT .
.IP
Ograniczenie
.IR fleksa ,
że
.B 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 
.I yyin.
.IP -
Procedura
.B unput()
nie jest redefiniowalna. Ograniczenie to jest zgodne z POSIX.
.IP -
Skanery
.I fleksa
nie są tak wielobieżne (reentrant) jak skanery
.IR 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:
.nf

    fatal flex scanner internal error--end of buffer missed

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

    yyrestart( yyin );

.fi
Zauważ, że wywołanie to wyrzuci wszelkie buforowane wejście; zwykle jednak
nie jest to problem przy skanerach interaktywnych.
.IP
Zauważ też, że klasy skanerów C++
.I są
wielobieżne (reentrant), więc używając opcji C++ powinieneś ich używać. Zobacz
sekcję o generowaniu skanerów C++.
.IP -
.B output()
nie jest obsługiwany. Wyjście makra
.B ECHO
jest wykonywane do wskaźnika plikowego
.I yyout
(domyślnie
.IR stdout ).
.IP
.B output()
nie jest częścią specyfikacji POSIX.
.IP -
.I lex
nie obsługuje wykluczających warunków początkowych (%x), choć znajdują się one w
specyfikacji POSIX.
.IP -
Przy rozwijaniu definicji,
.I flex
ujmuje je w nawiasy.
W leksie, następujące:
.nf

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

.fi
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.
.IP
Zauważ, że jeśli definicja rozpoczyna się od
.B ^
lub kończy się na
.B $
to
.I nie
jest rozwijana w nawiasach, aby umożliwić tym operatorom pojawienie się w
definicjach bez utraty ich znaczenia. Ale operatory
.BR <s> ,
.B /
i
.B <<EOF>>
nie mogą być używane w definicji fleksa.
.IP
Używanie
.B \-l
skutkuje
.I leksowym
zachowaniem braku nawiasów wokół definicji.
.IP
POSIX nakazuje ujmowanie definicji w nawiasy.
.IP -
Niektóre implementacje
.I leksa
umożliwiają rozpoczynanie akcji reguł w osobnej linii jeśli wzorzec reguły
ma doklejoną białą spację:
.nf

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

.fi
.I flex
nie obsługuje tej właściwości.
.IP -
.I Leksowe
.B %r
(generuj skaner Ratfor) nie jest obsługiwane. Nie jest częścią specyfikacji
POSIX.
.IP -
Po wywołaniu
.BR unput() ,
.I yytext
jest niezdefiniowane aż do dopasowania następnego tokenu, chyba że skaner
używa
.BR %array .
Inaczej ma się sprawa z
.I leksem
lub specyfikacją POSIX. Opcja
.B \-l
załatwia tę niezgodność.
.IP -
Pierwszeństwo operatora
.B {}
(zakresu numerycznego) jest inne.
.I lex
interpretuje "abc{1,3}" jako "dopasuj 1, 2 lub 3 pojawienia 'abc'", a
.I flex
interpretuje to jako "dopasuj 'ab' z doklejonym jednym, dwoma lub trzema
znakami 'c'". Interpretacja fleksowa jest zgodna ze specyfikacją POSIX.
.IP -
Pierwszeństwo operatora
.B ^
jest inne.
.I lex
interpretuje "^foo|bar" jako "dopasuj albo 'foo' z początku linii albo 'bar'
gdziekolwiek", podczas gdy
.I flex
rozumie to jako "dopasuj 'foo' lub 'bar' jeśli pojawią się na początku
linii". To drugie jest zgodne ze specyfikacją POSIX.
.IP -
Specjalne deklaracje rozmiaru-tablicy, takie jak
.BR %a ,
obsługiwane przez
.I lex
nie są wymagane przez skanery
.IR fleksa ;
.I flex
je ignoruje.
.IP -
Nazwa
.bd
FLEX_SCANNER
jest #definiowana, więc skanery mogą być pisane z przeznaczeniem do użycia z 
.I fleksem
lub
.IR leksem .
Skanery zawierają również
.B YY_FLEX_MAJOR_VERSION
i
.B YY_FLEX_MINOR_VERSION
wskazując na wersję
.IR fleksa ,
która wygenerowała skaner (na przykład dla wydania 2.5 definiowane są
odpowiednio liczby 2 i 5).
.PP
Następujące właściwości
.I fleksa
nie są zawarte w specyfikacjach
.I lex
ani POSIX:
.nf

    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

.fi
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
.IR leksie ,
następująca instrukcja
.nf

    foo    handle_foo(); ++num_foos_seen;

.fi
jest (raczej niespodziewanie) obcinana do
.nf

    foo    handle_foo();

.fi
.I flex
nie obcina akcji. Akcje które nie są objęte klamrami kończą się zwyczajnie
na końcu linii.
.SH DIAGNOSTYKA
.PP
.I warning, rule cannot be matched
(ostrzeżenie, reguła nie może być dopasowana) wskazuje, że podana reguła nie
może być dopasowana gdyż występuje za innymi regułami, które zawsze
dopasują jej tekst. Na przykład następujące foo nie może być dopasowane, gdyż
pojawia się po regule łap-wszystko:
.nf

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

.fi
Użycie w skanerze
.B REJECT
powstrzyma to ostrzeżenie.
.PP
.IR warning ,
.B \-s
.I 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
.BR \-s ,
zakłada się, że nie jest to celowe.
.PP
.I reject_used_but_not_detected undefined
lub
.I yymore_used_but_not_detected undefined
(niezdefiniowana fraza pierwsza lub druga) -
te błędy pojawiają się podczas kompilacji. Wskazują one, że
skaner używa
.B REJECT
lub
.BR yymore() , 
lecz
.I flex
nie poinformował o tym fakcie. Znaczy to, że
.I flex
przeskanował pierwsze dwie sekcji w poszukiwaniu pojawienia się tych akcji,
ale ich nie znalazł, bo jakoś je przemyciłeś (np. przez plik #include). Użyj
.B %option reject
lub
.B %option yymore
do wskazania fleksowi, że naprawdę używasz tych właściwości.
.PP
.I flex scanner jammed
\- skaner skompilowany z
.B \-s
napotkał łańcuch wejściowy, który nie został dopasowany do żadnej z jego
reguł. Błąd ten może się pojawić też z powodu problemów wewnętrznych.
.PP
.I token too large, exceeds YYLMAX
(token zbyt duży, przekracza YYLMAX) -
twój skaner używa
.B %array
a jedna z jego reguł dopasowała się do łańcucha dłuższego niż stała
.B YYLMAX
(domyślnie 8K). Możesz zwiększyć tę wartość zwiększając #definicję stałej
.B YYLMAX
w sekcji definicji swojego wejścia
.IR fleksa .
.PP
.I scanner requires \-8 flag to
.I use the character 'x' 
(skaner wymaga flagi \-8 do używania znaku 'x') -
specyfikacja twojego skanera zawiera rozpoznawanie znaku 8-bitowego
.IR 'x' ,
a nie podana została flaga \-8, w wyniku czego skaner użył 7-bit z powodu
wykorzystania opcji kompresji tablic
.B \-Cf
lub
.BR \-CF .
Dla szczegółów zobacz dyskusję flagi
.BR \-7 .
.PP
.I flex scanner push-back overflow
\- użyłeś
.B unput()
do wepchnięcia z powrotem tak długiego tekstu, że bufor skanera nie potrafił
przetrzymać wepchniętego tekstu i bieżącego tokena w
.B yytext.
Idealny skaner powinien dynamicznie zmienić rozmiar bufora, lecz obecnie tak
się nie dzieje.
.PP
.I input buffer overflow, can't enlarge buffer because scanner uses REJECT
(przekroczenie bufora wejściowego nie może powiększyć bufora gdyż skaner
używa REJECT) -
skaner pracował nad dopasowaniem bardzo dużego tokenu i potrzebował
rozszerzyć bufor wejściowy. Nie działa to ze skanerami, używającymi
.B
REJECT.
.PP
.I fatal flex scanner internal error--end of buffer missed
(krytyczny błąd wewnętrzny skanera flex -- rozminięto się z końcem bufora) -
Może się to pojawić w skanerze, który jest uruchomiony po długim skoku z
ramki aktywacji skanera. Przed powrotem do skanera użyj:
.nf

    yyrestart( yyin );

.fi
albo, jak wspomniano wyżej, przełącz się na używanie skanerów C++.
.PP
.I 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).
.SH PLIKI
.TP
.B \-lfl
biblioteka, z którą muszą być łączone skanery.
.TP
.I lex.yy.c
generowany skaner (nazywany na niektórych systemach
.IR lexyy.c ).
.TP
.I lex.yy.cc
generowana klasa skanera C++, po użyciu
.BR -+ .
.TP
.I <FlexLexer.h>
plik nagłówkowy definiujący klasę bazową skanera C++,
.B FlexLexer
i klasę pochodną,
.BR yyFlexLexer .
.TP
.I flex.skl
skaner szkieletowy. Plik ten jest używany tylko przy budowaniu fleksa, nie
przy jego uruchamianiu.
.TP
.I lex.backup
informacje wspierające (backing-up) dla flagi
.B \-b
(nazywany jest mianem
.I lex.bck
na niektórych systemach).
.SH NIEDOSTATKI / BŁĘDY
.PP
Niektóre wzorce wiszącego kontekstu nie mogą być poprawnie dopasowane i
generują komunikaty ostrzegawcze ("dangerous trailing context")
(niebezpieczny wiszący kontekst). Są to wzorce, gdzie zakończenie pierwszej
części reguły dopasowuje się do początku drugiej części, takie jak
"zx*/xy*", gdzie 'x*' dopasowuje 'x' na początku wiszącego kontekstu.
(Zauważ, że projekt POSIX-a określa, że dopasowany w takich wzorcach tekst jest
niezdefiniowany.)
.PP
Dla niektórych reguł wiszącego kontekstu, części które są w rzeczywistości
określonej długości nie są tak rozpoznawane. Prowadzi to do wspomnianej
wyżej straty wydajności. W szczególności, części używające '|' lub {n}
(takie jak "foo{3}") zawsze są uważane za zmienno-długościowe.
.PP
Łączenie wiszącego kontekstu z akcją specjalną '|' może spowodować, że
.I ustalony
(fixed) wiszący kontekst zostanie zmieniony w bardziej kosztowny, 
.I zmienny
wiszący kontekst. Na przykład następujące:
.nf

    %%
    abc      |
    xyz/def

.fi
.PP
Używanie
.B unput()
uszkadza yytext i yyleng, chyba że użyto dyrektywy
.B %array
lub opcji
.BR \-l . 
.PP
Dopasowywanie wzorców NUL-i jest znacznie wolniejsze niż dopasowywanie innych
znaków.
.PP
Dynamiczne zmiany rozmiaru bufora są wolne i wymagają reskanowania całego
tekstu dopasowanego dotąd przez bieżący (zwykle duży) token.
.PP
Z powodu buforowania wejścia i czytania z wyprzedzeniem, nie można łączyć
z regułami fleksa wywołań <stdio.h>, np.
.BR getchar() .
Zamiast tego wołaj
.BR input() . 
.PP
Wpisy całej tablicy (total table entries) wymieniane przez flagę
.B \-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 \fBREJECT\fR
jest równa liczbie stanów DFA, a w przeciwnym wypadku jest trochę większa.
.PP
.B REJECT
nie może być używany z opcjami
.B \-f
lub
.BR \-F .
.PP
Wewnętrzne algorytmy
.I fleksa
wymagają udokumentowania.
.SH ZOBACZ TAKŻE
.PP
lex(1), yacc(1), sed(1), awk(1).
.PP
John Levine, Tony Mason, and Doug Brown,
.IR "Lex & Yacc" ,
O'Reilly and Associates.  Upewnij się, że bierzesz 2-gie wydanie.
.PP
M. E. Lesk and E. Schmidt,
.I LEX \- Lexical Analyzer Generator
.PP
Alfred Aho, Ravi Sethi and Jeffrey Ullman,
.I Compilers: Principles, Techniques and Tools,
Addison-Wesley (1986).  Opisuje techniki dopasowywania wzorców używane przez
.I fleksa
(deterministyczne automaty skończone).
.SH AUTOR
Vern Paxson, z pomocą wielu pomysłów i inspiracji od
Vana Jacobsona.  Oryginalną wersję napisał Jef Poskanzer. 
Reprezentacja szybkiej tablicy jest częściową implementacją projektu
Vana Jacobsona. Implementacja została wykonana przez
Kevina Gonga and Verna Paxsona.
.PP
Podziękowania dla wielu beta testerów, komentatorów i kontrybutorów fleksa,
z których szczególnie zasłużone są następujące osoby:
Francois Pinard,
Casey Leedom,
Robert Abramovitz,
Stan Adermann, Terry Allen, David Barker-Plummer, John Basrai,
Neal Becker, Nelson H.F. Beebe, benson@odi.com,
Karl Berry, Peter A. Bigot, Simon Blanchard,
Keith Bostic, Frederic Brehm, Ian Brockbank, Kin Cho, Nick Christopher,
Brian Clapper, J.T. Conklin,
Jason Coughlin, Bill Cox, Nick Cropper, Dave Curtis, Scott David
Daniels, Chris G. Demetriou, Theo Deraadt,
Mike Donahue, Chuck Doucette, Tom Epperly, Leo Eskin,
Chris Faylor, Chris Flatters, Jon Forrest, Jeffrey Friedl,
Joe Gayda, Kaveh R. Ghazi, Wolfgang Glunz,
Eric Goldman, Christopher M. Gould, Ulrich Grepel, Peer Griebel,
Jan Hajic, Charles Hemphill, NORO Hideo,
Jarkko Hietaniemi, Scott Hofmann,
Jeff Honig, Dana Hudes, Eric Hughes, John Interrante,
Ceriel Jacobs, Michal Jaegermann, Sakari Jalovaara, Jeffrey R. Jones,
Henry Juengst, Klaus Kaempf, Jonathan I. Kamens, Terrence O Kane,
Amir Katz, ken@ken.hilco.com, Kevin B. Kenny,
Steve Kirsch, Winfried Koenig, Marq Kole, Ronald Lamprecht,
Greg Lee, Rohan Lenard, Craig Leres, John Levine, Steve Liddle,
David Loffredo, Mike Long,
Mohamed el Lozy, Brian Madsen, Malte, Joe Marshall,
Bengt Martensson, Chris Metcalf,
Luke Mewburn, Jim Meyering, R. Alexander Milowski, Erik Naggum,
G.T. Nicol, Landon Noll, James Nordby, Marc Nozell,
Richard Ohnemus, Karsten Pahnke,
Sven Panne, Roland Pesch, Walter Pelissero, Gaumond
Pierre, Esmond Pitt, Jef Poskanzer, Joe Rahmeh, Jarmo Raiha,
Frederic Raimbault, Pat Rankin, Rick Richardson,
Kevin Rodgers, Kai Uwe Rommel, Jim Roskind, Alberto Santini,
Andreas Scherer, Darrell Schiebel, Raf Schietekat,
Doug Schmidt, Philippe Schnoebelen, Andreas Schwab,
Larry Schwimmer, Alex Siegel, Eckehard Stolz, Jan-Erik Strvmquist,
Mike Stump, Paul Stuart, Dave Tallman, Ian Lance Taylor,
Chris Thewalt, Richard M. Timoney, Jodi Tsai,
Paul Tuinenga, Gary Weik, Frank Whaley, Gerhard Wilhelms, Kent Williams, Ken
Yap, Ron Zellar, Nathan Zelle, David Zuhn,
oraz ci, których nazwiska wyleciały z moich zdolności archiwizowania poczty,
lecz których wkład jest równie ważny.
.PP
Keith Bostic, Jon Forrest, Noah Friedman,
John Gilmore, Craig Leres, John Levine, Bob Mulcahy, G.T.
Nicol, Francois Pinard, Rich Salz i Richard Stallman pomogli z różnymi
problemami dystrybucji.
.PP
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.
.PP
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.
.PP
Komentarze ślij do vern@ee.lbl.gov.
.SH "INFORMACJE O TŁUMACZENIU"
Powyższe tłumaczenie pochodzi z nieistniejącego już Projektu Tłumaczenia Manuali i 
\fImoże nie być aktualne\fR. W razie zauważenia różnic między powyższym opisem
a rzeczywistym zachowaniem opisywanego programu lub funkcji, prosimy o zapoznanie 
się z oryginalną (angielską) wersją strony podręcznika za pomocą polecenia:
.IP
man \-\-locale=C 1 flex
.PP
Prosimy o pomoc w aktualizacji stron man \- więcej informacji można znaleźć pod
adresem http://sourceforge.net/projects/manpages\-pl/.
