import java_cup.runtime.*;
import java.io.*;
import java.util.*;
import symtab.*;
import type.*;

/* MiniJava  front end for CUP.  
 * Copyright (C) 2006 Silvano Rivoira <silvano.rivoira@polito.it>
 * This program is released under the terms of the GPL; see the file
 * COPYING for more details.  There is NO WARRANTY on this code.
 */

parser code  {: 

  public static Scanner s;
  public static int errors = 0;
  public static int warnings = 0;
  public static boolean first = true, second = false;
  public static StringBuffer code = new StringBuffer();
  public static String objLoc = "null";

  public static void main(String argv[]) {

    for (int i = 0; i < argv.length; i++) {
      try { System.out.println("\n... Creating Symbol-Table for \""+argv[i]+"\" ...\n");
	  	s = new Scanner(new FileReader(argv[i]));
        	mjavac p = new mjavac(s);
        	p.parse();
        	System.out.println("Number of errors = " + errors + ".");
        	System.out.println("Number of warnings = " + warnings + ".");
		Type.printTypes();
	  	first = false;
	  	second = true;
        	System.out.println("\n... Generating intermediate code for \""+argv[i]+"\" ...\n");
		code.append("		; ModuleID = '"+argv[i]+".ll'\n\n");
		code.append("target datalayout = \"e-p:32:32:32-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:32:64-f32:32:32-f64:32:64-v64:64:64-v128:128:128-a0:0:64-s0:0:64-f80:32:32\"\n");
		code.append("target triple = \"i686-pc-linux-gnu\"\n\n");
		code.append("		; Global values\n");
		Set glob = Env.getGlobals().entrySet();
  		Iterator it = glob.iterator();
		while(it.hasNext()) { Map.Entry entry = (Map.Entry)it.next();
					    String key = (String)entry.getKey();
					    String value = (String)entry.getValue();
					    if (value.equals("struct")) code.append(key+"\n");
					    else { int lth = key.length() + 1;
						     code.append(value+" = internal constant ["+lth+" x i8] "+" c\""+key+"\\00\"\n");
					    }
		}
		code.append("\n");
		code.append("		; External declarations\n");
		code.append("declare i32 @printf(i8*, ...)\n");
		code.append("declare i32 @scanf(i8*, ...)\n\n");
        	s = new Scanner(new FileReader(argv[i]));
	  	p = new mjavac(s);
        	p.parse();
        	System.out.println("Number of errors = " + errors + ".");
        	System.out.println("Number of warnings = " + warnings + ".");
		Type.printTypes();
	  	if (errors == 0) try { FileOutputStream  fStream;
   					     PrintWriter  myStream;
   			      	     fStream = new FileOutputStream(argv[i]+".ll");
   					     myStream = new PrintWriter(fStream);
   					     myStream.println(code); 
   					     myStream.flush();
   					     myStream.close();
        				     System.out.println("\nCode written to \""+argv[i]+".ll\".");
				     }
				     catch (IOException e) { e.printStackTrace(System.out);
        			    				     System.exit(1);
      			     }
		else System.out.println("\nNo code produced.");
		System.out.println("--------------------------------------- (mjavac)");
      }
      catch (Exception e) { e.printStackTrace(System.out);
        			    System.exit(1);
      }
    }
  }
  
  public void report_error(String message, Object info) {
    if (info instanceof String){
	errors++; 
      System.err.println("  "+ errors + "==> " + info + " "+ message + 
					"\n       Parsing resumed from 2nd token before" + s.current_lexeme()+"\n");
    }
    else {
    	StringBuffer m = new StringBuffer("Error ");
    	if (info instanceof java_cup.runtime.Symbol) 
     	   m.append( "("+info.toString()+")" );     
    	m.append(" : "+message);   
    	System.err.println(m);
    }
  }

  public void sem_error(String lexeme, String message) {
	errors++;
     	System.err.println("Error "+ s.current_lexeme() + " : Semantic error");
      System.err.println("  "+ errors + "==> " + message + ": "+ lexeme + "\n");
  }

  public void warning(String lexeme, String message) {
	warnings++;
     	System.err.println("Warning "+ s.current_lexeme());
      System.err.println("  "+ warnings + "==> " + message + ": "+ lexeme + "\n");
  } 
  
  public void report_fatal_error(String message, Object info) {
    	report_error(message, info);
    	throw new RuntimeException("Fatal Syntax Error");
  }

:};

action code {:

	class Sname {
	  public String name;
	  public Symb cs;
	  public Symb rs;

	  Sname(String n, Symb c, Symb r) {
		name = n;
		cs = c;
		rs = r;
	  }

	  public String toString(){
		return "Sname["+name+", "+cs+", "+rs+"]";
	  }
	}

	class Exp {
	  public Type type;
	  public String locT;
	  public String locN;
	  public Labels lab;

	  Exp(Type t, String lt, String ln) {
		type = t;
		locT = lt;
		locN = ln;
	  }

	  Exp(Type t, Labels l) {
		type = t;
		lab = l;
	  }

	  Exp(Type t, String lt, String ln, Labels l) {
		type = t;
		locT = lt;
		locN = ln;
		lab = l;
	  }

  	  public void load () {
		if(locN.startsWith("getelementptr")) { parser.code.append("	"+Tmp.nv()+" = "+locN+"\n");
								   locN = Tmp.lv();
		}
		if(locT.endsWith("*") && !locT.startsWith("%struct")) { parser.code.append("	"+Tmp.nv()+" = load "+locT+" "+locN+"\n");
					   	 					  locT = locT.replaceFirst("\\*", "");
					   	 					  locN = Tmp.lv();
		}
  	  }

  	  public String widen(Type w) {
		if(type == w) return locN;
 		if(type.isInteger() && w.isFloating()) parser.code.append("	"+Tmp.nv()+" = sitofp i32 "+locN+" to double\n");
 		if(type.isCharacter() && w.isFloating()) parser.code.append("	"+Tmp.nv()+" = sitofp i8 "+locN+" to double\n");
 		if(type.isCharacter() && w.isInteger()) parser.code.append("	"+Tmp.nv()+" = sext i8 "+locN+" to i32\n");
		return Tmp.lv();
  	  }

	}

	class ParamList {
	  public Type type;
	  public List list;

	  ParamList(Type t, List l) {
		type = t;
		list = l;
	  }
	}

	class Labels {
	  public String lBegin;
	  public String lTrue;
	  public String lFalse;
	  public String lNext;
	  public boolean bvalue;
	  public boolean bvDone;

	  Labels() {
		lBegin = Tmp.nl();
		lTrue = Tmp.nl();
		lFalse = Tmp.nl();
		lNext = Tmp.nl();
		bvalue = false;
		bvDone = false;
	  }
	}

	class Vars {
	  public Env e;
	  public List vl;

	  Vars(Env ee, List l) {
		e = ee;
		vl = l;
	  }
	}

:};

init with {:
	if(first) {	Type.initTypes();
			Env.initFirst();
	}
	if(second) Env.initSecond();
:};

terminal BOOLEAN; // primitive_type
terminal INT, CHAR, FLOAT; // numeric_type
terminal LBRACK, RBRACK; // array_type
terminal DOT; // qualified_name
terminal SEMICOLON, MULT, COMMA, LBRACE, RBRACE, EQ, LPAREN, RPAREN, COLON;
terminal PUBLIC; // modifier
terminal CLASS; // class_declaration
terminal EXTENDS; // super
terminal VOID; // method_header
terminal THIS, SUPER; // explicit_constructor_invocation
terminal AT; // reference operator
terminal IF, ELSE; // if_then_statement, if_then_else_statement
terminal WHILE; // while_statement, do_statement
terminal RETURN; // return_statement
terminal NEW; // class_instance_creation_expression
terminal PLUS, MINUS, NOT, DIV, MOD;
terminal LT, GT, LTEQ, GTEQ; // relational_expression
terminal EQEQ, NOTEQ; // equality_expression
terminal ANDAND; // conditional_and_expression
terminal OROR; // conditional_or_expression
terminal QUESTION; // conditional_expression
terminal ILLEGAL_CHARACTER; // illegal_character

terminal java.lang.Number INTEGER_LITERAL;
terminal java.lang.Number FLOATING_POINT_LITERAL;
terminal java.lang.Boolean BOOLEAN_LITERAL;
terminal java.lang.Character CHARACTER_LITERAL;
terminal java.lang.String STRING_LITERAL;
terminal java.lang.String IDENTIFIER; // name
terminal NULL_LITERAL;


// 1) The Syntactic Grammar
non terminal goal;

// 2) Lexical Structure
non terminal Exp literal;

// 3) Types, Values, and Variables
non terminal Type type, primitive_type, numeric_type;
non terminal Type reference_type;
non terminal Type array_type;

// 4) Names
non terminal Sname name;
non terminal Type M1;

// 5) Classes
non terminal class_declarations;

// 5.1) Class Declaration
non terminal class_declaration;
non terminal class_body, class_body_opt;
non terminal Boolean modifiers_opt;
non terminal class_body_declarations, class_body_declarations_opt;
non terminal class_body_declaration, class_member_declaration;

// 5.2) Field Declarations
non terminal field_declaration, variable_declarators;
non terminal String variable_declarator_id;

// 5.3) Method Declarations
non terminal method_declaration, M3;
non terminal Type method_header;
non terminal ParamList formal_parameter_list_opt, formal_parameter_list;
non terminal String formal_parameter;
non terminal method_body;

// 5.4) Constructor Declarations
non terminal constructor_declaration;
non terminal Type constructor_declarator;
non terminal constructor_body;
non terminal explicit_constructor_invocation;

// 6) Blocks and Statements
non terminal block, M2;
non terminal block_statements_opt, block_statements, block_statement;
non terminal local_variable_declaration_statement;
non terminal statement, statement_no_short_if;
non terminal statement_without_trailing_substatement;
non terminal empty_statement;
non terminal expression_statement, statement_expression;
non terminal if_then_statement;
non terminal if_then_else_statement, if_then_else_statement_no_short_if;
non terminal while_statement, while_statement_no_short_if;
non terminal return_statement;

// 7) Expressions
non terminal Exp primary, primary_no_new_array;
non terminal Exp class_instance_creation_expression;
non terminal ParamList argument_list_opt, argument_list;
non terminal Exp array_creation_expression;
non terminal Exp dim_exprs, dim_expr, dims_opt;
non terminal dims;
non terminal Labels M4, M5, M6, M7, M8, M9, M10, M11, M12, M13, M14;
non terminal Exp field_access, method_invocation, array_access;
non terminal Exp postfix_expression;
non terminal Exp unary_expression;
non terminal Exp multiplicative_expression, additive_expression;
non terminal Exp relational_expression, equality_expression;
non terminal Exp conditional_and_expression, conditional_or_expression;
non terminal Exp conditional_expression, assignment_expression;
non terminal Exp assignment;
non terminal Exp left_hand_side;
non terminal Exp expression_opt, expression;


start with goal;

// 1) The Syntactic Grammar
goal ::=
		class_declarations
 			{: Hashtable forwards;
			   if(parser.first) { forwards = Name.ForwardHashtable();
						    int err = forwards.size();
						    if(err > 0) { parser.errors = parser.errors + err;
     						    	     		System.err.println("Error : Semantic error");
      					    	     		System.err.println("  ==> CANNOT FIND CLASSES ("+err+"): "+forwards+"\n");
						    }
			   }
			:}
	;

