[AUG] Projekt semestralny – wersja demo

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:

bison

 

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

Ten wpis został opublikowany w kategorii PJWSTK – AUG. Dodaj zakładkę do bezpośredniego odnośnika.

Dodaj komentarz

Twój adres e-mail nie zostanie opublikowany. Wymagane pola są oznaczone *