%{
/* Funkcija za obradu greske */
#define yyerror printf

/* Broj prve neiskoriscene labele */
int free_label=0;

%}

/* Specijalni token cija je jedina svrha da  
   ima prioritet manji od tokena else i da 
   razresi IF-THEN-ELSE viseznacnost koja nastaje
   u gramatici */
%nonassoc IF_THEN
%nonassoc ELSE

/* Prioriteti i asocijativnost operatora */
%right '='
%left '<' '>' EQ LEQ GEQ
%left '+' '-' 
%left '*' '/'
%right PP MM

/* Tokenima dodeljujemo broj koji odgovara labeli 
   koja je dodeljena odgovarajucoj naredbi */
%token <int_value> WHILE IF ELSE
%token PRINT

/* Tokenima dodeljujemo tekst odgovarajuce lekseme */
%token <str_value> NUM ID

/* Tip steka definisemo tako da na njega mozemo 
   stavljati cele brojeve i stringove */
%union
{ int int_value;
  char* str_value;
}

%%

/* Instrukcija stop je poslednja u nizu */
program : stmts {printf("\tstop\n");}
        ;

stmts   : stmts stmt 
        | 
        ;

stmt :  ';' /* prazna naredba */
     |  expr ';' /* izraz */
     |  '{' stmts '}' /* blok naredbi */
     |  /*    while (expr) 
                 stmt
 
           se prevodi u 

               Lxxx : 
                     asm<expr>
                     jz Lxxx+1
                     asm<stmt>
                     jmp Lxxx
               Lxxx+1:
        */
                 
         
        
         WHILE 
             {   /* Tokenu WHILE dodeljujemo 
                 prvu slobodnu labelu (Lxxx) */
                 $1=free_label; 

                 /* Ispisujemo labelu Lxxx: */
                 printf("L%03d:\n",$1);

                 /* Za obradu while konstrukta ce
                 biti koriscene dve labele i odmah 
                 zbog toga rezervisemo i sledecu */
                 free_label+=2; 
                  
             } 
        '(' expr ')'  /* asm<expr> */
             {  /* Ispisujemo : jz Lxxx+1 */ 
                printf("\tjz L%03d\n",$1+1);   }  
        stmt /* asm<stmt> */
             { /* Ispisujemo 
                         jmp Lxxx
               printf("\tjmp L%03d\n",$1); 
               /* Ispisujemo Lxxx+1 */
               printf("L%03d:\n",$1+1);
             }
     |  /* Zajednicki deo if-then i if-then-else grananja 
           Viseznacnost se resava u pravilima za opt_else.
           U oba slucaja potrebno je ispisati 
               asm<expr>
               jnz Lxxx
               asm<stmt>
           Tokenu IF se pridruzuje vrednsot xxx zbog mogucnosti 
           kasnijeg referisanja 
        */

        IF '(' expr ')'  /* asm<expr> */
             { /* Rezervisemo slobodnu labelu za Lxxx */
               $1=free_label++; 
               printf("\tjnz L%03d\n",$1); 
             } 
        stmt /* asm<stmt> */
        opt_else /* Opciono else */

     |  
        /* Naredba dodele se prevodi u :
           asm <expr>
           pop ID
        */

       ID '=' expr ';'  /* asm <expr> */
             { printf("\tpop %s\n",$1); 
               free($1);  
             }

     |  /* Funkcija printf se prevodi u 
           asm <expr>
           print
        */
        PRINT '(' expr ')' ';'  /* asm<expr> */
             { printf("\tprint\n");  }
     ;

opt_else :   
           /* Epsilon pravilo govori o tome da se radi
              o IF-THEN grananju sto znaci da se zapoceti 
              asemblerski kod zavrsava definisanjem labele
                  Lxxx */
           {  /* Broj xxx je pridruzen tokenu IF i pristupamo
                 mu koriscenjem levog konteksta */
              printf("L%03d:\n",$<int_value>-5);
           }
           /* Dajemo ovom pravilu manji prioritet od sledeceg */
           %prec IF_THEN 
         | /* Grananje IF-THEN-ELSE TIPA. Zapoceti asemblerski
              kod dopunjavamo sa 
                   jmp Lyyy
               Lxxx:
                   asm<stmt>
               Lyyy:
           */
           ELSE 
              { /* Tokenu ELSE dodeljujemo vrednost broja Lyyy */
                $1=free_label++;
                printf("\tjmp L%03d\n",$1); 
                /* Vrednost xxx se nalazi na steku i pridruzena je 
                tokenu IF. Pristupamo joj koristeci levi kontekst */
                printf("L%03d:\n",$<int_value>-6);
              }
           stmt /* asm<stmt> */
              {  /* Vrednost yyy je pridruzena tokenu ELSE na steku */
                 printf("L%03d:\n",$1); 
              }
         ;


/*      epxr op expr 
   se prevodi u 
        asm<expr1> 
        asm<expr2>
        op
*/

expr    : expr '+' expr 
             {  printf("\tadd\n");      }
        | expr '-' expr 
             {  printf("\tsub\n");      }
        | expr '*' expr 
             {  printf("\tmul\n");      }
        | expr '/' expr 
             {  printf("\tdiv\n");      }
        | expr '<' expr 
             {  printf("\tcompLT\n");   }
        | expr '>' expr 
             {  printf("\tcompGT\n");   }
        | expr EQ  expr 
             {  printf("\tcompEQ\n");   }
        | expr LEQ  expr 
             {  printf("\tcompLEQ\n");  }
        | expr GEQ  expr 
             {  printf("\tcompGEQ\n");  }
        | '(' expr ')'

        | ID PP 
             {  printf("\tpush %s\n",$1);
                printf("\tpush %s\n",$1);
                printf("\tpush 1\n");
                printf("\tadd\n");
                printf("\tpop %s\n",$1);
                printf("\tpop\n"); 
                free($1);
             }
        | PP ID 
             {  printf("\tpush %s\n",$2);
                printf("\tpush 1\n");
                printf("\tadd\n");
                printf("\tpop %s\n",$2); 
                free($2); 
             }


/* Tokenima ID i NUM je tokom leksicke analize dodeljen 
   tekst odgovarajuce lekseme.
*/
        | ID  
             {  printf("\tpush %s\n",$1); 
                free($1); 
             }
        | NUM 
             {  printf("\tpush %s\n",$1); 
                free($1); 
             }
        ;
        
        

%%

main()
{   
    return yyparse();
}