// 2) Lexical Structure.
literal ::=
		INTEGER_LITERAL:l 
			{: 
			   RESULT = new Exp(Type.integer(), "i32", ""+l); 
			:}
	|	FLOATING_POINT_LITERAL:l
			{: 
			   RESULT = new Exp(Type.floating(), "double", ""+l); 
			:}
	|	BOOLEAN_LITERAL:l 
			{: 
			   RESULT = new Exp(Type.bool(), "i1", ""+l); 
			:}
	|	CHARACTER_LITERAL:l 
			{: 
			   RESULT = new Exp(Type.character(), "i8", ""+l); 
			:}
	|	STRING_LITERAL:l
			{: 
			   if(parser.first) Env.putGlobal(l , Tmp.ns());
			   if(parser.second) { int i = l.length() + 1;
			   			     String ary = "["+i+" x i8]";
			   			     String tmp = Env.getGlobal(l);
			   			     String loc = "i8* getelementptr ("+ary+"* "+tmp+", i32 0, i32 0)";
			   			     Symb s = Env.get("String", Env.getRoot());
			   			     if(s == null) RESULT = new Exp(Type.reference(Type.forwardName("String", parser.s.current_lexeme())), loc, ""); 
			   			     else RESULT = new Exp(Type.reference(s.getType()), loc, ""); 
			   }
			:}
	|	NULL_LITERAL
			{: 
			   RESULT = new Exp(Type.reference(Type.voidtype()), "", "null"); 
			:}
	;

// 3) Types, Values, and Variables
type	::=
		primitive_type:t
			{: RESULT = t; :}
	|	reference_type:t
			{: RESULT = t; :}
	;
primitive_type ::=
		numeric_type:t
			{: RESULT = t; :}
	|	BOOLEAN
			{: RESULT = Type.bool(); :}
	;
numeric_type ::= 
		INT
 			{: RESULT = Type.integer(); :}
	|	CHAR
 			{: RESULT = Type.character(); :}
	|	FLOAT
  			{: RESULT = Type.floating(); :}
	;
reference_type ::=
		name:sn
			{: Symb s = sn.rs; 
			   if(s == null) RESULT = Type.reference(Type.forwardName(sn.name, parser.s.current_lexeme())); 
			   else RESULT = Type.reference(s.getType());
			:}
	|	array_type:t
			{: RESULT = t; :}
	;
array_type ::=
		primitive_type:t dims
			{: RESULT = Type.array(0, t); :}
	|	name:sn dims
			{: Symb s = sn.cs; 
			   if(s == null) { parser.sem_error(sn.name,"UNKNOWN NAME");
				    		 RESULT = Type.errortype();
			   } 
			   else RESULT = Type.array(0, Type.reference(s.getType()));
			:}
	;

// 4) Names
name	::=
		IDENTIFIER:n
			{: RESULT = new Sname(n, Env.get(n), Env.get(n, Env.getRoot())); :}			
		|	name:sn DOT IDENTIFIER:n
			{: Symb s1 = sn.cs;
			   if(s1 == null) { parser.sem_error(sn.name, "UNKNOWN NAME");
						  RESULT = new Sname(n, null, null);
			   }
			   else { Type t1 = s1.getType();
				    if(!t1.isReference()) { parser.sem_error(""+t1, "NOT A REFERENCE");
								    RESULT = new Sname(n, null, null);
				    }
				    else { Type r = ((Reference)t1).getReferred();
					     if(!r.isName()) { parser.sem_error(""+r, "NOT A CLASS");
								     RESULT = new Sname(n, null, null);
					     }
					     else { Symb s2 = Env.get(n, ((Name)r).getEnv()); 
			   			     	if(s2 == null) { parser.sem_error(n,"UNKNOWN NAME"); 
									     RESULT = new Sname(n, null, null);
							}
			   			     	else if (!s2.isPublic() && !Name.getCurrentClass().isSubclass(s2.getOwner())) { parser.sem_error(n, "ILLEGAL ACCESS TO PRIVATE ATTRIBUTE");
				    					 				     			     	      		  RESULT = new Sname(n, null, null);
						  	     }
						  	     else { if(parser.second) { parser.code.append("		; name\n");
												  parser.code.append("	"+Tmp.nv()+" = load "+s1.getLoc()+"\n");
												  parser.objLoc = Tmp.lv();
									}
									RESULT = new Sname(n, s2, Env.get(n, Env.getRoot()));
							     }
					     }								
				    }
			   }
			:}	
	;

modifiers_opt::=
			{: RESULT = Boolean.valueOf(false); :}
	|	PUBLIC
			{: RESULT = Boolean.valueOf(true); :}
	;

// 5) Classes
class_declarations ::= 
	|	class_declarations class_declaration 
	|	error
			{: parser.report_error("class_declaration","WRONG"); :}
		class_declaration
	;

// 5.1) Class Declaration
class_declaration ::= 
		modifiers_opt:m CLASS IDENTIFIER:n1 
			{: System.out.println("CLASS ENTRY: "+n1);
			   if(parser.first) if (Env.putClass(n1, m.booleanValue()) != 0) parser.sem_error(n1,"DUPLICATE CLASS NAME");
 			   if(parser.second) { Env.next();
						     Name c = Env.get(n1).getOwner();
						     Name.putCurrentClass(c);
			   } 
			:}
		class_body
			{: System.out.println("CLASS EXIT: "+n1);
			   if(parser.first) { Env e = Env.getCurrent();
						    List vars = e.getEvars();
					          ListIterator l = vars.listIterator();
						    int n = 0;
						    String global = "%struct."+n1+" = type { ";
						    boolean first = true;
						    while(l.hasNext()) { String v = (String)l.next();
									    	 Symb s = Env.get(v);
									   	 String c = s.getType().getCode();
									   	 s.putLocT(c + "*");
									  	 s.putLocN("getelementptr %struct."+n1+"* %this, i32 0, i32 "+n);
									   	 n++;
									    	 if(first) { global = global + c;
											       first = false;
									   	 }
									   	 else global = global + ", " + c;
						    }
						    global = global + " }";
						    Env.putGlobal(global, "struct");
						    Env.get(n1).putLocT(global.replace("%struct."+n1+" = ",""));	    				
						    Env.pop();
						  }
			   if(parser.second) Env.next();
			:}
	|	modifiers_opt:m CLASS IDENTIFIER:n1 EXTENDS IDENTIFIER:n2
			{: System.out.println("CLASS ENTRY: "+n1);
			   if(parser.first) switch (Env.putClass(n1, m.booleanValue(),n2)){ case 1: parser.sem_error(n1,"DUPLICATE CLASS NAME");
							     								    break;
													        case 2: parser.sem_error(n2,"UNKNOWN CLASS");
					        }
  			   if(parser.second) { Env.next();
						     Env.next();
						     Name c = Env.get(n1).getOwner();
						     Name.putCurrentClass(c);
			   }
			:}
		class_body
			{: System.out.println("CLASS EXIT: "+n1);
			   if(parser.first) { List e_vars = new ArrayList();
						    Env e = Env.getCurrent();
						    e_vars.add(new Vars(e, e.getEvars()));
 						    Name cl = Env.get(n1).getOwner();
						    String sup = cl.getSuper();
						    while(sup != null){ cl = Env.get(sup).getOwner();
										e = cl.getEnv();
						    				e_vars.add(0, new Vars(e, e.getEvars()));
										sup = cl.getSuper();
						    }					    
					          ListIterator evl = e_vars.listIterator();
						    int n = 0;
						    String global = "%struct."+n1+" = type { ";
						    boolean first = true;
						    while(evl.hasNext()){ Vars ev = (Vars)evl.next();
										  ListIterator l = ev.vl.listIterator();										  
						    				  while(l.hasNext()){ String v = (String)l.next();
									    				    Symb s = Env.get(v, ev.e);
									    				    String c = s.getType().getCode();
									    				    s.putLocT(c + "*");
									    				    s.putLocN("getelementptr %struct."+n1+"* %this, i32 0, i32 "+n);
									    				    n++;
									    				    if(first) { global = global + c;
															    first = false;
													    }
													    else global = global + ", " + c;
										  }
						    }
						    global = global + " }";
						    Env.putGlobal(global, "struct");
						    Env.get(n1).putLocT(global.replace("%struct."+n1+" = ",""));
						    Env.pop();
						    Env.pop();
			   }
			   if(parser.second) { Env.next();
						     Env.next();
			   }
			:}
	;
class_body ::=
		LBRACE class_body_declarations_opt RBRACE
	;
class_body_declarations_opt ::= 
	|	class_body_declarations:w
	;
class_body_declarations ::= 
		class_body_declaration
	|	class_body_declarations class_body_declaration
	;
class_body_declaration ::=
		class_member_declaration
	|	constructor_declaration
	|	block
	;
class_member_declaration ::=
		field_declaration
	|	method_declaration
  	| 	SEMICOLON
	;

// 5.2) Field Declarations
field_declaration ::= 
		modifiers_opt type variable_declarators:w SEMICOLON
	|	modifiers_opt type error 
			{: parser.report_error("variable_declarators","WRONG"); :}
		SEMICOLON 
	|	modifiers_opt type error 
			{: parser.report_error("field_declaration","WRONG"); :} 
		method_declaration 
	;
variable_declarators ::=
		variable_declarator_id
	|	variable_declarators COMMA M1 variable_declarator_id
	|	error 
			{: parser.report_error("variable_declarator_id","WRONG"); :} 
		COMMA variable_declarator_id
	;
M1 ::=
			{: RESULT = (Type)((java_cup.runtime.Symbol)CUP$mjavac$stack.elementAt(CUP$mjavac$top-2)).value; :}
	;
variable_declarator_id ::=
		IDENTIFIER:n
			{: if(parser.first) { Boolean m = (Boolean)((java_cup.runtime.Symbol)CUP$mjavac$stack.elementAt(CUP$mjavac$top-2)).value;
			   			    boolean mod = m == null ? false: m.booleanValue();	
			  			    Type t = (Type)((java_cup.runtime.Symbol)CUP$mjavac$stack.elementAt(CUP$mjavac$top-1)).value;
			   			    Name c = Name.getCurrentClass();
			   			    String cname = c.getName();
			   			    Symb csymb = Env.get(cname);
			   			    Symb s;
			   			    if(csymb.isPublic()) s = new Symb(t, c, mod, t.getCode(), "%"+n);
			   			    else { if(mod) parser.warning(n, "PUBLIC FIELD DECLARED IN PRIVATE CLASS");
				    			     s = new Symb(t, c, false, t.getCode(), "%"+n);
			   			    }
			   			    if (!Env.putVar(n, s)) parser.sem_error(n,"DUPLICATE NAME");						    	
			   }
			   RESULT = n;
			:}
	|	variable_declarator_id LBRACK RBRACK
	;

// 5.3) Method Declarations
method_declaration ::=
		method_header:t method_body
			{: if(parser.second) { parser.code.append("		; method_declaration\n");
						     parser.code.append("	br label %return\n");
						     parser.code.append("return:\n");
						     if(t == Type.voidtype()) parser.code.append("	ret void\n"); 
						     else { parser.code.append("	"+Tmp.nv()+" = load "+t.getCode()+"* %retval\n");
						     	    parser.code.append("	ret "+t.getCode()+" "+Tmp.lv()+"\n");
						     }
						     parser.code.append("}\n\n");
			   }
			:}
	;
