Analiza leksykalna cz. 2

Rozpoznawanie kontekstu

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:

Predefiniowane zmienne i funkcje

Obiekt predefiniowanyZnaczenie
ECHOprzekopiowuje dopasowany ciąg znaków na wyjście
yytextdopasowany ciąg znaków
yylengdł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
REJECToznacza: "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
yyinwejście
yyoutwyjście
inputpobiera następny znak z wejścia
unput(c)wypisuje znak c na wyjście
yylvalzmienna 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.

Ćwiczenia

Ćwiczenie 1

Proszę zapoznać się z działaniem przykładów przedstawionych powyżej.

Ćwiczenie 2

Proszę napisać skaner, który rozpoznaje poprawne wywołania programu flex:

flex [-+] [-oplik1] plik2

WeWy
flexwyświetlenie informacji pomocy
flex example.lok
flex -oexample.c example.lok
flex example.c -oexample.lbląd

Ćwiczenie 3

Proszę napisać skaner, który będzie usuwał komentarze typu /* */. Przymijmy, że komentarze nie mogą być zagnieżdżane.

Ćwiczenie 4

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.

Ćwiczenie 5

Prosze napisać skaner, który zlicza w tekście wystąpienia ciągów znaków LR, SLR i LALR. Plik testowy test_lr.

Ćwiczenie 6

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:

DlugoscLiczba slow
14
24
31
44
52
65
72
85
91

Z poprzedniego laboratorium...

Ćwiczenie 5

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.

Ćwiczenie 6

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.