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();
}
- Deklarujemy zmienne które będą nam potrzebne wewnątrz akcji.
- Ta akcja zwiększa nam zmienne statystyk.
- Ta reguła zastępuje akcję wbudowaną która wypisuje nierozpoznane pliki na wyjście.
- 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();
}
- Nasze globalne argc/argv bez nazwy aplikacji.
- Jeżeli mamy co zamknąć to zamykamy.
- Jeżeli nie mamy co dalej czytać wypisujemy statystykę.
- Inaczej otwieramy kolejny plik.
- Poprawiamy stan w naszym argc/argv
- Generujemy nasze argc/argv bez nazwy aplikacji.
- Jeżeli nie mamy co czytać to z std input.
- 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".