method_header ::=
		modifiers_opt:m type:t1 IDENTIFIER:n 
 			{: if(parser.first) { if (!Env.put(n, null)) parser.sem_error(n,"DUPLICATE NAME");
						    System.out.println(" METHOD ENTRY: "+n);
		 			          Env.push();
			   }
			   if(parser.second) { System.out.println(" METHOD ENTRY: "+n);
						     Env.next();
						     parser.code.append("		; method_header\n");
						     parser.code.append("define "+t1.getCode()+" @"+Name.getCurrentClass().getName()+"_"+n+"(");
			   }
			:}
		LPAREN formal_parameter_list_opt:pL RPAREN
			{: if(parser.first) { Name c = Name.getCurrentClass();
			   			    String cname = c.getName();
			   			    Symb csymb = Env.get(cname);
			   			    Symb s;
			   			    if(csymb.isPublic()) s = new Symb(Type.method(pL.type, t1), c, m.booleanValue());
			   			    else { if(m.booleanValue()) parser.warning(n, "PUBLIC METHOD DECLARED IN PRIVATE CLASS");
				    		           s = new Symb(Type.method(pL.type, t1), c, false);
			   			    }
			   			    Env.putSymb(n, s); 
			   }
			   if(parser.second) { Name c = Name.getCurrentClass();
						     Symb msymb = Env.get(n, c.getEnv());
						     Method.putCurrent(n, (Method)msymb.getType());
						     String ptrt = "%struct."+c.getName()+"*";
						     parser.code.append(ptrt+" %this");
						     ListIterator l = pL.list.listIterator();
						     while(l.hasNext()) { String p = (String)l.next();
									        Symb s = Env.get(p);
						                       	  Type t = s.getType();
									        if(t.isArray()) { String tc = ((Array)t).getBase().getCode()+"*";
												        parser.code.append(", "+tc+" "+s.getLocN());
													  s.putLocT(tc);
									        }
									        else parser.code.append(", "+s.getLoc());
						     } 
						     parser.code.append(") {\nentry:\n");
						     l = pL.list.listIterator();
						     while(l.hasNext()) { String p = (String)l.next();
										  Symb s = Env.get(p);
										  Type t = s.getType();
			   			     				  String tc;
										  if(t.isArray()) tc = s.getLocT();
										  else tc = t.getCode();
						     				  parser.code.append("	%"+p+"_addr = alloca "+tc+"\n");
						     				  parser.code.append("	store "+s.getLoc()+", "+tc+"* %"+p+"_addr\n");
										  s.putLocT(tc+"*");
										  s.putLocN("%"+p+"_addr");
						     }
						     parser.code.append("	%retval = alloca "+t1.getCode()+"\n");
						     parser.code.append("	%tmp = alloca "+t1.getCode()+"\n");
						     RESULT = t1;
			   }
			:}
	|	modifiers_opt:m VOID IDENTIFIER:n 
			{: if(parser.first) { if (!Env.put(n, null)) parser.sem_error(n,"DUPLICATE NAME");
						    System.out.println(" METHOD ENTRY: "+n);
		 				    Env.push();
			   }
			   if(parser.second) { System.out.println(" METHOD ENTRY: "+n);
						     Env.next();
						     parser.code.append("		; method_header\n");
						     if(n.equals("main")) parser.code.append("define void @main(");
					           else parser.code.append("define "+Type.voidtype().getCode()+" @"+Name.getCurrentClass().getName()+"_"+n+"(");
			   }
			:}
		LPAREN formal_parameter_list_opt:pL RPAREN
 			{: if(parser.first) { Name c = Name.getCurrentClass();
			   			    String cname = c.getName();
			   			    Symb csymb = Env.get(cname);
			   			    Symb s;
			   			    if(csymb.isPublic()) s = new Symb(Type.method(pL.type, Type.voidtype()), c, m.booleanValue());
			   			    else { if(m.booleanValue()) parser.warning(n, "PUBLIC METHOD DECLARED IN PRIVATE CLASS");
				    		           s = new Symb(Type.method(pL.type, Type.voidtype()), c, false);
			   			    }
			   			    Env.putSymb(n, s);
			   }
			   if(parser.second) { Symb msymb = Env.get(n);
						     Method.putCurrent(n, (Method)msymb.getType());
						     String ptrt = "%struct."+Name.getCurrentClass().getName()+"*";
						     if(!n.equals("main")) parser.code.append(ptrt+" %this");
						     ListIterator l = pL.list.listIterator();
						     while(l.hasNext()) { String p = (String)l.next();
									        Symb s = Env.get(p);
						                       	  Type t = s.getType();
									        if(t.isArray()) { String tc = ((Array)t).getBase().getCode()+"*";
												        parser.code.append(", "+tc+" "+s.getLocN());
													  s.putLocT(tc);
									        }
									        else parser.code.append(", "+s.getLoc());
						     }  
						     parser.code.append(") {\nentry:\n");
						     l = pL.list.listIterator();
						     while(l.hasNext()) { String p = (String)l.next();
										  Symb s = Env.get(p);
										  Type t = s.getType();
			   			     				  String tc;
										  if(t.isArray()) tc = s.getLocT();
										  else tc = t.getCode();
						     				  parser.code.append("	%"+p+"_addr = alloca "+tc+"\n");
						     				  parser.code.append("	store "+s.getLoc()+", "+tc+"* %"+p+"_addr\n");
										  s.putLocT(tc+"*");
										  s.putLocN("%"+p+"_addr");
						     }
						     RESULT = Type.voidtype();
			   }
			:}
	|	error 
			{: parser.report_error("method_header","WRONG");
			   Env.push();
 			:} 
		LPAREN formal_parameter_list_opt RPAREN
	;
formal_parameter_list_opt ::=
			{: RESULT = new ParamList(Type.voidtype(), new ArrayList()); :}
	|	formal_parameter_list:pL
			{: RESULT = pL; :}
	;
formal_parameter_list ::=
		formal_parameter:n
 			{: List l = new ArrayList();
			   l.add(n);
			   RESULT = new ParamList(Env.get(n).getType(), l); 
			:}
	|	formal_parameter_list:pL COMMA formal_parameter:n
 			{: pL.list.add(n);
			   pL.type = Type.product(pL.type, Env.get(n).getType());
			   RESULT = pL; 
			:}
	|	error 
			{: parser.report_error("formal_parameter_list","WRONG"); :} 
		formal_parameter
	;
formal_parameter ::=
		type:t variable_declarator_id:n
			{: if(parser.first) Env.remVar(n);
			   RESULT = n; 
			:}
	;
method_body ::=
		LBRACE M3 block_statements_opt RBRACE
   			{: System.out.println(" METHOD EXIT");
			   if(parser.first) Env.pop(); 
			   if(parser.second) Env.next();
			:}
	|	LBRACE M3 error RBRACE
			{: parser.report_error("block","WRONG");
			   System.out.println(" METHOD EXIT");
			:}
	|	SEMICOLON
			{: System.out.println(" METHOD EXIT");
			   if(parser.first) Env.pop();
			   if(parser.second) Env.next(); 
			:}
	;
M3 ::= 
   			{: if(parser.second) { ListIterator l = Env.getVars().listIterator();
						     if(l.hasNext())parser.code.append("		; M3\n");
						     while(l.hasNext()) { String p = (String)l.next();
										  Symb s = Env.get(p);
			   			     				  Type t = s.getType();
										  if(!t.isArray()) { String tc = s.getType().getCode();										  
						     				  			   parser.code.append("	%"+p+" = alloca "+tc+"\n");						     				  
										  			   s.putLocT(tc+"*");
										  			   s.putLocN("%"+p);
										  }
						     }
			   }
			:}
	; 

// 5.4) Constructor Declarations
constructor_declaration ::=
		constructor_declarator:t constructor_body
			{: if(parser.second) { parser.code.append("		; constructor_declaration\n");
					       parser.code.append("	br label %return\n");
					       parser.code.append("return:\n");
					       parser.code.append("	ret void\n");
					       parser.code.append("}\n\n");
			   } 
			:}
	;
constructor_declarator ::=
		modifiers_opt:m IDENTIFIER:n 
			{: if(parser.first) { if (!Env.put(n, null)) parser.sem_error(n,"DUPLICATE NAME");
						    else { Name c = Name.getCurrentClass();
			   			    	     String cname = c.getName();
						    	     if (!n.equals(cname)) parser.sem_error(n+", "+cname,"CONSTRUCTOR NAME DIFFERENT FROM CLASS NAME");
						    }
						    System.out.println(" CONSTRUCTOR ENTRY: "+n);
		 				    Env.push();
			   }
			   if(parser.second) { System.out.println(" CONSTRUCTOR ENTRY: "+n);
						     Env.next();
						     Symb msymb = Env.get(n);
						     Constructor c = (Constructor)msymb.getType();
						     parser.code.append("		; constructor_declarator\n");
						     parser.code.append("define "+Type.voidtype().getCode()+" @"+n+"(");
			   }
			:}
		LPAREN formal_parameter_list_opt:pL RPAREN
			{: if(parser.first) { Name c = Name.getCurrentClass();
			   			    String cname = c.getName();
			   			    Symb csymb = Env.get(cname);
			   			    Symb s;
			   			    if(csymb.isPublic()) s = new Symb(Type.constructor(pL.type, Type.reference(Type.getName(n))), c, m.booleanValue());
			   			    else { if(m.booleanValue()) parser.warning(n, "PUBLIC CONSTRUCTOR DECLARED IN PRIVATE CLASS");
				    		           s = new Symb(Type.constructor(pL.type, Type.reference(Type.getName(n))), c, false);
			   			    }	
			   			    Env.putSymb(n, s);
			   }
			   if(parser.second) { Symb msymb = Env.get(n);
						     Constructor c = (Constructor)msymb.getType();
						     Constructor.putCurrent(n, c);
						     Type t2 = c.getRange();
						     String ptrt = "%struct."+n+"*";
						     parser.code.append(ptrt+" %this");
						     ListIterator l = pL.list.listIterator();
						     while(l.hasNext()) { String p = (String)l.next();
									        Symb s = Env.get(p);
						                       	  Type t = s.getType();
									        if(t.isArray()) { String tc = ((Array)t).getBase().getCode()+"*";
												        parser.code.append(", "+tc+" "+s.getLocN());
													  s.putLocT(tc);
									        }
									        else parser.code.append(", "+s.getLoc());
						     } 
						     parser.code.append(") {\nentry:\n");
						     l = pL.list.listIterator();
						     while(l.hasNext()) { String p = (String)l.next();
										  Symb s = Env.get(p);
										  Type t = s.getType();
			   			     				  String tc;
										  if(t.isArray()) tc = s.getLocT();
										  else tc = t.getCode();
						     				  parser.code.append("	%"+p+"_addr = alloca "+tc+"\n");
						     				  parser.code.append("	store "+s.getLoc()+", "+tc+"* %"+p+"_addr\n");
										  s.putLocT(tc+"*");
										  s.putLocN("%"+p+"_addr");
						     }
						     RESULT = t2;
			   }
			:}
	;
constructor_body ::=
		LBRACE M3 explicit_constructor_invocation block_statements_opt RBRACE
			{: System.out.println(" CONSTRUCTOR EXIT");
			   if(parser.first) Env.pop(); 
			   if(parser.second) Env.next();
			:}
	|	LBRACE M3 block_statements_opt RBRACE
   			{: System.out.println(" CONSTRUCTOR EXIT");
			   if(parser.first) Env.pop(); 
			   if(parser.second) Env.next();
			:}
	|	LBRACE M3 error RBRACE
   			{: parser.report_error("block","WRONG");
			   System.out.println(" CONSTRUCTOR EXIT");
			   if(parser.first) Env.pop(); 
			   if(parser.second) Env.next();  
			:} 
	;
M2 ::= 
   			{: System.out.println(" BLOCK ENTRY");
			   if(parser.first) Env.push(); 
			   if(parser.second) { Env.next();
						     ListIterator l = Env.getVars().listIterator();
						     if(l.hasNext()) parser.code.append("		; M2\n");
						     while(l.hasNext()) { String p = (String)l.next();
										  Symb s = Env.get(p);
			   			     				  Type t = s.getType();						     				 
										  String tc = s.getType().getCode();										  
						     				  parser.code.append("	%"+p+" = alloca "+tc+"\n");						     				  
										  s.putLocT(tc+"*");
										  s.putLocN("%"+p);
						     }
			   }
			:}
	;
