Flex i Bison/Flex yywrap

Wstęp

edytuj

Czasami musimy znać o ilości znaków w pliku czy ogólnie uzyskać informacje o przeanalizowanym pliku. Właśnie tutaj dowiemy się pierwszych informacji o zmianie pliku wejściowego i misternej acz użytecznej funkcji yywrap().

YYwrap

edytuj

Czego flex oczekuje

edytuj

Zanim flex zacznie dopasowywać tekst jest najpierw buforowany. A więc widzimy że nie można tak łatwo zmienić pliku w locie (w buforze zostaną jeszcze znaki z poprzedniego pliku). Natomiast yywrap() jest wołany zawsze gdy bufor jest pusty i flex oczekuje że zamknie on uchwyt do pliku i ew. otworzy nowy plik podmieniając stary. Jak już wiemy zwrócenie 0 oznacza działaj dalej, wzkaźniki zostały podmienione. Zwrócenie wartości dodatnich oznacza że wszystko posprzątaliśmy a plik zamknęliśmy. Możemy również to wywołanie użyć w własnym celu też.

Przykład

edytuj

Chcemy obliczyć statystykę ilości słów w pliku. Oraz ile średnio znaków zawierają.

%{
#include <stdio.h>
unsigned long long int slowa;//zmiena na ilość słów (1)
unsigned long long int znaki;//zmiena na ilość liter
%}
%%
[a-zA-z]+	{//(2)
		znaki += yyleng;//zwiększamy znaki o ilość dopasowanych
		slowa += 1;//inkrementujemy slowa
		}
.|\n		{;}//(3)
%%
int	yywrap	(void)
	{//(4)
	fprintf(yyout,"Tekst miał:\n");
	fprintf(yyout,"\tSłów: %d\n",slowa);
	fprintf(yyout,"\tZnaków: %d\n",znaki);
	fprintf(yyout,"\tZnaków/Słów: %f\n",(float)znaki / (float)slowa);
	return 1;
	}

int	main	(int argc,char** argv)
	{
	slowa = 0;
	znaki = 0;
	return yylex();
	}
  1. Deklarujemy zmienne które będą nam potrzebne wewnątrz akcji.
  2. Ta akcja zwiększa nam zmienne statystyk.
  3. Ta reguła zastępuje akcję wbudowaną która wypisuje nierozpoznane pliki na wyjście.
  4. Wywołanie yywrap oznacza koniec pliku a wiec możliwość wypisania statystyk.

Przykład rozszerzony

edytuj

Skoro już możemy podmieniać pliki to czemu tego nie wykorzystać? Możemy listę plików czytać z argc/argv tylko tu rodzi się problem z tym że to są zmienne prywatne main oraz nie chcemy czytać samej aplikacji.

%{
#include <stdio.h>
unsigned long long int slowa;
unsigned long long int znaki;
int my_argc;//(1)
char** my_argv;
%}

%%
[a-zA-z]+	{
		znaki += yyleng;
		slowa += 1;
		}
.|\n		{;}
%%

int	yywrap	(void)
	{
	if	(yyin != 0)//(2)
		fclose(yyin);
	if	(my_argc == 0)//(3)
		{
		printf("Tekst miał:\n");
		printf("\tSłów: %d\n",slowa);
		printf("\tZnaków: %d\n",znaki);
		return 1;
		}
	yyin = fopen(*my_argv,"r");//(4)
	my_argc -= 1;//(5)
	++my_argv;
	return 0;
	}

int	main	(int argc,char** argv)
	{
	slowa = 0;
	znaki = 0;
	//kopiujemy argc i argv bez nazwy aplikacji
	my_argc = --argc;//(6)
	my_argv = ++argv;
	if	(my_argc == 0)//(7)
		yyin = 0;
	else
		{
		yyin = fopen(*my_argv,"r");//(8)
		my_argc -= 1;
		++my_argv;
		}
	return yylex();
	}
  1. Nasze globalne argc/argv bez nazwy aplikacji.
  2. Jeżeli mamy co zamknąć to zamykamy.
  3. Jeżeli nie mamy co dalej czytać wypisujemy statystykę.
  4. Inaczej otwieramy kolejny plik.
  5. Poprawiamy stan w naszym argc/argv
  6. Generujemy nasze argc/argv bez nazwy aplikacji.
  7. Jeżeli nie mamy co czytać to z std input.
  8. Inaczej otwieramy pierwszy plik na liście

Ćwiczenia

edytuj
  • Polskie znaki
Zrób klasę znaków w deklaracji i zamieść tam oprócz [a-z...]|0x..0x.. znaków polskich.
Poprawiony program ma dopasować "Piotruś kocha Asię bo jest słodka" 28znaków a nie 31.
  • Statystyka każdego pliku
Zmodyfikuj yywrap żeby pokazywał statystykę każdego pliku i jego nazwę.
  • Robisz program do oceny krótkiej formy wypowiedzi na egzamin.
Poniżej 100słów i poniżej 2znaki na słowo się nie zdaje.
Program wypisze nazwę pliku i "nie zdał" lub nazwę pliku i jego tekst.
  • Tak jak poprzednio program ma też punktować dodatnio I'm i'll etc i liczyć ilość znaków z rozwiniętej wersji.
Program ma liczyć tylko poprawne użycie tychże a nie np. ja'mac.
  • Podpowiedź do 2 ostatnich:
Niekoniecznie musisz kopiować tekst, możesz użyć tmpfile lub plik który przetwarzałeś przed chwilą a unikniesz przepełnienia bufora.

Zakończenie

edytuj

Wiemy już do czego użyć yywrap i jak oddzielić kod leksera od funkcji main. Wiele plików wejściowych nie jest już dla nas straszne. Dzięki następnemu rozdziałowi poznamy jak zmienić plik w "locie".

Poprzedni rozdział: Flex-Wprowadzenie
Następny rozdział: Flex-wiele plików wejściowych