Zdarza się, że dla tego samego wyrażenia regularnego powinny być wykonane różne akcje w zależności od kontekstu w jakim zachodzi dopasowanie. Taka sytuacja wymaga rozpoznawania kontekstu przez skaner.
Flex oferuje następujące metody rozpoznawania kontekstu:
^ - początek linii, $ - koniec linii, / - prawy kontekst.
%{ /* passwd.l Analiza zawartosci pliku passwd */ %} %% ^ewa printf("uzytkownik ewa ma konto w systemie\n"); :$ printf("uzytkownik nie ma zdefiniowanego shell-a\n"); .|\n ;
Źródła: passwd.l | test_passwd
%{ /* shutdown.l dopasowuje ciag shut, jezeli wystapi po nim ciag down */ %} %% shut/down printf("%s\n", yytext); .|\n /* empty */
Źródła: shudown.l
%{ /* jaguar1.l Rozpoznawanie znaczenia slowa jaguar w zaleznosci od kontekstu z wykorzystaniem flagi */ char flag; %} %% ^a flag = 'a'; ^b flag = 'b'; jaguar switch(flag){ case 'a': printf("Duzy kot\n"); break; case 'b': printf("Samochod sportowy\n"); break; } .|\n ;
Źródła: jaguar1.l
%{ /* jaguar2.l Rozpoznawanie znaczenia slowa jaguar w zaleznosci od lewego kontekstu przy uzyciu stanow */ %} %s A B %% ^a BEGIN(A); ^b BEGIN(B); <A>jaguar printf("Duzy kot\n"); <B>jaguar printf("Sportowy samochod\n"); .|\n ;
Źródła: jaguar2.l
%{ /* exclusive.l demonstruje dzialanie wylacznych stanow startowych */ %} %x LITERAL %% raz ECHO; dwa ECHO; # BEGIN(LITERAL); <LITERAL># BEGIN(INITIAL); . /* empty */
Źródła: exclusive.l | test_exclusive
Obiekt predefiniowany | Znaczenie |
---|---|
ECHO | przekopiowuje dopasowany ciąg znaków na wyjście |
yytext | dopasowany ciąg znaków |
yyleng | długość dopasowanego ciągu znaków |
yywrap() | zwraca 1, gdy jest jeszcze coś na wejściu do przetworzenia; w przeciwnym razie zwraca 0 |
yymore() | powoduje, że następny rozpoznany ciąg zostanie dopisany do aktualnego, w yytext otrzymamy ich konkatenację |
yyless(n) | pozostawia w yytext tylko n początkowych znaków, pozostałe zwraca z powrotem na wejście |
REJECT | oznacza: "przejdź do innego wariantu"; porzuca aktualne dopasowanie i powoduje przejście do alternatywnej reguły; aktualnie dopasowany ciąg zostaje zwrócony na wejście |
yyin | wejście |
yyout | wyjście |
input | pobiera następny znak z wejścia |
unput(c) | wypisuje znak c na wyjście |
yylval | zmienna globalna wykorzystywana do przekazania atrybutu tokenu przekazywanego do parsera |
Przykłady:
%{ /* meta-dane.l demonstruje dzialanie funkcji yymore */ %} %% meta- ECHO; yymore(); dane ECHO;
Źródła: meta-dane.l | test_meta-dane
%{ /* very_easy.l demonstruje wykorzystanie dyrektywy REJECT */ %} %% very printf("%s ",yytext); REJECT; [a-z]+ ECHO;
Źródła: very_easy.l
REJECT /= yyless(0);
%{ /* block.l Zliczanie wystapien ciagow znakow block i lock */ int block_c, lock_c; %} %% block { block_c++; /* Po dopasowaniu ciagu block wycofaj wszystkie znaki poza pierwszym do ponownej analizy */ yyless(1); } lock lock_c++; \n|. /* empty */ %% main(){ yyout = fopen("scanner_output", "a+"); yylex(); fprintf(yyout, "block - %d, lock - %d.\n", block_c, lock_c); fclose(yyout); }
Źródła: block.l | test_block
%{ /* pary.l Tworzy statystyke wystapien wszystkich par malych liter */ int digram [26][26]; int i, j; %} %% [a-z][a-z] { digram[yytext[0]-97][yytext[1]-97]++; REJECT; } .|\n /* empty */ %% main(){ /* wyzeruj tablice */ for(i=0; i<26; i++) for(j=0; j<26; j++) digram[i][j] = 0; yylex(); /* wypisz tablice */ for(i=0; i<26; i++) for(j=0; j<26; j++) if (digram[i][j] != 0) printf("%c,%c: %d\n", i+97, j+97, digram[i][j]); }
Źródła: pary.l
Drukowanie podsumowań często umieszcza się też w funkcji yywrap
.
Proszę zapoznać się z działaniem przykładów przedstawionych powyżej.
Proszę napisać skaner, który rozpoznaje poprawne wywołania programu flex:
flex [-+] [-oplik1] plik2
We | Wy |
---|---|
flex | wyświetlenie informacji pomocy |
flex example.l | ok |
flex -oexample.c example.l | ok |
flex example.c -oexample.l | bląd |
Proszę napisać skaner, który będzie usuwał komentarze typu /* */. Przymijmy, że komentarze nie mogą być zagnieżdżane.
Proszę napisać skaner, który będzie zliczał wystąpienia ciągów znaków 'on' i 'ona' w pliku podanym na wejściu. Uwaga: każdy ciąg 'ona' zawiera podciąg 'on'. Plik testowy test_onona.
Prosze napisać skaner, który zlicza w tekście wystąpienia ciągów znaków LR, SLR i LALR. Plik testowy test_lr.
Prosze napisać skaner, który sporządza statystykę słów w tekście podanym na wejściu na podstawie ich długości. Proszę wykorzystać funkcję yywrap do wypisania zestawienia. Plik testowy test_yywrap. Przykładowe wyjście:
Dlugosc | Liczba slow |
---|---|
1 | 4 |
2 | 4 |
3 | 1 |
4 | 4 |
5 | 2 |
6 | 5 |
7 | 2 |
8 | 5 |
9 | 1 |
Proszę przygotować skaner akceptujący zawartość pliku /etc/services
, który będzie rozpoznawał poszczególne elementy pliku oraz wyświetlał ich "typ" i "zawartość" (patrz poniżej). Przykładowo:
telnet 23/tcp http 80/tcp www www-http # WorldWideWeb HTTP
Źródła: test_services
Przykładowy wynik działania programu:
nazwa serwisu: telnet numer portu: 23 nazwa protokolu: tcp brak opisu nazwa serwisu: http numer portu: 80 nazwa protokolu: tcp nazwa aliasu: www nazwa aliasu: www-http opis: WorldWideWeb HTTP
W razie wystąpienia błędów składni program powinien wypisać komunikat o błędzie.
Zadanie polega na stworzeniu skanera do analizy logów w formacie tcpdump (tcpdump manual).
Skaner po wczytaniu prawidłowego logu ma wypisywać informację o godzinie, adresie hostów między którymi przesyłane są pakiety oraz o liczbie ewentualnie przesłanych danych, czyli dla wejścia:
11:57:13.923781 IP 149.156.99.111.1791 > 66.102.9.99.80: P 1560712269:1560712573(304) ack... 11:57:13.980726 IP 66.102.9.99.80 > 149.156.99.111.1791: . ack 304 win 7886 11:57:13.981908 IP 66.102.9.99.80 > 149.156.99.111.1791: . ack 304 win 6432 11:57:13.986793 IP 66.102.9.99.80 > 149.156.99.111.1791: P 1431:1636(205) ack 304 win 6432 11:57:13.987073 IP 149.156.99.111.1791 > 66.102.9.99.80: . ack 1 win 65535
ma dawać na wyjściu:
11:57:13 from 149.156.99.111 to 66.102.9.99 304 bytes 11:57:13 from 66.102.9.99 to 149.156.99.111 11:57:13 from 66.102.9.99 to 149.156.99.111 11:57:13 from 66.102.9.99 to 149.156.99.111 205 bytes 11:57:13 from 149.156.99.111 to 66.102.9.99
Linie w nieprawidłowym formacie powinny być pominięte. Proszę sprawdzać poprawność części logu znajdującej się przed :, w tym poprawność godziny i adresu IP (adres może być zbudowany z liczb z zakresu 0-255).
Przyładowy log tcpdump: test-log.