explicit_constructor_invocation ::=
		THIS LPAREN argument_list_opt RPAREN SEMICOLON
	|	SUPER LPAREN argument_list_opt:e2 RPAREN SEMICOLON
			{: if(parser.second) { Name c = Name.getCurrentClass();
						     String sup = c.getSuper();
						     if(sup == null){ parser.sem_error(c.getName(), "NOT HAVING A SUPERCLASS");
				   		     			    RESULT = new Exp(Type.errortype(), "", "");
			   			     }
						     else { Name sc = Env.get(sup).getOwner();
								Env se = sc.getEnv();
								Symb ss = Env.get(sup, se);
								if(ss == null) { parser.sem_error(sup, "NOT HAVING A CONSTRUCTOR");
				   		     			           RESULT = new Exp(Type.errortype(), "", "");
			   			     		}
								else { Type t1 = ss.getType();
			   	    	     				 if(!t1.isConstructor()) { parser.sem_error(""+t1, "NOT A CONSTRUCTOR");
				    			    	     		 		  	   RESULT = new Exp(Type.errortype(), "", "");
			   	    	     			       } 
									 else { parser.code.append("		; explicit_constructor_invocation_SUPER\n");
										  ListIterator l = e2.list.listIterator();
										  while (l.hasNext()) { Exp e = (Exp)l.next();
											    			if(e.locT.contains("*")) if(e.locN.endsWith("&")) e.locN = e.locN.replace('&', ' ').trim(); 
											    				     			 else if(e.type.isArray()) { String t = ((Array)e.type).getBase().getCode()+"*";
																				   		     parser.code.append("	"+Tmp.nv()+" = bitcast "+e.locT+" "+e.locN+" to "+t+"\n");
 	    																			   		     e.locT = t;
																				    		     e.locN = Tmp.lv();
																    			}
																    			else if(!e.locT.startsWith("i8* getelementptr")) { parser.code.append("	"+Tmp.nv()+" = load "+e.locT+" "+e.locN+"\n");
																			         				     	      		e.locT = e.locT.replaceFirst("\\*", "");
																								     	      		e.locN = Tmp.lv();
											    								     }
										  }
			          	     				 	  if(((Constructor)t1).getDomain() != e2.type) { parser.sem_error(""+e2.type, "INCOMPATIBLE ARGUMENT IN CONSTRUCTOR INVOCATION "+sup);
				    					 				  		     	    		 RESULT = new Exp(Type.errortype(), "", "");
			   	  	   	    		     		  }
					   	    		              else { parser.code.append("	"+Tmp.nv()+" = bitcast %struct."+c.getName()+"* %this to %struct."+sup+"*\n");
											   parser.code.append("	call void @"+sup+"("+"%struct."+sup+"* "+Tmp.lv());
										     	   l = e2.list.listIterator();
										     	   while(l.hasNext()) { Exp pe = (Exp)l.next();
											    		 	 	parser.code.append(", "+pe.locT+" "+pe.locN);
										     	   }
										     	   parser.code.append(")\n");
											   RESULT = new Exp(((Constructor)t1).getRange(), "%struct."+sup+"*", Tmp.lv());
									    	  }
			 	    		     		      }
			   			           }
						    }
			   }
			   else RESULT = new Exp(Type.voidtype(), "", "");
			:}
	|	primary DOT THIS LPAREN argument_list_opt RPAREN SEMICOLON
	|	primary DOT SUPER LPAREN argument_list_opt RPAREN SEMICOLON
	;

// 6) Blocks and Statements
block ::=
		LBRACE M2 block_statements_opt RBRACE
   			{: System.out.println(" BLOCK EXIT");
			   if(parser.first) Env.pop(); 
			   if(parser.second) Env.next();
			:}		
	|	LBRACE M2 error 
			{: parser.report_error("block","WRONG");
			   System.out.println(" BLOCK EXIT");
			   if(parser.first) Env.pop(); 
			   if(parser.second) Env.next();
 			:} 
		RBRACE  
	;
block_statements_opt ::=
	|	block_statements
	;
block_statements ::=
		block_statement
	|	block_statements block_statement
	|	error 
			{: parser.report_error("block_statements","WRONG"); :} 
		block_statement 
	;
block_statement ::=
		local_variable_declaration_statement
	|	statement
	;
local_variable_declaration_statement ::=
		type variable_declarators SEMICOLON
	|	type error 
			{: parser.report_error("local_variable_declaration_statement","WRONG"); :} 
		SEMICOLON
	;
statement ::=
		statement_without_trailing_substatement
	|	if_then_statement
	|	if_then_else_statement
	|	while_statement
	;
statement_no_short_if ::=
		statement_without_trailing_substatement
	|	if_then_else_statement_no_short_if
	|	while_statement_no_short_if
	;
statement_without_trailing_substatement ::=
		block
	|	empty_statement
	|	expression_statement
	|	return_statement
	;
empty_statement ::=
		SEMICOLON
	;
expression_statement ::=
		statement_expression SEMICOLON
	;
statement_expression ::=
		assignment
	|	method_invocation
	|	class_instance_creation_expression
	;
M4 ::= 
			{: if(parser.second) RESULT = new Labels(); :}
	;
M5 ::= 
			{: if(parser.second) { Labels l = (Labels)((java_cup.runtime.Symbol)CUP$mjavac$stack.elementAt(CUP$mjavac$top-1)).value;
			    			     parser.code.append(l.lTrue+":	; l_True\n");
			   }
			:}
	;
M6 ::= 
			{: if(parser.second) { Labels l = (Labels)((java_cup.runtime.Symbol)CUP$mjavac$stack.elementAt(CUP$mjavac$top-4)).value;
			    			     parser.code.append("	br label %"+l.lNext+"\n");
			    			     parser.code.append(l.lFalse+":	; l_False\n");
			   }
			:}
	;
M7 ::= 
			{: if(parser.second) { Labels l = new Labels();
			    			     parser.code.append("	br label %"+l.lBegin+"\n");
			    			     parser.code.append(l.lBegin+":	; l_Begin\n");
						     RESULT = l;
			   } 
			:}
	;
if_then_statement ::=
		IF LPAREN M4:l expression:e M5 RPAREN statement
			{: if(parser.second) if(!e.type.isBool()) parser.sem_error(""+e.type, "NOT A BOOLEAN");
						   else { parser.code.append("	br label %"+l.lFalse+"\n");
							    parser.code.append(l.lFalse+":	; l_False\n");
						   }	
			:}
	|	IF error 
			{: parser.report_error("if_then_statement","WRONG"); :} 
		M5 RPAREN statement
	;
if_then_else_statement ::=
		IF LPAREN M4:l expression:e M5 RPAREN statement_no_short_if M6 ELSE statement
			{: if(parser.second) if(!e.type.isBool()) parser.sem_error(""+e.type, "NOT A BOOLEAN");
						   else { parser.code.append("	br label %"+l.lNext+"\n");
							    parser.code.append(l.lNext+":	; l_Next\n");
						   }	
			:}
	|	IF LPAREN M4 error 
			{: parser.report_error("expression","WRONG"); :} 
		M5 RPAREN statement_no_short_if ELSE statement
	;
if_then_else_statement_no_short_if ::=
		IF LPAREN M4:l expression:e M5 RPAREN statement_no_short_if M6 ELSE statement_no_short_if
			{: if(parser.second) if(!e.type.isBool()) parser.sem_error(""+e.type, "NOT A BOOLEAN");
						   else { parser.code.append("	br label %"+l.lNext+"\n");
							    parser.code.append(l.lNext+":	; l_Next\n");
						   }	
			:}
	;
while_statement ::=
		WHILE LPAREN M7:l expression:e M5 RPAREN statement
			{: if(parser.second) if(!e.type.isBool()) parser.sem_error(""+e.type, "NOT A BOOLEAN");
						   else { parser.code.append("	br label %"+l.lBegin+"\n");
							    parser.code.append(l.lFalse+":	; l_False\n");
						   }	
			:}
	|	WHILE error 
			{: parser.report_error("expression","WRONG"); :} 
		M5 RPAREN statement
	;
while_statement_no_short_if ::=
		WHILE LPAREN M7:l expression:e M5 RPAREN statement_no_short_if
			{: if(parser.second) if(!e.type.isBool()) parser.sem_error(""+e.type, "NOT A BOOLEAN");
						   else { parser.code.append("	br label %"+l.lBegin+"\n");
							    parser.code.append(l.lFalse+":	; lFalse\n");
						   }	
			:}
	;
 M14 ::= 
			{: if(parser.second) { Labels l = new Labels();
						     l.bvalue = true;
						     RESULT = l;
			   } 
			:} 
	; 
return_statement ::=
		RETURN M14 expression_opt:e SEMICOLON
			{: if(parser.second) { Type r = Method.getCurrent().getRange();
						     if(e.type != r) if(e.type.isNumber() && r.isNumber()) { Type mt = Type.max(e.type, r);
																 String t = mt.getCode();
															 	 if(e.type != mt) { parser.code.append("		; return_statement\n");
																			  e.load();
									  						 				  String l = e.widen(mt);
																			  parser.code.append("	store "+t+" "+l+", "+t+"*"+" %tmp\n");
																			  parser.code.append("	"+Tmp.nv()+" = load "+t+"*"+" %tmp\n");
																			  parser.code.append("	store "+t+" "+Tmp.lv()+", "+t+"*"+" %retval\n");
																			  parser.code.append("	br label %return\n");
																 }
							  				 					 else  parser.sem_error(""+r+", "+e.type, "RANGE TYPE SHORTER THAN RETURNED TYPE");
									   }
							  		   else  parser.sem_error(""+r+", "+e.type, "INCOMPATIBLE TYPES");
						     else { String t = e.type.getCode();
								parser.code.append("		; return_statement\n");
								e.load();
								parser.code.append("	store "+e.locT+" "+e.locN+", "+t+"*"+" %tmp\n");
								parser.code.append("	"+Tmp.nv()+" = load "+t+"*"+" %tmp\n");
								parser.code.append("	store "+t+" "+Tmp.lv()+", "+t+"*"+" %retval\n");
								parser.code.append("	br label %return\n");	
						     }
			   }
			:}
      ;

// 7) Expressions
primary ::=	
		primary_no_new_array:e 
			{: RESULT = e; :}
	|	array_creation_expression:e 
			{: RESULT = e; :}
	;
M11 ::=
			{: if (parser.second) RESULT = (Labels)((java_cup.runtime.Symbol)CUP$mjavac$stack.elementAt(CUP$mjavac$top-1)).value; :}
	;
