Początek mojego projektu semestralnego z AUG (analiza pliku Java z klasą, ustalenie jakie ma zmienne, metody, po czym dziedziczy i co implementuje).
Wykład na temat bisona:
https://edux.pjwstk.edu.pl/mat/229/lec/frames-jfa-main-node14.html
Trochę czytelniejszy przykład:
http://fhoerni.free.fr/comp/bison_flex.html
——————————————-
A teraz mój kod. Co robi? Wczytuje pierwsze linijki pliku javy, czyli np. 'package cos.cos.aaa;’ i wiele linii 'import cos.cos.xxxx;’ lub z gwiazdką na końcu 'import cos.cos.*;’
Oto rysunki automatów które potem zrealizowałem w bisonie:
A teraz kod z komentarzami:
parser.y (bison)
%{ #include <stdio.h> #include <strings.h> %} /* w tej czesci definiujemy zmienne i struktury z jakich mamy zamiar korzystac w naszym programie */ /* pod spodem zmienna do przechowywania danych przekazywanych z flex'a oprocz 'tokenow' jakie dostajemy z flex'a czasem chcemy tez przekazac ich wartosc w moim przypadku oprocz tego, ze chce przekazac to, ze trafilem na 'IDENTIFIER' (nazwe zmiennej/klasy/metody/pakietu) to chce tez wiedziec jak sie ten identifier nazwywal (nazwa zmiennej/...) (wiecej info w nastepnym komentarzu) */ %union { char *sval; }; /* jak widac IDENTIFIER ktory oprocz samego wystapienia 'zmiennej' informuje mnie tez o jej nazwie ma przed soba nazwe zmiennej w ktorej bedzie przechowywana nazwa (w flex'ie tez trzeba to specjalnie obsluzyc, zeby zapamietac ta nazwe) wiecej informacji o obsludze tego w komenatrzu o tytule 'POKAZANIE IDENTIFIER' */ %token <sval> IDENTIFIER %token PACKAGE %token IMPORT %token DOT %token STAR %token DECNUMBER %token SEMICOLON /* powyzej zdefiniowalem 'tokeny' ktore moge zwracac [np. return(STAR); ] z flex'a i interpretowac ich kolejnosc w bisonie*/ %% /* w tej sekcji zapisuje reguly /* STEP 1 */ /* najpierw wczytuje 0 lub 1 'package' i przechodze dalej */ input1 : package1 input2 ; /* STEP 2 */ /* teraz wczytuje 0 do nieskonczonosci 'import' i przechodze dalej */ input2 : import0 input3 ; /* STEP 3 */ /* wypisuje 'koniec', normalnie przeszedl bym tutaj do wczytywanie nazwy klasy, ale calego kodu programu jeszcze nie mam.. */ input3 : {printf("KONIEC\n");} ; /* package - wczytanie 0 lub 1 */ package1 : PACKAGE {printf("package\n");} package2 | /* to definiuje, ze moze nie istniec wogole 'package' */ ; /* POKAZANIE IDENTIFIER: w linijce ponizej wyswietlam wartosc zmiennej IDENTIFIER uzywajac w kodzie '$1' te $1 przez 'bison' zostanie zastapione zmienna, ktora zawiera wartosc ostatnio wczytanego IDENTIFIER'a [wiecej o tym skad $1 czy $3 jest w wykladzie na edux] */ package2 : IDENTIFIER {printf("%s\n", $1);} package3 ; package3 : DOT {printf(".\n");} package4 | SEMICOLON {printf(";\n");} package5 ; package4 : IDENTIFIER {printf("%s\n", $1);} package3 ; package5 : {printf("\n[wczytano package]\n");}; /* import - wczytanie w petli 0 do nieskonczonosci */ /* dla wlasnej wygody zaczalem numerowac od '0' te opcje ktore beda w petli [w zerze definiuje petle], a od 1 te ktore moga wystapic tylko raz */ import0 : import0 import1 /* regula powyzej tworzy petle: 'wczytaj import przed importem [import0] i jeden 'import' [import1]' wczyta wszystkie import do poki jakies sa, samo sie auto-magicznie kapnie kiedy sie skoncza importy i wyjdzie z petli */ | /* regula powyzej (pusta) definiuje, ze moze nie byc ani jednego import i za razem pozwala na skonczenie petli (!) */ ; import1 : IMPORT {printf("import\n");} import2 ; import2 : IDENTIFIER {printf("%s\n", $1);} import3 ; import3 : DOT {printf(".\n");} import4 | SEMICOLON {printf(";\n");} import6 ; import4 : IDENTIFIER {printf("%s\n", $1);} import3 | STAR {printf("*\n");} import5 ; import5 : SEMICOLON {printf(";\n");} import6 ; import6 : {printf("\n[wczytano import]\n");}; %% /* ponizej juz tylko typowe formulki */ int yyerror(char *s) { printf("skladnia wczytywanego kodu jest nie poprawna! [error: %s]\n",s); } int main(void) { yyparse(); }
parser.lex (flex)
%{ // zalaczamy plik wygenerowany przez bisona #include "parser.h" %} identifier [_a-zA-Z]+[_a-zA-Z0-9]* decimalNumber [0-9]+ blanks [ \t\n]+ %% %{ /* komentarze w flex trzeba umieszczac w takim czyms, inaczej moze wyskoczyc error przy kompilacji */ %} <INITIAL>"." return(DOT); <INITIAL>"*" return(STAR); <INITIAL>";" return(SEMICOLON); %{ /* po 'package' i 'import' powinna byc co najmniej jedna 'spacja/tab/nowa linia', sprawdze to od razu w flex, zeby nie miec 100 razy w bisonie wczytywania tokena nowego 'BLANKS' */ %} <INITIAL>"package"{blanks}+ return(PACKAGE); <INITIAL>"import"{blanks}+ return(IMPORT); <INITIAL>{identifier} { %{ /* magiczny kod do przekazania nazwy wczytanej z wejscia, aby w bisonie znac nazwe zmiennej, a nie tylko fakt jej istnienia (chce nie tylko znac skladnie, ale tez wyswietlic nazwy zmiennych) */ %} yylval.sval = malloc(strlen((char*)yytext)); strncpy(yylval.sval, yytext, strlen((char*)yytext)); return(IDENTIFIER); } <INITIAL>{decimalNumber} return(DECNUMBER); <INITIAL>{blanks} { /* nic */ }
c.sh (kompilacja w 1 skrypcie)
bison -d parser.y mv parser.tab.h parser.h mv parser.tab.c parser.y.c flex parser.lex mv lex.yy.c parser.lex.c gcc -g -c parser.lex.c -o parser.lex.o gcc -g -c parser.y.c -o parser.y.o gcc -g -o parser parser.lex.o parser.y.o -lfl
jak kompilować projekt (mając 3 powyższe pliki w jednym katalogu na linux):
sh c.sh
jak odpalić swój program przekazując mu na wejście zawartość pliku dane1.txt
./parser < dane1.txt
Może ktoś coś z tego ogarnie.
Na linux trzeba zainstalować (komenda dla ubuntu – wpisuje się w 'Terminal’):
sudo apt-get install bison flex gcc
Na windows są podobno duże problemy, więc nie wiem czy warto marnować czas czy od razu zainstalować wirtualke (VMware Player) z linux (Ubuntu Desktop).