from codegen.CodeGenError import ( # Utils, # IllegalEscape, IllegalOperandException, # IllegalRuntimeException ) # from Utils import * from checker.StaticCheck import MType # , Symbol # from StaticError import ( # # Kind, # Function, # Procedure, # Variable, # Parameter, # Identifier, # # StaticError, # Undeclared, # Redeclared, # TypeMismatchInExpression, # TypeMismatchInStatement, # FunctionNotReturn, # BreakNotInLoop, # ContinueNotInLoop, # NoEntryPoint, # UnreachableStatement, # Unreachable # ) from utils.AST import ( IntType, FloatType, BoolType, StringType, # ArrayType, VoidType, # Program, # Decl, # VarDecl, # FuncDecl, # Stmt, # Assign, # If, # While, # For, # Break, # Continue, # Return, # With, # CallStmt, # Expr, # BinaryOp, # UnaryOp, # CallExpr, # LHS, # Id, # ArrayCell, # Literal, IntLiteral, # FloatLiteral, # StringLiteral, # BooleanLiteral, ArrayPointerType, ClassType ) # from codegen.CodeGenerator import ArrayPointerType, ClassType from codegen.MachineCode import JasminCode class Emitter(): def __init__(self, filename): self.filename = filename self.buff = list() self.jvm = JasminCode() def getJVMType(self, inType): ''' https://docs.oracle.com/javase/specs/jvms/se11/html/jvms-4.html#jvms-4.3.2 ''' typeIn = type(inType) if typeIn is IntType: return "I" elif typeIn is FloatType: return "F" elif typeIn is BoolType: return "Z" elif typeIn is StringType: return "Ljava/lang/String;" elif typeIn is VoidType: return "V" elif typeIn is ArrayPointerType: return "[" + self.getJVMType(inType.eleType) elif typeIn is MType: return "({}){}".format( "".join(list(map(lambda x: self.getJVMType(x), inType.partype))), self.getJVMType(inType.rettype) ) # return "(" + "".join(list(map(lambda x: self.getJVMType(x), # inType.partype))) + ")" + self.getJVMType(inType.rettype) elif typeIn is ClassType: return "L" + inType.cname + ";" def getFullType(inType): typeIn = type(inType) if typeIn is IntType: return "int" elif typeIn is StringType: return "java/lang/String" elif typeIn is VoidType: return "void" def emitPUSHICONST(self, in_, frame): # in: Int or Sring # frame: Frame frame.push() if isinstance(in_, int): i = in_ if i >= -1 and i <= 5: return self.jvm.emitICONST(i) elif i >= -128 and i <= 127: return self.jvm.emitBIPUSH(i) elif i >= -32768 and i <= 32767: return self.jvm.emitSIPUSH(i) elif isinstance(in_, str): if in_ == "true": return self.emitPUSHICONST(1, frame) elif in_ == "false": return self.emitPUSHICONST(0, frame) else: return self.emitPUSHICONST(int(in_), frame) def emitPUSHFCONST(self, in_, frame): # in_: String # frame: Frame f = float(in_) frame.push() rst = "{0:.4f}".format(f) if rst == "0.0" or rst == "1.0" or rst == "2.0": return self.jvm.emitFCONST(rst) else: return self.jvm.emitLDC(in_) ''' * generate code to push a constant onto the operand stack. * @param in the lexeme of the constant * @param typ the type of the constant ''' def emitPUSHCONST(self, in_, typ, frame): # in_: String # typ: Type # frame: Frame if isinstance(typ, IntType): return self.emitPUSHICONST(in_, frame) elif isinstance(typ, BoolType): return self.emitPUSHICONST(in_, frame) elif isinstance(typ, StringType): frame.push() return self.jvm.emitLDC(in_) else: raise IllegalOperandException(in_) ############################################### def emitALOAD(self, in_, frame): # in_: Type # frame: Frame # ..., arrayref, index, value -> ... frame.pop() if isinstance(in_, IntType): return self.jvm.emitIALOAD() elif isinstance(in_, ArrayPointerType) or\ isinstance(in_, ClassType) or\ isinstance(in_, StringType): return self.jvm.emitAALOAD() else: raise IllegalOperandException(str(in_)) def emitASTORE(self, in_, frame): # in_: Type # frame: Frame # ..., arrayref, index, value -> ... frame.pop() frame.pop() frame.pop() if isinstance(in_, IntType): return self.jvm.emitIASTORE() elif isinstance(in_, ArrayPointerType) or\ isinstance(in_, ClassType) or\ isinstance(in_, StringType): return self.jvm.emitAASTORE() else: raise IllegalOperandException(str(in_)) ''' generate the var directive for a local variable. * @param in the index of the local variable. * @param varName the name of the local variable. * @param inType the type of the local variable. * @param fromLabel the starting label of the scope * where the variable is active. * @param toLabel the ending label of the scope * where the variable is active. ''' def emitVAR(self, in_, varName, inType, fromLabel, toLabel, frame): # in_: Int # varName: String # inType: Type # fromLabel: Int # toLabel: Int # frame: Frame return self.jvm.emitVAR( in_, varName, self.getJVMType(inType), fromLabel, toLabel) def emitREADVAR(self, name, inType, index, frame): # name: String # inType: Type # index: Int # frame: Frame # ... -> ..., value frame.push() if isinstance(inType, (IntType, BoolType)): return self.jvm.emitILOAD(index) elif isinstance(inType, FloatType): return self.jvm.emitFLOAD(index) elif isinstance(inType, ArrayPointerType) or\ isinstance(inType, ClassType) or\ isinstance(inType, StringType): return self.jvm.emitALOAD(index) else: raise IllegalOperandException(name) ''' generate the second instruction for array cell access * ''' def emitREADVAR2(self, name, typ, frame): # name: String # typ: Type # frame: Frame # ... -> ..., value # frame.push() raise IllegalOperandException(name) ''' * generate code to pop a value on top of the operand stack * and store it to a block-scoped variable. * @param name the symbol entry of the variable. ''' def emitWRITEVAR(self, name, inType, index, frame): # name: String # inType: Type # index: Int # frame: Frame # ..., value -> ... frame.pop() if isinstance(inType, (IntType, BoolType)): return self.jvm.emitISTORE(index) elif isinstance(inType, FloatType): return self.jvm.emitFSTORE(index) elif isinstance(inType, ArrayPointerType) or\ isinstance(inType, ClassType) or\ isinstance(inType, StringType): return self.jvm.emitASTORE(index) else: raise IllegalOperandException(name) ''' generate the second instruction for array cell access * ''' def emitWRITEVAR2(self, name, typ, frame): # name: String # typ: Type # frame: Frame # ..., value -> ... # frame.push() raise IllegalOperandException(name) ''' generate the field (static) directive for a class mutable or * immutable attribute. * @param lexeme the name of the attribute. * @param in the type of the attribute. * @param isFinal true in case of constant; false otherwise ''' def emitATTRIBUTE(self, lexeme, in_, isFinal, value): # lexeme: String # in_: Type # isFinal: Boolean # value: String return self.jvm.emitSTATICFIELD(lexeme, self.getJVMType(in_), False) def emitGETSTATIC(self, lexeme, in_, frame): # lexeme: String # in_: Type # frame: Frame frame.push() return self.jvm.emitGETSTATIC(lexeme, self.getJVMType(in_)) def emitPUTSTATIC(self, lexeme, in_, frame): # lexeme: String # in_: Type # frame: Frame frame.pop() return self.jvm.emitPUTSTATIC(lexeme, self.getJVMType(in_)) def emitGETFIELD(self, lexeme, in_, frame): # lexeme: String # in_: Type # frame: Frame return self.jvm.emitGETFIELD(lexeme, self.getJVMType(in_)) def emitPUTFIELD(self, lexeme, in_, frame): # lexeme: String # in_: Type # frame: Frame frame.pop() frame.pop() return self.jvm.emitPUTFIELD(lexeme, self.getJVMType(in_)) ''' generate code to invoke a static method * @param lexeme the qualified name of the method * (i.e., class-name/method-name) * @param in the type descriptor of the method. ''' def emitINVOKESTATIC(self, lexeme, in_, frame): # lexeme: String # in_: Type # frame: Frame typ = in_ list(map(lambda x: frame.pop(), typ.partype)) if not isinstance(typ.rettype, VoidType): frame.push() return self.jvm.emitINVOKESTATIC(lexeme, self.getJVMType(in_)) ''' generate code to invoke a special method * @param lexeme the qualified name of the method * (i.e., class-name/method-name) * @param in the type descriptor of the method. ''' def emitINVOKESPECIAL(self, frame, lexeme=None, in_=None): # lexeme: String # in_: Type # frame: Frame if lexeme is not None and in_ is not None: typ = in_ list(map(lambda x: frame.pop(), typ.partype)) frame.pop() if not isinstance(typ.rettype, VoidType): frame.push() return self.jvm.emitINVOKESPECIAL(lexeme, self.getJVMType(in_)) elif lexeme is None and in_ is None: frame.pop() return self.jvm.emitINVOKESPECIAL() ''' generate code to invoke a virtual method * @param lexeme the qualified name of the method * (i.e., class-name/method-name) * @param in the type descriptor of the method. ''' def emitINVOKEVIRTUAL(self, lexeme, in_, frame): # lexeme: String # in_: Type # frame: Frame typ = in_ list(map(lambda x: frame.pop(), typ.partype)) frame.pop() if not isinstance(typ, VoidType): frame.push() return self.jvm.emitINVOKEVIRTUAL(lexeme, self.getJVMType(in_)) ''' * generate ineg, fneg. * @param in the type of the operands. ''' def emitNEGOP(self, in_, frame): # in_: Type # frame: Frame # ..., value -> ..., result if isinstance(in_, IntType): return self.jvm.emitINEG() else: return self.jvm.emitFNEG() def emitNOT(self, in_, frame): # in_: Type # frame: Frame label1 = frame.getNewLabel() label2 = frame.getNewLabel() result = list() result.append(self.emitIFTRUE(label1, frame)) result.append(self.emitPUSHCONST("true", in_, frame)) result.append(self.emitGOTO(label2, frame)) result.append(self.emitLABEL(label1, frame)) result.append(self.emitPUSHCONST("false", in_, frame)) result.append(self.emitLABEL(label2, frame)) return ''.join(result) ''' * generate iadd, isub, fadd or fsub. * @param lexeme the lexeme of the operator. * @param in the type of the operands. ''' def emitADDOP(self, lexeme, in_, frame): # lexeme: String # in_: Type # frame: Frame # ..., value1, value2 -> ..., result frame.pop() if lexeme == "+": if isinstance(in_, IntType): return self.jvm.emitIADD() else: return self.jvm.emitFADD() else: if isinstance(in_, IntType): return self.jvm.emitISUB() else: return self.jvm.emitFSUB() ''' * generate imul, idiv, fmul or fdiv. * @param lexeme the lexeme of the operator. * @param in the type of the operands. ''' def emitMULOP(self, lexeme, in_, frame): # lexeme: String # in_: Type # frame: Frame # ..., value1, value2 -> ..., result frame.pop() if lexeme == "*": if isinstance(in_, IntType): return self.jvm.emitIMUL() else: return self.jvm.emitFMUL() else: if isinstance(in_, IntType): return self.jvm.emitIDIV() else: return self.jvm.emitFDIV() def emitDIV(self, frame): # frame: Frame frame.pop() return self.jvm.emitIDIV() def emitMOD(self, frame): # frame: Frame frame.pop() return self.jvm.emitIREM() ''' * generate iand ''' def emitANDOP(self, frame): # frame: Frame frame.pop() return self.jvm.emitIAND() ''' * generate ior ''' def emitOROP(self, frame): # frame: Frame frame.pop() return self.jvm.emitIOR() def emitREOP(self, op, in_, frame): # op: String # in_: Type # frame: Frame # ..., value1, value2 -> ..., result result = list() labelF = frame.getNewLabel() labelO = frame.getNewLabel() frame.pop() frame.pop() if isinstance(in_, FloatType): frame.push() result.append(self.jvm.emitFCMPL()) result.append(self.emitPUSHICONST(0, frame)) frame.pop() if op == ">": result.append(self.jvm.emitIFICMPLE(labelF)) elif op == ">=": result.append(self.jvm.emitIFICMPLT(labelF)) elif op == "<": result.append(self.jvm.emitIFICMPGE(labelF)) elif op == "<=": result.append(self.jvm.emitIFICMPGT(labelF)) elif op == "<>": result.append(self.jvm.emitIFICMPEQ(labelF)) elif op == "=": result.append(self.jvm.emitIFICMPNE(labelF)) result.append(self.emitPUSHCONST("1", IntType(), frame)) frame.pop() result.append(self.emitGOTO(labelO, frame)) result.append(self.emitLABEL(labelF, frame)) result.append(self.emitPUSHCONST("0", IntType(), frame)) result.append(self.emitLABEL(labelO, frame)) return ''.join(result) def emitRELOP(self, op, in_, trueLabel, falseLabel, frame): # op: String # in_: Type # trueLabel: Int # falseLabel: Int # frame: Frame # ..., value1, value2 -> ..., result result = list() frame.pop() frame.pop() if op == ">": result.append(self.jvm.emitIFICMPLE(falseLabel)) result.append(self.emitGOTO(trueLabel)) elif op == ">=": result.append(self.jvm.emitIFICMPLT(falseLabel)) elif op == "<": result.append(self.jvm.emitIFICMPGE(falseLabel)) elif op == "<=": result.append(self.jvm.emitIFICMPGT(falseLabel)) elif op == "!=": result.append(self.jvm.emitIFICMPEQ(falseLabel)) elif op == "==": result.append(self.jvm.emitIFICMPNE(falseLabel)) result.append(self.jvm.emitGOTO(trueLabel)) return ''.join(result) ''' generate the method directive for a function. * @param lexeme the qualified name of the method * (i.e., class-name/method-name). * @param in the type descriptor of the method. * @param isStatic true if the method is static; * false otherwise. ''' def emitMETHOD(self, lexeme, in_, isStatic, frame): # lexeme: String # in_: Type # isStatic: Boolean # frame: Frame return self.jvm.emitMETHOD(lexeme, self.getJVMType(in_), isStatic) ''' generate the end directive for a function. ''' def emitENDMETHOD(self, frame): # frame: Frame buffer = list() buffer.append(self.jvm.emitLIMITSTACK(frame.getMaxOpStackSize())) buffer.append(self.jvm.emitLIMITLOCAL(frame.getMaxIndex())) buffer.append(self.jvm.emitENDMETHOD()) return ''.join(buffer) def getConst(self, ast): # ast: Literal if isinstance(ast, IntLiteral): return (str(ast.value), IntType()) ''' generate code to initialize a local array variable.

* @param index the index of the local variable. * @param in the type of the local array variable. ''' ''' generate code to initialize local array variables. * @param in the list of symbol entries corresponding to * local array variable. ''' def emitIFEQ(self, label, frame): frame.pop() return self.jvm.emitIFEQ(label) def emitIFNE(self, label, frame): frame.pop() return self.jvm.emitIFNE(label) ''' generate code to jump to label if the value on top of * operand stack is true.

* ifgt label * @param label the label where the execution continues if * the value on top of stack is true. ''' def emitIFTRUE(self, label, frame): # label: Int # frame: Frame frame.pop() return self.jvm.emitIFGT(label) ''' * generate code to jump to label if the value on top of * operand stack is false.

* ifle label * @param label the label where the execution continues if * the value on top of stack is false. ''' def emitIFFALSE(self, label, frame): # label: Int # frame: Frame frame.pop() return self.jvm.emitIFLE(label) def emitIFICMPGT(self, label, frame): # label: Int # frame: Frame frame.pop() return self.jvm.emitIFICMPGT(label) def emitIFICMPLT(self, label, frame): # label: Int # frame: Frame frame.pop() return self.jvm.emitIFICMPLT(label) ''' generate code to duplicate the value on the top of the operand stack.

* Stack:

* Before: ...,value1

* After: ...,value1,value1

''' def emitDUP(self, frame): # frame: Frame frame.push() return self.jvm.emitDUP() def emitPOP(self, frame): # frame: Frame frame.pop() return self.jvm.emitPOP() ''' generate code to exchange an integer on top of stack to a floating-point number. ''' def emitI2F(self, frame): # frame: Frame return self.jvm.emitI2F() ''' generate code to return. *

* @param in the type of the returned expression. ''' def emitRETURN(self, in_, frame): # in_: Type # frame: Frame if isinstance(in_, (IntType, BoolType)): frame.pop() return self.jvm.emitIRETURN() elif isinstance(in_, FloatType): frame.pop() return self.jvm.emitFRETURN() elif isinstance(in_, VoidType): return self.jvm.emitRETURN() ''' generate code that represents a label * @param label the label * @return code Label