primary_no_new_array ::=
		literal:e 
			{: if((parser.second) && (e.type == Type.bool())) { Labels l = (Labels)((java_cup.runtime.Symbol)CUP$mjavac$stack.elementAt(CUP$mjavac$top-1)).value;									    
											    if(!l.bvalue) if (e.locN.equals("true")) parser.code.append("	br label %"+l.lTrue+"\n");
											    			else parser.code.append("	br label %"+l.lFalse+"\n"); 
											    RESULT = new Exp(Type.bool(), e.locT, e.locN, l);
			   }
			   else RESULT = e; 
			:}
	|	THIS 
			{: RESULT = new Exp(Type.reference(Name.getCurrentClass()), "", ""); :}
	|	LPAREN M11:l expression:e RPAREN 
			{: if((parser.second) && (e.type == Type.bool()) && (l!= null)) RESULT = new Exp(Type.bool(), e.locT, e.locN, l);
			   else RESULT = e; 
			:}
	|	class_instance_creation_expression:e 
			{: RESULT = e; :}
	|	field_access:e
			{: RESULT = e; :}
	|	method_invocation:e 
			{: if((parser.second) && (e.type == Type.bool())) { Labels l = (Labels)((java_cup.runtime.Symbol)CUP$mjavac$stack.elementAt(CUP$mjavac$top-1)).value;									    
											    if(!l.bvalue) parser.code.append("	br i1 "+e.locN+", label %"+l.lTrue+", label %"+l.lFalse+"\n");
											    l.bvDone = true;
											    RESULT = new Exp(Type.bool(), e.locT, e.locN, l);
			   }
			   else RESULT = e; 
			:}
	|	array_access:e 
			{: RESULT = e; :}
	|	primitive_type DOT CLASS
	|	VOID DOT CLASS
	|	array_type DOT CLASS
	|	name DOT CLASS
	|	name DOT THIS
	|	LPAREN M11 error
			{: parser.report_error("primary_no_new_array","WRONG"); :} 
		RPAREN
	|	error
 			{: parser.report_error("primary_no_new_array","WRONG"); :}
		DOT THIS 
	;
 class_instance_creation_expression ::=
		NEW name:sn
 			{: if(parser.second) if(sn.rs != null) { Type t = sn.rs.getType();
				    					     if(t.isName()) { Env e = ((Name)t).getEnv();
			   	    	     		      				    Symb s1 = Env.get(sn.name, e);
					     							    Type t1 = s1.getType();
			   	    	     							    if(t1.isConstructor()) { parser.code.append("		; class_instance_creation_expression\n");																 
																     parser.code.append("	"+Tmp.nv()+" = malloc %struct."+sn.name+"\n");																     
												    } 
									     }
									   }
			:}

		LPAREN argument_list_opt:e2 RPAREN class_body_opt
			{: if(parser.second) { if(sn.rs == null) { parser.sem_error(sn.name,"UNKNOWN NAME");
				   		     			       RESULT = new Exp(Type.errortype(), "", "");
			   			     }
			   			     else { String objptr = Tmp.lv();
								ListIterator l = e2.list.listIterator();
								parser.code.append("		; constructor_invocation\n");
								while (l.hasNext()) { Exp e = (Exp)l.next();
											    if(e.locT.contains("*")) if(e.locN.endsWith("&")) e.locN = e.locN.replace('&', ' ').trim(); 
											    				     else if(e.type.isArray()) { String t = ((Array)e.type).getBase().getCode()+"*";
																				   parser.code.append("	"+Tmp.nv()+" = bitcast "+e.locT+" "+e.locN+" to "+t+"\n");
 	    																			   e.locT = t;
																				   e.locN = Tmp.lv();
																    }
																    else if(!e.locT.startsWith("i8* getelementptr")) { parser.code.append("	"+Tmp.nv()+" = load "+e.locT+" "+e.locN+"\n");
																			         				     	    e.locT = e.locT.replaceFirst("\\*", "");
																								     	    e.locN = Tmp.lv();
											    						   }
								}
								Type t = sn.rs.getType();
				    		     		if (!t.isName()) { parser.sem_error(""+t, "NOT A CLASS");
				    		           				 RESULT = new Exp(Type.errortype(), "", "");
			   	    		     		}
			   	    		     		else { Env e = ((Name)t).getEnv();
			   	    	     		      	 Symb s1 = Env.get(sn.name, e);
					     				 Type t1 = s1.getType();
			   	    	     				 if(!t1.isConstructor()) { parser.sem_error(""+t1, "NOT A CONSTRUCTOR");
				    			    	     		 		  	   RESULT = new Exp(Type.errortype(), "", "");
			   	    	     			       }
			          	     				 else if(((Constructor)t1).getDomain() != e2.type) { parser.sem_error(""+e2.type, "INCOMPATIBLE ARGUMENT IN CONSTRUCTOR INVOCATION "+sn.name);
				    					 				  		     	    RESULT = new Exp(Type.errortype(), "", "");
			   	  	   	    		     		}
					   	    		            else if(!s1.isPublic() && !Name.getCurrentClass().isSubclass(sn.rs.getOwner())) { parser.sem_error(sn.name, "ILLEGAL ACCESS TO PRIVATE CONSTRUCTOR");
				    					 				     			     	      			          RESULT = new Exp(Type.errortype(), "", "");
						  	   		    	     }
						  	   		    	     else { parser.code.append("	call void @"+sn.name+"("+"%struct."+sn.name+"* "+objptr);
										     		l = e2.list.listIterator();
										     		while(l.hasNext()) { Exp pe = (Exp)l.next();
											    		 	 	   parser.code.append(", "+pe.locT+" "+pe.locN);
										     		}
										     		parser.code.append(")\n");
												RESULT = new Exp(((Constructor)t1).getRange(), "%struct."+sn.name+"*", objptr);
									    	    }
			 	    		     		     }
			   			     }
			   }
			   else RESULT = new Exp(Type.voidtype(), "", "");
			:}
//	|	primary DOT NEW IDENTIFIER LPAREN argument_list_opt RPAREN class_body_opt 
	;
class_body_opt ::=
	|	class_body
;
argument_list_opt ::=
			{: if(parser.second) RESULT = new ParamList(Type.voidtype(), new ArrayList()); :}
	|	argument_list:eL
			{: if(parser.second) RESULT = eL; :}
	;
argument_list ::=
		expression:e
 			{: if(parser.second) { List l = new ArrayList();
			   			     l.add(e);
			   			     RESULT = new ParamList(e.type, l);
			   } 
			:}
	|	argument_list:eL COMMA expression:e
 			{: if(parser.second) { eL.list.add(e);
			   			     eL.type = Type.product(eL.type, e.type);
			   			     RESULT = eL;
			   } 
			:}
	|	error 
			{: parser.report_error("argument_list","WRONG"); :} 
		expression
	;
array_creation_expression ::=
		NEW primitive_type:t1 dim_exprs:e2 dims_opt
			{: if(parser.second) if(!e2.type.isInteger()) { parser.sem_error(""+e2.type, "NOT AN INTEGER");
				    			 			 	RESULT = new Exp(Type.errortype(), "", "");
		   				   }
			   			   else try { int s = Integer.parseInt(e2.locN);
								  parser.code.append("		; array_creation\n");									
  								  parser.code.append("	"+Tmp.nv()+" = malloc ["+e2.locN+" x "+t1.getCode()+"]\n");
								  Type t = Type.array(s, t1);
								  String c = "["+s+" x "+t1.getCode()+"]*";
							    	  RESULT = new Exp(t, c, Tmp.lv());
						   	  }
							  catch (Exception e) { parser.sem_error(""+e2.locN, "NOT AN INTEGER_LITERAL");
				    			 			 	RESULT = new Exp(Type.errortype(), "", "");
							  }
			   else RESULT = new Exp(Type.voidtype(), "", "");
			:}
	|	NEW name:sn dim_exprs:e2 dims_opt
			{: if(parser.second) if(!e2.type.isInteger()) { parser.sem_error(""+e2.type, "NOT AN INTEGER");
				    			 			 	RESULT = new Exp(Type.errortype(), "", "");
		   	   			   }
			   			   else { if(sn.cs == null) { parser.sem_error(sn.name,"UNKNOWN NAME");
				   			 			  	RESULT = new Exp(Type.errortype(), "", "");
			   	    			    }
			   	    			    else try { int s = Integer.parseInt(e2.locN);
									   Type t1 = Type.reference(sn.cs.getType());
								  	   parser.code.append("		; array_creation\n");									
  								  	   parser.code.append("	"+Tmp.nv()+" = malloc ["+e2.locN+" x "+t1.getCode()+"]\n");
								  	   Type t = Type.array(s, t1);
								  	   String c = "["+s+" x "+t1.getCode()+"]*";
							    	  	   RESULT = new Exp(t, c, Tmp.lv());
						   	  	   }
							  	   catch (Exception e) { parser.sem_error(""+e2.locN, "NOT AN INTEGER_LITERAL");
				    			 			 		 RESULT = new Exp(Type.errortype(), "", "");
							  	   }
			   			   }
			   else RESULT = new Exp(Type.voidtype(), "", "");
			:}
	;
dim_exprs ::=
		dim_expr:e
			{: RESULT = e; :}
	|	dim_exprs:e1 dim_expr:e2
			{: RESULT = e1; :}
	|	error 
			{: parser.report_error("dim_expr","WRONG"); :} 
		dim_expr
	;
dim_expr ::=	LBRACK expression:e RBRACK
			{: RESULT = e; :}
	;
dims_opt ::=
	|	dims
	;
dims ::=
		LBRACK RBRACK
	|	dims LBRACK RBRACK
	;
field_access ::=
		primary:e DOT IDENTIFIER:n
			{: if(parser.second) { if(!e.type.isReference()) { parser.sem_error(""+e.type, "NOT A REFERENCE");
				    					 	    	   RESULT = new Exp(Type.errortype(), "", "");
						     }
				  		     else { Type r = ((Reference)e.type).getReferred();
								if(!r.isName()) { parser.sem_error(""+r, "NOT A CLASS");
				    					 	      RESULT = new Exp(Type.errortype(), "", "");
						     		}
								else { Symb s = Env.get(n, ((Name)r).getEnv()); 
			   			     			 if(s == null){ parser.sem_error(n,"UNKNOWN NAME");
				    	     				  		    RESULT = new Exp(Type.errortype(), "", "");
			   			     			 } 
			   			     			 else if(!s.isPublic() && !Name.getCurrentClass().isSubclass(s.getOwner())) { parser.sem_error(n, "ILLEGAL ACCESS TO PRIVATE FIELD");
				    					 				     	     				 	     			RESULT = new Exp(Type.errortype(), "", "");
						  	    			}
				  			    			else RESULT = new Exp(s.getType(), "", "");
						     		}								
						     }
			   }
			   else RESULT = new Exp(Type.voidtype(), "", "");
			:}
	|	SUPER DOT IDENTIFIER:n
			{: if(parser.second) { Name c = Name.getCurrentClass();
						     String sn = c.getSuper();						     
 			   			     if(sn == null){ parser.sem_error(""+c,"NOT A SUBCLASS");
				    	     				   RESULT = new Exp(Type.errortype(), "", "");
			   			     }
						     else {	Symb ss = Env.get(sn);
								Name sc = (Name)ss.getType();
						    		Symb s = Env.get(n, sc.getEnv());
			   			     		if(s == null){ parser.sem_error(n,"UNKNOWN NAME");
				    	     				  	   RESULT = new Exp(Type.errortype(), "", "");
			   			     		} 
				  			      else RESULT = new Exp(s.getType(), "", "");
						     }
			   }
			   else RESULT = new Exp(Type.voidtype(), "", "");
			:}
//	|	name DOT SUPER DOT IDENTIFIER
	;
