class CodeGenVisitor(BaseVisitor, Utils):
    def __init__(self, astTree, env, dir_):
        #astTree: AST
        #env: List[Symbol]
        #dir_: File

        self.astTree = astTree
        self.env = env
        self.className = "MCClass"
        self.path = dir_
        self.emit = Emitter(self.path + "/" + self.className + ".j")

    def visitProgram(self, ast, c):
        #ast: Program
        #c: Any

        self.emit.printout(self.emit.emitPROLOG(self.className, "java.lang.Object"))
        e = SubBody(None, self.env)
        list_vardecl=[]
        for item in ast.decl:
            if type(item) is VarDecl:
                self.visit(item, e)
                list_vardecl.insert(0,Symbol(item.variable,self.visit(item.varType,c),CName(self.className)))
            else:
                isMain= item.name.name == "main" and len(item.param)==0 and type(item.returnType) is VoidType
                intype=[ArrayPointerType(StringType())] if isMain else [self.visit(x.varType,c) for x in item.param]
                self.env.insert(0,Symbol(item.name.name,MType(intype,item.returnType),CName(self.className)))
        list(map(lambda x:self.visit(x,SubBody(None,self.env)) if type(x) is not VarDecl else None,ast.decl))
        # generate default constructor
        self.genMETHOD(FuncDecl(Id("<init>"), list(), None, Block(list())), c, Frame("<init>", VoidType))
        #generate function for static array global
        self.genMETHOD(FuncDecl(Id("<clinit>"), list(),None , Block(list())), list_vardecl, Frame("<clinit>", VoidType))
        self.emit.emitEPILOG()
        
        return c

    def genMETHOD(self, consdecl, o, frame):
        #consdecl: FuncDecl
        #o: Any
        #frame: Frame

        isInit = consdecl.returnType is None and consdecl.name.name=="<init>"
        # isMain = consdecl.name.name == "main" and len(consdecl.param) == 0 and type(consdecl.returnType) is VoidType
        isMain=consdecl.name.name == "main" and len(consdecl.param)==0 and type(consdecl.returnType) is VoidType

        isClinit=consdecl.name.name == "<clinit>"

        isFunc =(not isInit) and (not isMain) and (not isClinit)

        returnType = VoidType() if isInit else consdecl.returnType
        methodName = "<init>" if isInit else consdecl.name.name
        intype = [ArrayPointerType(StringType())] if isMain else [self.visit(x.varType,o) for x in consdecl.param]
        mtype = MType(intype, returnType)
        if isClinit:
            self.emit.printout(self.emit.emitMETHOD("<clinit>", MType([],VoidType()), isClinit, frame))
        else:
            self.emit.printout(self.emit.emitMETHOD(methodName, mtype, not isInit, frame))

        frame.enterScope(True)

        glenv = o

        # Generate code for parameter declarations
        if isInit:
            self.emit.printout(self.emit.emitVAR(frame.getNewIndex(), "this", ClassType(self.className), frame.getStartLabel(), frame.getEndLabel(), frame))
        if isMain:
            self.emit.printout(self.emit.emitVAR(frame.getNewIndex(), "args", ArrayPointerType(StringType()), frame.getStartLabel(), frame.getEndLabel(), frame))
            [self.visit(x,SubBody(frame,glenv)) for x in [ivar for ivar in consdecl.body.member if type(ivar) is VarDecl]]
        if isFunc:
            [self.visit(x,SubBody(frame,glenv)) for x in (consdecl.param+[ivar for ivar in consdecl.body.member if type(ivar) is VarDecl])]

        body = consdecl.body
        self.emit.printout(self.emit.emitLABEL(frame.getStartLabel(), frame))

        # Generate code for statements
        if isInit:
            self.emit.printout(self.emit.emitREADVAR("this", ClassType(self.className), 0, frame))
            self.emit.printout(self.emit.emitINVOKESPECIAL(frame))
        if isClinit:
            for item in glenv:
                if type(item.mtype) is ArrayType:
                    get_dimen=item.mtype.dimen
                    # self.emit.printout(self.emit.emitREADVAR("this", ClassType(self.className), 0, frame))
                    self.emit.printout(self.emit.emitPUSHICONST(int(get_dimen),frame))
                    self.emit.printout(self.emit.emitNEWARRAY(item.mtype.eleType,frame))
                    self.emit.printout(self.emit.emitPUTSTATIC(item.value.value+"/"+item.name,item.mtype,frame))
        
        # list(map(lambda x: self.visit(x, SubBody(frame, glenv)) if type(x) is not VarDecl else None, body.member))
        #moi them
        for item_func in body.member:
            if type(item_func) is BinaryOp:
                self.visitStatement(item_func,SubBody(frame,glenv))
            elif type(item_func) is not VarDecl:
                self.visit(item_func,SubBody(frame,glenv))
        
        self.emit.printout(self.emit.emitLABEL(frame.getEndLabel(), frame))
        if type(returnType) is VoidType:
            self.emit.printout(self.emit.emitRETURN(VoidType(), frame))
        if isClinit:
            self.emit.printout(self.emit.emitRETURN(VoidType(),frame))
        self.emit.printout(self.emit.emitENDMETHOD(frame))
        frame.exitScope()

    def visitFuncDecl(self, ast, o):
        #ast: FuncDecl
        #o: Any
        
        subctxt = o
        frame = Frame(ast.name, ast.returnType)
        self.genMETHOD(ast, subctxt.sym.copy(), frame)
        # return SubBody(None, [Symbol(ast.name, MType(list(), ast.returnType), CName(self.className))] + subctxt.sym)
        return None

    def visitVarDecl(self,ast,o):
        frame=o.frame
        nenv=o.sym
        getType=self.visit(ast.varType,o)
        if frame is None: #vardecl in global
            nenv.insert(0,Symbol(ast.variable,getType,CName(self.className)))
            self.emit.printout(self.emit.emitATTRIBUTE(ast.variable,getType,False,""))
        else: #vardecl in function or block
            index=frame.getNewIndex()
            nenv.insert(0,Symbol(ast.variable,getType,Index(index)))
            self.emit.printout(self.emit.emitVAR(index,ast.variable,getType,frame.getStartLabel(),frame.getEndLabel(),frame))
            #declare for arraytype variable
            if type(getType) is ArrayType:
                get_dimen=ast.varType.dimen
                self.emit.printout(self.emit.emitPUSHICONST(int(get_dimen),frame))
                self.emit.printout(self.emit.emitNEWARRAY(getType.eleType,frame))
                self.emit.printout(self.emit.emitWRITEVAR(ast.variable,getType,index,frame))
        

    def visitBlock(self,ast,o):
        subtxt=o
        frame=subtxt.frame
        sym=subtxt.sym.copy()
        frame.enterScope(False)
        list(map(lambda x: self.visit(x,SubBody(frame,sym)) if type(x) is VarDecl else None,ast.member))
        self.emit.printout(self.emit.emitLABEL(frame.getStartLabel(),frame))
        # get_list=list(map(lambda x: self.visit(x,SubBody(frame,sym)) if type(x) is not VarDecl else None,ast.member))
        get_list=[]
        for item in ast.member:
            if type(item) is BinaryOp:
                get_list.append(self.visitStatement(item,SubBody(frame,sym)))
            elif type(item) is not VarDecl:
                get_list.append(self.visit(item,SubBody(frame,sym)))
        self.emit.printout(self.emit.emitLABEL(frame.getEndLabel(),frame))
        frame.exitScope()
        for x in get_list:
            if str(x)=="Return":
                return "Return"
        return None


    #--------------------------------------Statement---------------------------------#
    def visitStatement(self,ast,o):
        codeBin,typeBin=self.visit(ast,o)
        self.emit.printout(codeBin)
        

    def visitReturn(self,ast,o):
        if ast.expr is None:
            self.emit.printout(self.emit.emitRETURN(VoidType(),o.frame))
        else:
            rettype=o.frame.returnType
            ecode, etype=self.visit(ast.expr,Access(o.frame,o.sym,False,True))
            temp=""
            if type(ast.expr) is ArrayCell:
                ecode1,etype1=self.visit(ast.expr,Access(o.frame,o.sym,False,False))
                temp=ecode1
                etype=etype.eleType
            if type(rettype) is FloatType and type(etype) is IntType:
                self.emit.printout(ecode + temp + self.emit.emitI2F(o.frame) + self.emit.emitRETURN(rettype,o.frame))
            else:
                self.emit.printout(ecode+ temp + self.emit.emitRETURN(rettype,o.frame))
        return "Return"

    def visitIf(self,ast,o):
        ctxt=o
        frame=o.frame
        sym=o.sym
        expCode, expType= self.visit(ast.expr,Access(frame,ctxt.sym,False,True))
        labelELSE= frame.getNewLabel()
        labelEXIT= frame.getNewLabel() if ast.elseStmt else None
        self.emit.printout(expCode+self.emit.emitIFFALSE(labelELSE,frame))
        # thenStmt=self.visit(ast.thenStmt,ctxt) if type(ast.thenStmt) is not BinaryOp else self.visitStatement(ast.thenStmt,ctxt)
        thenStmt=""
        if type(ast.thenStmt) is BinaryOp:
            thenStmt=self.visitStatement(ast.thenStmt,Access(frame,sym,False,True))
        elif type(ast.thenStmt) is CallExpr:
            thenStmt=self.visit(ast.thenStmt,SubBody(frame,sym))
        else:
            thenStmt=self.visit(ast.thenStmt,Access(frame,sym,False,True))

        self.emit.printout(self.emit.emitGOTO(labelEXIT,frame)) if ast.elseStmt and str(thenStmt) != "Return" else None
        self.emit.printout(self.emit.emitLABEL(labelELSE,frame))
        elseStmt=""
        if ast.elseStmt:
            # elseStmt=self.visit(ast.elseStmt,ctxt) if type(ast.elseStmt) is not BinaryOp else self.visitStatement(ast.elseStmt,ctxt)
            if type(ast.elseStmt) is BinaryOp:
                elseStmt=self.visitStatement(ast.elseStmt,Access(frame,sym,False,True))
            elif type(ast.elseStmt) is CallExpr:
                elseStmt=self.visit(ast.elseStmt,SubBody(frame,sym))
            else:
                elseStmt=self.visit(ast.elseStmt,Access(frame,sym,False,True))
            
            self.emit.printout(self.emit.emitLABEL(labelEXIT,frame))

        if str(thenStmt) == "Return" and str(elseStmt) == "Return":
            return "Return"
        return None
            
    def visitFor(self,ast,o):
        ctxt=o
        frame=o.frame
        sym=o.sym

        frame.enterLoop()
        labelLoop=frame.getNewLabel()
        labelBreak=frame.getBreakLabel()
        labelContinue=frame.getContinueLabel()
        
        #Generate code for initial expr
        self.visit(ast.expr1, Access(frame,sym,False,True)) if type(ast.expr1) is not BinaryOp else self.visitStatement(ast.expr1,Access(frame,sym,False,True))

        #Generate code label to start loop
        self.emit.printout(self.emit.emitLABEL(labelLoop,frame))
        #Generate code for condition expr
        expr2Code, expr2Type=self.visit(ast.expr2,Access(frame,sym,False,True))
        self.emit.printout(expr2Code)
        self.emit.printout(self.emit.emitIFFALSE(labelBreak,frame))
        #Generate code for loop statement
        # self.visit(ast.loop,Access(frame,sym,False,True)) if type(ast.loop) is not BinaryOp else self.visitStatement(ast.loop,Access(frame,sym,False,True))
        # self.visit(ast.loop,ctxt) if type(ast.loop) is not BinaryOp else self.visitStatement(ast.loop,ctxt)
        
        if type(ast.loop) is BinaryOp:
            self.visitStatement(ast.loop,Access(frame,sym,False,True))
        elif type(ast.loop) is CallExpr:
            self.visit(ast.loop,SubBody(frame,sym))
        else:
            self.visit(ast.loop,Access(frame,sym,False,True))

        #Generate code for expression step or increase variable count
        self.emit.printout(self.emit.emitLABEL(labelContinue,frame))
        self.visit(ast.expr3, Access(frame,sym,False,True)) if type(ast.expr3) is not BinaryOp else self.visitStatement(ast.expr3,Access(frame,sym,False,True))
        #goto label loop if condition is still true
        self.emit.printout(self.emit.emitGOTO(labelLoop,frame))
        #generate label exit or label break to exit
        self.emit.printout(self.emit.emitLABEL(labelBreak,frame))

        frame.exitLoop()

        return None

    # def visitDowhile(self,ast,o):
    #     ctxt=o
    #     frame=o.frame
    #     sym=o.sym

    #     frame.enterLoop()
    #     labelLoop=frame.getNewLabel()
    #     breakLabel=frame.getBreakLabel()
    #     continueLabel=frame.getContinueLabel()
    #     #generate label continue or label to start loop
    #     self.emit.printout(self.emit.emitLABEL(labelLoop,frame))
    #     #generate code for body of dowhile statement
    #     temp=[self.visit(x,Access(frame,sym,False,True)) for x in ast.sl]
    #     #generate code for label continue
    #     self.emit.printout(self.emit.emitLABEL(continueLabel,frame))
    #     #generate code for condition in while
    #     expCode,expType= self.visit(ast.exp,Access(frame,sym,False,True))
    #     self.emit.printout(expCode)
    #     #check condition is true or false
    #     self.emit.printout(self.emit.emitIFFALSE(breakLabel,frame))
    #     #goto label continue if condition is still true
    #     self.emit.printout(self.emit.emitGOTO(labelLoop,frame))
    #     #generate label exit or label break to exit
    #     self.emit.printout(self.emit.emitLABEL(breakLabel,frame))
        
    #     frame.exitLoop()
        # for item in temp:
        #     if str(item) == "Return":
        #         return "Return"
        # return None           

    def visitDowhile(self,ast,o):
        ctxt=o
        frame=o.frame
        sym=o.sym

        frame.enterLoop()

        labelStart = frame.getNewLabel() #new
        breakLabel=frame.getBreakLabel()
        continueLabel=frame.getContinueLabel()
        #generate label continue or label to start loop
        # self.emit.printout(self.emit.emitLABEL(continueLabel,frame)) 
        self.emit.printout(self.emit.emitLABEL(labelStart,frame)) 
        #generate code for body of dowhile statement
        # temp=[self.visit(x,Access(frame,sym,False,True)) for x in ast.sl]
        temp=[]
        for item in ast.sl:
            if type(item) is BinaryOp:
                temp.append(self.visitStatement(item,Access(frame,sym,False,True)))
            elif type(item) is CallExpr:
                temp.append(self.visit(item,SubBody(frame,sym)))
            else:
                temp.append(self.visit(item,Access(frame,sym,False,True)))
        self.emit.printout(self.emit.emitLABEL(continueLabel,frame))        
        #generate code for condition in while
        expCode,expType= self.visit(ast.exp,Access(frame,sym,False,True))
        self.emit.printout(expCode)
        #check condition is true or false
        self.emit.printout(self.emit.emitIFTRUE(labelStart,frame))
        #goto label continue if condition is still true
        # self.emit.printout(self.emit.emitGOTO(continueLabel,frame))
        #generate label exit or label break to exit
        self.emit.printout(self.emit.emitLABEL(breakLabel,frame))        
        frame.exitLoop()
        if temp != []:
            for x in temp:
                if str(x)== "Return":
                    return "Return"
                else:
                    return None
        else:
            return None
    
    def visitBreak(self,ast,o):
        frame=o.frame
        self.emit.printout(self.emit.emitGOTO(frame.getBreakLabel(),frame))

    def visitContinue(self,ast,o):
        frame=o.frame
        self.emit.printout(self.emit.emitGOTO(frame.getContinueLabel(),frame))

    #--------------------------------------Expression---------------------------------#


    def visitCallExpr(self, ast, o):
        #ast: CallExpr
        #o: Any

        ctxt = o
        frame = ctxt.frame
        nenv = ctxt.sym
        
        sym = self.lookup(ast.method.name, nenv, lambda x: x.name)
        cname = sym.value.value
    
        ctype = sym.mtype

        # in_ = ("", list())
        # for x in ast.param:
        #     str1, typ1 = self.visit(x, Access(frame, nenv, False, True))
        #     in_ = (in_[0] + str1, in_[1].append(typ1))
        # self.emit.printout(in_[0])
        # self.emit.printout(self.emit.emitINVOKESTATIC(cname + "/" + ast.method.name, ctype, frame))

        in_=""
        merge=list(zip(ctype.partype,ast.param))
        
        for x in merge:
            str1, typ1=self.visit(x[1],Access(frame,nenv,False,True))
            # if type(typ1) is ArrayType:
            if type(x[1]) is ArrayCell:
                str2,typ2= self.visit(x[1],Access(frame,nenv,False,False))
                str1+=str2
            if type(x[0]) is FloatType and type(typ1) is IntType:
                str1+=self.emit.emitI2F(frame)
            in_+=str1
            # if type(typ1) is ArrayPointerType:
            #     in_+=str2
        
        if type(ctxt) is SubBody:
            self.emit.printout(in_ + self.emit.emitINVOKESTATIC(cname+"/"+ast.method.name,ctype,frame)) 
            return "", ctype.rettype 
        if ctxt.isLeft==False and ctxt.isFirst==True:
            temp=in_ + self.emit.emitINVOKESTATIC(cname+"/"+ast.method.name,ctype,frame)
            return temp, ctype.rettype
        elif ctxt.isLeft==False and ctxt.isFirst==False:
            str_code, str_type=self.visit(ast.method,Access(frame,nenv,False,False))
            return str_code,str_type
        else:
            frame.push()
            str_code, str_type=self.visit(ast.method,Access(frame,nenv,True,False))
            return str_code,str_type
        
    def check_is_assign(self,type_to_check):
        if type(type_to_check) is BinaryOp:
            if type_to_check.op == '=':
                return True
        return False

    def visitBinaryOp(self,ast,o):
        ctxt=o
        frame=ctxt.frame
        nenv=ctxt.sym
        #visit assign
        if ast.op == "=":
            
            if self.check_is_assign(ast.right):
                rightCode, typeRight=self.visit(ast.right,Access(frame,nenv,False,False))
            else:
                rightCode, typeRight=self.visit(ast.right,Access(frame,nenv,False,True))
            leftCode, typeLeft=self.visit(ast.left,Access(frame,nenv,True,True))
            if type(typeRight) is not ArrayPointerType:
                if type(ast.right) is BinaryOp:
                    if ast.right.op != "=":
                        rightCode, typeRight=self.visit(ast.right,Access(frame,nenv,False,True))
                else:
                    rightCode, typeRight=self.visit(ast.right,Access(frame,nenv,False,True))

            if type(typeRight) in [ArrayType,ArrayPointerType]: #visit to load
                if type(ast.right) not in [CallExpr,Id]:
                    if not self.check_is_assign(ast.right):
                        rightCode1, typeRight1=self.visit(ast.right,Access(frame,nenv,False,False))
                        rightCode+=rightCode1
            #visit for many assign
            # if type(ast.right) is BinaryOp:
            #     if ast.right.op == "=":
            #         # temp=self.emit.emitDUP(frame)
            #         # rightCode+=temp
            #         self.visit(ast.right,Access(frame,nenv,False,False))
            #         # rightCode+=rightCode1

            temp_access=""
            if type(o) is Access:
                if o.isLeft==False and o.isFirst==False:
                    if type(ast.left) is not ArrayCell:
                        temp_access=self.emit.emitDUP(frame)
                    if type(ast.left) is ArrayCell:
                        temp_access=self.emit.emitDUPX2(frame)
                    # return temp,typeLeft
                    # rightCode+=temp_access

            if (type(typeLeft) is FloatType and type(typeRight) is IntType) :
                # self.emit.printout(rightCode+self.emit.emitI2F(frame)+leftCode)
                rightCode+=self.emit.emitI2F(frame)+temp_access+leftCode
            elif type(typeLeft) in [ArrayType,ArrayPointerType]:
                temp=""
                if type(typeLeft.eleType) is FloatType and type(typeRight) is IntType:
                    temp=self.emit.emitI2F(frame)
                if type(typeLeft.eleType) is FloatType and type(typeRight) in [ArrayType,ArrayPointerType]:
                    if type(typeRight.eleType) is IntType:
                        temp=self.emit.emitI2F(frame)
                
                if type(ast.left) is ArrayCell:
                    # if self.check_is_assign(ast.right):
                    #     self.emit.printout(leftCode)
                    # else:

                    # self.emit.printout(leftCode+rightCode+temp)
                    self.emit.printout(leftCode)

                    rightCode+=temp+temp_access
                    leftCode, typeLeft= self.visit(ast.left,Access(frame,nenv,True,False))
                    # self.emit.printout(leftCode)
                    rightCode+=leftCode
                else: #visit if lhs is array type
                    # self.emit.printout(rightCode+temp)
                    rightCode+=temp+temp_access
                    frame.push()
                    leftCode, typeLeft= self.visit(ast.left,Access(frame,nenv,True,True))
                    # self.emit.printout(leftCode)
                    rightCode+=leftCode
            else:
                # self.emit.printout(rightCode+leftCode)
                rightCode+=temp_access+ leftCode
            
            return rightCode,typeLeft
            # return None
        else:   # visit others
            leftCode, typeLeft=self.visit(ast.left,Access(frame,nenv,False,True))
            rightCode, typeRight=self.visit(ast.right,Access(frame,nenv,False,True))
            #visit second times for array cell
            if type(typeLeft) in [ArrayType,ArrayPointerType]:
                leftCode1, typeLeft1=self.visit(ast.left,Access(frame,nenv,False,False))
                leftCode+=leftCode1
                typeLeft=typeLeft.eleType
            if type(typeRight) in [ArrayType,ArrayPointerType]:
                rightCode1, typeRight1=self.visit(ast.right,Access(frame,nenv,False,False))
                rightCode+=rightCode1
                typeRight=typeRight.eleType
            #cast type
            if type(typeRight) is IntType and type(typeLeft) is FloatType:
                rightCode+=self.emit.emitI2F(frame)
                typeRight=FloatType()
            elif type(typeRight) is FloatType and type(typeLeft) is IntType:
                leftCode+=self.emit.emitI2F(frame)
                typeLeft=FloatType()
            
            if ast.op in ['+','-','*','/']:
                op_str=leftCode+rightCode
                op_str+=self.emit.emitADDOP(ast.op,typeLeft,frame) if ast.op in ['+','-'] else self.emit.emitMULOP(ast.op,typeLeft,frame)
                res_type=typeLeft
            elif ast.op == '%':
                op_str=leftCode+rightCode
                op_str += self.emit.emitMOD(frame)    
                res_type=IntType()
            elif ast.op in ['||','&&']:
                op_str=leftCode+rightCode
                op_str+= self.emit.emitANDOP(frame) if ast.op=='&&' else self.emit.emitOROP(frame)
                res_type=BoolType()
            elif ast.op in ['==','!=','>','<','>=','<=']:
                temp_new=self.emit.emitREOP(ast.op,typeLeft,frame)
                op_str=leftCode+rightCode+temp_new
                res_type=BoolType()

            return op_str,res_type

    def visitUnaryOp(self,ast,o):
        if type(o) is SubBody:
            expCode, expType=self.visit(ast.body, Access(o.frame,o.sym,False,True))
            if ast.op=='!':
                op_str=expCode+self.emit.emitNOT(expType,o.frame)
            elif ast.op=='-':
                op_str=expCode+self.emit.emitNEGOP(expType,o.frame)
            return op_str,expType
        #type o is Access and visit the first times
        if o.isLeft==False and o.isFirst==True:
            expCode, expType=self.visit(ast.body, Access(o.frame,o.sym,False,True))
            if type(expType) in [ArrayType,ArrayPointerType]: #array cell is return immediately, to the second visit, load data done, after visit op
                return expCode,expType
            else:
                if ast.op=='!':
                    op_str=expCode+self.emit.emitNOT(expType,o.frame)
                elif ast.op=='-':
                    op_str=expCode+self.emit.emitNEGOP(expType,o.frame)
                return op_str,expType
        else:   #visit the second times
            expCode, expType=self.visit(ast.body, Access(o.frame,o.sym,False,False))
            if ast.op=='!':
                op_str=expCode+self.emit.emitNOT(expType.eleType,o.frame)
            elif ast.op=='-':
                op_str=expCode+self.emit.emitNEGOP(expType.eleType,o.frame)
            return op_str,expType
            
    def visitId(self,ast,o):
        get_id=self.lookup(ast.name,o.sym,lambda x:x.name)
        if o.isFirst:
            if o.isLeft: #store
                if type(get_id.value) is CName:
                    return self.emit.emitPUTSTATIC(get_id.value.value+"/"+get_id.name,get_id.mtype,o.frame), get_id.mtype
                else:
                    return self.emit.emitWRITEVAR(get_id.name,get_id.mtype,get_id.value.value,o.frame) , get_id.mtype
            else:   #load
                if type(get_id.value) is CName:
                    return self.emit.emitGETSTATIC(get_id.value.value+"/"+get_id.name,get_id.mtype,o.frame), get_id.mtype
                else:
                    return self.emit.emitREADVAR(get_id.name,get_id.mtype,get_id.value.value,o.frame), get_id.mtype
        else:   #visit seconds times for array cell
            if o.isLeft: #store
                if type(get_id.mtype) is not MType:
                    get_type=get_id.mtype.eleType
                    return self.emit.emitWRITEVAR2(get_id.name,get_type,o.frame),get_id.mtype
                elif type(get_id.mtype) is MType:
                    get_type=get_id.mtype.rettype.eleType
                    return self.emit.emitWRITEVAR2(get_id.name,get_type,o.frame),get_id.mtype.rettype
            else: #load
                if type(get_id.mtype) is MType:
                    get_type=get_id.mtype.rettype.eleType
                    return self.emit.emitREADVAR2(get_id.name,get_type,o.frame),get_id.mtype.rettype
                else:
                    get_type=get_id.mtype.eleType
                    return self.emit.emitREADVAR2(get_id.name,get_type,o.frame),get_id.mtype

    def visitArrayCell(self,ast,o):
        frame=o.frame
        sym=o.sym
        
        if o.isFirst: #Lan 1 cho ca hai ben
            codeArr,typeArr=self.visit(ast.arr,Access(frame,sym,False,True))
            codeIdx,typeIdx=self.visit(ast.idx,Access(frame,sym,False,True))
            if type(ast.idx) is ArrayCell:
                codeArr1,typeArr1=self.visit(ast.idx,Access(frame,sym,False,False))
                codeIdx+=codeArr1
            
        elif o.isFirst==False and o.isLeft==True: #Ben trai va lan 2
            codeArr,typeArr=self.visit(ast.arr,Access(frame,sym,True,False))
            codeIdx=""
           
        elif o.isFirst==False and o.isLeft==False: #Ben phai va lan 2
            codeArr,typeArr=self.visit(ast.arr,Access(frame,sym,False,False))
            codeIdx=""
        
        return codeArr+codeIdx,typeArr
    
    #-----------------------------------Literals----------------------------------#
    def visitIntLiteral(self, ast, o):
        #ast: IntLiteral
        #o: Any
        ctxt = o
        frame = ctxt.frame
        return self.emit.emitPUSHICONST(ast.value, frame), IntType()

    def visitFloatLiteral(self,ast,o):
        ctxt=o
        frame=ctxt.frame
        return self.emit.emitPUSHFCONST(str(ast.value),frame), FloatType()

    def visitBooleanLiteral(self,ast,o):
        ctxt=o
        frame=ctxt.frame
        return self.emit.emitPUSHICONST(str(ast.value).lower(),frame), BoolType()

    def visitStringLiteral(self,ast,o):
        ctxt=o
        frame=ctxt.frame
        return self.emit.emitPUSHCONST(str(ast.value),StringType(),frame) , StringType()

    #---------------------------Type--------------------------------#
    def visitIntType(self, ast, o):
        return IntType()
    
    def visitFloatType(self, ast, o):
        return FloatType()
    
    def visitBoolType(self, ast, o):
        return BoolType()
    
    def visitStringType(self, ast, o):
        return StringType()
    
    def visitVoidType(self, ast, o):
        return VoidType()
    
    def visitArrayType(self, ast, o):
        return ArrayType(int(ast.dimen),self.visit(ast.eleType,o))

    def visitArrayPointerType(self,ast,o):
        return ArrayPointerType(self.visit(ast.eleType,o))
