package ppdz;
import java_cup.runtime.*;
import java.io.*;


parser code {:
        
    // glavni program, kao metod klase parser
    public static void main(String args[]) throws Exception {
    		FileReader r = new FileReader(args[0]);
    		Yylex skener = new Yylex(r);
    		parser p = new parser(skener);
        /******************************************************************
        * Staticka metoda init tabele simbola sluzi za pocetno pravljenje 
        * tabele simbola, odnosno "universe" opsega.
        ******************************************************************/
    	  Tab.init();
    		Symbol s = p.parse();  //pocetak parsiranja
        /*****************************************************************
        * Po zavrsetku analize, pozivom metode dump(), vrsimo ispis 
        * sadrzaja tabele simbola.
        *****************************************************************/ 
    		Tab.dump();
    }
  
    /***********************************************************************
    * slede redefinisani metodi za prijavu gresaka radi izmene teksta poruke
    ***********************************************************************/ 
    public void report_fatal_error(String message, Object   info) throws java.lang.Exception {
      done_parsing();
      report_error(message, info);
    }
  
    public void syntax_error(Symbol cur_token) {
        report_error("\nSintaksna greska", cur_token);
    }
  
    public void unrecovered_syntax_error(Symbol cur_token) throws java.lang.Exception {
        report_fatal_error("Fatalna greska, parsiranje se ne moze nastaviti", cur_token);
    }

  public void report_error(String message, Object info)
    {
      System.err.print(message);
      System.err.flush();
      if (info instanceof Symbol)
	        System.err.println(" u liniji " + ((Symbol)info).left);
      else System.err.println("");
    }

:}

action code {:
    /*****************************************************************
    * Sledece dve promenljive ce biti vidljive iz akcija u svim 
    * smenama. var_type sluzi za cuvanje cvora Struct koji 
    * odgovara tipu na pocetku deklaracije grupe promenljivih.
    * isVoid je logicka promenljiva koja ima vrednost true ako
    * je povratni tip metode void. returnExists je logicka 
    * promenljiva koja ima vrednost true ako metoda ima 
    * return iskaz.
    *****************************************************************/ 
    Struct var_type=Tab.noType;
    boolean isVoid=false, returnExists=false; 
:}

terminal PLUS, MINUS, TIMES, DIV;
terminal EQUAL, SEMI, COMMA, LPAREN;
terminal RPAREN, LSQUARE, RSQUARE, LBRACE, RBRACE, CLASS;
terminal PRINT, RETURN, VOID, FINAL;
terminal Integer NUMBER;
terminal Character CHARCONST;
terminal String IDENT;

non terminal program, declaration_list, method_declaration_list;
non terminal var_dec, rhs, var_list, var_part, const_dec;
non terminal method_dec, form_pars, stmt_list, parameter;
non terminal statement, designator, expr, act_pars, local_var_list;
non terminal addop, mulop, expr_list, term_list, term;
non terminal factor, factor_list, parameter_list, declaration_part;
non terminal Struct type;
non terminal Obj ident_expr_list, prog_id, return_type_ident;

/*****************************************************************
* o predstavlja Obj cvor glavne klase. Na kraju analize programa
* se unutrasnji opseg zatvara, a njegovi cvorovi vezuju za
* cvor glavne klase, tj. programa. 
*****************************************************************/ 
program ::= CLASS prog_id:o declaration_list LBRACE method_declaration_list RBRACE
	            {: o.locals=Tab.topScope.locals;
                 Tab.closeScope();
		          :};

/*****************************************************************
* Kada prepoznamo glavni program, u tabelu simbola ubacujemo 
* cvor koji mu odgovara i otvaramo novi opseg u koji cemo ubacivati 
* simbole glavnog programa.
*****************************************************************/ 
prog_id ::= IDENT: id
                {: RESULT = Tab.insert(Obj.Prog,id,idleft,Tab.noType);
                   Tab.openScope(); 
                :};

declaration_list ::= declaration_list declaration_part
			   |
			   /* epsilon */;

declaration_part ::= const_dec 
	               |
	               var_dec;
			
const_dec ::= FINAL type IDENT EQUAL rhs SEMI;

/*****************************************************************
* Kada parser prepozna da je naisao na identifikator tipa,
* neterminalu type se dodeljuje odgovarajuci Struct cvor.
* U deklarativnom delu za neterminale smo deklarisali da je 
* type tipa Struct, pa mu objekat tog tipa dodeljujemo koristeci
* predefinisanu promenljivu RESULT, koja implicitno oznacava 
* vrednost leve strane svake smene (left hand side).
*****************************************************************/ 
type ::= IDENT: id
             {: Obj novi=Tab.find(id, idleft);
                if (novi.kind == Obj.Type )
                   RESULT = novi.type;
                else {
                   parser.report_error("Greska u liniji "+idleft+" ("+id+") nije tip",null);
                   RESULT=Tab.noType;
               }
             :} 
;

rhs ::= NUMBER 
	  | 
	  CHARCONST; 

/*****************************************************************
* Da bi nam i pri uparivanju neterminala var_list bio dostupan
* tip grupe promenljivih koje se deklarisu, sacuvacemo ga u 
* promenljivoj var_type.
*****************************************************************/ 
var_dec ::= type: t {: var_type=t; :} var_list SEMI;