method_invocation ::=
		name:sn LPAREN argument_list_opt:eL RPAREN
			{: if(parser.second)
					  parse: { if(sn.cs == null) { parser.sem_error(sn.name,"UNKNOWN NAME");
				   	      			   	 RESULT = new Exp(Type.errortype(), "", "");
			   			     }
			   			     else { Name c = sn.cs.getOwner();
								String cName = null;
								if(c != null) cName = c.getName(); 
								ListIterator l = eL.list.listIterator();
								parser.code.append("		; method_invocation\n");
								while (l.hasNext()) { Exp e = (Exp)l.next();
											    if(e.locT.contains("*")) if(e.locN.endsWith("&")) e.locN = e.locN.replace('&', ' ').trim(); 
											    				     else if(e.type.isArray()) { String t = ((Array)e.type).getBase().getCode()+"*";
																				   parser.code.append("	"+Tmp.nv()+" = bitcast "+e.locT+" "+e.locN+" to "+t+"\n");
 	    																			   e.locT = t;
																				   e.locN = Tmp.lv();
																    }
																    else if(!e.locT.startsWith("i8* getelementptr")) { if(e.locN.startsWith("getelementptr")) { parser.code.append("	"+Tmp.nv()+" = "+e.locN+"\n");
														   																		   e.locN = Tmp.lv();
																									    }
																									  parser.code.append("	"+Tmp.nv()+" = load "+e.locT+" "+e.locN+"\n");
																			         				     	  e.locT = e.locT.replaceFirst("\\*", "");
																								     	  e.locN = Tmp.lv();
											    						   }
								}
								if(sn.name.equals("printf")) parser.code.append("	"+Tmp.nv()+" = call i32 (i8*, ...)* @printf(");
						   		else if(sn.name.equals("scanf")) parser.code.append("	"+Tmp.nv()+" = call i32 (i8*, ...)* @scanf(");
							  	     else { Type t1 = sn.cs.getType();
				    						if(!t1.isMethod()) { parser.sem_error(""+t1, "NOT A METHOD");
				    		             			   		   RESULT = new Exp(Type.errortype(), "", "");
													   break parse;
			   	    						}
										else { Type t3 = ((Method)t1).getRange();
											 if(t3 == Type.voidtype()) parser.code.append("	call void @"+cName+"_"+sn.name+"("); 
											 else parser.code.append("	"+Tmp.nv()+" = call "+t3.getCode()+" @"+cName+"_"+sn.name+"(");
 										}
								     }
								l = eL.list.listIterator();
								if(sn.name.equals("scanf") || sn.name.equals("printf")) { Exp e = (Exp)l.next();
											   						    parser.code.append(e.locT+" "+e.locN);
								}
								else { if(sn.cs == Env.get(sn.name)) if(Method.getCurrentName().equals("main")) parser.objLoc = "null";
														 else parser.objLoc = "%this";
									 parser.code.append("%struct."+cName+"* "+parser.objLoc);
								}											
								while(l.hasNext()) { Exp e = (Exp)l.next();
											   parser.code.append(", "+e.locT+" "+e.locN);
								}
			   	    				if(sn.name.equals("printf")||sn.name.equals("scanf")){ parser.code.append(")\n");
																	 RESULT = new Exp(Type.integer(), "", "");										  	 						 
								}
 								else { 
									 Type t1 = sn.cs.getType();
									 Type t5 = ((Method)t1).getDomain();
									 if(t5 != eL.type) { parser.sem_error(""+t5+", "+eL.type, "INCOMPATIBLE ARGUMENT IN METHOD INVOCATION "+sn.name);
				    				             	  	   RESULT = new Exp(Type.errortype(), "", "");
			   	  	   				 }
					   				 else { parser.code.append(")\n");
										  Type t4 = ((Method)t1).getRange();
										  Labels lab = (Labels)((java_cup.runtime.Symbol)CUP$mjavac$stack.elementAt(CUP$mjavac$top-4)).value;
									 	  RESULT = new Exp(t4, t4.getCode(), Tmp.lv(), lab);
									 }
								}
			   			     }
			   }
			   else RESULT = new Exp(Type.voidtype(), "", "");
			:}
	|	primary:e1 DOT IDENTIFIER:n LPAREN argument_list_opt:eL RPAREN
			{: if(parser.second) if(!e1.type.isReference()) { parser.sem_error(""+e1.type, "NOT A REFERENCE");
				    		 	   			   	  RESULT = new Exp(Type.errortype(), "", "");
			   			   }
			   			   else { Type name = ((Reference)e1.type).getReferred();
			   	   			    if (!name.isName()) { parser.sem_error(""+e1.type, "NOT A CLASS");
				    		       	 			  RESULT = new Exp(Type.errortype(), "", "");
			   	   			    }
			   	   			    else { Name c = (Name)name;
								     Symb sn = Env.get(n, c.getEnv()); 
			   			     		     if(sn == null) { parser.sem_error(n,"UNKNOWN NAME"); 
									       	    RESULT = new Exp(Type.errortype(), "", "");
								     }
			   			     		     else if (!sn.isPublic() && !Name.getCurrentClass().isSubclass(sn.getOwner())) { parser.sem_error(n, "ILLEGAL ACCESS TO PRIVATE ATTRIBUTE");
				    					 				     			     	      		  		 RESULT = new Exp(Type.errortype(), "", "");
						  	     		    }
						  	     		    else { String cName = c.getName(); 
										     ListIterator l = eL.list.listIterator();
								 		     parser.code.append("		; method_invocation\n");
										     parser.code.append("	"+Tmp.nv()+" = load "+e1.locT+" "+e1.locN+"\n");
										     String obj_ptr = Tmp.lv();
										     while (l.hasNext()) { Exp e = (Exp)l.next();
												   		   if(e.locT.contains("*")) if(e.locN.endsWith("&")) e.locN = e.locN.replace('&', ' ').trim(); 
											    				     	    		    else if(e.type.isArray()) { String t = ((Array)e.type).getBase().getCode()+"*";
																				   	  		  parser.code.append("	"+Tmp.nv()+" = bitcast "+e.locT+" "+e.locN+" to "+t+"\n");
 	    																			   	  		  e.locT = t;
																				   	  		  e.locN = Tmp.lv();
																    	   		   }
																    	   		   else if(!e.locT.startsWith("i8* getelementptr")) { if(e.locN.startsWith("getelementptr")) { parser.code.append("	"+Tmp.nv()+" = "+e.locN+"\n");
														   																		   	  		  e.locN = Tmp.lv();
																									    	   		   }
																									  	   		   parser.code.append("	"+Tmp.nv()+" = load "+e.locT+" "+e.locN+"\n");
																			         				     	  	   		   e.locT = e.locT.replaceFirst("\\*", "");
																								     	  	   		   e.locN = Tmp.lv();
											    						   	  	   }
								     		    } 
										    Type t1 = sn.getType();
				    				     		    if(!t1.isMethod()) { parser.sem_error(""+t1, "NOT A METHOD");
				    		             			   	  		 RESULT = new Exp(Type.errortype(), "", "");
			   	    				     		    }
								     		    else { Type t3 = ((Method)t1).getRange();
											     if(t3 == Type.voidtype()) parser.code.append("	call void @"+cName+"_"+n+"("); 
											     else parser.code.append("	"+Tmp.nv()+" = call "+t3.getCode()+" @"+cName+"_"+n+"(");
								     		    	     l = eL.list.listIterator();
								     		 	     parser.code.append("%struct."+cName+"* "+obj_ptr);											
								  			     while(l.hasNext()) { Exp e = (Exp)l.next();
											   			 	  parser.code.append(", "+e.locT+" "+e.locN);
								 			     }
								 		  	     Type t5 = ((Method)t1).getDomain();
										 	     if(t5 != eL.type) { parser.sem_error(""+t5+", "+eL.type, "INCOMPATIBLE ARGUMENT IN METHOD INVOCATION "+n);
				    				             	  				 RESULT = new Exp(Type.errortype(), "", "");
			   	  	   						     }
											     else { parser.code.append(")\n");
											  	      Labels lab = (Labels)((java_cup.runtime.Symbol)CUP$mjavac$stack.elementAt(CUP$mjavac$top-4)).value;
									 	  	   	      RESULT = new Exp(t3, t3.getCode(), Tmp.lv(), lab);
								     		  	     }
										    }
									   }								     
			   			          }
			   }
			   else RESULT = new Exp(Type.voidtype(), "", "");
			:}
	|	SUPER DOT IDENTIFIER:n LPAREN argument_list_opt:e RPAREN
//	|	name DOT SUPER DOT IDENTIFIER LPAREN argument_list_opt RPAREN
	;
array_access ::=
		name:sn LBRACK expression:e RBRACK
			{: if(parser.second) { if(sn.cs == null) { parser.sem_error(sn.name,"UNKNOWN NAME");
				   	      			   	 RESULT = new Exp(Type.errortype(), "", "");
			   			     }
			   			     else { Type t1 = sn.cs.getType();
				    				if(!t1.isArray()) { parser.sem_error(""+t1, "NOT AN ARRAY");
				    		 	   	    			  RESULT = new Exp(Type.errortype(), "", "");
			   	    				}
				   	    			else if(!e.type.isInteger()) { parser.sem_error(""+e.type, "NOT AN INTEGER");
				    		       	   		 			 RESULT = new Exp(Type.errortype(), "", "");
					  	   		     } 
					  	   		     else { parser.code.append("		; array_access\n");
										String locT = sn.cs.getLocT();
										String locN = sn.cs.getLocN();
										if(locT.endsWith("**")) { parser.code.append("	"+Tmp.nv()+" = load "+locT+" "+locN+"\n");
														  locT = locT.replaceFirst("\\*", "");
														  locN = Tmp.lv();
					   	 								  sn.cs.putLocT(locT);
					   	 								  sn.cs.putLocN(locN);
										}
										e.load();
										String index = Tmp.lv();
										if(locT.endsWith("]*")) parser.code.append("	"+Tmp.nv()+" = getelementptr "+locT+" "+locN+", i32 0, i32 "+index+"\n");
										else parser.code.append("	"+Tmp.nv()+" = getelementptr "+locT+" "+locN+", i32 "+index+"\n");
										Type t = ((Array)t1).getBase();
										RESULT = new Exp(t, t.getCode()+"*", Tmp.lv());
								     }
			   			     }
			   }
			   else RESULT = new Exp(Type.voidtype(), "", "");
			:}
	|	primary_no_new_array:e1 LBRACK expression:e2 RBRACK
			{: if(!e2.type.isInteger()) { parser.sem_error(""+e2.type, "NOT AN INTEGER");
				    		            RESULT = new Exp(Type.errortype(), "", "");
			   } 
			   else RESULT = e1;
			:}
	;
postfix_expression ::=
		primary:e
			{: RESULT = e; :}
	|	name:sn
			{: if(sn.cs == null) { parser.sem_error(sn.name,"UNKNOWN NAME");
				   		     RESULT = new Exp(Type.errortype(), "", "");
			   }
			   else { if((parser.second) && (sn.cs.getType() == Type.bool())) { Labels l = (Labels)((java_cup.runtime.Symbol)CUP$mjavac$stack.elementAt(CUP$mjavac$top-1)).value;									    
											    	     		  if(!l.bvalue) parser.code.append("	br i1 "+sn.cs.getLocN()+", label %"+l.lTrue+", label %"+l.lFalse+"\n");
											    	     		  RESULT = new Exp(Type.bool(), sn.cs.getLocT(), sn.cs.getLocN(), l);
			   	    }
				    else RESULT = new Exp(sn.cs.getType(), sn.cs.getLocT(), sn.cs.getLocN()); 
			   }
			:}
	|	AT name:sn
			{: if(sn.cs == null) { parser.sem_error(sn.name,"UNKNOWN NAME");
				   		     RESULT = new Exp(Type.errortype(), "", "");
			   }
			   else RESULT = new Exp(Type.reference(sn.cs.getType()), sn.cs.getLocT(), sn.cs.getLocN()+"&"); 
			:}
	;
M10 ::=
			{: if(parser.second) { parser.code.append("		; unary_NOT_expression\n");
			   			     RESULT = (Labels)((java_cup.runtime.Symbol)CUP$mjavac$stack.elementAt(CUP$mjavac$top-1)).value;
			   }
			:}
	;
unary_expression ::=
		postfix_expression:e
			{: RESULT = e; :}
	|	NOT M10:l unary_expression:e
			{: if(parser.second) if(!e.type.isBool()) { parser.sem_error(""+e.type, "NOT A BOOLEAN");
				    		   			   	  RESULT = new Exp(Type.errortype(), "", "");
						   }	   
			   			   else { String s = l.lTrue;
			   			     	    l.lTrue = l.lFalse;
			   			     	    l.lFalse = s;
 							    RESULT = new Exp(Type.bool(), l);
						   }						    
			:}
	|	PLUS unary_expression:e
			{: if(parser.second) if(!e.type.isNumber()) { parser.sem_error(""+e.type, "NOT A NUMBER");
				    		   			     	    RESULT = new Exp(Type.errortype(), "", "");
						   }	   
			   			   else  RESULT = e;						    
			:}
	|	MINUS unary_expression:e
			{: if(parser.second) { if(!e.type.isNumber()) { parser.sem_error(""+e.type, "NOT A NUMBER");
				    		   			     	    RESULT = new Exp(Type.errortype(), "", "");
						   }	   
			   			   parser.code.append("		; unary_minus_expression\n");
						   e.load();
						   String loc = ""+Tmp.nv();
						   parser.code.append("	"+loc+" = sub "+e.locT+" 0"+", "+e.locN+"\n");
						   Type t = e.type;
						   RESULT = new Exp(t, t.getCode(), loc);
			   }						    
			:}
	;