Example #2
0
class CodeGenVisitor(BaseVisitor, Utils):
    def __init__(self, astTree, env, dir_):
        #astTree: AST
        #env: List[Symbol]
        #dir_: File
        self.astTree = astTree
        self.env = env
        self.className = "MCClass"
        self.path = dir_
        self.emit = Emitter(self.path + "/" + self.className + ".j")

    def visitProgram(self, ast, c):
        #ast: Program
        #c: Any

        # Create header
        self.emit.printout(self.emit.emitPROLOG(
            self.className, "java.lang.Object"))

        # Get all decl init

        for decl in ast.decl:
            if isinstance(decl, VarDecl):
                self.emit.printout(self.emit.emitATTRIBUTE(
                    decl.variable, decl.varType))
                temp = Symbol(decl.variable, decl.varType)
                c.sym.append(temp)
            else:
                temp = Symbol(decl.name.name, MType(
                    [x.varType for x in decl.param], decl.returnType), CName(self.className))
                c.sym.append(temp)

        # Initial class method (object constructor)
        init_frame = Frame('<clinit>', VoidType())
        self.emit.printout(self.emit.emitMETHOD(
            '<clinit>', MType([], VoidType()), False, init_frame))

        # generate default constructor
        for x in ast.decl:
            if isinstance(x, VarDecl) and isinstance(x.varType, ArrayType):
                self.emit.printout(self.emit.emitPUSHICONST(
                    x.varType.dimen, init_frame))
                self.emit.printout(self.emit.emitNEWARRAY(x.varType.eleType))
                self.emit.printout(self.emit.emitPUTSTATIC(
                    self.className + '.' + x.variable, x.varType, init_frame))
        self.emit.printout(self.emit.emitLIMITSTACK(
            init_frame.getMaxOpStackSize()))
        self.emit.printout(self.emit.emitLIMITLOCAL(init_frame.getMaxIndex()))
        self.emit.printout(self.emit.emitRETURN(VoidType(), init_frame))
        

        self.emit.printout(self.emit.emitENDMETHOD(init_frame))

        #start visit others
        for decl in ast.decl:
            if isinstance(decl, FuncDecl):
                self.visit(decl, c)
        self.emit.emitEPILOG()
        return

    def visitFuncDecl(self, ast, c):
        method_header = ''
        frame = None
        if (ast.name.name == 'main'):
            frame = Frame('main', VoidType())
            method_header = self.emit.emitMETHOD('main', MType(
                [ArrayPointerType(StringType())], VoidType()), True, frame)
            c.is_main = True

        else:
            c.is_main = False
            frame = Frame(ast.name.name, ast.returnType)
            method_header = self.emit.emitMETHOD(ast.name.name, MType(
                [x.varType for x in ast.param], ast.returnType), True, frame)

        self.emit.printout(method_header)
        c.frame = frame
        c.func_param = ast.param
        c.is_body = True
        c.return_type = ast.returnType
        result = self.visit(ast.body, c)

        self.emit.printout(self.emit.emitLIMITSTACK(frame.getMaxOpStackSize()))
        self.emit.printout(self.emit.emitLIMITLOCAL(frame.getMaxIndex()))

        self.emit.printout(result)
        self.emit.printout(self.emit.emitENDMETHOD(frame))

    def visitBlock(self, ast, c):
        c = c.dup()
        frame = c.frame
        frame.enterScope(c.is_body)
        result = []
        block_header = []
        start_label = frame.getStartLabel()
        end_label = frame.getEndLabel()

        # Before enter scope, params insert/init
        if (c.is_body):
            if (c.is_main):
                t = self.emit.emitVAR(frame.getNewIndex(), 'args', ArrayPointerType(
                    StringType()), start_label, end_label, frame)
                block_header.append(t)
            else:
                for decl in c.func_param:
                    t1, t2 = self.visit(decl, c)
                    block_header.append(t1)
                    result += t2

        # Inside block
        result.append(self.emit.emitLABEL(start_label, frame))
        for stmt in ast.member:
            if isinstance(stmt, VarDecl):
                head, code = self.visit(stmt, c)
                block_header.append(head)
                result += code
            elif isinstance(stmt, Expr):
                code = self.visit(stmt, c)[0]
                result += code
                if frame.getStackSize() > 0:
                    result.append(self.emit.emitPOP(c.frame))
            else:
                c1 = c.dup()
                c1.is_body = False
                x = self.visit(stmt, c1)
                result += x
        if isinstance(c.return_type, VoidType) and (c.is_body):
            result.append(self.emit.emitRETURN(c.return_type, frame))
            
        elif isinstance(c.return_type, IntType) and (c.is_body):
            result.append(self.emit.emitPUSHICONST(1, frame))
            result.append(self.emit.emitRETURN(c.return_type, frame))
        elif isinstance(c.return_type, FloatType) and (c.is_body):
            result.append(self.emit.emitPUSHFCONST(1, frame))
            result.append(self.emit.emitRETURN(c.return_type, frame))
        elif isinstance(c.return_type, BoolType) and (c.is_body):
            result.append(self.emit.emitPUSHICONST(1, frame))
            result.append(self.emit.emitRETURN(c.return_type, frame))


        result.append(self.emit.emitLABEL(end_label, frame))

        frame.exitScope()
        return ''.join(block_header + result)

    def visitVarDecl(self, ast, c):
        frame = c.frame
        new_label = frame.getNewLabel()
        index = frame.getNewIndex()
        header = self.emit.emitVAR(
            index, ast.variable, ast.varType, new_label, frame.getEndLabel(), frame)

        result = [self.emit.emitLABEL(new_label, frame)]
        c.sym.append(Symbol(ast.variable, ast.varType, index))
        if isinstance(ast.varType, ArrayType):
            result.append(self.emit.emitPUSHICONST(str(ast.varType.dimen), frame))
            result.append(self.emit.emitNEWARRAY(ast.varType.eleType))
            result.append(self.emit.emitWRITEVAR(ast.variable, ast.varType, index, frame))
        return header, result

    def visitCallExpr(self, ast, c):
        #ast: CallExpr
        #c: Any
        frame = c.frame
        symbol = c.find(ast.method.name)
        cname = symbol.value.value
        result = []
        for p, st in zip(ast.param, symbol.mtype.partype):
            code, pt = self.visit(p, c)
            result += code
            if isinstance(pt, IntType) and isinstance(st, FloatType):
                result.append(self.emit.emitI2F(frame))
        result.append(self.emit.emitINVOKESTATIC(
            str(cname) + "/" + ast.method.name, symbol.mtype, frame))
        return result, symbol.mtype.rettype

    def visitId(self, ast, c):
        frame = c.frame
        symbol = c.find(ast.name)
        result = []
        if (symbol and symbol.value is not None):
            result.append(self.emit.emitREADVAR(
                symbol.name, symbol.mtype, symbol.value, frame))
        else:
            result.append(self.emit.emitGETSTATIC(
                self.className + '/' + ast.name, symbol.mtype, frame))
        return result, symbol.mtype

    def visitIntLiteral(self, ast, c):
        frame = c.frame
        return [self.emit.emitPUSHICONST(ast.value, frame)], IntType()

    def visitFloatLiteral(self, ast, c):
        frame = c.frame
        return [self.emit.emitPUSHFCONST(ast.value, frame)], FloatType()

    def visitBooleanLiteral(self, ast, c):
        frame = c.frame
        result = self.emit.emitPUSHCONST("true" if ast.value else "false", IntType(), c.frame)
        return [result], BoolType()

    def visitStringLiteral(self, ast, c):
        frame = c.frame
        return [self.emit.emitPUSHCONST(str(ast.value), StringType(), c.frame)], StringType()

    def visitBinaryOp(self, ast, c):
        frame = c.frame
        result = []
        retType = None
        if ast.op in ['&&', '||']:
            end_label = frame.getNewLabel()
            left, leftType = self.visit(ast.left, c)
            result += left
            result.append(self.emit.emitDUP(frame))

            if ast.op == '||':
                result.append(self.emit.emitIFTRUE(end_label, frame))
            else:
                result.append(self.emit.emitIFFALSE(end_label, frame))
 
            right, rightType = self.visit(ast.right, c)
            result += right

            if ast.op == '||':
                code = self.emit.emitOROP(frame)
            else:
                code = self.emit.emitANDOP(frame)
            result.append(code)

            result.append(self.emit.emitLABEL(end_label, frame))
            retType = BoolType()

        elif ast.op in ['==','!=']:
            left, leftType = self.visit(ast.left, c)
            right, rightType = self.visit(ast.right, c)
            result += left
            result += right
            result.append(self.emit.emitREOP(ast.op, leftType, frame))
            retType = leftType

        elif ast.op in ['>=', '<=', '>', '<']:
            left, leftType = self.visit(ast.left, c)
            right, rightType = self.visit(ast.right, c)
            if isinstance(leftType, FloatType) or isinstance(rightType, FloatType):
                if isinstance(leftType, IntType):
                    result += left
                    result.append(self.emit.emitI2F(frame))
                    result += right
                elif isinstance(rightType, IntType):
                    result += left
                    result += right
                    result.append(self.emit.emitI2F(frame))
                else:
                    result += left
                    result += right
                result.append(self.emit.emitREOPFlOAT(ast.op, FloatType(), frame))
                retType = FloatType()
            else:
                result += left
                result += right
                if isinstance(leftType, IntType):
                    result.append(self.emit.emitREOP(ast.op, leftType, frame))
                else:
                    result.append(self.emit.emitREOPFlOAT(ast.op, leftType, frame))

                retType = leftType

        elif ast.op in ['%']:
            left, leftType = self.visit(ast.left, c)
            right, rightType = self.visit(ast.right, c)
            result += left
            result += right
            result.append(self.emit.emitMOD(frame))
            retType = IntType()

        elif ast.op in ['+', '-']:
            left, leftType = self.visit(ast.left, c)
            right, rightType = self.visit(ast.right, c)
            if isinstance(leftType, FloatType) or isinstance(rightType, FloatType):
                if isinstance(leftType, IntType):
                    result += left
                    result.append(self.emit.emitI2F(frame))
                    result += right
                elif isinstance(rightType, IntType):
                    result += left
                    result += right
                    result.append(self.emit.emitI2F(frame))
                else:
                    result += left
                    result += right
                result.append(self.emit.emitADDOP(ast.op, FloatType(), frame))
                retType = FloatType()
            else:
                result += left
                result += right
                result.append(self.emit.emitADDOP(ast.op, leftType, frame))
                retType = leftType
        
        elif ast.op in ['*', '/']:
            left, leftType = self.visit(ast.left, c)
            right, rightType = self.visit(ast.right, c)

            if isinstance(leftType, FloatType) or isinstance(rightType, FloatType):
                if isinstance(leftType, IntType):
                    result += left
                    result.append(self.emit.emitI2F(frame))
                    result += right
                elif isinstance(rightType, IntType):
                    result += left
                    result += right
                    result.append(self.emit.emitI2F(frame))
                else:
                    result += left
                    result += right
                result.append(self.emit.emitMULOP(ast.op, FloatType(), frame))
                retType = FloatType()
            else:
                result += left
                result += right
                result.append(self.emit.emitMULOP(ast.op, leftType, frame))
                retType = leftType

        elif ast.op in ['=']:
            if isinstance(ast.left, Id):
                right, rightType = self.visit(ast.right, c)
                result += right
                symbol = c.find(ast.left.name)
                if isinstance(rightType, IntType) and isinstance(symbol.mtype, FloatType):
                    result.append(self.emit.emitI2F(c.frame))
                result.append(self.emit.emitDUP(c.frame))
                if symbol.value is not None:
                    result.append(self.emit.emitWRITEVAR(ast.left.name, symbol.mtype, symbol.value, c.frame))
                else:
                    result.append(self.emit.emitPUTSTATIC(self.className + '.' + symbol.name, symbol.mtype, c.frame))
                retType = symbol.mtype
            elif isinstance(ast.left, ArrayCell):
                left, left_type = self.visit(ast.left.arr, c)
                result += left
                itmp, iType = self.visit(ast.left.idx, c)   
                result += itmp
                right, right_type = self.visit(ast.right, c)
                result += right
                if isinstance(right_type, IntType) and isinstance(left_type.eleType, FloatType):
                    result.append(self.emit.emitI2F(c.frame)) 
                result.append(self.emit.emitDUPX2(c.frame))
                result.append(self.emit.emitASTORE(left_type.eleType, c.frame))

                retType = left_type.eleType

        return result, retType
        
    def visitUnaryOp(self, ast, c):
        frame = c.frame
        result, valType = self.visit(ast.body, c)
        if ast.op == '-':
            result.append(self.emit.emitNEGOP(valType, frame))
        elif ast.op == '!':
            result.append(self.emit.emitNOT(valType, frame))
            c.frame.pop()

        return result, valType

    def visitArrayCell(self, ast, c):
        result = []
        tmp, ctype = self.visit(ast.arr, c)
        result += tmp
        itmp, itype = self.visit(ast.idx, c)
        result += itmp
        result.append(self.emit.emitALOAD(ctype.eleType, c.frame))
        return result, ctype.eleType

    def visitFor(self, ast, c):
        frame = c.frame
        frame.enterLoop()
        result = []

        # Before for, do expr1 
        result += self.visit(ast.expr1, c)[0]
        if c.frame.getStackSize() > 0:
            result.append(self.emit.emitPOP(c.frame))
        # Label for next loop (condition expr)
        start_label = frame.getNewLabel()
        continue_label = str(frame.getContinueLabel())
        end_label = str(frame.getBreakLabel())

        result.append(self.emit.emitLABEL(start_label, frame))

        # Code gen for expr 2
        result += self.visit(ast.expr2,c)[0]
        result.append(self.emit.emitIFFALSE(str(end_label), frame))
        
        if isinstance(ast.loop, Expr): 
            result += self.visit(ast.loop, c)[0]
            if c.frame.getStackSize() > 0:
                result.append(self.emit.emitPOP(c.frame))
        else:
            result += self.visit(ast.loop, c)
        result.append(self.emit.emitLABEL(continue_label, frame))
        result += self.visit(ast.expr3, c)[0]
        if c.frame.getStackSize() > 0:
            result.append(self.emit.emitPOP(c.frame))
        result.append(self.emit.emitGOTO(str(start_label), frame))
        result.append(self.emit.emitLABEL(end_label, frame))
        frame.exitLoop()

        return result
    
    def visitIf(self, ast, c):
        frame = c.frame
        result = []
        result += self.visit(ast.expr, c)[0]
        end_label = frame.getNewLabel()

        if (ast.elseStmt):
            else_label = frame.getNewLabel()

            result.append(self.emit.emitIFFALSE(else_label, frame))
            if isinstance(ast.thenStmt, Expr):
                result += self.visit(ast.thenStmt, c)[0]
                if c.frame.getStackSize() > 0:
                    result.append(self.emit.emitPOP(c.frame))
            else:
                result += self.visit(ast.thenStmt, c)
            
            result.append(self.emit.emitGOTO(str(end_label), frame))
            result.append(self.emit.emitLABEL(else_label, frame))

            if (ast.elseStmt): 
                if isinstance(ast.elseStmt, Expr):
                    result += self.visit(ast.elseStmt, c)[0]
                    if c.frame.getStackSize() > 0:
                        result.append(self.emit.emitPOP(c.frame))
                else:
                    result += self.visit(ast.elseStmt, c)
        else:
            result.append(self.emit.emitIFFALSE(end_label, frame))
            if isinstance(ast.thenStmt, Expr):
                result += self.visit(ast.thenStmt, c)[0]
                if c.frame.getStackSize() > 0:
                    result.append(self.emit.emitPOP(c.frame))
            else:
                result += self.visit(ast.thenStmt, c)
        result.append(self.emit.emitLABEL(end_label, frame))
        return result
    
    def visitDowhile(self, ast, c):
        frame = c.frame
        frame.enterLoop()
        result = []
        start_label = str(frame.getContinueLabel())
        end_label = str(frame.getBreakLabel())
        c.break_label = end_label
        c.continue_label = start_label
        result.append(self.emit.emitLABEL(start_label, frame))

        for stmt in ast.sl:
            if isinstance(stmt, Expr):
                result += self.visit(stmt, c)[0]
                if c.frame.getStackSize() > 0:
                    result.append(self.emit.emitPOP(c.frame))
            else:
                result += self.visit(stmt, c)
            
        result += self.visit(ast.exp, c)[0]
        result.append(self.emit.emitIFFALSE(end_label, frame))
        result.append(self.emit.emitGOTO(str(start_label), frame))
        result.append(self.emit.emitLABEL(end_label, frame))
        frame.exitLoop()
        return result
    
    def visitBreak(self, ast, c):
        frame = c.frame
        result = []
        result.append(self.emit.emitGOTO(str(frame.getBreakLabel()), c.frame))
        return result
    
    def visitContinue(self, ast, c):
        frame = c.frame
        result = []
        result.append(self.emit.emitGOTO(str(frame.getContinueLabel()), c.frame))
        return result
    
    def visitReturn(self, ast, c):
        frame = c.frame
        return_type = c.return_type
        result = []
        if not isinstance(c.return_type, VoidType):
            code, rType = self.visit(ast.expr, c)
            result += code
            if isinstance(rType, IntType) and isinstance(return_type, FloatType):
                result.append(self.emit.emitI2F(frame))
        result.append(self.emit.emitRETURN(return_type, frame))
        return result
