Example #1
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")
        self.curFunc = Symbol("null", MType([], VoidType()),
                              CName(self.className))

    def VarGlobal(self, ast, c):
        ctxt = c
        nameAtt = ast.variable
        typeAtt = ast.varType
        self.emit.printout(self.emit.emitATTRIBUTE(nameAtt, typeAtt, False,
                                                   ""))
        symbol = Symbol(ast.variable, typeAtt, CName(self.className))
        c.append(symbol)
        return c

    def FuncGlobal(self, ast, c):
        ctxt = c
        nameFunc = ast.name.name
        typeFunc = MType([x.varType for x in ast.param], ast.returnType)
        symbol = Symbol(nameFunc, typeFunc, CName(self.className))
        c.append(symbol)
        return c

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

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

        lsVar = list(filter(lambda x: type(x) is VarDecl, ast.decl))
        lsArrayVar = list(filter(lambda x: type(x.varType) is ArrayType,
                                 lsVar))
        lsFun = list(filter(lambda x: type(x) is FuncDecl, ast.decl))

        # printout static field and add them to self.env
        functools.reduce(lambda x,y: self.VarGlobal(y,x) if type(y) is VarDecl else self.FuncGlobal(y,x), \
                ast.decl, self.env if self.env else [])

        # visit func decl
        functools.reduce(lambda a, b: self.visit(b, a), lsFun,
                         SubBody(None, self.env))

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

    def visitVarDecl(self, ast, c):
        #ast: VarDecl
        #c  : SubBody
        env = c.sym if type(c) is SubBody else []
        indx = c.frame.getNewIndex()
        self.emit.printout(
            self.emit.emitVAR(indx, ast.variable, ast.varType,
                              c.frame.getStartLabel(), c.frame.getEndLabel(),
                              c.frame))
        return SubBody(c.frame,
                       [Symbol(ast.variable, ast.varType, Index(indx))] + env)

    def arrayTypeDecl(self, ast, c):
        #ast : VarDecl
        #c   : any

        # coi lai
        index = (self.lookup(ast.variable, c.sym,
                             lambda x: x.name)).value.value
        self.emit.printout(self.emit.emitNEWARRAY(ast.varType, c.frame))
        self.emit.printout(
            self.emit.emitWRITEVAR(ast.variable, ast.varType, index, c.frame))
        return SubBody(c.frame, c.sym)

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

        isInit = consdecl.returnType is None
        isMain = consdecl.name.name == "main" and len(
            consdecl.param) == 0 and type(consdecl.returnType) is VoidType
        returnType = VoidType() if isInit else consdecl.returnType
        methodName = "<init>" if isInit else consdecl.name.name
        intype = [ArrayPointerType(StringType())
                  ] if isMain else [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))

        glSubBody = SubBody(frame, glenv)
        if (isMain is False) and (intype != []):
            glSubBody = functools.reduce(lambda a, b: self.visit(b, a),
                                         consdecl.param, SubBody(frame, glenv))

        body = consdecl.body
        lstdecl = list(filter(lambda x: type(x) is VarDecl, body.member))
        curenv = functools.reduce(lambda a, b: self.visit(b, a), lstdecl,
                                  glSubBody)

        lsArrVarDecl = list(
            filter(lambda x: type(x.varType) is ArrayType, lstdecl))
        self.emit.printout(self.emit.emitLABEL(frame.getStartLabel(), frame))

        for x in lsArrVarDecl:
            self.arrayTypeDecl(x, curenv)

        # 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))
        lststmt = list(filter(lambda x: not type(x) is VarDecl, body.member))
        returnStmt = list(filter(lambda x: type(x) is Return, lststmt))

        list(map(lambda x: self.printoutStmt(x, curenv), lststmt))

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

    def printoutStmt(self, ast, env):
        #env    : SubBody
        frame = env.frame
        newEnv = env.sym
        if type(ast) is BinaryOp:
            if ast.op == "=":
                self.emit.printout(
                    self.visit(ast, Access(frame, newEnv, True, True,
                                           False))[0])
            else:
                self.emit.printout(
                    self.visit(ast, Access(frame, newEnv, False, True,
                                           True))[0])
                self.emit.printout(self.emit.emitPOP(frame))

        elif type(ast) is CallExpr:
            self.emit.printout(
                self.visit(ast, Access(frame, newEnv, False, True, True))[0])
            sym = self.lookup(ast.method.name, newEnv, lambda x: x.name)
            returnType = sym.mtype.rettype

            if type(returnType) != VoidType:
                self.emit.printout(self.emit.emitPOP(frame))

        elif (type(ast) is UnaryOp) or (type(ast) is Id) or (type(ast) is ArrayCell) or (type(ast) is IntLiteral) or \
                (type(ast) is FloatLiteral) or (type(ast) is StringLiteral) or (type(ast) is BooleanLiteral):

            self.emit.printout(
                self.visit(ast, Access(frame, newEnv, False, True, True))[0])
            self.emit.printout(self.emit.emitPOP(frame))

        elif (type(ast) is Block) or (type(ast) is If) or (type(ast) is For) or (type(ast) is Break) or \
                (type(ast) is Continue) or (type(ast) is Return) or (type(ast) is Dowhile):
            # NOTE env hay new env vay cha noi
            self.visit(ast, env)  #vi la stmt nen truyen vao SubBody
        else:
            pass

    def visitFuncDecl(self, ast, o):
        #ast: FuncDecl

        subctxt = o
        frame = Frame(ast.name.name, ast.returnType)
        self.curFunc = self.lookup(ast.name.name, subctxt.sym,
                                   lambda x: x.name)
        self.genMETHOD(ast, subctxt.sym, frame)

        return o
        #return SubBody(None, [Symbol(ast.name, MType(list(), ast.returnType), CName(self.className))] + subctxt.sym)

    def visitBlock(self, ast, c):
        #c  : SubBody
        ctxt = c
        frame = ctxt.frame
        newEnv = ctxt.sym

        frame.enterScope(False)
        #varEnv = ast.decl.foldLeft(SubBody(frame, newEnv))((x, e) => visit(e, x).asInstanceOf[SubBody])
        lstdecl = list(filter(lambda x: type(x) is VarDecl, ast.member))
        varEnv = functools.reduce(lambda a, b: self.visit(b, a), lstdecl,
                                  SubBody(frame, newEnv))

        # list Vardecl
        listVarDecl = ast.member
        listArrayVarDecl = filter(lambda x: type(x) is ArrayType, listVarDecl)

        self.emit.printout(self.emit.emitLABEL(frame.getStartLabel(), frame))
        list(map(lambda x: self.arrayTypeDecl(x, varEnv), listArrayVarDecl))
        lststmt = list(filter(lambda x: not type(x) is VarDecl, ast.member))
        list(map(lambda x: self.printoutStmt(x, varEnv), lststmt))
        self.emit.printout(self.emit.emitLABEL(frame.getEndLabel(), frame))
        frame.exitScope()
        return c

    def visitIf(self, ast, c):
        #c  : SubBody
        frame = c.frame
        env = c.sym
        (resExpr,
         typeExpr) = ast.expr.accept(self, Access(frame, env, False, True,
                                                  True))
        falseLabel = frame.getNewLabel()

        self.emit.printout(resExpr + self.emit.emitIFFALSE(falseLabel, frame))
        self.printoutStmt(ast.thenStmt, c)
        if not ast.elseStmt:
            self.emit.printout(self.emit.emitLABEL(falseLabel, frame))
        else:
            trueLabel = frame.getNewLabel()
            self.emit.printout(
                self.emit.emitGOTO(trueLabel, frame) +
                self.emit.emitLABEL(falseLabel, frame))
            self.printoutStmt(ast.elseStmt, c)
            self.emit.printout(self.emit.emitLABEL(trueLabel, frame))

    def visitFor(self, ast, c):
        #c  : SubBody
        frame = c.frame
        env = c.sym
        beginLabel = frame.getNewLabel()
        frame.enterLoop()

        self.printoutStmt(ast.expr1, SubBody(frame, env))
        self.emit.printout(self.emit.emitLABEL(beginLabel, frame))
        (resExpr2,
         typeExpr2) = ast.expr2.accept(self,
                                       Access(frame, env, False, True, False))
        self.emit.printout(resExpr2)

        self.emit.printout(self.emit.emitIFFALSE(frame.getBreakLabel(), frame))
        self.printoutStmt(ast.loop, c)

        self.emit.printout(self.emit.emitLABEL(frame.getContinueLabel(),
                                               frame))
        self.printoutStmt(ast.expr3, SubBody(frame, env))

        self.emit.printout(self.emit.emitGOTO(beginLabel, frame))
        self.emit.printout(self.emit.emitLABEL(frame.getBreakLabel(), frame))

        frame.exitLoop()

    def visitDowhile(self, ast, c):
        #c  : SubBody
        frame = c.frame
        env = c.sym
        beginLabel = frame.getNewLabel()
        frame.enterLoop()

        self.emit.printout(self.emit.emitLABEL(beginLabel, frame))
        list(map(lambda x: self.printoutStmt(x, c), ast.sl))

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

        (resExpr, typeExpr) = self.visit(ast.exp,
                                         Access(frame, env, False, True, True))

        self.emit.printout(resExpr)
        self.emit.printout(self.emit.emitIFFALSE(frame.getBreakLabel(), frame))
        self.emit.printout(self.emit.emitGOTO(beginLabel, frame))
        self.emit.printout(self.emit.emitLABEL(frame.getBreakLabel(), frame))
        frame.exitLoop()

    def visitBreak(self, ast, c):
        self.emit.printout(self.emit.emitGOTO(c.frame.getBreakLabel(),
                                              c.frame))

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

    def visitReturn(self, ast, c):
        if ast.expr:
            (resExpr,
             resType) = self.visit(ast.expr,
                                   Access(c.frame, c.sym, False, True, True))
            typeFunc = self.curFunc.mtype.rettype
            if type(typeFunc) == FloatType and type(resType) == IntType:
                self.emit.printout(resExpr + self.emit.emitI2F(c.frame) +
                                   self.emit.emitRETURN(FloatType(), c.frame))
            else:
                self.emit.printout(resExpr +
                                   self.emit.emitRETURN(resType, c.frame))
        else:
            self.emit.printout(self.emit.emitRETURN(VoidType(), c.frame))

    def visitBinaryOp(self, ast, c):
        #c  : Access (co the la SubBody)
        ctxt = c
        frame = ctxt.frame
        env = ctxt.sym
        op = ast.op
        op_Str = ""
        str_Dup = ""
        str_I2f = ""
        resType = IntType()

        if op == "=":
            (resLeft1,
             typeLeft1) = self.visit(ast.left,
                                     Access(frame, env, True, True, False))
            (resRight,
             typeRight) = self.visit(ast.right,
                                     Access(frame, env, False, True, True))

            if type(typeLeft1) == FloatType and type(typeRight) == IntType:
                str_I2f = self.emit.emitI2F(frame)
            if ctxt.isDup == True:
                if type(ast.left) is Id:
                    str_Dup = self.emit.emitDUP(frame)
                else:
                    str_Dup = self.emit.emitDUP_X2(frame)

            (resLeft2,
             typeLeft2) = self.visit(ast.left,
                                     Access(frame, env, True, False, False))
            op_Str = resLeft1 + resRight + str_I2f + str_Dup + resLeft2

            resType = typeLeft1

        else:
            (resLeft,
             typeLeft) = self.visit(ast.left,
                                    Access(frame, env, False, True, True))
            (resRight,
             typeRight) = self.visit(ast.right,
                                     Access(frame, env, False, True, True))

            if op == "+" or op == "-":
                if type(typeLeft) is FloatType and type(typeRight) is IntType:
                    op_Str = resLeft + resRight + self.emit.emitI2F(
                        frame) + self.emit.emitADDOP(op, FloatType(), frame)
                    resType = FloatType()
                elif type(typeLeft) is IntType and type(
                        typeRight) is FloatType:
                    op_Str = resLeft + self.emit.emitI2F(
                        frame) + resRight + self.emit.emitADDOP(
                            op, FloatType(), frame)
                    resType = FloatType()
                else:
                    op_Str = resLeft + resRight + self.emit.emitADDOP(
                        op, typeLeft, frame)
                    resType = typeLeft

            elif op == "*" or op == "/":
                if type(typeLeft) is FloatType and type(typeRight) is IntType:
                    op_Str = resLeft + resRight + self.emit.emitI2F(
                        frame) + self.emit.emitMULOP(op, FloatType(), frame)
                    resType = FloatType()
                elif type(typeLeft) is IntType and type(
                        typeRight) is FloatType:
                    op_Str = resLeft + self.emit.emitI2F(
                        frame) + resRight + self.emit.emitMULOP(
                            op, FloatType(), frame)
                    resType = FloatType()
                else:
                    op_Str = resLeft + resRight + self.emit.emitMULOP(
                        op, typeLeft, frame)
                    resType = typeLeft
            elif op == "%":
                op_Str = resLeft + resRight + self.emit.emitMOD(frame)
                resType = IntType()
            elif (op == "<") or (op == "<=") or (op == ">") or (op == ">="):
                if type(typeLeft) is FloatType and type(typeRight) is IntType:
                    op_Str = resLeft + resRight + self.emit.emitI2F(
                        frame) + self.emit.emitREOP(op, FloatType(), frame)
                elif type(typeLeft) is IntType and type(
                        typeRight) is FloatType:
                    op_Str = resLeft + self.emit.emitI2F(
                        frame) + resRight + self.emit.emitREOP(
                            op, FloatType(), frame)
                else:
                    op_Str = resLeft + resRight + self.emit.emitREOP(
                        op, typeLeft, frame)
                resType = BoolType()
            elif (op == "==") or (op == "!="):
                if type(typeLeft) is BoolType and type(typeRight) is BoolType:
                    op_Str = resLeft + resRight + self.emit.emitREOP(
                        op, IntType(), frame)
                if type(typeLeft) is IntType and type(typeRight) is IntType:
                    op_Str = resLeft + resRight + self.emit.emitREOP(
                        op, IntType(), frame)
                else:
                    resType = BoolType()
            elif (op == "&&") or (op == "||"):
                op_Str = self.emit.emitAND_OR_SHORT_CIRCUIT(
                    op, resLeft, resRight, frame)
                resType = BoolType()

        return (op_Str, resType)

    def visitUnaryOp(self, ast, c):
        ctxt = c
        frame = ctxt.frame
        env = ctxt.sym
        (resExpr, typeExpr) = self.visit(ast.body,
                                         Access(frame, env, False, True, True))
        if ast.op == "!":
            return (resExpr + self.emit.emitNOT(typeExpr, frame), typeExpr)
        elif ast.op == "-":
            return (resExpr + self.emit.emitNEGOP(typeExpr, frame), typeExpr)

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

        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
        returnType = ctype.rettype

        if ctxt.isLeft is True and ctxt.isFirst is False:
            return (self.emit.emitWRITEVAR2(ast.method.name, returnType,
                                            frame), returnType)
        else:
            listParamType = ctype.partype
            # zip
            checkList = []
            for item in range(len(listParamType)):
                checkList.append((ast.param[item], listParamType[item]))

            in_ = ("", [])
            for x in checkList:
                (str1,
                 typ1) = self.visit(x[0], Access(frame, nenv, False, True,
                                                 True))
                if type(typ1) is IntType and type(x[1]) is FloatType:
                    in_ = (in_[0] + str1 + self.emit.emitI2F(frame),
                           in_[1].append(typ1))
                else:
                    in_ = (in_[0] + str1, in_[1] + [typ1])

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

        #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))

    def visitId(self, ast, o, fl=False):
        # o : Access (co the la SubBody)
        if type(o) != SubBody:
            sym = self.lookup(ast.name, o.sym, lambda x: x.name)

            code = ""
            if o.isLeft is True and o.isFirst is True:
                pass
            elif o.isLeft is True and o.isFirst is False:
                if type(sym.mtype) is ArrayType or type(
                        sym.mtype) is ArrayPointerType:
                    code = self.emit.emitWRITEVAR2(ast.name, sym.mtype,
                                                   o.frame)
                else:
                    if type(sym.value) is CName:
                        code = self.emit.emitPUTSTATIC(
                            sym.value.value + "." + ast.name, sym.mtype,
                            o.frame)
                    elif type(sym.value) is Index:
                        code = self.emit.emitWRITEVAR(ast.name, sym.mtype,
                                                      sym.value.value, o.frame)

            elif o.isLeft is False:
                if type(sym.value) is CName:
                    code = self.emit.emitGETSTATIC(
                        sym.value.value + "." + ast.name, sym.mtype, o.frame)
                elif type(sym.value) is Index:
                    code = self.emit.emitREADVAR(ast, sym.mtype,
                                                 sym.value.value, o.frame)

            return (code, sym.mtype)
        else:
            sym = self.lookup(ast.name, o.sym, lambda x: x.name)
            return ("", sym.mtype)

    def visitArrayCell(self, ast, o):

        # if in body expression
        if type(o) != SubBody:
            frame = o.frame
            lsSym = o.sym
            if o.isLeft is True and o.isFirst is True:
                (resArr, typeArr) = self.visit(
                    ast.arr, Access(frame, lsSym, False, True, False))
                (resIdx,
                 typIdx) = self.visit(ast.idx,
                                      Access(frame, lsSym, False, True, True))
                return (resArr + resIdx, typeArr.eleType)

            elif o.isLeft is True and o.isFirst is False:
                (resArr, typeArr) = self.visit(
                    ast.arr, Access(frame, lsSym, True, False, False))
                return (resArr, typeArr)

            elif o.isLeft is False:
                (resArr, typeArr) = self.visit(
                    ast.arr, Access(frame, lsSym, False, True, False))
                (resIdx,
                 typIdx) = self.visit(ast.idx,
                                      Access(frame, lsSym, False, True, True))
                if type(typeArr) is ArrayType:
                    arrayType = typeArr.eleType
                    aload = self.emit.emitALOAD(arrayType, frame)
                    return (resArr + resIdx + aload, arrayType)
                elif type(typeArr) is ArrayPointerType:
                    arrayPointerType = typeArr.eleType
                    aload = self.emit.emitALOAD(arrayPointerType, frame)
                    return (resArr + resIdx + aload, arrayPointerType)
        else:
            # ko o trong body expr thi o dau ta. o ngoai, vi du: a[6] = 4;
            frame = o.frame
            lsSym = o.sym
            (resArr,
             typeArr) = self.visit(ast.arr,
                                   Access(frame, lsSym, False, True, False))
            arrayType = typeArr.eleType
            return ("", arrayType)

    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):
        #ast: FloatLiteral
        #o: Any
        ctxt = o
        frame = ctxt.frame
        return self.emit.emitPUSHFCONST(str(ast.value), frame), FloatType()

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

    def visitBooleanLiteral(self, ast, o):
        ctxt = o
        frame = ctxt.frame
        return (self.emit.emitPUSHICONST(str(ast.value), frame), BoolType())
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")
        # global variable
        self.listGlobalArray = [] 

    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:
                paramType = [y.varType for y in x.param]
                staticDecl = [Symbol(x.name.name.lower(), MType(paramType, x.returnType), CName(self.className))] + staticDecl
            else:
                newSymbol = self.visit(x, SubBody(None, None, isGlobal=True))
                staticDecl = [newSymbol] + staticDecl

        e = SubBody(None, staticDecl)

        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 genMETHOD(self, consdecl: FuncDecl, o, frame: Frame):
        #consdecl: FuncDecl
        #o: Any
        #frame: Frame

        global_env = o

        funcName = consdecl.name.name

        isInit = consdecl.returnType is None and funcName == "<init>"

        isClassInit = consdecl.returnType is None and funcName == "<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 [Ctils.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)

        # 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 = [] 
        paramList = SubBody(frame, global_env)


        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.dimen
                self.emit.printout(self.emit.emitInitNewStaticArray(self.className + "/" + x.variable, size, x.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))
            
        
        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 visitFuncDecl(self, ast: FuncDecl, o):
        #ast: FuncDecl
        #o: Any

        subcontext = o
        frame = Frame(ast.name, ast.returnType)

        self.genMETHOD(ast, subcontext.sym, frame)

        return SubBody(None, [Symbol(ast.name, MType([], ast.returnType), CName(self.className))] + subcontext.sym)

    def visitVarDecl(self, ast: VarDecl, subbody: SubBody):
        subcontext = subbody
        frame = subbody.frame
        isGlobal = subbody.isGlobal
        varName = ast.variable
        varType = ast.varType

        if isGlobal:
            self.emit.printout(self.emit.emitATTRIBUTE(varName, Ctils.retrieveType(varType), False, ""))
            if type(ast.varType) is ArrayType:
                self.listGlobalArray.append(ast)
            return Symbol(varName, varType)

        idx = frame.getNewIndex()

        self.emit.printout(self.emit.emitVAR(idx, varName, Ctils.retrieveType(varType), frame.getStartLabel(), frame.getEndLabel(), frame))

        return SubBody(frame, [Symbol(varName, varType, Index(idx))] + subcontext.sym)

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

        listLocalArray = []
        
        for x in ast.member:
            if type(x) is VarDecl:
                subcontext = self.visit(x, subcontext)
                symbols = subcontext.sym
                if type(x.varType) is ArrayType:
                    index = subcontext.sym[0].value.value
                    varType = subcontext.sym[0].mtype
                    size = varType.dimen
                    self.emit.printout(self.emit.emitInitNewLocalArray(index, size, varType.eleType, frame))
            else: 
                e = SubBody(frame, symbols)
                if self.visit(x, e) == True:
                    return True
        return False

    def visitIf(self, ast: If, o: SubBody):
        subcontext = o
        frame = subcontext.frame
        newEnv = subcontext.sym
        expCode, expType = self.visit(ast.expr, Access(frame, newEnv, False, True))
        self.emit.printout(expCode)

        labelT = frame.getNewLabel() # if expr is true
        labelE = frame.getNewLabel() # label end
        
        self.emit.printout(self.emit.emitIFTRUE(labelT, frame))
        # handle else stmt
        if ast.elseStmt:
            # False
            isReturn = True in [self.visit(ast.elseStmt, subcontext)]
            if not isReturn:
                self.emit.printout(self.emit.emitGOTO(labelE, frame))
            # True
            self.emit.printout(self.emit.emitLABEL(labelT, frame))
            isReturn = True in [self.visit(ast.thenStmt, subcontext)] and isReturn
            # End
            self.emit.printout(self.emit.emitLABEL(labelE, frame))
            return isReturn
        else:
            # self.emit.printout(self.emit.emitIFTRUE(labelT, frame))
            self.emit.printout(self.emit.emitGOTO(labelE, frame))
            # True
            self.emit.printout(self.emit.emitLABEL(labelT, frame))
            isReturn = True in [self.visit(ast.thenStmt, subcontext)] 
            # End
            self.emit.printout(self.emit.emitLABEL(labelE, frame))
            return isReturn

    def visitFor(self, ast: For, o: SubBody):
        subcontext = o
        frame = subcontext.frame
        newEnv = subcontext.sym

        expr2, _ = self.visit(ast.expr2, Access(frame, newEnv, False, True))

        labelS = frame.getNewLabel()
        labelE = frame.getNewLabel()

        # init value
        self.visit(ast.expr1, SubBody(frame, newEnv))
        frame.enterLoop()

        # loop
        self.emit.printout(self.emit.emitLABEL(labelS, frame))

        # condition
        self.emit.printout(expr2)
        self.emit.printout(self.emit.emitIFFALSE(labelE, frame))

        # stmt
        isReturn = True in [self.visit(ast.loop, subcontext)]
        self.emit.printout(self.emit.emitLABEL(frame.getContinueLabel(), frame))

        # update index
        self.visit(ast.expr3, SubBody(frame, newEnv))

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

    def visitDowhile(self, ast: Dowhile, o: SubBody):
        subcontext = o
        frame = subcontext.frame
        newEnv = subcontext.sym
        expr, exprType = self.visit(ast.exp, Access(frame, newEnv, False, True))

        labelS = frame.getNewLabel()
        labelE = frame.getNewLabel()

        # enter loop
        frame.enterLoop()
        [self.visit(x, o) for x in ast.sl]
        self.emit.printout(self.emit.emitLABEL(labelS, frame))
        self.emit.printout(expr)
        self.emit.printout(self.emit.emitIFFALSE(labelE, frame))
        isReturn = True in [self.visit(x, o) for x in ast.sl]
        self.emit.printout(self.emit.emitLABEL(frame.getContinueLabel(), frame))
        if not isReturn:
            self.emit.printout(self.emit.emitGOTO(labelS, frame))
        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):
        subcontext = o
        frame = subcontext.frame
        return self.emit.printout(self.emit.emitGOTO(frame.getBreakLabel(), frame))

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

    def visitReturn(self, ast: Return, o: SubBody):
        subcontext = o
        frame = subcontext.frame1
        newEnv = subcontext.sym
        retType = frame.returnType

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

    def visitBinaryOp(self, ast: BinaryOp, o: SubBody):
        subcontext = o
        frame = subcontext.frame
        newEnv = subcontext.sym
        op = str(ast.op)

        if Ctils.isOpForNum(op):
            left, lType = self.visit(ast.left, subcontext)
            right, rType = self.visit(ast.right, subcontext)
            mtype = Ctils.mergeNumberType(lType, rType)
            if type(lType) is IntType and type(mtype) != type(lType):
                left += self.emit.emitI2F(frame)
            if type(rType) is IntType and type(mtype) != type(rType):
                right += self.emit.emitI2F(frame)
            if Ctils.isOpForNumToNum(op):
                if op in ['+', '-']:
                    return left + right + self.emit.emitADDOP(op, mtype, frame), mtype
                if op in ['*', '/']:
                    return left + right + self.emit.emitMULOP(op, mtype, frame), mtype
                if op == '%':
                    return left + right + self.emit.emitMOD(frame), mtype
            else:
                return left + right + self.emit.emitREOP(op, mtype, frame), BoolType()
        elif op == '=':
            if type(o) is Access:
                isArray, _ = self.visit(ast.left, Access(frame, newEnv, True, True, True))

                if not isArray: 
                    expr, expType = self.visit(ast.right, Access(frame, newEnv, False, True))
                    lhs, lhsType = self.visit(ast.left, Access(frame, newEnv, True, True))
                    if type(lhsType) is FloatType and type(expType) is IntType:
                        expr += self.emit.emitI2F(frame)
                    self.emit.printout(expr + self.emit.emitDUP(frame) + lhs)
                    return "", expType
                # else:
                #     for i in range(0, 2):
                #         frame.push()
                #     if o.getLeft == False:
                #         expr, expType = self.visit(ast.right, Access(frame, newEnv, False, True, False, False))
                #         lhs, lhsType = self.visit(ast.left, Access(frame, newEnv, True, True))
                #         if type(lhsType) is FloatType and type(expType) is IntType:
                #             expr += self.emit.emitI2F(frame)
                #         self.emit.printout(lhs[0])
                #         for i in range(0, 2):
                #             frame.push()
                #         return expr, expType
                #     elif o.getLeft == True:
                #         expr, expType = self.visit(ast.right, Access(frame, newEnv, False, True, False, True))
                #         lhs, lhsType = self.visit(ast.left, Access(frame, newEnv, True, True))
                #         if type(lhsType) is FloatType and type(expType) is IntType:
                #             expr += self.emit.emitI2F(frame)
                #         self.emit.printout(self.emit.emitDUPX2(frame) + lhs[1])
                #         for i in range(0, 2):
                #             frame.push()
                #         return None, None
                
            else:
                isArray, _ = self.visit(ast.left, Access(frame, newEnv, True, True, True))

                # if isArray: 
                #     for i in range(0, 2):
                #         frame.push()
                #     expr, expType = self.visit(ast.right, Access(frame, newEnv, False, True))
                #     lhs, lhsType = self.visit(ast.left, Access(frame, newEnv, True, True))
                #     self.emit.printout(lhs[0] + expr)
                #     _, _ = self.visit(ast.right, Access(frame, newEnv, False, True, False, True))
                #     self.emit.printout(lhs[1])
                #     for i in range(0, 2):
                #         frame.pop()
                else:
                    frame.push()
                    expr, expType = self.visit(ast.right, Access(frame, newEnv, False, True))
                    lhs, lhsType = self.visit(ast.left, Access(frame, newEnv, True, True))
                    if type(lhsType) is FloatType and type(expType) is IntType:
                        expr += self.emit.emitI2F(frame)
                    self.emit.printout(expr + lhs)
                    frame.pop()
Example #3
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 = "MPClass"
        self.path = dir_
        self.emit = Emitter(self.path + "/" + self.className + ".j")
        self.curFunc = None

    #generate the static field for global variable
    def VarGlobal(self, ast, c):
        ctxt = c
        nameAt = ast.variable.name
        typeAt = ast.varType 
        self.emit.printout(self.emit.emitATTRIBUTE(nameAt, typeAt, False, ""))
        symbol = Symbol(ast.variable.name, typeAt, CName(self.className))
        c.append(symbol)
        return c

    def FuncGlobal(self,ast, c):
        ctxt = c
        nameFunc = ast.name.name
        typeFunc = MType([x.varType for x in ast.param], ast.returnType)
        symbol = Symbol(nameFunc, typeFunc, CName(self.className))
        c.append(symbol)
        return c
    
    def printoutstmt(self, ast, env):
        #env : subbody
        frame = env.frame
        newEnv = env.sym
        if type(ast) is Assign:
            self.emit.printout(self.visit(ast, Access(frame, newEnv, True, True))[0])
        elif type(ast) is BinaryOp:
            self.emit.printout(self.visit(ast, Access(frame, newEnv, False, True))[0])
        elif type(ast) is CallExpr:
            self.emit.printout(self.visit(ast, Access(frame, newEnv, False, True, True))[0])
            sym = self.lookup(ast.method.name, newEnv, lambda x:x.name) 
            returnType = sym.mtype.rettype

        elif type(ast) is CallStmt:
            self.emit.printout(self.visit(ast, SubBody(frame, newEnv)))
        elif type(ast) is UnaryOp or type(ast) is Id or type(ast) is IntLiteral or type(ast) is FloatLiteral or type(ast) is StringLiteral or type(ast) is BooleanLiteral:
            self.emit.printout(self.visit(ast, Access(frame, newEnv, False, True))[0])
            self.emit.printout(self.emit.emitPOP(frame))
        else:
            self.visit(ast, env)


    def visitProgram(self, ast, c):
        #ast: Program
        #c: Any
        self.emit.printout(self.emit.emitPROLOG(self.className, "java.lang.Object"))
        nenv = reduce(lambda x,y: self.VarGlobal(y,x) if type(y) is VarDecl else self.FuncGlobal(y,x), ast.decl, self.env if self.env else [])
        
        lstFunc = list(filter(lambda x:type(x) is FuncDecl, ast.decl))
        reduce(lambda a,b: self.visit(b,a), lstFunc, SubBody(None, nenv))
        #e = SubBody(None, self.env)
        #for x in ast.decl:
        #    e = self.visit(x, e)
        # generate default constructor
        self.genMETHOD(FuncDecl(Id("<init>"), list(), list(), list(),None), c, Frame("<init>", VoidType))
        self.emit.emitEPILOG()
        return c

    def visitVarDecl(self, ast, c):
        env = c.sym if type(c) is SubBody else []
        indx = c.frame.getNewIndex()
        self.emit.printout(self.emit.emitVAR(indx, ast.variable.name, ast.varType, c.frame.getStartLabel(), c.frame.getEndLabel(), c.frame))
        return SubBody(c.frame, [Symbol(ast.variable.name, ast.varType, Index(indx))] + env)

    def genMETHOD(self, consdecl, o, frame):
        #consdecl: FuncDecl
        #o: Any
        #frame: Frame
        isInit = consdecl.returnType is None
        isMain = consdecl.name.name == "main" and len(consdecl.param) == 0 and type(consdecl.returnType) is VoidType
        returnType = VoidType() if isInit else consdecl.returnType
        methodName = "<init>" if isInit else consdecl.name.name
        intype = [ArrayPointerType(StringType())] if isMain else [x.varType for x in consdecl.param]
        mtype = MType(intype, returnType)

        #generate method for a function . use emitMETHOD(methodname, type_descrip, true if static, frame)
        self.emit.printout(self.emit.emitMETHOD(methodName, mtype, not isInit, frame))

        #involked when parsing new scope
        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))
        
        glSubBody = SubBody(frame, glenv)

        if (isMain is False) and (intype != []):
            glSubBody = reduce(lambda a,b: self.visit(b,a), consdecl.param, SubBody(frame, glenv))

        temp = glSubBody
        glSubBody = reduce(lambda a,b: self.visit(b, a), consdecl.local, temp) 

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

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

        #list(map(lambda x: self.visit(x, SubBody(frame, glSubBody.sym)), body))
        list(map(lambda x: self.printoutstmt(x, glSubBody),body))
        
        self.emit.printout(self.emit.emitLABEL(frame.getEndLabel(), frame))

        returnstmt = list(filter(lambda x: type(x)is Return, body))
        if type(returnType) is VoidType or not returnstmt:
            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 ... subbody
        subctxt = o
        frame = Frame(ast.name, ast.returnType)
        self.curFunc = self.lookup(ast.name.name.lower(), subctxt.sym, lambda x: x.name.lower())
        self.genMETHOD(ast, subctxt.sym, frame)

        return o
        #return SubBody(None, [Symbol(ast.name, MType(list(), ast.returnType), CName(self.className))] + subctxt.sym)

    def visitAssign(self, ast, c):
        ctxt = c
        frame = ctxt.frame
        env = ctxt.sym

        assign_str = ""
        str_I2F = ""

        (resLeft, typeLeft) = self.visit(ast.lhs, Access(frame, env, True, True))
        (resRight, typeRight) = self.visit(ast.exp, Access(frame, env, False, True))

        if type(typeLeft) == FloatType and type(typeRight) == IntType:
            str_I2F = self.emit.emitI2F(frame)

        (resLeft1, typeLeft1) = self.visit(ast.lhs, Access(frame, env, True, False))
        assign_str = resLeft + resRight + str_I2F + resLeft1
        resType = typeLeft1

        return (assign_str, resType)
    
    def visitIf(self, ast, c):
        #c : SubBody
        frame = c.frame
        env = c.sym
        (resExpr, typeExpr) = self.visit(ast.expr, Access(frame, env, False, True))
        if not ast.elseStmt:
            falseLabel = frame.getNewLabel()
            self.emit.printout(resExpr + self.emit.emitIFFALSE(falseLabel, frame))
            list(map(lambda x: self.printoutstmt(x, c),ast.thenStmt))
            self.emit.printout(self.emit.emitLABEL(falseLabel, frame))
        else:
            falseLabel = frame.getNewLabel()
            trueLabel = frame.getNewLabel()
            self.emit.printout(resExpr + self.emit.emitIFFALSE(falseLabel, frame))
            list(map(lambda x: self.printoutstmt(x, c),ast.thenStmt))
            self.emit.printout(self.emit.emitGOTO(str(trueLabel), frame) + self.emit.emitLABEL(str(falseLabel), frame))
            list(map(lambda x: self.printoutstmt(x, c),ast.elseStmt))
            self.emit.printout(self.emit.emitLABEL(str(trueLabel), frame))


    def visitWhile(self, ast, c):
        frame = c.frame
        env = c.sym

        frame.enterLoop()
        self.emit.printout(self.emit.emitLABEL(frame.getContinueLabel(), frame))
        resExpr, typeExpr = self.visit(ast.exp,Access(frame, env,False,False))
        self.emit.printout(resExpr)
        self.emit.printout(self.emit.emitIFFALSE(frame.getBreakLabel(), frame))
        list(map(lambda x:self.printoutstmt(x,c) ,ast.sl))
        self.emit.printout(self.emit.emitGOTO(frame.getContinueLabel(), frame))
        self.emit.printout(self.emit.emitLABEL(frame.getBreakLabel(), frame))
        frame.exitLoop()

    def visitFor(self, ast, c):
        frame = c.frame
        env = c.sym
        beginLabel = frame.getNewLabel()
        frame.enterLoop()
        #sinh ma cho expr1
        self.emit.printout(self.visit(Assign(ast.id,ast.expr1),SubBody(frame, env))[0])
        self.emit.printout(self.emit.emitLABEL(beginLabel, frame))
        
        #sinh ma cho expr2
        op_ = ('<=','+') if ast.up is True else ('>=','-')
        self.emit.printout(self.visit(BinaryOp(op_[0],ast.id,ast.expr2),SubBody(frame, env))[0])
        self.emit.printout(self.emit.emitIFFALSE(frame.getBreakLabel(), frame))

        #sinh stmt
        list(map(lambda x:self.printoutstmt(x,c) ,ast.loop))

        #sinh ma continue
        self.emit.printout(self.emit.emitLABEL(frame.getContinueLabel(), frame))
        
        #sinh ma expr3
        self.emit.printout(self.visit(Assign(ast.id,BinaryOp(op_[1],ast.id,IntLiteral(1))),SubBody(frame, env))[0])
        
        #back
        self.emit.printout(self.emit.emitGOTO(beginLabel, frame))

        #sinh ma break
        self.emit.printout(self.emit.emitLABEL(frame.getBreakLabel(), frame))
        frame.exitLoop()

    def visitBreak(self,ast,c):
        self.emit.printout(self.emit.emitGOTO(c.frame.getBreakLabel(), c.frame))
    
    def visitContinue(self,ast,c):
        self.emit.printout(self.emit.emitGOTO(c.frame.getContinueLabel(), c.frame))

    def visitWith(self, ast, c):
        #decl:list(VarDecl)
        #stmt:list(Stmt)
        frame = c.frame
        env = c.sym
            
        frame.enterScope(False)
        withSubBody = reduce(lambda a,b: self.visit(b, a), ast.decl, c)
        self.emit.printout(self.emit.emitLABEL(frame.getStartLabel(), frame))
        list(map(lambda x: self.printoutstmt(x, withSubBody),ast.stmt))  
        self.emit.printout(self.emit.emitLABEL(frame.getEndLabel(), frame)) 
        frame.exitScope()
        
        return c

    def visitReturn(self, ast, c):
        #expr:Expr
        if ast.expr:
            (resExpr, resType) = self.visit(ast.expr, Access(c.frame, c.sym, False, True))
            typeFunc = self.curFunc.mtype.rettype
            if type(typeFunc) == FloatType and type(resType) == IntType:
                self.emit.printout(resExpr + self.emit.emitI2F(c.frame) + self.emit.emitRETURN(FloatType(), c.frame))
            else:
                self.emit.printout(resExpr + self.emit.emitRETURN(resType, c.frame))
        else:
            self.emit.printout(self.emit.emitRETURN(VoidType(), c.frame))

    def visitCallStmt(self, ast, o):
        ctxt = o
        frame = ctxt.frame
        nenv = ctxt.sym
        sym = self.lookup(ast.method.name.lower(), nenv, lambda x: x.name.lower())

        method_name = sym.name
        cname = sym.value.value
        ctype = sym.mtype
        returnType = ctype.rettype

        listparamType = ctype.partype
        
        checkList = []
        for item in range(len(listparamType)):
            checkList.append((ast.param[item], listparamType[item]))
        
        in_ = ("", [])
        for x in checkList:
            #gan param
            (code, ty) = self.visit(x[0], Access(frame, nenv, False, True))
            if type(ty) is IntType and type(x[1]) is FloatType:
                in_ = (in_[0] + code + self.emit.emitI2F(frame), in_[1] + [ty])
            else:
                in_ = (in_[0] + code, in_[1] + [ty])
        
        return in_[0] + self.emit.emitINVOKESTATIC(cname + "/" + method_name, ctype, frame)

    def visitCallExpr(self, ast, o):
        #ast: CallStmt
        #o: Any
        #method:Id
        #param:list(Expr)
        ctxt = o
        frame = ctxt.frame
        nenv = ctxt.sym
        sym = self.lookup(ast.method.name.lower(), nenv, lambda x: x.name.lower())

        method_name = sym.name
        cname = sym.value.value
        ctype = sym.mtype
        returnType = ctype.rettype 
        
        if ctxt.isLeft is True and ctxt.isFirst is False:
            return (self.emit.emitWRITEVAR2(ast.method.name, returnType, frame), returnType)
        else:
            listParamType = ctype.partype
            # zip
            checkList=[]
            for item in range(len(listParamType)):
                checkList.append((ast.param[item],listParamType[item]))
            
            in_ = ("",[])
            for x in checkList:
                #str1 : param
                #typ1 : type param
                (str1,typ1) = self.visit(x[0],Access(frame,nenv,False,True))
                if type(typ1) is IntType and type(x[1]) is FloatType:
                    in_ = (in_[0] + str1 + self.emit.emitI2F(frame), in_[1] + [typ1])
                else:
                    in_ = (in_[0] + str1, in_[1] + [typ1])
        
        return (in_[0] + self.emit.emitINVOKESTATIC(cname + "/" + method_name, ctype, frame), returnType)
        #return (in_[0], returnType)
          

    def visitBinaryOp(self, ast, c):
        #op:string
        #left:Expr
        #right:Expr
        ctxt = c
        frame = c.frame
        env = ctxt.sym
        op = ast.op
        resType = IntType()
        code = ""
        opstr = ""
        stri2f = ""

        (resLeft, typeLeft) = self.visit(ast.left, Access(frame, env, False, True))
        (resRight, typeRight) = self.visit(ast.right, Access(frame, env, False, True))

        if op == "+" or op == "-":
            if type(typeLeft) is FloatType and type(typeRight) is IntType:
                opstr = resLeft + resRight + self.emit.emitI2F(frame) + self.emit.emitADDOP(op, FloatType(), frame) 
                resType = FloatType()   
            elif type(typeLeft) is IntType and type(typeRight) is FloatType:
                opstr = resLeft + self.emit.emitI2F(frame) + resRight + self.emit.emitADDOP(op, FloatType(), frame)             
                resType = FloatType()
            else:
                opstr = resLeft + resRight + self.emit.emitADDOP(op, typeLeft, frame)
                resType = typeLeft
        elif op == "*" :
            if type(typeLeft) is FloatType and type(typeRight) is IntType:
                opstr = resLeft + resRight + self.emit.emitI2F(frame) + self.emit.emitMULOP(op, FloatType(), frame)
                resType = FloatType()
            elif type(typeLeft) is IntType and type(typeRight) is FloatType:
                opstr = resLeft + self.emit.emitI2F(frame) + resRight + self.emit.emitMULOP(op, FloatType(), frame)
                resType = FloatType()
            else:
                opstr = resLeft + resRight + self.emit.emitMULOP(op, typeLeft, frame)
                resType = typeLeft
        elif op == "/" :
            if type(typeLeft) is IntType and type(typeRight) is IntType:
                opstr = resLeft + self.emit.emitI2F(frame) + resRight + self.emit.emitI2F(frame) + self.emit.emitMULOP(op, FloatType(), frame)
                resType = FloatType()
            if type(typeLeft) is FloatType and type(typeRight) is IntType:
                opstr = resLeft + resRight + self.emit.emitI2F(frame) + self.emit.emitMULOP(op, FloatType(), frame)
                resType = FloatType()
            elif type(typeLeft) is IntType and type(typeRight) is FloatType:
                opstr = resLeft + self.emit.emitI2F(frame) + resRight + self.emit.emitMULOP(op, FloatType(), frame)
                resType = FloatType()
            elif type(typeLeft) is FloatType and type(typeRight) is FloatType:
                opstr = resLeft + resRight + self.emit.emitMULOP(op, FloatType(), frame)
                resType = FloatType()
             


        elif op.lower() == "div":
            opstr = resLeft + resRight + self.emit.emitDIV(frame)
            resType = IntType()
        elif op.lower() == "mod":
            opstr = resLeft + resRight + self.emit.emitMOD(frame)
            resType = IntType()

        elif (op == ">") or (op == "<") or (op == ">=") or (op == "<="):
            if type(typeLeft) is FloatType and type(typeRight) is IntType:
                opstr = resLeft + resRight + self.emit.emitI2F(frame) + self.emit.emitREOP(op, FloatType(), frame)
            elif type(typeLeft) is IntType and type(typeRight) is FloatType:
                opstr = resLeft + self.emit.emitI2F(frame) + resRight + self.emit.emitREOP(op, FloatType(), frame)
            else:
                opstr = resLeft + resRight + self.emit.emitREOP(op, typeLeft, frame)
            resType = BoolType()
        
        elif (op == "<>" or op == "="):
            if type(typeLeft) is FloatType and type(typeRight) is IntType:
                opstr = resLeft + resRight + self.emit.emitI2F(frame) + self.emit.emitREOP(op, FloatType(), frame)
            elif type(typeLeft) is IntType and type(typeRight) is FloatType:
                opstr = resLeft + self.emit.emitI2F(frame) + resRight + self.emit.emitREOP(op, FloatType(), frame)
            else:
                opstr = resLeft + resRight + self.emit.emitREOP(op, typeLeft, frame)
            
            resType = BoolType()
            
        elif (op.lower() == "and" or op.lower() == "or"):
            #opstr = resLeft + resRight + self.emit.emitANDOP(frame) if ast.op.lower() == "and" else self.emit.emitOROP(frame)
            if ast.op.lower() == "and":
                opstr = resLeft + resRight + self.emit.emitANDOP(frame)
            elif ast.op.lower() == "or":
                opstr = resLeft + resRight + self.emit.emitOROP(frame)
            resType = BoolType()
        elif ast.op.lower() == "andthen" or ast.op.lower() == "orelse":
            opstr = self.emit.emitAndThen_OrElse(ast.op.lower(), resLeft, resRight, frame)
            resType = BoolType()
        
        return(opstr, resType)        

    def visitUnaryOp(self,ast,c):
        ctxt = c
        frame = ctxt.frame
        env = ctxt.sym
        (resExpr, typeExpr) = self.visit(ast.body, Access(frame, env, False, True))
        if ast.op.lower() == "not":
            return (resExpr + self.emit.emitNOT(typeExpr, frame), typeExpr)

        elif ast.op == "-": 
            return (resExpr + self.emit.emitNEGOP(typeExpr, frame), typeExpr)
    

    
    def visitId(self, ast, o):
        #o : Access or SubBody
        
        if type(o) is not SubBody: 
            sym = self.lookup(ast.name.lower(), o.sym, lambda x: x.name.lower())

            name_id = sym.name
            code = ""
            if o.isLeft is True and o.isFirst is True:
                pass
            #putStatic or store var
            elif o.isLeft is True:
                if type(sym.value) is CName:
                    code = self.emit.emitPUTSTATIC(sym.value.value + "." + name_id, sym.mtype, o.frame)
                elif type(sym.value) is Index:
                    code = self.emit.emitWRITEVAR(name_id, sym.mtype, sym.value.value, o.frame)
            #if lhs -> getstatic or load var
            elif o.isLeft is False:
                if type(sym.value) is CName:
                    code = self.emit.emitGETSTATIC(sym.value.value + "." + name_id, sym.mtype, o.frame)
                elif type(sym.value) is Index:
                    code = self.emit.emitREADVAR(name_id, sym.mtype, sym.value.value, o.frame)
            return (code, sym.mtype)    
        else:
            sym = self.lookup(ast.name, o.sym, lambda x: x.name)
            return ("", sym.mtype)

    def visitIntLiteral(self, ast, o):
        #ast: IntLiteral
        #o: Any

        ctxt = o
        frame = ctxt.frame
        return self.emit.emitPUSHICONST(int(ast.value), frame), IntType()

    def visitFloatLiteral(self, ast, o):
        #ast : FloatLiteral
        #o : any

        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):
        #ast: StringLiteral
        #o: Any
        ctxt = o
        frame = ctxt.frame
        return self.emit.emitPUSHCONST(str(ast.value), StringType(), frame), StringType()
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 #6
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") #accept input that file MCClass.j

#==================================Declaration===================================
    '''
    this section is to visit Declaration for generating drectives:

        .source MCClass.java
        .class public MCClass
        .super java.lang.Object
        .method public static foo(I)I

    '''
    def visitProgram(self, ast, gloenvi):
        '''
        *print lines of directives 
        1. visit all vardecls in global and set static field
        2. visit funcdecls
        '''
        gloenvi=self.env
        
        self.emit.printout(self.emit.emitPROLOG(self.className, "java.lang.Object"))

        #in global: frame = None
        subBody = SubBody(None,gloenvi )

       
        #visit Declarations:
        '''
        1. visit all var declarations in global
        2. add func declarations in global to env (for calling inter functions)
        3. visit all all func declarations
        '''
        for x in filter(lambda x: isinstance(x,VarDecl), ast.decl):
            subBody = self.visit(x, subBody)
        for x in filter(lambda x: isinstance(x,FuncDecl), ast.decl):
            subBody.sym = [Symbol(x.name.name, MType([y.varType for y in x.param],x.returnType),CName(self.className))]+subBody.sym
        for x in filter(lambda x: isinstance(x,FuncDecl), ast.decl):
            subBody = self.visit(x, subBody)

        # generate default constructor
        initConstructor = FuncDecl(Id("<init>"), list(), None, Block(list()))
        initClass = FuncDecl(Id("<clinit>"), list(), None, Block(list()))
        self.genMETHOD(initConstructor, subBody.sym, Frame("<init>", VoidType))
        self.genMETHOD(initClass, subBody.sym, Frame("<clinit>", VoidType))

        self.emit.emitEPILOG()
        return gloenvi

    def visitFuncDecl(self, ast, subBody):
        
        frame = Frame(ast.name.name, ast.returnType) #Frame('main', void)
        subBody.sym=[Symbol(ast.name.name, MType([x.varType for x in ast.param] if ast.name.name not in ['main','<init>'] else list(), ast.returnType), CName(self.className))] + subBody.sym
        self.genMETHOD(ast, subBody.sym, frame)
        return subBody
    
    def genMETHOD(self, ast,envi, frame):
     
        #ast: FuncDecl
        #envi: list Symbols
        #frame: Frame

        isInit = ast.name.name == "<init>"
        isClinit = ast.name.name == "<clinit>"
        isMain = ast.name.name == "main" 
       
        returnType = VoidType() if isInit or isClinit else ast.returnType
        methodName = ast.name.name
        #main: String[] args
        intype = [ArrayPointerType(StringType())] if isMain else [x.varType for x in ast.param]
    
        mtype = MType(intype, VoidType()) if isMain else MType(intype,returnType) 

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

        frame.enterScope(True)

        localEnvi = envi
        # Generate code for parameter declarations
        if isInit:
            #.var 0 is this LMCClass; from Label0 to Label1
            self.emit.printout(self.emit.emitVAR(frame.getNewIndex(), "this", ClassType(self.className), frame.getStartLabel(), frame.getEndLabel(), frame))
        elif isClinit:
            #in isClinit, there is not declaration, just initialize
            pass
        if isMain:
            #.var 0 is args [Ljava/lang/String; from Label0 to Label1
            self.emit.printout(self.emit.emitVAR(frame.getNewIndex(), "args", ArrayPointerType(StringType()), frame.getStartLabel(), frame.getEndLabel(), frame))
        
        s= SubBody(frame,localEnvi)
        
        for x in ast.param:
            s= self.visit(x,s)
         
        body = ast.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:
            '''
            bipush 10
            newarray int
            putstatic MCClass.a [I
	
            '''
            globalArrVar = filter(lambda x:(isinstance(x.value, CName) and isinstance(x.mtype, ArrayType)),envi )
            for ast_i in globalArrVar:
                self.emit.printout(self.emit.emitClinitForArrayGlobal(ast_i.value.value+"."+ ast_i.name, ast_i.mtype,frame ))
        for x in body.member:
            if isinstance(x,VarDecl):
                s= self.visit(x,s)
            else:
                self.visit(x,s)

          
        # [self.visit(x, s) for x in body.member]
        # [print(x.name) for x in s]
        self.emit.printout(self.emit.emitLABEL(frame.getEndLabel(), frame))
        # Generate Return for only VoidType or Return in Main function must be convert into VoidType:
  
        if (type(returnType) is VoidType) or (frame.name == "main"):
            self.emit.printout(self.emit.emitRETURN(VoidType(), frame))

        self.emit.printout(self.emit.emitENDMETHOD(frame))
        frame.exitScope();

    def visitVarDecl(self, ast, sBody):
        #in global declaration
        if sBody.frame is None: 
            #============================drafts
            #neu la global thi dung clinit de new array
            tem=self.emit.emitATTRIBUTE(ast.variable, ast.varType, False, "")
            self.emit.printout(tem)
            return SubBody(None, [Symbol(ast.variable, ast.varType, CName(self.className))] + sBody.sym)
        else: #in local of function
            index = sBody.frame.getNewIndex()
            self.emit.printout(self.emit.emitVAR(index, ast.variable, ast.varType, sBody.frame.getStartLabel(), sBody.frame.getEndLabel(), sBody.frame))
            return SubBody(sBody.frame, [Symbol(ast.variable, ast.varType, Index(index))] + sBody.sym)

#==================================Statement=====================================
    '''
    this section is to visit Statement:
        - Return nothing
        - printout rigth when visit
    '''

    def visitReturn(self, ast: Return, o: SubBody):
        '''generate Return for methods different VoidType and not in main function'''

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

        if (not isinstance(retType, VoidType)) and (frame.name != "main"):
            expCode, expType = self.visit(ast.expr, Access(frame, nenv, False, False))
            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 #isReturn , for check isReturn later
    def visitIf(self, ast: If, o: SubBody):
        '''
            1. visit expr
            2. if true go to label 1
            3. do elseStmt
            4. go to label 2
            5.Label 1
            6. do thenStmt
            7.Label 2
            8.label end

        '''
        exprCode, exprType = self.visit(ast.expr, Access(o.frame, o.sym, False, False))
        self.emit.printout(exprCode)
        
        labelTrue = o.frame.getNewLabel()
        labelEnd = o.frame.getNewLabel()
        #labelFalse = o.frame.getNewLabel()
        self.emit.printout(self.emit.emitIFTRUE(labelTrue, o.frame))

        # self.emit.printout(self.emit.emitGOTO(labelFalse, o.frame))
        isReturnElse=False
        if ast.elseStmt is not None:
            if self.visit(ast.elseStmt,o) is True: isReturnElse = True

        if not isReturnElse:
            self.emit.printout(self.emit.emitGOTO(labelEnd, o.frame))

        self.emit.printout(self.emit.emitLABEL(labelTrue, o.frame))
        
        isReturnThen=False
        if self.visit(ast.thenStmt,o) is True:
            isReturnThen=True
        
        self.emit.printout(self.emit.emitLABEL(labelEnd, o.frame))
        return isReturnThen and isReturnElse
    def visitDowhile(self, ast:Dowhile, o: SubBody):
        '''
        1. Label Start
        2. visit list stmt
        3. Label Continue
        4. visit Expr
        5. if True go to Label Start
        6. Label Break
        
        '''
        labelStart = o.frame.getNewLabel()
        
        o.frame.enterLoop()

        self.emit.printout(self.emit.emitLABEL(labelStart, o.frame))

        [self.visit(x, o) for x in ast.sl]

        self.emit.printout(self.emit.emitLABEL(o.frame.getContinueLabel(), o.frame))

        expCode, expType = self.visit(ast.exp, Access(o.frame, o.sym, False, False))

        self.emit.printout(expCode)
        
        self.emit.printout(self.emit.emitIFTRUE(labelStart, o.frame))
        
        self.emit.printout(self.emit.emitLABEL(o.frame.getBreakLabel(), o.frame))
        
        o.frame.exitLoop()
        
    def visitFor(self, ast: For, o: SubBody):
        '''
        1.visit expr 1 (like visit a stmt: visit(ast, SubBody) because this is assign op)
        2.Label condition
        3.visit expr 2 (like visit a expr: visit(ast, Access)) 
        4.if True goto Label True
        5.go to Label Break (the same purpose with Label End)
        6.Label True
        7.visit Stmt
        8.Label Continue
        9.visit expr3 (like visit a stmt: visit(ast, SubBody))
        10. go to Label condition
        11. Label Break

        '''
        labelTrue  = o.frame.getNewLabel()
        labelCondition = o.frame.getNewLabel()
        o.frame.enterLoop()
        labelBreak = o.frame.getBreakLabel()
        labelContinue = o.frame.getContinueLabel()
        isReturn=False
        self.visit(ast.expr1,o)
        self.emit.printout(self.emit.emitLABEL(labelCondition, o.frame))
        expr2Code, expr2Type = self.visit(ast.expr2, Access(o.frame, o.sym, False, False))
        self.emit.printout(expr2Code)
        self.emit.printout(self.emit.emitIFTRUE(labelTrue, o.frame))
        self.emit.printout(self.emit.emitGOTO(labelBreak, o.frame))
        self.emit.printout(self.emit.emitLABEL(labelTrue, o.frame))
        if self.visit(ast.loop, o) is True:
            isReturn = True
        self.emit.printout(self.emit.emitLABEL(labelContinue, o.frame))
        if not isReturn:
            self.visit(ast.expr3, o)
            self.emit.printout(self.emit.emitGOTO(labelCondition, o.frame))
        self.emit.printout(self.emit.emitLABEL(labelBreak, o.frame))

        o.frame.exitLoop()
        return isReturn

    def visitBreak(self, ast: Break, o: SubBody):
        '''
        when enter the loop, we call frame.enterLoop to create BreakLabel
        and then we placed it in some place
        '''
        self.emit.printout(self.emit.emitGOTO(o.frame.getBreakLabel(), o.frame))
    
    def visitContinue(self, ast: Continue, o:SubBody):
        '''
        when enter the loop, we call frame.enterLoop to create ContinueLabel
        and then we placed it in some place
        '''
        self.emit.printout(self.emit.emitGOTO(o.frame.getContinueLabel(),o.frame))

    def visitBlock(self, ast: Block, o: SubBody):
        '''
        remember to pop variable declared in scope
        '''
        frame=o.frame
        frame.enterScope(False)
        self.emit.printout(self.emit.emitLABEL(frame.getStartLabel(),frame))
        #count the number of vardecl to pop it out of sym after exit scope
        count=0
        isReturn=False
        for x in ast.member:
            if isinstance(x,VarDecl):
                o= self.visit(x,o)
                count+=1
            else:
                if self.visit(x,o) is True:
                    isReturn=True
        o.sym = o.sym[count:]
        self.emit.printout(self.emit.emitLABEL(frame.getEndLabel(),frame))
        frame.exitScope()
        return isReturn

#==================================Expression====================================
    '''
    *Param:     ast, Access(frame, sym, isLeft, isFirst)
    *Return:    code, type
    *Note:      the first command in each method  "isinstance(acc,SubBody): return" say that 
                if expr which called as a statement should do nothing (except assign expression, and call)
    '''

    def visitId(self, ast: Id, acc):
        '''
        *every varible is visited two times
        *array, field of class in left : first: load reference,  second: store value
        *simple variable  and array, field of class in right :  first: do nothing,     second: store value
        '''

        if isinstance(acc,SubBody): return
        resultCode=""
        sym = self.lookup(ast.name, acc.sym, lambda x: x.name)
        isGlobal =isinstance(sym.value,CName)
        if acc.isLeft: #left
            if acc.isFirst:#first
                if isinstance(sym.mtype,(ArrayPointerType,ArrayType)):
                    if isGlobal :
                        resultCode+= self.emit.emitGETSTATIC(sym.value.value+"." + ast.name, sym.mtype,acc.frame)
                    else:
                        resultCode+= self.emit.emitREADVAR(ast.name, sym.mtype, sym.value.value, acc.frame)
            
            else: #second
                if isGlobal:
                    #name = Class name + "." +field name 
                    resultCode+= self.emit.emitPUTSTATIC(sym.value.value+"." + ast.name, sym.mtype, acc.frame)
                else:
                    resultCode+= self.emit.emitWRITEVAR(ast.name, sym.mtype, sym.value.value, acc.frame)

        else: #right
            if acc.isFirst: #left
                pass
            else:#second
                if  isGlobal:
                    resultCode+= self.emit.emitGETSTATIC(sym.value.value+"." + ast.name, sym.mtype, acc.frame)
                else:
                    resultCode+= self.emit.emitREADVAR(ast.name, sym.mtype, sym.value.value, acc.frame)
        return resultCode, sym.mtype
    
    def visitArrayCell(self, ast: ArrayCell, acc):
        '''
        *in LSH and in second time access: store (aload)
        *in RHS: load value (<type>astore)
        '''
        if isinstance(acc,SubBody): return
        
        if acc.isLeft: #LHS
            '''
            *if a[10] is LHS example: a[10]=3000;
                1. aload , iconst (first time)
                2. iconst 3000 (don't care)
                3. iastore      (second time)
            '''
            
            if acc.isFirst:
                # aload , iconst
                arrcode1, arrtype1 = self.visit(ast.arr, acc)
                indexcode,indextype = self.visit(ast.idx, Access(acc.frame, acc.sym,False, False ))
                result = arrcode1+ indexcode 
            else:
                arrcode1, arrtype1 = self.visit(ast.arr, Access(acc.frame, acc.sym,True, True ))
                 # iastore
                result = self.emit.emitASTORE(arrtype1.eleType, acc.frame)


        else :#RHS
            '''
            *if a[10] is RHS example: i = a[10];
                1. aload <index of a> (second time)
                2. iconst 10            (second time)
                3. iaload               (second time)
                4. istore <dont care>
            '''
            
            if acc.isFirst:
                result=""
            else:
                arrcode1, arrtype1 = self.visit(ast.arr, acc)
                indexcode, indextype = self.visit(ast.idx, acc)
                result= arrcode1 + indexcode + self.emit.emitALOAD(arrtype1.eleType, acc.frame)

        #print(" left ",acc.isLeft , ";     first ", acc.isFirst," :\n ",result)
        return result, arrtype1.eleType

    def visitCallExpr(self, ast: CallExpr, o):
        '''
        *input o: subBody or Access
        *subBody when Call is a statement => print code JVM
        *Access when Call is a expression => return code JVM for caller
        '''

        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 i, arg in enumerate(ast.param):
            #arg:expr
            #in RHS just visit in 2nd time
            paramcode, paramtype = self.visit(arg, Access(frame, nenv, False, False))

            #convert float to int
            if isinstance(paramtype,IntType) and isinstance(sym.mtype.partype[i],FloatType): 
                paramcode+= self.emit.emitI2F(frame)
 
            in_ = (in_[0] + paramcode, in_[1]+[paramtype])

        
        temp= self.emit.emitINVOKESTATIC(cname + "/" + ast.method.name, ctype, frame)
        if isinstance(o,SubBody):
            self.emit.printout(in_[0])
            self.emit.printout(temp)
        else:
            return in_[0] + temp, sym.mtype.rettype

    def visitBinaryOp(self, ast:BinaryOp, acc):
        '''
        divide into two type of Operation
        1. Assign: can be statement or expression
        2. Others: just be expression
        '''
        # Not assign Op
        if ast.op != '=':
            lcode, ltype = self.visit(ast.left, acc)
            #print("binOp left: "+ str(acc.frame.currOpStackSize))
            rcode, rtype = self.visit(ast.right, acc)
            #print("binOp right: "+ str(acc.frame.currOpStackSize))

            result=lcode
            #coercions type
            if isinstance(ltype,IntType) and isinstance(rtype,FloatType):
                ltype=rtype=FloatType()
                result+=self.emit.emitI2F(acc.frame)
                result+=rcode
            elif isinstance(ltype,FloatType) and isinstance(rtype,IntType):
                ltype=rtype=FloatType()
                result+=rcode
                result+=self.emit.emitI2F(acc.frame)
            else:
                result+=rcode
            
            #generate code for Op
            if ast.op in [ '<', '<=', '>', '>=', '==', '!=' ]: 
                result+= self.emit.emitREOP(ast.op,ltype,acc.frame)
            elif ast.op == '||': 
                result+= self.emit.emitOROP(acc.frame)
            elif ast.op == '&&': 
                result += self.emit.emitANDOP(acc.frame)
            elif ast.op in ['+','-']:
                result+= self.emit.emitADDOP(ast.op,ltype,acc.frame)
            elif ast.op in ['*','/']:
                result+= self.emit.emitMULOP(ast.op,ltype,acc.frame)
            elif ast.op == '%':
                result+= self.emit.emitMOD(acc.frame)
            #print(ast,result)
            return result, ltype
            
        else: #ast.op is assign op
            lcode, ltype = self.visit(ast.left, Access(acc.frame,acc.sym,True,True))

            rcode, rtype = self.visit(ast.right, Access(acc.frame,acc.sym,False,False))
         
            lcode2, ltype2 = self.visit(ast.left, Access(acc.frame,acc.sym,True,False))

            if isinstance(ltype,FloatType) and isinstance(rtype,IntType):
                result= lcode+rcode+self.emit.emitI2F(acc.frame)+lcode2
            else:
                result= lcode+rcode+lcode2

            #just print if this is assign Op:
            if isinstance(acc,SubBody): #statement
                self.emit.printout(result)
            else: #expression => a=4 return 5 => need load a to Op stack
                rcode2, rtype2 = self.visit(ast.left, Access(acc.frame,acc.sym,False,False))
                return result + rcode2, ltype

    def visitUnaryOp(self, ast: UnaryOp, acc):
        '''
        too easy to describe
        '''
        if isinstance(acc,SubBody): return

        bodycode, bodytype = self.visit(ast.body,acc)
        if ast.op == '-':
            return bodycode + self.emit.emitNEGOP(bodytype, acc.frame), bodytype
        if ast.op =='!':
            return bodycode + self.emit.emitNOT(bodytype, acc.frame), bodytype
        
    def visitIntLiteral(self, ast, acc):
        if isinstance(acc,SubBody): return
        return self.emit.emitPUSHICONST(ast.value, acc.frame), IntType()

       
    def visitFloatLiteral(self, ast, acc):
        if isinstance(acc,SubBody): return
        return self.emit.emitPUSHFCONST(str(ast.value), acc.frame), FloatType()

    def visitBooleanLiteral(self, ast, acc):
        if isinstance(acc,SubBody): return
        return self.emit.emitPUSHICONST(str(ast.value), acc.frame), BoolType()
   
    
    def visitStringLiteral(self, ast, acc):
        if isinstance(acc,SubBody): return
        return self.emit.emitPUSHCONST(ast.value,StringType(), acc.frame), StringType()
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()
Example #8
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 = "MPClass"
        self.path = dir_
        self.emit = Emitter(self.path + "/" + self.className + ".j")

        self.listGlobalArray = []  # list(VarDecl: array declare global)

    def visitProgram(self, ast: Program, c):
        # 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)
        [self.visit(x, e) for x in ast.decl if type(x) is FuncDecl]

        # generate default constructor
        self.genMETHOD(FuncDecl(Id("<init>"), list(), list(), list(), None), c,
                       Frame("<init>", VoidType))
        # class init - static field
        self.genMETHOD(FuncDecl(Id("<clinit>"), list(), list(), list(), None),
                       c, Frame("<clinit>", VoidType))

        self.emit.emitEPILOG()
        return c

    def visitFuncDecl(self, ast: FuncDecl, o: SubBody):
        subctxt = o
        frame = Frame(ast.name.name, ast.returnType)
        self.genMETHOD(ast, subctxt.sym, frame)

    def visitVarDecl(self, ast: VarDecl, o: SubBody):
        subctxt = o
        frame = o.frame
        isGlobal = o.isGlobal
        varName = ast.variable.name
        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, decl: FuncDecl, o, frame: Frame):
        # o: Any

        glenv = o

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

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

        frame.enterScope(isProc)

        # 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)))
        varList = SubBody(frame, glenv)
        for x in decl.param:
            varList = self.visit(x, varList)
            if type(x.varType) is ArrayType:
                listParamArray.append(varList.sym[0])
        for x in decl.local:
            varList = self.visit(x, varList)
            if type(x.varType) is ArrayType:
                listLocalArray.append(varList.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))

        list(map(lambda x: self.visit(x, varList), decl.body))

        self.emit.printout(self.emit.emitLABEL(frame.getEndLabel(), frame))
        if isProc:
            self.emit.printout(self.emit.emitRETURN(VoidType(), frame))
        # else:
        #     self.emit.printout(self.emit.emitRETURN(returnType, frame))
        self.emit.printout(self.emit.emitENDMETHOD(frame))
        frame.exitScope()

    # ================   Visit Statements   =================
    # Param:    o: SubBody(frame, sym)

    def visitCallStmt(self, ast: CallStmt, o: SubBody):
        ctxt = o
        frame = ctxt.frame
        symbols = ctxt.sym
        self.handleCall(ast, frame, symbols, isStmt=True)

    def handleCall(self, ast, frame, symbols, isStmt=False):
        # ast: CallStmt | CallExpr

        sym = self.lookup(ast.method.name.lower(), symbols,
                          lambda x: x.name.lower())
        cname = sym.value.value
        ctype = sym.mtype
        paramTypes = ctype.partype

        paramsCode = ""
        idx = 0
        for x in ast.param:
            pCode, pType = self.visit(x, Access(frame, symbols, False, True))
            if type(paramTypes[idx]) is FloatType and type(pType) is IntType:
                pCode = pCode + self.emit.emitI2F(frame)
            if type(paramTypes[idx]) is ArrayType:
                pass
            paramsCode = paramsCode + pCode
            idx = idx + 1

        # if sym.name.lower() == "main": ctype = MType([ArrayPointerType(StringType())], VoidType())
        code = paramsCode + self.emit.emitINVOKESTATIC(cname + "/" + sym.name,
                                                       ctype, frame)
        if isStmt:
            self.emit.printout(code)
        else:
            return code, ctype.rettype

    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.emitGOTO(frame.getEndLabel(), frame))
        self.emit.printout(self.emit.emitRETURN(retType, frame))
        return True

    def visitIf(self, ast: If, o: SubBody):
        ctxt = o
        frame = ctxt.frame
        nenv = ctxt.sym
        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
        hasReturnStmt = True in [self.visit(x, o) for x in ast.elseStmt]
        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(x, o)
                                 for x in ast.thenStmt] and hasReturnStmt
        # End
        self.emit.printout(self.emit.emitLABEL(labelE, frame))
        return hasReturnStmt

    def visitWhile(self, ast: While, o: SubBody):
        ctxt = o
        frame = ctxt.frame
        nenv = ctxt.sym
        expCode, expType = self.visit(ast.exp, Access(frame, nenv, False,
                                                      True))

        labelS = frame.getNewLabel()  # label start
        labelE = frame.getNewLabel()  # label end
        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 visitFor(self, ast: For, o: SubBody):
        ctxt = o
        frame = ctxt.frame
        nenv = ctxt.sym

        exp1Code, _ = self.visit(ast.expr1, Access(frame, nenv, False, True))
        exp2Code, _ = self.visit(ast.expr2, Access(frame, nenv, False, True))
        lhsWCode, _ = self.visit(ast.id, Access(frame, nenv, True,
                                                True))  # Write code
        lhsRCode, _ = self.visit(ast.id, Access(frame, nenv, False,
                                                False))  # Read code

        labelS = frame.getNewLabel()  # label start
        labelE = frame.getNewLabel()  # label end

        # Init value
        self.emit.printout(exp1Code)
        self.emit.printout(lhsWCode)
        frame.enterLoop()
        # Loop
        self.emit.printout(self.emit.emitLABEL(labelS, frame))
        # 1. Condition
        self.emit.printout(lhsRCode)
        self.emit.printout(exp2Code)
        if ast.up:
            self.emit.printout(self.emit.emitIFICMPGT(labelE, frame))
        else:
            self.emit.printout(self.emit.emitIFICMPLT(labelE, frame))
        # 2. Statements
        hasReturnStmt = True in [self.visit(x, o) for x in ast.loop]
        self.emit.printout(self.emit.emitLABEL(frame.getContinueLabel(),
                                               frame))
        # 3. Update index
        self.emit.printout(lhsRCode)
        self.emit.printout(self.emit.emitPUSHICONST(1, frame))
        self.emit.printout(
            self.emit.emitADDOP('+' if ast.up else '-', IntType(), frame))
        self.emit.printout(lhsWCode)

        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 visitWith(self, ast: With, o: SubBody):
        ctxt = o
        frame = ctxt.frame
        nenv = ctxt.sym

        frame.enterScope(False)

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

        varList = SubBody(frame, nenv)
        for x in ast.decl:
            varList = self.visit(x, varList)
            if type(x.varType) is ArrayType:
                listLocalArray.append(varList.sym[0])

        self.emit.printout(self.emit.emitLABEL(frame.getStartLabel(), 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))

        list(map(lambda x: self.visit(x, varList), ast.stmt))
        self.emit.printout(self.emit.emitLABEL(frame.getEndLabel(), frame))
        frame.exitScope()

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

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

    def visitAssign(self, ast: Assign, o: SubBody):
        ctxt = o
        frame = ctxt.frame
        nenv = ctxt.sym

        # Pre-prepare for assign to array cell
        # stack: ..., arrayref, index, value -> ...
        # push 2 slot for arrayref and index, visit exp first
        isArray, _ = self.visit(
            ast.lhs, Access(frame, nenv, True, True, checkArrayType=True))
        if isArray: [frame.push() for i in range(0, 2)]

        # Visit LHS: Id || ArrayCell
        expCode, expType = self.visit(ast.exp, Access(frame, nenv, False,
                                                      True))
        lhsCode, lhsType = self.visit(ast.lhs, 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)]

    # ================   Visit Expression   =================
    # Param:    o: Access(frame, sym, isLeft, isFirst)
    # Return:   (code, type)

    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.lower(), symbols, lambda x: x.name.lower())

        # 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 visitCallExpr(self, ast: CallExpr, o: Access):
        ctxt = o
        frame = ctxt.frame
        symbols = ctxt.sym
        return self.handleCall(ast, frame, symbols, isStmt=False)

    def visitBinaryOp(self, ast: BinaryOp, o: Access):
        ctxt = o
        frame = ctxt.frame
        op = str(ast.op).lower()

        if op in ['orelse', 'andthen']:
            result = []
            lCode, lType = self.visit(ast.left, ctxt)
            result.append(lCode)

            labelF = frame.getNewLabel()  # eval is false
            labelT = frame.getNewLabel()  # eval is true

            if op == 'andthen':
                result.append(self.emit.emitIFFALSE(labelF, frame))  # false
            else:
                result.append(self.emit.emitIFTRUE(labelT, frame))  # true

            rCode, rType = self.visit(ast.right, ctxt)
            result.append(rCode)

            if op == 'andthen':
                result.append(self.emit.emitIFFALSE(labelF, frame))  # false
                result.append(self.emit.emitPUSHICONST("true",
                                                       frame))  # push true
                result.append(self.emit.emitGOTO(labelT, frame))  # go to true
                result.append(self.emit.emitLABEL(labelF, frame))  # push false
                result.append(self.emit.emitPUSHICONST("false", frame))
                result.append(self.emit.emitLABEL(labelT, frame))
            else:
                result.append(self.emit.emitIFTRUE(labelT, frame))  # true
                result.append(self.emit.emitPUSHICONST("false",
                                                       frame))  # push false
                result.append(self.emit.emitGOTO(labelF, frame))  # go to false
                result.append(self.emit.emitLABEL(labelT, frame))  # push true
                result.append(self.emit.emitPUSHICONST("true", frame))
                result.append(self.emit.emitLABEL(labelF, frame))

            return ''.join(result), BoolType()

        lCode, lType = self.visit(ast.left, ctxt)
        rCode, rType = self.visit(ast.right, ctxt)
        if StupidUtils.isOpForNumber(op):  # for number type
            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 == 'div':
                    return lCode + rCode + self.emit.emitDIV(frame), mType
                if op == 'mod':
                    return lCode + rCode + self.emit.emitMOD(frame), mType
            else:  # op to boolean: > <= = <>, ...
                return lCode + rCode + self.emit.emitREOP(op, mType,
                                                          frame), BoolType()
        else:  # for boolean type
            mType = BoolType()
            if op == 'or':
                return lCode + rCode + self.emit.emitOROP(frame), mType
            if op == 'and':
                return lCode + rCode + self.emit.emitANDOP(frame), mType

    def visitUnaryOp(self, ast: UnaryOp, o: Access):
        ctxt = o
        frame = ctxt.frame
        op = str(ast.op).lower()
        bCode, bType = self.visit(ast.body, ctxt)
        if op == '-': return bCode + self.emit.emitNEGOP(bType, frame), bType
        if op == 'not': return bCode + self.emit.emitNOT(bType, frame), bType

    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()
Example #9
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 = "MPClass"
        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)
        for x in ast.decl:
            e = self.visit(x, e)
        # generate default constructor
        self.genMETHOD(FuncDecl(Id("<init>"), list(), list(), list(), None), c,
                       Frame("<init>", VoidType))
        self.emit.emitEPILOG()
        return c

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

        isInit = consdecl.returnType is None
        isMain = consdecl.name.name == "main" and len(
            consdecl.param) == 0 and type(consdecl.returnType) is VoidType
        returnType = VoidType() if isInit else consdecl.returnType
        methodName = "<init>" if isInit else consdecl.name.name
        intype = [ArrayPointerType(StringType())] if isMain else list()
        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))

        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))
        list(map(lambda x: self.visit(x, SubBody(frame, glenv)), body))

        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 visitFuncDecl(self, ast, o):
        #ast: FuncDecl
        #o: Any

        subctxt = o
        frame = Frame(ast.name, ast.returnType)
        self.genMETHOD(ast, subctxt.sym, frame)
        return SubBody(None, [
            Symbol(ast.name, MType(list(), ast.returnType),
                   CName(self.className))
        ] + subctxt.sym)

    def visitCallStmt(self, ast, o):
        #ast: CallStmt
        #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))

    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):
        #ast: FloatLiteral
        #o: Any

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

    def visitBinaryOp(self, ast, o):
        ctxt = o
        frame = ctxt.frame
        lcode, ltype = self.visit(ast.left, ctxt)
        rcode, rtype = self.visit(ast.right, ctxt)
        if FloatType in (type(ltype), type(rtype)):
            if type(ltype) is IntType:
                lcode += self.emit.emitI2F(frame)
                ltype = FloatType()
            if type(rtype) is IntType:
                rcode += self.emit.emitI2F(frame)
                rtype = FloatType()
        if ast.op in ('+', '-'):
            return lcode + rcode + self.emit.emitADDOP(ast.op, ltype,
                                                       frame), ltype
        elif ast.op == '*':
            return lcode + rcode + self.emit.emitMULOP(ast.op, ltype,
                                                       frame), ltype
        else:
            if type(ltype) is IntType:
                lcode += self.emit.emitI2F(frame)
                rcode += self.emit.emitI2F(frame)
            return lcode + rcode + self.emit.emitMULOP("/", FloatType(),
                                                       frame), FloatType()
Example #10
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 = "MPClass"
        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(Frame(None, None), self.env)
        list_fun = []
        #visit de tao sym
        for x in ast.decl:
            if type(x) is FuncDecl:
                list_fun = list_fun + [x]
            else:
                e = self.visit(x, e)
        # print(','.join(str(x) for x in e.sym))
        # visit doi voi fun_decl de tao constructor
        # print(','.join(str(x) for x in list_fun))
        for x in list_fun:
            e = self.visit(x, e)

        # print(','.join(str(x) for x in e.sym))
        en_sym = e.sym
        for func in list_fun:
            # print(func)
            e.frame.name = func.name
            e.frame.returnType = func.returnType
            self.genMETHOD(func, en_sym, e.frame)

        # generate default constructor
        self.genMETHOD(FuncDecl(Id("<init>"), list(), list(), list(), None),
                       e.sym, Frame("<init>", VoidType))
        self.emit.emitEPILOG()
        return c

    def genMETHOD(self, consdecl, o, frame):
        #consdecl: FuncDecl
        #o: sym
        #frame: Frame
        # print(','.join(str(x) for x in o))
        isInit = consdecl.name.name.lower(
        ) == "<init>"  #or (consdecl.returnType is None)

        isMain = consdecl.name.name.lower() == "main" and len(
            consdecl.param) == 0 and type(consdecl.returnType) is VoidType
        # print(isMain)
        returnType = VoidType() if isInit else consdecl.returnType

        methodName = "<init>" if isInit else consdecl.name.name

        intype = None
        if isInit:  #constructor
            intype = list()

        elif isMain:  #main
            intype = [ArrayPointerType(StringType())
                      ]  #Note.........................................

        else:  #function and proceduce
            intype = [x.varType for x in consdecl.param]
            # print(','.join(str(x) for x in intype))

        mtype = MType(intype, returnType)

        if isMain or isInit:
            self.emit.printout(
                self.emit.emitMETHOD(methodName.lower(), mtype, not isInit,
                                     frame))
        else:
            self.emit.printout(
                self.emit.emitMETHOD(methodName, mtype, not isInit, frame))
        frame.enterScope(True)

        glenv = o  # sym

        # 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", ArrayType(None,None,StringType()), frame.getStartLabel(), frame.getEndLabel(), frame))
            self.emit.printout(
                self.emit.emitVAR(frame.getNewIndex(), "args",
                                  ArrayPointerType(StringType()),
                                  frame.getStartLabel(), frame.getEndLabel(),
                                  frame)
            )  # Note..............................................

        e = SubBody(frame, glenv)
        body = consdecl.body

        if not isInit:
            for x in consdecl.param:
                e = self.visit(x, e)

            for y in consdecl.local:
                e = self.visit(y, e)

        self.emit.printout(self.emit.emitLABEL(frame.getStartLabel(), frame))
        #Note...............................................
        #phan xu li array cho ki su tai nang bo qua!!!!
        #Note...............................................
        # 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))

        list(map(lambda x: self.visit(x, SubBody(frame, e.sym)), body))

        self.emit.printout(self.emit.emitLABEL(frame.getEndLabel(), frame))
        self.emit.printout(self.emit.emitRETURN(returnType, frame))
        self.emit.printout(self.emit.emitENDMETHOD(frame))
        frame.exitScope()

#####################################################Decl#################################################################

    def visitVarDecl(self, ast, o):
        subctxt = o
        frame_ = o.frame
        sym_ = o.sym

        check_local = (len(frame_.indexLocal) != 0)

        if check_local:  #bien local
            idex = frame_.getNewIndex()
            var_dec1 = self.emit.emitVAR(idex, ast.variable.name, ast.varType,
                                         frame_.getStartLabel(),
                                         frame_.getEndLabel(), frame_)
            sym_ = sym_ + [Symbol(ast.variable.name, ast.varType, idex)]
            self.emit.printout(var_dec1)

        else:  #bien global
            var_dec2 = self.emit.emitATTRIBUTE(ast.variable.name, ast.varType,
                                               False, None)
            sym_ = sym_ + [Symbol(ast.variable.name, ast.varType, None)]
            self.emit.printout(var_dec2)

        return SubBody(frame_, sym_)

    def visitFuncDecl(self, ast, o):
        #ast: FuncDecl
        #o: Any

        subctxt = o
        sym_ = o.sym
        frame_ = o.frame

        if frame_ is None:
            frame_ = Frame(ast.name, ast.returnType)

        else:
            frame_.name = ast.name
            frame_.returnType = ast.returnType

        return SubBody(
            frame_, sym_ + [
                Symbol(ast.name.name,
                       MType([x.varType for x in ast.param], ast.returnType),
                       CName(self.className))
            ])

#############################################Statement################################################

    def visitAssign(self, ast, o):
        subctxt = o
        sym_ = subctxt.sym
        frame_ = subctxt.frame
        # print(ast)
        #visit left : 1 la code , 2 la type
        # left_1 ,left_2 = self.visit(ast.lhs, Access(frame_,sym_,True,True)) danh cho arraycell ma array khong dung den
        #Note...........................
        #visit right:

        right_1, right_2 = self.visit(ast.exp,
                                      Access(frame_, sym_, False, False))
        #visit left:

        left_1, left_2 = self.visit(ast.lhs, Access(frame_, sym_, True, False))

        #ep kieu
        if (type(left_2) is FloatType) and (
                type(right_2) is
                IntType):  #note..................................
            right_1 = right_1 + self.emit.emitI2F(frame_)

        self.emit.printout(right_1 + left_1)

    def visitIf(self, ast, o):
        subctxt = o
        sym_ = subctxt.sym
        frame_ = subctxt.frame

        #exp_1 la code , exp_2 la kieu
        exp_1, exp_2 = self.visit(ast.expr, Access(frame_, sym_, False, False))
        self.emit.printout(exp_1)

        thenLabel = frame_.getNewLabel()
        elseLabel = frame_.getNewLabel()

        self.emit.printout(self.emit.emitIFTRUE(thenLabel, frame_))

        if ast.elseStmt != []:
            e_2 = SubBody(frame_, sym_)
            list(map(lambda x: self.visit(x, e_2), ast.elseStmt))

        self.emit.printout(self.emit.emitGOTO(elseLabel, frame_))
        self.emit.printout(self.emit.emitLABEL(thenLabel, frame_))

        e_1 = SubBody(frame_, sym_)
        list(map(lambda x: self.visit(x, e_1), ast.thenStmt))
        self.emit.printout(self.emit.emitLABEL(elseLabel, frame_))

    # def visitFor(self, ast, o):
    #     subctxt = o
    #     sym_ = subctxt.sym
    #     frame_ = subctxt.frame
    #     var = self.lookup(ast.id.name.lower(),sym_[::-1],lambda x: x.name.lower())
    #     # print(var)
    #     # print(";".join(str(x) for x in sym_))
    #     expr_ = None
    #     if ast.up == True:
    #         expr_ = Assign(ast.id,BinaryOp('-' ,ast.expr1 ,IntLiteral(1)))
    #     else:
    #         expr_ = Assign(ast.id,BinaryOp('+' ,ast.expr1 ,IntLiteral(1)))

    #     # print(expr_)
    #     esub = SubBody(frame_,sym_)
    #     self.visit(expr_, esub)
    #     # print(temp[0])
    #     esub.frame.enterLoop()
    #     continue_ = esub.frame.getContinueLabel()
    #     break_ = esub.frame.getBreakLabel()
    #     self.emit.printout(self.emit.emitLABEL(continue_,esub.frame))

    #     if var.value is not None:
    #         self.emit.printout(self.emit.emitREADVAR(var.name ,var.mtype ,var.value ,esub.frame))
    #     else:
    #         self.emit.printout(self.emit.emitGETSTATIC("io" + "." + var.name, var.mtype ,esub.frame))
    #     self.emit.printout(self.emit.emitPUSHICONST(1 ,esub.frame))

    #     if ast.up == True: #up
    #         self.emit.printout(self.emit.emitADDOP('+' ,IntType() ,esub.frame))
    #     else:
    #         self.emit.printout(self.emit.emitADDOP('-' ,IntType() ,esub.frame))

    #     if var.value is not None:
    #         self.emit.printout(self.emit.emitWRITEVAR(var.name ,var.mtype ,var.value ,esub.frame))
    #     else:
    #         self.emit.printout(self.emit.emitPUTSTATIC("io" + "." + var.name ,var.mtype , esub.frame))

    #     if var.value is not None:
    #         self.emit.printout(self.emit.emitREADVAR(var.name ,var.mtype ,var.value , esub.frame))
    #     else:
    #         self.emit.printout(self.emit.emitGETSTATIC("io" + "." + var.name ,var.mtype, esub.frame))

    #     exp_2,texp_2 = self.visit(ast.expr2,Access(esub.frame , esub.sym ,False,False))
    #     self.emit.printout(exp_2)

    #     if ast.up == True: #up
    #         self.emit.printout(self.emit.emitREOP('<=',IntType(), esub.frame))
    #     else:
    #         self.emit.printout(self.emit.emitREOP('>=',IntType(), esub.frame))
    #     self.emit.printout(self.emit.emitIFFALSE(break_, esub.frame))

    #     e = SubBody(esub.frame, esub.sym)
    #     list(map(lambda x: self.visit(x, e), ast.loop))

    #     self.emit.printout(self.emit.emitGOTO(continue_ ,esub.frame))
    #     self.emit.printout(self.emit.emitLABEL(break_ ,esub.frame))

    #     esub.frame.exitLoop()

    def visitFor(self, ast, o):

        subctxt = o
        frame_ = subctxt.frame
        sym_ = subctxt.sym

        labelfor = frame_.getNewLabel()

        frame_.enterLoop()
        accessT = Access(frame_, sym_, True, False)
        accessF = Access(frame_, sym_, False, False)

        expr1_, texpr1_ = self.visit(ast.expr1,
                                     Access(frame_, sym_, False, False))

        id_, tid_ = self.visit(ast.id, Access(frame_, sym_, True, False))

        self.emit.printout(expr1_)
        self.emit.printout(id_)

        self.emit.printout(self.emit.emitLABEL(labelfor, frame_))

        continue_ = frame_.getContinueLabel()
        break_ = frame_.getBreakLabel()

        if ast.up is True:
            id_, tid_ = self.visit(ast.id, Access(frame_, sym_, False, False))
            self.emit.printout(id_)
            expr2_, texpr2_ = self.visit(ast.expr2,
                                         Access(frame_, sym_, False, False))
            self.emit.printout(expr2_)
            self.emit.printout(self.emit.emitIFICMPGT(break_, frame_))

        else:
            id_, tid_ = self.visit(ast.id, Access(frame_, sym_, False, False))
            self.emit.printout(id_)
            expr2_, texpr2_ = self.visit(ast.expr2,
                                         Access(frame_, sym_, False, False))
            self.emit.printout(expr2_)
            self.emit.printout(self.emit.emitIFICMPLT(break_, frame_))

        list(map(lambda x: self.visit(x, o), ast.loop))
        self.emit.printout(self.emit.emitLABEL(continue_, frame_))

        if ast.up is True:
            expr_, texpr_ = self.visit(BinaryOp('+', ast.id, IntLiteral(1)),
                                       Access(frame_, sym_, False, False))
            id__, tid__ = self.visit(ast.id, Access(frame_, sym_, True, False))
            self.emit.printout(expr_)
            self.emit.printout(id__)

        else:
            expr_, texpr_ = self.visit(BinaryOp('-', ast.id, IntLiteral(1)),
                                       Access(frame_, sym_, False, False))
            id__, tid__ = self.visit(ast.id, Access(frame_, sym_, True, False))
            self.emit.printout(expr_)
            self.emit.printout(id__)

        self.emit.printout(self.emit.emitGOTO(labelfor, frame_))
        self.emit.printout(self.emit.emitLABEL(break_, frame_))

        frame_.exitLoop()

    def visitWhile(self, ast, o):
        subctxt = o
        sym_ = subctxt.sym
        frame_ = subctxt.frame

        frame_.enterLoop()
        continue_ = frame_.getContinueLabel()
        break_ = frame_.getBreakLabel()

        self.emit.printout(self.emit.emitLABEL(continue_, frame_))

        exp_, texp_ = self.visit(ast.exp, Access(frame_, sym_, False, False))
        self.emit.printout(exp_)
        self.emit.printout(self.emit.emitIFFALSE(break_, frame_))

        e_ = SubBody(frame_, sym_)
        list(map(lambda x: self.visit(x, e_), ast.sl))

        self.emit.printout(self.emit.emitGOTO(continue_, frame_))
        self.emit.printout(self.emit.emitLABEL(break_, frame_))

        frame_.exitLoop()

    def visitBreak(self, ast, o):
        subctxt = o
        sym_ = subctxt.sym
        frame_ = subctxt.frame

        break_ = frame_.getBreakLabel()
        self.emit.printout(self.emit.emitGOTO(break_, frame_))

    def visitContinue(self, ast, o):
        subctxt = o
        sym_ = subctxt.sym
        frame_ = subctxt.frame

        continue_ = frame_.getContinueLabel()
        self.emit.printout(self.emit.emitGOTO(continue_, frame_))

    def visitReturn(self, ast, o):
        subctxt = o
        sym_ = subctxt.sym
        frame_ = subctxt.frame

        if ast.expr is not None:
            exp_, texp_ = self.visit(ast.expr,
                                     Access(frame_, sym_, False, False))
            self.emit.printout(exp_)

            if (type(texp_) is IntType) and (type(frame_.returnType) is
                                             FloatType):
                self.emit.printout(self.emit.emitI2F(frame_))

        self.emit.printout(self.emit.emitGOTO(frame_.getEndLabel(), frame_))

    def visitCallStmt(self, ast, o):
        #ast: CallStmt
        #o: Any

        ctxt = o
        frame = ctxt.frame
        nenv = ctxt.sym
        # print(ast)
        #tim ham tuong ung de goi
        sym = self.lookup(ast.method.name.lower(), nenv[::-1],
                          lambda x: x.name.lower())
        # print(sym)
        # print(sym)
        cname = sym.value.value

        ctype = sym.mtype

        in_ = ("", [])
        idx_ = 0
        for x in ast.param:
            str1, typ1 = self.visit(x, Access(frame, nenv, False, False))
            in_ = (in_[0] + str1, in_[1] + [typ1])

            if (type(sym.mtype.partype[idx_]) is FloatType) and (type(typ1) is
                                                                 IntType):
                in_ = (in_[0] + self.emit.emitI2F(frame), in_[1])

            idx_ = idx_ + 1
        # print(str1,typ1)
        # print(in_[0])
        self.emit.printout(in_[0])
        self.emit.printout(
            self.emit.emitINVOKESTATIC(cname + "/" + sym.name, ctype, frame))

    def visitWith(self, ast, o):
        ctxt = o
        frame_ = ctxt.frame
        sym_ = []
        [sym_.append(x) for x in ctxt.sym]

        frame_.enterScope(False)
        self.emit.printout(self.emit.emitLABEL(frame_.getStartLabel(), frame_))

        e = SubBody(frame_, sym_)

        for x in ast.decl:
            e = self.visit(x, e)

        list(map(lambda x: self.visit(x, e), ast.stmt))
        #xoa bien cuc bo trong With
        e.sym = e.sym[:len(e.sym) - len(ast.decl)]

        self.emit.printout(self.emit.emitLABEL(frame_.getEndLabel(), frame_))
        frame_.exitScope()

##########################################Exp#############################################

    def visitArrayCell(self, ast, o):  #bo qua vi khong lam phan array!!!
        pass

    def visitId(self, ast, o):
        subctxt = o
        frame_ = subctxt.frame
        sym_ = subctxt.sym
        # print(','.join(str(x) for x in sym_))
        # print(ast)
        isLeft_ = subctxt.isLeft
        isFirst_ = subctxt.isFirst
        sym_id = self.lookup(ast.name.lower(), sym_[::-1],
                             lambda x: x.name.lower())
        # print(sym_id)
        #khong lam kieu array nen khong quan tam isFirst
        if not isLeft_ and not isFirst_:  #danh cho ve phai
            # print(1)
            if sym_id.value is None:
                return self.emit.emitGETSTATIC(
                    self.className + "." + sym_id.name, sym_id.mtype,
                    frame_), sym_id.mtype
            else:
                return self.emit.emitREADVAR(sym_id.name, sym_id.mtype,
                                             sym_id.value,
                                             frame_), sym_id.mtype

        if isLeft_ and not isFirst_:  #danh cho ve trai
            # print(2)
            if type(sym_id.mtype) is not ArrayType:
                if sym_id.value is None:
                    return self.emit.emitPUTSTATIC(
                        self.className + "." + sym_id.name, sym_id.mtype,
                        frame_), sym_id.mtype
                else:
                    return self.emit.emitWRITEVAR(sym_id.name, sym_id.mtype,
                                                  sym_id.value,
                                                  frame_), sym_id.mtype
            # else:
            #     return self.emit.emitASTORE(id_info.mtype.eleType,frame),id_info.mtype.eleType # load array khong dung den

    def visitCallExpr(self, ast, o):
        subctxt = o
        frame_ = subctxt.frame
        sym_ = subctxt.sym
        isLeft = subctxt.isLeft
        isFirst = subctxt.isFirst

        sym_id = self.lookup(ast.method.name.lower(), sym_[::-1],
                             lambda x: x.name.lower())
        cname = sym_id.value.value
        ctype = sym_id.mtype
        # print(sym_id)
        in_ = ("", [])
        idx_ = 0
        for x in ast.param:
            str1, typ1 = self.visit(x, Access(frame_, sym_, False, False))
            in_ = (in_[0] + str1, in_[1] + [typ1])

            if (type(sym_id.mtype.partype[idx_]) is
                    FloatType) and (type(typ1) is IntType):
                in_ = (in_[0] + self.emit.emitI2F(frame_), in_[1])

            idx_ = idx_ + 1
        # print(str1, typ1)
        # print(in_[0])
        return in_[0] + self.emit.emitINVOKESTATIC(
            cname + "/" + sym_id.name, ctype, frame_), sym_id.mtype.rettype

    def visitUnaryOp(self, ast, o):
        subctxt = o
        frame_ = subctxt.frame
        sym_ = subctxt.sym

        unexp_, tunexp_ = self.visit(ast.body,
                                     Access(frame_, sym_, False, False))

        if (type(tunexp_) is IntType) and (ast.op == '-'):
            return unexp_ + self.emit.emitNEGOP(IntType(), frame_), IntType()

        if (type(tunexp_) is FloatType) and (ast.op == '-'):
            return unexp_ + self.emit.emitNEGOP(FloatType(),
                                                frame_), FloatType()

        if ast.op.lower() == 'not':
            return unexp_ + self.emit.emitNOT(BoolType(), frame_), BoolType()

    def visitBinaryOp(self, ast, o):
        subctxt = o
        frame_ = subctxt.frame
        sym_ = subctxt.sym
        # print(ast)
        lexp_, tlexp_ = self.visit(ast.left, Access(frame_, sym_, False,
                                                    False))
        # print(lexp_, tlexp_)
        rexp_, trexp_ = self.visit(ast.right, Access(frame_, sym_, False,
                                                     False))
        # print(rexp_, trexp_)
        if (type(tlexp_) is BoolType) or (type(trexp_) is BoolType):
            if ast.op.lower() == 'and':
                return lexp_ + rexp_ + self.emit.emitANDOP(frame_), BoolType()

            elif ast.op.lower() == 'or':
                return lexp_ + rexp_ + self.emit.emitOROP(frame_), BoolType()

            elif ast.op.lower() == 'andthen':
                label1 = frame_.getNewLabel()
                label2 = frame_.getNewLabel()
                code_j = lexp_
                code_j += self.emit.emitPUSHICONST(1, frame_)
                code_j += self.emit.emitANDOP(frame_)
                code_j += self.emit.emitIFFALSE(label2, frame_)
                code_j += rexp_
                code_j += self.emit.emitPUSHICONST(1, frame_)
                code_j += self.emit.emitANDOP(frame_)
                code_j += self.emit.emitIFFALSE(label2, frame_)
                code_j += self.emit.emitPUSHICONST(1, frame_)
                code_j += self.emit.emitGOTO(label1, frame_)
                code_j += self.emit.emitLABEL(label2, frame_)
                code_j += self.emit.emitPUSHICONST(0, frame_)
                code_j += self.emit.emitLABEL(label1, frame_)

                return code_j, BoolType()

            elif ast.op.lower() == 'orelse':
                label1 = frame_.getNewLabel()
                label2 = frame_.getNewLabel()
                code_j = rexp_
                code_j += self.emit.emitPUSHICONST(0, frame_)
                code_j += self.emit.emitOROP(frame_)
                code_j += self.emit.emitIFTRUE(label1, frame_)
                code_j += rexp_
                code_j += self.emit.emitPUSHICONST(0, frame_)
                code_j += self.emit.emitOROP(frame_)
                code_j += self.emit.emitIFTRUE(label1, frame_)
                code_j += self.emit.emitPUSHICONST(0, frame_)
                code_j += self.emit.emitGOTO(str(label2), frame_)
                code_j += self.emit.emitLABEL(label1, frame_)
                code_j += self.emit.emitPUSHICONST(1, frame_)
                code_j += self.emit.emitLABEL(label2, frame_)
                return code_j, BoolType()

            elif ast.op in ['=', '<>']:
                return lexp_ + rexp_ + self.emit.emitREOP(
                    ast.op, BoolType(), frame_), BoolType()

        if (type(tlexp_) is IntType) and (type(trexp_) is IntType):
            # print(1)
            if ast.op in ['+', '-']:
                return lexp_ + rexp_ + self.emit.emitADDOP(
                    ast.op, IntType(), frame_), IntType()

            elif ast.op == '*':
                return lexp_ + rexp_ + self.emit.emitMULOP(
                    ast.op, IntType(), frame_), IntType()

            elif ast.op.lower() == 'div':
                return lexp_ + rexp_ + self.emit.emitDIV(frame_), IntType()

            elif ast.op.lower() == 'mod':
                # print(1)
                return lexp_ + rexp_ + self.emit.emitMOD(frame_), IntType()

            elif ast.op == '/':
                lexp_ = lexp_ + self.emit.emitI2F(frame_)
                rexp_ = rexp_ + self.emit.emitI2F(frame_)
                return lexp_ + rexp_ + self.emit.emitMULOP(
                    ast.op, FloatType(), frame_), FloatType()

            elif ast.op in ['<', '<=', '>', '>=', '<>', '=']:
                # print(1)
                return lexp_ + rexp_ + self.emit.emitREOP(
                    ast.op, IntType(), frame_), BoolType()

        #either left or right is FLoat
        if type(tlexp_) is IntType:
            lexp_ = lexp_ + self.emit.emitI2F(frame_)

        if type(trexp_) is IntType:
            rexp_ = rexp_ + self.emit.emitI2F(frame_)

        if ast.op in ['+', '-']:
            return lexp_ + rexp_ + self.emit.emitADDOP(ast.op, FloatType(),
                                                       frame_), FloatType()

        elif ast.op in ['*', '/']:
            return lexp_ + rexp_ + self.emit.emitMULOP(ast.op, FloatType(),
                                                       frame_), FloatType()

        elif ast.op in ['<', '<=', '>', '>=', '<>', '=']:
            return lexp_ + rexp_ + self.emit.emitFREOP(ast.op, FloatType(),
                                                       frame_), FloatType()

    # def visitBinaryOp(self, ast, o):
    #     #o: any
    #     #ast.op:string: AND THEN => andthen; OR ELSE => orelse; other => keep it
    #     #ast.left:Expr
    #     #ast.right:Expr

    #     ctxt = o
    #     frame = ctxt.frame
    #     nenv = ctxt.sym

    #     leftOprandstr, typL = self.visit(ast.left, Access(frame, nenv, False, False))
    #     rightOperandstr, typR = self.visit(ast.right, Access(frame, nenv, False, False))

    #     if type(typL) == type(typR):
    #         if type(typL) is BoolType:
    #             if ast.op.lower() == 'and':
    #                 return leftOprandstr + rightOperandstr + self.emit.emitANDOP(frame), BoolType()
    #             elif ast.op.lower() == 'or':
    #                 return leftOprandstr + rightOperandstr + self.emit.emitOROP(frame), BoolType()
    #             elif ast.op.lower() == 'andthen':
    #                 # right, typR1 = self.visit(BooleanLiteral(False), o)
    #                 # lst = leftOprandstr + right + self.emit.emitREOP('==', IntType(), frame)
    #                 lst = list()
    #                 label1 = frame.getNewLabel()
    #                 label2 = frame.getNewLabel()
    #                 lst.append(leftOprandstr)
    #                 lst.append(self.emit.emitIFFALSE(label1, frame))
    #                 lst.append(rightOperandstr)
    #                 lst.append(self.emit.emitIFFALSE(label1, frame))
    #                 lst.append(self.emit.emitPUSHICONST("true", frame))
    #                 lst.append(self.emit.emitGOTO(label2,frame))
    #                 lst.append(self.emit.emitLABEL(label1,frame))
    #                 lst.append(self.emit.emitPUSHICONST("false", frame))
    #                 lst.append(self.emit.emitLABEL(label2,frame))
    #                 frame.pop()
    #                 return ''.join(lst), BoolType()
    #             elif ast.op.lower() == 'orelse':
    #                 # right, typR1 = self.visit(BooleanLiteral(True), o)
    #                 # lst = leftOprandstr + right + self.emit.emitREOP('==', IntType(), frame)
    #                 lst = list()
    #                 label1 = frame.getNewLabel()
    #                 label2 = frame.getNewLabel()
    #                 lst.append(leftOprandstr)
    #                 lst.append(self.emit.emitIFTRUE(label1, frame))
    #                 lst.append(rightOperandstr)
    #                 lst.append(self.emit.emitIFTRUE(label1, frame))
    #                 lst.append(self.emit.emitPUSHICONST("false", frame))
    #                 lst.append(self.emit.emitGOTO(label2,frame))
    #                 lst.append(self.emit.emitLABEL(label1,frame))
    #                 lst.append(self.emit.emitPUSHICONST("true", frame))
    #                 lst.append(self.emit.emitLABEL(label2,frame))
    #                 frame.pop()
    #                 return ''.join(lst), BoolType()
    #         elif type(typL) is IntType:
    #             if ast.op in ['+', '-']:
    #                 return leftOprandstr + rightOperandstr + self.emit.emitADDOP(ast.op, IntType(), frame), IntType()
    #             elif ast.op == '*':
    #                 return leftOprandstr + rightOperandstr + self.emit.emitMULOP(ast.op, IntType(), frame), IntType()
    #             elif ast.op.lower() == 'div':
    #                 return leftOprandstr + rightOperandstr + self.emit.emitDIV(frame), IntType()
    #             elif ast.op.lower() == 'mod':
    #                 return leftOprandstr + rightOperandstr + self.emit.emitMOD(frame), IntType()
    #             elif ast.op in ['<', '<=', '>', '>=']:
    #                 return leftOprandstr + rightOperandstr + self.emit.emitREOP(ast.op, IntType(), frame), BoolType()
    #             elif ast.op == '<>':
    #                 return leftOprandstr + rightOperandstr + self.emit.emitREOP('!=', IntType(), frame), BoolType()
    #             elif ast.op == '=':
    #                 return leftOprandstr + rightOperandstr + self.emit.emitREOP('==', IntType(), frame), BoolType()
    #             elif ast.op == '/':
    #                 leftOprandstr += self.emit.emitI2F(frame)
    #                 rightOperandstr += self.emit.emitI2F(frame)
    #                 return leftOprandstr + rightOperandstr + self.emit.emitMULOP(ast.op, FloatType(), frame), FloatType()
    #         elif type(typL) is FloatType:
    #             if ast.op in ['+', '-']:
    #                 return leftOprandstr + rightOperandstr + self.emit.emitADDOP(ast.op, FloatType(), frame), FloatType()
    #             elif ast.op == '*':
    #                 return leftOprandstr + rightOperandstr + self.emit.emitMULOP(ast.op, FloatType(), frame), FloatType()
    #             elif ast.op == '/':
    #                 return leftOprandstr + rightOperandstr + self.emit.emitMULOP(ast.op, FloatType(), frame), FloatType()
    #             elif ast.op in ['<', '<=', '>', '>=']:
    #                 return self.emit.emitFREOP(ast.op, leftOprandstr, rightOperandstr, frame), BoolType()
    #             elif ast.op == '<>':
    #                 return self.emit.emitFREOP('!=', leftOprandstr, rightOperandstr, frame), BoolType()
    #             elif ast.op == '=':
    #                 return self.emit.emitFREOP('==', leftOprandstr, rightOperandstr, frame), BoolType()
    #     else:
    #         if ast.op in ['+', '-']:
    #             if type(typL) is FloatType and type(typR) is IntType:
    #                 return leftOprandstr + rightOperandstr + self.emit.emitI2F(frame) + self.emit.emitADDOP(ast.op, FloatType(), frame), FloatType()
    #             elif type(typL) is IntType and type(typR) is FloatType:
    #                 return leftOprandstr + self.emit.emitI2F(frame) + rightOperandstr + self.emit.emitADDOP(ast.op, FloatType(), frame), FloatType()
    #         elif ast.op == '*':
    #             if type(typL) is FloatType and type(typR) is IntType:
    #                 return leftOprandstr + rightOperandstr + self.emit.emitI2F(frame) + self.emit.emitMULOP(ast.op, FloatType(), frame), FloatType()
    #             elif type(typL) is IntType and type(typR) is FloatType:
    #                 return leftOprandstr + self.emit.emitI2F(frame) + rightOperandstr + self.emit.emitMULOP(ast.op, FloatType(), frame), FloatType()
    #         else:
    #             if type(typL) is IntType:
    #                 leftOprandstr += self.emit.emitI2F(frame)
    #             if type(typR) is IntType:
    #                 rightOperandstr += self.emit.emitI2F(frame)
    #             if ast.op == '/':
    #                 return leftOprandstr + rightOperandstr + self.emit.emitMULOP(ast.op, FloatType(), frame), FloatType()
    #             elif ast.op in ['<', '<=', '>', '>=']:
    #                 return self.emit.emitFREOP(ast.op, leftOprandstr, rightOperandstr, frame), BoolType()
    #             elif ast.op == '<>':
    #                 return self.emit.emitFREOP('!=', leftOprandstr, rightOperandstr, frame), BoolType()
    #             elif ast.op == '=':
    #                 return self.emit.emitFREOP('==', leftOprandstr, rightOperandstr, frame), BoolType()

    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(
            '"' + ast.value + '"', StringType(),
            frame), StringType()  #Note......................................
class CodeGenVisitor(BaseVisitor, Utils):
  def __init__(self, astTree, env, dir_):
    #astTree: AST
    #env: List[Symbol] built in function from codegenerator
    #dir_: File

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

  def getDeclType(self, varType):
    return ArrayPointerType(varType.eleType) if type(varType) is ArrayType else varType

  def visitProgram(self, ast, c):
    #ast: Program
    #c: Any
    
    # print(ast)
    self.emit.printout(self.emit.emitPROLOG(self.className, "java.lang.Object"))
    staticDeclList = self.env
    for decl in ast.decl:
      if type(decl) is FuncDecl:
        paramType = [paramTemp.varType for paramTemp in decl.param]
        staticDeclList = [Symbol(decl.name.name, MType(paramType, decl.returnType), CName(self.className))] + staticDeclList
      else:
        varSym = self.visit(decl, (SubBody(None, self.env), "global"))
        staticDeclList = [varSym] + staticDeclList
    env = SubBody(None, staticDeclList)
    self.genMETHOD(FuncDecl(Id("<init>"), list(), None, Block(list())), c, Frame("<init>", VoidType))
    if self.globalArrayVarList:
      self.genMETHOD(FuncDecl(Id("<clinit>"), list(), None, Block(list())), c, Frame("<clinit>", VoidType))
    list(map(lambda func: self.visit(func, env), filter(lambda decl: type(decl) is FuncDecl, ast.decl)))
    self.emit.emitEPILOG()
    return c

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

    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
    inType = [ArrayPointerType(StringType())] if isMain else [self.getDeclType(decl.varType) for decl in consdecl.param]
    mtype = MType(inType, returnType)

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

    frame.enterScope(True)

    # 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))
    varList = SubBody(frame, globalEnv)
    for param in consdecl.param:
      varList = self.visit(param, (varList, "parameter"))
      if type(param.varType) is ArrayType:
        name = varList.sym[0].name
        idx = varList.sym[0].value.value
        varType = varList.sym[0].mtype
        self.emit.printout(self.emit.emitVAR(idx, name, varType, frame.getStartLabel(), frame.getEndLabel(), frame))

    self.emit.printout(self.emit.emitLABEL(frame.getStartLabel(), frame))
    # Generate code for statements
    body = consdecl.body
    if isInit:
      self.emit.printout(self.emit.emitREADVAR("this", ClassType(self.className), 0, frame))
      self.emit.printout(self.emit.emitINVOKESPECIAL(frame))
    if isClassInit:
      for arrayVar in self.globalArrayVarList:
        # emitINITNEWARRAY(self, name, frame, varType, varSize)
        self.emit.printout(self.emit.emitINITNEWARRAY(self.className + "." + arrayVar.variable, frame, arrayVar.varType.eleType, arrayVar.varType.dimen))
    for member in body.member:
      if type(member) is VarDecl:
        varList = self.visit(member, (varList, "local"))
        if type(member.varType) is ArrayType:
          idx = varList.sym[0].value.value
          varType = varList.sym[0].mtype.eleType
          self.emit.printout(self.emit.emitINITNEWARRAY(idx, frame, varType, member.varType.dimen, "local"))
      else:
        self.visit(member, varList)
        if frame.getStackSize() and type(member) is Expr:
          self.emit.printout(self.emit.emitPOP(frame))
          
    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 visitFuncDecl(self, ast, subGlobal):
    subGlobalTemp = subGlobal
    frame = Frame(ast.name.name, ast.returnType)
    self.genMETHOD(ast, subGlobalTemp.sym, frame)

  def visitVarDecl(self, ast, trackSubBody):
    subctxt = trackSubBody[0]
    frame = subctxt.frame
    varName = ast.variable
    varType = ast.varType
    if trackSubBody[1] == "global":
      self.emit.printout(self.emit.emitATTRIBUTE(varName, self.getDeclType(varType), False, ""))
      if type(ast.varType) is ArrayType: 
        self.globalArrayVarList.append(ast)
      return Symbol(varName, varType)
    elif trackSubBody[1] == "parameter": # param
      idx = frame.getNewIndex()
      self.emit.printout(self.emit.emitVAR(idx, varName, self.getDeclType(varType), frame.getStartLabel(), frame.getEndLabel(), frame))
      return SubBody(frame, [Symbol(varName, varType, Index(idx))] + subctxt.sym)      
    else: # local
      idx = frame.getNewIndex()
      localVarLabel = frame.getNewLabel()
      self.emit.printout(self.emit.emitVAR(idx, varName, self.getDeclType(varType), localVarLabel, frame.getEndLabel(), frame))
      self.emit.printout(self.emit.emitLABEL(localVarLabel, frame))
      return SubBody(frame, [Symbol(varName, varType, Index(idx))] + subctxt.sym)

  # statement
  def visitCallExpr(self, ast, trackSubBody):
    ctxt = trackSubBody
    frame = ctxt.frame
    symLst = ctxt.sym
    sym = self.lookup(ast.method.name, symLst, lambda x: x.name)
    cname = sym.value.value
    ctype = sym.mtype

    paramsCode = ""
    for i in range(len(ast.param)):
      paramCode, paramType = self.visit(ast.param[i], Access(frame, symLst, False, True))
      if type(sym.mtype.partype[i]) is FloatType and type(paramType) is IntType:
        paramCode = paramCode + self.emit.emitI2F(frame)
      paramsCode += paramCode
    result = paramsCode + self.emit.emitINVOKESTATIC(cname + "/" + sym.name, ctype, frame)
    if type(ctxt) is SubBody: self.emit.printout(result)
    else: return result, ctype.rettype

  def visitBlock(self, ast, trackSubBody):
    ctxt = trackSubBody
    frame = ctxt.frame
    symLst = ctxt.sym

    frame.enterScope(False)
    self.emit.printout(self.emit.emitLABEL(frame.getStartLabel(), frame))
    varList = SubBody(frame, symLst)
    # Generate code for statements
    for member in ast.member:
      if type(member) is VarDecl:
        varList = self.visit(member, (varList, "local"))
        if type(member.varType) is ArrayType:
          idx = varList.sym[0].value.value
          varType = varList.sym[0].mtype.eleType
          self.emit.printout(self.emit.emitINITNEWARRAY(idx, frame, varType, member.varType.dimen, "local"))
      else:
        self.visit(member, varList)
        if frame.getStackSize() and type(member) is Expr:
          self.emit.printout(self.emit.emitPOP(frame))
    self.emit.printout(self.emit.emitLABEL(frame.getEndLabel(), frame))
    frame.exitScope()

  def visitIf(self, ast, trackSubBody):
    ctxt = trackSubBody
    frame = ctxt.frame
    symLst = ctxt.sym

    labelTrue = frame.getNewLabel()
    labelEnd = frame.getNewLabel()
    exprCode, exprType = self.visit(ast.expr, Access(frame, symLst, False, True))

    self.emit.printout(exprCode)
    self.emit.printout(self.emit.emitIFTRUE(labelTrue, frame))
    if ast.elseStmt is not None:
      self.visit(ast.elseStmt, ctxt)
    self.emit.printout(self.emit.emitGOTO(labelEnd, frame))
    self.emit.printout(self.emit.emitLABEL(labelTrue, frame))
    self.visit(ast.thenStmt, ctxt)
    self.emit.printout(self.emit.emitLABEL(labelEnd, frame))

  def visitDowhile(self, ast, trackSubBody):
    ctxt = trackSubBody
    frame = ctxt.frame
    symLst = ctxt.sym
    
    labelStart = frame.getNewLabel()
    labelEnd = frame.getNewLabel()
    expCode, expType = self.visit(ast.exp, Access(frame, symLst, False, True))

    frame.enterLoop()
    self.emit.printout(self.emit.emitLABEL(labelStart, frame))
    list(map(lambda x: self.visit(x, ctxt), ast.sl))
    self.emit.printout(self.emit.emitLABEL(frame.getContinueLabel(), frame))
    self.emit.printout(expCode)
    self.emit.printout(self.emit.emitIFFALSE(labelEnd, frame))
    self.emit.printout(self.emit.emitGOTO(labelStart, frame))
    self.emit.printout(self.emit.emitLABEL(frame.getBreakLabel(), frame))
    self.emit.printout(self.emit.emitLABEL(labelEnd, frame))
    frame.exitLoop()

  def visitFor(self, ast, trackSubBody):
    ctxt = trackSubBody
    frame = ctxt.frame
    symLst = ctxt.sym
    
    labelStart = frame.getNewLabel()
    labelEnd = frame.getNewLabel()
    expr1Code, expr1Type = self.visit(ast.expr1, Access(frame, symLst, False, True))
    expr2Code, expr2Type = self.visit(ast.expr2, Access(frame, symLst, False, True))
    expr3Code, expr3Type = self.visit(ast.expr3, Access(frame, symLst, False, True))

    self.emit.printout(expr1Code)
    self.emit.printout(self.emit.emitPOP(frame))
    frame.enterLoop()
    self.emit.printout(self.emit.emitLABEL(labelStart, frame))
    self.emit.printout(expr2Code)
    self.emit.printout(self.emit.emitIFFALSE(labelEnd, frame))
    self.visit(ast.loop, ctxt)
    self.emit.printout(self.emit.emitLABEL(frame.getContinueLabel(), frame))
    self.emit.printout(expr3Code)
    self.emit.printout(self.emit.emitPOP(frame))
    self.emit.printout(self.emit.emitGOTO(labelStart, frame))
    self.emit.printout(self.emit.emitLABEL(frame.getBreakLabel(), frame))
    self.emit.printout(self.emit.emitLABEL(labelEnd, frame))
    frame.exitLoop()

  def visitBreak(self, ast, trackSubBody):
    ctxt = trackSubBody
    frame = ctxt.frame
    self.emit.printout(self.emit.emitGOTO(frame.getBreakLabel(), frame))

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

  def visitReturn(self, ast, trackSubBody):
    ctxt = trackSubBody
    frame = ctxt.frame
    symLst = ctxt.sym
    returnType = frame.returnType
    if not type(returnType) is VoidType:
      exprCode, exprType = self.visit(ast.expr, Access(frame, symLst, False, True))
      if type(returnType) is FloatType and type(exprType) is IntType:
        exprCode = exprCode + self.emit.emitI2F(frame)
      self.emit.printout(exprCode)
    self.emit.printout(self.emit.emitRETURN(returnType, frame))

  # expression
  def visitBinaryOp(self, ast, o):
    ctxt = o
    frame = ctxt.frame
    op = ast.op
    symLst = ctxt.sym
    if op in ['+', '-', '*', '/', '%', '<', '<=', '>', '>=', '==', '!=']:
      leftCode, leftType = self.visit(ast.left, Access(frame, symLst, False, False))
      rightCode, rightType = self.visit(ast.right, Access(frame, symLst, False, False))
      returnType = FloatType() if FloatType in [type(leftType), type(rightType)] else IntType()
      if type(leftType) is IntType and type(returnType) != type(leftType): 
        leftCode = leftCode + self.emit.emitI2F(frame)
      if type(rightType) is IntType and type(returnType) != type(rightType): 
        rightCode = rightCode + self.emit.emitI2F(frame)
      if op in ['+', '-', '*', '/', '%']:
        if op in ['+', '-']:
          return leftCode + rightCode + self.emit.emitADDOP(op, returnType, frame), returnType
        elif op in ['*', '/']:
          return leftCode + rightCode + self.emit.emitMULOP(op, returnType, frame), returnType
        elif op == '%':
          return leftCode + rightCode + self.emit.emitMOD(frame), IntType()
      else:
        return leftCode + rightCode + self.emit.emitREOP(op, returnType, frame), BoolType()
    elif op in ['||', '&&']:
      # short-circuit
      preOp = ctxt.labelShortCircuit[0] if type(o) is Access else None
      labelShortCircuit = ctxt.labelShortCircuit[1] if type(o) is Access else None
      if op != preOp:
        labelShortCircuit = frame.getNewLabel()
      leftCode, leftType = self.visit(ast.left, Access(frame, symLst, False, False, (op, labelShortCircuit)))
      rightCode, rightType = self.visit(ast.right, Access(frame, symLst, False, False, (op, labelShortCircuit)))
      if op != preOp:
        if op == '||':
          return leftCode + self.emit.emitDUP(frame) + self.emit.emitIFTRUE(labelShortCircuit, frame) + self.emit.emitPOP(frame) + rightCode + self.emit.emitLABEL(labelShortCircuit, frame), BoolType()
        else:
          return leftCode + self.emit.emitDUP(frame) + self.emit.emitIFFALSE(labelShortCircuit, frame) + self.emit.emitPOP(frame) + rightCode + self.emit.emitLABEL(labelShortCircuit, frame), BoolType()          
      else:
        if op == '||':
          return leftCode + self.emit.emitDUP(frame) + self.emit.emitIFTRUE(labelShortCircuit, frame) + self.emit.emitPOP(frame) + rightCode, BoolType()
        else:
          return leftCode + self.emit.emitDUP(frame) + self.emit.emitIFFALSE(labelShortCircuit, frame) + self.emit.emitPOP(frame) + rightCode, BoolType()
    else:
      rightCode, rightType = self.visit(ast.right, Access(frame, symLst, False, False))
      leftCode, leftType = self.visit(ast.left, Access(frame, symLst, True, False))
      isArrayType = type(leftType) in [ArrayType, ArrayPointerType]
      if isArrayType:
        leftType = leftType.eleType
      if type(leftType) is FloatType and type(rightType) is IntType:
        rightCode = rightCode + self.emit.emitI2F(frame)
      if type(ctxt) is SubBody:
        if isArrayType:
          returnOp = leftCode[0] + rightCode + leftCode[1]
          [frame.push() for i in range(2)]
        else:
          returnOp = rightCode + leftCode
        self.emit.printout(returnOp)
        if isArrayType: [frame.pop() for i in range(2)]
      else:
        if isArrayType:
          returnOp = rightCode
          returnOp += leftCode[0] + rightCode + leftCode[1]
          [frame.push() for i in range(2)] # keep arr for right to assign
        else:
          returnOp = rightCode + self.emit.emitDUP(frame) + leftCode
        returnType = leftType
        return returnOp, returnType

  def visitUnaryOp(self, ast, o):
    ctxt = o
    frame = ctxt.frame
    symLst = ctxt.sym
    op = ast.op
    binaryCode, binaryType = self.visit(ast.body, Access(frame, symLst, False, True))
    if op == '-': return binaryCode + self.emit.emitNEGOP(binaryType, frame), binaryType
    if op == '!': return binaryCode + self.emit.emitNOT(binaryType, frame), binaryType

  def visitId(self, ast, ctxtId):
    ctxt = ctxtId
    frame = ctxt.frame
    symLst = ctxt.sym
    sym = self.lookup(ast.name, symLst, lambda x: x.name)
    varType = self.getDeclType(sym.mtype)
    if type(ctxt) is not SubBody:
      isLeft = ctxt.isLeft
      isFirst = ctxt.isFirst
      if isLeft: frame.push()
      if sym.value is None:
        if isLeft and type(sym.mtype) not in [ArrayType, ArrayPointerType]: returnCode = self.emit.emitPUTSTATIC(self.className + "." + sym.name, varType, frame)
        else: returnCode = self.emit.emitGETSTATIC(self.className + "." + sym.name, varType, frame)
      else:
        if isLeft and type(sym.mtype) not in [ArrayType, ArrayPointerType]: returnCode = self.emit.emitWRITEVAR(sym.name, varType, sym.value.value, frame)
        else: returnCode = self.emit.emitREADVAR(sym.name, varType, sym.value.value, frame)
    else:
      if sym.value is None:
        returnCode = self.emit.emitGETSTATIC(self.className + "." + sym.name, varType, frame)
      else:
        returnCode = self.emit.emitREADVAR(sym.name, varType, sym.value.value, frame)
    return returnCode, sym.mtype

  def visitArrayCell(self, ast, o):
    ctxt = o
    frame = ctxt.frame
    symLst = ctxt.sym
    isLeft = ctxt.isLeft
    isFirst = ctxt.isFirst
    
    arrCode, arrType = self.visit(ast.arr, Access(frame, symLst, True, True))
    idxCode, idxType = self.visit(ast.idx, Access(frame, symLst, False, True))
    if isLeft:
      return [arrCode + idxCode, self.emit.emitASTORE(arrType.eleType, frame)], arrType
    # stack to keep right code if assign in another op
    [frame.push() for i in range(2)]
    return arrCode + idxCode + self.emit.emitALOAD(arrType.eleType, frame), arrType.eleType

  def visitIntLiteral(self, ast, o):
    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), frame), BoolType()

  def visitStringLiteral(self, ast, o):
    ctxt = o
    frame = ctxt.frame
    return self.emit.emitPUSHCONST(ast.value, StringType(), frame), StringType()
Example #12
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 = "MPClass"
        self.path = dir_
        self.emit = Emitter(self.path + "/" + self.className + ".j")

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

        isGlobalArray = o.isGlobalArray
        lstDeclArray = o.lstDeclArray

        if isGlobalArray:
            # Constructor cho khai bao bien global la array
            returnType = VoidType()
            methodName = "<clinit>"
            intype = list()
            mtype = MType(intype, returnType)
            self.emit.printout(
                self.emit.emitMETHOD(methodName, mtype, True, frame))
            frame.enterScope(True)
            for x in lstDeclArray:
                lexeme = self.className + "." + x.variable.name
                self.emit.printout(
                    self.emit.emitINITARRAY(lexeme, x.varType, frame))
            self.emit.printout(self.emit.emitRETURN(returnType, frame))
            self.emit.printout(self.emit.emitENDMETHOD(frame))
            # if frame.getStackSize() != 0:
            #     print(methodName)
            frame.exitScope()
        else:
            isInit = consdecl.returnType is None
            isMain = consdecl.name.name == "main" and len(
                consdecl.param) == 0 and type(consdecl.returnType) is VoidType
            returnType = VoidType() if isInit else consdecl.returnType
            methodName = "<init>" if isInit else consdecl.name.name
            intype = [ArrayPointerType(StringType())] if isMain else list(
                map(lambda x: x.varType, consdecl.param))
            mtype = MType(intype, returnType)
            self.emit.printout(
                self.emit.emitMETHOD(methodName, mtype, not isInit, frame))
            frame.enterScope(True)
            glenv = o.sym
            # Generate code for parameter declarations

            # danh sach cac array trong parameter declarations
            lstArrayParam = list()

            if isInit:
                idx = frame.getNewIndex()
                self.emit.printout(
                    self.emit.emitVAR(idx, "this", ClassType(self.className),
                                      frame.getStartLabel(),
                                      frame.getEndLabel(), frame))
            elif isMain:
                idx = frame.getNewIndex()
                self.emit.printout(
                    self.emit.emitVAR(idx, "args",
                                      ArrayPointerType(StringType()),
                                      frame.getStartLabel(),
                                      frame.getEndLabel(), frame))
                glenv.insert(
                    0,
                    Symbol("args", ArrayPointerType(StringType()), Index(idx)))
            else:
                # Sinh ma cho parameter declarations
                e = SubBody(frame, glenv)
                for x in consdecl.param:
                    e = self.visit(x, e)
                    glenv = e.sym
                    if type(x.varType) is ArrayType:
                        lstArrayParam.append(glenv[0])
            if not isInit:
                # Sinh ma cho local declarations
                e = SubBody(frame, glenv)
                for x in consdecl.local:
                    e = self.visit(x, e)
                    glenv = e.sym
                    if type(x.varType) is ArrayType:
                        idx = glenv[0].value.value
                        self.emit.printout(
                            self.emit.emitINITARRAY(idx, x.varType, frame))

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

            # Sao chep array tu param vao mot array moi
            for x in lstArrayParam:
                self.emit.printout(
                    self.emit.emitCOPPYARRAY(x.value.value, x.mtype, frame))

            if isInit:
                self.emit.printout(
                    self.emit.emitREADVAR("this", ClassType(self.className), 0,
                                          frame))
                self.emit.printout(self.emit.emitINVOKESPECIAL(frame))

            # visit than ham
            list(
                map(lambda x: self.visit(x, SubBody(frame, glenv)),
                    consdecl.body))
            self.emit.printout(self.emit.emitLABEL(frame.getEndLabel(), frame))
            self.emit.printout(self.emit.emitRETURN(returnType, frame))
            self.emit.printout(self.emit.emitENDMETHOD(frame))
            # if frame.getStackSize() != 0:
            #     print(methodName)
            frame.exitScope()

    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 cac bien array trong global
        lstArray = list()

        ## Them global declarations vao self.env
        for x in ast.decl:
            if type(x) is VarDecl:
                e = self.visit(x, e)
                self.env = e.sym
                if type(x.varType) is ArrayType:
                    lstArray.append(x)
            else:
                self.env.insert(
                    0,
                    Symbol(
                        x.name.name,
                        MType(list(map(lambda y: y.varType, x.param)),
                              x.returnType), CName(self.className)))

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

        # generate default constructor
        self.genMETHOD(FuncDecl(Id("<init>"), list(), list(), list(), None),
                       MyClass(c, False, list()), Frame("<init>", VoidType))

        # Constructor cho array
        if len(lstArray) > 0:
            self.genMETHOD(
                FuncDecl(Id("<clinit>"), list(), list(), list(), None),
                MyClass(c, True, lstArray), Frame("<clinit>", VoidType))

        self.emit.emitEPILOG()
        return c

    # Declaration
    def visitFuncDecl(self, ast, o):
        #o: SubBody
        #ast.name: Id
        #ast.param: list(VarDecl)
        #ast.returnType: Type => VoidType for Procedure
        #ast.local: list(VarDecl)
        #ast.body: list(Stmt)

        subctxt = o
        frame = Frame(ast.name.name, ast.returnType)
        self.genMETHOD(ast, MyClass(subctxt.sym, False, list()), frame)
        # return SubBody(None, [Symbol(ast.name, MType(list(), ast.returnType), CName(self.className))] + subctxt.sym)
        return SubBody(None, subctxt.sym)

    def visitVarDecl(self, ast, o):
        #ast: VarDecl
        #o: SubBody
        #ast.variable: Id
        #ast.varType: Type

        subctxt = o
        frame = subctxt.frame
        mtype = ast.varType
        name = ast.variable.name

        if frame is None:
            # Decl mot bien global
            self.emit.printout(self.emit.emitATTRIBUTE(name, mtype, False, ""))
            return SubBody(None, [Symbol(name, mtype, CName(self.className))] +
                           subctxt.sym)
        else:
            # Decl mot bien local hoac param
            idx = frame.getNewIndex()
            self.emit.printout(
                self.emit.emitVAR(idx, name, mtype, frame.getStartLabel(),
                                  frame.getEndLabel(), frame))
            return SubBody(frame,
                           [Symbol(name, mtype, Index(idx))] + subctxt.sym)

    # Statement
    # o.frame: Frame
    # o.sym: List[Symbol]
    def visitAssign(self, ast, o):
        #o: Any
        #ast.lhs: Expr
        #ast.exp: Expr

        ctxt = o
        frame = ctxt.frame
        nenv = ctxt.sym

        if type(ast.lhs) is ArrayCell:
            # gan mot gia tri cho mot index expression.
            lc, lt = self.visit(ast.lhs, Access(frame, nenv, True, True))
            self.emit.printout(lc)
            rc, rt = self.visit(ast.exp, Access(frame, nenv, False, True))
            self.emit.printout(rc)
            if type(lt) != type(rt):
                self.emit.printout(self.emit.emitI2F(frame))
            self.emit.printout(self.emit.emitASTORE(lt, frame))
        else:
            # gan mot gia tri cho mot bien
            rc, rt = self.visit(ast.exp, Access(frame, nenv, False, True))
            lc, lt = self.visit(ast.lhs, Access(frame, nenv, True, True))
            if type(rt) is IntType and type(lt) is FloatType:
                rc += self.emit.emitI2F(frame)
            self.emit.printout(rc + lc)

    def visitIf(self, ast, o):
        #o:any
        #ast.expr:Expr
        #ast.thenStmt:list(Stmt)
        #ast.elseStmt:list(Stmt)

        ctxt = o
        frame = ctxt.frame
        nenv = ctxt.sym
        # Kiem tra dieu kien
        expr, _ = self.visit(ast.expr, Access(frame, nenv, False, True))
        self.emit.printout(expr)
        # label sai
        label1 = frame.getNewLabel()
        # label dung
        label2 = None
        if len(ast.elseStmt) != 0:
            label2 = frame.getNewLabel()
        # Neu dieu kien sai thi nhay toi label1
        self.emit.printout(self.emit.emitIFFALSE(label1, frame))
        list(map(lambda x: self.visit(x, o), ast.thenStmt))
        if len(ast.elseStmt) != 0:
            self.emit.printout(self.emit.emitGOTO(label2, frame))
        self.emit.printout(self.emit.emitLABEL(label1, frame))
        if len(ast.elseStmt) != 0:
            list(map(lambda x: self.visit(x, o), ast.elseStmt))
            self.emit.printout(self.emit.emitLABEL(label2, frame))

    def visitWhile(self, ast, o):
        #o: any
        #ast.sl: list(Stmt)
        #ast.exp: Expr

        ctxt = o
        frame = ctxt.frame
        nenv = ctxt.sym

        frame.enterLoop()

        labelContinue = frame.getContinueLabel()
        labelBreak = frame.getBreakLabel()

        self.emit.printout(self.emit.emitLABEL(labelContinue, frame))
        expr, _ = self.visit(ast.exp, Access(frame, nenv, False, True))
        self.emit.printout(expr)
        self.emit.printout(self.emit.emitIFFALSE(labelBreak, frame))
        list(map(lambda x: self.visit(x, o), ast.sl))

        self.emit.printout(self.emit.emitGOTO(labelContinue, frame))
        self.emit.printout(self.emit.emitLABEL(labelBreak, frame))

        frame.exitLoop()

    def visitFor(self, ast, o):
        #o:any
        #ast.id: Id
        #ast.expr1,expr2: Expr
        #ast.loop: list(Stmt)
        #ast.up: Boolean #True => increase; False => decrease

        ctxt = o
        frame = ctxt.frame
        nenv = ctxt.sym

        label1 = frame.getNewLabel()

        frame.enterLoop()
        accessT = Access(frame, nenv, True, True)
        accessF = Access(frame, nenv, False, True)
        # Gan gia tri expr1 cho id
        expr1, _ = self.visit(ast.expr1, accessF)
        id1, _ = self.visit(ast.id, accessT)
        self.emit.printout(expr1)
        self.emit.printout(id1)
        # In ra label 1
        self.emit.printout(self.emit.emitLABEL(label1, frame))

        labelContinue = frame.getContinueLabel()
        labelBreak = frame.getBreakLabel()

        if ast.up is True:
            id1, _ = self.visit(ast.id, accessF)
            self.emit.printout(id1)
            expr2, _ = self.visit(ast.expr2, accessF)
            self.emit.printout(expr2)
            self.emit.printout(self.emit.emitIFICMPGT(labelBreak, frame))
        else:
            id1, _ = self.visit(ast.id, accessF)
            self.emit.printout(id1)
            expr2, _ = self.visit(ast.expr2, accessF)
            self.emit.printout(expr2)
            self.emit.printout(self.emit.emitIFICMPLT(labelBreak, frame))

        list(map(lambda x: self.visit(x, o), ast.loop))
        self.emit.printout(self.emit.emitLABEL(labelContinue, frame))

        if ast.up is True:
            # i + 1 dat len stack
            expr, _ = self.visit(BinaryOp('+', ast.id, IntLiteral(1)), accessF)
            # Gan gia tri tren stack vao Id
            id2, _ = self.visit(ast.id, accessT)
            self.emit.printout(expr)
            self.emit.printout(id2)
        else:
            # i - 1 dat len stack
            expr, _ = self.visit(BinaryOp('-', ast.id, IntLiteral(1)), accessF)
            # Gan gia tri tren stack vao id
            id2, _ = self.visit(ast.id, accessT)
            self.emit.printout(expr)
            self.emit.printout(id2)
        # quay lai label1
        self.emit.printout(self.emit.emitGOTO(label1, frame))
        self.emit.printout(self.emit.emitLABEL(labelBreak, frame))
        frame.exitLoop()

    def visitBreak(self, ast, o):
        #o:any

        ctxt = o
        frame = ctxt.frame
        self.emit.printout(self.emit.emitGOTO(frame.getBreakLabel(), frame))

    def visitContinue(self, ast, o):
        #o:any

        ctxt = o
        frame = ctxt.frame
        self.emit.printout(self.emit.emitGOTO(frame.getContinueLabel(), frame))

    def visitReturn(self, ast, o):
        #o:any
        #ast.expr: Expr

        ctxt = o
        frame = ctxt.frame
        nenv = ctxt.sym

        if ast.expr is not None:
            str1, typ1 = self.visit(ast.expr, Access(frame, nenv, False, True))
            if type(typ1) is IntType and type(frame.returnType) is FloatType:
                str1 += self.emit.emitI2F(frame)
            self.emit.printout(str1)
        self.emit.printout(self.emit.emitGOTO(frame.getEndLabel(), frame))

    def visitWith(self, ast, o):
        #o:any
        #ast.decl: list(VarDecl)
        #ast.stmt: list(Stmt)

        ctxt = o
        frame = ctxt.frame
        glenv = ctxt.sym

        frame.enterScope(False)
        e = SubBody(frame, glenv)
        for x in ast.decl:
            e = self.visit(x, e)
            glenv = e.sym
            if type(x.varType) is ArrayType:
                idx = glenv[0].value.value
                self.emit.printout(
                    self.emit.emitINITARRAY(idx, x.varType, frame))
        e = SubBody(frame, glenv)
        body = ast.stmt
        self.emit.printout(self.emit.emitLABEL(frame.getStartLabel(), frame))
        list(map(lambda x: self.visit(x, e), body))
        self.emit.printout(self.emit.emitLABEL(frame.getEndLabel(), frame))
        frame.exitScope()

    def visitCallStmt(self, ast, o):
        #o: Any
        #ast.method: Id
        #ast.param: list(Expr)

        ctxt = o
        frame = ctxt.frame
        nenv = ctxt.sym

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

        access = Access(frame, nenv, False, True)
        for i in range(len(ast.param)):
            str1, typ1 = self.visit(ast.param[i], access)
            if type(typ1) is IntType and type(
                    sym.mtype.partype[i]) is FloatType:
                str1 += self.emit.emitI2F(frame)
            self.emit.printout(str1)
        self.emit.printout(
            self.emit.emitINVOKESTATIC(cname + "/" + sym.name, ctype, frame))

    # Expression
    #o.frame: Frame
    #o.sym: List[Symbol]
    #o.isLeft: Boolean
    #o.isFirst: Boolean
    def visitBinaryOp(self, ast, o):
        #o: any
        #ast.op:string: AND THEN => andthen; OR ELSE => orelse; other => keep it
        #ast.left:Expr
        #ast.right:Expr

        ctxt = o
        frame = ctxt.frame
        nenv = ctxt.sym

        leftOprandstr, typL = self.visit(ast.left,
                                         Access(frame, nenv, False, True))
        rightOperandstr, typR = self.visit(ast.right,
                                           Access(frame, nenv, False, True))

        if type(typL) == type(typR):
            if type(typL) is BoolType:
                if ast.op.lower() == 'and':
                    return leftOprandstr + rightOperandstr + self.emit.emitANDOP(
                        frame), BoolType()
                elif ast.op.lower() == 'or':
                    return leftOprandstr + rightOperandstr + self.emit.emitOROP(
                        frame), BoolType()
                elif ast.op.lower() == 'andthen':
                    lst = list()
                    label1 = frame.getNewLabel()
                    label2 = frame.getNewLabel()
                    lst.append(leftOprandstr)
                    lst.append(self.emit.emitIFFALSE(label1, frame))
                    lst.append(rightOperandstr)
                    lst.append(self.emit.emitIFFALSE(label1, frame))
                    lst.append(self.emit.emitPUSHICONST("true", frame))
                    lst.append(self.emit.emitGOTO(label2, frame))
                    lst.append(self.emit.emitLABEL(label1, frame))
                    lst.append(self.emit.emitPUSHICONST("false", frame))
                    lst.append(self.emit.emitLABEL(label2, frame))
                    frame.pop()
                    return ''.join(lst), BoolType()
                elif ast.op.lower() == 'orelse':
                    lst = list()
                    label1 = frame.getNewLabel()
                    label2 = frame.getNewLabel()
                    label3 = frame.getNewLabel()
                    lst.append(leftOprandstr)
                    lst.append(self.emit.emitIFNE(label1, frame))
                    lst.append(rightOperandstr)
                    lst.append(self.emit.emitIFEQ(label2, frame))
                    lst.append(self.emit.emitLABEL(label1, frame))
                    lst.append(self.emit.emitPUSHICONST("true", frame))
                    lst.append(self.emit.emitGOTO(label3, frame))
                    lst.append(self.emit.emitLABEL(label2, frame))
                    lst.append(self.emit.emitPUSHICONST("false", frame))
                    lst.append(self.emit.emitLABEL(label3, frame))
                    frame.pop()
                    return ''.join(lst), BoolType()
            elif type(typL) is IntType:
                if ast.op in ['+', '-']:
                    return leftOprandstr + rightOperandstr + self.emit.emitADDOP(
                        ast.op, IntType(), frame), IntType()
                elif ast.op == '*':
                    return leftOprandstr + rightOperandstr + self.emit.emitMULOP(
                        ast.op, IntType(), frame), IntType()
                elif ast.op.lower() == 'div':
                    return leftOprandstr + rightOperandstr + self.emit.emitDIV(
                        frame), IntType()
                elif ast.op.lower() == 'mod':
                    return leftOprandstr + rightOperandstr + self.emit.emitMOD(
                        frame), IntType()
                elif ast.op in ['<', '<=', '>', '>=']:
                    return leftOprandstr + rightOperandstr + self.emit.emitREOP(
                        ast.op, IntType(), frame), BoolType()
                elif ast.op == '<>':
                    return leftOprandstr + rightOperandstr + self.emit.emitREOP(
                        '!=', IntType(), frame), BoolType()
                elif ast.op == '=':
                    return leftOprandstr + rightOperandstr + self.emit.emitREOP(
                        '==', IntType(), frame), BoolType()
                elif ast.op == '/':
                    leftOprandstr += self.emit.emitI2F(frame)
                    rightOperandstr += self.emit.emitI2F(frame)
                    return leftOprandstr + rightOperandstr + self.emit.emitMULOP(
                        ast.op, FloatType(), frame), FloatType()
            elif type(typL) is FloatType:
                if ast.op in ['+', '-']:
                    return leftOprandstr + rightOperandstr + self.emit.emitADDOP(
                        ast.op, FloatType(), frame), FloatType()
                elif ast.op == '*':
                    return leftOprandstr + rightOperandstr + self.emit.emitMULOP(
                        ast.op, FloatType(), frame), FloatType()
                elif ast.op == '/':
                    return leftOprandstr + rightOperandstr + self.emit.emitMULOP(
                        ast.op, FloatType(), frame), FloatType()
                elif ast.op in ['<', '<=', '>', '>=']:
                    return self.emit.emitFREOP(ast.op, leftOprandstr,
                                               rightOperandstr,
                                               frame), BoolType()
                elif ast.op == '<>':
                    return self.emit.emitFREOP('!=', leftOprandstr,
                                               rightOperandstr,
                                               frame), BoolType()
                elif ast.op == '=':
                    return self.emit.emitFREOP('==', leftOprandstr,
                                               rightOperandstr,
                                               frame), BoolType()
        else:
            if ast.op in ['+', '-']:
                if type(typL) is FloatType and type(typR) is IntType:
                    return leftOprandstr + rightOperandstr + self.emit.emitI2F(
                        frame) + self.emit.emitADDOP(ast.op, FloatType(),
                                                     frame), FloatType()
                elif type(typL) is IntType and type(typR) is FloatType:
                    return leftOprandstr + self.emit.emitI2F(
                        frame) + rightOperandstr + self.emit.emitADDOP(
                            ast.op, FloatType(), frame), FloatType()
            elif ast.op == '*':
                if type(typL) is FloatType and type(typR) is IntType:
                    return leftOprandstr + rightOperandstr + self.emit.emitI2F(
                        frame) + self.emit.emitMULOP(ast.op, FloatType(),
                                                     frame), FloatType()
                elif type(typL) is IntType and type(typR) is FloatType:
                    return leftOprandstr + self.emit.emitI2F(
                        frame) + rightOperandstr + self.emit.emitMULOP(
                            ast.op, FloatType(), frame), FloatType()
            else:
                if type(typL) is IntType:
                    leftOprandstr += self.emit.emitI2F(frame)
                if type(typR) is IntType:
                    rightOperandstr += self.emit.emitI2F(frame)
                if ast.op == '/':
                    return leftOprandstr + rightOperandstr + self.emit.emitMULOP(
                        ast.op, FloatType(), frame), FloatType()
                elif ast.op in ['<', '<=', '>', '>=']:
                    return self.emit.emitFREOP(ast.op, leftOprandstr,
                                               rightOperandstr,
                                               frame), BoolType()
                elif ast.op == '<>':
                    return self.emit.emitFREOP('!=', leftOprandstr,
                                               rightOperandstr,
                                               frame), BoolType()
                elif ast.op == '=':
                    return self.emit.emitFREOP('==', leftOprandstr,
                                               rightOperandstr,
                                               frame), BoolType()

    def visitUnaryOp(self, ast, o):
        #o:any
        #ast.op: string
        #ast.body: Exprs

        ctxt = o
        frame = ctxt.frame
        nenv = ctxt.sym

        body, typ = self.visit(ast.body, Access(frame, nenv, False, True))
        if ast.op.lower() == 'not' and type(typ) is BoolType:
            return body + self.emit.emitNOT(IntType(), frame), BoolType()
        elif ast.op == '-' and type(typ) is IntType:
            return body + self.emit.emitNEGOP(IntType(), frame), IntType()
        elif ast.op == '-' and type(typ) is FloatType:
            return body + self.emit.emitNEGOP(FloatType(), frame), FloatType()

    def visitCallExpr(self, ast, o):
        #o:any
        #ast.method: Id
        #ast.param: list(Expr)

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

        lst = list()
        access = Access(frame, nenv, False, True)
        for i in range(len(ast.param)):
            str1, typ1 = self.visit(ast.param[i], access)
            if type(typ1) is IntType and type(
                    sym.mtype.partype[i]) is FloatType:
                str1 += self.emit.emitI2F(frame)
            lst.append(str1)
        lst.append(
            self.emit.emitINVOKESTATIC(cname + "/" + sym.name, ctype, frame))
        return ''.join(lst), sym.mtype.rettype

    # LHS
    def visitId(self, ast, o):
        #o:any
        #ast.name: string

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

        typ = sym.mtype

        if o.isLeft:
            if type(sym.value) is CName:
                return self.emit.emitPUTSTATIC(
                    sym.value.value + "/" + sym.name, typ, o.frame), typ
            else:
                return self.emit.emitWRITEVAR(sym.name, typ, sym.value.value,
                                              o.frame), typ
        else:
            if type(sym.value) is CName:
                return self.emit.emitGETSTATIC(
                    sym.value.value + "/" + sym.name, typ, o.frame), typ
            else:
                return self.emit.emitREADVAR(sym.name, typ, sym.value.value,
                                             o.frame), typ

    def visitArrayCell(self, ast, o):
        #o:any
        #ast.arr:Expr
        #ast.idx:Expr

        ctxt = o
        frame = ctxt.frame
        nenv = ctxt.sym

        lst = list()
        arr, typeArr = self.visit(ast.arr, Access(frame, nenv, False, True))
        idx, typeIdx = self.visit(ast.idx, Access(frame, nenv, False, True))

        typ = typeArr.eleType
        lst.append(arr)
        lst.append(idx)
        lst.append(self.emit.emitPUSHICONST(typeArr.lower, frame))
        lst.append(self.emit.emitADDOP('-', IntType(), frame))
        if not o.isLeft:
            lst.append(self.emit.emitALOAD(typ, frame))
        return ''.join(lst), typ

    # Literal
    def visitIntLiteral(self, ast, o):
        #o: Any
        #ast.value:int

        ctxt = o
        frame = ctxt.frame
        return self.emit.emitPUSHCONST(ast.value, IntType(), frame), IntType()

    def visitFloatLiteral(self, ast, o):
        #o:any
        #ast.value:float

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

    def visitBooleanLiteral(self, ast, o):
        #o:any
        #ast.value:boolean

        ctxt = o
        frame = ctxt.frame
        return self.emit.emitPUSHCONST(
            str(ast.value).lower(), IntType(), frame), BoolType()

    def visitStringLiteral(self, ast, o):
        #o:any
        #ast.value:string

        ctxt = o
        frame = ctxt.frame
        return self.emit.emitPUSHCONST(ast.value, StringType(),
                                       frame), StringType()
Example #13
0
class CodeGenVisitor(BaseVisitor, Utils):
    def lst(self,l): 
        a = []
        for x in l: 
            a += [[i.name for i in x ]]
        print(a)
    def lst2(self,l): 
        print([x.name for x in l])
    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 callBody(self, ast, o):
        
        ctxt = o
        frame = ctxt.frame
        nenv = ctxt.sym

        symLst = []
        for x in o.sym: 
             symLst+= x
        sym = self.lookup(ast.method.name.lower(), symLst, lambda x: x.name.lower())
        cname = sym.value.value
       # print(cname)
        ctype = sym.mtype

        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(x[0]) is FloatType and type(typ1) is IntType:
                str1 += self.emit.emitI2F(frame)
            in_ +=  str1

        return  in_ + self.emit.emitINVOKESTATIC(cname + "/" + sym.name, ctype, frame), ctype.rettype

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

        self.emit.printout(self.emit.emitPROLOG(self.className, "java.lang.Object"))
        for x in ast.decl:
            if type(x) is VarDecl:
                temp = self.visit(x, SubBody(None, self.env))
                self.env = temp+ self.env
                
            else:
                isMain = x.name.name == "main" and len(x.param) == 0 and type(x.returnType) is VoidType
                intype = [ArrayPointerType(StringType())] if isMain else [self.visit(x.varType,c) for x in x.param]
                self.env.insert(0,Symbol(x.name.name, MType(intype, x.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))
        
        self.emit.emitEPILOG()
        return c
    def genMETHOD(self, consdecl, o, frame):
        #consdecl: FuncDecl
        #o: Any
        #frame: Frame

        isInit = consdecl.returnType is None
        isMain = consdecl.name.name == "main" and len(consdecl.param) == 0 and type(consdecl.returnType) is VoidType
        isFunc = (not isMain) and (not isInit)

        returnType = VoidType() if isInit else consdecl.returnType
        methodName = "<init>" if isInit else consdecl.name.name
        if isMain :
            intype = [ArrayPointerType(StringType())] 
        else: 
            intype = [self.visit(x.varType,o) 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[-1] if o != None else None
        # 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))
            #list(map(lambda x: self.visit(x, SubBody(frame, glenv)), consdecl.local))
        if isFunc:
            list(map(lambda x: self.visit(x, SubBody(frame, glenv)), consdecl.param ))
            
        
        # Generate code for statements
        body = consdecl.body.member
        self.emit.printout(self.emit.emitLABEL(frame.getStartLabel(), frame))        
        if isInit:
            self.emit.printout(self.emit.emitREADVAR("this", ClassType(self.className), 0, frame))
            self.emit.printout(self.emit.emitINVOKESPECIAL(frame))

        if o!=None:  
            d = o.copy()
            
            for x in body: 
                temp = self.visit(x, SubBody(frame, d))
                if type(x) is VarDecl: 
                    d[0] = temp + d[0]
                
        #list(map(lambda x: self.visit(x, SubBody(frame, glenv)), body))


        self.emit.printout(self.emit.emitLABEL(frame.getEndLabel(), frame))
        if type(returnType) is VoidType:
            self.emit.printout(self.emit.emitRETURN(returnType, frame))
        self.emit.printout(self.emit.emitENDMETHOD(frame))
        
        frame.exitScope()
#----------------------------------------------------------------------------#
#----------------------------Declaration-------------------------------------#
    
    def visitFuncDecl(self, ast, o):
        #frame: Frame
        #sym: List[Symbol]
        frame = Frame(ast.name, ast.returnType)
        sym = []
        for x in ast.param: 
            index = frame.getNewIndex()
            sym+= [Symbol(x.variable, self.visit(x.varType,o), Index(index))]
           #[Symbol(x.variable, self.visit(x.varType,o), CName(self.className))]
        d = [sym, o.sym]
        subctxt = o
        self.genMETHOD(ast, d, frame)
        return None

    

    def visitVarDecl(self, ast, o):
        frame = o.frame
        nenv = o.sym 
        if frame is None:# var declaration in golbal
            #nenv.insert(0,Symbol(ast.variable, self.visit(ast.varType,o), CName(self.className)))
            #print([x.name.lower() for x in nenv] )             
            self.emit.printout(self.emit.emitATTRIBUTE(ast.variable, self.visit(ast.varType, o), False, ""))

            return [Symbol(ast.variable, self.visit(ast.varType,o), CName(self.className))]
        else:# var declaration in function or with statement
            index = frame.getNewIndex()
            #nenv.insert(0, Symbol(ast.variable, self.visit(ast.varType,o), Index(index))) #chu y varType
            self.emit.printout(self.emit.emitVAR(index, ast.variable, self.visit(ast.varType, o), frame.getStartLabel(), frame.getEndLabel(),frame))
            return [Symbol(ast.variable, self.visit(ast.varType,o), Index(index))]
#----------------------------------------------------------------------------#
#-----------------------------Statements-------------------------------------#

    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))
            if type(rettype) is FloatType and type(etype) is IntType:
                self.emit.printout(ecode + self.emit.emitI2F(o.frame) + self.emit.emitRETURN(rettype, o.frame)) 
            else:
                self.emit.printout(ecode + self.emit.emitRETURN(rettype, o.frame))
        return "Return"                

    def visitCallExpr(self, ast, o):
        symLst = []
        for x in o.sym: 
             symLst+= x
        sym = self.lookup(ast.method.name.lower(), symLst, lambda x: x.name.lower())
        cname = sym.value.value
        if cname == 'io':
            self.emit.printout(self.callBody(ast, o)[0])
            return None  
        else: 
            return self.callBody(ast, o)
        
    def visitIf(self, ast, o): #exp:Expr , thenStmt:Stmt, elseStmt: Stmt = None
        ctxt = o
        frame = o.frame
        expCode, expType = self.visit(ast.expr, Access(o.frame, o.sym, False, True))
        self.emit.printout(expCode)
        labelF = frame.getNewLabel()
        labelT = frame.getNewLabel() if ast.elseStmt else None
    
        self.emit.printout(self.emit.emitIFFALSE(labelF, frame))
        thenStmt = list(map(lambda x: self.visit(x, ctxt), [ast.thenStmt]))
        self.emit.printout(self.emit.emitGOTO(labelT,frame)) if ast.elseStmt and (len(thenStmt)==0 or str(thenStmt[-1]) != "Return") else None
        self.emit.printout(self.emit.emitLABEL(labelF, frame))

        elseStmt = []
        if ast.elseStmt:
            elseStmt = list(map(lambda x: self.visit(x, ctxt), [ast.elseStmt]))
            self.emit.printout(self.emit.emitLABEL(labelT, frame))

        if thenStmt != [] and elseStmt != [] and str(thenStmt[-1]) == "Return" and str(elseStmt[-1]) == "Return":
            return "Return"        
        else:
            return None    
    
    def visitFor(self, ast, o):
        ctxt = o
        frame = o.frame
        sym = o.sym
        frame.enterLoop()
        labelContinue = frame.getContinueLabel()
        labelBreak = frame.getBreakLabel()
        labelStart = frame.getNewLabel()
        #expCode, expType = self.visit(ast.exp, Access(frame, sym, False, True))
        exp1, e1 = self.visit(ast.expr1, Access(frame, sym, False, True))
        if exp1!=None:  self.emit.printout(exp1)


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

        exp2, e2 = self.visit(ast.expr2, Access(frame, sym, False, True))
        if exp2!=None:  self.emit.printout(exp2)

        self.emit.printout(self.emit.emitIFFALSE(labelBreak, frame))
        self.visit(ast.loop, ctxt)

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

        exp3, e3 = self.visit(ast.expr3, Access(frame, sym, False, True))
        if exp3!=None:  self.emit.printout(exp3)
        self.emit.printout(self.emit.emitGOTO(labelStart, frame)+ self.emit.emitLABEL(labelBreak,frame))
        frame.exitLoop()
        return None        
    def visitDowhile(self, ast, o):
        ctxt = o
        frame = o.frame
        sym = o.sym
        frame.enterLoop()
        labelContinue = frame.getContinueLabel()
        labelBreak = frame.getBreakLabel()
        labelStart = frame.getNewLabel()
        self.emit.printout(self.emit.emitLABEL(labelStart,frame))
        list(map(lambda x: self.visit(x, ctxt), ast.sl))
        self.emit.printout(self.emit.emitLABEL(labelContinue,frame))
        expCode, expType = self.visit(ast.exp, Access(frame, sym, False, True))
        self.emit.printout(expCode + self.emit.emitIFTRUE(labelStart, frame) + self.emit.emitLABEL(labelBreak,frame))
        frame.exitLoop()
        return None        
    def visitBlock(self, ast,o): 
        
        ctxt = o
        frame = o.frame
        sym = o.sym
        frame.enterScope(False)
        self.emit.printout(self.emit.emitLABEL(frame.getStartLabel(), frame))
        sym4Scope = [[]] + sym
        for x in ast.member: 
            temp = self.visit(x, SubBody(frame, sym4Scope))
            if type(x) is VarDecl: 
                sym4Scope[0] = temp + sym4Scope[0]
#        stmt = list(map(lambda x: self.visit(x, SubBody(frame, sym)), ast.member))

        self.emit.printout(self.emit.emitLABEL(frame.getEndLabel(), frame))
        frame.exitScope()
        #return stmt[-1] if stmt != [] else None
        return None
        
    def visitContinue(self, ast, o):
        self.emit.printout(self.emit.emitGOTO(o.frame.getContinueLabel(), o.frame))
        return None        
    
    def visitBreak(self, ast, o):
        self.emit.printout(self.emit.emitGOTO(o.frame.getBreakLabel(), o.frame))
        return None        

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



    def visitBinaryOp(self, ast, o):
    #################### ASSIGN ####################
        if (ast.op == '='): 
            rightCode, typeRight = self.visit(ast.right, Access(o.frame, o.sym, False, True))
            leftCode, typeLeft = self.visit(ast.left, Access(o.frame, o.sym, True, False))
            if type(typeLeft) is FloatType and type(typeRight) is IntType:
                self.emit.printout(rightCode + self.emit.emitI2F(o.frame) + leftCode)
            else:
                self.emit.printout(rightCode + leftCode)
            return None,typeRight
    ################################################

        ctxt = o
        frame = ctxt.frame
        nenv = ctxt.sym
        leftCode, typeLeft = self.visit(ast.left, Access(frame, nenv, False, True))
        rightCode, typeRight = self.visit(ast.right, Access(frame, nenv, False, True))
        if type(typeLeft) != type(typeRight):
            if type(typeLeft) is IntType:
                leftCode += self.emit.emitI2F(frame)
                typeLeft = FloatType()
            elif type(typeRight) is IntType:
                rightCode += self.emit.emitI2F(frame)
                typeRight = FloatType()
        
        if ast.op in ['+', '-']: 
            op_str = leftCode + rightCode
            op_str +=  self.emit.emitADDOP(ast.op, typeLeft, frame)
            resType = typeLeft
        elif ast.op is '*': 
            op_str = leftCode + rightCode
            op_str +=  self.emit.emitMULOP(ast.op, typeLeft, frame)
            resType = typeLeft
        elif ast.op in ['/', '%']:
            op_str = leftCode + rightCode
            op_str +=  self.emit.emitDIV(frame) if ast.op =='/' else self.emit.emitMOD(frame)
            resType = IntType()
        elif ast.op in ['&&','||']:
            op_str = leftCode + rightCode 
            op_str +=  self.emit.emitANDOP(frame) if ast.op.lower() =='and' else self.emit.emitOROP(frame)
            resType = BoolType()
        elif ast.op in ['==', '!=', '<', '<=', '>', '>='] :
            op_str = leftCode + rightCode +  self.emit.emitREOP(ast.op, typeLeft, frame)
            resType = BoolType()
        
        return op_str, resType

    def visitUnaryOp(self, ast, o):
        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)
        else:
            op_str = expCode + self.emit.emitNEGOP(expType, o.frame)
        return op_str ,expType
    
    def visitId(self, ast, o):
        symLst = []
        for x in o.sym:
            symLst+= x
        sym = self.lookup(ast.name.lower(), symLst, lambda x: x.name.lower())
        if o.isLeft:
            if type(sym.value) is CName:
                return self.emit.emitPUTSTATIC(sym.value.value + "/" + sym.name,sym.mtype,o.frame), sym.mtype #store
            else:
                return self.emit.emitWRITEVAR(sym.name, sym.mtype, sym.value.value, o.frame), sym.mtype
        else:
            if type(sym.value) is CName:
                
                return self.emit.emitGETSTATIC(sym.value.value + "/" + sym.name,sym.mtype,o.frame), sym.mtype #load
            else:
                
                return self.emit.emitREADVAR(sym.name, sym.mtype, sym.value.value, o.frame), sym.mtype
    
#----------------------------------------------------------------------------#
#-----------------------------Literal----------------------------------------#

    def visitIntLiteral(self, ast, o):
        return self.emit.emitPUSHCONST(ast.value, IntType(), o.frame), IntType()

    def visitFloatLiteral(self, ast, o):
        return self.emit.emitPUSHCONST(ast.value, FloatType(), o.frame), FloatType()

    def visitBooleanLiteral(self, ast, o):
        return self.emit.emitPUSHCONST(ast.value, BoolType(), o.frame), BoolType()

    def visitStringLiteral(self, ast, o):
        return self.emit.emitPUSHCONST(ast.value, StringType(), o.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 ArrayPointerType(self.visit(ast.eleType,o))
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 = "MPClass"
        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)

        var_lst = [x for x in ast.decl if type(x) is VarDecl]
        func_lst = [x for x in ast.decl if type(x) is FuncDecl]

        for x in var_lst:
            e = self.visit(x, e)

        for i in func_lst:
            lst = [x.varType for x in i.param]
            e.sym.append(
                Symbol(i.name.name, MType(lst, i.returnType),
                       CName(self.className)))

        for x in func_lst:
            self.visit(x, e)

        # generate default constructor

        self.genMETHOD(FuncDecl(Id("<init>"), list(), list(), list(), None), c,
                       Frame("<init>", VoidType))
        self.emit.emitEPILOG()
        return c

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

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

        intype = [ArrayPointerType(StringType())
                  ] if isMain else [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))

        for x in consdecl.param + consdecl.local:
            glenv = self.visit(x, SubBody(frame, glenv.sym))

        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))
        list(map(lambda x: self.visit(x, SubBody(frame, glenv.sym)), body))

        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 visitFuncDecl(self, ast, o):
        #ast: FuncDecl
        #o: Any
        if ast.name.name.lower() == "main":
            ast.name.name = "main"

        subctxt = o
        frame = Frame(ast.name, ast.returnType)

        self.genMETHOD(ast, subctxt, frame)

        # return SubBody(None, [Symbol(ast.name, MType(lst, ast.returnType), CName(self.className))] + subctxt.sym)

    def visitVarDecl(self, ast, o):
        ctxt = o

        if ctxt.frame is not None:
            frame = ctxt.frame
            index = frame.getNewIndex()
            txt = self.emit.emitVAR(index, ast.variable.name, ast.varType,
                                    frame.getStartLabel(), frame.getEndLabel(),
                                    frame)
            self.emit.printout(txt)
            return SubBody(ctxt.frame,
                           [Symbol(ast.variable.name, ast.varType, index)] +
                           ctxt.sym)

        else:
            txt = self.emit.emitATTRIBUTE(ast.variable.name, ast.varType,
                                          False, None)
            self.emit.printout(txt)
            return SubBody(
                None, ctxt.sym + [
                    Symbol(ast.variable.name, ast.varType, CName(
                        self.className))
                ])

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

        frame.enterLoop()
        self.emit.printout(self.emit.emitLABEL(frame.getContinueLabel(),
                                               frame))

        expcode, exptyp = self.visit(ast.exp, Access(frame, sym, False, True))
        self.emit.printout(expcode)
        self.emit.printout(self.emit.jvm.emitIFEQ(frame.getBreakLabel()))

        list(map(lambda x: self.visit(x, SubBody(frame, sym)), ast.sl))
        self.emit.printout(self.emit.emitGOTO(frame.getContinueLabel(), frame))

        self.emit.printout(self.emit.emitLABEL(frame.getBreakLabel(), frame))
        frame.exitLoop()

    def visitFor(self, ast, o):
        #id:Id
        #expr1,expr2:Expr
        #loop:list(Stmt)
        #up:Boolean #True => increase; False => decrease

        ctxt = o
        frame = ctxt.frame

        frame.enterLoop()

        exp1, exp1typ = self.visit(ast.expr1,
                                   Access(frame, ctxt.sym, False, False))
        self.emit.printout(exp1)

        idstore, idtyp = self.visit(ast.id, Access(frame, ctxt.sym, True,
                                                   False))
        self.emit.printout(idstore)

        idload, idtypnew = self.visit(ast.id,
                                      Access(frame, ctxt.sym, False, False))

        # Lan dau tien
        self.emit.printout(idload + self.emit.emitPUSHICONST(1, frame))

        #if up -1, if downto +1
        if ast.up:
            self.emit.printout(self.emit.emitADDOP('-', IntType(), frame))
        else:
            self.emit.printout(self.emit.emitADDOP('+', IntType(), frame))

        self.emit.printout(idstore)

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

        exp2, exp2typ = self.visit(ast.expr2,
                                   Access(frame, ctxt.sym, False, False))

        if ast.up:
            self.emit.printout(idload + self.emit.emitPUSHICONST(1, frame) +
                               self.emit.emitADDOP('+', IntType(), frame))
            self.emit.printout(idstore)
        else:
            self.emit.printout(idload + self.emit.emitPUSHICONST(1, frame) +
                               self.emit.emitADDOP('-', IntType(), frame))
            self.emit.printout(idstore)

        if ast.up:
            self.emit.printout(idload + exp2 +
                               self.emit.emitREOP("<=", IntType(), frame))
        else:
            self.emit.printout(idload + exp2 +
                               self.emit.emitREOP(">=", IntType(), frame))

        self.emit.printout(self.emit.jvm.emitIFEQ(frame.getBreakLabel()))
        list(map(lambda x: self.visit(x, SubBody(frame, ctxt.sym)), ast.loop))

        self.emit.printout(self.emit.emitGOTO(frame.getContinueLabel(), frame))
        self.emit.printout(self.emit.emitLABEL(frame.getBreakLabel(), frame))

        frame.exitLoop()

    def visitIf(self, ast, o):
        ctxt = o
        frame = ctxt.frame
        labelExit = frame.getNewLabel()

        exprcode, exptyp = self.visit(ast.expr,
                                      Access(frame, ctxt.sym, False, False))
        self.emit.printout(exprcode)

        flagThen = self.checkFuncNoReturn(ast.thenStmt)

        if len(ast.elseStmt) == 0:
            self.emit.printout(self.emit.jvm.emitIFEQ(labelExit))
            list(
                map(lambda x: self.visit(x, SubBody(frame, ctxt.sym)),
                    ast.thenStmt))
            if not flagThen:
                self.emit.printout(self.emit.emitGOTO(labelExit, frame))

        else:
            labelElse = frame.getNewLabel()
            flagElse = self.checkFuncNoReturn(ast.elseStmt)
            self.emit.printout(self.emit.jvm.emitIFEQ(labelElse))
            list(
                map(lambda x: self.visit(x, SubBody(frame, ctxt.sym)),
                    ast.thenStmt))

            if not flagThen:
                self.emit.printout(self.emit.emitGOTO(labelExit, frame))

            self.emit.printout(self.emit.emitLABEL(labelElse, frame))
            list(
                map(lambda x: self.visit(x, SubBody(frame, ctxt.sym)),
                    ast.elseStmt))

            if not flagElse:
                self.emit.printout(self.emit.emitGOTO(labelExit, frame))

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

    def checkFuncNoReturn(self, list):
        check = False
        for i in list:
            if type(i) is If:
                a = self.checkFuncNoReturn(i.thenStmt)
                b = self.checkFuncNoReturn(
                    i.elseStmt) if i.elseStmt != [] else False
                check = a and b
            if type(i) is With:
                check = self.checkFuncNoReturn(i.stmt)
            if type(i) is Return:
                check = True
        return check

    def visitCallStmt(self, ast, o):
        #ast: CallStmt
        #o: Any

        ctxt = o
        frame = ctxt.frame

        sym = self.lookup(ast.method.name.lower(), ctxt.sym,
                          lambda x: x.name.lower())

        for x in ctxt.sym:
            if x.name.lower() == sym.name.lower():
                ast.method.name = x.name
                # print(ast.method.name)

        cname = sym.value.value

        ctype = sym.mtype
        i = 0
        in_ = ("", list())

        for x in ast.param:
            str1, typ1 = self.visit(x, Access(frame, ctxt.sym, False, True))
            if type(typ1) is IntType and type(
                    sym.mtype.partype[i]) is FloatType:
                in_ = (in_[0] + str1 + self.emit.emitI2F(frame),
                       in_[1] + [typ1])
            else:
                in_ = (in_[0] + str1, in_[1] + [typ1])
            i += 1
        self.emit.printout(in_[0])
        self.emit.printout(
            self.emit.emitINVOKESTATIC(cname + "/" + ast.method.name, ctype,
                                       frame))

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

        ctxt = o
        frame = ctxt.frame
        sym = self.lookup(ast.method.name.lower(), ctxt.sym,
                          lambda x: x.name.lower())

        for x in ctxt.sym:
            if x.name.lower() == sym.name.lower():
                ast.method.name = x.name

        cname = sym.value.value

        ctype = sym.mtype
        i = 0
        in_ = ("", list())

        for x in ast.param:
            str1, typ1 = self.visit(x, Access(frame, ctxt.sym, False, True))
            if type(typ1) is IntType and type(
                    sym.mtype.partype[i]) is FloatType:
                in_ = (in_[0] + str1 + self.emit.emitI2F(frame),
                       in_[1] + [typ1])
            else:
                in_ = (in_[0] + str1, in_[1] + [typ1])
            i += 1

        return in_[0] + self.emit.emitINVOKESTATIC(cname + "/" + sym.name,
                                                   ctype, frame), ctype.rettype

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

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

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

        right, righttyp = self.visit(ast.exp, Access(frame, nenv, False, True))
        left, lefttyp = self.visit(ast.lhs, Access(frame, nenv, True, False))

        self.emit.printout(right)

        if type(righttyp) is IntType and type(lefttyp) is FloatType:
            self.emit.printout(self.emit.emitI2F(frame))

        self.emit.printout(left)
        return

    def visitBinaryOp(self, ast, o):

        ctxt = o
        frame = ctxt.frame
        #lexeme = ast.op
        leftcode, lefttyp = self.visit(ast.left, o)
        rightcode, righttyp = self.visit(ast.right, o)
        retyp = lefttyp
        result = ""

        if ast.op in ['+', '-']:
            if type(lefttyp) is type(righttyp):
                return leftcode + rightcode + self.emit.emitADDOP(
                    ast.op, lefttyp, frame), retyp
            else:
                retyp = FloatType()
                if type(lefttyp) is IntType:
                    return leftcode + self.emit.emitI2F(
                        frame) + rightcode + self.emit.emitADDOP(
                            ast.op, retyp, frame), retyp
                else:
                    return leftcode + rightcode + self.emit.emitI2F(
                        frame) + self.emit.emitADDOP(ast.op, retyp,
                                                     frame), retyp
        elif ast.op == '*':
            if type(lefttyp) is type(righttyp):
                return leftcode + rightcode + self.emit.emitMULOP(
                    ast.op, lefttyp, frame), retyp
            else:
                retyp = FloatType()
                if type(lefttyp) is IntType:
                    return leftcode + self.emit.emitI2F(
                        frame) + rightcode + self.emit.emitMULOP(
                            ast.op, retyp, frame), retyp
                else:
                    return leftcode + rightcode + self.emit.emitI2F(
                        frame) + self.emit.emitMULOP(ast.op, retyp,
                                                     frame), retyp
        elif ast.op == '/':
            retyp = FloatType()
            if type(lefttyp) is type(righttyp):
                if type(lefttyp) is IntType:
                    return leftcode + self.emit.emitI2F(
                        frame) + rightcode + self.emit.emitI2F(
                            frame) + self.emit.emitMULOP(ast.op, retyp,
                                                         frame), retyp
                else:
                    return leftcode + rightcode + self.emit.emitMULOP(
                        ast.op, retyp, frame), retyp
            else:
                if type(lefttyp) is IntType:
                    return leftcode + self.emit.emitI2F(
                        frame) + rightcode + self.emit.emitMULOP(
                            ast.op, retyp, frame), retyp
                else:
                    return leftcode + rightcode + self.emit.emitI2F(
                        frame) + self.emit.emitMULOP(ast.op, retyp,
                                                     frame), retyp

        elif ast.op.lower() == "div":
            return leftcode + rightcode + self.emit.emitDIV(frame), IntType()

        elif ast.op.lower() == "mod":
            return leftcode + rightcode + self.emit.emitMOD(frame), IntType()

        elif ast.op.lower() == "and":
            return leftcode + rightcode + self.emit.emitANDOP(frame), BoolType(
            )

        elif ast.op.lower() == "or":
            return leftcode + rightcode + self.emit.emitOROP(frame), BoolType()

        elif ast.op in ['>', '>=', '<', '<=', '<>', '=']:
            retyp = BoolType()
            if type(lefttyp) is type(righttyp):
                return leftcode + rightcode + self.emit.emitREOP(
                    ast.op, lefttyp, frame), retyp
            else:
                if type(lefttyp) is IntType:
                    return leftcode + self.emit.emitI2F(
                        frame) + rightcode + self.emit.emitREOP(
                            ast.op, FloatType(), frame), retyp
                else:
                    return leftcode + rightcode + self.emit.emitI2F(
                        frame) + self.emit.emitREOP(ast.op, FloatType(),
                                                    frame), retyp

        #TODO andthen & orelse
        #if 5 > 3 and then 2 > 1

        elif ast.op.lower() == 'andthen':
            retyp = BoolType()
            labelLz = frame.getNewLabel()
            # labelTh = frame.getNewLabel()
            result += leftcode
            result += self.emit.emitDUP(frame)
            result += self.emit.jvm.emitIFEQ(labelLz)
            result += rightcode
            result += self.emit.emitANDOP(frame)
            result += self.emit.emitLABEL(labelLz, frame)
            return result, retyp

        elif ast.op.lower() == 'orelse':
            retyp = BoolType()
            labelLz = frame.getNewLabel()
            result += leftcode
            result += self.emit.emitDUP(frame)
            result += self.emit.jvm.emitIFNE(labelLz)
            result += rightcode
            result += self.emit.emitOROP(frame)
            result += self.emit.emitLABEL(labelLz, frame)
            return result, retyp

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

        frame.enterScope(False)
        labelSta = frame.getStartLabel()
        labelEnd = frame.getEndLabel()

        for x in ast.decl:
            # print(type(sym))
            if type(sym) is SubBody:
                sym = self.visit(x, SubBody(frame, sym.sym))
            else:
                sym = self.visit(x, SubBody(frame, sym))

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

        list(map(lambda x: self.visit(x, SubBody(frame, sym.sym)), ast.stmt))
        self.emit.printout(self.emit.emitLABEL(labelEnd, frame))
        frame.exitScope()

    def visitUnaryOp(self, ast, o):
        ctxt = o
        frame = ctxt.frame
        unacode, unatyp = self.visit(ast.body, o)

        if ast.op is '-':
            return unacode + self.emit.emitNEGOP(unatyp, frame), unatyp
        if ast.op.lower() == 'not':
            return unacode + self.emit.emitNOT(BoolType(), frame), unatyp

    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):
        #ast: FloatLiteral
        #o: Any

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

    def visitBooleanLiteral(self, ast, o):
        #ast: BooleanLiteral
        #o: Any

        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('"' + ast.value + '"', StringType(),
                                       frame), StringType()

    def visitReturn(self, ast, o):
        ctxt = o
        frame = ctxt.frame
        refunctyp = frame.returnType

        if ast.expr:
            expcode, exptyp = self.visit(ast.expr,
                                         Access(frame, o.sym, False, True))
            self.emit.printout(expcode)

            if type(exptyp) is not type(
                    refunctyp) and type(refunctyp) is FloatType:
                self.emit.printout(self.emit.emitI2F(frame))

        self.emit.printout(self.emit.emitRETURN(refunctyp, frame))

    def visitId(self, ast, o):
        ctxt = o
        frame = ctxt.frame
        isLeft = ctxt.isLeft

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

        if isLeft:
            if type(sym.value) is CName:
                name = self.className + "/" + sym.name
                return self.emit.emitPUTSTATIC(name, sym.mtype,
                                               frame), sym.mtype
            else:
                return self.emit.emitWRITEVAR(sym.name, sym.mtype, sym.value,
                                              frame), sym.mtype

        else:
            if type(sym.value) is CName:
                name = self.className + "/" + sym.name
                return self.emit.emitGETSTATIC(name, sym.mtype,
                                               frame), sym.mtype
            else:
                return self.emit.emitREADVAR(sym.name, sym.mtype, sym.value,
                                             frame), sym.mtype
Example #15
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

    #     self.emit.printout(self.emit.emitPROLOG(self.className, "java.lang.Object"))
    #     e = SubBody(None, self.env)
    #     for x in ast.decl:
    #         e = self.visit(x, e)
    #     # generate default constructor
    #     self.genMETHOD(FuncDecl(Id("<init>"), list(), None, Block(list())), c, Frame("<init>", VoidType))
    #     self.emit.emitEPILOG()
    #     return c

    def VarGlobal(self, ast, c):
        ctxt = c
        nameAttr = ast.variable
        typeAttr = ast.varType
        self.emit.printout(
            self.emit.emitATTRIBUTE(nameAttr, typeAttr, False, ""))
        c.append(Symbol(nameAttr, typeAttr, CName(self.className)))
        return c

    def FuncGlobal(self, ast, c):
        ctxt = c
        nameFunc = ast.name.name
        funcsym = Symbol(nameFunc,
                         MType([x.varType for x in ast.param], ast.returnType),
                         CName(self.className))
        c.append(funcsym)
        return c

    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)
        lsVar = list(filter(lambda x: type(x) is VarDecl, ast.decl))
        lsArray = list(filter(lambda x: type(x.varType) is ArrayType, lsVar))
        lsFunction = list(filter(lambda x: type(x) is FuncDecl, ast.decl))
        #print static field and add to self.env
        reduce(
            lambda x, y: self.VarGlobal(y, x)
            if type(y) is VarDecl else self.FuncGlobal(y, x), ast.decl,
            self.env if self.env else [])
        #visit FuncDecl
        reduce(lambda x, y: self.visit(y, x), lsFunction,
               SubBody(None, self.env))

        #generate defaule constructor
        self.genMETHOD(FuncDecl(Id("<init>"), [], None, Block([])), c,
                       Frame("<init>", VoidType))

        # if lsArray:
        #     self.emit.printout(self.emit.emitCLINIT())
        self.emit.emitEPILOG()
        return c

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

        isInit = consdecl.returnType is None
        isMain = consdecl.name.name == "main" and len(
            consdecl.param) == 0 and type(consdecl.returnType) is VoidType
        returnType = VoidType() if isInit else consdecl.returnType
        methodName = "<init>" if isInit else consdecl.name.name
        intype = [ArrayPointerType(StringType())
                  ] if isMain else [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))

        glSubBody = SubBody(frame, glenv)
        if (isMain is False) and (intype != []):
            glSubBody = reduce(lambda x, y: self.visit(y, x), consdecl.param,
                               SubBody(frame, glenv))

        body = consdecl.body
        lstvardecl = list(filter(lambda x: type(x) is VarDecl, body.member))
        curenv = reduce(lambda x, y: self.visit(y, x), lstvardecl, glSubBody)
        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))

        lststmt = list(filter(lambda x: not type(x) is VarDecl, body.member))
        # list(map(lambda x: self.visit(x, curenv), lststmt))
        list(map(lambda x: self.printoutStmt(x, curenv),
                 lststmt))  #glSubBody after add vardecl in func

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

    def visitVarDecl(self, ast, o):
        #ast: VarDecl
        #o  : SubBody
        env = o.sym
        idx = o.frame.getNewIndex()
        self.emit.printout(
            self.emit.emitVAR(idx, ast.variable, ast.varType,
                              o.frame.getStartLabel(), o.frame.getEndLabel(),
                              o.frame))
        return SubBody(o.frame,
                       env.append(Symbol(ast.variable, ast.varType, Index)))

    def visitFuncDecl(self, ast, o):
        #ast: FuncDecl
        #o: Any

        subctxt = o
        frame = Frame(ast.name, ast.returnType)
        self.genMETHOD(ast, subctxt.sym, frame)
        return SubBody(None, [
            Symbol(ast.name, MType(list(), ast.returnType),
                   CName(self.className))
        ] + subctxt.sym)

    def visitArrayType(self, ast, c):
        #ast:
        #c : SubBody

        index = self.lookup(ast.variable, c.sym, lambda x: x.name).value.value
        self.emit.printout(self.emit.emitNEWARRAY(ast.varType, c.frame))
        self.emit.printout(
            self.emit.emitWRITEVAR(ast.variable, ast.varType, index, c.frame))
        return SubBody(c.frame, c.sym)

    def visitBlock(self, ast, c):
        ctxt = c
        frame = c.frame
        sym = c.sym
        frame.EnterScope(False)

        lstvar = list(filter(lambda x: type(x) is VarDecl, ast.member))
        varenv = reduce(lambda x, y: self.visit(y, x), lstvar,
                        SubBody(frame, sym))

        listArrayVarDecl = filter(lambda x: type(x) is ArrayType, lstvar)

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

        list(map(lambda x: self.visitArrayType(x, varenv), listArrayVarDecl))
        lststmt = list(filter(lambda x: not type(x) is VarDecl, ast.member))
        list(map(lambda x: self.printoutStmt(x, varenv), lststmt))

        self.emit.printout(self.emit.emitLABEL(frame.getEndLabel(), frame))
        frame.exitScope()
        return c

    def printoutStmt(self, ast, c):
        frame = c.frame
        env = c.sym
        if type(ast) is BinaryOp:
            if ast.op == '=':
                self.emit.printout(
                    self.visit(ast, Access(frame, env, True, True, False)))
            else:
                self.emit.printout(
                    self.visit(ast, Access(frame, env, False, True, True)))
                self.emit.printout(self.emit.emitPOP(frame))
        elif type(ast) is CallExpr:
            self.emit.printout(
                self.visit(ast, Access(frame, env, False, True, True)))
            sym = self.lookup(ast.method.name, env, lambda x: x.name)
            returnType = sym.mtype.rettype

            if type(returnType) != VoidType:
                self.emit.printout(self.emit.emitPOP(frame))
        elif (type(ast) is UnaryOp) or (type(ast) is Id) or (type(ast) is ArrayCell) or (type(ast) is IntLiteral) or \
                (type(ast) is FloatLiteral) or (type(ast) is StringLiteral) or (type(ast) is BooleanLiteral):

            self.emit.printout(
                self.visit(ast, Access(frame, newEnv, False, True, True))[0])
            self.emit.printout(self.emit.emitPOP(frame))

        elif (type(ast) is Block) or (type(ast) is If) or (type(ast) is For) or (type(ast) is Break) or \
                (type(ast) is Continue) or (type(ast) is Return) or (type(ast) is Dowhile):

            self.visit(ast, env)
        else:
            pass

    # def printoutStmt(self,ast,env):
    #     #env    : SubBody
    #     frame  = env.frame
    #     newEnv = env.sym
    #     if type(ast) is BinaryOp:
    #         if ast.op == "=":
    #             self.emit.printout(self.visit(ast, Access(frame, newEnv, True, True, False))[0])
    #         else:
    #             self.emit.printout(self.visit(ast, Access(frame, newEnv, False, True, True))[0])
    #             self.emit.printout(self.emit.emitPOP(frame))

    #     elif type(ast) is CallExpr:
    #         self.emit.printout(self.visit(ast, Access(frame, newEnv, False, True, True))[0])
    #         sym = self.lookup(ast.method.name, newEnv, lambda x:x.name)
    #         returnType = sym.mtype.rettype

    #         if type(returnType) != VoidType:
    #             self.emit.printout(self.emit.emitPOP(frame))

    #     elif (type(ast) is UnaryOp) or (type(ast) is Id) or (type(ast) is ArrayCell) or (type(ast) is IntLiteral) or \
    #             (type(ast) is FloatLiteral) or (type(ast) is StringLiteral) or (type(ast) is BooleanLiteral):

    #             self.emit.printout(self.visit(ast, Access(frame, newEnv, False, True, True))[0])
    #             self.emit.printout(self.emit.emitPOP(frame))

    #     elif (type(ast) is Block) or (type(ast) is If) or (type(ast) is For) or (type(ast) is Break) or \
    #             (type(ast) is Continue) or (type(ast) is Return) or (type(ast) is Dowhile):
    #                 # NOTE env hay new env vay cha noi
    #             self.visit(ast, env)
    #     else:
    #         pass
    # def visitIf(self,ast,c):
    #     #c  : SubBody
    #     frame = c.frame
    #     env = c.sym
    #     (resExpr, typeExpr) = ast.expr.accept(self, Access(frame, env, False, True, True))
    #     falseLabel = frame.getNewLabel()

    #     self.emit.printout(resExpr + self.emit.emitIFFALSE(falseLabel, frame))
    #     self.printoutStmt(ast.thenStmt, c)
    #     if not ast.elseStmt:
    #         self.emit.printout(self.emit.emitLABEL(falseLabel, frame))
    #     else:
    #         trueLabel = frame.getNewLabel()
    #         self.emit.printout(self.emit.emitGOTO(trueLabel, frame) + self.emit.emitLABEL(falseLabel, frame)
    #                 )
    #         self.printoutStmt(ast.elseStmt, c)
    #         self.emit.printout(self.emit.emitLABEL(trueLabel, frame))
    def visitIf(self, ast, c):
        #c: SubBody
        frame = c.frame
        env = c.sym
        (resExpr, typeExpr) = self.visit(ast,
                                         Access(frame, env, False, True, True))
        falseLabel = frame.getNewLabel()

        self.emit.printout(resExpr + self.emit.emitIFFALSE(falseLabel, frame))
        self.printoutStmt(ast.thenStmt, c)
        if not ast.elseStmt:
            self.emit.printout(self.emit.emitLABEL(falseLabel, frame))
        else:
            exitLabel = frame.getNewLabel()
            self.emit.printout(
                self.emit.emitGOTO(exitLabel, frame) +
                self.emit.emitLABEL(falseLabel, frame))
            self.printoutStmt(ast.elseStmt, c)
            self.emit.printout(self.emit.emitLABEL(exitLabel, frame))

    # def visitFor(self,ast,c):
    #     #c  : SubBody
    #     frame = c.frame
    #     env = c.sym
    #     beginLabel = frame.getNewLabel()
    #     frame.enterLoop()

    #     self.printoutStmt(ast.expr1, SubBody(frame, env))
    #     self.emit.printout(self.emit.emitLABEL(beginLabel, frame))
    #     (resExpr2, typeExpr2) = ast.expr2.accept(self,Access(frame, env, False, True, False))
    #     self.emit.printout(resExpr2)

    #     self.emit.printout(self.emit.emitIFTRUE(frame.getBreakLabel(), frame))
    #     self.printoutStmt(ast.loop, c)

    #     self.emit.printout(self.emit.emitLABEL(frame.getContinueLabel(), frame))
    #     self.printoutStmt(ast.expr3, SubBody(frame, env))

    #     self.emit.printout(self.emit.emitGOTO(beginLabel, frame))
    #     self.emit.printout(self.emit.emitLABEL(frame.getBreakLabel(), frame))

    #     frame.exitLoop()

    def visitFor(self, ast, c):
        #c = SubBody
        frame = c.frame
        env = c.sym
        beginLabel = frame.getNewLabel()
        frame.enterLoop()
        # visit expr1
        self.printoutStmt(ast.expr1, SubBody(frame, env))
        # label loop
        self.emit.printout(self.emit.emitLABEL(beginLabel, frame))
        #visit expr2
        (resExpr2,
         typeExpr2) = self.visit(ast.expr2,
                                 Access(frame, env, False, True, False))
        self.emit.printout(resExpr2)

        self.emit.printout(self.emit.emitIFTRUE(frame.getBreakLabel(), frame))
        self.printoutStmt(ast.loop, c)

        self.emit.printout(self.emit.emitLABEL(frame.getContinueLabel(),
                                               frame))  ###??
        self.printoutStmt(ast.expr3, SubBody(frame, env))

        self.emit.printout(self.emit.emitGOTO(beginLabel, frame))
        self.emit.print(self.emit.emitLABEL(frame.getBreakLabel(), frame))

        frame.exitLoop()

    # def visitDowhile(self,ast,c):
    #     #c  : SubBody
    #     frame = c.frame
    #     env = c.sym
    #     beginLabel = frame.getNewLabel()
    #     frame.enterLoop()

    #     self.emit.printout(self.emit.emitLABEL(beginLabel, frame))
    #     list(map(lambda x:self.printoutStmt(x,c) ,ast.sl))

    #     self.emit.printout(self.emit.emitLABEL(frame.getContinueLabel(), frame))

    #     (resExpr, typeExpr) = ast.exp.accept(self, Access(frame, env, False, True, True))
    #     self.emit.printout(resExpr)
    #     self.emit.printout(self.emit.emitIFTRUE(frame.getBreakLabel(), frame))
    #     self.emit.printout(self.emit.emitGOTO(beginLabel, frame))
    #     self.emit.printout(self.emit.emitLABEL(frame.getBreakLabel(), frame))
    #     frame.exitLoop()

    def visitDowhile(self, ast, c):
        # c  = SubBody
        frame = c.frame
        env = c.sym
        beignLabel = frame.getNewLabel()
        frame.enterLoop()

        self.visit.printout(self.emit.emitLABEL(beignLabel, frame))
        list(map(self.printoutStmt(x, c), ast.sl))

        self.emit.printout(self.emit.emitLABEL(frame.getContinueLabel(),
                                               frame))  #?

        (resExpr, typeExpr) = self.visit(ast.exp,
                                         Access(frame, env, False, True, True))
        self.emit.printout(resExpr)
        self.emit.printout(self.emit.emitIFTRUE(frame.getBreakLabel(), frame))
        self.emit.printout(self.emit.emitGOTO(beignLabel, frame))
        self.emit.printout(self.emit.emitLABEL(frame.getBreakLabel(), frame))
        frame.exitLoop()

    def visitBreak(self, ast, c):
        self.emit.printout(self.emit.emitGOTO(c.frame.getBreakLabel(),
                                              c.frame))

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

    # def visitReturn(self,ast,c):
    #     if ast.expr:
    #         (resExpr, resType) = self.visit(ast.expr, Access(c.frame, c.sym, False, True, True))
    #         typeFunc = self.curFunc.mtype.rettype
    #         if type(typeFunc) == FloatType and type(resType) == IntType:
    #             self.emit.printout(resExpr + self.emit.emitI2F(c.frame) + self.emit.emitRETURN(FloatType(), c.frame))
    #         else:
    #             self.emit.printout(resExpr + self.emit.emitRETURN(resType, c.frame))
    #     else:
    #         self.emit.printout(self.emit.emitRETURN(VoidType(), c.frame))
    def visitReturn(self, ast, c):
        if ast.expr:
            (resExpr,
             resType) = self.visit(ast.expr,
                                   Access(c.frame, c.sym, False, True, True))
            typeFunc = self.curFunc.mtype.rettype
            if type(typeFunc) == FloatType and type(resType) == IntType:
                self.emit.printout(resExpr + self.emit.emitI2F(c.frame) +
                                   self.emit.emitRETURN(FloatType(), c.frame))
            else:
                self.emit.printout(resExpr +
                                   self.emit.emitRETURN(resType, c.frame))
        else:
            self.emit.printout(self.emit.emitRETURN(VoidType(), c.frame))

    # def visitUnaryOp(self,ast,c):
    #     ctxt = c
    #     frame = ctxt.frame
    #     env = ctxt.sym
    #     (resExpr, typeExpr) = self.visit(ast.body, Access(frame, env, False, True, True))
    #     if ast.op == "!":
    #         return (resExpr + self.emit.emitNOT(typeExpr, frame), typeExpr)
    #     elif ast.op == "-":
    #         return (resExpr + self.emit.emitNEGOP(typeExpr, frame), typeExpr)

    def visitUnaryOp(self, ast, c):
        frame = c.frame
        env = c.sym
        (resExpr, typeExpr) = self.visit(ast.body,
                                         Access(frame, env, False, True, True))

        if ast.op == "!":
            return (resExpr + self.emit.emitNOT(typeExpr, frame), typeExpr)
        elif ast.op == "-":
            return (resExpr + self.emit.emitNEG(typeExpr, frame), typeExpr)

    # def visitBinaryOp(self,ast,c):
    #     #c  : Access (co the la SubBody)
    #     ctxt = c
    #     frame = ctxt.frame
    #     env = ctxt.sym
    #     op = ast.op
    #     op_Str = ""
    #     str_Dup = ""
    #     str_I2f = ""
    #     resType = IntType()

    #     if op == "=":
    #         (resLeft1, typeLeft1) = self.visit(ast.left, Access(frame, env, True, True, False))
    #         (resRight, typeRight) = self.visit(ast.right,Access(frame, env, False, True, True))

    #         if type(typeLeft1) == FloatType and type(typeRight) == IntType:
    #             str_I2f = self.emit.emitI2F(frame)

    #         if ctxt.isDup == True:
    #             if type(ast.left) is Id:
    #                 str_Dup = self.emit.emitDUP(frame)
    #             else:
    #                 str_Dup = self.emit.emitDUP_X2(frame)

    #         (resLeft2, typeLeft2) = self.visit(ast.left, Access(frame, env, True, False, False))
    #         op_Str = resLeft1 + resRight + str_I2f + str_Dup  + resLeft2

    #         resType = typeLeft1

    #     else:
    #         (resLeft, typeLeft) = self.visit(ast.left, Access(frame, env, False, True, True))
    #         (resRight, typeRight) = self.visit(ast.right, Access(frame, env, False, True, True))

    #         if op == "+" or op == "-":
    #             if type(typeLeft) is FloatType and type(typeRight) is IntType:
    #                 op_Str = resLeft + resRight + self.emit.emitI2F(frame) + self.emit.emitADDOP(op, FloatType(), frame)
    #                 resType = FloatType()
    #             elif type(typeLeft) is IntType and type(typeRight) is FloatType:
    #                 op_Str = resLeft + self.emit.emitI2F(frame) + resRight + self.emit.emitADDOP(op, FloatType(), frame)
    #                 resType = FloatType()
    #             else:
    #                 op_Str = resLeft + resRight + self.emit.emitADDOP(op, typeLeft, frame)
    #                 resType = typeLeft

    #         elif op == "*" or op == "/":
    #             if type(typeLeft) is FloatType and type(typeRight) is IntType:
    #                 op_Str = resLeft + resRight + self.emit.emitI2F(frame) + self.emit.emitMULOP(op, FloatType(), frame)
    #                 resType = FloatType()
    #             elif type(typeLeft) is IntType and type(typeRight) is FloatType:
    #                 op_Str = resLeft + self.emit.emitI2F(frame) + resRight + self.emit.emitMULOP(op, FloatType(), frame)
    #                 resType = FloatType()
    #             else:
    #                 op_Str = resLeft + resRight + self.emit.emitMULOP(op, typeLeft, frame)
    #                 resType = typeLeft
    #         elif op == "%":
    #             op_Str = resLeft + resRight + self.emit.emitMOD(frame)
    #             resType = IntType()
    #         elif (op == "<") or (op == "<=") or (op == ">") or (op == ">="):
    #             if type(typeLeft) is FloatType and type(typeRight) is IntType:
    #                 op_Str = resLeft + resRight + self.emit.emitI2F(frame) + self.emit.emitREOP(op, FloatType(), frame)
    #             elif type(typeLeft) is IntType and type(typeRight) is FloatType:
    #                 op_Str = resLeft + self.emit.emitI2F(frame) + resRight + self.emit.emitREOP(op, FloatType(), frame)
    #             else:
    #                 op_Str = resLeft + resRight + self.emit.emitREOP(op, typeLeft, frame)
    #             resType = BoolType()
    #         elif (op == "==") or (op == "!="):
    #             if type(typeLeft) is BoolType and type(typeRight) is BoolType:
    #                 op_Str = resLeft + resRight + self.emit.emitREOP(op, IntType(), frame)
    #             if type(typeLeft) is IntType and type(typeRight) is IntType:
    #                 op_Str = resLeft + resRight + self.emit.emitREOP(op, IntType(), frame)
    #             else:
    #                 resType = BoolType()
    #         elif (op == "&&") or (op == "||"):
    #             op_Str = self.emit.emitAND_OR_SHORT_CIRCUIT(op, resLeft, resRight, frame)
    #             resType = BoolType()

    #     return (op_Str, resType)
    def visitBinaryOp(self, ast, c):
        ctxt = c
        frame = c.frame
        env = c.sym
        op = ast.op
        op_str = ""
        str_dup = ""
        str_I2F = ""
        resType = IntType()

        if op == "=":
            (resLHS,
             typeLHS) = self.visit(ast.left,
                                   Access(frame, env, True, True, False))
            (resRHS,
             typeRHS) = self.visit(ast.right,
                                   Access(frame, env, False, True, True))

            if type(typeLHS) is FloatType and type(typeRHS) is IntType:
                str_I2F = self.emit.emitI2F(frame)
            if ctxt.isDup == True:
                str_dup = self.emit.emitDUP(frame)
            else:
                str_dup = self.emit.emitDUP_X2(frame)

            (resLHS2,
             typeLHS2) = self.visit(ast.left,
                                    Access(frame, env, True, False, False))
            op_str = resLHS + resRHS + str_I2F + str_dup + resLHS2

            resType = typeLHS
        else:
            (resLHS,
             typeLHS) = self.visit(ast.left,
                                   Access(frame, env, False, True, True))
            (resRHS,
             typeRHS) = self.visit(ast.right,
                                   Access(frame, env, False, True, True))

            if op in ["+", "-"]:
                if type(typeLHS) is FloatType and type(typeRHS) is IntType:
                    op_str = resLHS + resRHS + self.emit.emitI2F(
                        frame) + self.emit.emitADDOP(op, FloatType(), frame)
                    resType = FloatType()
                elif type(typeLHS) is IntType and type(typeRHS) is FloatType:
                    op_str = resLHS + self.emit.emitI2F(
                        frame) + resRHS + self.emit.emitADDOP(
                            op, FloatType(), frame)
                    resType = FloatType()
                else:
                    op_str = resLHS + resRHS + self.emit.emitADDOP(
                        op, typeLHS, frame)
                    resType = typeLHS
            elif op in ["*", "/"]:
                if type(typeLHS) is FloatType and type(typeRHS) is IntType:
                    op_str = resLHS + resRHS + self.emit.emitI2F(
                        frame) + self.emit.emitMULOP(op, FloatType(), frame)
                    resType = FloatType()
                elif type(typeLHS) is IntType and type(typeRHS) is FloatType:
                    op_str = resLHS + self.emit.emitI2F(
                        frame) + resRHS + self.emit.emitMULOP(
                            op, FloatType(), frame)
                    resType = FloatType()
                else:
                    op_str = resLHS + resRHS + self.emit.emitMULOP(
                        op, typeLHS, frame)
                    resType = typeLHS
            elif op == "%":
                op_str = resLHS + resRHS + self.emit.emitMOD(op, resLHS, frame)
                resType = IntType()
            elif op in ["<=", "<", ">=", ">"]:
                if type(typeLHS) is FloatType and type(typeRHS) is IntType:
                    op_str = resLHS + resRHS + self.emit.emitI2F(
                        frame) + self.emit.emitREOP(op, FloatType(), frame)
                elif type(typeLHS) is IntType and type(typeRHS) is FloatType:
                    op_str = resLHS + self.emit.emitI2F(
                        frame) + resRHS + self.emit.emitREOP(
                            op, FloatType(), frame)
                else:
                    op_str = resLHS + resRHS + self.emit.emitREOP(
                        op, typeLHS, frame)
                    resType = BoolType()
            elif op in ["==", "!="]:
                if type(typeLHS) is BoolType and type(typeRHS) is BoolType:
                    op_str = resLHS + resRHS + self.emit.emitREOP(
                        op, IntType(), frame)
                elif type(typeLHS) is IntType and type(typeRHS) is IntType:
                    op_str = resLHS + resRHS + self.emit.emitREOP(
                        op, IntType, frame)
                resType = BoolType()
            elif op in ["||", '&&']:
                op_str = resLHS + resRHS + self.emit.emitREOP(
                    op, BoolType(), frame)
                resType = BooType()
        return (op_str, resType)

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

    #     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
    #     returnType = ctype.rettype

    #     if ctxt.isLeft is True and ctxt.isFirst is False:
    #         return (self.emit.emitWRITEVAR2(ast.method.name, returnType, frame), returnType)
    #     else:
    #         listParamType = ctype.partype
    #         # zip
    #         checkList=[]
    #         for item in range(len(listParamType)):
    #             checkList.append((ast.param[item],listParamType[item]))

    #         in_ = ("",[])
    #         for x in checkList:
    #             (str1,typ1) = self.visit(x[0],Access(frame,nenv,False,True,True))
    #             if type(typ1) is IntType and type(x[1]) is FloatType:
    #                 in_ = (in_[0] + str1 + self.emit.emitI2F(frame), in_[1].append(typ1))
    #             else:
    #                 in_ = (in_[0] + str1, in_[1] + [typ1])

    #     return (in_[0] + self.emit.emitINVOKESTATIC(cname + "/" + ast.method.name, ctype, frame), returnType)
    # 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))

    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
        returnType = ctype.rettype

        if ctxt.isLeft is True and ctxt.isFirst is False:
            return (self.emit.emitWRITEVAR2(ast.method.name, returnType,
                                            frame))
        else:
            listParamType = ctype.partype
            checkList = zip(ast.param, listParamType)

            in_ = ("", list())
            for x in checkList:
                str1, typ1 = self.visit(x[0], Access(frame, nenv, False, True))
                if type(typ1) is IntType and type(x[1]) is FloatType:
                    in_ = (in_[0] + str1 + self.emit.emitI2F(frame),
                           in_[1].append(typ1))  # not append(x[1])
                else:
                    in_ = (in_[0] + str1, in_[1].append(typ1))
            #self.emit.printout(in_[0])
        return (in_[0] + self.emit.printout(
            self.emit.emitINVOKESTATIC(cname + "/" + ast.method.name, ctype,
                                       frame)), returnType)
        #self.emit.printout(self.emit.emitINVOKESTATIC(cname + "/" + ast.method.name, ctype, frame))
    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):
        #ast: IntLiteral
        #o: Any
        ctxt = o
        frame = ctxt.frame
        return self.emit.emitPUSHFCONST(str(ast.value), frame), FloatType()

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

    def visitStringLiteral(self, ast, o):
        ctxt = o
        frame = ctxt.frame
        return self.emit.emitPUSHCONST(str(ast.value), StringType(),
                                       frame), StringType()
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 = "MPClass"
        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"))
        #First visit
        e = SubBody(None, self.env, True)
        for x in ast.decl:
            e = self.visit(x, e)
        #Second visit
        e.firstVisit = False
        for x in ast.decl:
            self.visit(x, e)
        # generate default constructor
        self.genMETHOD(FuncDecl(Id("<init>"), list(), list(), list(), None), c,
                       Frame("<init>", VoidType))
        self.emit.emitEPILOG()
        return c

    def genMETHOD(self, consdecl, o, frame):
        #consdecl: FuncDecl
        #o: Any
        #frame: Frame
        isInit = consdecl.returnType is None
        isMain = consdecl.name.name == "main" and len(
            consdecl.param) == 0 and type(consdecl.returnType) is VoidType
        returnType = VoidType() if isInit else consdecl.returnType
        methodName = "<init>" if isInit else consdecl.name.name
        intype = [ArrayPointerType(StringType())] if isMain else list()
        mtype = MType(intype, returnType)

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

        frame.enterScope(True)

        glenv = o
        env = SubBody(frame, glenv)
        #print(env.sym[0].name)

        # 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))
        else:
            for x in consdecl.param:
                env = self.visit(x, env)
        for x in consdecl.local:
            env = self.visit(x, env)

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

        if isInit:
            self.emit.printout(
                self.emit.emitREADVAR("this", ClassType(self.className), 0,
                                      frame))
            self.emit.printout(self.emit.emitINVOKESPECIAL(frame))
        list(map(lambda x: self.visit(x, env), body))

        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 visitFuncDecl(self, ast, o):
        #ast: FuncDecl
        #o: Any

        subctxt = o
        frame = Frame(ast.name, ast.returnType)
        #First visit
        if subctxt.firstVisit:
            return SubBody(None, [
                Symbol(ast.name.name, MType(list(), ast.returnType),
                       CName(self.className))
            ] + subctxt.sym, True)
        #second visit
        self.genMETHOD(ast, subctxt.sym, frame)

        #print(ast.name.name)
        #return SubBody(None, [Symbol(ast.name.name, MType(list(), ast.returnType), CName(self.className))] + subctxt.sym)

    def visitCallStmt(self, ast, o):
        #ast: CallStmt
        #o: Any

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

        in_ = ("", list())
        L = zip(ast.param, ctype.partype)
        #print(ctype.partype)
        for x in L:
            str1, typ1 = self.visit(x[0], Access(frame, nenv, False, True))
            in_ = (in_[0] + str1 +
                   (self.emit.emitI2F(frame) if type(typ1) is IntType
                    and type(x[1]) is FloatType else ""), in_[1] + [typ1])

        self.emit.printout(in_[0])
        self.emit.printout(
            self.emit.emitINVOKESTATIC(cname + "/" + sym.name, ctype, frame))

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

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

        in_ = ("", list())
        L = zip(ast.param, ctype.partype)
        #print(ctype.partype)
        for x in L:
            str1, typ1 = self.visit(x[0], Access(frame, nenv, False, True))
            #print(str1)
            in_ = (in_[0] + str1 +
                   (self.emit.emitI2F(frame) if type(typ1) is IntType
                    and type(x[1]) is FloatType else ""), in_[1] + [typ1])
        # self.emit.printout(in_[0])
        # self.emit.printout(self.emit.emitINVOKESTATIC(cname + "/" + ast.method.name, ctype, frame))
        return in_[0] + self.emit.emitINVOKESTATIC(cname + "/" + sym.name,
                                                   ctype, frame), ctype.rettype

    def visitId(self, ast, o):
        ctxt = o
        frame = ctxt.frame
        # print(ctxt.sym[0].name)
        sym = self.lookup(ast.name.lower(), ctxt.sym, lambda x: x.name.lower())
        if (ctxt.isLeft):
            if type(sym.value) is Index:
                return self.emit.emitWRITEVAR(sym.name, sym.mtype,
                                              sym.value.value,
                                              frame), sym.mtype
            else:
                return self.emit.emitPUTSTATIC(
                    sym.value.value + '/' + sym.name, sym.mtype,
                    frame), sym.mtype
        else:
            if type(sym.value) is Index:
                return self.emit.emitREADVAR(sym.name, sym.mtype,
                                             sym.value.value, frame), sym.mtype
            else:
                return self.emit.emitGETSTATIC(
                    sym.value.value + '/' + sym.name, sym.mtype,
                    frame), sym.mtype

    def visitAssign(self, ast, o):
        #lhs:Expr
        #exp:Expr
        ctxt = o
        frame = ctxt.frame
        rhs = self.visit(ast.exp, Access(frame, ctxt.sym, False, ""))[0]
        lhs = self.visit(ast.lhs, Access(frame, ctxt.sym, True, ""))[0]
        self.emit.printout(rhs + lhs)
        return

    def visitIf(self, ast, o):
        ctxt = o
        frame = ctxt.frame
        env = ctxt.sym
        hasElse = len(ast.elseStmt) != 0
        exp, expType = self.visit(ast.expr, Access(frame, env, False, ""))
        elseLabel = frame.getNewLabel()
        nextLabel = frame.getNewLabel() if hasElse else elseLabel
        self.emit.printout(exp + self.emit.emitIFFALSE(elseLabel, frame))
        for stmt in ast.thenStmt:
            self.visit(stmt, SubBody(frame, env))
        self.emit.printout(self.emit.emitGOTO(nextLabel, frame))
        self.emit.printout(self.emit.emitLABEL(elseLabel, frame))
        for stmt in ast.elseStmt:
            self.visit(stmt, SubBody(frame, env))
        if hasElse:
            self.emit.printout(self.emit.emitLABEL(nextLabel, frame))

    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))

    def visitReturn(self, ast, o):
        ctxt = o
        frame = ctxt.frame
        env = ctxt.sym
        if ast.expr is None:
            self.emit.printout(self.emit.emitRETURN(VoidType(), frame))
        else:
            str1, typ1 = self.visit(ast.expr, Access(frame, env, False, ""))
            self.emit.printout(str1)
            if (type(frame.returnType) == FloatType and type(typ1) == IntType):
                self.emit.printout(self.emit.emitI2F(frame))
            self.emit.printout(self.emit.emitRETURN(frame.returnType, frame))

    def visitWhile(self, ast, o):
        frame = o.frame
        frame.enterLoop()
        label1 = frame.getContinueLabel()
        label2 = frame.getBreakLabel()
        code = self.emit.emitLABEL(label1, frame)
        code += self.visit(ast.exp, Access(frame, o.sym, False, True))[0]
        code += self.emit.emitIFFALSE(label2, frame)
        self.emit.printout(code)

        list(map(lambda x: self.visit(x, o), ast.sl))

        code = self.emit.emitGOTO(str(label1), frame)
        code += self.emit.emitLABEL(label2, frame)
        self.emit.printout(code)
        frame.exitLoop()

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

        conLabel = frame.getContinueLabel()
        breLabel = frame.getBreakLabel()
        labelXXX = frame.getNewLabel()

        expr1, typ1 = self.visit(ast.expr1, Access(frame, nenv, False, False))
        _id1, tid_1 = self.visit(ast.id, Access(frame, nenv, True, False))
        self.emit.printout(expr1)
        self.emit.printout(_id1)
        self.emit.printout(self.emit.emitLABEL(labelXXX, frame))
        if ast.up is True:
            _id2, tid_2 = self.visit(ast.id, Access(frame, nenv, False, False))
            self.emit.printout(_id2)
            expr2, typ2 = self.visit(ast.expr2,
                                     Access(frame, nenv, False, False))
            self.emit.printout(expr2)
            self.emit.printout(self.emit.emitIFICMPGT(breLabel, frame))
        else:
            _id2, tid_2 = self.visit(ast.id, Access(frame, nenv, False, False))
            self.emit.printout(_id2)
            expr2, typ2 = self.visit(ast.expr2,
                                     Access(frame, nenv, False, False))
            self.emit.printout(expr2)
            self.emit.printout(self.emit.emitIFICMPLT(breLabel, frame))
        for x in ast.loop:
            self.visit(x, SubBody(frame, nenv))
        self.emit.printout(self.emit.emitLABEL(conLabel, frame))
        if ast.up is True:
            exp_2, ty_2 = self.visit(BinaryOp('+', ast.id, IntLiteral(1)),
                                     Access(frame, nenv, False, False))
            id_1, t_1 = self.visit(ast.id, Access(frame, nenv, True, False))
            self.emit.printout(exp_2)
            self.emit.printout(id_1)
        else:
            exp_2, ty_2 = self.visit(BinaryOp('-', ast.id, IntLiteral(1)),
                                     Access(frame, nenv, False, False))
            id_1, t_1 = self.visit(ast.id, Access(frame, nenv, True, False))
            self.emit.printout(exp_2)
            self.emit.printout(id_1)
        self.emit.printout(self.emit.emitGOTO(labelXXX, frame))
        self.emit.printout(self.emit.emitLABEL(breLabel, frame))
        frame.exitLoop()

    def visitWith(self, ast, o):
        frame = o.frame
        frame.enterScope(False)
        e = SubBody(frame, [])
        for x in ast.decl:
            e = self.visit(x, e)
        self.emit.printout(self.emit.emitLABEL(frame.getStartLabel(), frame))

        arraySym = list(filter(lambda x: isinstance(x.mtype, ArrayType),
                               e.sym))
        codelist = list(
            map(
                lambda x: self.visit(
                    IntLiteral(x.mtype.upper - x.mtype.lower + 1), e)[0] + self
                .emit.emitNEW(x.mtype.eleType, frame) + self.emit.emitWRITEVAR(
                    x.name, ArrayPointerType(x.mtype.eleType), x.value.value, e
                    .frame), arraySym))
        #if codelist !=[]:
        code = reduce(lambda x, y: x + y, codelist, "")
        self.emit.printout(code)

        o = SubBody(frame, e.sym + o.sym)
        list(map(lambda x: self.visit(x, o), ast.stmt))
        self.emit.printout(self.emit.emitLABEL(frame.getEndLabel(), frame))
        frame.exitScope()

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

    def visitIntLiteral(self, ast, o):
        #ast: IntLiteral
        #o: Any

        ctxt = o
        frame = ctxt.frame
        return self.emit.emitPUSHICONST(ast.value, frame), IntType()

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

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

    def visitVarDecl(self, ast, o):
        ctxt = o
        frame = ctxt.frame
        #first visit
        if ctxt.firstVisit:
            return SubBody(None, [
                Symbol(ast.variable.name, ast.varType, CName(self.className))
            ] + ctxt.sym, True)
        #Second visit
        if frame is None:
            self.emit.printout(
                self.emit.emitATTRIBUTE(ast.variable.name, ast.varType, False,
                                        ""))
            #return SubBody(None, [Symbol(ast.variable.name, ast.varType, CName(self.className))] + ctxt.sym)
        else:  #parameter or local variable
            self.emit.printout(
                self.emit.emitVAR(frame.getNewIndex(), ast.variable.name,
                                  ast.varType, frame.getStartLabel(),
                                  frame.getEndLabel(), frame))
            return SubBody(frame, [
                Symbol(ast.variable.name, ast.varType,
                       Index(frame.getCurrIndex() - 1))
            ] + ctxt.sym)

    def visitUnaryOp(self, ast, o):
        ctxt = o
        frame = ctxt.frame
        exp, expType = self.visit(ast.body, o)
        if ast.op == '-':
            return exp + self.emit.emitNEGOP(expType, frame), expType
        elif ast.op == 'not':
            return exp + self.emit.emitNOT(expType, frame), expType

    def visitBinaryOp(self, ast, o):
        #op:string: AND THEN => andthen; OR ELSE => orelse; other => keep it
        #left:Expr
        #right:Expr

        ctxt = o
        frame = ctxt.frame

        leftExp, leftType = self.visit(ast.left, o)
        rightExp, rightType = self.visit(ast.right, o)
        if type(leftType) != type(rightType):
            if type(leftType) == IntType:
                leftExp += self.emit.emitI2F(frame)
            if type(rightType) == IntType:
                rightExp += self.emit.emitI2F(frame)
            if ast.op in ['+', '-']:
                return leftExp + rightExp + self.emit.emitADDOP(
                    ast.op, FloatType(), frame), FloatType()
            elif ast.op in ['*', '/']:
                return leftExp + rightExp + self.emit.emitMULOP(
                    ast.op, FloatType(), frame), FloatType()
            elif ast.op in ['<', '>', '>=', '<=', '=', '<>']:
                return leftExp + rightExp + self.emit.emitREOP(
                    ast.op, leftType, frame), BoolType()

        else:
            if ast.op in ['+', '-']:
                return leftExp + rightExp + self.emit.emitADDOP(
                    ast.op, leftType, frame), leftType
            elif ast.op == '*':
                return leftExp + rightExp + self.emit.emitMULOP(
                    ast.op, leftType, frame), leftType
            elif ast.op == '/':
                if type(leftType) == IntType:
                    return leftExp + self.emit.emitI2F(
                        frame) + rightExp + self.emit.emitI2F(
                            frame) + self.emit.emitMULOP(
                                ast.op, FloatType(), frame), FloatType()
                else:
                    return leftExp + rightExp + self.emit.emitMULOP(
                        ast.op, FloatType(), frame), FloatType()
            elif ast.op.lower() == 'or':
                return leftExp + rightExp + self.emit.emitOROP(
                    frame), BoolType()
            elif ast.op.lower() == 'and':
                return leftExp + rightExp + self.emit.emitANDOP(
                    frame), BoolType()
            elif ast.op.lower() == 'mod':
                return leftExp + rightExp + self.emit.emitMOD(frame), IntType()
            elif ast.op.lower() == 'div':
                return leftExp + rightExp + self.emit.emitDIV(frame), IntType()
            elif ast.op in ['<', '>', '>=', '<=', '=', '<>']:
                if type(leftType) == IntType:
                    leftExp += self.emit.emitI2F(frame)
                if type(rightType) == IntType:
                    rightExp += self.emit.emitI2F(frame)
                return leftExp + rightExp + self.emit.emitREOP(
                    ast.op, leftType, frame), BoolType()
Example #17
0
class CodeGenVisitor(BaseVisitor, Utils):
    def __init__(self, astTree, env, dir_):
        self.astTree = astTree
        self.env = env
        self.className = "MPClass"
        self.path = dir_
        self.emit = Emitter(self.path + "/" + self.className + ".j")
        self.arrayGlobals = list()

    def visitProgram(self, ast, c):
        self.emit.printout(self.emit.emitPROLOG(self.className, "java/lang/Object"))
        funcDecls = []
        for d in ast.decl:
            if isinstance(d, VarDecl):
                self.env = self.visit(d, self.env)
                if type(d.varType) is ArrayType:
                    self.arrayGlobals.append(d)
            else:
                funcDecls.append(d)
                self.env = self.visit(d, (self.env, True))
        for d in funcDecls:
            self.visit(d, (self.env, False))
        self.genCONSTRUCTOR(Frame("<init>", VoidType()))
        if len(self.arrayGlobals) > 0:
            self.genSTATICCONSTRUCTOR(Frame("<clinit>", VoidType()))
        self.emit.emitEPILOG()

    def genSTATICCONSTRUCTOR(self, frame):
        self.emit.printout(self.emit.emitMETHOD("<clinit>", MType(list(), VoidType()), True, frame))
        frame.enterScope(True)
        for arr in self.arrayGlobals:
            self.emit.printout(self.emit.emitNEWARRAY(arr.varType, frame))
            self.emit.printout(self.emit.emitPUTSTATIC(self.className + '.' + arr.variable.name, arr.varType, frame))
        self.emit.printout(self.emit.emitRETURN(VoidType(), frame))
        self.emit.printout(self.emit.emitENDMETHOD(frame))
        frame.exitScope()

    def genCONSTRUCTOR(self, frame):
        self.emit.printout(self.emit.emitMETHOD("<init>", MType(list(), VoidType()), False, frame))
        frame.enterScope(True)
        self.emit.printout(self.emit.emitVAR(frame.getNewIndex(), "this", ClassType(self.className), frame.getStartLabel(), frame.getEndLabel(), frame))
        self.emit.printout(self.emit.emitLABEL(frame.getStartLabel(), frame))
        self.emit.printout(self.emit.emitREADVAR("this", ClassType(self.className), 0, frame))
        self.emit.printout(self.emit.emitINVOKESPECIAL(frame))
        self.emit.printout(self.emit.emitLABEL(frame.getEndLabel(), frame))
        self.emit.printout(self.emit.emitRETURN(VoidType(), frame))
        self.emit.printout(self.emit.emitENDMETHOD(frame))
        frame.exitScope()

    def genMETHOD(self, consdecl, env, frame):
        returnType = consdecl.returnType
        isMain = consdecl.name.name.lower() == "main" and len(consdecl.param) == 0 and type(returnType) is VoidType
        methodName = "main" if isMain else consdecl.name.name
        intype = [ArrayType(None, None, StringType())] if isMain else [p.varType for p in consdecl.param]
        mtype = MType(intype, returnType)
        self.emit.printout(self.emit.emitMETHOD(methodName, mtype, True, frame))
        frame.enterScope(True)
        nenv = env[:]
        if isMain:
            self.emit.printout(self.emit.emitVAR(frame.getNewIndex(), "args", ArrayType(None, None, StringType()), frame.getStartLabel(), frame.getEndLabel(), frame))
        else:
            for p in consdecl.param:
                index = frame.getNewIndex()
                nenv = [Symbol(p.variable.name, p.varType, Index(index))] + nenv
                self.emit.printout(self.emit.emitVAR(index, p.variable.name, p.varType, frame.getStartLabel(), frame.getEndLabel(), frame))
        self.emit.printout(self.emit.emitLABEL(frame.getStartLabel(), frame))
        for v in consdecl.local:
            index = frame.getNewIndex()
            nenv = [Symbol(v.variable.name, v.varType, Index(index))] + nenv
            if type(v.varType) is ArrayType:
                self.emit.printout(self.emit.emitNEWARRAY(v.varType, frame))
                self.emit.printout(self.emit.emitWRITEVAR(v.variable.name, v.varType, index, frame))
        list(map(lambda x: self.visit(x, SubBody(frame, nenv)), consdecl.body))
        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 visitFuncDecl(self, ast, o):
        glenv, isFirstTime = o
        if isFirstTime:
            inType = [x.varType for x in ast.param]
            return [Symbol(ast.name.name, MType(inType, ast.returnType), CName(self.className))] + glenv
        frame = Frame(ast.name.name, ast.returnType)
        self.genMETHOD(ast, glenv, frame)

    def visitVarDecl(self, ast, o):
        glenv = o
        self.emit.printout(self.emit.emitATTRIBUTE(ast.variable.name, ast.varType, False, None))
        return [Symbol(ast.variable.name, ast.varType, CName(self.className))] + glenv

    def visitAssign(self, ast, o):
        ctxt = o
        frame, nenv = ctxt.frame, ctxt.sym
        if isinstance(ast.lhs, Id):
            expCode, expType = self.visit(ast.exp, Access(frame, nenv, False, True))
            self.emit.printout(expCode)
            sym = self.lookup(ast.lhs.name.lower(), nenv, lambda x: x.name.lower())
            if isinstance(expType, IntType) and isinstance(sym.mtype, FloatType):
                self.emit.printout(self.emit.emitI2F(frame))
        else:
            arrayCellCode, arrayCellType = self.visit(ast.lhs, Access(frame, nenv, True, True))
            self.emit.printout(arrayCellCode)
            expCode, expType = self.visit(ast.exp, Access(frame, nenv, False, True))
            self.emit.printout(expCode)
            if isinstance(expType, IntType) and isinstance(arrayCellType, FloatType):
                self.emit.printout(self.emit.emitI2F(frame))
        self.visit(ast.lhs, Access(frame, nenv, True, False))
        return False
    
    def visitWith(self, ast, o):
        ctxt = o
        frame, nenv = ctxt.frame, ctxt.sym
        arrays = list()
        frame.enterScope(False)
        for d in ast.decl:
            index = frame.getNewIndex()
            nenv = [Symbol(d.variable.name, d.varType, Index(index))] + nenv
            if type(d.varType) is ArrayType:
                arrays.append((d, index))
            self.emit.printout(self.emit.emitVAR(index, d.variable.name, d.varType, frame.getStartLabel(), frame.getEndLabel(), frame))
        self.emit.printout(self.emit.emitLABEL(frame.getStartLabel(), frame))
        for arr, index in arrays:
            self.emit.printout(self.emit.emitNEWARRAY(arr.varType, frame))
            self.emit.printout(self.emit.emitWRITEVAR(arr.variable.name, arr.varType, index, frame))
        unReachable = False
        for stmt in ast.stmt:
            unReachable = self.visit(stmt, SubBody(frame, nenv))
        self.emit.printout(self.emit.emitLABEL(frame.getEndLabel(), frame))
        frame.exitScope()
        return unReachable

    def visitIf(self, ast, o):
        ctxt = o
        frame, nenv = ctxt.frame, ctxt.sym
        self.emit.printout(self.visit(ast.expr, Access(frame, nenv, False, True))[0])
        if len(ast.elseStmt) == 0:
            label = frame.getNewLabel()
            self.emit.printout(self.emit.emitIFFALSE(label, frame))
            list(map(lambda x: self.visit(x, SubBody(frame, nenv)), ast.thenStmt))
            self.emit.printout(self.emit.emitLABEL(label, frame))
            return False
        else:
            label1 = frame.getNewLabel()
            label2 = None
            unReachableThen, unReachableElse = False, False
            self.emit.printout(self.emit.emitIFFALSE(label1, frame))
            for stmt in ast.thenStmt:
                unReachableThen = self.visit(stmt, SubBody(frame, nenv))
            if not unReachableThen:
                label2 = frame.getNewLabel()
                self.emit.printout(self.emit.emitGOTO(label2, frame))
            self.emit.printout(self.emit.emitLABEL(label1, frame))
            for stmt in ast.elseStmt:
                unReachableElse = self.visit(stmt, SubBody(frame, nenv))
            if not unReachableThen:
                self.emit.printout(self.emit.emitLABEL(label2, frame))
            return unReachableThen and unReachableElse
    
    def visitFor(self, ast, o):
        ctxt = o
        frame, nenv = ctxt.frame, ctxt.sym
        frame.enterLoop()
        continueLabel, breakLabel = frame.getContinueLabel(), frame.getBreakLabel()
        labelS = frame.getNewLabel()
        ini_ = Assign(ast.id, ast.expr1)
        self.visit(ini_, SubBody(frame, nenv))
        self.emit.printout(self.emit.emitLABEL(labelS, frame))
        sym = self.lookup(ast.id.name.lower(), nenv, lambda x: x.name.lower())
        isLocal = type(sym.value) is Index
        if isLocal:
            self.emit.printout(self.emit.emitREADVAR(sym.name, sym.mtype, sym.value.value, frame))
        else:
            self.emit.printout(self.emit.emitGETSTATIC(sym.value.value + '.' + sym.name, sym.mtype, frame))
        expr2Code, expr2Type = self.visit(ast.expr2, Access(frame, nenv, False, True))
        self.emit.printout(expr2Code)
        if ast.up:
            self.emit.printout(self.emit.emitIFICMPGT(breakLabel, frame))
        else:
            self.emit.printout(self.emit.emitIFICMPLT(breakLabel, frame))
        list(map(lambda x: self.visit(x, SubBody(frame, nenv)), ast.loop))
        self.emit.printout(self.emit.emitLABEL(continueLabel, frame))
        step_ = Assign(ast.id, BinaryOp('+', ast.id, IntLiteral(1))) if ast.up else Assign(ast.id, BinaryOp('-', ast.id, IntLiteral(1)))
        self.visit(step_, SubBody(frame, nenv))
        self.emit.printout(self.emit.emitGOTO(labelS, frame))
        self.emit.printout(self.emit.emitLABEL(breakLabel, frame))
        frame.exitLoop()
        return False

    def visitContinue(self, ast, o):
        ctxt = o
        frame, nenv = ctxt.frame, ctxt.sym
        self.emit.printout(self.emit.emitGOTO(frame.getContinueLabel(), frame))
        return True
    
    def visitBreak(self, ast, o):
        ctxt = o
        frame, nenv = ctxt.frame, ctxt.sym
        self.emit.printout(self.emit.emitGOTO(frame.getBreakLabel(), frame))
        return True
    
    def visitReturn(self, ast, o):
        ctxt = o
        frame, nenv = ctxt.frame, ctxt.sym
        if ast.expr is None:
            self.emit.printout(self.emit.emitRETURN(VoidType(), frame))
        else:
            expCode, expType = self.visit(ast.expr, Access(frame, nenv, False, True))
            self.emit.printout(expCode)
            retType = expType
            if isinstance(expType, IntType) and isinstance(frame.returnType, FloatType):
                self.emit.printout(self.emit.emitI2F(frame))
                retType = FloatType()
            self.emit.printout(self.emit.emitRETURN(retType, frame))
        return True
    
    def visitWhile(self, ast, o):
        ctxt = o
        frame, nenv = ctxt.frame, ctxt.sym
        frame.enterLoop()
        continueLabel, breakLabel = frame.getContinueLabel(), frame.getBreakLabel()
        self.emit.printout(self.emit.emitLABEL(continueLabel, frame))
        self.emit.printout(self.visit(ast.exp, Access(frame, nenv, False, True))[0])
        self.emit.printout(self.emit.emitIFFALSE(breakLabel, frame))
        list(map(lambda x: self.visit(x, SubBody(frame, nenv)), ast.sl))
        self.emit.printout(self.emit.emitGOTO(continueLabel, frame))
        self.emit.printout(self.emit.emitLABEL(breakLabel, frame))
        frame.exitLoop()
        return False

    def visitCallStmt(self, ast, o):
        ctxt = o
        frame, nenv = ctxt.frame, ctxt.sym
        sym = self.lookup(ast.method.name.lower(), nenv, lambda x: x.name.lower())
        cName = sym.value.value
        ctype = sym.mtype
        inType = ctype.partype
        for i in range(len(inType)):
            expCode, expType = self.visit(ast.param[i], Access(frame, nenv, False, True))
            self.emit.printout(expCode)
            if isinstance(expType, IntType) and isinstance(inType[i], FloatType):
                self.emit.printout(self.emit.emitI2F(frame))
            if isinstance(expType, ArrayType):
                if type(expType.eleType) is not StringType:
                    self.emit.printout(self.emit.emitCLONE(expType, frame))
        self.emit.printout(self.emit.emitINVOKESTATIC(cName + "/" + sym.name, ctype, frame))
        return False

    def visitBinaryOp(self, ast, o):
        access = o
        frame, nenv = access.frame, access.sym
        leftCode, leftType = self.visit(ast.left, Access(frame, nenv, False, True))
        op = ast.op.lower()
        expCode = ""
        if op in ['+', '-', '*', '/', 'div', 'mod', 'and', 'or']:
            rightCode, rightType = self.visit(ast.right, Access(frame, nenv, False, True))
            expType = FloatType() if type(leftType) != type(rightType) else leftType
            expType = FloatType() if op == '/' else expType
            expCode += leftCode
            if type(leftType) != type(expType):
                expCode += self.emit.emitI2F(frame)
            expCode += rightCode
            if type(rightType) != type(expType):
                expCode += self.emit.emitI2F(frame)
            if op in ['+', '-']:
                expCode += self.emit.emitADDOP(op, expType, frame)
            elif op in ['*', '/']:
                expCode += self.emit.emitMULOP(op, expType, frame)
            elif op == 'div':
                expCode += self.emit.emitDIV(frame)
            elif op == 'mod':
                expCode += self.emit.emitMOD(frame)
            elif op == 'and':
                expCode += self.emit.emitANDOP(frame)
            elif op == 'or':
                expCode += self.emit.emitOROP(frame)
        elif op in ['>', '<', '>=', '<=', '<>', '=']:
            rightCode, rightType = self.visit(ast.right, Access(frame, nenv, False, True))
            expType = BoolType()
            relType = FloatType() if type(leftType) is not type(rightType) else leftType
            expCode += leftCode
            if type(leftType) != type(relType):
                expCode += self.emit.emitI2F(frame)
            expCode += rightCode
            if type(rightType) != type(relType):
                expCode += self.emit.emitI2F(frame)
            expCode += self.emit.emitREOP(op, relType, frame)
        else:
            expType = BoolType()
            expCode += leftCode
            label1 = frame.getNewLabel()
            label2 = frame.getNewLabel()
            expCode += self.emit.emitIFFALSE(label1, frame) if op == 'andthen' else self.emit.emitIFTRUE(label1, frame)
            rightCode, rightType = self.visit(ast.right, Access(frame, nenv, False, True))
            expCode += rightCode
            expCode += self.emit.emitIFFALSE(label1, frame) if op == 'andthen' else self.emit.emitIFTRUE(label1, frame)
            expCode += self.emit.emitPUSHCONST(str(op == 'andthen'), BoolType(), frame)
            expCode += self.emit.emitGOTO(label2, frame)
            expCode += self.emit.emitLABEL(label1, frame)
            expCode += self.emit.emitPUSHCONST(str(op == 'orelse'), BoolType(), frame)
            expCode += self.emit.emitLABEL(label2, frame)
            frame.pop()
        return expCode, expType

    def visitUnaryOp(self, ast, o):
        access = o
        frame, nenv = access.frame, access.sym
        bodyCode, bodyType = self.visit(ast.body, Access(frame, nenv, False, True))
        expCode, expType = bodyCode, bodyType
        if ast.op == '-':
            expCode += self.emit.emitNEGOP(expType, frame)
        else:
            expCode += self.emit.emitNOT(expType, frame)
        return expCode, expType
    
    def visitCallExpr(self, ast, o):
        access = o
        frame, nenv = access.frame, access.sym
        sym = self.lookup(ast.method.name.lower(), nenv, lambda x: x.name.lower())
        cName = sym.value.value
        ctype = sym.mtype
        inType = ctype.partype
        expCode, expType = '', ctype.rettype
        for i in range(len(inType)):
            paramCode, paramType = self.visit(ast.param[i], Access(frame, nenv, False, True))
            expCode += paramCode
            if isinstance(paramType, IntType) and isinstance(inType[i], FloatType):
                expCode += self.emit.emitI2F(frame)
            if isinstance(paramType, ArrayType):
                if type(paramType.eleType) is not StringType:
                    expCode += self.emit.emitCLONE(paramType, frame)
        expCode += self.emit.emitINVOKESTATIC(cName + "/" + sym.name, ctype, frame)
        return expCode, expType
    
    def visitId(self, ast, o):
        access = o
        frame, nenv, isLeft, isFirst = access.frame, access.sym, access.isLeft, access.isFirst
        sym = self.lookup(ast.name.lower(), nenv, lambda x: x.name.lower())
        idType, isLocal = sym.mtype, type(sym.value) is Index
        if isLeft:
            if isLocal:
                self.emit.printout(self.emit.emitWRITEVAR(sym.name, idType, sym.value.value, frame))
            else:
                self.emit.printout(self.emit.emitPUTSTATIC(sym.value.value + '.' + sym.name, idType, frame))
        else:
            if isLocal:
                idCode = self.emit.emitREADVAR(sym.name, idType, sym.value.value, frame)
            else:
                idCode = self.emit.emitGETSTATIC(sym.value.value + '.' + sym.name, idType, frame)
            return idCode, idType

    def visitArrayCell(self, ast, o):
        access = o
        frame, nenv, isLeft, isFirst = access.frame, access.sym, access.isLeft, access.isFirst
        if isLeft and not isFirst:
            isId = type(ast.arr) is Id
            name = ast.arr.name if isId else ast.arr.method.name
            sym = self.lookup(name.lower(), nenv, lambda x: x.name.lower())
            arrType = sym.mtype if isId else sym.mtype.rettype
            frame.push()
            self.emit.printout(self.emit.emitASTORE(arrType.eleType, frame))
        else:
            arrCode, arrType = self.visit(ast.arr, Access(frame, nenv, False, True))
            idxCode, idxType = self.visit(ast.idx, Access(frame, nenv, False, True))
            arrayCellCode = arrCode + idxCode
            arrayCellCode += self.emit.emitPUSHICONST(arrType.lower, frame)
            arrayCellCode += self.emit.emitADDOP('-', IntType(), frame)
            if not isLeft:
                arrayCellCode += self.emit.emitALOAD(arrType.eleType, frame)
            return arrayCellCode, arrType.eleType
    
    def visitIntLiteral(self, ast, o):
        access = o
        frame = access.frame
        return self.emit.emitPUSHICONST(ast.value, frame), IntType()
    
    def visitFloatLiteral(self, ast, o):
        access = o
        frame = access.frame
        return self.emit.emitPUSHFCONST(ast.value, frame), FloatType()

    def visitBooleanLiteral(self, ast, o):
        access = o
        frame = access.frame
        return self.emit.emitPUSHCONST(str(ast.value), BoolType(), frame), BoolType()

    def visitStringLiteral(self, ast, o):
        access = o
        frame = access.frame
        return self.emit.emitPUSHCONST(ast.value, StringType(), frame), StringType()
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)
        for x in ast.decl:
            e = self.visit(x, e)

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

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

        isInit = consdecl.returnType is None
        isMain = consdecl.name.name == "main" and len(
            consdecl.param) == 0 and type(consdecl.returnType) is VoidType
        returnType = VoidType() if isInit else consdecl.returnType
        methodName = "<init>" if isInit else consdecl.name.name
        intype = [ArrayPointerType(StringType())] if isMain else list()
        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
        listParamArray = []  # list(Symbol(name, mtype, value: Index(idx)))
        listLocalArray = []  # list(Symbol(name, mtype, value: Index(idx)))

        if isInit:
            self.emit.printout(
                self.emit.emitVAR(frame.getNewIndex(), "this",
                                  ClassType(self.className),
                                  frame.getStartLabel(), frame.getEndLabel(),
                                  frame))
        elif isMain:
            self.emit.printout(
                self.emit.emitVAR(frame.getNewIndex(), "args",
                                  ArrayPointerType(StringType()),
                                  frame.getStartLabel(), frame.getEndLabel(),
                                  frame))
        else:
            e = SubBody(frame, glenv)
            for x in consdecl.param:
                e = self.visit(x, e)
                glenv = e.sym
                if type(x.varType) is ArrayType:
                    idx = glenv[0].value.value
                    self.emit.printout(
                        self.emit.emitINITARRAY(idx, x.varType, frame))
                    listParamArray.append(glenv[0])

        for x in listParamArray:
            self.emit.printout(
                self.emit.emitCOPYARRAY(x.value.value, x.mtype, frame))

        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))
        #list(map(lambda x: self.visit(x, SubBody(frame, glenv)), body.member))
        for x in body.member:
            if type(x) is VarDecl:
                e = SubBody(frame, glenv)
                e = self.visit(x, e)
                glenv = e.sym
                if type(x.varType) is ArrayType:
                    idx = glenv[0].value.value
                    self.emit.printout(
                        self.emit.emitINITARRAY(idx, x.varType, frame))
            #   self.emit.printout(self.visit(x, SubBody(frame, glenv))[0])
            else:
                self.visit(x, 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))
        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, frame)
        return SubBody(None, [
            Symbol(ast.name.name, MType(list(), ast.returnType),
                   CName(self.className))
        ] + subctxt.sym)

    def visitBlock(self, ast, o):
        #ast: Block

        ctxt = o
        frame = ctxt.frame
        nenv = ctxt.sym
        for x in ast.member:
            self.visit(x, o)

    def visitVarDecl(self, ast, o):
        #ast: VarDecl
        #o: SubBody
        #ast.variable: Id
        #ast.varType: Type

        subctxt = o
        frame = subctxt.frame
        mtype = ast.varType
        name = ast.variable
        if frame is None:
            # Decl mot bien global
            self.emit.printout(self.emit.emitATTRIBUTE(name, mtype, False, ""))

            return SubBody(None, [Symbol(name, mtype, CName(self.className))] +
                           subctxt.sym)
        else:
            # Decl mot bien local hoac param
            idx = frame.getNewIndex()
            self.emit.printout(
                self.emit.emitVAR(idx, name, mtype, frame.getStartLabel(),
                                  frame.getEndLabel(), frame))
            return SubBody(frame,
                           [Symbol(name, mtype, Index(idx))] + subctxt.sym)

    def visitIf(self, ast, o):
        #ast: If
        #o: Any
        #expr:Expr
        #thenStmt:Stmt
        #elseStmt:Stmt
        ctxt = o
        frame = ctxt.frame
        nenv = ctxt.sym

        expr, _ = self.visit(ast.expr, Access(frame, nenv, False, True))
        self.emit.printout(expr)

        label1 = frame.getNewLabel()
        label2 = None
        if ast.elseStmt is not None:
            label2 = frame.getNewLabel()

        self.emit.printout(self.emit.emitIFFALSE(label1, frame))
        self.visit(ast.thenStmt, o)
        if ast.elseStmt is not None:
            self.emit.printout(self.emit.emitGOTO(str(label2), frame))
        self.emit.printout(self.emit.emitLABEL(label1, frame))
        if ast.elseStmt is not None:
            self.visit(ast.elseStmt, o)
            self.emit.printout(self.emit.emitLABEL(label2, frame))

    def visitDowhile(self, ast, o):
        #o: Any
        #ast.sl: List[Stmt]
        #ast.exp: Expr

        ctxt = o
        frame = ctxt.frame
        nenv = ctxt.sym

        frame.enterLoop()

        labelContinue = frame.getContinueLabel()
        labelBreak = frame.getBreakLabel()
        list(map(lambda x: self.visit(x, o), ast.sl))
        self.emit.printout(self.emit.emitLABEL(labelContinue, frame))

        expr, _ = self.visit(ast.exp, Access(frame, nenv, False, True))
        self.emit.printout(expr)
        self.emit.printout(self.emit.emitIFFALSE(labelBreak, frame))
        list(map(lambda x: self.visit(x, o), ast.sl))

        self.emit.printout(self.emit.emitGOTO(str(labelContinue), frame))
        self.emit.printout(self.emit.emitLABEL(labelBreak, frame))

    def visitFor(self, ast, o):
        #o: Any
        #expr1:Expr
        #expr2:Expr
        #expr3:Expr
        #loop:Stmt

        ctxt = o
        frame = ctxt.frame
        nenv = ctxt.sym

        expr1, _ = self.visit(ast.expr1, Access(frame, nenv, False, True))
        self.emit.printout(expr1)

        frame.enterLoop()
        labelContinue = frame.getContinueLabel()
        labelBreak = frame.getBreakLabel()

        expr2, _ = self.visit(ast.expr2, Access(frame, nenv, False, True))
        self.emit.printout(expr2)
        self.emit.printout(self.emit.emitIFFALSE(labelBreak, frame))

        self.visit(ast.loop, o)

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

        if type(ast.expr3) is BinaryOp and ast.expr3.op == '=':
            self.visit(ast.expr3, Access(frame, nenv, False, True))
        else:
            expr3, _ = self.visit(ast.expr3, Access(frame, nenv, False, True))
            self.emit.printout(expr3)

        expr2, _ = self.visit(ast.expr2, Access(frame, nenv, False, True))
        self.emit.printout(expr2)
        self.emit.printout(self.emit.emitIFFALSE(labelBreak, frame))

        self.visit(ast.loop, o)

        self.emit.printout(self.emit.emitGOTO(str(labelContinue), frame))
        self.emit.printout(self.emit.emitLABEL(labelBreak, frame))

    def visitBreak(self, ast, o):
        #o:any

        ctxt = o
        frame = ctxt.frame
        self.emit.printout(self.emit.emitGOTO(frame.getBreakLabel(), frame))

    def visitContinue(self, ast, o):
        #o:any

        ctxt = o
        frame = ctxt.frame
        self.emit.printout(self.emit.emitGOTO(frame.getContinueLabel(), frame))

    def visitReturn(self, ast, o):
        #o:any
        #ast.expr: Expr

        ctxt = o
        frame = ctxt.frame
        nenv = ctxt.sym

        if ast.expr is not None:
            str1, typ1 = self.visit(ast.expr, Access(frame, nenv, False, True))
            if type(typ1) is IntType and type(frame.returnType) is FloatType:
                str1 += self.emit.emitI2F(frame)
            self.emit.printout(str1)
        #self.emit.printout(self.emit.emitGOTO(frame.getEndLabel(), frame))
        self.emit.printout(self.emit.emitRETURN(frame.returnType, frame))

    def visitArrayCell(self, ast, o):
        #o:any
        #ast.arr:Expr
        #ast.idx:Expr

        ctxt = o
        frame = ctxt.frame
        nenv = ctxt.sym

        lst = []
        arr, typeArr = self.visit(ast.arr, Access(frame, nenv, False, True))
        idx, typeIdx = self.visit(ast.idx, Access(frame, nenv, False, True))
        typ = typeArr.eleType
        lst.append(arr)
        lst.append(self.emit.emitPUSHICONST(typeArr.dimen - 1, frame))
        lst.append(idx)
        lst.append(self.emit.emitADDOP('-', IntType(), frame))
        if not o.isLeft:
            lst.append(self.emit.emitALOAD(typ, frame))
        return ''.join(lst), typ

    def visitBinaryOp(self, ast, o):
        #ast:BinaryOp
        #ast.op:str
        #ast.left:Expr
        #ast.right:Expr

        ctxt = o
        frame = ctxt.frame
        nenv = ctxt.sym

        leftOp, typeLHS = self.visit(ast.left, Access(frame, nenv, False,
                                                      True))
        if type(ast.right) == CallExpr:
            self.visit(ast.right, Access(frame, nenv, False, True))
        else:
            rightOp, typeRHS = self.visit(ast.right,
                                          Access(frame, nenv, False, True))

        if ast.op == '=':
            if type(ast.left) is ArrayCell:
                leftOp, typeLHS = self.visit(ast.left,
                                             Access(frame, nenv, True, True))
                self.emit.printout(leftOp)
                if type(ast.right) == CallExpr:
                    self.visit(ast.right, Access(frame, nenv, False, True))
                else:
                    rightOp, typeRHS = self.visit(
                        ast.right, Access(frame, nenv, False, True))
                    self.emit.printout(rightOp)
                if type(typeLHS) != type(typeRHS):
                    self.emit.printout(self.emit.emitI2F(frame))
                self.emit.printout(self.emit.emitASTORE(typeLHS, frame))
            else:
                leftOp, typeLHS = self.visit(ast.left,
                                             Access(frame, nenv, True, True))
                if type(ast.right) == CallExpr:
                    self.visit(ast.right, Access(frame, nenv, False, True))
                else:
                    rightOp, typeRHS = self.visit(
                        ast.right, Access(frame, nenv, False, True))
                    if type(typeRHS) is IntType and type(typeLHS) is FloatType:
                        rightOp += self.emit.emitI2F(frame)
                    self.emit.printout(rightOp + leftOp)
        elif type(typeLHS) == type(typeRHS):
            if type(typeLHS) is BoolType:
                if ast.op.lower() == '&&':
                    return leftOp + rightOp + self.emit.emitANDOP(
                        frame), BoolType()
                elif ast.op.lower() == '||':
                    return leftOp + rightOp + self.emit.emitOROP(
                        frame), BoolType()
                elif ast.op in ['==', '!=']:
                    return leftOp + rightOp + self.emit.emitREOP(
                        ast.op, BoolType(), frame), BoolType()
            elif type(typeLHS) is IntType:
                if ast.op in ['+', '-']:
                    return leftOp + rightOp + self.emit.emitADDOP(
                        ast.op, IntType(), frame), IntType()
                elif ast.op == '*':
                    return leftOp + rightOp + self.emit.emitMULOP(
                        ast.op, IntType(), frame), IntType()
                elif ast.op == '/':
                    return leftOp + rightOp + self.emit.emitDIV(
                        frame), IntType()
                elif ast.op == '%':
                    return leftOp + rightOp + self.emit.emitMOD(
                        frame), IntType()
                elif ast.op in ['<', '<=', '>', '>=', '!=', '==']:
                    return leftOp + rightOp + self.emit.emitREOP(
                        ast.op, IntType(), frame), BoolType()
            elif type(typeLHS) is FloatType:
                if ast.op in ['+', '-']:
                    return leftOp + rightOp + self.emit.emitADDOP(
                        ast.op, FloatType(), frame), FloatType()
                elif ast.op == '*':
                    return leftOp + rightOp + self.emit.emitMULOP(
                        ast.op, FloatType(), frame), FloatType()
                elif ast.op == '/':
                    return leftOp + rightOp + self.emit.emitMULOP(
                        ast.op, FloatType(), frame), FloatType()
                elif ast.op in ['<', '<=', '>', '>=', '!=', '==']:
                    return self.emit.emitFREOP(ast.op, leftOp, rightOp,
                                               frame), BoolType()
        else:
            if ast.op in ['+', '-']:
                if type(typeLHS) is FloatType and type(typeRHS) is IntType:
                    return leftOp + rightOp + self.emit.emitI2F(
                        frame) + self.emit.emitADDOP(ast.op, FloatType(),
                                                     frame), FloatType()
                elif type(typeLHS) is IntType and type(typeRHS) is FloatType:
                    return leftOp + self.emit.emitI2F(
                        frame) + rightOp + self.emit.emitADDOP(
                            ast.op, FloatType(), frame), FloatType()
            elif ast.op == '*':
                if type(typeLHS) is FloatType and type(typeRHS) is IntType:
                    return leftOp + rightOp + self.emit.emitI2F(
                        frame) + self.emit.emitMULOP(ast.op, FloatType(),
                                                     frame), FloatType()
                elif type(typeLHS) is IntType and type(typeRHS) is FloatType:
                    return leftOp + self.emit.emitI2F(
                        frame) + rightOp + self.emit.emitMULOP(
                            ast.op, FloatType(), frame), FloatType()
            else:
                if type(typeLHS) is IntType:
                    leftOp += self.emit.emitI2F(frame)
                if type(typeRHS) is IntType:
                    rightOp += self.emit.emitI2F(frame)
                if ast.op == '/':
                    return leftOp + rightOp + self.emit.emitMULOP(
                        ast.op, FloatType(), frame), FloatType()
                elif ast.op in ['<', '<=', '>', '>=', '!=', '==']:
                    return self.emit.emitFREOP(ast.op, leftOp, rightOp,
                                               frame), BoolType()

    def visitUnaryOp(self, ast, o):
        #o: Any
        #ast.op: string
        #ast.body: Expr

        ctxt = o
        frame = ctxt.frame
        nenv = ctxt.sym

        body, typ = self.visit(ast.body, Access(frame, nenv, False, True))
        if ast.op == '!' and type(typ) is BoolType:
            return body + self.emit.emitNOT(IntType(), frame), BoolType()
        elif ast.op == '-' and type(typ) is IntType:
            return body + self.emit.emitNEGOP(IntType(), frame), IntType()
        elif ast.op == '-' and type(typ) is FloatType:
            return body + self.emit.emitNEGOP(FloatType(), frame), FloatType()

    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_ = ("", [])
        for x in ast.param:
            if type(x) is CallExpr:
                self.visit(x, Access(frame, nenv, False, True))
            else:
                str1, typ1 = self.visit(x, Access(frame, nenv, False, True))
                in_ = (in_[0] + str1, in_[1] + [typ1])
        self.emit.printout(in_[0])
        self.emit.printout(
            self.emit.emitINVOKESTATIC(cname + "/" + ast.method.name, ctype,
                                       frame))

    def visitId(self, ast, o):
        #o: Any
        #ast.name: string

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

        typ = sym.mtype

        if o.isLeft:
            if type(sym.value) is CName:
                return self.emit.emitPUTSTATIC(
                    sym.value.value + "/" + sym.name, typ, o.frame), typ
            else:
                return self.emit.emitWRITEVAR(sym.name, typ, sym.value.value,
                                              o.frame), typ
        else:
            if type(sym.value) is CName:
                return self.emit.emitGETSTATIC(
                    sym.value.value + "/" + sym.name, typ, o.frame), typ
            else:
                return self.emit.emitREADVAR(sym.name, typ, sym.value.value,
                                             o.frame), typ

    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):
        #ast: IntLiteral
        #o: Any

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

    def visitBooleanLiteral(self, ast, o):
        #ast: BooleanLiteral
        #o: Any

        ctxt = o
        frame = ctxt.frame
        return self.emit.emitPUSHCONST(
            str(ast.value).lower(), IntType(), frame), BoolType()

    def visitStringLiteral(self, ast, o):
        #ast: StringLiteral
        #o: Any

        ctxt = o
        frame = ctxt.frame
        return self.emit.emitPUSHCONST('''"''' + ast.value + '''"''',
                                       StringType(), frame), StringType()
Example #19
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

        self.emit.printout(
            self.emit.emitPROLOG(self.className, "java.lang.Object"))
        e = SubBody(None, self.env)
        for x in ast.decl:
            e = self.visit(x, e)
        # generate default constructor
        self.genMETHOD(FuncDecl(Id("<init>"), list(), None, Block(list())), c,
                       Frame("<init>", VoidType))
        self.emit.emitEPILOG()
        return c

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

        isInit = consdecl.returnType is None
        isMain = consdecl.name.name == "main" and len(
            consdecl.param) == 0 and type(consdecl.returnType) is VoidType
        returnType = VoidType() if isInit else consdecl.returnType
        methodName = "<init>" if isInit else consdecl.name.name
        intype = [ArrayPointerType(StringType())] if isMain else list()
        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))

        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))

        #list(map(lambda x: self.visit(x, SubBody(frame, glenv)), body.member))
        for x in body.member:  # TODO: change to visitBody
            if type(x) is VarDecl:
                glenv = self.visit(x, SubBody(frame, glenv.sym))
            else:
                self.visit(x, SubBody(frame, glenv.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 visitReturn(self, ast, o):
        ctxt = o
        frame = ctxt.frame
        retType = frame.returnType

        if ast.expr:
            exprStr, exprType = self.visit(
                ast.expr, Access(frame, ctxt.sym, False, True))
            self.emit.printout(exprStr)

            if type(exprType) is IntType and type(retType) is FloatType:
                self.emit.printout(self.emit.emitI2F(frame))

        self.emit.printout(self.emit.emitRETURN(retType, frame))

    def visitFuncDecl(self, ast, o):
        #ast: FuncDecl
        #o: Any

        subctxt = o
        frame = Frame(ast.name.name, ast.returnType)
        self.genMETHOD(ast, subctxt, frame)
        paramTypes = list(map(lambda x: x.varType, ast.param))
        return SubBody(None, [
            Symbol(ast.name.name, MType(paramTypes, ast.returnType),
                   CName(self.className))
        ] + subctxt.sym)

    def visitVarDecl(self, ast, o):
        ctxt = o
        frame = ctxt.frame
        varName = ast.variable
        varType = ast.varType
        if frame is None:  #global
            self.emit.printout(
                self.emit.emitATTRIBUTE(varName, varType, False, None))
            return SubBody(None,
                           [Symbol(varName, varType, CName(self.className))] +
                           ctxt.sym)

        else:  #local var
            index = frame.getNewIndex()
            var = self.emit.emitVAR(index, varName, varType,
                                    frame.getStartLabel(), frame.getEndLabel(),
                                    frame)
            self.emit.printout(
                var)  # TODO: Can you move .getLabel() into Emitter?
            return SubBody(frame, [Symbol(varName, varType, index)] + ctxt.sym)

    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))
        return in_[0], ctype

    def visitDowhile(self, ast, o):
        ctxt = o
        frame = ctxt.frame
        frame.enterLoop()
        self.emit.printout(self.emit.emitLABEL(frame.getContinueLabel(),
                                               frame))
        for st in ast.sl:  # TODO: Add updating sym
            self.visit(st, SubBody(frame, ctxt.sym))
        exp, exptyp = self.visit(ast.exp, Access(frame, ctxt.sym, False,
                                                 False))
        self.emit.printout(exp)
        self.emit.printout(
            self.emit.emitIFTRUE(frame.getContinueLabel(), frame))
        self.emit.printout(self.emit.emitLABEL(frame.getBreakLabel(), frame))

        frame.exitLoop()

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

        elseLabel = frame.getNewLabel()
        endLabel = frame.getNewLabel()

        expStr, expType = self.visit(ast.expr,
                                     Access(frame, o.sym, False, False))
        self.emit.printout(expStr)
        self.emit.printout(self.emit.emitIFFALSE(elseLabel, frame))

        self.visit(ast.thenStmt, o)
        self.emit.printout(self.emit.emitGOTO(endLabel, frame))

        self.emit.printout(self.emit.emitLABEL(elseLabel, frame))
        if ast.elseStmt:
            self.visit(ast.elseStmt, o)  # TODO: printout somewhere??

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

    def visitFor(self, ast, o):
        # expr1:Expr
        # expr2:Expr
        # expr3:Expr
        # loop:Stmt
        ctxt = o
        frame = ctxt.frame
        frame.enterLoop()
        exp1, exp1typ = self.visit(ast.expr1,
                                   Access(frame, ctxt.sym, False, False))

        self.emit.printout(self.emit.emitLABEL(frame.getContinueLabel(),
                                               frame))
        exp2, exp2typ = self.visit(ast.expr2,
                                   Access(frame, ctxt.sym, False, False))
        self.emit.printout(exp2)
        self.emit.printout(self.emit.emitIFFALSE(frame.getBreakLabel(), frame))

        # loop
        self.visit(ast.loop, ctxt)  #TODO: ctxt Correct?
        #list(map(lambda x: self.visit(x, SubBody(frame, ctxt.sym)), ast.loop)) # from Jim
        #exp3
        exp3, exp3typ = self.visit(ast.expr3, o)

        self.emit.printout(self.emit.emitGOTO(frame.getContinueLabel(), frame))
        self.emit.printout(self.emit.emitLABEL(frame.getBreakLabel(), frame))

        frame.exitLoop()

    # evaluate expr1
    # label1
    # evaluate expr2
    # if false, jump to label 2
    # evaluate loop
    # evaluate expr 3
    # goto label1
    # label 2

    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))

    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):
        #ast: FloatLiteral
        #o: Any

        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('"' + ast.value + '"', StringType(),
                                       frame), BoolType()

    def visitUnaryOp(self, ast, o):
        # TODO: [] needed?
        ctxt = o
        frame = ctxt.frame
        operator = ast.op
        expStr, expType = self.visit(ast.body, o)

        if operator == '-':
            return expStr + self.emit.emitNEGOP(expType, frame), expType
        if operator == '!':
            return expStr + self.emit.emitNOT(BoolType(), frame), expType

    def visitBinaryOp(self, ast, o):
        ctxt = o
        frame = ctxt.frame
        if ast.op == "=":
            return self.visitAssignment(ast, o)

        if ast.op in ["+", "-", "*", "/"]:
            operandStr, type_ = self.getOperands(ast.left, ast.right, o)
            if ast.op == "+" or ast.op == "-":
                operandStr += self.emit.emitADDOP(ast.op, type_, frame)
            else:
                operandStr += self.emit.emitMULOP(ast.op, type_, frame)

            return operandStr, type_
        if ast.op in [">", "<", ">=", "<=", "==", "!="]:
            leftStr, leftType = self.visit(ast.left,
                                           Access(frame, o.sym, False, False))
            rightStr, rightType = self.visit(
                ast.right, Access(frame, o.sym, False, False))

            operandStr = leftStr + rightStr
            if type(leftType) is FloatType and type(rightType) is IntType:
                operandStr += self.emit.emitI2F(frame)

            operandStr += self.emit.emitREOP(ast.op, leftType, frame)
            return operandStr, BoolType()  # TODO: with or without brackets?!

    def visitAssignment(self, ast, o):
        # a = b (index1 = index2)
        # iload_2
        # dup # always leave value on stack after assignment/expression
        # istore_1

        ctxt = o
        frame = ctxt.frame
        #this visit just for type checking
        _, l_ = self.visit(ast.left, Access(frame, o.sym, True, True))

        rightStr, r_ = self.visit(ast.right, Access(frame, o.sym, False,
                                                    False))
        operandStr = rightStr

        leftType = type(l_)
        rightType = type(r_)

        if leftType is not rightType:
            if leftType is IntType and rightType is FloatType:
                raise Exception(
                    "Cannot assign float to int.. But didn't we do that in StaticCheck?!"
                )
            elif leftType is FloatType and rightType is IntType:
                operandStr += self.emit.emitI2F(frame)
            # else:
            #     raise NotImplementedError("Supporting only Int and Float atm") # cannot use this because it breaks genMETHOD. lol.

            # duplicate result of assignment so it stays after storing
            # get store cell
        leftStr, leftType = self.visit(ast.left,
                                       Access(frame, o.sym, True, False))
        operandStr += leftStr  # TODO: CHECK: deleted "self.emit.emitDUP(frame) + " before leftStr so Code works.. Does it get dup'd elsewhere?

        self.emit.printout(operandStr)
        return operandStr, leftType

    def visitId(self, ast, o):
        frame = o.frame
        symbols = o.sym
        isFirst = o.isFirst
        isLeft = o.isLeft

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

        type_ = id_.mtype

        if type(id_.value) is CName:
            name = self.className + "/" + id_.name
            if isLeft:
                if isFirst:  #just for type checking, NO emit here
                    x = "", type_
                    return x  # find id in symbols
                else:
                    return self.emit.emitPUTSTATIC(
                        name, type_, frame), type_  # find id, store
            else:
                return self.emit.emitGETSTATIC(
                    name, type_, frame), type_  #find id in symbols, load index
        else:  #local
            name = id_.name
            index = id_.value
            if isLeft:
                if isFirst:  #just for type checking, NO emit here
                    x = "", type_
                    return x  # find id in symbols
                else:
                    return self.emit.emit(
                        name, type_, index, frame
                    ), type_  # find id, store # TODO: Jim has writeVAR func
            else:
                return self.emit.emitREADVAR(
                    name, type_, index,
                    frame), type_  #find id in symbols, load index

    def getOperands(self, lOp, rOp, o):
        frame = o.frame
        lStr, l_ = self.visit(lOp, Access(frame, o.sym, False, False))
        rStr, r_ = self.visit(rOp, Access(frame, o.sym, False, False))

        lType = type(l_)
        rType = type(r_)

        if lType is rType:
            return lStr + rStr, lType
        elif lType is FloatType and rType is IntType:
            return lStr + rStr + self.emit.emitI2F(
                frame), FloatType  #TODO: delete () again (move to Emitter)
        elif lType is IntType and rType is FloatType:
            return lStr + self.emit.emitI2F(frame) + rStr, FloatType
        else:
            raise Exception("Should never come here")
Example #20
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 = "MPClass"
        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)

        for x in ast.decl:
            e = self.visit(x, e)

        fun_ref = list(filter(lambda x: isinstance(x, FuncDecl), ast.decl))

        e.frame = Frame(None, None)
        for fun in fun_ref:
            self.visit(fun, e)
        # generate default constructor
        self.genMETHOD(FuncDecl(Id("<init>"), list(), list(), list(), None), c,
                       Frame("<init>", VoidType))
        self.emit.emitEPILOG()
        return c

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

        isInit = consdecl.returnType is None
        isMain = consdecl.name.name == "main" and len(
            consdecl.param) == 0 and type(consdecl.returnType) is VoidType
        returnType = VoidType() if isInit else consdecl.returnType
        methodName = "<init>" if isInit else consdecl.name.name
        if isMain:
            intype = [ArrayPointerType(StringType())]
        else:
            intype = [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))

        if glenv is None:
            glenv = []

        s = SubBody(frame, glenv)
        for x in consdecl.param:
            s = self.visit(x, s)

        for x in consdecl.local:
            s = self.visit(x, s)

        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))

        # visit stmt => dont return
        list(map(lambda x: self.visit(x, s), body))

        self.emit.printout(self.emit.jvm.INDENT + 'nop' + self.emit.jvm.END)

        self.emit.printout(self.emit.emitLABEL(frame.getEndLabel(), frame))
        if type(returnType) is VoidType:
            self.emit.printout(self.emit.emitRETURN(VoidType(), frame))
        else:
            # self.emit.printout(self.emit.jvm.INDENT + 'nop' + self.emit.jvm.END)
            pass
        self.emit.printout(self.emit.emitENDMETHOD(frame))
        frame.exitScope()

    def visitFuncDecl(self, ast, o):
        #ast: FuncDecl
        #o: Any

        subctxt = o
        frame = subctxt.frame
        if frame is not None:
            frame = Frame(ast.name.name, ast.returnType)
            self.genMETHOD(ast, subctxt.sym, frame)
        else:
            return SubBody(None, [
                Symbol(ast.name.name,
                       MType([x.varType for x in ast.param], ast.returnType),
                       CName(self.className))
            ] + subctxt.sym)

    def visitVarDecl(self, ast, o):

        subctxt = o
        frame = subctxt.frame

        if frame is None:
            self.emit.printout(
                self.emit.emitATTRIBUTE(ast.variable.name, ast.varType, False,
                                        ""))
            return SubBody(None, [
                Symbol(ast.variable.name, ast.varType, CName(self.className))
            ] + subctxt.sym)
        else:
            index = frame.getNewIndex()
            self.emit.printout(
                self.emit.emitVAR(index, ast.variable.name, ast.varType,
                                  frame.getStartLabel(), frame.getEndLabel(),
                                  frame))
            return SubBody(
                frame, [Symbol(ast.variable.name, ast.varType, Index(index))] +
                subctxt.sym)

    def visitCallExpr(self, ast, o):
        ctxt = o
        frame = ctxt.frame
        nenv = ctxt.sym
        sym = self.lookup(ast.method.name.lower(), nenv,
                          lambda x: x.name.lower())
        cname = sym.value.value
        ctype = sym.mtype  # model
        params = ctype.partype

        in_ = ("", list())
        for i, arg in enumerate(ast.param):
            '''
            arg: Expr
            '''
            str1, typ1 = self.visit(arg, Access(frame, nenv, False, True))

            if isinstance(typ1, MType):
                if not isinstance(typ1.rettype, type(params[i])):
                    str1 = str1 + self.emit.emitI2F(frame)
            elif not isinstance(typ1, type(params[i])):
                str1 = str1 + self.emit.emitI2F(frame)
            in_ = (in_[0] + str1, in_[1] + [typ1])

        return in_[0] + (self.emit.emitINVOKESTATIC(cname + "/" + sym.name,
                                                    ctype, frame)), ctype

    def visitCallStmt(self, ast, o):
        #ast: CallStmt
        #o: Any

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

        ctype = sym.mtype  # model
        params = ctype.partype
        params = ctype.partype

        in_ = ("", list())
        for i, arg in enumerate(ast.param):
            '''
            arg: Expr
            '''
            str1, typ1 = self.visit(arg, Access(frame, nenv, False, True))

            if isinstance(typ1, MType):
                if not isinstance(typ1.rettype, type(params[i])):
                    str1 = str1 + self.emit.emitI2F(frame)
            elif not isinstance(typ1, type(params[i])):
                str1 = str1 + self.emit.emitI2F(frame)
            in_ = (in_[0] + str1, in_[1] + [typ1])
        self.emit.printout(in_[0])
        self.emit.printout(
            self.emit.emitINVOKESTATIC(cname + "/" + sym.name, ctype, frame))

    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):
        #ast: FloatLiteral
        #o: Any

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

    def visitStringLiteral(self, ast, o):
        #ast: FloatLiteral
        #o: Any

        ctxt = o
        frame = ctxt.frame
        return self.emit.emitPUSHCONST('"' + ast.value + '"', StringType(),
                                       frame), StringType()

    def visitBooleanLiteral(self, ast, o):
        #ast: FloatLiteral
        #o: Any

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

    def visitAssign(self, ast, o):
        #lhs:Expr
        #exp:Expr
        #frame: Frame
        #sym: List[Symbol]

        subctxt = o
        frame = subctxt.frame
        sym = subctxt.sym
        # input(ast.exp)
        # input(ast.lhs)
        exp, type_exp = self.visit(ast.exp, Access(frame, sym, False, True))

        lhs, type_lhs = self.visit(ast.lhs, Access(frame, sym, True, True))

        if isinstance(type_exp, MType):
            type_exp = type_exp.rettype

        if not isinstance(type_exp, type(type_lhs)):
            exp = exp + self.emit.emitI2F(frame)

        self.emit.printout(exp + lhs)

    def visitId(self, ast, o):
        subctxt = o
        frame = subctxt.frame
        sym = subctxt.sym
        isLeft = subctxt.isLeft

        res = self.lookup(ast.name.lower(), sym, lambda env: env.name.lower())
        # res = symbol(,,Cname)
        if res is None:
            return None, None

        if isLeft is True:
            if isinstance(res.value, CName):
                varname = res.value.value + '/' + res.name
                code = self.emit.emitPUTSTATIC(varname, res.mtype, frame)
            else:
                code = self.emit.emitWRITEVAR(res.name, res.mtype,
                                              res.value.value, frame)

        else:
            if isinstance(res.value, CName):
                varname = res.value.value + '/' + res.name
                code = self.emit.emitGETSTATIC(varname, res.mtype, frame)
            else:
                code = self.emit.emitREADVAR(res.name, res.mtype,
                                             res.value.value, frame)

        return code, res.mtype

    def visitBinaryOp(self, ast, o):
        subctxt = o
        frame = subctxt.frame
        sym = subctxt.sym

        left, type_left = self.visit(ast.left, Access(frame, sym, False, True))
        right, type_right = self.visit(ast.right,
                                       Access(frame, sym, False, True))
        #input(ast.right)
        op = ast.op.lower()

        if isinstance(type_left, MType):
            type_left = type_left.rettype
        if isinstance(type_right, MType):
            type_right = type_right.rettype

        if not isinstance(type_left, type(type_right)):
            if isinstance(type_left, FloatType):
                right = right + self.emit.emitI2F(frame)
                type_right = FloatType()
            else:
                left = left + self.emit.emitI2F(frame)
                type_left = FloatType()

        if op in ["+", "-"]:
            code = left + right + self.emit.emitADDOP(op, type_left, frame)
        elif op in ["*"]:
            code = left + right + self.emit.emitMULOP(op, type_left, frame)
        elif op in ["/"]:
            if isinstance(type_left, IntType):
                left = left + self.emit.emitI2F(frame)
                right = right + self.emit.emitI2F(frame)
                type_left = FloatType()
                type_right = FloatType()
            code = left + right + self.emit.emitMULOP(op, type_left, frame)
        elif op in ["<=", ">=", "=", ">", "<", "<>"]:
            code = left + right + self.emit.emitREOP(op, type_left, frame)
            return code, BoolType()
        elif op == "or":
            code = left + right + self.emit.emitOROP(frame)
        elif op == "and":
            code = left + right + self.emit.emitANDOP(frame)
        elif op == "div":
            code = left + right + self.emit.emitDIV(frame)
        elif op == "mod":
            code = left + right + self.emit.emitMOD(frame)
        # and then, or else
        elif op == "andthen":
            falseLabel = frame.getNewLabel()
            endLabel = frame.getNewLabel()
            code = ''
            code = code + left
            # if left = 0 False
            code = code + self.emit.emitIFEQ(falseLabel, frame)
            code = code + right
            code = code + self.emit.emitIFEQ(falseLabel, frame)
            code = code + self.emit.emitPUSHICONST(1, frame)
            code = code + self.emit.emitGOTO(endLabel, frame)
            code = code + self.emit.emitLABEL(falseLabel, frame)
            code = code + self.emit.emitPUSHICONST(0, frame)
            code = code + self.emit.emitLABEL(endLabel, frame)
            type_left = BoolType()

        elif op == "orelse":
            trueLabel = frame.getNewLabel()
            endLabel = frame.getNewLabel()
            code = ''
            code = code + left
            code = code + self.emit.emitIFNE(trueLabel, frame)
            code = code + right
            code = code + self.emit.emitIFNE(trueLabel, frame)
            code = code + self.emit.emitPUSHICONST(0, frame)
            code = code + self.emit.emitGOTO(endLabel, frame)
            code = code + self.emit.emitLABEL(trueLabel, frame)
            code = code + self.emit.emitPUSHICONST(1, frame)
            code = code + self.emit.emitLABEL(endLabel, frame)
            type_left = BoolType()

        return code, type_left

    def visitUnaryOp(self, ast, o):
        subctxt = o
        frame = subctxt.frame
        sym = subctxt.sym

        body, type_body = self.visit(ast.body, Access(frame, sym, False, True))
        op = ast.op.lower()

        if op == "not":
            code = body + self.emit.emitNOT(type_body, frame)
        else:
            code = body + self.emit.emitNEGOP(type_body, frame)

        return code, type_body

    def visitIf(self, ast, o):
        subctxt = o
        frame = subctxt.frame
        sym = subctxt.sym

        exp, type_exp = self.visit(ast.expr, Access(frame, sym, False, True))

        self.emit.printout(exp)

        label1 = frame.getNewLabel()
        label2 = frame.getNewLabel()

        self.emit.printout(self.emit.emitIFEQ(label1, frame))

        for x in ast.thenStmt:
            self.visit(x, SubBody(frame, sym))

        self.emit.printout(self.emit.emitGOTO(label2, frame))

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

        for x in ast.elseStmt:
            self.visit(x, SubBody(frame, sym))

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

    def visitWhile(self, ast, o):
        subctxt = o
        frame = subctxt.frame
        sym = subctxt.sym

        frame.enterLoop()
        breakLabel = frame.getBreakLabel()
        continueLabel = frame.getContinueLabel()
        self.emit.printout(self.emit.emitLABEL(continueLabel, frame))

        exp, type_exp = self.visit(ast.exp, Access(frame, sym, False, True))

        self.emit.printout(exp)
        self.emit.printout(self.emit.emitIFEQ(breakLabel, frame))

        for x in ast.sl:
            self.visit(x, SubBody(frame, sym))

        self.emit.printout(self.emit.emitGOTO(continueLabel, frame))

        self.emit.printout(self.emit.emitLABEL(breakLabel, frame))
        frame.exitLoop()

    def visitFor(self, ast, o):
        # if ast.up is False:
        #     condition = BinaryOp(">=", ast.id, ast.expr2)
        # else:
        #     condition = BinaryOp("<=", ast.id, ast.expr2)

        # self.visit(Assign(ast.id, ast.expr1), o)
        # increment = BinaryOp(inrop, ast.id, IntLiteral(1))
        # loop = ast.loop[:] + [increment]
        # self.visit(While(condition, loop), o)

        subctxt = o
        frame = subctxt.frame
        sym = subctxt.sym

        frame.enterLoop()
        breakLabel = frame.getBreakLabel()
        startLabel = frame.getNewLabel()
        continueLabel = frame.getContinueLabel()

        expr1 = ast.expr1
        self.visit(Assign(ast.id, expr1), o)
        self.emit.printout(self.emit.emitLABEL(startLabel, frame))

        if ast.up is False:
            exp, type_exp = self.visit(BinaryOp(">=", ast.id, ast.expr2), o)
        else:
            exp, type_exp = self.visit(BinaryOp("<=", ast.id, ast.expr2), o)

        self.emit.printout(exp)
        self.emit.printout(self.emit.emitIFEQ(breakLabel, frame))

        for x in ast.loop:
            self.visit(x, SubBody(frame, sym))
        self.emit.printout(self.emit.emitGOTO(continueLabel, frame))
        #tang giam 1
        self.emit.printout(self.emit.emitLABEL(continueLabel, frame))

        if ast.up is False:
            expr1 = BinaryOp("-", ast.id, IntLiteral(1))
        else:
            expr1 = BinaryOp("+", ast.id, IntLiteral(1))

        # input(expr1)

        #self.emit.printout(expr1)

        self.visit(Assign(ast.id, expr1), o)

        self.emit.printout(self.emit.emitGOTO(startLabel, frame))

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

        frame.exitLoop()

    def visitWith(self, ast, o):
        subctxt = o
        frame = subctxt.frame
        sym = subctxt.sym

        frame.enterScope(False)
        s = SubBody(frame, sym[:])

        for x in ast.decl:
            s = self.visit(x, s)

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

        for x in ast.stmt:
            self.visit(x, s)

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

        frame.exitScope()

    def visitReturn(self, ast, o):
        subctxt = o
        frame = subctxt.frame
        sym = subctxt.sym
        code = ''

        if ast.expr:
            exp, type_exp = self.visit(ast.expr,
                                       Access(frame, sym, False, True))
            code += exp
            if not isinstance(type_exp, type(frame.returnType)):
                # func returns Float, while "return Int"
                # other cases, checker

                code += self.emit.emitI2F(frame)

        code += self.emit.emitRETURN(frame.returnType, frame)

        self.emit.printout(code)

    def visitBreak(self, ast, o):
        subctxt = o
        frame = subctxt.frame
        sym = subctxt.sym

        breakLabel = frame.getBreakLabel()
        self.emit.printout(self.emit.emitGOTO(breakLabel, frame))

    def visitContinue(self, ast, o):
        subctxt = o
        frame = subctxt.frame
        sym = subctxt.sym

        continueLabel = frame.getContinueLabel()

        self.emit.printout(self.emit.emitGOTO(continueLabel, frame))
Example #21
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 = "MPClass"
        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"))
        for x in ast.decl:
            if type(x) is VarDecl:
                self.env += [Symbol(x.variable.name,x.varType, CName(self.className))]
                self.emit.printout(self.emit.emitATTRIBUTE(x.variable.name, x.varType,False,""))
            else:
                self.env += [Symbol(x.name.name,MType([i.varType for i in x.param ] if len(x.param)>0 else [],x.returnType), CName(self.className))]
        #e = SubBody(None, self.env)
        for x in ast.decl:
            self.visit(x, SubBody(None,self.env[:]))
        # generate default constructor
        a = self.genMETHOD(FuncDecl(Id("<init>"), list(), list(), list(),None), c, Frame("<init>", VoidType))
        self.emit.emitEPILOG()
        return c

    def genMETHOD(self, consdecl, o, frame):
        #consdecl: FuncDecl
        #o: Any
        #frame: Frame
        isInit = consdecl.returnType is None
        isMain = consdecl.name.name == "main" and len(consdecl.param) == 0 and type(consdecl.returnType) is VoidType
        returnType = VoidType() if isInit else consdecl.returnType
        methodName = "<init>" if isInit else consdecl.name.name
        intype = [ArrayPointerType(StringType())] if isMain else [x.varType for x in consdecl.param]
        mtype = MType(intype, returnType)

        a =  self.emit.emitMETHOD(methodName, mtype, not isInit, frame)
        self.emit.printout(a)
        frame.enterScope(True)

        # Generate code for parameter declarations
        if isInit:
            self.emit.printout(self.emit.emitVAR(frame.getNewIndex(), "this", ClassType(self.className), frame.getStartLabel(), frame.getEndLabel(), frame))
        else:
            glenv = o.sym
            if isMain: self.emit.printout(self.emit.emitVAR(frame.getNewIndex(), "args", ArrayPointerType(StringType()), frame.getStartLabel(), frame.getEndLabel(), frame))
            else:
                for x in consdecl.param:
                    idx = frame.getNewIndex()
                    glenv += [Symbol(x.variable.name, x.varType, Index(idx))]
                    c = self.emit.emitVAR(idx,x.variable.name,x.varType,frame.getStartLabel(), frame.getEndLabel(),frame)
                    self.emit.printout(c)
                    #idx += 1
            for x in consdecl.local:
                idx = frame.getNewIndex()
                glenv += [Symbol(x.variable.name, x.varType, Index(idx))]
                d = self.emit.emitVAR(idx,x.variable.name,x.varType,frame.getStartLabel(), frame.getEndLabel(),frame)
                self.emit.printout(d)
                #idx += 1

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

        # 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))
        for x in body:
            self.visit(x, 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))
        self.emit.printout(self.emit.emitENDMETHOD(frame))
        frame.exitScope()

    def visitFuncDecl(self, ast, o):
        subctxt = o
        frame = Frame(ast.name, ast.returnType)
        self.genMETHOD(ast, subctxt, frame)
        return o
        #return SubBody(None, [Symbol(ast.name.name, MType([i for i in ast.param], ast.returnType), CName(self.className))]+ o.sym[1])

    def visitVarDecl(self,ast,o):
        vtype = ast.varType
        if type(vtype) is ArrayType: raise Exception("No Array Pls")
        #self.emit.printout(self.emit.emitATTRIBUTE(ast.variable.name,vtype,False,""))
        #return None 
        return SubBody(None,[Symbol(ast.variable.name,vtype,CName(self.className))]+o.sym)

    def visitAssign(self,ast,o):
        rc, rt = self.visit(ast.exp,Access(o.frame,o.sym,False,True))
        lc,lt = self.visit(ast.lhs,Access(o.frame,o.sym,True,False))
        if type(lt) is FloatType and type(rt) is IntType:
            self.emit.printout(rc + self.emit.emitI2F(None) +lc)
            #self.emit.emitI2F(None)
        else:
            self.emit.printout(rc+lc)
        pass

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

        in_ = ("", list())
        for idx, val in enumerate(ast.param):
            str1, typ1 = self.visit(val, Access(frame, nenv, False, True))
            in_ = (in_[0] + str1, in_[1] + [typ1])
            if type(sym.mtype.partype[idx]) is FloatType and type(in_[1][idx]) is IntType:
                in_ = (in_[0] + self.emit.emitI2F(frame), in_[1])
            
        #self.emit.printout(in_[0])
        #checkPrintIntWithPutFloat = (cname + "/" + sym.name)
        #if checkPrintIntWithPutFloat in ['io/putFloat','io/putFloatLn'] and type(in_[1][0]) is IntType:
        #    buonNgu = self.emit.emitI2F(frame)
        #else:
        #    buonNgu = ""
        self.emit.printout(in_[0] + self.emit.emitINVOKESTATIC(cname + "/" + sym.name, ctype, frame))
        
    def visitIf(self,ast,o):
        ctxt = o
        frame = ctxt.frame
        nenv = ctxt.sym

        checkReturnAtTheEndThen = False
        
        expc, expt = self.visit(ast.expr,Access(frame,nenv,False,True))
        self.emit.printout(expc)
        falseLabel = frame.getNewLabel()
        endLabel = frame.getNewLabel()
        self.emit.printout(self.emit.emitIFFALSE(falseLabel,frame))
        if ast.thenStmt == []: 
            self.emit.printout(self.emit.emitGOTO(endLabel,frame))
        for x in ast.thenStmt: self.visit(x,SubBody(frame,nenv))
        if len(ast.thenStmt) > 0 and not type(ast.thenStmt[-1]) is Return:
            self.emit.printout(self.emit.emitGOTO(endLabel,frame))
        self.emit.printout(self.emit.emitLABEL(falseLabel,frame))
        for x in ast.elseStmt:
            self.visit(x,SubBody(frame,nenv))
        self.emit.printout(self.emit.emitLABEL(endLabel,frame))
        pass

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

        startLabel = frame.getNewLabel()
        endLabel = frame.getNewLabel()
        frame.brkLabel.append(endLabel)
        frame.conLabel.append(startLabel)

        self.emit.printout(self.emit.emitLABEL(startLabel,frame))
        expc, expt = self.visit(ast.exp,Access(frame,nenv,False,True))
        self.emit.printout(expc)
        self.emit.printout(self.emit.emitIFFALSE(endLabel,frame))
        for x in ast.sl:
            self.visit(x,SubBody(frame,nenv))
        self.emit.printout(self.emit.emitGOTO(startLabel,frame))
        self.emit.printout(self.emit.emitLABEL(endLabel,frame))
        
        frame.brkLabel = frame.brkLabel[:-1]
        frame.conLabel = frame.conLabel[:-1]
        pass

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

        loopLable = frame.getNewLabel()
        endLabel = frame.getNewLabel()
        increaseLabel = frame.getNewLabel()
        frame.brkLabel.append(endLabel)
        frame.conLabel.append(increaseLabel)

        exp1c, exp1t = self.visit(ast.expr1,Access(frame,nenv,False,True))
        self.emit.printout(exp1c)
        idc, idt = self.visit(ast.id,Access(frame,nenv,True,False))
        self.emit.printout(idc)
        self.emit.printout(self.emit.emitLABEL(loopLable,frame))
        idc1, idt1 = self.visit(ast.id,Access(frame,nenv,False,True))
        self.emit.printout(idc1)
        exp2c, exp2t = self.visit(ast.expr2,Access(frame,nenv,False,True))
        self.emit.printout(exp2c)
        if ast.up: 
            incrcode = "\tiadd\n"
            self.emit.printout(self.emit.emitIFICMPGT(endLabel,frame))
        else:
            incrcode = "\tisub\n"
            self.emit.printout(self.emit.emitIFICMPLT(endLabel,frame))
        for x in ast.loop:
            self.visit(x,SubBody(frame,nenv))
        #self.emit.printout(self.emit.emitLABEL(loopLable,frame))
        idc2, idt2 = self.visit(ast.id,Access(frame,nenv,False,True))
        self.emit.printout(self.emit.emitLABEL(increaseLabel,frame))
        self.emit.printout(idc2 + "\ticonst_1\n" + incrcode)
        idc3, idt3 = self.visit(ast.id,Access(frame,nenv,True,False))
        self.emit.printout(idc3)
        self.emit.printout(self.emit.emitGOTO(loopLable,frame))
        self.emit.printout(self.emit.emitLABEL(endLabel,frame))

        frame.brkLabel = frame.brkLabel[:-1]
        frame.conLabel = frame.conLabel[:-1]
        pass

    def visitWith(self,ast,o):
        ctxt = o
        frame = ctxt.frame
        nenv = o.sym[:]

        frame.enterScope(False)
        startLabel = frame.getNewLabel()
        endLabel = frame.getNewLabel()
        for x in ast.decl:
            idx = frame.getNewIndex()
            self.emit.printout(self.emit.emitVAR(idx,x.variable.name,x.varType,startLabel,endLabel,frame))
            nenv += [Symbol(x.variable.name, x.varType, Index(idx))]
        self.emit.printout(self.emit.emitLABEL(startLabel,frame))
        for x in ast.stmt:
            self.visit(x,SubBody(frame,nenv))
        self.emit.printout(self.emit.emitLABEL(endLabel,frame))
        frame.exitScope()
        pass    

    def visitReturn(self,ast,o):
        ctxt = o
        frame = ctxt.frame
        nenv = ctxt.sym
        if ast.expr:
            exp_c, exp_t = self.visit(ast.expr,Access(frame,nenv,False,True))
            self.emit.printout(exp_c)
            if type(frame.returnType) is FloatType and type(exp_t) is IntType:
                self.emit.printout(self.emit.emitI2F(frame))
                exp_t = frame.returnType
            self.emit.printout(self.emit.emitRETURN(exp_t,frame))
        else:
            self.emit.printout(self.emit.emitRETURN(VoidType(),frame))
        pass

    def visitBreak(self,ast,o):
        frame = o.frame
        #self.emit.printout(self.emit.emitLABEL(frame.getBreakLabel(),frame))
        self.emit.printout(self.emit.emitGOTO(frame.getBreakLabel(),frame))
        pass

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

    def visitBinaryOp(self,ast,o):
        ctxt = o
        frame = ctxt.frame
        op = ast.op

        if op in ['andthen','orelse']:
            outLabel = frame.getNewLabel()
            falseLabel = frame.getNewLabel()
            trueLabel = frame.getNewLabel()

            lcode, ltype = self.visit(ast.left,Access(frame,o.sym,False,True))
            self.emit.printout(lcode)
            if op == 'andthen':
                self.emit.printout(self.emit.emitIFFALSE(falseLabel,frame))
                rcode, rtype = self.visit(ast.right,Access(frame,o.sym,False,True))
                self.emit.printout(rcode)
                self.emit.printout(self.emit.emitGOTO(outLabel,frame))
                self.emit.printout(self.emit.emitLABEL(falseLabel,frame))
                self.emit.printout("\ticonst_0\n")
                self.emit.printout(self.emit.emitLABEL(outLabel,frame))
            else:
                self.emit.printout(self.emit.emitIFTRUE(trueLabel,frame))
                rcode, rtype = self.visit(ast.right,Access(frame,o.sym,False,True))
                self.emit.printout(rcode)
                self.emit.printout(self.emit.emitGOTO(outLabel,frame))
                self.emit.printout(self.emit.emitLABEL(trueLabel,frame))
                self.emit.printout("\ticonst_1\n")
                self.emit.printout(self.emit.emitLABEL(outLabel,frame))
            return "",BoolType()
        else:
            lcode, ltype = self.visit(ast.left,Access(frame,o.sym,False,True))
            rcode, rtype = self.visit(ast.right,Access(frame,o.sym,False,True))
            if type(ltype) != type(rtype):
                if type(ltype) is IntType:
                    lcode += self.emit.emitI2F(frame)
                    ltype = FloatType()
                else:
                    rcode += self.emit.emitI2F(frame)
                    rtype = FloatType()
            if op in ['+','-']:
                opcode = self.emit.emitADDOP(op,ltype,frame)
            elif op in ['*','/']:
                if op == '/' and type(ltype) is IntType and type(rtype) is IntType:
                    lcode += self.emit.emitI2F(frame)
                    rcode += self.emit.emitI2F(frame)
                    ltype = FloatType()
                    rtype = FloatType()
                opcode = self.emit.emitMULOP(op,ltype,frame)
            elif op.lower() == 'and':
                opcode = self.emit.emitANDOP(frame)
            elif op.lower() == 'or':
                opcode = self.emit.emitOROP(frame)
            elif op.lower() == 'div':
                opcode = self.emit.emitDIV(frame)
            elif op.lower() == 'mod':
                opcode = self.emit.emitMOD(frame)
            else:
                if type(ltype) is FloatType:
                    falseLabel = frame.getNewLabel()
                    outLabel = frame.getNewLabel()
                    if op == '=': code = "\tifne Label" 
                    elif op == '<>': code = "\tifeq Label" 
                    elif op == '>': code = "\tifle Label" 
                    elif op == '<': code = "\tifge Label" 
                    elif op == '>=': code = "\tiflt Label" 
                    elif op == '<=': code = "\tifgt Label" 
                    opcode = "\tfcmpl\n" + code + str(falseLabel) + "\n" + "\ticonst_1\n" + self.emit.emitGOTO(outLabel,frame) + self.emit.emitLABEL(falseLabel,frame) + "\ticonst_0\n" + self.emit.emitLABEL(outLabel,frame)
                    ltype = BoolType()
                else:
                    opcode = self.emit.emitREOP(op,ltype,frame)
                    ltype = BoolType()
            #sef.emit.printout(lcode + rcode + opcode)
            return lcode + rcode + opcode,ltype

    def visitUnaryOp(self,ast,o):
        ctxt = o
        frame = ctxt.frame
        op = ast.op

        expc, expt = self.visit(ast.body,Access(frame,o.sym,False,True))
        if op == '-':
            opcode = self.emit.emitNEGOP(expt,frame)
        elif op == "not":
            opcode = self.emit.emitNOT(expt,frame)
        return expc + opcode, expt

    def visitId(self,ast,o):
        sym = self.lookup(ast.name.lower(),o.sym[::-1],lambda x:x.name.lower())
        if o.isLeft:
            if type(sym.value) is CName:
                res = self.emit.emitPUTSTATIC(sym.value.value + "/" + sym.name, sym.mtype, o.frame)
                #self.emit.printout(res)
                return res,sym.mtype
            elif type(sym.value) is Index:
                res = self.emit.emitWRITEVAR(sym.name,sym.mtype,sym.value.value,o.frame)
                #self.emit.printout(res)
                return res,sym.mtype
            else:
                return "",VoidType()
        else:
            if type(sym.value) is CName:
                res = self.emit.emitGETSTATIC(sym.value.value + "/" + sym.name, sym.mtype, o.frame)
                #self.emit.printout(res)
                return res,sym.mtype
            elif type(sym.value) is Index:
                res = self.emit.emitREADVAR(sym.name,sym.mtype,sym.value.value,o.frame)
                #self.emit.printout(res)
                return res,sym.mtype
            else:
                return "",VoidType()

    def visitCallExpr(self,ast,o):
        ctxt = o
        frame = ctxt.frame
        nenv = ctxt.sym
        sym = self.lookup(ast.method.name.lower(), nenv, lambda x: x.name.lower())
        cname = sym.value.value
        ctype = sym.mtype
        in_ = ("", [])
        for idx, val in enumerate(ast.param):
            str1, typ1 = self.visit(val, Access(frame, nenv, False, True))
            in_ = (in_[0] + str1, in_[1] + [typ1])
            if type(sym.mtype.partype[idx]) is FloatType and type(in_[1][idx]) is IntType:
                in_ = (in_[0] + self.emit.emitI2F(frame), in_[1])
        res = in_[0] + self.emit.emitINVOKESTATIC(cname + "/" + sym.name, ctype, frame)
        #self.emit.printout(res)
        return res,ctype.rettype 

    def visitArrayCell(self,ast,o):
        raise Exception("Array Cant visit that shit")

    def visitIntLiteral(self, ast, o):
        ctxt = o
        frame = ctxt.frame
        res = self.emit.emitPUSHICONST(ast.value, frame)
        #self.emit.printout(res)
        return res,IntType()

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

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

    def visitStringLiteral(self,ast,o):
        ctxt = o
        frame = ctxt.frame
        res = self.emit.emitPUSHCONST(str(ast.value),StringType(),frame)
        #self.emit.printout(res)
        return res,StringType()
Example #22
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 = "MPClass"
        self.path = dir_
        self.emit = Emitter(self.path + "/" + self.className + ".j")


    def visitProgram(self, ast: Program, c):
        # 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, 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)
        [self.visit(x, e) for x in ast.decl if type(x) is FuncDecl]

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



    def visitFuncDecl(self, ast: FuncDecl, o: SubBody):
        subctxt = o
        frame = Frame(ast.name.name, ast.returnType)
        self.genMETHOD(ast, subctxt.sym, frame)


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



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

        glenv = o

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

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

        frame.enterScope(isProc)

        # 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))

        varList = SubBody(frame, glenv)
        for x in decl.param + decl.local:
            varList = self.visit(x, varList)

        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))
        
        list(map(lambda x: self.visit(x, varList), decl.body))

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




# ================   Visit Statements   =================
# Param:    o: SubBody(frame, sym)


    def visitCallStmt(self, ast: CallStmt, o: SubBody):
        ctxt = o
        frame = ctxt.frame
        symbols = ctxt.sym
        self.handleCall(ast, frame, symbols, isStmt=True)



    def handleCall(self, ast, frame, symbols, isStmt=False):
        # ast: CallStmt | CallExpr

        sym = self.lookup(ast.method.name.lower(), symbols, lambda x: x.name.lower())
        cname = sym.value.value
        ctype = sym.mtype
        paramTypes = ctype.partype

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

        code = paramsCode + self.emit.emitINVOKESTATIC(cname + "/" + sym.name, ctype, frame) 
        if isStmt: self.emit.printout(code)
        else: return code, ctype.rettype



    def visitAssign(self, ast: Assign, o: SubBody):
        ctxt = o
        frame = ctxt.frame
        nenv = ctxt.sym
        # Visit LHS: Id || ArrayCell, return name, type, index
        lhsName, lhsType, lhsIndex = self.visit(ast.lhs, Access(frame, nenv, True, True))
        expCode, expType = self.visit(ast.exp, Access(frame, nenv, False, True))
        if type(lhsType) is FloatType and type(expType) is IntType:
            expCode = expCode + self.emit.emitI2F(frame)
        self.emit.printout(expCode)
        if lhsIndex is None: # global var - static field
            self.emit.printout(self.emit.emitPUTSTATIC(self.className + "/" + lhsName, lhsType, frame))
        else:
            self.emit.printout(self.emit.emitWRITEVAR(lhsName, lhsType, lhsIndex.value, frame))


    def visitIf(self, ast: If, o: SubBody):
        ctxt = o
        frame = ctxt.frame
        nenv = ctxt.sym
        returnType = frame.returnType
        expCode, expType = self.visit(ast.expr, Access(frame, nenv, False, True))

        isProc = type(returnType) is VoidType

        self.emit.printout(expCode)

        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.emitPUSHICONST("false", frame)) # push true
        [self.visit(x, o) for x in ast.elseStmt]
        self.emit.printout(self.emit.emitGOTO(labelE, frame)) # go to end

        self.emit.printout(self.emit.emitLABEL(labelT, frame))
        self.emit.printout(self.emit.emitPUSHICONST("true", frame))
        [self.visit(x, o) for x in ast.thenStmt]

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



# ================   Visit Expression   =================
# Param:    o: Access(frame, sym, isLeft, isFirst)
# Return:   (code, type)

    def visitBinaryOp(self, ast: BinaryOp, o: Access):
        ctxt = o
        frame = ctxt.frame
        op = str(ast.op).lower()
        lCode, lType = self.visit(ast.left, ctxt)
        rCode, rType = self.visit(ast.right, ctxt)
        if ExpUtils.isOpForNumber(op): # for number type
            mType = ExpUtils.mergeNumberType(lType, rType)
            if op == '/': mType = FloatType() # mergeType >= lType, rType
            lCode, rCode = (c if type(t) == type(mType) else c+self.emit.emitI2F(frame) \
                            for c,t in [(lCode, lType), (rCode, rType)])
            if ExpUtils.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 == 'div':
                    return lCode + rCode + self.emit.emitDIV(frame), mType
                if op == 'mod':
                    return lCode + rCode + self.emit.emitMOD(frame), mType
            else: # op to boolean: > <= = <>, ...
                return lCode + rCode + self.emit.emitREOP(op, mType, frame), BoolType()
        else: # for boolean type
            mType = BoolType()
            if op == 'or': return lCode + rCode + self.emit.emitOROP(frame), mType
            if op == 'and': return lCode + rCode + self.emit.emitANDOP(frame), mType
            if op == 'orelse': return self.emit.emitORELSE(frame, lCode, rCode), mType
            if op == 'andthen': return self.emit.emitANDTHEN(frame, lCode, rCode), mType




    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
        sym = self.lookup(ast.name.lower(), symbols, lambda x: x.name.lower())
        if isLeft: return sym.name, sym.mtype, sym.value
        if sym.value is None: # not index -> global var - static field
            return self.emit.emitGETSTATIC(self.className + "/" + sym.name, sym.mtype, frame), sym.mtype
        return self.emit.emitREADVAR(sym.name, sym.mtype, sym.value.value, frame), sym.mtype



    def visitCallExpr(self, ast: CallExpr, o: Access):
        ctxt = o
        frame = ctxt.frame
        symbols = ctxt.sym
        return self.handleCall(ast, frame, symbols, isStmt=False)



    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()
Example #23
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 = "MPClass"
        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)
        for x in ast.decl:
            e = self.visit(x, (e, True))  # get name global
        for x in ast.decl:
            self.visit(x, (e, False))  # generate code
        # generate default constructor
        self.genMETHOD(FuncDecl(Id("<init>"), list(), list(), list(), None),
                       e.sym, Frame("<init>", VoidType))
        self.emit.emitEPILOG()
        return c

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

        isInit = consdecl.returnType is None
        isMain = consdecl.name.name == "main" and len(
            consdecl.param) == 0 and type(consdecl.returnType) is VoidType
        returnType = VoidType() if isInit else consdecl.returnType
        methodName = "<init>" if isInit else consdecl.name.name
        intype = [ArrayPointerType(StringType())
                  ] if isMain else [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))
        glenv = reduce(lambda x, y: [self.visit(y, (frame, "decl"))] + x,
                       consdecl.param, glenv)
        glenv = reduce(lambda x, y: [self.visit(y, (frame, "decl"))] + x,
                       consdecl.local, glenv)
        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))
        list(map(lambda x: self.visit(x, SubBody(frame, glenv)), body))

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

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

    def visitFuncDecl(self, ast, o):
        #ast: FuncDecl
        #o: Any
        subctxt = o[0]
        if o[1] is True:
            retType = MType([x.varType for x in ast.param], ast.returnType)
            return SubBody(
                None, [Symbol(ast.name.name, retType, CName(self.className))] +
                subctxt.sym)
        # o[1] is False
        frame = Frame(ast.name, ast.returnType)
        self.genMETHOD(ast, subctxt.sym, frame)

    def visitVarDecl(self, ast, o):
        subctxt = o[0]
        if o[1] is True:
            retType = ast.varType
            self.emit.printout(
                self.emit.emitATTRIBUTE(ast.variable.name, retType, False,
                                        None))
            return SubBody(
                None,
                [Symbol(ast.variable.name, retType, CName(self.className))] +
                subctxt.sym)
        elif o[1] == "decl":  # o[0] is frame
            retType = ast.varType
            frame = o[0]
            index = frame.getNewIndex()
            self.emit.printout(
                self.emit.emitVAR(index, ast.variable.name, retType,
                                  frame.getStartLabel(), frame.getEndLabel(),
                                  frame))
            return Symbol(ast.variable.name, retType, Index(index))

    def visitWith(self, ast, o):
        subctxt = o
        frame = subctxt.frame
        frame.enterScope(False)

        nenv = reduce(lambda x, y: [self.visit(y, (frame, "decl"))] + x,
                      ast.decl, subctxt.sym)

        self.emit.printout(self.emit.emitLABEL(frame.getStartLabel(), frame))
        # generate code for body
        for x in ast.stmt:
            self.visit(x, SubBody(frame, nenv))

        self.emit.printout(self.emit.emitLABEL(frame.getEndLabel(), frame))
        frame.exitScope()

    def visitIf(self, ast, o):
        subctxt = o
        frame = subctxt.frame
        sym = subctxt.sym
        elseLb = frame.getNewLabel()
        endLb = frame.getNewLabel()

        expcode, expType = self.visit(ast.expr, Access(frame, sym, False,
                                                       True))

        self.emit.printout(expcode)
        # generate code for thenstmt
        self.emit.printout(self.emit.emitIFFALSE(elseLb, frame))
        list(map(lambda x: self.visit(x, subctxt), ast.thenStmt))
        self.emit.printout(self.emit.emitGOTO(endLb, frame))
        # generate code for elsestmt
        self.emit.printout(self.emit.emitLABEL(elseLb, frame))
        list(map(lambda x: self.visit(x, subctxt), ast.elseStmt))
        self.emit.printout(self.emit.emitLABEL(endLb, frame))

    def visitFor(self, ast, o):
        frame = o.frame
        sym = o.sym
        code_expr1, typeExpr1 = self.visit(ast.expr1,
                                           Access(frame, sym, False, True))
        idStore, _ = self.visit(ast.id, Access(frame, sym, True, False))
        self.emit.printout(code_expr1 + idStore)

        frame.enterLoop()
        strLabel = frame.getNewLabel()
        ConLabel = frame.getContinueLabel()
        BrkLabel = frame.getBreakLabel()

        self.emit.printout(self.emit.emitLABEL(strLabel, frame))
        idLoad, _ = self.visit(ast.id, Access(frame, sym, False, False))
        code_expr2, typeExpr2 = self.visit(ast.expr2,
                                           Access(frame, sym, False, True))
        if ast.up:
            self.emit.printout(idLoad + code_expr2 +
                               self.emit.emitREOP("<=", typeExpr2, frame))
        else:
            self.emit.printout(idLoad + code_expr2 +
                               self.emit.emitREOP(">=", typeExpr2, frame))
        self.emit.printout(self.emit.emitIFFALSE(BrkLabel, frame))
        for x in ast.loop:
            self.visit(x, Access(frame, sym, False, True))

        self.emit.printout(self.emit.emitLABEL(ConLabel, frame))
        self.emit.printout(
            self.emit.emitINCREASE(idStore, idLoad, ast.up, frame))
        self.emit.printout(self.emit.emitGOTO(strLabel, frame))
        self.emit.printout(self.emit.emitLABEL(BrkLabel, frame))
        frame.exitLoop()

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

        frame.enterLoop()
        continueLabel = frame.getContinueLabel()
        breakLabel = frame.getBreakLabel()

        #generate code for continue label
        self.emit.printout(self.emit.emitLABEL(continueLabel, frame))

        #generate code for expr
        code, ret = self.visit(ast.exp, Access(frame, sym, False, True))
        self.emit.printout(code)

        #generate code for break label
        self.emit.printout(self.emit.emitIFFALSE(breakLabel, frame))

        #generate code for listStmt
        list(map(lambda x: self.visit(x, o), ast.sl))

        self.emit.printout(self.emit.emitGOTO(continueLabel, frame))
        self.emit.printout(self.emit.emitLABEL(breakLabel, frame))
        frame.exitLoop()

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

    def visitBreak(self, ast, o):
        self.emit.printout(self.emit.emitGOTO(frame.getBreakLabel(), o.frame))

    def visitReturn(self, ast, o):
        frame = o.frame
        retType = frame.returnType
        expcode, expType = "", []
        if ast.expr:
            expcode, expType = self.visit(ast.expr,
                                          Access(frame, o.sym, False, True))
            if type(retType) != type(expType):
                expcode = expcode + self.emit.emitI2F(frame)
        self.emit.printout(expcode)
        self.emit.printout(self.emit.emitGOTO(frame.getEndLabel(), frame))

    def visitAssign(self, ast, o):
        cRight, retRight = self.visit(ast.exp,
                                      Access(o.frame, o.sym, False, True))
        cLeft, retLeft = self.visit(ast.lhs, Access(o.frame, o.sym, True,
                                                    False))
        code = cRight + cLeft
        if type(retRight) != type(retLeft):
            code = cRight + self.emit.emitI2F(o.frame) + cLeft
        self.emit.printout(code)

    def visitId(self, ast, o):
        sym = o.sym
        frame = o.frame
        sym = self.lookup(ast.name.lower(), sym, lambda x: x.name.lower())
        reType = sym.mtype
        cname = sym.value
        idx = sym.value.value
        id_code = ""
        # id is left not first
        if o.isLeft == True and o.isFirst == False:
            if type(cname) is CName:
                id_code = self.emit.emitPUTSTATIC(cname.value + "." + ast.name,
                                                  reType, frame)
            else:
                id_code = self.emit.emitWRITEVAR(ast.name, reType, idx, frame)
        elif o.isLeft == False:
            if type(cname) is CName:
                id_code = self.emit.emitGETSTATIC(cname.value + "." + ast.name,
                                                  reType, frame)
            else:
                id_code = self.emit.emitREADVAR(ast.name, reType, idx, frame)
        return id_code, reType

    def visitCallStmt(self, ast, o):
        #ast: CallStmt
        #o: Any

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

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

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

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

        in_ = ("", [])
        for x in ast.param:
            str1, typ1 = self.visit(x, Access(frame, nenv, False, True))
            in_ = (in_[0] + str1, in_[1] + [typ1])

        return in_[0] + self.emit.emitINVOKESTATIC(cname + "/" + sym.name,
                                                   ctype, frame), ctype.rettype

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

        lc, lt = self.visit(ast.left, o)
        rc, rt = self.visit(ast.right, o)
        retType = None

        if type(lt) == type(rt):
            if ast.op == '/':
                lc += self.emit.emitI2F(frame)
                rc += self.emit.emitI2F(frame)
                retType = FloatType()
            else:
                retType = lt
        elif type(lt) is IntType:
            lc += self.emit.emitI2F(frame)
            retType = FloatType()
        else:
            rc += self.emit.emitI2F(frame)
            retType = FloatType()
        if ast.op in ["+", "-"]:
            return lc + rc + self.emit.emitADDOP(ast.op, retType,
                                                 frame), retType
        elif ast.op in ["*", "/"]:
            return lc + rc + self.emit.emitMULOP(ast.op, retType,
                                                 frame), retType
        elif ast.op.lower() == "div":
            return lc + rc + self.emit.emitDIV(frame), retType
        elif ast.op.lower() == "mod":
            return lc + rc + self.emit.emitMOD(frame), retType
        elif ast.op.lower() == "and":  # and
            return lc + rc + self.emit.emitANDOP(frame), retType
        elif ast.op.lower() == "or":
            return lc + rc + self.emit.emitOROP(frame), retType
        elif ast.op.lower() in ["andthen", "orelse"]:
            pass
        else:  #( >, <, <>, =, >=, <=)
            return lc + rc + self.emit.emitREOP(ast.op, retType,
                                                frame), BoolType()

    def visitUnaryOp(self, ast, o):
        frame = o.frame
        op = ast.op.lower()
        cExp, retType = self.visit(ast.body, o)
        code = ""
        if op == "not":
            code = cExp + self.emit.emitNOT(retType, frame)
            retType = BoolType()
        elif op == "-":
            code = cExp + self.emit.emitNEGOP(retType, frame)
        return code, retType

    def visitIntLiteral(self, ast, o):
        #ast: IntLiteral
        #o: Any
        return self.emit.emitPUSHICONST(ast.value, o.frame), IntType()

    def visitFloatLiteral(self, ast, o):
        #ast: FloatLiteral
        #o: Any
        return self.emit.emitPUSHFCONST(str(float(ast.value)),
                                        o.frame), FloatType()

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

    def visitStringLiteral(self, ast, o):
        return self.emit.emitPUSHCONST(ast.value, StringType(),
                                       o.frame), StringType()
Example #24
0
class CodeGenVisitor(BaseVisitor, Utils):

    var_status = ["Global", "Parameter", "Local"]

    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"))
        declList = self.env
        # e = SubBody(None, self.env)
        # for x in ast.decl:
        #     e = self.visit(x, e)
        for x in ast.decl:
            if type(x) is FuncDecl:
                declList = [
                    Symbol(x.name.name,
                           MType([y.varType for y in x.param], x.returnType),
                           CName(self.className))
                ] + declList
            else:
                symbol = self.visit(x, (SubBody(None, None), "Global"))
                declList = [symbol] + declList

        e = SubBody(None, declList)
        [self.visit(x, e) for x in ast.decl if type(x) is FuncDecl]
        # generate default constructor
        self.genMETHOD(FuncDecl(Id("<init>"), list(), None, Block(list())), c,
                       Frame("<init>", VoidType))
        self.emit.emitEPILOG()
        return c

    def genMETHOD(self, consdecl, o, frame):
        #consdecl: FuncDecl
        #o: Any
        #frame: Frame
        glenv = o
        isInit = consdecl.returnType is None
        isMain = consdecl.name.name == "main" and len(
            consdecl.param) == 0 and type(consdecl.returnType) is VoidType
        returnType = VoidType() if isInit else consdecl.returnType
        isProc = type(returnType) is VoidType
        methodName = "<init>" if isInit else consdecl.name.name
        intype = [ArrayPointerType(StringType())
                  ] if isMain else [x.varType for x in consdecl.param]
        mtype = MType(intype, returnType)

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

        frame.enterScope(isProc)

        # 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))

        var_List = SubBody(frame, glenv)
        for x in consdecl.param:
            var_List = self.visit(x, (var_List, "Parameter"))

        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))
        # list(map(lambda x: self.visit(x, SubBody(frame, glenv)), body.member))

        for x in consdecl.body.member:
            if type(x) is not VarDecl:
                self.visit(x, var_List)
            else:
                var_List = self.visit(x, (var_List, "Local"))

        self.emit.printout(self.emit.emitLABEL(frame.getEndLabel(), frame))
        if isProc:
            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, frame)
        return SubBody(None, [
            Symbol(ast.name, MType(list(), ast.returnType),
                   CName(self.className))
        ] + subctxt.sym)

    def visitVarDecl(self, ast, o):
        ctxt, location = o
        frame = ctxt.frame
        varName = ast.variable
        varType = ast.varType
        if location == "Global":
            self.emit.printout(
                self.emit.emitATTRIBUTE(varName, varType, False, ""))
            return Symbol(ast.variable, ast.varType)
        elif location == "Local":
            idx = frame.getNewIndex()
            labelStart = frame.getNewLabel()
            self.emit.printout(
                self.emit.emitVAR(idx, varName, varType, labelStart,
                                  frame.getEndLabel(), frame))
            self.emit.printout(self.emit.emitLABEL(labelStart, frame))
            return SubBody(frame,
                           [Symbol(varName, varType, Index(idx))] + ctxt.sym)
        else:
            idx = frame.getNewIndex()
            self.emit.printout(
                self.emit.emitVAR(idx, varName, varType, frame.getStartLabel(),
                                  frame.getEndLabel(), frame))
            return SubBody(frame,
                           [Symbol(varName, varType, Index(idx))] + ctxt.sym)

    # Visit statements:
    def visitBlock(self, ast, o):
        ctxt = o
        frame = o.frame
        nenv = o.sym
        var_List = SubBody(frame, nenv)
        frame.enterScope(False)
        self.emit.printout(self.emit.emitLABEL(frame.getStartLabel(), frame))
        for x in ast.member:
            if type(x) is not VarDecl:
                self.visit(x, var_List)
            else:
                var_List = self.visit(x, (var_List, False))
        self.emit.printout(self.emit.emitLABEL(frame.getEndLabel(), frame))
        frame.exitScope()

    def visitIf(self, ast, o):
        ctxt = o
        frame = ctxt.frame
        nenv = ctxt.sym
        expCode, expType = self.visit(ast.expr, Access(frame, nenv, False,
                                                       True))
        self.emit.printout(expCode)
        labelThen = frame.getNewLabel()  # eval is true
        labelExit = frame.getNewLabel()  # label end
        if ast.elseStmt is None:
            self.emit.printout(self.emit.emitIFTRUE(labelThen, frame))
            self.emit.printout(self.emit.emitGOTO(labelExit, frame))
            self.emit.printout(self.emit.emitLABEL(labelThen, frame))
            self.visit(ast.thenStmt, o)
        else:  # has else stmt
            self.emit.printout(self.emit.emitIFTRUE(labelThen, frame))
            if type(ast.thenStmt) is Block:
                [self.visit(x, o) for x in ast.elseStmt.member]
            else:
                self.visit(ast.elseStmt, o)
            self.emit.printout(self.emit.emitGOTO(labelExit, frame))
            self.emit.printout(self.emit.emitLABEL(labelThen, frame))
            # if type(ast.thenStmt) is Block:
            #     [self.visit(x, o) for x in ast.thenStmt.member]
            # else:
            self.visit(ast.thenStmt, o)
        self.emit.printout(self.emit.emitLABEL(labelExit, frame))

    def visitDowhile(self, ast, o):
        ctxt = o
        frame = ctxt.frame
        nenv = ctxt.sym
        labelLoop = frame.getNewLabel()
        labelExit = frame.getNewLabel()
        frame.enterLoop()
        self.emit.printout(self.emit.emitLABEL(labelLoop, frame))
        list(
            map(
                lambda x: self.visit(x, o)
                if type(x) is not VarDecl else self.visit(x, (o, "Local")),
                ast.sl))

        self.emit.printout(self.emit.emitLABEL(frame.getContinueLabel(),
                                               frame))
        expCode, expType = self.visit(ast.exp, Access(frame, nenv, False,
                                                      True))
        self.emit.printout(expCode)
        self.emit.printout(self.emit.emitIFTRUE(labelLoop, frame))
        self.emit.printout(self.emit.emitGOTO(labelExit, frame))
        self.emit.printout(self.emit.emitLABEL(labelExit, frame))
        self.emit.printout(self.emit.emitLABEL(frame.getBreakLabel(), frame))
        frame.exitLoop()

    def visitFor(self, ast, o):
        ctxt = o
        frame = ctxt.frame
        nenv = ctxt.sym
        labelLoop = frame.getNewLabel()
        labelExit = frame.getNewLabel()
        # exp1Code, exp1Type = self.visit(ast.expr1,  o)
        exp2Code, _ = self.visit(ast.expr2, Access(frame, nenv, False, True))
        # exp3Code, _ = self.visit(ast.expr3, o)
        frame.enterLoop()
        # self.emit.printout(exp1Code)
        self.visit(ast.expr1, o)
        self.emit.printout(self.emit.emitLABEL(labelLoop, frame))
        self.emit.printout(exp2Code)
        self.emit.printout(self.emit.emitIFFALSE(labelExit, frame))
        self.visit(ast.loop, o)
        # self.emit.printout(exp3Code)
        self.emit.printout(self.emit.emitLABEL(frame.getContinueLabel(),
                                               frame))
        self.visit(ast.expr3, o)
        self.emit.printout(self.emit.emitGOTO(labelLoop, frame))
        self.emit.printout(self.emit.emitLABEL(labelExit, frame))
        self.emit.printout(self.emit.emitLABEL(frame.getBreakLabel(), frame))
        frame.exitLoop()

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

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

    def visitReturn(self, ast, o):
        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))

    # Visit expression
    def visitBinaryOp(self, ast, o):
        ctxt = o
        frame = ctxt.frame
        op = ast.op
        nenv = ctxt.sym
        if op == "=":
            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)
            returnCode = expCode + lhsCode
            frame.push()
            if type(o) is SubBody:
                # return "", lhsType
                self.emit.printout(returnCode)
            else:
                returnCode = expCode + self.emit.emitDUP(frame) + lhsCode
                return returnCode, lhsType
        else:
            is_statement = type(o) is SubBody
            leftCode, leftType = self.visit(ast.left,
                                            Access(frame, nenv, False, True))
            rightCode, rightType = self.visit(ast.right,
                                              Access(frame, nenv, False, True))
            expr_type = FloatType() if type(leftType) is not type(
                rightType) else leftType
            if type(expr_type) is FloatType:
                if type(leftType) is IntType:
                    leftCode = leftCode + self.emit.emitI2F(frame)
                if type(rightType) is IntType:
                    rightCode = rightCode + self.emit.emitI2F(frame)
            if op in ["+", "-"]:
                code = leftCode + rightCode + self.emit.emitADDOP(
                    op, expr_type, frame)
            elif op in ["*", "/"]:
                code = leftCode + rightCode + self.emit.emitMULOP(
                    op, expr_type, frame)
            elif op == "%":
                code = leftCode + rightCode + self.emit.emitMOD(frame)
            elif op == "||":
                labelTrue = frame.getNewLabel()
                labelEnd = frame.getNewLabel()
                code = leftCode + self.emit.emitIFTRUE(labelTrue, frame)
                code += rightCode + self.emit.emitIFTRUE(labelTrue, frame)
                code += self.emit.emitPUSHICONST(0, frame)
                code += self.emit.emitGOTO(labelEnd, frame)
                code += self.emit.emitLABEL(labelTrue, frame)
                code += self.emit.emitPUSHICONST(1, frame)
                code += self.emit.emitLABEL(labelEnd, frame)
                # code = leftCode + rightCode + self.emit.emitOROP(frame)
            elif op == "&&":
                labelEnd = frame.getNewLabel()
                labelFalse = frame.getNewLabel()
                code = leftCode + self.emit.emitIFFALSE(labelFalse, frame)
                code += rightCode + self.emit.emitIFFALSE(labelFalse, frame)
                code += self.emit.emitPUSHICONST(1, frame)
                code += self.emit.emitGOTO(labelEnd, frame)
                code += self.emit.emitLABEL(labelFalse, frame)
                code += self.emit.emitPUSHICONST(0, frame)
                code += self.emit.emitLABEL(labelEnd, frame)
                # code = leftCode + rightCode + self.emit.emitANDOP(frame)
            else:
                code = leftCode + rightCode + self.emit.emitREOP(
                    op, expr_type, frame)
                expr_type = BoolType()

            if is_statement:
                self.emit.printout(code)
                self.emit.printout(self.emit.emitPOP(frame))
            else:
                return code, expr_type

    def visitUnaryOp(self, ast, o):
        ctxt = o
        frame = ctxt.frame
        op = ast.op
        nenv = ctxt.sym
        expCode, expType = self.visit(ast.body, Access(frame, nenv, False,
                                                       True))
        if op == "!":
            expCode = expCode + self.emit.emitNOT(BoolType(), frame)
        else:
            expCode = expCode + self.emit.emitNEGOP(expType, frame)
        if type(o) is SubBody:
            self.emit.printout(expCode)
            self.emit.printout(self.emit.emitPOP(frame))
        else:
            return expCode, expType

    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
        paramTypes = ctype.partype

        code = ""
        idx = 0
        for x in ast.param:
            paraCode, paraType = self.visit(x, Access(frame, nenv, False,
                                                      True))
            if type(paramTypes[idx]) is FloatType and type(
                    paraType) is IntType:
                paraCode = paraCode + self.emit.emitI2F(frame)
            code = code + paraCode
            idx += 1

        code = code + self.emit.emitINVOKESTATIC(cname + "/" + ast.method.name,
                                                 ctype, frame)
        # self.emit.printout(self.emit.emitINVOKESTATIC(cname + "/" + ast.method.name, ctype, frame))
        if type(o) is SubBody:
            self.emit.printout(code)
        else:
            return code, ctype.rettype

    def visitId(self, ast, o):
        ctxt = o
        frame = ctxt.frame
        symbols = ctxt.sym
        if type(o) is SubBody:
            sym = self.lookup(ast.name, symbols, lambda x: x.name)
            emitType = sym.mtype
            if sym.value is None:  # not index -> global var - static field
                retCode = self.emit.emitGETSTATIC(
                    self.className + "/" + sym.name, emitType, frame)
            else:
                retCode = self.emit.emitREADVAR(sym.name, emitType,
                                                sym.value.value, frame)
            self.emit.printout(retCode)
            self.emit.printout(self.emit.emitPOP(frame))
        else:
            isLeft = ctxt.isLeft
            isFirst = ctxt.isFirst
            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()
            emitType = sym.mtype
            if sym.value is None:  # not index -> global var - static field
                if isLeft:
                    retCode = self.emit.emitPUTSTATIC(
                        self.className + "/" + sym.name, emitType, frame)
                else:
                    retCode = self.emit.emitGETSTATIC(
                        self.className + "/" + sym.name, emitType, frame)
            else:
                if isLeft:
                    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 visitIntLiteral(self, ast, o):
        #ast: IntLiteral
        #o: Any
        ctxt = o
        frame = ctxt.frame
        code = self.emit.emitPUSHICONST(ast.value, frame)
        if type(o) is SubBody:
            self.emit.printout(code)
            self.emit.printout(self.emit.emitPOP(frame))
        else:
            return code, IntType()

    def visitFloatLiteral(self, ast, o):
        #ast: FloatLiteral
        #o: Any

        ctxt = o
        frame = ctxt.frame
        code = self.emit.emitPUSHFCONST(str(ast.value), frame)
        if type(o) is SubBody:
            self.emit.printout(code)
            self.emit.printout(self.emit.emitPOP(frame))
        else:
            return code, FloatType()

    def visitStringLiteral(self, ast, o):
        ctxt = o
        frame = ctxt.frame
        code = self.emit.emitPUSHCONST('''"''' + str(ast.value) + '''"''',
                                       StringType(), frame)
        if type(o) is SubBody:
            self.emit.printout(code)
            self.emit.printout(self.emit.emitPOP(frame))
        else:
            return code, StringType()

    def visitBooleanLiteral(self, ast, o):
        ctxt = o
        frame = ctxt.frame
        code = self.emit.emitPUSHICONST(str(ast.value).lower(), frame)
        if type(o) is SubBody:
            self.emit.printout(code)
            self.emit.printout(self.emit.emitPOP(frame))
        else:
            return code, BoolType()
Example #25
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")
        self.currentFunc = Symbol("null", MType([], VoidType()),
                                  CName(self.className))

    def visitVarGlobal(self, ast, c):
        _ctxt = c
        _name = ast.variable
        _type = ast.varType
        self.emit.printout(self.emit.emitATTRIBUTE(_name, _type, False, ""))
        _sym = Symbol(_name, _type, CName(self.className))
        _ctxt.append(_sym)
        return _ctxt

    def visitFunGlobal(self, ast, c):
        _ctxt = c
        _name = ast.name.name
        _type = MType([x.varType for x in ast.param], ast.returnType)
        _sym = Symbol(_name, _type, CName(self.className))
        _ctxt.append(_sym)
        return _ctxt

    def visitProgram(self, ast, c):
        self.emit.printout(
            self.emit.emitPROLOG(self.className, 'java.lang.Object'))
        lstVarDecl = []
        lstFuncDecl = []

        for x in ast.decl:
            if type(x) is VarDecl:
                lstVarDecl += [x]
            elif type(x) is FuncDecl:
                lstFuncDecl += [x]
        for x in ast.decl:
            self.env += self.visitVarGlobal(
                x, self.env) if type(x) is VarDecl else self.visitFunGlobal(
                    x, self.env)
        for funDecl in lstFuncDecl:
            self.visit(funDecl, SubBody(None, self.env))
        self.genMETHOD(FuncDecl(Id("<init>"), [], None, []), c,
                       Frame("<init>", VoidType))
        lstArrayType = []
        for item in lstVarDecl:
            if type(item.varType) is ArrayType:
                lstArrayType += [item]

        if lstArrayType:
            self.emit.printout(
                self.emit.emitCLINIT(self.className, lstArrayType,
                                     Frame("<clinit>", VoidType())))
        self.emit.emitEPILOG()
        return c

    def genMETHOD(self, decl, c, frame):
        IsInit = True if decl.returnType is None else False
        IsMain = True if decl.name.name == "main" else False
        nameMethod = "<init>" if IsInit else decl.name.name
        typePara = [ArrayPointerType(StringType())
                    ] if IsMain else [x.varType for x in decl.param]
        returntype = VoidType() if IsInit else decl.returnType
        mtype = MType(typePara, returntype)
        self.emit.printout(
            self.emit.emitMETHOD(nameMethod, mtype, not IsInit, frame))

        frame.enterScope(True)

        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))
        subBody = SubBody(frame, c)
        if not IsMain and len(decl.param) != 0:
            for param in decl.param:
                subBody = self.visit(param, subBody)
        body = decl.body
        if not IsInit:
            for member in body.member:
                if type(member) is VarDecl:
                    subBody = self.visit(member, subBody)

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

        if IsInit:
            self.emit.printout(
                self.emit.emitREADVAR('this', ClassType(self.className), 0,
                                      frame))
            self.emit.printout(self.emit.emitINVOKESPECIAL(frame))

        lstReturnStmt = []
        lstVarArrayType = []
        if not IsInit:
            for member in decl.body.member:
                if type(member) is VarDecl:
                    if type(member.varType) is ArrayType:
                        lstVarArrayType += [member]
        [self.ArrayTypeDecl(x, subBody) for x in lstVarArrayType]
        if not IsInit:
            for member in decl.body.member:
                if not type(member) is VarDecl:
                    if type(member) is Return:
                        lstReturnStmt += [member]
                    self.visit(member, subBody)

        for i in range(0, frame.getStackSize(), 1):
            self.emit.printout(self.emit.emitPOP(frame))
        self.emit.printout(self.emit.emitLABEL(frame.getEndLabel(), frame))

        if (type(returntype) is VoidType or len(lstReturnStmt) == 0):
            self.emit.printout(self.emit.emitRETURN(VoidType(), frame))

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

        frame.exitScope()

    def visitVarDecl(self, ast, c):
        frame = c.frame
        lstSym = c.sym if type(c) is SubBody else []
        varIndex = frame.getNewIndex()
        varName = ast.variable
        varType = ast.varType
        self.emit.printout(
            self.emit.emitVAR(varIndex, varName,
                              varType, frame.getStartLabel(),
                              frame.getEndLabel(), frame))
        lstSym = [Symbol(varName, varType, Index(varIndex))] + lstSym
        return SubBody(frame, lstSym)

    def visitFuncDecl(self, ast, c):
        frame = Frame(ast.name.name, ast.returnType)
        lstSym = c.sym
        self.currentFunc = self.lookup(ast.name.name, lstSym, lambda x: x.name)
        self.genMETHOD(ast, lstSym, frame)
        return c

    def genBinString(self, frame, retLeft, typeLeft, retRight, typeRight):
        if type(typeLeft) is IntType and type(typeRight) is FloatType:
            return retLeft + self.emit.emitI2F(frame) + retRight, FloatType()
        if type(typeLeft) is FloatType and type(typeRight) is IntType:
            return retLeft + retRight + self.emit.emitI2F(frame), FloatType()
        if type(typeLeft) is FloatType and type(typeRight) is FloatType():
            return retLeft + self.emit.emitI2F(
                frame) + retRight + self.emit.emitI2F(frame), FloatType()
        stringReturn = retLeft + retRight, typeLeft
        # print(stringReturn)
        return stringReturn

    def visitUnaryOp(self, ast, c):
        op = ast.op
        frame = c.frame
        lstSym = c.sym
        (retExpr, typeExpr) = self.visit(ast.body,
                                         Access(frame, lstSym, False, True))
        if op == '-':
            return retExpr + self.emit.emitNEGOP(typeExpr, frame), typeExpr
        elif op == '!':
            return retExpr + self.emit.emitNOT(BoolType(), frame), BoolType()

    def ArrayTypeDecl(self, ast, c):
        lstSym = c.sym
        frame = c.frame
        sym = self.lookup(ast.variable, lstSym, lambda x: x.name)
        index = sym.value.value
        self.emit.printout(self.emit.emitNEWARRAY(ast.varType, frame))
        self.emit.printout(
            self.emit.emitWRITEVAR(ast.variable, ast.varType, index, frame))
        return SubBody(frame, sym)

    def visitBlock(self, ast, c):
        frame = c.frame
        oldEnv = c.sym
        frame.enterScope(False)
        varDeclInBlock = SubBody(frame, oldEnv)
        arrayDecl = []
        lstStmt = []
        for item in ast.member:
            if type(item) is VarDecl:
                varDeclInBlock = self.visit(item, c)
            else:
                lstStmt += [item]
            if type(item.varType) is ArrayType:
                arrayDecl += [item]

        self.emit.printout(self.emit.emitLABEL(frame.getStartLabel(), frame))
        [self.ArrayTypeDecl(x, varDeclInBlock) for x in arrayDecl]
        [self.visit(x, varDeclInBlock) for x in lstStmt]
        self.emit.printout(self.emit.emitLABEL(frame.getEndLabel(), frame))
        frame.exitScope()
        return None

    def visitIf(self, ast, c):
        frame = c.frame
        lstSym = c.sym

        stringReturn, typeReturn = self.visit(
            ast.expr, Access(frame, lstSym, False, True))
        falseLabel = frame.getNewLabel()
        self.emit.printout(stringReturn +
                           self.emit.emitIFFALSE(falseLabel, frame))
        if not type(ast.thenStmt) is Block:
            [self.visit(ast.thenStmt, c)]
        else:
            [self.visit(x, c) for x in ast.thenStmt.member]
        if not ast.elseStmt:
            self.emit.printout(self.emit.emitLABEL(falseLabel, frame))
        else:
            trueLabel = frame.getNewLabel()
            self.emit.printout(self.emit.emitGOTO(trueLabel, frame))
            self.emit.printout(self.emit.emitLABEL(falseLabel, frame))
            if not type(ast.elseStmt) is Block:
                [self.visit(ast.elseStmt, c)]
            else:
                [self.visit(x, c) for x in ast.elseStmt.member]
            self.emit.printout(self.emit.emitLABEL(trueLabel, frame))
        return None

    def visitDowhile(self, ast, c):
        frame = c.frame
        lstSym = c.sym
        loopLabel = frame.getNewLabel()
        frame.enterLoop()
        self.emit.printout(self.emit.emitLABEL(loopLabel, frame))
        [self.visit(x, c) for x in ast.sl]
        self.emit.printout(self.emit.emitLABEL(frame.getContinueLabel(),
                                               frame))
        stringReturn, typeReturn = self.visit(
            ast.exp, Access(frame, lstSym, False, True))
        self.emit.printout(stringReturn)
        self.emit.printout(self.emit.emitIFFALSE(frame.getBreakLabel(), frame))
        self.emit.printout(self.emit.emitGOTO(loopLabel, frame))
        self.emit.printout(self.emit.emitLABEL(frame.getBreakLabel(), frame))
        frame.exitLoop()
        return None

    def visitFor(self, ast, c):
        frame = c.frame
        lstSym = c.sym
        loopLabel = frame.getNewLabel()
        frame.enterLoop()
        self.visit(ast.expr1, Access(frame, lstSym, False, True))
        self.emit.printout(self.emit.emitLABEL(loopLabel, frame))
        stringReturn, typeReturn = self.visit(
            ast.expr2, Access(frame, lstSym, False, True))
        self.emit.printout(stringReturn)
        self.emit.printout(self.emit.emitIFFALSE(frame.getBreakLabel(), frame))
        if (type(ast.loop) is Block):
            [self.visit(x, c) for x in ast.loop.member]
        else:
            [self.visit(ast.loop, c)]
        self.emit.printout(self.emit.emitLABEL(frame.getContinueLabel(),
                                               frame))
        self.visit(ast.expr3, Access(frame, lstSym, False, True))
        self.emit.printout(self.emit.emitGOTO(loopLabel, frame))
        self.emit.printout(self.emit.emitLABEL(frame.getBreakLabel(), frame))
        frame.exitLoop()
        return None

    def visitCallExpr(self, ast, c):
        lstSym = c.sym
        frame = c.frame
        sym = self.lookup(ast.method.name, lstSym, lambda x: x.name)
        cname = sym.value.value
        ctype = sym.mtype
        lstParaType = ctype.partype

        if type(c) is Access:
            if c.isLeft and not c.isFirst:
                return self.emit.emitWRITEVAR2(ast.method.name, ctype.rettype,
                                               frame), ctype.rettype
        _in = ("", [])
        lstCheck = []
        for item in range(len(lstParaType)):
            lstCheck.append((ast.param[item], lstParaType[item]))
        for x in lstCheck:
            (stringReturn,
             typeReturn) = self.visit(x[0], Access(frame, lstSym, False, True))
            if type(x[1]) is FloatType and type(typeReturn) is IntType:
                _in = (_in[0] + stringReturn + self.emit.emitI2F(frame),
                       _in[1] + [typeReturn])
            else:
                _in = (_in[0] + stringReturn, _in[1] + [typeReturn])

        self.emit.printout(_in[0])
        self.emit.printout(
            self.emit.emitINVOKESTATIC(cname + "/" + sym.name, ctype, frame))
        return "", ctype.rettype

    def visitArrayCell(self, ast, c):

        frame = c.frame
        lstSym = c.sym
        if type(c) != SubBody:
            if c.isLeft == True and c.isFirst == True:
                arrStringReturn, arrTypeReturn = self.visit(
                    ast.arr, Access(frame, lstSym, False, True))
                idxStringReturn, idxTypeReturn = self.visit(
                    ast.idx, Access(frame, lstSym, False, True))
                return arrStringReturn + idxStringReturn, arrTypeReturn.eleType
            elif c.isLeft == True and c.isFirst == False:
                arrStringReturn, arrTypeReturn = self.visit(
                    ast.arr, Access(frame, lstSym, True, False))
                return arrStringReturn, arrTypeReturn
            elif c.isLeft == False:
                arrStringReturn, arrTypeReturn = self.visit(
                    ast.arr, Access(frame, lstSym, False, True))
                idxStringReturn, idxTypeReturn = self.visit(
                    ast.idx, Access(frame, lstSym, False, True))
                if type(arrTypeReturn) is ArrayType:
                    arrayType = arrTypeReturn.eleType
                    aload = self.emit.emitALOAD(arrayType, frame)
                    return arrStringReturn + idxStringReturn + aload, arrayType
                elif type(arrTypeReturn) is ArrayPointerType:
                    arrayPointerType = arrTypeReturn.eleType
                    aload = self.emit.emitALOAD(arrayPointerType, frame)
                    return arrStringReturn + idxStringReturn + aload, arrayPointerType
        else:
            arrStringReturn, arrTypeReturn = self.visit(
                ast.arr, Access(frame, lstSym, False, True))
            arrType = arrTypeReturn.eleType
            return "", arrTypeReturn
        return None

    def visitBinaryOp(self, ast, c):
        frame = c.frame
        lstSym = c.sym
        op = ast.op
        if op != '=':
            (retLeft,
             typeLeft) = self.visit(ast.left, Access(frame, lstSym, False,
                                                     True))
            (retRight,
             typeRight) = self.visit(ast.right,
                                     Access(frame, lstSym, False, True))
            stringExp, typeExp = self.genBinString(frame, retLeft, typeLeft,
                                                   retRight, typeRight)
            if (op in ['+', '-']):
                stringOp = self.emit.emitADDOP(op, typeExp, frame)
            elif op in ['*', '/']:
                stringOp = self.emit.emitMULOP(op, typeExp, frame)
            elif op in ['>', '>=', '<', '<=', '!=', '==']:
                stringOp = self.emit.emitREOP(op, typeExp, frame)
                typeExp = BoolType()
            elif op == '%':
                stringOp = self.emit.emitMOD(frame)
            elif op == '||':
                stringOp = self.emit.emitOROP(frame)
                typeExp = BoolType()
            elif op == '&&':
                stringOp = self.emit.emitANDOP(frame)
                typeExp = BoolType()
            return stringExp + stringOp, typeExp
        elif op == '=':
            # print(ast)
            strDup = ""
            strForI2F = ""
            stringReturn = ""
            (retFirstLeft,
             typeFirstLeft) = self.visit(ast.left,
                                         Access(frame, lstSym, True, True))
            (retRight,
             typeRight) = self.visit(ast.right,
                                     Access(frame, lstSym, False, True, True))

            if type(typeFirstLeft) is FloatType and type(typeRight) is IntType:
                strForI2F = self.emit.emitI2F(frame)
            if (type(c) is Access) and (c.isDup == True):
                strDup = self.emit.emitDUP(frame)
                self.emit.printout(retRight)
            stringReturn = retFirstLeft + strForI2F
            self.emit.printout(stringReturn + strDup)

            (retSecondLeft,
             typeSecondLeft) = self.visit(ast.left,
                                          Access(frame, lstSym, True, False))

            self.emit.printout(retSecondLeft)
            return (stringReturn, typeSecondLeft)

    def visitId(self, ast, c):
        if not type(c) is SubBody:
            frame = c.frame
            lstSym = c.sym
            sym = self.lookup(ast.name, lstSym, lambda x: x.name)
            stringReturn = ""
            if c.isLeft == True and c.isFirst == True:
                pass
            elif c.isLeft == True and c.isFirst == False:
                if type(sym.mtype) is ArrayType or type(
                        sym.mtype) is ArrayPointerType:
                    stringReturn = self.emit.emitWRITEVAR2(
                        sym.name, sym.mtype, frame)
                else:
                    if type(sym.value) is CName:
                        stringReturn = self.emit.emitPUTSTATIC(
                            sym.value.value + "." + sym.name, sym.mtype, frame)
                    elif type(sym.value) is Index:
                        stringReturn = self.emit.emitWRITEVAR(
                            sym.name, sym.mtype, sym.value.value, frame)
            elif c.isLeft == False:
                if type(sym.value) is CName:
                    stringReturn = self.emit.emitGETSTATIC(
                        sym.value.value + "." + sym.name, sym.mtype, frame)
                elif type(sym.value) is Index:
                    stringReturn = self.emit.emitREADVAR(
                        sym.name, sym.mtype, sym.value.value, frame)
            return stringReturn, sym.mtype
        else:

            sym = self.lookup(ast.name, lstSym, lambda x: x.name)
            return ("", sym.mtype)

    def visitReturn(self, ast, c):
        frame = c.frame
        lstSym = c.sym
        if ast.expr:
            stringReturn, typeReturn = self.visit(
                ast.expr, Access(frame, lstSym, False, True))
            typeFuncReturn = self.currentFunc.mtype.rettype
            if (type(typeFuncReturn) is FloatType
                    and type(typeReturn) is IntType):
                self.emit.printout(stringReturn + self.emit.emitI2F(frame) +
                                   self.emit.emitRETURN(FloatType(), frame))
            else:
                self.emit.printout(stringReturn +
                                   self.emit.emitRETURN(typeReturn, frame))
        else:
            self.emit.printout(self.emit.emitRETURN(VoidType(), frame))

    def visitContinue(self, ast, c):
        self.emit.printout(
            self.emit.emitGOTO(c.frame.getContinueLabel(), c.frame))
        return None

    def visitBreak(self, ast, c):
        self.emit.printout(self.emit.emitGOTO(c.frame.getBreakLabel(),
                                              c.frame))
        return None

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

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

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

    def visitBooleanLiteral(self, ast, c):
        ctxt = c
        frame = ctxt.frame
        return self.emit.emitPUSHICONST(str(ast.value).lower(),
                                        frame), BoolType()
Example #26
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 = "MPClass"
        self.path = dir_
        self.emit = Emitter(self.path + "/" + self.className + ".j")
        self.curFunc = Symbol("null", MType([], VoidType()),
                              CName(self.className))

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

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

        lsFunc = list(filter(lambda x: type(x) is FuncDecl, ast.decl))

        for x in ast.decl:
            if type(x) is VarDecl:
                self.emit.printout(
                    self.emit.emitATTRIBUTE(x.variable.name, x.varType, False,
                                            ""))
                symbol = Symbol(x.variable.name, x.varType,
                                CName(self.className))
                self.env.append(symbol)
            else:
                typeFunc = MType([y.varType for y in x.param], x.returnType)
                symbol = Symbol(x.name.name, typeFunc, CName(self.className))
                self.env.append(symbol)

        e = SubBody(None, self.env)
        for x in lsFunc:
            self.visit(x, e)

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

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

        isInit = consdecl.returnType is None
        isMain = consdecl.name.name == "main" and len(
            consdecl.param) == 0 and type(consdecl.returnType) is VoidType
        returnType = VoidType() if isInit else consdecl.returnType
        methodName = "<init>" if isInit else consdecl.name.name
        intype = [ArrayPointerType(StringType())
                  ] if isMain else [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)

        lsVarLocal = []
        # 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))
        else:
            for x in consdecl.param:
                self.visit(x, SubBody(frame, lsVarLocal))
                #self.emit.printout(self.emit.emitVAR(frame.getNewIndex(), x.variable.name, x.varType, frame.getStartLabel(), frame.getEndLabel(), frame))
                #lsVarLocal.append(Symbol(x.variable.name, x.varType,Index(frame.currIndex-1)))
        for y in consdecl.local:
            self.visit(y, SubBody(frame, lsVarLocal))
            #self.emit.printout(self.emit.emitVAR(frame.getNewIndex(), y.variable.name, y.varType, frame.getStartLabel(), frame.getEndLabel(), frame))
            #lsVarLocal.append(Symbol(y.variable.name, y.varType,Index(frame.currIndex-1)))

        glenv = lsVarLocal + (o if o != None else [])

        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))

        list(map(lambda x: self.visit(x, SubBody(frame, glenv)), body))
        self.emit.printout(self.emit.emitLABEL(frame.getEndLabel(), frame))
        if type(returnType) is VoidType:
            self.emit.printout(self.emit.emitRETURN(VoidType(), frame))
        else:

            self.emit.printout(self.emit.emitRETURN(returnType, frame))
        self.emit.printout(self.emit.emitENDMETHOD(frame))
        frame.exitScope()

    def visitVarDecl(self, ast, c):
        ctxt = c
        frame = ctxt.frame
        glenv = ctxt.sym
        name = ast.variable.name
        mtype = ast.varType

        if frame is not None:
            idx = frame.getNewIndex()
            self.emit.printout(
                self.emit.emitVAR(idx, name, mtype, frame.getStartLabel(),
                                  frame.getEndLabel(), frame))
        return SubBody(frame, glenv.append(Symbol(name, mtype, Index(idx))))

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

    def visitAssign(self, ast, c):
        ctxt = c
        frame = ctxt.frame
        env = ctxt.sym
        str_I2f = ''
        (resExpr, typeExpr) = self.visit(ast.exp,
                                         Access(c.frame, c.sym, False, True))
        (reslhs, typelhs) = self.visit(ast.lhs,
                                       Access(c.frame, c.sym, True, False))
        #print(typeExpr)
        #print(reslhs)
        if type(typelhs) == FloatType and type(typeExpr) == IntType:
            str_I2f = self.emit.emitI2F(frame)
        self.emit.printout(resExpr + str_I2f + reslhs)

    def visitIf(self, ast, c):  #
        ctxt = c
        frame = ctxt.frame
        nenv = ctxt.sym
        #Code for expr
        expr, typeD = self.visit(ast.expr, Access(frame, nenv, False, False))

        #Get 2 new lables
        falseLabel = frame.getNewLabel()
        trueLabel = frame.getNewLabel()
        #Code "ifeq" + falseLabel
        self.emit.printout(expr + self.emit.emitIFFALSE(falseLabel, frame))
        #Code for "then" stmt
        for i in ast.thenStmt:
            self.visit(i, SubBody(frame, nenv))

        #visitStmt(ast.thenStmt, frame, nenv)

        #code "goto" + trueLabel
        self.emit.printout(
            self.emit.emitGOTO(str(trueLabel), frame) +
            self.emit.emitLABEL(falseLabel, frame))

        #code for "else" stmt
        if (ast.elseStmt != None):
            for x in ast.elseStmt:
                self.visit(x, SubBody(frame, nenv))
        # visitStmt(ast.elseStmt.get, frame, nenv)

        #Code "trueLabel"
        self.emit.printout(self.emit.emitLABEL(trueLabel, frame))

    # def visitFor(self, ast, c):

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

    def visitBreak(self, ast, c):
        self.emit.printout(self.emit.emitGOTO(c.frame.getBreakLabel(),
                                              c.frame))

    def visitReturn(self, ast, c):
        if ast.expr:
            (resExp, resType) = self.visit(ast.expr,
                                           Access(c.frame, c.sym, False, True))
            typeFun = self.curFunc.mtype.rettype
            if type(typeFun) is FloatType and type(resType) is IntType:
                self.emit.printout(resExp + self.emit.emitI2F(c.frame))
            else:
                self.emit.printout(resExp)
        self.emit.printout(self.emit.emitGOTO(c.frame.getEndLabel(), c.frame))

    def visitFor(self, ast, c):
        frame = c.frame
        frame.enterLoop()
        inB = frame.getNewLabel()
        contiLabel = frame.getContinueLabel()
        bkLabel = frame.getBreakLabel()

        self.visit(Assign(ast.id, ast.expr1), c)
        self.emit.printout(self.emit.emitLABEL(inB, frame))
        op = '<=' if ast.up else '>='
        (resExC, resT) = self.visit(BinaryOp(op, ast.id, ast.expr2),
                                    Access(frame, c.sym, False, True))
        self.emit.printout(resExC + self.emit.emitIFFALSE(bkLabel, frame))
        for i in ast.loop:
            self.visit(i, SubBody(frame, c.sym))
        self.emit.printout(self.emit.emitLABEL(contiLabel, frame))
        op = '+' if ast.up else '-'
        self.visit(Assign(ast.id, BinaryOp(op, ast.id, IntLiteral(1))), c)
        self.emit.printout(self.emit.emitGOTO(inB, frame))
        self.emit.printout(self.emit.emitLABEL(bkLabel, frame))
        frame.exitLoop()

    def visitWith(self, ast, c):
        frame = c.frame

        frame.enterScope(False)
        lsVarLocal = []
        # Generate code for parameter declarations

        for y in ast.decl:
            self.emit.printout(
                self.emit.emitVAR(frame.getNewIndex(), y.variable.name,
                                  y.varType, frame.getStartLabel(),
                                  frame.getEndLabel(), frame))
            lsVarLocal.append(
                Symbol(y.variable.name, y.varType, Index(frame.currIndex - 1)))

        glenv = lsVarLocal + c.sym
        self.emit.printout(self.emit.emitLABEL(frame.getStartLabel(), frame))
        for x in ast.stmt:
            self.visit(x, SubBody(frame, glenv))
        self.emit.printout(self.emit.emitLABEL(frame.getEndLabel(), frame))
        frame.exitScope()

    def visitWhile(self, ast, c):  #
        frame = c.frame
        nenv = c.sym
        # enter loop
        frame.enterLoop()
        breakLabel = frame.getBreakLabel()
        continueLabel = frame.getContinueLabel()
        # code for continueLabel
        self.emit.printout(self.emit.emitLABEL(continueLabel, frame))
        expr, typeD = self.visit(ast.exp, Access(frame, nenv, False, True))
        #print(expr)
        #print(typeD)
        self.emit.printout(expr + self.emit.emitIFFALSE(breakLabel, frame))

        for x in ast.sl:
            self.visit(x, SubBody(frame, nenv))

        self.emit.printout(self.emit.emitGOTO(continueLabel, frame))
        self.emit.printout(self.emit.emitLABEL(breakLabel, frame))
        frame.exitLoop()

    def visitCallStmt(self, ast, o):
        self.callStmtAndExp(ast, o)
        #print(self.callStmtAndExp(ast, o))
    def visitCallExpr(self, ast, o):
        return (self.callStmtAndExp(ast, o)[0], self.callStmtAndExp(ast, o)[1])

    def callStmtAndExp(self, ast, o):
        #ast: CallStmt
        #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
        returnType = ctype.rettype
        partype = sym.mtype.partype
        #print(ctype)
        #print(cname)
        #print((sym.name))
        # print(frame)
        code = ""
        typeList = list()
        for x in range(len(ast.param)):
            #  print(ast.param[x])
            str1, typ1 = self.visit(ast.param[x],
                                    Access(frame, nenv, False, True))
            # print(str1)
            # print(typ1)
            code += str1
            typeList.append(typ1)

            if type(partype[x]) is FloatType and type(typ1) is IntType:
                code += self.emit.emitI2F(frame)
        if type(ast) is CallStmt:
            self.emit.printout(code)
            self.emit.printout(
                self.emit.emitINVOKESTATIC(cname + "/" + ast.method.name,
                                           ctype, frame))
        else:
            return (code + self.emit.emitINVOKESTATIC(
                cname + "/" + ast.method.name, ctype, frame), returnType)

    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), frame), BoolType()

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

    def visitId(self, ast, c):
        sym = self.lookup(ast.name, c.sym, lambda x: x.name)
        if c.isLeft:
            if type(sym.value) is CName:
                return self.emit.emitPUTSTATIC(self.className + '.' + sym.name,
                                               sym.mtype, c.frame), sym.mtype
            else:
                return self.emit.emitWRITEVAR(sym.name, sym.mtype,
                                              sym.value.value,
                                              c.frame), sym.mtype
        else:
            if type(sym.value) is CName:
                return self.emit.emitGETSTATIC(self.className + '.' + sym.name,
                                               sym.mtype, c.frame), sym.mtype
            else:
                return self.emit.emitREADVAR(sym.name, sym.mtype,
                                             sym.value.value,
                                             c.frame), sym.mtype

    def checkEqual(self, lt, rt):
        if type(lt) == type(rt):
            return True
        else:
            return False

    def visitUnaryOp(self, ast, o):
        lc, lt = self.visit(ast.body, Access(o.frame, o.sym, False, True))
        if ast.op == '-':
            return lc + self.emit.emitNEGOP(lt, o.frame), lt
        elif ast.op == 'not':
            return lc + self.emit.emitNOT(lt, o.frame), lt

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

        if ast.op == 'andthen':
            codeEndthen = ""
            Label1 = frame.getNewLabel()
            Label2 = frame.getNewLabel()
            lc, lt = self.visit(ast.left, Access(frame, o.sym, False, True))
            codeEndthen += lc + self.emit.emitIFFALSE(
                Label1, frame) + self.emit.emitPUSHICONST('true', frame)
            rc, rt = self.visit(ast.right, Access(frame, o.sym, False, True))
            codeEndthen += rc + self.emit.emitANDOP(
                frame) + self.emit.emitGOTO(
                    Label2, frame) + self.emit.emitLABEL(
                        Label1, frame) + self.emit.emitPUSHICONST(
                            'false', frame) + self.emit.emitLABEL(
                                Label2, frame)
            #print(codeEndthen)
            return codeEndthen, BoolType()
        elif ast.op == 'orelse':
            codeOrelse = ""
            Label1 = frame.getNewLabel()
            Label2 = frame.getNewLabel()
            lc, lt = self.visit(ast.left, Access(frame, o.sym, False, True))
            codeOrelse += lc + self.emit.emitIFFALSE(
                Label1, frame) + self.emit.emitPUSHICONST('false', frame)
            rc, rt = self.visit(ast.right, Access(frame, o.sym, False, True))
            codeOrelse += rc + self.emit.emitOROP(frame) + self.emit.emitGOTO(
                Label2, frame) + self.emit.emitLABEL(
                    Label1, frame) + self.emit.emitPUSHICONST(
                        'true', frame) + self.emit.emitLABEL(Label2, frame)
            #print(codeEndthen)
            return codeOrelse, BoolType()

        lc, lt = self.visit(ast.left, Access(frame, o.sym, False, True))
        rc, rt = self.visit(ast.right, Access(frame, o.sym, False, True))
        # print(lc)
        #print(rc) a(ifl)+b(int)
        if ast.op in ['+', '-']:
            if self.checkEqual(lt, rt):
                return lc + rc + self.emit.emitADDOP(ast.op, lt, frame), lt
            else:
                if type(lt) is FloatType and type(rt) is IntType:
                    return lc + rc + self.emit.emitI2F(
                        frame) + self.emit.emitADDOP(ast.op, lt, frame), lt
                else:
                    return lc + self.emit.emitI2F(
                        frame) + rc + self.emit.emitADDOP(ast.op, rt,
                                                          frame), rt
        elif ast.op == '*':
            if self.checkEqual(lt, rt):
                return lc + rc + self.emit.emitMULOP(ast.op, lt, frame), lt
            else:
                if type(lt) is FloatType and type(rt) is IntType:
                    return lc + rc + self.emit.emitI2F(
                        frame) + self.emit.emitMULOP(ast.op, lt, frame), lt
                else:
                    return lc + self.emit.emitI2F(
                        frame) + rc + self.emit.emitMULOP(ast.op, rt,
                                                          frame), rt
        elif ast.op == '/':
            if self.checkEqual(lt, rt):
                if type(lt) is IntType:
                    return lc + self.emit.emitI2F(
                        frame) + rc + self.emit.emitI2F(
                            frame) + self.emit.emitMULOP(
                                ast.op, FloatType, frame), FloatType()
                return lc + rc + self.emit.emitMULOP(ast.op, lt, frame), lt
            else:
                if type(lt) is FloatType and type(rt) is IntType:
                    return lc + rc + self.emit.emitI2F(
                        frame) + self.emit.emitMULOP(ast.op, lt, frame), lt
                else:
                    return lc + self.emit.emitI2F(
                        frame) + rc + self.emit.emitMULOP(ast.op, rt,
                                                          frame), rt
        elif ast.op == 'div':
            return lc + rc + self.emit.emitDIV(frame), lt
        elif ast.op == 'mod':
            return lc + rc + self.emit.emitMOD(frame), lt
        elif ast.op == 'and':
            return lc + rc + self.emit.emitANDOP(frame), BoolType()
        elif ast.op == 'or':
            return lc + rc + self.emit.emitOROP(frame), BoolType()
        elif ast.op in ['<', '<=', '>', '>=', '<>', '=']:
            if type(lt) is FloatType and type(rt) is IntType:
                return lc + rc + self.emit.emitI2F(frame) + self.emit.emitREOP(
                    ast.op, FloatType(), frame), BoolType()
            elif type(lt) is IntType and type(rt) is FloatType:
                return lc + self.emit.emitI2F(frame) + rc + self.emit.emitREOP(
                    ast.op, FloatType(), frame), BoolType()
            else:
                return lc + rc + self.emit.emitREOP(ast.op, lt,
                                                    frame), BoolType()
Example #27
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 = "MPClass"
        self.path = dir_
        self.emit = Emitter(self.path + "/" + self.className + ".j")

    def visitProgram(self, ast, ctxt):
        self.emit.printout(
            self.emit.emitPROLOG(self.className, "java.lang.Object"))
        global_env = SubBody(None, self.env)
        functions = list(filter(lambda y: not type(y) is VarDecl, ast.decl))
        if len(functions) < len(ast.decl): self.emit.printout("\n")
        # static fields
        for x in ast.decl:
            global_env = self.visit(x, global_env)
        # default constructor
        self.genMETHOD(FuncDecl(Id("<init>"), list(), list(), list(), None),
                       ctxt, Frame("<init>", VoidType))
        # generate code for functions
        for x in functions:
            self.visit(x, SubBody("", global_env.sym))
        self.emit.emitEPILOG()
        return ctxt

    def visitVarDecl(self, ast, ctxt):
        frame = ctxt.frame
        if frame is None:
            self.emit.printout(
                self.emit.emitATTRIBUTE(ast.variable.name, ast.varType, False,
                                        ""))
            return SubBody(frame, [
                Symbol(ast.variable.name, ast.varType, CName(self.className))
            ] + ctxt.sym)
        else:
            value = frame.getNewIndex()
            self.emit.printout(
                self.emit.emitVAR(value, ast.variable.name, ast.varType,
                                  frame.getStartLabel(), frame.getEndLabel(),
                                  frame))
            return SubBody(frame,
                           [Symbol(ast.variable.name, ast.varType, value)] +
                           ctxt.sym)

    def visitFuncDecl(self, ast, ctxt):
        if ctxt.frame is None:
            return SubBody(None, [
                Symbol(ast.name.name,
                       MType([x.varType for x in ast.param], ast.returnType),
                       CName(self.className))
            ] + ctxt.sym)
        else:
            self.genMETHOD(ast, ctxt.sym, Frame(ast.name, ast.returnType))

    def genMETHOD(self, ast, global_env, frame):
        #ast: FuncDecl
        isInit = ast.returnType is None
        isMain = ast.name.name.lower() == "main" and len(
            ast.param) == 0 and type(ast.returnType) is VoidType
        returnType = VoidType() if isInit else ast.returnType
        methodName = "<init>" if isInit else ast.name.name
        # main function uses java syntax
        intype = [ArrayPointerType(StringType())
                  ] if isMain else [x.varType for x in ast.param]
        mtype = MType(intype, returnType)
        self.emit.printout(
            self.emit.emitMETHOD("main" if isMain else methodName, mtype,
                                 not isInit, frame))
        frame.enterScope(True)

        # special methods
        if isInit:
            self.emit.printout(
                self.emit.emitVAR(frame.getNewIndex(), "this",
                                  ClassType(self.className),
                                  frame.getStartLabel(), frame.getEndLabel(),
                                  frame))
            self.emit.printout(
                self.emit.emitREADVAR("this", ClassType(self.className), 0,
                                      frame))
            self.emit.printout(self.emit.emitINVOKESPECIAL(frame))
        if isMain:
            self.emit.printout(
                self.emit.emitVAR(frame.getNewIndex(), "args",
                                  ArrayPointerType(StringType()),
                                  frame.getStartLabel(), frame.getEndLabel(),
                                  frame))

        # Generate code for parameter declarations
        curr_env = SubBody(frame, global_env)
        for x in ast.param + ast.local:
            curr_env = self.visit(x, curr_env)
        # Visit body and generate code for statements
        self.emit.printout(self.emit.emitLABEL(frame.getStartLabel(), frame))
        for x in ast.body:
            self.visit(x, SubBody(frame, curr_env.sym))
        self.emit.printout(self.emit.emitLABEL(frame.getEndLabel(), frame))
        # handle the last areturn/ireturn/freturn
        if not type(returnType) is VoidType:
            frame.push()
            retcode = self.emit.emitRETURN(returnType, frame)
            self.emit.reversedremove(retcode)
        else:
            retcode = self.emit.emitRETURN(returnType, frame)
        self.emit.printout(retcode)
        # .end method
        self.emit.printout(self.emit.emitENDMETHOD(frame))
        frame.exitScope()

    def visitId(self, ast, ctxt):
        r = self.getSymbol(ast.name, ctxt.sym)
        frame = ctxt.frame
        if type(r.value) is CName:  # static (global)
            if ctxt.isLeft:
                return self.emit.emitPUTSTATIC(r.value.value + '/' + r.name,
                                               r.mtype, frame), r.mtype
            else:
                return self.emit.emitGETSTATIC(r.value.value + '/' + r.name,
                                               r.mtype, frame), r.mtype
        else:  # local
            if ctxt.isLeft:
                return self.emit.emitWRITEVAR(r.name, r.mtype, r.value,
                                              frame), r.mtype
            else:
                return self.emit.emitREADVAR(r.name, r.mtype, r.value,
                                             frame), r.mtype

    ################################################################
    ## Statements
    ################################################################

    def visitAssign(self, ast, ctxt):
        exp_code, exp_type = self.visit(
            ast.exp, Access(ctxt.frame, ctxt.sym, False, True))
        lhs_code, lhs_type = self.visit(
            ast.lhs, Access(ctxt.frame, ctxt.sym, True, True))
        if type(exp_type) is IntType and type(lhs_type) is FloatType:
            exp_code += self.emit.emitI2F(ctxt.frame)
            exp_type = FloatType()
        self.emit.printout(exp_code + lhs_code)

    def visitCall(self, ast, ctxt):
        frame = ctxt.frame
        r = self.getSymbol(ast.method.name, ctxt.sym)
        out_ = ""
        for x in zip(ast.param, r.mtype.partype):
            xstr, xtype = self.visit(x[0], Access(frame, ctxt.sym, False,
                                                  True))
            if type(xtype) is IntType and type(x[1]) is FloatType:
                xstr += self.emit.emitI2F(frame)
                xtype = FloatType()
            out_ += xstr
        out_ += self.emit.emitINVOKESTATIC(r.value.value + "/" + r.name,
                                           r.mtype, frame)
        return out_, r.mtype.rettype

    def visitCallStmt(self, ast, ctxt):
        self.emit.printout(self.visitCall(ast, ctxt)[0])

    def visitReturn(self, ast, ctxt):
        frame = ctxt.frame
        if not ast.expr is None:
            code, typ = self.visit(ast.expr,
                                   Access(frame, ctxt.sym, False, True))
            if type(typ) is IntType and type(frame.returnType) is FloatType:
                code += self.emit.emitI2F(frame)
                typ = FloatType()
            self.emit.printout(code)
        else:
            typ = VoidType()
        self.emit.printout(self.emit.emitRETURN(typ, frame))

    def visitWith(self, ast, ctxt):
        frame = ctxt.frame
        frame.enterScope(False)
        # with scope declarations
        curr_env = SubBody(ctxt.frame, ctxt.sym)
        for x in ast.decl:
            curr_env = self.visit(x, curr_env)
        # statements
        self.emit.printout(self.emit.emitLABEL(frame.getStartLabel(), frame))
        for x in ast.stmt:
            self.visit(x, curr_env)
        self.emit.printout(self.emit.emitLABEL(frame.getEndLabel(), frame))
        frame.exitScope()

    def visitIf(self, ast, ctxt):
        frame = ctxt.frame
        if len(ast.elseStmt) > 0:
            labelFalse = frame.getNewLabel()
            labelOut = frame.getNewLabel()
            # visit if expression
            exp_code = self.visit(ast.expr, Access(frame, ctxt.sym, False,
                                                   True))[0]
            self.emit.printout(exp_code +
                               self.emit.emitIFFALSE(labelFalse, frame))
            # visit true statements
            for x in ast.thenStmt:
                self.visit(x, ctxt)
            self.emit.printout(self.emit.emitGOTO(labelOut, frame))
            # visit false statements
            self.emit.printout(self.emit.emitLABEL(labelFalse, frame))
            for x in ast.elseStmt:
                self.visit(x, ctxt)
        else:
            labelOut = frame.getNewLabel()
            # visit if expression
            exp_code = self.visit(ast.expr, Access(frame, ctxt.sym, False,
                                                   True))[0]
            self.emit.printout(exp_code +
                               self.emit.emitIFFALSE(labelOut, frame))
            # visit true statements
            for x in ast.thenStmt:
                self.visit(x, ctxt)
        # labelOut
        self.emit.printout(self.emit.emitLABEL(labelOut, frame))

    def visitFor(self, ast, ctxt):
        frame = ctxt.frame
        frame.enterLoop()
        labelIn = frame.getNewLabel()
        labelContinue = frame.getContinueLabel()
        labelBreak = frame.getBreakLabel()
        # store first to i
        self.visit(Assign(ast.id, ast.expr1), ctxt)
        self.emit.printout(self.emit.emitLABEL(labelIn, frame))
        # compare i with expr2 to gen jump code
        rcode = self.visit(ast.id, Access(ctxt.frame, ctxt.sym, False,
                                          True))[0]
        expr2 = self.visit(ast.expr2, Access(ctxt.frame, ctxt.sym, False,
                                             True))[0]
        jumpcode = self.emit.emitIFICMPGT(
            labelBreak, frame) if ast.up else self.emit.emitIFICMPLT(
                labelBreak, frame)
        self.emit.printout(rcode + expr2 + jumpcode)
        # loop body
        for x in ast.loop:
            self.visit(x, ctxt)
        # continue label (i:=i+1)
        self.emit.printout(self.emit.emitLABEL(labelContinue, frame))
        self.visit(
            Assign(ast.id,
                   BinaryOp('+' if ast.up else '-', ast.id, IntLiteral(1))),
            ctxt)
        # goto loop and break label
        self.emit.printout(
            self.emit.emitGOTO(labelIn, frame) +
            self.emit.emitLABEL(labelBreak, frame))
        frame.exitLoop()

    def visitWhile(self, ast, ctxt):
        frame = ctxt.frame
        frame.enterLoop()
        labelContinue = frame.getContinueLabel()
        labelBreak = frame.getBreakLabel()
        # labelContinue and expression
        self.emit.printout(self.emit.emitLABEL(labelContinue, frame))
        exp_code = self.visit(ast.exp, Access(frame, ctxt.sym, False, True))[0]
        self.emit.printout(exp_code + self.emit.emitIFFALSE(labelBreak, frame))
        # statements
        for x in ast.sl:
            self.visit(x, ctxt)
        self.emit.printout(self.emit.emitGOTO(labelContinue, frame))
        # labelBreak
        self.emit.printout(self.emit.emitLABEL(labelBreak, frame))
        frame.exitLoop()

    def visitBreak(self, ast, ctxt):
        self.emit.printout(
            self.emit.emitGOTO(ctxt.frame.getBreakLabel(), ctxt.frame))

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

    ################################################################
    ## Expressions
    ################################################################

    def visitCallExpr(self, ast, ctxt):
        return self.visitCall(ast, ctxt)

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

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

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

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

    def visitBinaryOp(self, ast, ctxt):
        frame = ctxt.frame
        lcode, ltype = self.visit(ast.left, ctxt)
        rcode, rtype = self.visit(ast.right, ctxt)
        if FloatType in (type(ltype), type(rtype)) or ast.op == '/':
            if type(ltype) is IntType:
                lcode += self.emit.emitI2F(frame)
                ltype = FloatType()
            if type(rtype) is IntType:
                rcode += self.emit.emitI2F(frame)
                rtype = FloatType()

        if ast.op in ('+', '-'):
            return lcode + rcode + self.emit.emitADDOP(ast.op, ltype,
                                                       frame), ltype
        elif ast.op in ('*', '/'):
            return lcode + rcode + self.emit.emitMULOP(ast.op, ltype,
                                                       frame), ltype
        elif ast.op.lower() == 'div':
            return lcode + rcode + self.emit.emitDIV(frame), IntType()
        elif ast.op.lower() == 'mod':
            return lcode + rcode + self.emit.emitMOD(frame), IntType()
        elif ast.op.lower() == 'and':
            return lcode + rcode + self.emit.emitANDOP(frame), BoolType()
        elif ast.op.lower() == 'or':
            return lcode + rcode + self.emit.emitOROP(frame), BoolType()
        elif ast.op.lower() in ('>', '>=', '<', '<=', '<>', '='):
            return lcode + rcode + self.emit.emitREOP(ast.op, ltype,
                                                      frame), BoolType()
        elif ast.op in ('andthen', 'orelse'):
            return self.genShortCircuit(ast.op, lcode, rcode, frame)
        else:
            return

    def genShortCircuit(self, op, left, right, frame):
        res = ""
        labelBrk = frame.getNewLabel()
        labelOut = frame.getNewLabel()
        if op == 'andthen':
            res += left + self.emit.emitIFFALSE(labelBrk, frame)\
             + right + self.emit.emitIFFALSE(labelBrk, frame)\
             + self.emit.emitPUSHCONST("1", IntType(), frame)\
             + self.emit.emitGOTO(labelOut, frame)\
             + self.emit.emitLABEL(labelBrk, frame)\
             + self.emit.emitPUSHCONST("0", IntType(), frame)\
             + self.emit.emitLABEL(labelOut, frame)
        else:
            res += left + self.emit.emitIFTRUE(labelBrk, frame)\
             + right + self.emit.emitIFTRUE(labelBrk, frame)\
             + self.emit.emitPUSHCONST("0", IntType(), frame)\
             + self.emit.emitGOTO(labelOut, frame)\
             + self.emit.emitLABEL(labelBrk, frame)\
             + self.emit.emitPUSHCONST("1", IntType(), frame)\
             + self.emit.emitLABEL(labelOut, frame)
        return res, BoolType()

    def visitUnaryOp(self, ast, ctxt):
        code, typ = self.visit(ast.body, ctxt)
        if ast.op.lower() == 'not':
            return code + self.emit.emitNOT(ctxt.frame), BoolType()
        else:
            return code + self.emit.emitNEGOP(typ, ctxt.frame), typ

    def getSymbol(self, name, env):
        return self.lookup(name.lower(), env, lambda x: x.name.lower())
Example #28
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 = "MPClass"
        self.path = dir_
        self.emit = Emitter(self.path + "/" + self.className + ".j")
        self.current_function = None  #Symbol("",MType([],VoidType()),CName(self.className))

    def VarGlobal(self, ast, c):
        ctxt = c
        nameAtt = ast.variable.name
        typeAtt = ast.varType
        self.emit.printout(self.emit.emitATTRIBUTE(nameAtt, typeAtt, False,
                                                   ""))
        symbol = Symbol(nameAtt, typeAtt, CName(self.className))
        c.append(symbol)
        return c

    def FuncGlobal(self, ast, c):
        ctxt = c
        nameFunc = ast.name.name
        typeFunc = MType([x.varType for x in ast.param], ast.returnType)
        symbol = Symbol(nameFunc, typeFunc, CName(self.className))
        c.append(symbol)
        return c

    def visitProgram(self, ast, c):
        #ast: Program
        #c: Any
        self.emit.printout(
            self.emit.emitPROLOG(self.className, "java.lang.Object"))
        #get global env
        nenv = functools.reduce(
            lambda x, y: self.VarGlobal(y, x)
            if type(y) is VarDecl else self.FuncGlobal(y, x), ast.decl,
            self.env if self.env else [])

        e = SubBody(None, nenv)
        # visit all funtion in program
        lsFun = list(filter(lambda x: type(x) is FuncDecl, ast.decl))
        for x in lsFun:
            e = self.visit(x, e)
        # generate default constructor for MPClass
        self.genMETHOD(FuncDecl(Id("<init>"), list(), list(), list(), None), c,
                       Frame("<init>", VoidType))
        self.emit.emitEPILOG()
        return c

    def genMETHOD(self, consdecl, c, frame):
        #consdecl: FuncDecl
        #c: Any
        #frame: Frame
        isInit = consdecl.returnType is None
        isMain = consdecl.name.name == "main" and len(
            consdecl.param) == 0 and type(consdecl.returnType) is VoidType
        returnType = VoidType() if isInit else consdecl.returnType
        methodName = "<init>" if isInit else consdecl.name.name
        intype = [ArrayPointerType(StringType())
                  ] if isMain else [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 = c

        # 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))

        glSubBody = SubBody(frame, glenv)
        if (isMain is False) and (intype != []):
            glSubBody = functools.reduce(lambda a, b: self.visit(b, a),
                                         consdecl.param, glSubBody)

        body = consdecl.body

        current_env = functools.reduce(lambda a, b: self.visit(b, a),
                                       consdecl.local, glSubBody)

        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))

        #visit body of function
        list(map(lambda x: self.visit(x, current_env), body))

        # get and print Endlabel
        self.emit.printout(self.emit.emitLABEL(frame.getEndLabel(), frame))

        returnstmt = list(filter(lambda x: type(x) is Return, body))
        if type(returnType) is VoidType or not returnstmt:
            self.emit.printout(self.emit.emitRETURN(VoidType(), frame))
        self.emit.printout(self.emit.emitENDMETHOD(frame))
        frame.exitScope()

    def visitVarDecl(self, ast, c):
        # this function visit local variable(not global)
        #ast: VarDecl
        #c  : SubBody
        env = c.sym if type(c) is SubBody else []
        indx = c.frame.getNewIndex()
        self.emit.printout(
            self.emit.emitVAR(indx, ast.variable.name, ast.varType,
                              c.frame.getStartLabel(), c.frame.getEndLabel(),
                              c.frame))
        return SubBody(c.frame,
                       [Symbol(ast.variable.name, ast.varType, Index(indx))] +
                       env)

    def visitFuncDecl(self, ast, o):
        #ast: FuncDecl
        #o: Any
        subctxt = o
        frame = Frame(ast.name, ast.returnType)
        self.current_function = self.lookup(ast.name.name.lower(), subctxt.sym,
                                            lambda x: x.name.lower())
        self.genMETHOD(ast, subctxt.sym, frame)
        return o

    def Call(self, ast, c):
        ctxt = c
        frame = ctxt.frame
        nenv = ctxt.sym

        sym = self.lookup(ast.method.name.lower(), nenv,
                          lambda x: x.name.lower())

        cname = sym.value.value
        ctype = sym.mtype
        method_name = sym.name

        param_type = list(zip(ast.param, ctype.partype))
        ret = ""
        for x in param_type:
            str1, typ1 = self.visit(x[0], Access(frame, nenv, False, True))
            ret += str1 + self.emit.emitI2F(frame) if (type(typ1), type(
                x[1])) == (IntType, FloatType) else str1
        return (ret + self.emit.emitINVOKESTATIC(cname + "/" + method_name,
                                                 ctype, frame), ctype.rettype)

    def visitCallStmt(self, ast, c):
        self.emit.printout(self.Call(ast, c)[0])

    def visitCallExpr(self, ast, c):
        return self.Call(ast, c)

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

        resRight, typeRight = self.visit(ast.exp,
                                         Access(frame, env, False, False))
        resLeft, typeLeft = self.visit(ast.lhs, Access(frame, env, True,
                                                       False))
        str_I2f = self.emit.emitI2F(frame) if (
            type(typeLeft), type(typeRight)) == (FloatType, IntType) else ""

        self.emit.printout(resRight + str_I2f + resLeft)

    def visitIf(self, ast, c):
        frame = c.frame
        env = c.sym

        resExpr, typeExpr = self.visit(ast.expr,
                                       Access(frame, env, False, True))

        if len(ast.elseStmt) == 0:
            falseLabel = frame.getNewLabel()
            self.emit.printout(resExpr +
                               self.emit.emitIFFALSE(falseLabel, frame))
            list(map(lambda x: self.visit(x, c), ast.thenStmt))
            self.emit.printout(self.emit.emitLABEL(falseLabel, frame))
        else:
            falseLabel = frame.getNewLabel()
            trueLabel = frame.getNewLabel()
            # if false go to falselabel
            self.emit.printout(resExpr +
                               self.emit.emitIFFALSE(falseLabel, frame))
            # if true then do stmt
            list(map(lambda x: self.visit(x, c), ast.thenStmt))
            # then go to Truelable
            self.emit.printout(self.emit.emitGOTO(trueLabel, frame))

            self.emit.printout(self.emit.emitLABEL(falseLabel, frame))
            list(map(lambda x: self.visit(x, c), ast.elseStmt))
            self.emit.printout(self.emit.emitLABEL(trueLabel, frame))

    def visitWhile(self, ast, c):
        frame = c.frame
        env = c.sym

        frame.enterLoop()

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

        resExpr, typeExpr = self.visit(ast.exp, Access(frame, env, False,
                                                       False))

        self.emit.printout(resExpr)

        self.emit.printout(self.emit.emitIFFALSE(frame.getBreakLabel(), frame))

        list(map(lambda x: self.visit(x, c), ast.sl))

        self.emit.printout(self.emit.emitGOTO(frame.getContinueLabel(), frame))

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

        frame.exitLoop()

    def visitFor(self, ast, c):

        frame = c.frame
        env = c.sym
        beginLabel = frame.getNewLabel()
        frame.enterLoop()

        self.visit(Assign(ast.id, ast.expr1), SubBody(frame, env))

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

        op_ = ('<=', '+') if ast.up is True else ('>=', '-')

        self.emit.printout(
            self.visit(BinaryOp(op_[0], ast.id, ast.expr2),
                       SubBody(frame, env))[0])

        self.emit.printout(self.emit.emitIFFALSE(frame.getBreakLabel(), frame))

        list(map(lambda x: self.visit(x, SubBody(frame, env)), ast.loop))

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

        self.visit(Assign(ast.id, BinaryOp(op_[1], ast.id, IntLiteral(1))),
                   SubBody(frame, env))

        self.emit.printout(self.emit.emitGOTO(beginLabel, frame))

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

        frame.exitLoop()

    def visitReturn(self, ast, c):
        if ast.expr:
            resExpr, resType = self.visit(ast.expr,
                                          Access(c.frame, c.sym, False, True))
            typeFunc = self.current_function.mtype.rettype
            if (type(typeFunc), type(resType)) == (FloatType, IntType):
                self.emit.printout(resExpr + self.emit.emitI2F(c.frame) +
                                   self.emit.emitRETURN(FloatType(), c.frame))
            else:
                self.emit.printout(resExpr +
                                   self.emit.emitRETURN(resType, c.frame))

            # try:
            #     lable = c.frame.getBreakLabel()
            # except:
            #     lable = -1
            # if lable !=-1 :self.emit.printout(self.emit.emitGOTO(c.frame.getBreakLabel(), c.frame))

        else:
            self.emit.printout(self.emit.emitRETURN(VoidType(), c.frame))

    def visitBreak(self, ast, c):
        self.emit.printout(self.emit.emitGOTO(c.frame.getBreakLabel(),
                                              c.frame))

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

    def visitWith(self, ast, c):

        ctxt = c
        frame = ctxt.frame
        newEnv = ctxt.sym

        frame.enterScope(False)

        varEnv = functools.reduce(lambda a, b: self.visit(b, a), ast.decl,
                                  SubBody(frame, newEnv))

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

        list(map(lambda x: self.visit(x, varEnv), ast.stmt))

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

        frame.exitScope()

        return c

    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):
        #ast: FloatLiteral
        #o: Any
        ctxt = o
        frame = ctxt.frame
        return self.emit.emitPUSHFCONST(str(ast.value), frame), FloatType()

    def visitBinaryOp(self, ast, o):
        ctxt = o
        frame = ctxt.frame
        nenv = ctxt.sym
        op = ast.op.lower()

        # str1, type1 = self.visit(ast.left,o)

        # str2, type2 = self.visit(ast.right,o)
        str1, type1 = self.visit(ast.left, Access(frame, nenv, False, False))

        str2, type2 = self.visit(ast.right, Access(frame, nenv, False, False))

        retType = type1
        # convert type of factor and return type of expression
        if (type(type1), type(type2)) == (FloatType, IntType):
            str2 += self.emit.emitI2F(frame)
            retType = FloatType()
        elif (type(type1), type(type2)) == (IntType, FloatType):
            str1 += self.emit.emitI2F(frame)
            retType = FloatType()
        elif (type(type1), type(type2)) == (IntType, IntType):
            if ast.op is '/':
                str2 += self.emit.emitI2F(frame)
                str1 += self.emit.emitI2F(frame)
                retType = FloatType()
            else:
                retType = IntType()

        if op in ['+', '-']:
            return str1 + str2 + self.emit.emitADDOP(op, retType,
                                                     frame), retType
        elif op in ['*', '/']:
            return str1 + str2 + self.emit.emitMULOP(op, retType,
                                                     frame), retType
        elif op == 'mod':  # error ,let fix as soon as posible
            return str1 + str2 + self.emit.emitMOD(frame), IntType()
        elif op == 'div':
            return str1 + str2 + self.emit.emitDIV(frame), IntType()
        elif op in ['<', '>', '<=', '>=', '=', '<>']:
            return str1 + str2 + self.emit.emitREOP(op, retType,
                                                    frame), BoolType()

        elif op in ['and', 'or']:
            str_op = self.emit.emitANDOP(
                frame) if op == 'and' else self.emit.emitOROP(frame)
            return str1 + str2 + str_op, BoolType()
        elif op in ['andthen', 'orelse']:
            return self.emit.emit_ANDTHEN_ORELSE(op, str1, str2,
                                                 frame), BoolType()

    def visitUnaryOp(self, ast, o):
        ctxt = o
        frame = ctxt.frame
        env = ctxt.sym
        resExpr, typeExpr = self.visit(ast.body,
                                       Access(frame, env, False, True))
        if ast.op.lower() == "not":
            return resExpr + self.emit.emitNOT(typeExpr, frame), typeExpr
        elif ast.op.lower() == "-":
            return resExpr + self.emit.emitNEGOP(typeExpr, frame), typeExpr

    def visitId(self, ast, o):

        if type(o) is Access:
            sym = self.lookup(ast.name.lower(), o.sym,
                              lambda x: x.name.lower())
            code = ""
            nameId = sym.name
            if o.isLeft:
                if type(sym.value) is CName:
                    code = self.emit.emitPUTSTATIC(
                        sym.value.value + "." + nameId, sym.mtype, o.frame)
                elif type(sym.value) is Index:
                    code = self.emit.emitWRITEVAR(nameId, sym.mtype,
                                                  sym.value.value, o.frame)
            else:
                if type(sym.value) is CName:
                    code = self.emit.emitGETSTATIC(
                        sym.value.value + "." + nameId, sym.mtype, o.frame)
                elif type(sym.value) is Index:
                    code = self.emit.emitREADVAR(nameId.lower(), sym.mtype,
                                                 sym.value.value, o.frame)

            return code, sym.mtype
        # else:
        #     sym  = self.lookup(ast.name.lower(), o.sym, lambda x: x.name.lower())
        #     return "",sym.mtype

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

    def visitBooleanLiteral(self, ast, o):
        ctxt = o
        frame = ctxt.frame
        return self.emit.emitPUSHICONST(str(ast.value).lower(),
                                        frame), BoolType()
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 = "MPClass"
        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"))

        glenv = [self.visit(x, 0) for x in ast.decl]
        e = SubBody(None, self.env + glenv)
        for x in ast.decl:
            self.visit(x, e)

        # generate default constructor
        self.genMETHOD(FuncDecl(Id("<init>"), list(), list(), list(), None), c,
                       Frame("<init>", VoidType))
        self.genMETHOD(
            FuncDecl(Id("<clinit>"), list(), list(), list(), VoidType()),
            e.sym, 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
        isMain = consdecl.name.name == "main" and len(
            consdecl.param) == 0 and type(consdecl.returnType) is VoidType
        returnType = VoidType() if isInit else consdecl.returnType
        methodName = "<init>" if isInit else consdecl.name.name
        ls_param = [self.visit(x, 1) for x in consdecl.param]
        intype = [ArrayPointerType(StringType())] if isMain else ls_param
        mtype = MType(intype, returnType)

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

        frame.enterScope(True)
        # 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))

        # glenv = o
        glenv = [] if o is None else o
        if consdecl.name.name == "<clinit>":
            for x in glenv:
                if type(x.mtype) is ArrayType:
                    self.emit.printout(
                        self.visit(
                            IntLiteral(
                                int(str(x.mtype.upper)) -
                                int(str(x.mtype.lower)) + 1),
                            SubBody(frame, glenv))[0])
                    self.emit.printout(
                        self.emit.emitArray(x.mtype, x.value, frame, x.name))
            self.emit.printout(self.emit.emitRETURN(VoidType(), frame))
            self.emit.printout(self.emit.emitENDMETHOD(frame))
            frame.exitScope()
            return

        lcenv = []
        e = SubBody(frame, lcenv)
        penv = []
        p = SubBody(frame, penv)
        for x in consdecl.param:
            p = self.visit(x, p)
        for x in consdecl.local:
            e = self.visit(x, e)
        body = consdecl.body
        self.emit.printout(self.emit.emitLABEL(frame.getStartLabel(), frame))
        for x in e.sym:
            if type(x.mtype) is ArrayType:
                self.emit.printout(
                    self.visit(
                        IntLiteral(
                            int(str(x.mtype.upper)) - int(str(x.mtype.lower)) +
                            1), e)[0])
                self.emit.printout(
                    self.emit.emitArray(x.mtype, x.value, frame, x.name))

        e.sym = e.sym + p.sym + glenv
        # 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))
        list(map(lambda x: self.visit(x, e), body))

        self.emit.printout(self.emit.emitLABEL(frame.getEndLabel(), frame))
        self.emit.printout(self.emit.emitRETURN(VoidType(), frame))
        self.emit.printout(self.emit.emitENDMETHOD(frame))
        frame.exitScope()

    def visitVarDecl(self, ast, o):
        # def emitATTRIBUTE(self, lexeme, in_, isFinal, value):
        #lexeme: String
        #in_: Type
        #isFinal: Boolean
        #value: String
        if o == 0:
            self.emit.printout(
                self.emit.emitATTRIBUTE(ast.variable.name, ast.varType, False,
                                        None))
            return Symbol(ast.variable.name, ast.varType,
                          CName(self.className))
        if o == 1:
            return ast.varType
        subctxt = o
        frame = o.frame
        env = o.sym
        if frame is None:

            return SubBody(None, [
                Symbol(ast.variable.name, ast.varType, CName(self.className))
            ] + subctxt.sym)
        else:
            idx = frame.getNewIndex()
            self.emit.printout(
                self.emit.emitVAR(idx, ast.variable.name, ast.varType,
                                  frame.getStartLabel(), frame.getEndLabel(),
                                  frame))
            return SubBody(
                frame,
                [Symbol(ast.variable.name, ast.varType, Index(idx))] + env)

    def visitFuncDecl(self, ast, o):
        #ast: FuncDecl
        #o: Any

        if o == 0:
            ls_param = [self.visit(x, 1) for x in ast.param]
            return Symbol(ast.name.name, MType(ls_param, ast.returnType),
                          CName(self.className))

        subctxt = o
        frame = Frame(ast.name, ast.returnType)

        self.genMETHOD(ast, subctxt.sym, frame)
        return SubBody(None, [
            Symbol(ast.name.name, MType(list(), ast.returnType),
                   CName(self.className))
        ] + subctxt.sym)

    def visitIf(self, ast, o):
        #expr:Expr
        #thenStmt:list(Stmt)
        #elseStmt:list(Stmt)
        ctxt = o
        frame = ctxt.frame
        nenv = ctxt.sym
        exp, exptype = self.visit(ast.expr, Access(frame, nenv, False, True))
        label1 = frame.getNewLabel()
        self.emit.printout(exp)
        self.emit.printout(self.emit.emitIFFALSE(label1, frame))
        list(map(lambda x: self.visit(x, o), ast.thenStmt))

        if ast.elseStmt != []:
            label2 = frame.getNewLabel()
            self.emit.printout(self.emit.emitGOTO(label2, frame))
        self.emit.printout(self.emit.emitLABEL(label1, frame))
        if ast.elseStmt != []:
            list(map(lambda x: self.visit(x, o), ast.elseStmt))
            self.emit.printout(self.emit.emitLABEL(label2, frame))

    def visitWhile(self, ast, o):
        ctxt = o
        frame = ctxt.frame
        nenv = ctxt.sym
        exp, exptype = self.visit(ast.exp, Access(frame, nenv, False, True))
        frame.enterLoop()
        label1 = frame.getContinueLabel()
        label2 = frame.getBreakLabel()
        self.emit.printout(self.emit.emitLABEL(label1, frame))
        self.emit.printout(exp)
        self.emit.printout(self.emit.emitIFFALSE(label2, frame))
        list(map(lambda x: self.visit(x, o), ast.sl))
        self.emit.printout(self.emit.emitGOTO(label1, frame))
        self.emit.printout(self.emit.emitLABEL(label2, frame))
        frame.exitLoop()

    def visitFor(self, ast, o):
        #id:Id
        #expr1,expr2:Expr
        #loop:list(Stmt)
        #up:Boolean #True => increase; False => decrease
        ctxt = o
        frame = ctxt.frame
        nenv = ctxt.sym
        op = '<=' if ast.up else '>='
        op2 = '+' if ast.up else '-'
        op3 = '-' if ast.up else '+'
        frame.enterLoop()
        label1 = frame.getContinueLabel()
        label2 = frame.getBreakLabel()
        self.visit(Assign(ast.id, ast.expr1), o)
        self.visit(Assign(ast.id, BinaryOp(op3, ast.id, IntLiteral(1))), o)
        self.emit.printout(self.emit.emitLABEL(label1, frame))
        self.visit(Assign(ast.id, BinaryOp(op2, ast.id, IntLiteral(1))), o)
        self.emit.printout(
            self.visit(BinaryOp(op, ast.id, ast.expr2),
                       Access(frame, nenv, False, True))[0])

        self.emit.printout(self.emit.emitIFFALSE(label2, frame))
        list(map(lambda x: self.visit(x, o), ast.loop))
        self.emit.printout(self.emit.emitGOTO(label1, frame))
        self.emit.printout(self.emit.emitLABEL(label2, frame))
        frame.exitLoop()

    def visitWith(self, ast, o):
        ctxt = o
        frame = o.frame
        #decl:list(VarDecl)
        #stmt:list(Stmt)
        env = o.sym
        frame.enterScope(False)
        e = SubBody(frame, [])
        for x in ast.decl:
            e = self.visit(x, e)
        env = e.sym + env
        for y in e.sym:
            if type(y.mtype) is ArrayType:
                self.emit.printout(
                    self.visit(
                        IntLiteral(
                            int(str(y.mtype.upper)) - int(str(y.mtype.lower)) +
                            1), SubBody(frame, env))[0])
                self.emit.printout(
                    self.emit.emitArray(y.mtype, y.value, frame, y.name))

        self.emit.printout(self.emit.emitLABEL(frame.getStartLabel(), frame))
        list(map(lambda x: self.visit(x, SubBody(frame, env)), ast.stmt))
        self.emit.printout(self.emit.emitLABEL(frame.getEndLabel(), frame))
        frame.exitScope()

    def visitCallStmt(self, ast, o):
        #ast: CallStmt
        #o: Any
        ctxt = o
        frame = ctxt.frame
        nenv = ctxt.sym
        sym = self.lookup(ast.method.name.lower(), reversed(nenv),
                          lambda x: x.name.lower())
        cname = sym.value.value
        ctype = sym.mtype
        ctype2 = MType([], ClassType('java/lang/Object'))
        in_ = ("", list())
        ls = zip(ast.param, ctype.partype)
        for x, y in ls:
            str1, typ1 = self.visit(x, Access(frame, nenv, False, True))
            if type(typ1) is ArrayType:
                bonus = self.emit.emitINVOKEVIRTUAL(
                    self.emit.getJVMType(typ1) + '/clone', ctype2, frame
                ) + '\t' + 'checkcast ' + self.emit.getJVMType(typ1) + '\n'
                str1 = str1 + bonus

            if type(typ1) is IntType and type(y) is FloatType:
                bonus2 = self.emit.emitI2F(frame)
                str1 = str1 + bonus2
            if type(typ1) is ArrayPointerType and type(
                    typ1.eleType) is IntType and type(y) is FloatType:
                bonus2 = self.emit.emitI2F(frame)
                str1 = str1 + bonus2
            in_ = (in_[0] + str1, in_[1] + [typ1])
        self.emit.printout(in_[0])
        self.emit.printout(
            self.emit.emitINVOKESTATIC(cname + "/" + sym.name, ctype, frame))

    def visitBinaryOp(self, ast, o):
        #frame: Frame
        #sym: List[Symbol]
        #isLeft: Boolean
        # #isFirst: Boolean
        #  emitREOP emitRELOP
        ctxt = o
        frame = ctxt.frame
        leftcode, lefttype = self.visit(ast.left, o)
        rightcode, righttype = self.visit(ast.right, o)
        leftcode2 = leftcode + self.emit.emitI2F(frame) if (
            type(lefttype) is IntType or
            (type(lefttype) is ArrayPointerType
             and type(lefttype.eleType) is IntType)) else leftcode
        rightcode2 = rightcode + self.emit.emitI2F(frame) if (
            type(righttype) is IntType or
            (type(righttype) is ArrayPointerType
             and type(righttype.eleType) is IntType)) else rightcode
        typ1 = lefttype.eleType if type(
            lefttype) is ArrayPointerType else lefttype
        typ2 = righttype.eleType if type(
            righttype) is ArrayPointerType else righttype
        if type(typ1) == type(typ2):
            if ast.op in ['+', '-']:
                return leftcode + rightcode + self.emit.emitADDOP(
                    ast.op, typ1, frame), lefttype
            elif ast.op is '*':
                return leftcode + rightcode + self.emit.emitMULOP(
                    ast.op, typ1, frame), lefttype
            elif ast.op is '/':
                return leftcode2 + rightcode2 + self.emit.emitMULOP(
                    ast.op, FloatType(), frame), FloatType()
            elif ast.op.lower() == 'div':
                return leftcode + rightcode + self.emit.emitDIV(frame), typ1
            elif ast.op.lower() == 'mod':
                return leftcode + rightcode + self.emit.emitMOD(frame), typ1
            elif ast.op == 'and':
                return leftcode + rightcode + self.emit.emitANDOP(
                    frame), BoolType()
            elif ast.op == 'or':
                return leftcode + rightcode + self.emit.emitOROP(
                    frame), BoolType()
            elif ast.op in ['<', '>', '<=', '>=', '=', '<>']:
                return leftcode + rightcode + self.emit.emitREOP(
                    ast.op, typ1, frame), BoolType()
            elif ast.op.lower() == 'andthen':
                label1 = frame.getNewLabel()
                code = leftcode + self.emit.emitDUP(frame)
                code = code + self.emit.emitIFFALSE(label1, frame)
                code = code + rightcode + self.emit.emitANDOP(frame)
                code = code + self.emit.emitLABEL(label1, frame)
                return code, BoolType()
            elif ast.op.lower() == 'orelse':
                label1 = frame.getNewLabel()
                code = leftcode + self.emit.emitDUP(frame)
                code = code + self.emit.emitIFTRUE(label1, frame)
                code = code + rightcode + self.emit.emitOROP(frame)
                code = code + self.emit.emitLABEL(label1, frame)
                return code, BoolType()
        else:
            if ast.op in ['+', '-']:
                return leftcode2 + rightcode2 + self.emit.emitADDOP(
                    ast.op, FloatType(), frame), FloatType()
            elif ast.op in ['*', '/']:
                return leftcode2 + rightcode2 + self.emit.emitMULOP(
                    ast.op, FloatType(), frame), FloatType()
            elif ast.op.lower() in ['<', '>', '<=', '>=', '=', '<>']:
                return leftcode2 + rightcode2 + self.emit.emitREOP(
                    ast.op, FloatType(), frame), BoolType()

    def visitCallExpr(self, ast, o):
        ctxt = o
        frame = ctxt.frame
        nenv = ctxt.sym
        sym = self.lookup(ast.method.name.lower(), reversed(nenv),
                          lambda x: x.name.lower())
        cname = sym.value.value
        ctype = sym.mtype
        ctype2 = MType([], ClassType('java/lang/Object'))
        in_ = ("", list())
        ls = zip(ast.param, ctype.partype)
        for x, y in ls:
            str1, typ1 = self.visit(x, Access(frame, nenv, False, True))
            if type(typ1) is ArrayType:
                bonus = self.emit.emitINVOKEVIRTUAL(
                    self.emit.getJVMType(typ1) + '/clone', ctype2, frame
                ) + '\t' + 'checkcast ' + self.emit.getJVMType(typ1) + '\n'
                str1 = str1 + bonus
            if type(typ1) is IntType and type(y) is FloatType:
                bonus2 = self.emit.emitI2F(frame)
                str1 = str1 + bonus2
            if type(typ1) is ArrayPointerType and type(
                    typ1.eleType) is IntType and type(y) is FloatType:
                bonus2 = self.emit.emitI2F(frame)
                str1 = str1 + bonus2
            in_ = (in_[0] + str1, in_[1] + [typ1])
        return in_[0] + self.emit.emitINVOKESTATIC(cname + "/" + sym.name,
                                                   ctype, frame), ctype.rettype

    def visitUnaryOp(self, ast, o):
        ctxt = o
        frame = ctxt.frame
        bodyCode, bodyType = self.visit(ast.body, o)
        _type = bodyType.eleType if type(
            bodyType) is ArrayPointerType else bodyType
        if ast.op.lower() == 'not':
            return bodyCode + self.emit.emitNOT(StringType(),
                                                frame), BoolType()
        else:
            return bodyCode + self.emit.emitNEGOP(_type, frame), _type

    def visitAssign(self, ast, o):
        frame = o.frame
        frame.push()
        frame.push()
        expCode, expType = self.visit(ast.exp, Access(frame, o.sym, False,
                                                      True))
        lhsCode, lhsType = self.visit(ast.lhs, Access(frame, o.sym, True,
                                                      True))
        if isinstance(lhsType, FloatType) and isinstance(expType, IntType):
            expCode = expCode + self.emit.emitI2F(frame)
        if isinstance(ast.lhs, ArrayCell):
            self.emit.printout(
                lhsCode + expCode +
                self.visit(ast.lhs, Access(frame, o.sym, True, False)))
        else:
            self.emit.printout(expCode + lhsCode)
        frame.pop()
        frame.pop()

    def visitId(self, ast, o):
        frame = o.frame
        sym = self.lookup(ast.name.lower(), o.sym, lambda x: x.name.lower())
        _type = sym.mtype if not isinstance(
            sym.mtype, ArrayType) else ArrayPointerType(sym.mtype.eleType)
        if not isinstance(sym.value, Index):
            if isinstance(o, Access) and o.isLeft is True:
                return self.emit.emitPUTSTATIC(
                    sym.value.value + "." + sym.name, _type, frame), sym.mtype
            else:
                return self.emit.emitGETSTATIC(
                    sym.value.value + "." + sym.name, _type, frame), sym.mtype
        else:
            if isinstance(o, Access) and o.isLeft is True:
                return self.emit.emitWRITEVAR(sym.name, _type, sym.value.value,
                                              frame), sym.mtype
            else:
                return self.emit.emitREADVAR(sym.name, _type, sym.value.value,
                                             frame), sym.mtype

    def visitArrayCell(self, ast, o):
        #arr:Expr
        #idx:Expr

        frame = o.frame
        arr, arrtype = self.visit(ast.arr, Access(frame, o.sym, False, True))
        idx, idxtype = self.visit(ast.idx, Access(frame, o.sym, False, True))
        idx = idx + self.visit(IntLiteral(
            arrtype.lower), o)[0] + self.emit.emitADDOP("-", IntType(
            ), frame) if arrtype.lower >= 0 else idx + self.visit(
                IntLiteral(-arrtype.lower), o)[0] + self.emit.emitADDOP(
                    "+", IntType(), frame)
        if isinstance(o, Access) and o.isLeft is True and o.isFirst is True:
            return arr + idx, arrtype.eleType
        elif isinstance(o, Access) and o.isLeft is True and o.isFirst is False:
            return self.emit.emitASTORE(arrtype.eleType, frame)
        else:
            return arr + idx + self.emit.emitALOAD(arrtype.eleType,
                                                   frame), arrtype.eleType

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

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

    def visitReturn(self, ast, o):
        ctxt = o
        frame = o.frame
        if ast.expr is None:
            self.emit.printout(self.emit.emitRETURN(VoidType(), frame))
        else:
            expcode, exptype = self.visit(ast.expr,
                                          Access(frame, o.sym, False, True))

            if type(frame.returnType) is FloatType and type(
                    exptype) is IntType:
                self.emit.printout(expcode + self.emit.emitI2F(frame) +
                                   self.emit.emitRETURN(FloatType(), frame))
            else:
                self.emit.printout(expcode +
                                   self.emit.emitRETURN(exptype, frame))

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

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

    def visitIntLiteral(self, ast, o):
        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()
Example #30
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 = "MPClass"
        self.path = dir_
        self.emit = Emitter(self.path + "/" + self.className + ".j")

    def visitProgram(self, ast, c):
        #ast: Program
        #c: Any
        stDecl = self.env
        self.emit.printout(self.emit.emitPROLOG(self.className, "java.lang.Object"))
        # e = SubBody(None, self.env)
        # for x in ast.decl:
        #     e = self.visit(x, e)
        for x in ast.decl:
            if type(x) is FuncDecl:
                paramTypeList = [y.varType for y in x.param]
                stDecl = [Symbol(x.name.name.lower(),MType(paramTypeList,x.returnType),CName(self.className))] + stDecl
            else:
                

                newSym = self.visit(x,SubBody(Frame("<clinit>",x.varType),list(),isGlobal=True))
                stDecl = [newSym] + stDecl
        e = SubBody(None, stDecl)
        [self.visit(x, e) for x in ast.decl if type(x) is FuncDecl]
        # generate default constructor
        self.genMETHOD(FuncDecl(Id("<init>"), list(), list(), list(),None), c, Frame("<init>", VoidType))
        self.emit.emitEPILOG()
        return c

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

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

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

        frame.enterScope(True)

        glenv = o

        # Generate code for parameter declarations
        # frameInit = Frame("<clinit>",VoidType)
        # self.emit.printout(self.emit.emitMETHOD("<clinit>", MType(list(),VoidType()), False, frameInit))
        # frameInit.enterScope(True)
        # self.emit.printout(self.emit.emitVAR(frameInit.getNewIndex(), "this", ClassType(self.className), frameInit.getStartLabel(), frameInit.getEndLabel(), frameInit))
        # self.emit.printout(self.emit.emitLABEL(frameInit.getStartLabel(), frameInit))
        # self.emit.printout(self.emit.emitREADVAR("this", ClassType(self.className), 0, frameInit))
        # self.emit.printout(self.emit.emitINVOKESPECIAL(frameInit))
        
        # self.emit.printout(self.emit.emitREADVAR("this",ClassType(self.className), 0, frameInit))
        # self.emit.printout(self.emit.emitPUSHICONST(arraySize,frame))
        # self.emit.printout(self.emit.jvm.emitNEWARRAY(self.emit.getFullType(eleType)))
        # self.emit.printout(self.emit.emitPUTSTATIC(self.className + "." + o[1],ast,frame))
        
        
        # self.emit.printout(self.emit.emitLABEL(frameInit.getEndLabel(), frameInit))
        # self.emit.printout(self.emit.emitRETURN(VoidType(), frameInit))
        # self.emit.printout(self.emit.emitENDMETHOD(frameInit))
        # frameInit.exitScope()

        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))

        local_sub = SubBody(frame,glenv)
        for x in consdecl.param + consdecl.local:
            local_sub = self.visit(x,local_sub)

        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))
        list(map(lambda x: self.visit(x, SubBody(frame, local_sub.sym)), body))

        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();
######################### DECLARATION ############################
    def visitFuncDecl(self, ast, o):
        #ast: FuncDecl
        #o: Any

        subctxt = o
        frame = Frame(ast.name, ast.returnType)
        self.genMETHOD(ast, subctxt.sym, frame)
        return SubBody(None, [Symbol(ast.name, MType(list(), ast.returnType), CName(self.className))] + subctxt.sym)

    def visitVarDecl(self, ast, o):
        varName = ast.variable.name.lower()
        varType = ast.varType
        frame = o.frame
        subctxt = o

        if o.isGlobal:
            self.emit.printout(self.emit.emitATTRIBUTE(varName,varType,False,""))
            if type(varType) == ArrayType:
                self.visit(varType,(subctxt,varName))
            return Symbol(varName, varType)
        else:
            index = frame.getNewIndex()
            self.emit.printout(self.emit.emitVAR(index,varName,varType,frame.getStartLabel(),frame.getEndLabel(),frame))
            return SubBody(frame,[Symbol(varName,varType,Index(index))] + subctxt.sym)


################ EXPRESSION ##########################
    
    def visitBinaryOp(self, ast, o):
        leftOpr = ast.left
        rightOpr = ast.right
        op = ast.op.lower()
        accessCtxt = o
        frame = accessCtxt.frame
        leftCode, leftType = self.visit(leftOpr, accessCtxt)
        rightCode, rightType = self.visit(rightOpr, accessCtxt)
        mtype = leftType
        if type(leftType) == type(rightType):
            if type(leftType) == IntType:
                if op == '/':
                    mtype = FloatType() 
                    leftCode = leftCode + self.emit.emitI2F(frame)
                    rightCode = rightCode + self.emit.emitI2F(frame)
        else:
            mtype = FloatType()
            if type(leftType) == IntType:
                leftCode = leftCode + self.emit.emitI2F(frame)
            else: 
                rightCode = rightCode + self.emit.emitI2F(frame)
        
        if op in ['+','-']:
            return leftCode + rightCode + self.emit.emitADDOP(op,mtype,frame), mtype
        if op in ['*','/']:
            return leftCode + rightCode + self.emit.emitMULOP(op,mtype,frame), mtype
        if op == 'div':
            return leftCode + rightCode + self.emit.emitDIV(frame), mtype
        if op == 'mod':
            return leftCode + rightCode + self.emit.emitMOD(frame), mtype
        if op in ['=','>','<','>=','<=','<>']:
            return leftCode + rightCode + self.emit.emitREOP(op,mtype,frame), BoolType()
        if op == 'or': return leftCode + rightCode + self.emit.emitOROP(frame), BoolType()
        if op == 'and': return leftCode + rightCode + self.emit.emitANDOP(frame), BoolType()
        if op == 'orelse': return self.emit.emitORELSE(frame, leftCode, rightCode), BoolType()
        if op == 'andthen': return self.emit.emitANDTHEN(frame, leftCode, rightCode), BoolType()

    def visitUnaryOp(self, ast, o):
        op = ast.op.lower()
        accessCtxt = o
        frame = accessCtxt.frame
        operandCode, operandType = self.visit(ast.body,accessCtxt)

        if op == 'not':
            return operandCode + self.emit.emitNOT(operandType,frame), operandType
        else:
            return operandCode + self.emit.emitNEGOP(operandType,frame), operandType

    # def visitId(self,ast,o):
    #     accessCtxt = o
    #     frame = accessCtxt.frame
    #     symList = accessCtxt.sym
    #     isLeft = accessCtxt.isLeft
    #     isFirst = accessCtxt.isFirst

    #     sym = self.lookup(ast.name.lower(),symList, lambda x: x.name.lower())
    #     if isLeft:
    #         return sym.name, sym.mtype, sym.value
    #     if sym.value is None: #global var
    #         return self.emit.emitGETSTATIC(self.className + "." + sym.name,sym.mtype, frame), sym.mtype
    #     return self.emit.emitREADVAR(sym.name, sym.mtype, sym.value.value, frame), sym.mtype
        


    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):
        #ast: FloatLiteral
        #o: Any

        ctxt = o
        frame = ctxt.frame
        return self.emit.emitPUSHFCONST(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(ast.value,StringType(),frame), StringType()
        
    
############## STATEMENT #######################
    def visitCallStmt(self, ast, o):
        #ast: CallStmt
        #o: Any

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

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

    # def visitAssign(self,ast,o):
    #     subBodyCtxt = o
    #     frame = subBodyCtxt.frame
    #     sym = subBodyCtxt.sym 
    #     writevar = ""
    #     putstatic = ""

    #     result = list()
    #     lhsName, lhsType, lhsValue = self.visit(ast.lhs,Access(frame, sym, True, True))
    #     rhsCode, rhsType = self.visit(ast.exp,Access(frame, sym, False, True))
    #     if (type(rhsType), type(lhsType)) == (IntType,FloatType):
    #         rhsCode = rhsCode + self.emit.emitI2F(frame)
    #     self.emit.printout(rhsCode)
    #     result.append(rhsCode)

    #     if lhsValue:
    #         writevar = self.emit.emitWRITEVAR(lhsName,lhsType,lhsValue.value,frame)
    #         result.append(writevar)
    #         self.emit.printout(writevar)
    #     else:
    #         result.append(putstatic)
    #         putstatic = self.emit.emitPUTSTATIC(self.className + "." + lhsName,lhsType,frame)
    #         self.emit.printout(putstatic)
    #     return ''.join(result)


    def visitIf(self,ast,o):
        subBodyCtxt = o
        frame = subBodyCtxt.frame 
        sym = subBodyCtxt.sym

        expr = ast.expr
        label1 = frame.getNewLabel()
        label2 = frame.getNewLabel()

        
        self.emit.printout(self.visit(expr,Access(frame,sym,False,True))[0])
        #self.emit.printout(self.emit.emitLABEL(label1,frame))
        #self.emit.printout(self.emit.emitLABEL(label2,frame))
        self.emit.printout(self.emit.emitIFFALSE(label1,frame))

        [self.visit(stmt,subBodyCtxt) for stmt in ast.thenStmt]
        self.emit.printout(self.emit.emitGOTO(label2,frame))
        self.emit.printout(self.emit.emitLABEL(label1,frame))
        [self.visit(stmt,subBodyCtxt) for stmt in ast.elseStmt]
        self.emit.printout(self.emit.emitLABEL(label2,frame))

    def visitWhile(self,ast,o):
        subBodyCtxt = o
        frame = subBodyCtxt.frame 
        sym = subBodyCtxt.sym
        expr = ast.exp

        frame.enterLoop()
        continueLabel = frame.getContinueLabel()
        breakLabel = frame.getBreakLabel()
        self.emit.printout(self.emit.emitLABEL(continueLabel,frame))
        self.emit.printout(self.visit(expr,Access(frame,sym,False,True))[0])
        self.emit.printout(self.emit.emitIFFALSE(breakLabel,frame))
        [self.visit(stmt,subBodyCtxt) for stmt in ast.sl]
        self.emit.printout(self.emit.emitGOTO(continueLabel,frame))
        self.emit.printout(self.emit.emitLABEL(breakLabel,frame))

    def visitFor(self,ast,o):
        subBodyCtxt = o
        frame = subBodyCtxt.frame 
        sym = subBodyCtxt.sym
        
        idCode = self.visit(ast.id,Access(frame,sym,False,True))
        frame.enterLoop()
        continueLabel = frame.getContinueLabel()
        breakLabel = frame.getBreakLabel()
        
        assignAST = Assign(ast.id,ast.expr1)
        assignIncrAST = Assign(ast.id,BinaryOp('+',ast.id,IntLiteral(1)))
        assignDecrAST = Assign(ast.id,BinaryOp('-',ast.id,IntLiteral(1)))
        toExprAST = BinaryOp('<=',ast.id,ast.expr2)
        downtoExprAST = BinaryOp('>=',ast.id,ast.expr2)
        
        self.visit(assignAST,subBodyCtxt)
        self.emit.printout(self.emit.emitLABEL(continueLabel,frame))
        if ast.up:
            self.emit.printout(self.visit(toExprAST,Access(frame,sym,False,True))[0])
        else:
            self.emit.printout(self.visit(downtoExprAST,Access(frame,sym,False,True))[0])
        self.emit.printout(self.emit.emitIFFALSE(breakLabel,frame))
        [self.visit(stmt,subBodyCtxt) for stmt in ast.loop]
        if ast.up:
            self.visit(assignIncrAST,subBodyCtxt)
        else: 
            self.visit(assignDecrAST,subBodyCtxt)
        self.emit.printout(self.emit.emitGOTO(continueLabel,frame))
        self.emit.printout(self.emit.emitLABEL(breakLabel,frame))
        #compare id vs expr2



    def visitBreak(self,ast,o):
        subBodyCtxt = o
        frame = subBodyCtxt.frame

        breakLabel = frame.getBreakLabel()
        self.emit.printout(self.emit.emitGOTO(breakLabel,frame))

    def visitContinue(self,ast,o):
        subBodyCtxt = o
        frame = subBodyCtxt.frame

        continueLabel = frame.getContinueLabel()
        self.emit.printout(self.emit.emitGOTO(continueLabel,frame))

    def visitWith(self,ast,o):
        subBodyCtxt = SubBody(o.frame,o.sym,isGlobal=False)
        
        for vardecl in ast.decl: ####### update symbol list
            subBodyCtxt = self.visit(vardecl,subBodyCtxt)

        [self.visit(stmt,subBodyCtxt) for stmt in ast.stmt]

    def visitReturn(self,ast,o):
        subBodyCtxt = o
        frame = subBodyCtxt.frame
        sym = subBodyCtxt.sym
        expr = None
        if ast.expr:
            expr = self.visit(ast.expr,Access(frame,sym,False,True))
            self.emit.printout(expr[0])
            self.emit.printout(self.emit.emitRETURN(expr[1],frame))
        else:
            self.emit.printout(self.emit.emitRETURN(VoidType(),frame))
    def visitCallExpr(self,ast,o):
        #ast: CallStmt
        #o: Any
        result = list()
        ctxt = o
        frame = ctxt.frame
        nenv = ctxt.sym
        sym = self.lookup(ast.method.name.lower(), nenv, lambda x: x.name)
        cname = sym.value.value
    
        ctype = sym.mtype

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


    def visitArrayType(self,ast,o):
        lower = ast.lower
        upper = ast.upper
        arraySize = upper - lower + 1
        eleType = ast.eleType
        frame = o[0].frame
        #label1 = frame.getNewLabel()
        #label2 = frame.getNewLabel()

        #if type(x.varType) == ArrayType:
        frameInit = Frame("<clinit>",VoidType)
        self.emit.printout(self.emit.emitMETHOD("<clinit>", MType(list(),VoidType()), False, frameInit))
        
        #frameInit.enterScope(True)
        #self.emit.printout(self.emit.emitVAR(frameInit.getNewIndex(), "this", ClassType(self.className), frameInit.getStartLabel(), frameInit.getEndLabel(), frameInit))
        #self.emit.printout(self.emit.emitLABEL(frameInit.getStartLabel(), frameInit))
        #self.emit.printout(self.emit.emitREADVAR("this", ClassType(self.className), 0, frameInit))
        #self.emit.printout(self.emit.emitINVOKESPECIAL(frameInit))
        
        #self.emit.printout(self.emit.emitREADVAR("this",ClassType(self.className), 0, frameInit))
        self.emit.printout(self.emit.emitPUSHICONST(arraySize,frameInit))
        self.emit.printout(self.emit.jvm.emitNEWARRAY(self.emit.getFullType(eleType)))
        self.emit.printout(self.emit.emitPUTSTATIC(self.className + "." + o[1],ast,frameInit))
        
        
        #self.emit.printout(self.emit.emitLABEL(frameInit.getEndLabel(), frameInit))
        self.emit.printout(self.emit.emitRETURN(VoidType(), frameInit))
        self.emit.printout(self.emit.emitENDMETHOD(frameInit))
        #frameInit.exitScope();

        
        #self.emit.printout(self.emit.emitLABEL(label1,frame))
        
        #self.emit.printout(self.emit.emitLABEL(label2,frame))
    def visitId(self,ast,o):
        accessCtxt = o
        frame = accessCtxt.frame
        symList = accessCtxt.sym
        isLeft = accessCtxt.isLeft
        isFirst = accessCtxt.isFirst

        sym = self.lookup(ast.name.lower(),symList, lambda x: x.name)
        if isLeft:
            return sym.name.lower(), sym.mtype, sym.value
        if sym.value is None: #global var
            return self.emit.emitGETSTATIC(self.className + "." + sym.name.lower(),sym.mtype, frame), sym.mtype
        return self.emit.emitREADVAR(sym.name.lower(), sym.mtype, sym.value.value, frame), sym.mtype
    def visitAssign(self,ast,o):
        subBodyCtxt = o
        frame = subBodyCtxt.frame
        sym = subBodyCtxt.sym 
        writevar = ""
        putstatic = ""

        result = list()
        
        lhsName, lhsType, lhsValue = self.visit(ast.lhs,Access(frame, sym, True, True))
        if type(lhsType) == ArrayType:
            lhsCode,temp = self.visit(ast.lhs,Access(frame,sym,True,False))
            self.emit.printout(lhsCode)
        rhsCode, rhsType = self.visit(ast.exp,Access(frame, sym, False, False))
        if (type(rhsType), type(lhsType)) == (IntType,FloatType):
            rhsCode = rhsCode + self.emit.emitI2F(frame)
        self.emit.printout(rhsCode)
        result.append(rhsCode)

        if lhsValue:
            writevar = ""
            if type(lhsType) == ArrayType:
                writevar = self.emit.emitWRITEVAR2(lhsName,lhsType.eleType,frame)
            else: writevar = self.emit.emitWRITEVAR(lhsName,lhsType,lhsValue.value,frame)

            result.append(writevar)
            self.emit.printout(writevar)
        else:
            result.append(putstatic)
            putstatic = ""
            if type(lhsType) == ArrayType:
                putstatic = self.emit.emitWRITEVAR2(lhsName,lhsType.eleType,frame)
            else:   putstatic = self.emit.emitPUTSTATIC(self.className + "." + lhsName,lhsType,frame)
            
            self.emit.printout(putstatic)
        
        return ''.join(result)
    def visitArrayCell(self,ast,o):
        accessCtxt = o
        frame = accessCtxt.frame
        sym = accessCtxt.sym
        lhsName, lhsType, lhsValue = self.visit(ast.arr,Access(frame,sym,True,True))
        #arraySym = self.lookup()
        if not accessCtxt.isLeft:
            result = list()
            
            if lhsValue is None: #global var
                result.append(self.emit.emitGETSTATIC(self.className + "." + lhsName,lhsType, frame))
            else: result.append(self.emit.emitREADVAR(lhsName, lhsType, lhsValue.value, frame))
            indexCode, indexType = self.visit(ast.idx,Access(frame,sym,False,False))
            result.append(indexCode)
            result.append(self.emit.emitALOAD(lhsType.eleType,frame))
            return ''.join(result), lhsType.eleType

        if accessCtxt.isFirst:
            return lhsName.lower(),lhsType,lhsValue
        else: 
            arrCode, arrType = self.visit(ast.arr,Access(frame,sym,False,True))
            indexCode, indexType = self.visit(ast.idx,Access(frame,sym,False,False))
            return arrCode+indexCode,arrType