multiplicative_expression ::=
		unary_expression:e
			{: RESULT = e; :}
	|	multiplicative_expression:e1 MULT unary_expression:e2
			{: if(parser.second) { boolean b1 = e1.type.isNumber();
						     if(!b1) parser.sem_error(""+e1.type, "NOT A NUMBER");
			   			     boolean b2 = e2.type.isNumber();
						     if(!b2) parser.sem_error(""+e2.type, "NOT A NUMBER");
				    		     if(b1 && b2) { parser.code.append("		; multiplicative_MULT_expression\n");
									  e1.load();
									  e2.load();
									  String loc = ""+Tmp.nv();
									  Type t = Type.max(e1.type, e2.type);
									  String l1 = e1.widen(t);
									  String l2 = e2.widen(t);
									  parser.code.append("	"+loc+" = mul "+t.getCode()+" "+l1+", "+l2+"\n");						  
									  RESULT = new Exp(t, t.getCode(), loc);
						     }	   
			   			     else  RESULT = new Exp(Type.errortype(), "", "");
						   } 
			:}
	|	multiplicative_expression:e1 DIV unary_expression:e2
			{: if(parser.second) { boolean b1 = e1.type.isNumber();
						     if(!b1) parser.sem_error(""+e1.type, "NOT A NUMBER");
			   			     boolean b2 = e2.type.isNumber();
						     if(!b2) parser.sem_error(""+e2.type, "NOT A NUMBER");
				    		     if(b1 && b2) { parser.code.append("		; multiplicative_DIV_expression\n");
									  e1.load();
									  e2.load();
									  String loc = ""+Tmp.nv();
									  Type t = Type.max(e1.type, e2.type);
									  String l1 = e1.widen(t);
									  String l2 = e2.widen(t);
									  if(t.isFloating()) parser.code.append("	"+loc+" = fdiv "+t.getCode()+" "+l1+", "+l2+"\n");
									  else parser.code.append("	"+loc+" = sdiv "+t.getCode()+" "+l1+", "+l2+"\n");					  
									  RESULT = new Exp(t, t.getCode(), loc);
						     }   
			   			     else  RESULT = new Exp(Type.errortype(), "", "");
						   } 
			:}
	|	multiplicative_expression:e1 MOD unary_expression:e2
			{: if(parser.second) { boolean b1 = e1.type.isInteger();
						     if(!b1) parser.sem_error(""+e1.type, "NOT AN INTEGER");
			   			     boolean b2 = e2.type.isInteger();
						     if(!b2) parser.sem_error(""+e2.type, "NOT AN INTEGER");
				    		     if(b1 && b2) { parser.code.append("		; multiplicative_MOD_expression\n");
									  e1.load();
									  e2.load();
									  String loc = ""+Tmp.nv();
									  parser.code.append("	"+loc+" = srem i32 "+e1.locN+", "+e2.locN+"\n");					  
									  RESULT = new Exp(e1.type, "i32", loc);
						     }   	   
			   			     else  RESULT = new Exp(Type.errortype(), "", "");
						   } 
			:}
	|	error 
			{: parser.report_error("multiplicative_expression","WRONG"); :} 
		MULT unary_expression
	|	error 
			{: parser.report_error("multiplicative_expression","WRONG"); :} 
		DIV unary_expression	
	|	error 
			{: parser.report_error("multiplicative_expression","WRONG"); :} 
		MOD unary_expression
	;
additive_expression ::=
		multiplicative_expression:e
			{: RESULT = e; :}
	|	additive_expression:e1 PLUS multiplicative_expression:e2
			{: if(parser.second) { boolean b1 = e1.type.isNumber();
						     if(!b1) parser.sem_error(""+e1.type, "NOT A NUMBER");
			   			     boolean b2 = e2.type.isNumber();
						     if(!b2) parser.sem_error(""+e2.type, "NOT A NUMBER");
				    		     if(b1 && b2) { parser.code.append("		; additive_PLUS_expression\n");
									  e1.load();
									  e2.load();
									  String loc = ""+Tmp.nv();
									  Type t = Type.max(e1.type, e2.type);
									  String l1 = e1.widen(t);
									  String l2 = e2.widen(t);
									  parser.code.append("	"+loc+" = add "+t.getCode()+" "+l1+", "+l2+"\n");
									  RESULT = new Exp(t, t.getCode(), loc);
						     }	   
			   			     else  RESULT = new Exp(Type.errortype(), "", "");
						   } 
			:}
	|	additive_expression:e1 MINUS multiplicative_expression:e2
			{: if(parser.second) { boolean b1 = e1.type.isNumber();
						     if(!b1) parser.sem_error(""+e1.type, "NOT A NUMBER");
			   			     boolean b2 = e2.type.isNumber();
						     if(!b2) parser.sem_error(""+e2.type, "NOT A NUMBER");
				    		     if(b1 && b2) { parser.code.append("		; additive_MINUS_expression\n");
									  e1.load();
									  e2.load();
									  String loc = ""+Tmp.nv();
									  Type t = Type.max(e1.type, e2.type);
									  String l1 = e1.widen(t);
									  String l2 = e2.widen(t);
									  parser.code.append("	"+loc+" = sub "+t.getCode()+" "+l1+", "+l2+"\n");
									  RESULT = new Exp(t, t.getCode(), loc);
						     }	   
			   			     else  RESULT = new Exp(Type.errortype(), "", "");
						   } 
			:}
	;
relational_expression ::=
		additive_expression:e
			{: RESULT = e; :}
	|	relational_expression:e1 LT additive_expression:e2
			{: if(parser.second) { boolean b1 = e1.type.isNumber();
						     if(!b1) parser.sem_error(""+e1.type, "NOT A NUMBER");
			   			     boolean b2 = e2.type.isNumber();
						     if(!b2) parser.sem_error(""+e2.type, "NOT A NUMBER");
				    		     if(b1 && b2) { parser.code.append("		; relational_LT_expression\n");
									  Labels l = (Labels)((java_cup.runtime.Symbol)CUP$mjavac$stack.elementAt(CUP$mjavac$top-3)).value;
									  e1.load();
									  e2.load();
									  Type t = Type.max(e1.type, e2.type);
									  String l1 = e1.widen(t);
									  String l2 = e2.widen(t);
									  parser.code.append("	"+Tmp.nv()+" = icmp slt "+t.getCode()+" "+l1+", "+l2+"\n");
									  parser.code.append("	br i1 "+Tmp.lv()+", label %"+l.lTrue+", label %"+l.lFalse+"\n");
									  RESULT = new Exp(Type.bool(), "i1", Tmp.lv(), l);
						     }	   
			   			     else  RESULT = new Exp(Type.errortype(), "", "");
						   } 
			:}
	|	relational_expression:e1 GT additive_expression:e2
			{: if(parser.second) { boolean b1 = e1.type.isNumber();
						     if(!b1) parser.sem_error(""+e1.type, "NOT A NUMBER");
			   			     boolean b2 = e2.type.isNumber();
						     if(!b2) parser.sem_error(""+e2.type, "NOT A NUMBER");
				    		     if(b1 && b2) { parser.code.append("		; relational_GT_expression\n");
									  Labels l = (Labels)((java_cup.runtime.Symbol)CUP$mjavac$stack.elementAt(CUP$mjavac$top-3)).value;
									  e1.load();
									  e2.load();
									  Type t = Type.max(e1.type, e2.type);
									  String l1 = e1.widen(t);
									  String l2 = e2.widen(t);
									  parser.code.append("	"+Tmp.nv()+" = icmp sgt "+t.getCode()+" "+l1+", "+l2+"\n");
									  parser.code.append("	br i1 "+Tmp.lv()+", label %"+l.lTrue+", label %"+l.lFalse+"\n");
									  RESULT = new Exp(Type.bool(), "i1", Tmp.lv(), l);
						     }	   	   
			   			     else  RESULT = new Exp(Type.errortype(), "", "");
						   } 
			:}
	|	relational_expression:e1 LTEQ additive_expression:e2
			{: if(parser.second) { boolean b1 = e1.type.isNumber();
						     if(!b1) parser.sem_error(""+e1.type, "NOT A NUMBER");
			   			     boolean b2 = e2.type.isNumber();
						     if(!b2) parser.sem_error(""+e2.type, "NOT A NUMBER");
				    		     if(b1 && b2) { parser.code.append("		; relational_LTEQ_expression\n");
									  Labels l = (Labels)((java_cup.runtime.Symbol)CUP$mjavac$stack.elementAt(CUP$mjavac$top-3)).value;
									  e1.load();
									  e2.load();
									  Type t = Type.max(e1.type, e2.type);
									  String l1 = e1.widen(t);
									  String l2 = e2.widen(t);
									  parser.code.append("	"+Tmp.nv()+" = icmp sle "+t.getCode()+" "+l1+", "+l2+"\n");
									  parser.code.append("	br i1 "+Tmp.lv()+", label %"+l.lTrue+", label %"+l.lFalse+"\n");
									  RESULT = new Exp(Type.bool(), "i1", Tmp.lv(), l);
						     }	   	   
			   			     else  RESULT = new Exp(Type.errortype(), "", "");
						   } 
			:}
	|	relational_expression:e1 GTEQ additive_expression:e2
			{: if(parser.second) { boolean b1 = e1.type.isNumber();
						     if(!b1) parser.sem_error(""+e1.type, "NOT A NUMBER");
			   			     boolean b2 = e2.type.isNumber();
						     if(!b2) parser.sem_error(""+e2.type, "NOT A NUMBER");
				    		     if(b1 && b2) { parser.code.append("		; relational_GTEQ_expression\n");
									  Labels l = (Labels)((java_cup.runtime.Symbol)CUP$mjavac$stack.elementAt(CUP$mjavac$top-3)).value;
									  e1.load();
									  e2.load();
							  		  Type t = Type.max(e1.type, e2.type);
									  String l1 = e1.widen(t);
									  String l2 = e2.widen(t);
									  parser.code.append("	"+Tmp.nv()+" = icmp sge "+t.getCode()+" "+l1+", "+l2+"\n");
									  parser.code.append("	br i1 "+Tmp.lv()+", label %"+l.lTrue+", label %"+l.lFalse+"\n");
									  RESULT = new Exp(Type.bool(), "i1", Tmp.lv(), l);
						     }	   	   
			   			     else  RESULT = new Exp(Type.errortype(), "", "");
						   } 
			:}
	|	error 
			{: parser.report_error("relational_expression","WRONG"); :} 
		LT additive_expression
	|	error 
			{: parser.report_error("relational_expression","WRONG"); :} 
		GT additive_expression
	|	error 
			{: parser.report_error("relational_expression","WRONG"); :} 
		LTEQ additive_expression
	|	error 
			{: parser.report_error("relational_expression","WRONG"); :} 
		GTEQ additive_expression
	;
