%{ /* Prvi deo prvog dela ove datoteke se doslovno prenosi na pocetak y.tab.c fajla, slicno kao i kod lex-a Ovde ukljucujemo neka zaglavlja, definisemo funkciju yyerror i deklarisemo yylex sto je obavezno */ #include #include #include /* maksimalan broj promenljivih */ #define MAX_PROM 16 void yyerror(char *str) { fprintf(stderr, "Pojavila se greska: %s\n", str); exit(EXIT_FAILURE); } /* Ovu funkciju cemo dobiti od lex sistema */ int yylex(); /* Ovde deklarisemo niz promenljivih duzine MAX_PROM od kojih svaki element sadrzi ime i vrednost promenljive. broj_promenljivih predstavlja koliko je do sada promenljivih upotrebljavano */ typedef struct { char *ime; double vrednost; } promenljiva; promenljiva promenljive[MAX_PROM]; int broj_promenljivih = 0; /* Funkcija koja nam za neki string vraca indeks u nizu vrednosi koji pokazuje na vrednost promenljive sa tim imenom ili -1 ako ta promenljiva nije do tada upotrebljavana */ int definisana(char *s) { int i; for (i=0; i num print op_dodele %token id /* Kako smo koristili viseznacnu gramatiku da bi razresili shift-reduce konflikte koji bi se javili pri prevodjenju, definisemo asocijativnost i prioritet sledecih tokena */ %left '+' '-' %left '*' '/' %left UMINUS /* I za neterminale moramo specificirati koji im je atribut pridruzen */ %type E %% /* Pocetni simbol je sad niz naredbi */ Niz_naredbi : Niz_naredbi Naredba { /* kada krenemo sa novom naredbom, moramo da postavimo ok na 1 jer mozda u prethodnoj naredbi nije bilo sve u redu */ ok = 1; } | ; Naredba : Naredba_print | Naredba_dodela ; Naredba_print : print E ';' { /* Ako u izgradnji E-a nije bilo promenljivih koje nisu definisane, ispisujemo vrednost E-a */ if (ok) printf("%f\n", $2); } ; Naredba_dodela : id op_dodele E ';' { int poz; /* ako je E validno */ if (ok) { /* Ako je promenljiva vec definisana samo menjamo njenu vrednost */ if ((poz = definisana($1))>=0) promenljive[poz].vrednost = $3; /* a ako nije, gledamo da li imamo mesta za definisanje nove promenljive */ else if (broj_promenljivih < MAX_PROM) { /* pamtimo novu promenljivu i njenu vrednost i povecavamo broj promenljivih u upotrebi */ promenljive[broj_promenljivih].ime = $1; promenljive[broj_promenljivih++].vrednost = $3; } else printf("dostignut maximalan broj promenljivih\n"); } } ; /* koristili smo viseznacnu gramatiku i za svako reduce pravilo smo definisali akcije Svaki token ima vrednost yylval koja je njemu pridruzena Po default-u ova vrednost je tipa int $$ - vrednost desne stane pravila $i - vrednost i-tog tokena na desnoj strani pravila */ E : E '+' E { $$ = $1 + $3; } | E '-' E { $$ = $1 - $3; } | E '*' E { $$ = $1 * $3; } | E '/' E { if ($3 == 0) yyerror("Deljenje sa nulom"); $$ = $1 / $3; } | '(' E ')' { $$ = $2; } /* Kako unarni minus ima veci prioritet i od +, -, * i / Uveli smo dodatni "lazni" token UMINUS sa vecim prioritetom, a za nas minus koji se pojavljuje u pravilima preciziramo da ima asocijativnos i prioritet kao UMINUS */ | '-' E %prec UMINUS { $$ = -$2; } | num { $$ = $1; } | id { int poz; if ((poz = definisana($1))<0) { printf("Promenljiva %s jos uvek nije definisana\n", $1); /* postavljamo indikator da nije sve kako treba */ ok = 0; } else $$ = promenljive[poz].vrednost; free($1); } ; %% /* Main funkcija */ int main() { return yyparse(); }