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).
