Skip to content
Snippets Groups Projects
Commit 08e2af84 authored by Nicholas Robinson's avatar Nicholas Robinson
Browse files

j1_reachability_return & static errors

parent a07f6098
No related branches found
No related tags found
No related merge requests found
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
# 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' or c.name == 'COMPID':
return NameNode(c, False, typeName) # TODO is this always False??
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)
###################################################################################
# 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
##################################################################################
# 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))
#################################################################################
# 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))
# 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
class FieldAccessNode(ASTNode):
# always list all fields in the init method to show the class structure
def __init__(self, parseTree, typeName):
self.parseTree = parseTree
self.primary = ''
self.ID = '' # method/fieldName
self.env = None
self.children = []
self.typeName = typeName # the type (class/interface) this node belongs under
# 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)
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):
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
###################################################################################
# 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)
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)
if m:
# check static
if self.ID.shouldBeStatic and (not 'static' in m.mods):
raise Exception("ERROR: Static access of non-static method {}.".format(m.name))
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
################# 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
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
# 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' or c.name == 'COMPID':
return NameNode(c, False, typeName) # TODO is this always False??
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)
###################################################################################
# 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
##################################################################################
# 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))
#################################################################################
# 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))
# 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
class FieldAccessNode(ASTNode):
# always list all fields in the init method to show the class structure
def __init__(self, parseTree, typeName):
self.parseTree = parseTree
self.primary = ''
self.ID = '' # method/fieldName
self.env = None
self.children = []
self.typeName = typeName # the type (class/interface) this node belongs under
# 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)
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):
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
###################################################################################
# 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)
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)
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
################# 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
from AST import ASTNode, getParseTreeNodes
from TheTypeNode import TypeStruct, getSupers
import MemberNodes
import TypeNodes
from Environment import Env
# name nodes: contains compID and IDs
# TODO:
# Optional stuff:
# clean up findNode vs getNode (remove findNode if getNode works properly)[optional]
# if sth is None in line Node, don't append to children
# add comments for what is self.typeName at each class (the type name that this node is underneath)
# Necessary:
# 2) checking if remaining compid are instance fields
# a) check if the last ID in the name is a method if methodInvoke == true (might not be necessary) [this is after type checking]
class NameNode(ASTNode):
def __init__(self, parseTree, methodInvoke, typeName):
self.parseTree = parseTree
self.name = "" # str: stores original lex of the name (ID/COMPID)
self.IDs = [] # a queue of strings: each string is an ID in a COMPID/ID.
self.prefix = "" # str: stores the currently resolved/identified prefix in the name
self.prefixLink = None # 3 possible values:
# 1) a pointer to an AST node (i.e. the prefix is resolved)
# 2) "contain" (prefix isn't resolved but is identified)
# 3) None: neither of 1 or 2
self.methodInvoke = methodInvoke # Bool: true if the last ID in the name is a method invokation
self.methodName = ""
self.env = None
self.typeName = typeName # the name of the class or interface that this node belongs under
self.children = []
self.myType = None # will become TypeStruct to tell us what the whole is/returns
self.shouldBeStatic = False
self.pointToThis = False
self.name = getParseTreeNodes(["ID", "COMPID"], parseTree)[0].lex
self.IDs = self.name.split(".")
# Updates the resolved/identified prefix
# ASSUMPTION: will never be called when self.IDs is empty
def addToPrefix(self, node):
# If self.prefixLink is contain, that means our self.prefix wasn't resolved yet
if type(self.prefixLink) is str and self.prefixLink == "contain":
self.prefixLink = node
return
# Otherwise, we update both self.prefix and self.prefixLink, since we have now resolved a new ID
if self.prefix:
self.prefix += "."
self.prefix += self.IDs[0]
self.prefixLink = node
self.IDs.pop(0)
# Checks and updates prefix if the next ID in self.IDs is a "this" pointer
def checkThis(self):
if not self.IDs:
return True
# The scanner makes sure that "this" can only be in the beginning of a compid (e.g. a.b.this.d, or a.b.this are both not allowed)
if self.IDs[0] == "this":
typeNode = self.env.getNode(self.typeName, "type")
self.addToPrefix(typeNode)
return True
return False
def checkLength(self):
if not self.IDs:
return True
if self.IDs[0] == "length":
return True
return False
# Checks and updates prefix if the next ID in self.IDs is a local variable or a parameter
def checkLocalVar(self):
if not self.IDs:
return True
ID = self.IDs[0]
localVarNode = self.env.findNode(ID, "expr")
if localVarNode:
self.addToPrefix(localVarNode)
return True
return False
# Checks and updates prefix if the next ID in self.IDs is in the contains set
# TODO: figure out how to do the resolution after type checking is done
def checkContains(self, update=False):
if not self.IDs:
return True
ID = self.IDs[0]
if self.env.findNode(ID, "fieldDcl") or self.env.findNode(ID, "method"):
self.prefixLink = "contain"
self.addToPrefix("contain")
return True
return False
# Finding a static field
def checkStatic(self):
if not self.IDs:
return True
currPrefix = ""
for index, ID in enumerate(self.IDs):
if currPrefix:
currPrefix += "."
currPrefix += ID
try:
typeNode = self.env.getNode(currPrefix, "type")
except Exception as e:
continue
# checking if the next ID is a static field in the class/interface
if index+1 >= len(self.IDs):
return False
# if the next field is not a method inovocation
# (it could only be a method invocation if self.methodinvoke is true and the next field is the last element in the compID)
if not (self.methodInvoke and index+1 == len(self.IDs) - 1):
staticFieldName = self.IDs[index+1]
typeFieldNode = typeNode.env.getNode(staticFieldName, "fieldDcl")
if "static" in typeFieldNode.mods:
self.prefixLink = typeFieldNode.variableDcl
# if it is primitive, then we leave it as a VarDclNode
if not self.prefixLink.dclType.myType.isPrimitive:
self.prefixLink = self.prefixLink.dclType.myType.typePointer
self.prefix = currPrefix + "." + staticFieldName
self.IDs = self.IDs[index+2:]
# if protected, check if we have access to it
if "protected" in typeFieldNode.mods:
checkProtected(typeFieldNode, self)
return True
return False
elif self.methodInvoke and index+1 == len(self.IDs) - 1:
# TODO set the most recent? is this the correct implementation we want?
self.prefixLink = typeNode
self.prefix = currPrefix
self.IDs = self.IDs[index+1:]
return True
return False
# Finds the shortest prefix that either is a local variable, instance field or a static field.
# Raises error if no such prefix is found in the environment
# ASSUMPTION: only the left most nodes in the AST
# (e.g. if there's sth like a.b.c().d, there would be 2 parse tree nodes: methodInvoke(a.b.c) and fieldAccess(d),
# then disambigName is only called on the methodInvoke node)
def disambigName(self):
# Checking if a1 is "this"
if self.checkThis():
self.pointToThis = True
return
# Checking if a1 is length
if self.checkLength():
return
# Checking if a1 is a local variable
if self.checkLocalVar():
return
# Checking if a1 is in contains set
if self.checkContains():
self.pointToThis = True
return
# Checking if the shortest prefix is a static field
if self.checkStatic():
self.shouldBeStatic = True
return
raise Exception("ERROR at disambiguating namespace: no prefix of name {} is found in environment.".format(self.name))
def checkType(self):
# type checking: go through each prefix and determine what type it is, get that type, and check if that type contains the next access
# eg: a.b.c.d - disambigName would have already determined what the heck the shortest prefix is for this, so take that (let's say it's a.b) then check type c, see if it contains d, then get d return type and add it to self.myType
if not self.prefixLink or self.prefixLink == 'contain':
self.prefixLink = self
curType = self.prefixLink
if self.IDs:
while self.IDs:
if len(self.IDs) == 1:
if self.methodInvoke:
if curType.__class__.__name__ in ['ParamNode', 'VarDclNode']:
curType = curType.myType.typePointer
curType = curType.env.getNode(self.IDs[0], 'method')
self.methodName = self.IDs[0]
else:
if curType.myType and curType.myType.isArray and self.IDs[0] == 'length':
self.myType = TypeStruct("int", None)
return
else:
if curType.__class__.__name__ in ['ParamNode', 'VarDclNode']:
curType = curType.myType.typePointer
curType = curType.env.getNode(self.IDs[0], 'fieldDcl')
# for methods, we want to keep prefixLink pointing to the class for getting method later
else:
curType = curType.env.getNode(self.IDs[0], 'fieldDcl')
# at this stage, all newly resolved field should be non static:
if curType.__class__.__name__ == 'FieldNode':
if 'static' in curType.mods:
raise Exception("ERROR: Non-static access of static field {}".format(curType.name))
# if protected, check if we have access to it
if "protected" in curType.mods:
checkProtected(curType, self)
self.prefix = self.prefix + "." + self.IDs[0]
self.IDs.pop(0)
self.prefixLink = curType
# if self.prefixLink.__class__.__name__ == 'ParamNode':
# from pprint import pprint
# pprint(vars(self.prefixLink))
# self.prefixLink = self.prefixLink.myType.typePointer
if self.methodInvoke:
self.myType = 'TObeResolved'
else:
self.myType = self.prefixLink.myType
if not self.myType:
# from pprint import pprint
# pprint(vars(self.prefixLink))
# pprint(vars(self))
raise Exception("ERROR: Cannot check type of name {}".format(self.name))
# helper
def checkProtected(dcl, usage):
# get curType's class it was declared in
typeFieldNodeClass = dcl.env.getNode(dcl.typeName, "type")
# get current class we're in
curClass = usage.env.getNode(usage.typeName, "type")
# check to see if curType's class is in the current class' super list
if typeFieldNodeClass.canonName != curClass.canonName and typeFieldNodeClass.canonName not in getSupers(curClass) and typeFieldNodeClass.packageName != curClass.packageName:
raise Exception("ERROR: Class {} is attempting to access a protected field from class {}".format(curClass.canonName, typeFieldNodeClass.canonName))
from AST import ASTNode, getParseTreeNodes
from TheTypeNode import TypeStruct, getSupers
import MemberNodes
import TypeNodes
from Environment import Env
# name nodes: contains compID and IDs
# TODO:
# Optional stuff:
# clean up findNode vs getNode (remove findNode if getNode works properly)[optional]
# if sth is None in line Node, don't append to children
# add comments for what is self.typeName at each class (the type name that this node is underneath)
# Necessary:
# 2) checking if remaining compid are instance fields
# a) check if the last ID in the name is a method if methodInvoke == true (might not be necessary) [this is after type checking]
class NameNode(ASTNode):
def __init__(self, parseTree, methodInvoke, typeName):
self.parseTree = parseTree
self.name = "" # str: stores original lex of the name (ID/COMPID)
self.IDs = [] # a queue of strings: each string is an ID in a COMPID/ID.
self.prefix = "" # str: stores the currently resolved/identified prefix in the name
self.prefixLink = None # 3 possible values:
# 1) a pointer to an AST node (i.e. the prefix is resolved)
# 2) "contain" (prefix isn't resolved but is identified)
# 3) None: neither of 1 or 2
self.methodInvoke = methodInvoke # Bool: true if the last ID in the name is a method invokation
self.methodName = ""
self.env = None
self.typeName = typeName # the name of the class or interface that this node belongs under
self.children = []
self.myType = None # will become TypeStruct to tell us what the whole is/returns
self.shouldBeStatic = False
self.pointToThis = False
self.name = getParseTreeNodes(["ID", "COMPID"], parseTree)[0].lex
self.IDs = self.name.split(".")
# Updates the resolved/identified prefix
# ASSUMPTION: will never be called when self.IDs is empty
def addToPrefix(self, node):
# If self.prefixLink is contain, that means our self.prefix wasn't resolved yet
if type(self.prefixLink) is str and self.prefixLink == "contain":
self.prefixLink = node
return
# Otherwise, we update both self.prefix and self.prefixLink, since we have now resolved a new ID
if self.prefix:
self.prefix += "."
self.prefix += self.IDs[0]
self.prefixLink = node
self.IDs.pop(0)
# Checks and updates prefix if the next ID in self.IDs is a "this" pointer
def checkThis(self):
if not self.IDs:
return True
# The scanner makes sure that "this" can only be in the beginning of a compid (e.g. a.b.this.d, or a.b.this are both not allowed)
if self.IDs[0] == "this":
typeNode = self.env.getNode(self.typeName, "type")
self.addToPrefix(typeNode)
return True
return False
def checkLength(self):
if not self.IDs:
return True
if self.IDs[0] == "length":
return True
return False
# Checks and updates prefix if the next ID in self.IDs is a local variable or a parameter
def checkLocalVar(self):
if not self.IDs:
return True
ID = self.IDs[0]
localVarNode = self.env.findNode(ID, "expr")
if localVarNode:
self.addToPrefix(localVarNode)
return True
return False
# Checks and updates prefix if the next ID in self.IDs is in the contains set
# TODO: figure out how to do the resolution after type checking is done
def checkContains(self, update=False):
if not self.IDs:
return True
ID = self.IDs[0]
if self.env.findNode(ID, "fieldDcl") or self.env.findNode(ID, "method"):
self.prefixLink = "contain"
self.addToPrefix("contain")
return True
return False
# Finding a static field
def checkStatic(self):
if not self.IDs:
return True
currPrefix = ""
for index, ID in enumerate(self.IDs):
if currPrefix:
currPrefix += "."
currPrefix += ID
try:
typeNode = self.env.getNode(currPrefix, "type")
except Exception as e:
continue
# checking if the next ID is a static field in the class/interface
if index+1 >= len(self.IDs):
return False
# if the next field is not a method inovocation
# (it could only be a method invocation if self.methodinvoke is true and the next field is the last element in the compID)
if not (self.methodInvoke and index+1 == len(self.IDs) - 1):
staticFieldName = self.IDs[index+1]
typeFieldNode = typeNode.env.getNode(staticFieldName, "fieldDcl")
if "static" in typeFieldNode.mods:
self.prefixLink = typeFieldNode.variableDcl
# if it is primitive, then we leave it as a VarDclNode
if not self.prefixLink.dclType.myType.isPrimitive:
self.prefixLink = self.prefixLink.dclType.myType.typePointer
self.prefix = currPrefix + "." + staticFieldName
self.IDs = self.IDs[index+2:]
# if protected, check if we have access to it
if "protected" in typeFieldNode.mods:
checkProtected(typeFieldNode, self)
return True
return False
elif self.methodInvoke and index+1 == len(self.IDs) - 1:
# TODO set the most recent? is this the correct implementation we want?
self.prefixLink = typeNode
self.prefix = currPrefix
self.IDs = self.IDs[index+1:]
return True
return False
# Finds the shortest prefix that either is a local variable, instance field or a static field.
# Raises error if no such prefix is found in the environment
# ASSUMPTION: only the left most nodes in the AST
# (e.g. if there's sth like a.b.c().d, there would be 2 parse tree nodes: methodInvoke(a.b.c) and fieldAccess(d),
# then disambigName is only called on the methodInvoke node)
def disambigName(self):
# Checking if a1 is "this"
if self.checkThis():
self.pointToThis = True
return
# Checking if a1 is length
if self.checkLength():
return
# Checking if a1 is a local variable
if self.checkLocalVar():
return
# Checking if a1 is in contains set
if self.checkContains():
self.pointToThis = True
return
# Checking if the shortest prefix is a static field
if self.checkStatic():
self.shouldBeStatic = True
return
raise Exception("ERROR at disambiguating namespace: no prefix of name {} is found in environment.".format(self.name))
def checkType(self):
# type checking: go through each prefix and determine what type it is, get that type, and check if that type contains the next access
# eg: a.b.c.d - disambigName would have already determined what the heck the shortest prefix is for this, so take that (let's say it's a.b) then check type c, see if it contains d, then get d return type and add it to self.myType
if not self.prefixLink or self.prefixLink == 'contain':
self.prefixLink = self
curType = self.prefixLink
if self.IDs:
while self.IDs:
if len(self.IDs) == 1:
if self.methodInvoke:
if curType.__class__.__name__ in ['ParamNode', 'VarDclNode', 'FieldNode']:
curType = curType.myType.typePointer
curType = curType.env.getNode(self.IDs[0], 'method')
self.methodName = self.IDs[0]
else:
if curType.myType and curType.myType.isArray and self.IDs[0] == 'length':
self.myType = TypeStruct("int", None)
return
else:
if curType.__class__.__name__ in ['ParamNode', 'VarDclNode']:
curType = curType.myType.typePointer
curType = curType.env.getNode(self.IDs[0], 'fieldDcl')
# for methods, we want to keep prefixLink pointing to the class for getting method later
else:
curType = curType.env.getNode(self.IDs[0], 'fieldDcl')
# at this stage, all newly resolved field should be non static:
if curType.__class__.__name__ == 'FieldNode':
if 'static' in curType.mods:
raise Exception("ERROR: Non-static access of static field {}".format(curType.name))
# if protected, check if we have access to it
if "protected" in curType.mods:
checkProtected(curType, self)
self.prefix = self.prefix + "." + self.IDs[0]
self.IDs.pop(0)
self.prefixLink = curType
# if self.prefixLink.__class__.__name__ == 'ParamNode':
# from pprint import pprint
# pprint(vars(self.prefixLink))
# self.prefixLink = self.prefixLink.myType.typePointer
if self.methodInvoke:
self.myType = 'TObeResolved'
else:
self.myType = self.prefixLink.myType
if not self.myType:
# from pprint import pprint
# pprint(vars(self.prefixLink))
# pprint(vars(self))
raise Exception("ERROR: Cannot check type of name {}".format(self.name))
# helper
def checkProtected(dcl, usage):
# get curType's class it was declared in
typeFieldNodeClass = dcl.env.getNode(dcl.typeName, "type")
# get current class we're in
curClass = usage.env.getNode(usage.typeName, "type")
# check to see if curType's class is in the current class' super list
if typeFieldNodeClass.canonName != curClass.canonName and typeFieldNodeClass.canonName not in getSupers(curClass) and typeFieldNodeClass.packageName != curClass.packageName:
raise Exception("ERROR: Class {} is attempting to access a protected field from class {}".format(curClass.canonName, typeFieldNodeClass.canonName))
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment