Stworzenie prostego leksera będzie składać się z następujących kroków:
- Stworzenie pliku lexa.
- Wygenerowanie kodu.
- Skompilowanie całości do postaci wykonywalnej.
- Test.
Naszym celem w trakcie tego poradnika jest stworzenie prostego leksera, który każde wystąpienie słowa username będzie zastępować naszym prawdziwym loginem (za poradnikiem którego adresu nie pamiętam:P).
Stworzenie pliku lexa.
cała struktura pliku lexa składa się z trzech sekcji:
definicje
%%
reguły
%%
kod użytkownika
- definicje
- definiują używane symbole (bardzo często za pomocą wyrażeń regularnych), np. "jeśli napotkasz znak od 0 do 9 to wiedz, że jest to symbol cyfra". Często znajduje się tutaj też dodatkowa podsekcja dodawania do wygenerowanego kodu dodatkowych deklaracji języka C i C++ (takich jak deklaracje zmiennych globalnych, instrukcje włączania #include itp.).
- reguły
- określają, jaki kod wygenerować dla danego symbolu, np. "jeśli napotkany symbol jest liczbą, to zrób to i to".
- kod użytkownika
- kod który ma być dodany do wygenerowanego pliku. Najczęściej jest to funkcja main, w której m. in. wywoływana jest funkcja yylex();
Skoro już wiemy, co znajduje się w każdej sekcji, możemy przystąpić do pisania własnego pliku.
Nasza struktura będzie oparta na takiej koncepcji:
nic nie definiujemy, bo nie mamy takiej potrzeby
%%
jeśli napotkasz ciąg znaków "username", to w jego miejsce wypisz mój login
%%
main
{
wywołaj funkcją yylex()
}
Teraz przeniesiemy to do prawdziwego kodu. Do wypisania loginu wykorzystamy funkcję systemową Linuxa: getlogin().
%% username printf("%s", getlogin()); %% int main(int argc, char** argv) { yylex(); //wywołujemy funkcję yylex(), która zostanie dla nas wygenerowana. return 0; }
W ten sposób mamy gotowy plik lexa. Nazwijmy go sobie user.l
Wygenerowanie kodu.
Kod za pomocą lexa generuje się w najprostszym wypadku następującym poleceniem:
flex nazwapliku
Na skutek tego wywołania powinien powwstać plik o nazwie lex.yy.c. Zawiera on także naszego maina, którego napisaliśmy w pliku user.l. Można też, jeśli komuś nie podoba się nazwa wygenerowanego pliku, zastosować przełącznik -o, np.
flex -o user.c user.l
Jedyna różnica jest w nazwie wygenerowanego pliku (tu nazywa się user.c zamiast lex.yy.cc). Dalej zakładam, że plik nosi standardową nazwę lex.yy.cc.
Skompilowanie całości do postaci wykonywalnej.
Przy kompilacji należy pamiętać o jednej rzeczy - zlinkowaniu biblioteki flexa za pomocą przełącznika -lfl. Cała reszta jest standardowa:
gcc lex.yy.c -o user -lfl
Lub, jeśli ktoś woli:
gcc lex.yy.c -o user -Wall -pedantic -O2 -lfl
Wyprodukuje to plik wykonywalny o nazwie user.
Test.
Pozostaje teraz odpalenie programu user. Będzie on konwertować łańcuch "username" w wpisanym przez nas tekście na nasz login. Przykładowa sesja z programem może wyglądać tak:
astral@astral-laptop:~/Programming/flex$ ./user
Mój login to username. Podkreślam jeszcze raz: username!
Mój login to astral. Podkreślam jeszcze raz: astral!
Literatura:
Dokumentacja flexa, z której pochodzi zamieszczony przykład.
Bardzo zwięzłe wprowadzenie. Zero lania wody. Same konkrety.