ExprPrimaryNodes.py 36.92 KiB
from AST import ASTNode, getParseTreeNodes
from Environment import Env
from UnitNodes import LiteralNode
import MemberNodes
from TheTypeNode import TypeNode, TypeStruct
from NameNode import NameNode, checkProtected
from CodeGenUtils import p, pLabel, genMethodInvoke, importHelper, getCFlowLabel
# file containing smaller (lower level nodes) in the AST
# nodes in this file:
# ArgsNode
# ArrayAccessNode
# ArrayCreateNode
# AssignNode
# CastNode
# ClassCreateNode
# ExprNode
# FieldAccessNode
# MethodInvNode
# TODO: go over nodes in this file to see if need to overright buildEnv
###########################################################
# factory methods
###########################################################
# parses the expr node in the parse tree to be either ID or CastNode or ExprNode
def makeNodeFromExpr(parseTree, typeName):
c = parseTree
while (42):
if c.name == 'primaryAndArray':
return makeNodeFromAllPrimary(c, typeName)
elif c.name == 'ID':
return NameNode(c, False, typeName) # TODO is this always False??
elif c.name == 'COMPID':
return FieldAccessNode(c, typeName, True)
elif c.name == 'assignment':
return AssignNode(c, typeName)
elif c.name == 'refType':
return TypeNode(c, typeName)
elif len(c.children) == 1:
c = c.children[0]
elif c.name == 'castExpr':
return CastNode(c, typeName)
else:
return ExprNode(c, typeName)
# parses the primaryAndArray/primary/primaryNoArrayAccess node in the parse tree and return corresponding AST nodes
def makeNodeFromAllPrimary(parseTree, typeName):
if parseTree.name == 'primaryAndArray':
if parseTree.children[0].name == 'arrayCreationExpr':
parseTree = parseTree.children[0]
return ArrayCreateNode(parseTree, typeName)
elif parseTree.children[0].name == 'primary':
if parseTree.children[0].children[0].name == 'arrayAccess':
return ArrayAccessNode(parseTree.children[0].children[0], typeName)
parseTree = parseTree.children[0].children[0]
if parseTree.name == 'primary':
if parseTree.children[0].name == 'arrayAccess':
return ArrayAccessNode(parseTree.children[0], typeName)
parseTree = parseTree.children[0]
node = parseTree.children[0]
if node.name == 'literal':
return LiteralNode(node, typeName)
elif node.name == 'LPAREN': # primaryNoArrayAccess LPAREN expr RPAREN
return makeNodeFromExpr(parseTree.children[1], typeName)
elif node.name == 'classInstanceCreate':
return ClassCreateNode(node.children[0], typeName)
elif node.name == 'methodInvoc':
return MethodInvNode(node, typeName)
elif node.name == 'fieldAccess':
return FieldAccessNode(node, typeName)
else:
raise Exception('ERROR: something wrong at primaryNoArrayAccess')
###########################################################
# helper methods
###########################################################
def helperDisambigName(node):
if node and node.__class__.__name__ == "NameNode":
try:
node.disambigName()
except Exception as e:
raise e
###########################################################
########################### Node Definitions #####################################
###################################################################################
# args exprs, exprs expr COMMA exprs
class ArgsNode(ASTNode):
# always list all fields in the init method to show the class structure
def __init__(self, parseTree, typeName):
self.parseTree = parseTree
self.exprs = [] # a list of expressions
self.env = None
self.children = []
self.typeName = typeName # the type (class/interface) this node belongs under
exprs = getParseTreeNodes(['expr'], parseTree)
for e in exprs:
self.exprs.append(makeNodeFromExpr(e, typeName))
self.children.extend(self.exprs)
def disambigName(self):
for expr in self.exprs:
expr.disambigName()
# helperDisambigName(expr)
def codeGen(self):
if hasattr(self, "code"):
return
self.code = ""
# Generating code for each of the arguments
for arg in self.exprs:
if arg and hasattr(arg, "codeGen"):
# children hasn't generated code yet
# Note: this check is redundant if we're certain that every override of this method has the initial check
if not hasattr(arg, "code"):
arg.codeGen()
self.code += arg.code
self.code += p(instruction="push", arg1="eax", comment="pushing result of evaluation of argument")
###################################################################################
# Array Access
class ArrayAccessNode(ASTNode):
# always list all fields in the init method to show the class structure
def __init__(self, parseTree, typeName):
self.parseTree = parseTree
self.array = '' # either a variableName, a field, or array access
self.index = '' # expr
self.env = None
self.children = []
self.typeName = typeName # the type (class/interface) this node belongs under
# input parse tree is either: arrayAccess name LSQRBRACK expr RSQRBRACK
# arrayAccess ID LSQRBRACK expr RSQRBRACK
# arrayAccess primaryNoArrayAccess LSQRBRACK expr RSQRBRACK
if (parseTree.children[0].name == 'primaryNoArrayAccess'):
self.array = makeNodeFromAllPrimary(parseTree.children[0], typeName)
else:
self.array = NameNode(parseTree.children[0], False, typeName)
self.index = makeNodeFromExpr(parseTree.children[2], typeName)
self.children.append(self.array)
self.children.append(self.index)
def disambigName(self):
self.array.disambigName()
self.index.disambigName()
def checkType(self):
self.array.disambigName() # hacky fix, not sure why disambigName wasn't called before
self.array.checkType()
self.index.checkType()
if not self.array.myType.isArray:
raise Exception("ERROR: Cannot perform array access on non-array {}".format(self.array.name))
if not self.index.myType.isNum():
raise Exception("ERROR: Array index must be a number.")
self.myType = TypeStruct(self.array.myType.name, self.array.myType.typePointer)
###################################################################################
# arrayCreationExpr
# arrayCreationExpr NEW primitiveType LSQRBRACK expr RSQRBRACK
# arrayCreationExpr NEW name LSQRBRACK expr RSQRBRACK
# arrayCreationExpr NEW primitiveType LSQRBRACK RSQRBRACK
# arrayCreationExpr NEW name LSQRBRACK RSQRBRACK
class ArrayCreateNode(ASTNode):
# always list all fields in the init method to show the class structure
def __init__(self, parseTree, typeName):
self.parseTree = parseTree
self.arrayType = ''
self.arraySize = 0 # or Expr
self.env = None
self.children = []
self.typeName = typeName # the type (class/interface) this node belongs under
# input is arrayCreationExpr NEW type LSQRBRACK expr RSQRBRACK
self.arrayType = TypeNode(parseTree.children[1], typeName)
expr = getParseTreeNodes(['expr'], parseTree)
if len(expr) > 0:
self.arraySize = makeNodeFromExpr(expr[0], typeName)
self.children.append(self.arrayType)
self.children.append(self.arraySize)
def disambigName(self):
self.arraySize.disambigName()
# helperDisambigName(self.arraySize)
def checkType(self):
if self.arraySize != 0:
self.arraySize.checkType()
if not self.arraySize.myType.isNum():
raise Exception("ERROR: Array index must be a number.")
self.myType = TypeStruct(self.arrayType.myType.name, self.arrayType.myType.typePointer)
self.myType.isArray = True
###################################################################################
# assignment leftHandSide ASSIGN expr
class AssignNode(ASTNode):
# always list all fields in the init method to show the class structure
def __init__(self, parseTree, typeName):
self.parseTree = parseTree
self.left = None
self.right = makeNodeFromExpr(parseTree.children[2], typeName)
self.env = None
self.children = []
self.typeName = typeName # the type (class/interface) this node belongs under
if parseTree.children[0].children[0].name == 'fieldAccess':
self.left = FieldAccessNode(parseTree.children[0].children[0], typeName)
elif parseTree.children[0].children[0].name == 'arrayAccess':
self.left = ArrayAccessNode(parseTree.children[0].children[0], typeName)
else:
self.left = NameNode(parseTree.children[0].children[0], False, typeName)
self.children.append(self.right)
self.children.append(self.left)
def disambigName(self):
self.left.disambigName()
self.right.disambigName()
# helperDisambigName(self.right)
# helperDisambigName(self.left)
def checkType(self):
self.left.checkType()
self.right.checkType()
if self.left.myType.assignable(self.right.myType):
self.myType = self.left.myType
return
raise Exception("ERROR: assignment operation failed. Cannot assign type {0} to type {1} at class {2}".format(self.left.myType.name, self.right.myType.name, self.typeName))
def reachCheck(self, inMaybe):
if not inMaybe:
raise Exception("ERROR: not reaching a assignment statement")
self.outMaybe = inMaybe
def codeGen(self):
self.right.codeGen()
self.code = self.right.code
if self.left.__class__.__name__ == "NameNode":
if self.left.prefixLink.__class__.__name__ == "VarDclNode":
# move init result to var location
self.code += p("mov", "[ebp - " + str(self.left.prefixLink.offset) + "]", "eax")
##################################################################################
# cast: castExpr LPAREN castType RPAREN unaryNotPlusMinus
class CastNode(ASTNode):
# always list all fields in the init method to show the class structure
def __init__(self, parseTree, typeName):
self.parseTree = parseTree
self.left = parseTree.children[1] # cast: (left)right
self.right = makeNodeFromExpr(parseTree.children[3], typeName) # expr
self.env = None
self.children = []
self.typeName = typeName # the type (class/interface) this node belongs under
if self.left.name == 'expr':
self.left = makeNodeFromExpr(self.left, typeName)
else: #primitiveType or ArrayType
self.left = TypeNode(self.left, typeName)
# since a type might be mis-parsed as a name
if self.left.__class__.__name__ == 'NameNode':
self.left = TypeNode(self.parseTree.children[1], typeName)
self.children.append(self.left)
self.children.append(self.right)
def disambigName(self):
if self.left.__class__.__name__ != 'TypeNode':
self.left.disambigName()
self.right.disambigName()
# helperDisambigName(self.left)
# helperDisambigName(self.right)
def checkType(self):
self.left.checkType()
from pprint import pprint
self.right.disambigName()
self.right.checkType()
if (self.left.myType.isNum() and self.right.myType.isNum()) \
or self.left.myType.assignable(self.right.myType) \
or self.right.myType.assignable(self.left.myType):
self.myType = self.left.myType
return
raise Exception("ERROR: Cannot cast type {} to type {}.".format(self.right.myType.name, self.left.myType.name))
###################################################################################
# unqualCreate NEW name LPAREN args RPAREN
class ClassCreateNode(ASTNode):
# always list all fields in the init method to show the class structure
def __init__(self, parseTree, typeName):
self.parseTree = parseTree
self.className = TypeNode(parseTree.children[1], typeName)
self.args = ArgsNode(parseTree.children[3], typeName)
self.env = None
self.children = [self.className, self.args]
self.typeName = typeName
self.cons = None # the constructor used to create the class
def checkType(self):
# return # TO REMOVE after name node type checking is done
self.args.checkType()
classDef = self.className.myType.typePointer
# check class is not abstract
if 'abstract' in classDef.mods:
raise Exception('ERROR: Cannot create an instance of abstract class {}.'.format(self.className.myType.name))
elif classDef.__class__.__name__ != 'ClassNode':
raise Exception('ERROR: Cannot create an instance of {}, it is not a class.'.format(self.className.myType.name))
# check 0 arguement constructor of superclass exists
su = classDef.superClass
while su != '': # if it doesn't have an explict super class, its super class is java.lang.object, which is safe
found = False
for c in su.constructors:
if c.params == []:
found = True
break
if not found:
raise Exception("ERROR: Class {} doesn't have a zero-arguement constructor.".format(su.name))
su = su.superClass
# get constructor using arg Types
m = getMethod(classDef.constructors, "", self.args)
if m:
self.cons = m
self.myType = self.className.myType
else:
raise Exception("ERROR: Class {} doesn't have a constructor with given argument types.".format(classDef.name))
# check to make sure we are allowed to call this (protected?)
# if self.cons is protected, check that:
# - current class is in the same package
if 'protected' in self.cons.mods:
curClass = self.env.getNode(self.typeName, 'type')
if curClass.packageName != classDef.packageName:
raise Exception("ERROR: In class {0}, using a protected constructor, but class {1} is not in class {0}'s package ({2}).".format(curClass.name, classDef.name, curClass.packageName))
def codeGen(self):
if hasattr(self, "code"):
return
self.code = ""
# 1. Allocating space for the object in heap
classDef = self.className.myType.typePointer
fieldOffset = classDef.fieldOffset
numFields = len(fieldOffset)
numBytes = (numFields + 1) * 4
self.code += "; Creating an object for class " + self.typeName + "\n"
self.code += p(instruction="mov", arg1="eax", arg2=numBytes, comment="allocating memory for object") + \
p(instruction="call", arg1="__malloc")
# 2. Pointing first item to vtable
self.code += importHelper(classDef.name, self.typeName, "C_"+classDef.name)
self.code += p(instruction="mov", arg1="[eax]", arg2="dword C_" + classDef.name, comment="first item is vtable pointer")
# 3. Calling constructor
self.code += "; Calling constructor for object\n"
self.code += p(instruction="push", arg1="eax", comment="pushing object as first argument")
# Evaluate arguments and pushing parameters
if self.args and hasattr(self.args, "codeGen"):
self.args.codeGen()
self.code += self.args.code
label = "M_" + self.cons.typeName + "_" + self.cons.name + "_" + self.cons.paramTypes
self.code += importHelper(classDef.name, self.typeName, label)
self.code += p(instruction="call", arg1=label, comment="Calling constructor")
# 4. Popping parameters and pointer to object
self.code += p(instruction="add", arg1="esp", arg2=len(self.args.exprs)*4, comment="Popping parameters") + \
p(instruction="pop", arg1="eax", comment="eax now contains pointer to newly created object")
self.code += ";End of object creation\n"
#################################################################################
# condOrExpr
class ExprNode(ASTNode):
# always list all fields in the init method to show the class structure
def __init__(self, parseTree, typeName):
self.parseTree = parseTree
self.left = None
self.op = ''
self.right = None # another expr
self.env = None
self.children = []
self.typeName = typeName # the type (class/interface) this node belongs under
if parseTree.name == 'unaryNotPlusMinus' or parseTree.name == 'unaryExpr':
self.op = parseTree.children[0].lex
self.right = makeNodeFromExpr(parseTree.children[1], typeName)
else:
self.left = makeNodeFromExpr(parseTree.children[0], typeName)
self.op = parseTree.children[1].lex
self.right = makeNodeFromExpr(parseTree.children[2], typeName)
self.children.append(self.left)
self.children.append(self.right)
def disambigName(self):
if self.left:
self.left.disambigName()
self.right.disambigName()
# helperDisambigName(self.left)
# helperDisambigName(self.right)
# use wrong name to stop method from being called until we finish other implemetation
# def checkType(self):
def checkType(self):
# steps of type checking:
# check children's types (children's myType field will be populated with a typeStruct)
# check using the rule for current node
# make a TypeStruct node and populate myType field for self
super().checkType() # check children's type first to populate their myType field
# Unary operations:
if not self.left:
if self.op == '-' and self.right.myType.isNum():
self.myType = TypeStruct("int", None)
return
elif self.op == '!' and self.right.myType.name == 'boolean':
self.myType = self.myType = TypeStruct("boolean", None)
return
# Numeric types
if self.left.myType.isNum() and self.right.myType.isNum():
# Comparisons:
if self.op in ['==', '!=', '<=', '>=', '>', '<']:
self.myType = TypeStruct("boolean", None)
return
# numeric operations:
elif self.op in ['+', '-', '*', '/', '%']:
self.myType = TypeStruct("int", None)
return
# Boolean operations:
if self.left.myType.name == 'boolean' and self.right.myType.name == 'boolean':
if self.op in ['&&', '&', '|', '||', '!=', '==']:
self.myType = TypeStruct("boolean", None)
return
if self.left.myType.assignable(self.right.myType) or self.right.myType.assignable(self.left.myType):
if self.op == '==' or self.op == '!=' or self.op == 'instanceof':
self.myType = TypeStruct("boolean", None)
return
# String concat:
if ((self.left.myType.name =='java.lang.String' and self.right.myType.name not in ['void']) \
or (self.right.myType.name =='java.lang.String' and self.left.myType.name not in ['void'])) and self.op == '+':
self.myType = TypeStruct('java.lang.String', self.env.getNode('java.lang.String', 'type'))
self.myType.link(self.env)
return
raise Exception("ERROR: Incompatible types. Left of {} type can't be used with right of {} type on operation {}".format(self.left.myType.name, self.right.myType.name, self.op))
def codeGen(self):
if hasattr(self, "code") and self.code != "":
return
self.code = ""
# Unary operations:
if not self.left:
# get right output
if not hasattr(self.right, "code"):
self.right.codeGen()
self.code += self.right.code
if self.op == '-':
self.code += p('neg', 'eax')
return
if self.op == '!':
self.code += p('not', 'eax')
return
# Comparisons that short-circuit:
if self.op in ['&&', '||']:
n = getCFlowLabel()
endLabel = "_end" + n
# get left code
if not hasattr(self.left, "code"):
self.left.codeGen()
self.code += self.left.code
if self.op == '&&':
self.code += p("cmp", "eax", "0")
elif self.op == '||':
self.code += p("cmp", "eax", "1")
self.code += p("je", endLabel, "", " breaking out of " + self.op)
# get right code
if not hasattr(self.right, "code"):
self.right.codeGen()
self.code += self.right.code
self.code += p(endLabel + ":", "")
return
if self.op in ['==', '!=', '<=', '>=', '>', '<']:
n = getCFlowLabel()
falseLabel = "_false" + n
endLabel = "_end" + n
self.code += self.getIfFalse(falseLabel)
self.code += p("mov", "eax", "1")
self.code += p("jmp", endLabel)
self.code += p(falseLabel + ":", "")
self.code += p("mov", "eax", "0")
self.code += p(endLabel + ":", "")
return
# is binary and rest can use helper function:
self.code += self.codeGenLeftRight()
# now:
# ebx = left result
# eax = right result
# so, basically do "eax = ebx op eax"
# Comparisons:
if self.op == '&':
self.code += p("and", "eax", "ebx", " right:eax " + self.op + " left:ebx")
return
if self.op == '|':
self.code += p("por", "eax", "ebx", " right:eax " + self.op + " left:ebx")
return
# Binary operations:
# String Add TODO
if (self.left.myType.name =='java.lang.String' or self.right.myType.name =='java.lang.String') \
and self.op == '+':
return
# Number Add, Subtract, Multiply
if self.op in ['+', '-', '*']:
# operation -> generated code
ops = {
'+': "add",
'-': "sub",
'*': "imul"
}
self.code += p(ops[self.op], "ebx", "eax", " left:ebx " + self.op + " right:eax")
self.code += p("mov", "eax", "ebx", " move result to eax")
return
# Divide, Modulus
if self.op in ['/', '%']:
# switch eax and ebx, because "idiv ebx" -> eax = edx:eax / ebx
self.code += p('xchg', 'eax', 'ebx')
self.code += p('push', 'edx') # save edx in case someone else was using it
self.code += p('cdq', '', None, " set edx to the sign of eax")
self.code += p("idiv", "ebx", "", " left:eax " + self.op + " right:ebx")
# eax = eax / abx
# edx = eax % ebx
if self.op == '%':
self.code += p("mov", "eax", "edx", " move quotient result to eax") # quotient is in edx
self.code += p('pop', 'edx') # restore edx
return
# if self.op == 'instanceof': TODO
# return
# generate shorter code if self.op = comparison
def getIfFalse(self, label):
if self.op == '&&':
result = self.left.getIfFalse(label)
result += self.right.getIfFalse(label)
return result
elif self.op not in ['==', '!=', '<=', '>=', '>', '<']:
self.codeGen()
return self.code
else:
result = self.codeGenLeftRight()
result += p("cmp", "ebx", "eax", "left " + self.op + " right")
if self.op == '==':
result += p("jne", label) # jump if false (not equal)
elif self.op == '!=':
result += p("je", label) # jump if false (equal)
elif self.op == '>':
result += p("jle", label) # jump if false (le)
elif self.op == '<':
result += p("jge", label) # jump if false (ge)
elif self.op == '>=':
result += p("jl", label) # jump if false (lt)
elif self.op == '<=':
result += p("jg", label) # jump if false (gt)
return result
# helper for codeGen for getting code from left and right
def codeGenLeftRight(self):
result = ""
# get left output
if not hasattr(self.left, "code"):
self.left.codeGen()
result += self.left.code
result += p("push", "eax") # left result onto stack
# get right output
if not hasattr(self.right, "code"):
self.right.codeGen()
result += self.right.code
result += p("pop", "ebx") # left output into ebx
return result
# returns True, False, Int or None (for non-constant expr)
# children of exprNode is either exprNode or literalNode
def getConstant(self):
if not hasattr(self.right, "getConstant"):
return None
cRight = self.right.getConstant()
if cRight == None:
return None
# Unary Ops
if not self.left:
if self.op == '-':
return -cRight
return not cRight # op = '!'
else:
if not hasattr(self.left, "getConstant"):
return None
cLeft = self.left.getConstant()
if cLeft == None:
return None
# arithmetic
if self.op == '+':
return cLeft + cRight
elif self.op == '-':
return cLeft - cRight
elif self.op == '*':
return cLeft * cRight
elif self.op == '/':
return cLeft // cRight
elif self.op == '%':
return cLeft % cRight
# Comparison
elif self.op == '==':
return cLeft == cRight
elif self.op == '!=':
return cLeft != cRight
elif self.op == '>':
return cLeft > cRight
elif self.op == '<':
return cLeft < cRight
elif self.op == '>=':
return cLeft >= cRight
elif self.op == '<=':
return cLeft <= cRight
# boolean Ops
elif self.op == '&&' or self.op == '&':
return cLeft and cRight
elif self.op == '||' or self.op == '|':
return cLeft or cRight
else:
return None
###################################################################################
# fieldAccess primary PERIOD ID
# Could also just be a COMPID
class FieldAccessNode(ASTNode):
# always list all fields in the init method to show the class structure
def __init__(self, parseTree, typeName, compid=False):
self.parseTree = parseTree
self.primary = ''
self.ID = '' # field name
self.env = None
self.children = []
self.typeName = typeName # the type (class/interface) this node belongs under
if not compid:
# input: fieldAccess primary PERIOD ID
self.primary = makeNodeFromAllPrimary(parseTree.children[0], typeName)
self.ID = NameNode(parseTree.children[2], False, typeName)
self.children.append(self.primary)
else:
# input: COMPID
self.ID = NameNode(parseTree, False, typeName)
self.children.append(self.ID)
def disambigName(self):
if not self.primary: # this implies that the ID has nothing that comes before it
# helperDisambigName(self.ID)
self.ID.disambigName()
# self.right.disambigName()
else:
self.primary.disambigName()
def checkType(self):
if self.primary:
self.primary.checkType()
if self.primary.myType.isArray or self.primary.myType.isPrimitive:
self.ID.prefixLink = self.primary
else:
self.ID.prefixLink = self.primary.myType.typePointer
self.ID.checkType()
self.myType = self.ID.myType
# check protected
try:
if "protected" in self.ID.prefixLink.mods:
checkProtected(self.ID.prefixLink, self)
except: # where there are no mods
return
# generates code that evaluates the address of field access
# result stored in eax (address)
# NOTE: this is always called by codeGen, so self.code is already intialized to ""
def addr(self):
result = ""
fieldNode = self.ID.prefixLink
if fieldNode.__class__.__name__ == "VarDclNode":
fieldNode = self.ID.staticField
if "static" in fieldNode.mods:
label = fieldNode.typeName + "_" + fieldNode.name
result += "; Start of calculating address for static field: " + label + "\n" + \
importHelper(fieldNode.typeName, self.typeName, "S_"+label) + \
p(instruction="mov", arg1="eax", arg2="dword S_"+label, comment="getting address to static field") + \
"; End of field access\n"
else:
result += "; Start of calculating address for non-static field\n"
if self.primary:
self.primary.codeGen()
result += self.primary.code
else:
self.ID.codeGen()
result += self.ID.code
# Null check
# At this point, eax stores the address of the object
result += p(instruction="call", arg1="H__Null_Check", comment="calling null check function")
# Make eax store the address to the field we're accessing
result += p(instruction="add", arg1="eax", arg2=self.ID.prefixLink.offset, comment="calculating pointer to the field")
result += "; End of calculating address for non-static field\n"
return result
def codeGen(self):
if hasattr(self, "code"):
return
fieldNode = self.ID.prefixLink
label = fieldNode.typeName + "_" + fieldNode.name
self.code = "; Accessing a field :" + label + "\n"
# Evaluating the address of the field we're trying to access
self.code += self.addr()
self.code += p(instruction="mov", arg1="eax", arg2="[eax]") + \
"; End of field access\n"
###################################################################################
# methodInvoc
class MethodInvNode(ASTNode):
# always list all fields in the init method to show the class structure
def __init__(self, parseTree, typeName):
self.parseTree = parseTree
self.primary = None # can be empty
self.ID = '' # can be either ID or compID
self.args = None
self.env = None
self.children = []
self.method = None
self.typeName = typeName # the type (class/interface) this node belongs under
# input parse tree is either: methodInvoc primary PERIOD ID LPAREN args RPAREN
# methodInvoc name LPAREN args RPAREN
self.ID = NameNode(parseTree.children[-4], True, typeName)
self.args = ArgsNode(parseTree.children[-2], typeName)
if parseTree.children[0].name == 'primary':
self.primary = makeNodeFromAllPrimary(parseTree.children[0], typeName)
self.children.append(self.primary)
self.children.append(self.args)
self.children.append(self.ID)
def disambigName(self):
if not self.primary: # this implies our ID doesn't have anything that comes before it
if isinstance(self.ID, NameNode) and len(self.ID.IDs) > 1:
self.ID.disambigName()
# helperDisambigName(self.ID)
if self.args:
self.args.disambigName()
def checkType(self):
# steps of type checking:
# check param's types
# check using the rule for current node
# make a TypeStruct node for self (return type of method)
# populate params myTypes
for param in self.args.exprs:
param.checkType()
# now that we have the method name, param types, we need to:
# - check if method exists under the class its under
m = None
if not self.primary:
self.ID.checkType()
m = getMethod(self.ID.prefixLink.values(), self.ID.methodName, self.args)
self.methodClass = self.ID.methodClass
else:
self.primary.checkType()
methods = []
methods.extend(self.primary.myType.typePointer.methods)
methods.extend([meth for meth in self.primary.myType.typePointer.inherits if isinstance(meth, MemberNodes.MethodNode)]) # need to check inherited methods as well
m = getMethod(methods, self.ID.name, self.args)
self.methodClass = self.primary.myType
if m:
# I don't see any need for this check, this check causes more harm than good
# because you can call System.out.println, where 'System' is a class with static field 'out',
# which is of type 'PrintStream' which has non-static method 'println', thus going into this if statement
# if self.ID.shouldBeStatic and (not 'static' in m.mods):
# raise Exception("ERROR: Static access of non-static method {}.".format(m.name))
# check static
if (not self.ID.shouldBeStatic) and 'static' in m.mods:
raise Exception("ERROR: Non-static access of static method {}.".format(m.name))
# check protected
if "protected" in m.mods:
checkProtected(m, self)
self.method = m
self.myType = m.methodType.myType
return
else:
raise Exception("ERROR: Class {} doesn't have a method {} with given argument types.".format(self.typeName, self.ID.name))
def reachCheck(self, inMaybe):
if not inMaybe:
raise Exception("ERROR: not reaching a variable declaration statement for var {}".format(self.name))
self.outMaybe = inMaybe
def codeGen(self):
if hasattr(self, "code"):
return
self.code = ""
# Only invoking static methods
if "static" in self.method.mods:
mLabel = "M_" + self.method.typeName + "_" + self.method.name + "_" + self.method.paramTypes
(pro, epi) = genMethodInvoke(mLabel)
self.code += pro
# Evaluate arguments
if self.args and hasattr(self.args, "codeGen"):
if not hasattr(self.args, "code"):
self.args.codeGen()
self.code += self.args.code
# Calling static method
self.code += importHelper(self.method.typeName, self.typeName, mLabel)
self.code += p(instruction="call", arg1=mLabel, comment="calling method")
# Popping off all the arguments
toPop = 0
if self.args:
toPop = len(self.args.exprs)
self.code += p(instruction="add", arg1="esp", arg2=toPop*4, comment="popping off all arguments off stack")
# Epilogue
self.code += epi
# TODO: To be replaced later
else:
for c in self.children:
if c and hasattr(c, "codeGen"):
# children hasn't generated code yet
# Note: this check is redundant if we're certain that every override of this method has the initial check
if not hasattr(c, "code"):
c.codeGen()
self.code += c.code
################# Helper #######################
def getMethod(methods, methodName, args):
# methodName = "" if it's constructor
for c in methods:
if (methodName == "" or c.name == methodName) and len(args.exprs) == len(c.params):
found = True
for i, param in enumerate(args.exprs):
if c.params[i].paramType.myType != param.myType:
found = False
if found:
return c
return None