class CodeGenVisitor(BaseVisitor, Utils):
    def __init__(self, astTree, env, dir_):
        # astTree: AST
        # env: List[Symbol]
        # dir_: File

        self.astTree = astTree
        self.env = env
        self.className = "MCClass"
        self.path = dir_
        self.emit = Emitter(self.path + "/" + self.className + ".j")
            # not in original version
        self.listGlobalArray = [] # list(VarDecl: array declare global)


    def visitProgram(self, ast, c):
        # ast: Program
        # c: Any

        self.emit.printout(self.emit.emitPROLOG(self.className, "java.lang.Object"))

        ###################################################################################
        staticDecl = self.env
        for x in ast.decl:
            if type(x) is FuncDecl:
                partype = [i.varType for i in x.param]
                staticDecl = [Symbol(x.name.name.lower(), MType(
                    partype, x.returnType), CName(self.className))] + staticDecl
            else:
                newSym = self.visit(x, SubBody(None, None, isGlobal=True))
                staticDecl = [newSym] + staticDecl

        e = SubBody(None, staticDecl)

        # visit ast tree
        for x in ast.decl:
            if type(x) is FuncDecl:
                e = self.visit(x, e)

        ###################################################################################

        # generate default constructor
        self.genMETHOD(FuncDecl(Id("<init>"), [], None, Block([])), c, Frame("<init>", VoidType))
        self.genMETHOD(FuncDecl(Id("<clinit>"), [], None, Block([])), c, Frame("<clinit>", VoidType))
        self.emit.emitEPILOG()
        return c

    def visitVarDecl(self, ast: VarDecl, o: SubBody):
        subctxt = o
        frame = o.frame
        isGlobal = o.isGlobal
        varName = ast.variable
        varType = ast.varType
        if isGlobal:
            self.emit.printout(self.emit.emitATTRIBUTE(varName, StupidUtils.retrieveType(varType), False, ""))
            if type(ast.varType) is ArrayType: 
                self.listGlobalArray.append(ast)
            return Symbol(varName, varType)
        # params
        idx = frame.getNewIndex()
        self.emit.printout(self.emit.emitVAR(idx, varName, StupidUtils.retrieveType(varType), frame.getStartLabel(), frame.getEndLabel(), frame))
        return SubBody(frame, [Symbol(varName, varType, Index(idx))] + subctxt.sym)

    def genMETHOD(self, consdecl: FuncDecl, o, frame: Frame):
        # o: Any

        glenv = o

        methodName = consdecl.name.name

        isInit = consdecl.returnType is None and methodName == "<init>"
        isClassInit = consdecl.returnType is None and methodName == "<clinit>"
        isMain = consdecl.name.name == "main" and len(consdecl.param) == 0 and type(consdecl.returnType) is VoidType
        returnType = VoidType() if isInit or isClassInit else consdecl.returnType
        methodName = "<init>" if isInit else consdecl.name.name
        intype = [ArrayPointerType(StringType())] if isMain else [StupidUtils.retrieveType(x.varType) for x in consdecl.param]
        mtype = MType(intype, returnType)

        self.emit.printout(self.emit.emitMETHOD(methodName, mtype, not isInit, frame))

        frame.enterScope(True)

        # glenv = o

        # Generate code for parameter declarations
        if isInit:
            self.emit.printout(self.emit.emitVAR(frame.getNewIndex(), "this", ClassType(
                self.className), frame.getStartLabel(), frame.getEndLabel(), frame))
        if isMain:
            self.emit.printout(self.emit.emitVAR(frame.getNewIndex(), "args", ArrayPointerType(
                StringType()), frame.getStartLabel(), frame.getEndLabel(), frame))

        # listParamArray = [] # list(Symbol(name, mtype, value: Index(idx)))
        # listLocalArray = [] # list(Symbol(name, mtype, value: Index(idx)))
        paramList = SubBody(frame, glenv)

        for x in consdecl.param:
            paramList = self.visit(x, paramList)
            if type(x.varType) is ArrayType:
                listParamArray.append(paramList.sym[0])

        self.emit.printout(self.emit.emitLABEL(frame.getStartLabel(), frame))

        # Generate code for statements
        if isInit:
            self.emit.printout(self.emit.emitREADVAR("this", ClassType(self.className), 0, frame))
            self.emit.printout(self.emit.emitINVOKESPECIAL(frame))

        # # Init global array declare
        # if isClassInit:
        #     for x in self.listGlobalArray:
        #         size = x.varType.upper - x.varType.lower + 1
        #         self.emit.printout(self.emit.emitInitNewStaticArray(self.className + "/" + x.variable.name, size, x.varType.eleType, frame))

        # # Init local array declare
        # for sym in listLocalArray:
        #     index = sym.value.value
        #     varType = sym.mtype
        #     size = varType.upper - varType.lower + 1
        #     self.emit.printout(self.emit.emitInitNewLocalArray(index, size, varType.eleType, frame))

        # # Clone params array
        # for sym in listParamArray:
        #     index = sym.value.value
        #     eleType = sym.mtype.eleType
        #     self.emit.printout(self.emit.emitCloneArray(index, eleType, frame))

        # for x in o:
        #     print(x)

        # visit block member
        # list(map(lambda x: self.visit(x, SubBody(frame, paramList.sym)), consdecl.body.member))
        self.visit(consdecl.body, SubBody(frame, paramList.sym))

        self.emit.printout(self.emit.emitLABEL(frame.getEndLabel(), frame))
        if type(returnType) is VoidType:
            self.emit.printout(self.emit.emitRETURN(VoidType(), frame))
        self.emit.printout(self.emit.emitENDMETHOD(frame))
        frame.exitScope()

    def visitBlock(self, ast: Block, o: SubBody):
        ctxt = o
        frame = ctxt.frame
        symbols = ctxt.sym

        listLocalArray = [] # list(Symbol(name, mtype, value: Index(idx)))

        hasReturnStmt = False
        for x in ast.member:
            if type(x) is VarDecl:
                # var decl
                ctxt = self.visit(x, ctxt)
                symbols = ctxt.sym
                # handle array variable
                if type(x.varType) is ArrayType:
                    index = ctxt.sym[0].value.value
                    varType = ctxt.sym[0].mtype
                    size = varType.dimen
                    self.emit.printout(self.emit.emitInitNewLocalArray(index, size, varType.eleType, frame))
            else:
                # statement
                e = SubBody(frame, symbols)
                if self.visit(x, e) == True:
                    hasReturnStmt = True
        return hasReturnStmt

    def visitCallExpr(self, ast: CallExpr, o):
        # call statement
        if type(o) is SubBody:
            ctxt = o
            frame = ctxt.frame
            nenv = ctxt.sym
            sym = self.lookup(ast.method.name, nenv, lambda x: x.name)
            cname = sym.value.value

            ctype = sym.mtype
            paramType = ctype.partype

            paramsCode = ""
            idx = 0
            for x in ast.param:
                pCode, pType = self.visit(x, Access(frame, nenv, False, True))
                if type(paramType[idx]) is FloatType and type(pType) is IntType:
                    pCode = pCode + self.emit.emitI2F(frame)
                if type(paramType[idx]) is ArrayType:
                    pass
                paramsCode = paramsCode + pCode
                idx = idx + 1
        
            code = paramsCode + self.emit.emitINVOKESTATIC(cname + "/" + sym.name, ctype, frame) 
            self.emit.printout(code)

            # idx = 0
            # in_ = ("", [])
            # for x in ast.param:
            #     pCode, pType = self.visit(x, Access(frame, nenv, False, True))
            #     if type(paramType[idx]) is FloatType and type(pType) is IntType:
            #         pCode = pCode + self.emit.emitI2F(frame)
            #     in_ = (in_[0] + pCode, in_[1].append(pType))
            #     idx = idx + 1

            # code = in_[0] + self.emit.emitINVOKESTATIC(cname + "/" + ast.method.name, ctype, frame)
            # self.emit.printout(code)
        else:
            # call expression
            ctxt = o
            frame = ctxt.frame
            nenv = ctxt.sym
            sym = self.lookup(ast.method.name, nenv, lambda x: x.name)
            cname = sym.value.value

            ctype = sym.mtype
            paramType = ctype.partype


            paramsCode = ""
            idx = 0
            for x in ast.param:
                pCode, pType = self.visit(x, Access(frame, nenv, False, True))
                if type(paramType[idx]) is FloatType and type(pType) is IntType:
                    pCode = pCode + self.emit.emitI2F(frame)
                if type(paramType[idx]) is ArrayType:
                    pass
                paramsCode = paramsCode + pCode
                idx = idx + 1
        
            code = paramsCode + self.emit.emitINVOKESTATIC(cname + "/" + sym.name, ctype, frame) 
            return code, ctype.rettype

            # idx = 0
            # in_ = ("", [])
            # for x in ast.param:
            #     print(len(ast.param))
            #     pCode, pType = self.visit(x, Access(frame, nenv, False, True))
            #     if type(paramType[idx]) is FloatType and type(pType) is IntType:
            #         pCode = pCode + self.emit.emitI2F(frame)
            #     in_ = (in_[0] + pCode, in_[1].append(pType))
            #     idx = idx + 1

            # code = in_[0] + self.emit.emitINVOKESTATIC(cname + "/" + ast.method.name, ctype, frame)
            # return code, ctype.rettype

    def visitIf(self, ast: If, o: SubBody):
        # ctxt = o
        # frame = ctxt.frame
        # nenv = ctxt.sym
        # # [print(x) for x in nenv]
        # expCode, expType = self.visit(ast.expr, Access(frame, nenv, False, True))
        # self.emit.printout(expCode)

        # labelT = frame.getNewLabel() # eval is true
        # labelE = frame.getNewLabel() # label end

        # self.emit.printout(self.emit.emitIFTRUE(labelT, frame)) # false
        # # False
        # self.visit(ast.elseStmt, ctxt)
        # self.emit.printout(self.emit.emitGOTO(labelE, frame)) # go to end
        # # True
        # self.emit.printout(self.emit.emitLABEL(labelT, frame))
        # self.visit(ast.thenStmt, ctxt)
        # # End
        # self.emit.printout(self.emit.emitLABEL(labelE, frame))
        # return False

        ######################################################################
        ctxt = o
        frame = ctxt.frame
        nenv = ctxt.sym
        expCode, expType = self.visit(ast.expr, Access(frame, nenv, False, True))
        self.emit.printout(expCode)
        if ast.elseStmt:
            labelT = frame.getNewLabel() # eval is true
            labelE = frame.getNewLabel() # label end

            self.emit.printout(self.emit.emitIFTRUE(labelT, frame)) # false
            # False
            hasReturnStmt = True in [self.visit(ast.elseStmt, ctxt)]
            if not hasReturnStmt:
                    self.emit.printout(self.emit.emitGOTO(labelE, frame)) # go to end
            # True
            self.emit.printout(self.emit.emitLABEL(labelT, frame))
            hasReturnStmt = True in [self.visit(ast.thenStmt, ctxt)] and hasReturnStmt
            # End
            self.emit.printout(self.emit.emitLABEL(labelE, frame))
            return hasReturnStmt
        else:
            labelT = frame.getNewLabel() # eval is true
            labelE = frame.getNewLabel() # label end

            self.emit.printout(self.emit.emitIFTRUE(labelT, frame)) # false
            self.emit.printout(self.emit.emitGOTO(labelE, frame)) # go to end
            # True
            self.emit.printout(self.emit.emitLABEL(labelT, frame))
            hasReturnStmt = True in [self.visit(ast.thenStmt, ctxt)] and hasReturnStmt
            # End
            self.emit.printout(self.emit.emitLABEL(labelE, frame))
            return hasReturnStmt

    def visitDowhile(self, ast: Dowhile, o: SubBody):
        ctxt = o
        frame = ctxt.frame
        nenv = ctxt.sym
        expCode, expType = self.visit(ast.exp, Access(frame, nenv, False, True))
        
        # do statement once before go in loop
        frame.enterLoop()
        [self.visit(x, o) for x in ast.sl]
        self.emit.printout(self.emit.emitLABEL(frame.getContinueLabel(), frame))
        self.emit.printout(self.emit.emitLABEL(frame.getBreakLabel(), frame))
        frame.exitLoop()

        labelS = frame.getNewLabel() # label start
        labelE = frame.getNewLabel() # label end
        # enter loop
        frame.enterLoop()
        self.emit.printout(self.emit.emitLABEL(labelS, frame))
        self.emit.printout(expCode)
        self.emit.printout(self.emit.emitIFFALSE(labelE, frame))
        hasReturnStmt = True in [self.visit(x, o) for x in ast.sl]
        self.emit.printout(self.emit.emitLABEL(frame.getContinueLabel(), frame))
        if not hasReturnStmt:
            self.emit.printout(self.emit.emitGOTO(labelS, frame)) # loop
        self.emit.printout(self.emit.emitLABEL(labelE, frame))
        self.emit.printout(self.emit.emitLABEL(frame.getBreakLabel(), frame))
        frame.exitLoop()

        # def visitBreak(self, ast: Break, o: SubBody):
        # ctxt = o
        # frame = ctxt.frame
        # return self.emit.printout(self.emit.emitGOTO(frame.getBreakLabel(), frame))


    def visitFor(self, ast: For, o: SubBody):
        ctxt = o
        frame = ctxt.frame
        nenv = ctxt.sym

        exp2Code, _ = self.visit(ast.expr2, Access(frame, nenv, False, True))
        
        labelS = frame.getNewLabel() # label start
        labelE = frame.getNewLabel() # label end

        # Init value
        self.visit(ast.expr1, SubBody(frame, nenv))
        frame.enterLoop()
        # Loop
        self.emit.printout(self.emit.emitLABEL(labelS, frame))
        # 1. Condition
        self.emit.printout(exp2Code)
        self.emit.printout(self.emit.emitIFFALSE(labelE, frame))
        # 2. Statements
        hasReturnStmt = True in [self.visit(ast.loop, ctxt)]
        self.emit.printout(self.emit.emitLABEL(frame.getContinueLabel(), frame))
        # 3. Update index
        self.visit(ast.expr3, SubBody(frame, nenv))

        if not hasReturnStmt:
            self.emit.printout(self.emit.emitGOTO(labelS, frame)) # loop
        self.emit.printout(self.emit.emitLABEL(labelE, frame))
        self.emit.printout(self.emit.emitLABEL(frame.getBreakLabel(), frame))
        frame.exitLoop()

    def visitReturn(self, ast: Return, o: SubBody):

        ctxt = o
        frame = ctxt.frame
        nenv = ctxt.sym
        retType = frame.returnType

        if not type(retType) is VoidType:
            expCode, expType = self.visit(ast.expr, Access(frame, nenv, False, True))
            if type(retType) is FloatType and type(expType) is IntType:
                expCode = expCode + self.emit.emitI2F(frame)
            self.emit.printout(expCode)
        self.emit.printout(self.emit.emitRETURN(retType, frame))
        return True

    def visitBreak(self, ast: Break, o: SubBody):
        ctxt = o
        frame = ctxt.frame
        return self.emit.printout(self.emit.emitGOTO(frame.getBreakLabel(), frame))

    def visitContinue(self, ast: Continue, o: SubBody):
        ctxt = o
        frame = ctxt.frame
        return self.emit.printout(self.emit.emitGOTO(frame.getContinueLabel(), frame))

    #################### Expression ######################
    def visitId(self, ast: Id, o: Access):
        # Return (name, type, index)
        ctxt = o
        frame = ctxt.frame
        symbols = ctxt.sym
        isLeft = ctxt.isLeft
        isFirst = ctxt.isFirst


        if isLeft and ctxt.checkArrayType:
            return False, None

        sym = self.lookup(ast.name, symbols, lambda x: x.name)

        # recover status of stack in frame
        if not isFirst and isLeft: 
            frame.push()
        elif not isFirst and not isLeft: 
            frame.pop()

        isArrayType = type(sym.mtype) is ArrayType
        emitType = StupidUtils.retrieveType(sym.mtype)
        if sym.value is None: # not index -> global var - static field
            if isLeft and not isArrayType:
                retCode = self.emit.emitPUTSTATIC(self.className + "/" + sym.name, emitType, frame)
            else:
                retCode = self.emit.emitGETSTATIC(self.className + "/" + sym.name, emitType, frame)
        else:
            if isLeft and not isArrayType:
                retCode = self.emit.emitWRITEVAR(sym.name, emitType, sym.value.value, frame)
            else:
                retCode = self.emit.emitREADVAR(sym.name, emitType, sym.value.value, frame)

        return retCode, sym.mtype

    
    def visitArrayCell(self, ast: ArrayCell, o: Access):
        ctxt = o
        frame = ctxt.frame
        symbols = ctxt.sym
        isLeft = ctxt.isLeft
        isFirst = ctxt.isFirst

        if isLeft and ctxt.checkArrayType: return True, None

        arrCode, arrType = self.visit(ast.arr, Access(frame, symbols, True, True))
        idxCode, idxType = self.visit(ast.idx, Access(frame, symbols, False, True))
        # update index jvm, i.e [3..5] -> [0..2], access [4] -> [1]
        # idxCode = idxCode + self.emit.emitPUSHICONST(arrType.lower, frame) + self.emit.emitADDOP('-', IntType(), frame)
        # Steps: aload(address index) -> iconst(access index) -> iaload
        if isLeft:
            return [arrCode + idxCode, self.emit.emitASTORE(arrType.eleType, frame)], arrType.eleType
        return arrCode + idxCode + self.emit.emitALOAD(arrType.eleType, frame), arrType.eleType
        

    def visitBinaryOp(self, ast: BinaryOp, o):
        ctxt = o
        frame = ctxt.frame
        nenv = ctxt.sym
        op = str(ast.op)
            
        if StupidUtils.isOpForNumber(op): # for number type
            lCode, lType = self.visit(ast.left, ctxt)
            rCode, rType = self.visit(ast.right, ctxt)
            mType = StupidUtils.mergeNumberType(lType, rType)
            # if op == '/': 
            #     mType = FloatType() # mergeType >= lType, rType
            if type(lType) is IntType and type(mType) != type(lType):
                lCode = lCode + self.emit.emitI2F(frame)
            if type(rType) is IntType and type(mType) != type(rType):
                rCode = rCode + self.emit.emitI2F(frame)
            if StupidUtils.isOpForNumberToNumber(op):
                if op in ['+', '-']:
                    return lCode + rCode + self.emit.emitADDOP(op, mType, frame), mType
                if op in ['*', '/']:
                    return lCode + rCode + self.emit.emitMULOP(op, mType, frame), mType
                if op == '%':
                    return lCode + rCode + self.emit.emitMOD(frame), mType
            else: # op to boolean: > <= == !=, ...
                return lCode + rCode + self.emit.emitREOP(op, mType, frame), BoolType()
        elif op == '=':
            if type(o) is Access:
                # print("access")
                isArray, _ = self.visit(ast.left, Access(frame, nenv, True, True, True))

                if not isArray:
                    expCode, expType = self.visit(ast.right, Access(frame, nenv, False, True))
                    lhsCode, lhsType = self.visit(ast.left, Access(frame, nenv, True, True))

                    if type(lhsType) is FloatType and type(expType) is IntType:
                        expCode = expCode + self.emit.emitI2F(frame)
                    self.emit.printout(expCode + self.emit.emitDUP(frame) + lhsCode)
                    return "",expType
                else:
                    for i in range(0, 2): frame.push()

                    # handle array
                    if o.getLeft == False:
                        # print("right")
                        expCode, expType = self.visit(ast.right, Access(frame, nenv, False, True, False, False))
                        lhsCode, lhsType = self.visit(ast.left, Access(frame, nenv, True, True))

                        if type(lhsType) is FloatType and type(expType) is IntType:
                            expCode = expCode + self.emit.emitI2F(frame)
                        self.emit.printout(lhsCode[0])
                        for i in range(0, 2): frame.push()
                        return expCode, expType
                    elif o.getLeft == True:
                        # print("left")
                        expCode, expType = self.visit(ast.right, Access(frame, nenv, False, True, False, True))
                        lhsCode, lhsType = self.visit(ast.left, Access(frame, nenv, True, True))

                        if type(lhsType) is FloatType and type(expType) is IntType:
                            expCode = expCode + self.emit.emitI2F(frame)
                        self.emit.printout(self.emit.emitDUPX2(frame) + lhsCode[1])
                        for i in range(0, 2): frame.push()
                        return None, None
            else:
                # print("subbody")
                isArray, _ = self.visit(ast.left, Access(frame, nenv, True, True, True))

                if isArray:
                    # handle array
                    for i in range(0, 2): frame.push()

                    lhsCode, lhsType = self.visit(ast.left, Access(frame, nenv, True, True))
                    expCode, expType = self.visit(ast.right, Access(frame, nenv, False, True))
                    self.emit.printout(lhsCode[0] + expCode)
                    _, _ = self.visit(ast.right, Access(frame, nenv, False, True, False, True))
                    self.emit.printout(lhsCode[1])
                    
                    for i in range(0, 2): frame.pop()
                else:
                    # push 1 slot for duplicate value
                    frame.push()
                    expCode, expType = self.visit(ast.right, Access(frame, nenv, False, True))
                    lhsCode, lhsType = self.visit(ast.left, Access(frame, nenv, True, True))

                    if type(lhsType) is FloatType and type(expType) is IntType:
                        expCode = expCode + self.emit.emitI2F(frame)

                    self.emit.printout(expCode + lhsCode)
                    # recover status of stack
                    frame.pop()

                ###################################################
                # frame.push()
                # expCode, expType = self.visit(ast.right, Access(frame, nenv, False, True))
                # lhsCode, lhsType = self.visit(ast.left, Access(frame, nenv, True, True))

                # if type(lhsType) is FloatType and type(expType) is IntType:
                #     expCode = expCode + self.emit.emitI2F(frame)
                # if not isArray:
                #     self.emit.printout(expCode + lhsCode)
                #     frame.pop()
                # else:
                #     self.emit.printout(lhsCode[0] + expCode + lhsCode[1])
                #     [frame.pop() for i in range(0,2)]
            ############################################################################
            # isArray, _ = self.visit(ast.left, Access(frame, nenv, True, True, True))

            # if isArray:
            #     for i in range(0, 2):
            #         frame.push()

            # expCode, expType = self.visit(ast.right, Access(frame, nenv, False, True))
            # lhsCode, lhsType = self.visit(ast.left, Access(frame, nenv, True, True))

            # if type(lhsType) is FloatType and type(expType) is IntType:
            #     expCode = expCode + self.emit.emitI2F(frame)
            # if not isArray:
            #     self.emit.printout(expCode + lhsCode)
            # else:
            #     self.emit.printout(lhsCode[0] + expCode + lhsCode[1])
            #     # recover stack status
            #     [frame.pop() for i in range(0,2)]
            # return expCode + lhsCode, None

        else: # for boolean type
            lCode, lType = self.visit(ast.left, ctxt)
            rCode, rType = self.visit(ast.right, ctxt)
            mType = BoolType()
            if op == '||': return lCode + rCode + self.emit.emitOROP(frame), mType
            if op == '&&': return lCode + rCode + self.emit.emitANDOP(frame), mType


    def visitUnaryOp(self, ast: UnaryOp, o: Access):
        ctxt = o
        frame = ctxt.frame
        op = ast.op
        bodyCode, bodyType = self.visit(ast.body, ctxt)
        if op == '-':
            return bodyCode + self.emit.emitNEGOP(bodyType, frame), bodyType
        if op == '!':
            return bodyCode + self.emit.emitNOT(bodyType, frame), bodyType

    ###################### Literal ##########################
    def visitIntLiteral(self, ast: IntLiteral, o: Access):
        ctxt = o
        frame = ctxt.frame
        return self.emit.emitPUSHICONST(ast.value, frame), IntType()

    def visitFloatLiteral(self, ast: FloatLiteral, o: Access):
        ctxt = o
        frame = ctxt.frame
        return self.emit.emitPUSHFCONST(str(ast.value), frame), FloatType()

    def visitBooleanLiteral(self, ast: BooleanLiteral, o: Access):
        ctxt = o
        frame = ctxt.frame
        return self.emit.emitPUSHICONST(str(ast.value).lower(), frame), BoolType()

    def visitStringLiteral(self, ast: StringLiteral, o: Access):
        ctxt = o
        frame = ctxt.frame
        return self.emit.emitPUSHCONST(ast.value, StringType(), frame), StringType()