var_list ::= var_list COMMA var_part 
		 | 
 		 var_part;

/*****************************************************************
* Kada u deklaraciji dodjemo do imena pojedinacne promenljive,
* ubacujemo je u tabelu simbola (cak i kada je tip noType, da bismo u
* oporavku izbegli greske nedeklarisane promenljive)
*****************************************************************/ 
var_part ::= IDENT: id 
                  {: Tab.insert(Obj.Var, id, idleft, var_type);
                  :} 
		 |
		 IDENT: id LSQUARE RSQUARE
                  {: Tab.insert(Obj.Var, id, idleft, new Struct(Struct.Arr, var_type));
                  :};

local_var_list ::= local_var_list var_dec
	         	   |
	             /* epsilon */;

method_declaration_list ::= method_declaration_list method_dec
		      	    |
		      	    /* epsilon */;

return_type_ident ::= type:t IDENT:id
		             {: isVoid = (t == Tab.noType );
		                RESULT = Tab.insert( Obj.Meth, id, idleft, t); 
                    Tab.openScope(); 
		             :} 
		        | 
		        VOID IDENT:id
                  {: isVoid=true; 
		                RESULT = Tab.insert( Obj.Meth, id, idleft, Tab.noType); 
                    Tab.openScope(); 
                  :};

/*****************************************************************
* Na kraju obrade metoda proveravamo da li se radi o metodu
* koji nije void, a u njegovom telu nema return iskaza. 
* Ako je to slucaj, prijavljuje se greska.
*****************************************************************/ 
method_dec ::= return_type_ident:o LPAREN form_pars RPAREN local_var_list LBRACE stmt_list RBRACE
		             {:
		                  if (!isVoid && !returnExists) { 
			                    parser.report_error("Greska u liniji "+oleft+": Metod mora imati return iskaz jer nije deklarisan sa void",null);
			                }
                      returnExists=false;
                      o.level=0; o.locals = Tab.topScope.locals; Tab.closeScope();
	               :}
           ;
           
form_pars ::= parameter_list
	        |
	        /* epsilon */;
		
parameter_list ::= parameter_list COMMA parameter
	             |
	             parameter;

parameter ::= type IDENT
	  	  | 
		  type IDENT LSQUARE RSQUARE;


stmt_list ::= stmt_list statement
	        |
	        /* epsilon */;

/*****************************************************************
* Pri prepoznavanju return iskaza postavljamo promenljivu 
* returnExists. Ako metoda koja nije void ima prazan return 
* iskaz, prijavljuje se greska. Ako void metoda im return 
* iskaz koji vraca vrednost, prijavljuje se greska.
*****************************************************************/
statement ::= designator EQUAL expr SEMI 
		      |
	        designator LPAREN RPAREN SEMI      
	        |
	        designator LPAREN act_pars RPAREN SEMI
	        |
	        RETURN:dummy SEMI // dummy samo zbog broja linije, nema semant. vrednost
		         {: 
	               returnExists=true;
		             if (!isVoid)
			               parser.report_error("Greska u liniji "+dummyleft+": metod mora imati return iskaz sa izrazom jer nije deklarisan sa void",null);
	           :}
	        |
	        RETURN:dummy expr SEMI // dummy samo zbog broja linije, nema semant. vrednost
		         {: 
	               returnExists=true;
		             if (isVoid)
			                parser.report_error("Greska u liniji "+dummyleft+": metod ne sme imati return sa izrazom jer je deklarisan sa void",null);
	           :}
	        |
	        PRINT LPAREN expr RPAREN SEMI;

designator ::= ident_expr_list;

/*****************************************************************
* Kada u telu metode detektujemo koriscenje nekog imena, 
* upotrebom metode find, proveravamo da li se objekat koji 
* odgovara tom imenu nalazi u tabeli simbola. Ako se ne nalazi
* prijavljujemo gresku. Ako se radi o koriscenju niza, moramo 
* proveriti i da li je objekat u tabeli simbola tipa niza.
*****************************************************************/ 
ident_expr_list ::= ident_expr_list: id  LSQUARE expr RSQUARE
                       {: if (id.type.kind!=Struct.Arr) 
                              parser.report_error("Greska u liniji "+idleft+": Ocekivan niz",null);
                          RESULT=id;
                       :}
	              |
	              IDENT: id
                       {:
    		                   Obj ob=Tab.find(id, idleft); // find prijavljuje gresku u slucaju da ne nadje
                           if (ob != Tab.noObj) 
                               System.out.println("Pretraga "+idleft+" ("+id+"), nadjeno "+ob); 
                           RESULT=ob; 
                       :}
                ;

addop ::= PLUS 
	    | 
	    MINUS;

mulop ::= TIMES 
	    | 
       DIV;

act_pars ::= expr_list;

expr_list ::= expr_list COMMA expr 
	        |
	        expr;

expr ::= term_list;
		
term_list ::= term_list addop term
	        |
	        term;

term ::= factor_list;

factor_list ::= factor_list mulop factor
	          |
	          factor
            |
            MINUS factor;

factor ::= designator LPAREN act_pars RPAREN   
           |                                                                              
           designator                           
           |
           designator LPAREN RPAREN      
           |
           NUMBER             
           |
           CHARCONST     
           |
           LPAREN expr RPAREN; 