equality_expression ::=
		relational_expression:e
			{: RESULT = e; :}
	|	equality_expression:e1 EQEQ relational_expression:e2
			{: if(parser.second) if(e1.type != e2.type) if(e1.type.isNumber() && e2.type.isNumber()) { parser.code.append("		; equality_EQEQ_expression\n");
							    											 Labels l = (Labels)((java_cup.runtime.Symbol)CUP$mjavac$stack.elementAt(CUP$mjavac$top-3)).value;
							    											 e1.load();
							    											 e2.load();
									  									 Type t = Type.max(e1.type, e2.type);
									  									 String l1 = e1.widen(t);
									  									 String l2 = e2.widen(t);
							    											 parser.code.append("	"+Tmp.nv()+" = icmp eq "+t.getCode()+" "+l1+", "+l2+"\n");
							    											 parser.code.append("	br i1 "+Tmp.lv()+", label %"+l.lTrue+", label %"+l.lFalse+"\n");
							    											 RESULT = new Exp(Type.bool(), "i1", Tmp.lv(), l);
							  			  }
							  			  else { parser.sem_error(""+e1.type+", "+e2.type, "INCOMPATIBLE TYPES");
				    						    	   RESULT = new Exp(Type.errortype(), "", "");
			   			   	  			  }
 						   else { parser.code.append("		; equality_EQEQ_expression\n");
							    Labels l = (Labels)((java_cup.runtime.Symbol)CUP$mjavac$stack.elementAt(CUP$mjavac$top-3)).value;
							    e1.load();
							    e2.load();
							    parser.code.append("	"+Tmp.nv()+" = icmp eq "+e1.locT+" "+e1.locN+", "+e2.locN+"\n");
							    parser.code.append("	br i1 "+Tmp.lv()+", label %"+l.lTrue+", label %"+l.lFalse+"\n");
							    RESULT = new Exp(Type.bool(), "i1", Tmp.lv(), l);
						   }	   	   
			:}
	|	equality_expression:e1 NOTEQ relational_expression:e2
			{: if(parser.second) if(e1.type != e2.type) if(e1.type.isNumber() && e2.type.isNumber()) { parser.code.append("		; equality_NOTEQ_expression\n");
							    											 Labels l = (Labels)((java_cup.runtime.Symbol)CUP$mjavac$stack.elementAt(CUP$mjavac$top-3)).value;
							    											 e1.load();
							    											 e2.load();
									  									 Type t = Type.max(e1.type, e2.type);
									  									 String l1 = e1.widen(t);
									  									 String l2 = e2.widen(t);
							    											 parser.code.append("	"+Tmp.nv()+" = icmp ne "+t.getCode()+" "+l1+", "+l2+"\n");
							    											 parser.code.append("	br i1 "+Tmp.lv()+", label %"+l.lTrue+", label %"+l.lFalse+"\n");
							    											 RESULT = new Exp(Type.bool(), "i1", Tmp.lv(), l);
							  			  }
							  			  else { parser.sem_error(""+e1.type+", "+e2.type, "INCOMPATIBLE TYPES");
				    						    	   RESULT = new Exp(Type.errortype(), "", "");
			   			   	  			  }
 						   else { parser.code.append("		; equality_NOTEQ_expression\n");
							    Labels l = (Labels)((java_cup.runtime.Symbol)CUP$mjavac$stack.elementAt(CUP$mjavac$top-3)).value;
							    e1.load();
							    e2.load(); 
							    parser.code.append("	"+Tmp.nv()+" = icmp ne "+e1.locT+" "+e1.locN+", "+e2.locN+"\n");
							    parser.code.append("	br i1 "+Tmp.lv()+", label %"+l.lTrue+", label %"+l.lFalse+"\n");
							    RESULT = new Exp(Type.bool(), "i1", Tmp.lv(), l);
						   }	   
			:}
	|	error 
			{: parser.report_error("equality_expression","WRONG"); :} 
		EQEQ relational_expression
	|	error 
			{: parser.report_error("equality_expression","WRONG"); :} 
		NOTEQ relational_expression
	;
M8 ::=
			{: if(parser.second) { Labels l = (Labels)((java_cup.runtime.Symbol)CUP$mjavac$stack.elementAt(CUP$mjavac$top-2)).value;
						     parser.code.append("		; conditional_and_expression\n");
						     parser.code.append(l.lTrue+":	; l_True\n");
						     l.lTrue = Tmp.nl();
			   	    		     RESULT = l;
			   }
			:}
	;
conditional_and_expression ::=
		equality_expression:e
			{: RESULT = e; :}
	|	conditional_and_expression:e1 ANDAND M8:l equality_expression:e2
			{: if(parser.second) { boolean b1 = e1.type.isBool();
						     if(!b1) parser.sem_error(""+e1.type, "NOT A BOOLEAN");
			   			     boolean b2 = e2.type.isBool();
						     if(!b2) parser.sem_error(""+e2.type, "NOT A BOOLEAN");
				    		     if(b1 && b2) RESULT = e2;	   
			   			     else  RESULT = new Exp(Type.errortype(), "", "");
						   } 
			:}
	|	error 
			{: parser.report_error("conditional_and_expression","WRONG"); :} 
		ANDAND equality_expression
	;
M9 ::=
			{: if(parser.second) { Labels l = (Labels)((java_cup.runtime.Symbol)CUP$mjavac$stack.elementAt(CUP$mjavac$top-2)).value;
						     parser.code.append("		; conditional_or_expression\n");
						     parser.code.append(l.lFalse+":	; l_False\n");
						     l.lFalse = Tmp.nl();
			   	    		     RESULT = l;
			   }
			:}
	;
conditional_or_expression ::=
		conditional_and_expression:e
			{: RESULT = e; :}
	|	conditional_or_expression:e1 OROR M9:l conditional_and_expression:e2
			{: if(parser.second) { boolean b1 = e1.type.isBool();
						     if(!b1) parser.sem_error(""+e1.type, "NOT A BOOLEAN");
			   			     boolean b2 = e2.type.isBool();
						     if(!b2) parser.sem_error(""+e2.type, "NOT A BOOLEAN");
				    		     if(b1 && b2) RESULT = e2;	   
			   			     else  RESULT = new Exp(Type.errortype(), "", "");
						   } 
			:}
	|	error 
			{: parser.report_error("conditional_or_expression","WRONG"); :} 
		OROR conditional_and_expression
	;
M12 ::=
			{: if(parser.second) { Exp e = (Exp)((java_cup.runtime.Symbol)CUP$mjavac$stack.elementAt(CUP$mjavac$top-1)).value;
						     parser.code.append("		; conditional_expression\n");
						     if (e.lab.bvalue) { e.load();
										 parser.code.append("	br i1 "+e.locN+", label %"+e.lab.lTrue+", label %"+e.lab.lFalse+"\n");
						     }
						     parser.code.append(e.lab.lTrue+": 	; l_True\n");
						     RESULT = e.lab; 
			   }
			:}
	;
M13 ::=
			{: if(parser.second) { Exp e1 = (Exp)((java_cup.runtime.Symbol)CUP$mjavac$stack.elementAt(CUP$mjavac$top-4)).value;
						     Exp e2 = (Exp)((java_cup.runtime.Symbol)CUP$mjavac$stack.elementAt(CUP$mjavac$top-1)).value;
						     e2.load();
						     parser.code.append("	br label %"+e1.lab.lNext+"\n");
						     parser.code.append(e1.lab.lFalse+": 	; l_False\n");
						     RESULT = e1.lab;
			   }
			:}
	;
conditional_expression ::=
		conditional_or_expression:e
			{: RESULT = e; :}
	|	conditional_or_expression:e1 QUESTION M12 expression:e2 COLON M13 conditional_expression:e3
			{: if(parser.second) { boolean b1 = e1.type.isBool();
						     if(!b1) { parser.sem_error(""+e1.type, "NOT A BOOLEAN");
								   RESULT = new Exp(Type.errortype(), "", "");
						     }
			   			     else if(e2.type != e3.type) if(e2.type.isNumber() && e3.type.isNumber()) { e2.load();
							    											 	  e3.load();
									  									 	  Type t = Type.max(e2.type, e3.type);
									  									 	  String l2 = e2.widen(t);
									  									 	  String l3 = e3.widen(t);
												  							  parser.code.append("	br label %"+e1.lab.lNext+"\n");
						     						  							  parser.code.append(e1.lab.lNext+": 	; l_Next\n");
												  							  parser.code.append("	"+Tmp.nv()+" = phi "+t.getCode()+" ["+l2+", %"+e1.lab.lTrue+"] ,["+l3+", %"+e1.lab.lFalse+"]\n");
											       							  RESULT = new Exp(t, t.getCode(), Tmp.lv());
						     					   }
							    				   else { parser.sem_error(""+e2.type+", "+e3.type, "INCOMPATIBLE TYPES");
								     				    RESULT = new Exp(Type.errortype(), "", "");
						     	    				   }
				    		          else { e3.load();
								     parser.code.append("	br label %"+e1.lab.lNext+"\n");
						     		     parser.code.append(e1.lab.lNext+": 	; l_Next\n");
								     parser.code.append("	"+Tmp.nv()+" = phi "+e2.locT+" ["+e2.locN+", %"+e1.lab.lTrue+"] ,["+e3.locN+", %"+e1.lab.lFalse+"]\n");
								     RESULT = new Exp(e2.type, e2.type.getCode(), Tmp.lv());
						     	    }	   
						   } 
			:}
	|	error 
			{: parser.report_error("conditional_expression","WRONG"); :} 
		QUESTION expression COLON conditional_or_expression
	;
assignment_expression ::=
		conditional_expression:e
			{: RESULT = e; :}
	|	assignment:e
			{: RESULT = e; :}
	;
assignment ::=
		left_hand_side:e1 EQ M14:l assignment_expression:e2
			{: if(parser.second) if(e1.type.isArray()) { if(e1.type != e2.type) { parser.sem_error(""+e1.type+", "+e2.type, "INCOMPATIBLE TYPES");
				    						    	   			    RESULT = new Exp(Type.errortype(), "", "");
			   			   	  			   }
										   else { Symb s = Env.get(e1.locN.replaceFirst("%", ""));
											    s.putLocT(e2.locT);
											    s.putLocN(e2.locN);
										   }
						   }
						   else if(e1.type != e2.type) if(e1.type.isNumber() && e2.type.isNumber()) { Type t = Type.max(e1.type, e2.type);
															 				if(e2.type != t) { parser.code.append("		; assignment\n");
																						 e2.load();
									  						 							 String l2 = e2.widen(t);
							    	  														 parser.code.append("	store "+t.getCode()+" "+l2+", "+e1.locT+" "+e1.locN+"\n");
							    	  														 RESULT = new Exp(e1.type, e1.locT, e1.locN, l);
																			}
							  				 								else { parser.sem_error(""+e1.type+", "+e2.type, "LEFT-HAND TYPE SHORTER THAN RIGHT-END TYPE");
				    						    	   	  								 RESULT = new Exp(Type.errortype(), "", "");
			   			   	  				 								}
											 }
							  				 else { parser.sem_error(""+e1.type+", "+e2.type, "INCOMPATIBLE TYPES");
				    						    	   	  RESULT = new Exp(Type.errortype(), "", "");
			   			   	  				 }
			   			   	  else { parser.code.append("		; assignment\n");
							    	   if(e2.type.isBool() && !e2.lab.bvDone) { parser.code.append(e2.lab.lTrue+":	; l_True\n");
											   			   	  parser.code.append("	br label %"+e2.lab.lNext+"\n");
											   			   	  parser.code.append(e2.lab.lFalse+":	; l_False\n");
											   			  	  parser.code.append("	br label %"+e2.lab.lNext+"\n");
											   			  	  parser.code.append(e2.lab.lNext+":	; l_Next\n");
											  			   	  parser.code.append("	"+Tmp.nv()+" = phi i1 [true, %"+e2.lab.lTrue+"] ,[false, %"+e2.lab.lFalse+"]\n");
											   			   	  e2.locT = "i1";
											   			  	  e2.locN = Tmp.lv();
							    	   }
								   e2.load();
							    	   parser.code.append("	store "+e2.locT+" "+e2.locN+", "+e1.locT+" "+e1.locN+"\n");
							    	   RESULT = new Exp(e1.type, e1.locT, e1.locN, l);
						   	 }
			   else RESULT = new Exp(Type.voidtype(), "", "");
			:}
	|	error 
			{: parser.report_error("left_hand_side","WRONG"); :} 
		EQ assignment_expression
	;
left_hand_side ::=
		name:sn
			{: if(parser.second) { if(sn.cs == null) { parser.sem_error(sn.name,"UNKNOWN NAME");
				   		 			   	 RESULT = new Exp(Type.errortype(), "", "");
			   			     }
			   			     else { String locN = sn.cs.getLocN();
								if(locN.startsWith("getelementptr")) { String cn = Name.getCurrentClass().getName();
														   parser.code.append("		; left_hand_side\n");
														   locN = locN.replaceFirst("struct.+\\*", "struct."+cn+"*");
														   parser.code.append("	"+Tmp.nv()+" = "+locN+"\n");
														   locN = Tmp.lv();
					   	 								   RESULT = new Exp(sn.cs.getType(), sn.cs.getLocT(), Tmp.lv());
							      }
								else RESULT = new Exp(sn.cs.getType(), sn.cs.getLocT(), sn.cs.getLocN());
						     }

			   }
			   else RESULT = new Exp(Type.voidtype(), "", "");
			:}
	|	field_access:e
			{: RESULT = e; :}
	|	array_access:e
			{: RESULT = e; :}
	;
expression_opt ::=
	|	expression:e
			{: RESULT = e; :}
	;
expression ::=
		assignment_expression:e
			{: RESULT = e; :}
	;
