MPC/codegen/Emitter.py
Nguyễn Anh Khoa bed5a47d55 init
2018-12-04 02:05:06 +07:00

785 lines
21 KiB
Python

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 <code>true</code> if the method is static;
* <code>false</code> 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.<p>
* @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.<p>
* 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.<p>
* 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.<p>
* Stack:<p>
* Before: ...,value1<p>
* After: ...,value1,value1<p>
'''
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.
* <ul>
* <li>ireturn if the type is IntegerType or BooleanType
* <li>freturn if the type is RealType
* <li>return if the type is null
* </ul>
* @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<label>:
'''
def emitLABEL(self, label, frame):
# label: Int
# frame: Frame
return self.jvm.emitLABEL(label)
''' generate code to jump to a label
* @param label the label
* @return code goto Label<label>
'''
def emitGOTO(self, label, frame):
# label: Int
# frame: Frame
return self.jvm.emitGOTO(label)
''' generate some starting directives for a class.<p>
* .source MPC.CLASSNAME.java<p>
* .class public MPC.CLASSNAME<p>
* .super java/lang/Object<p>
'''
def emitPROLOG(self, name, parent):
# name: String
# parent: String
result = list()
result.append(self.jvm.emitSOURCE(name + ".java"))
result.append(self.jvm.emitCLASS("public " + name))
result.append(
self.jvm.emitSUPER(
"java/land/Object" if parent == "" else parent))
return ''.join(result)
def emitLIMITSTACK(self, num):
# num: Int
return self.jvm.emitLIMITSTACK(num)
def emitLIMITLOCAL(self, num):
# num: Int
return self.jvm.emitLIMITLOCAL(num)
def emitEPILOG(self):
file = open(self.filename, "w")
file.write(''.join(self.buff))
file.close()
''' print out the code to screen
* @param in the code to be printed out
'''
def printout(self, in_):
# in_: String
self.buff.append(in_)
def clearBuff(self):
self.buff.clear()