Skip to content
Snippets Groups Projects
Commit a6488875 authored by Xun Yang's avatar Xun Yang
Browse files

fix namenode Type checking

parent e9910271
No related branches found
No related tags found
2 merge requests!7Alicia a3,!6fix namenode Type checking
......@@ -52,7 +52,7 @@ class ASTNode():
def checkHierarchy(self):
pass
def disambigName(self):
for c in self.children:
if c and hasattr(c, 'disambigName'):
......
......@@ -37,10 +37,7 @@ class Env:
else:
if key in self.map:
raise Exception('ERROR: Declaration of {} is already in current Environment'.format(node.name))
if namespace == 'expr':
self.map[key] = (False, node)
else:
self.map[key] = node
self.map[key] = node
def getNode(self, name, namespace):
......
......@@ -104,7 +104,8 @@ class ArgsNode(ASTNode):
def disambigName(self):
for expr in self.exprs:
helperDisambigName(expr)
expr.disambigName()
# helperDisambigName(expr)
###################################################################################
......@@ -132,8 +133,17 @@ class ArrayAccessNode(ASTNode):
self.children.append(self.index)
def disambigName(self):
helperDisambigName(self.array)
helperDisambigName(self.index)
self.array.disambigName()
self.index.disambigName()
# helperDisambigName(self.array)
# helperDisambigName(self.index)
def checkType(self):
self.array.checkType()
self.index.checkType()
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
......@@ -162,7 +172,16 @@ class ArrayCreateNode(ASTNode):
self.children.append(self.arraySize)
def disambigName(self):
helperDisambigName(self.arraySize)
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
......@@ -187,8 +206,10 @@ class AssignNode(ASTNode):
self.children.append(self.left)
def disambigName(self):
helperDisambigName(self.right)
helperDisambigName(self.left)
self.left.disambigName()
self.right.disambigName()
# helperDisambigName(self.right)
# helperDisambigName(self.left)
##################################################################################
......@@ -207,13 +228,31 @@ class CastNode(ASTNode):
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):
helperDisambigName(self.left)
helperDisambigName(self.right)
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))
###################################################################################
# classInstanceCreate unqualCreate
......@@ -253,8 +292,7 @@ class ClassCreateNode(ASTNode):
su = su.superClass
# get constructor using arg Types
found = False
m = getMethod(classDef.constructors, self.args)
m = getMethod(classDef.constructors, "", self.args)
if m:
self.cons = m
self.myType = self.className.myType
......@@ -287,12 +325,15 @@ class ExprNode(ASTNode):
self.children.append(self.right)
def disambigName(self):
helperDisambigName(self.left)
helperDisambigName(self.right)
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 checkType1(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
......@@ -316,12 +357,12 @@ class ExprNode(ASTNode):
self.myType = TypeStruct("boolean", None)
return
# numeric operations:
elif self.op in ['+', '-', '*', '/']:
elif self.op in ['+', '-', '*', '/', '%']:
self.myType = TypeStruct("int", None)
return
# Boolean operations:
elif self.left.myType.name == 'boolean' and self.right.myType.name == 'boolean':
if self.op in ['&&', '&', '|', '||']:
if self.op in ['&&', '&', '|', '||', '!=', '==']:
self.myType = TypeStruct("boolean", None)
return
# Other Comparisons:
......@@ -337,7 +378,7 @@ class ExprNode(ASTNode):
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.op, self.left.myType.name, self.right.myType.name))
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))
###################################################################################
......@@ -361,7 +402,15 @@ class FieldAccessNode(ASTNode):
def disambigName(self):
if not self.primary: # this implies that the ID has nothing that comes before it
helperDisambigName(self.ID)
# helperDisambigName(self.ID)
self.ID.disambigName()
# self.right.disambigName()
def checkType(self):
self.primary.checkType()
self.ID.prefixLink = self.primary.myType.typePointer
self.ID.checkType()
self.myType = self.ID.myType
......@@ -376,6 +425,7 @@ class MethodInvNode(ASTNode):
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
......@@ -392,9 +442,12 @@ class MethodInvNode(ASTNode):
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:
helperDisambigName(self.ID)
self.ID.disambigName()
# helperDisambigName(self.ID)
if self.args:
self.args.disambigName()
def checkType1(self):
def checkType(self):
# steps of type checking:
# check param's types
# check using the rule for current node
......@@ -407,24 +460,34 @@ class MethodInvNode(ASTNode):
# now that we have the method name, param types, we need to:
# - check if method exists under the class its under
methodClass = self.ID.prefixLink
for m in methodClass.methods:
if m.name == '': # replace with method name trying to call
for i, param in enumerate(self.args.exprs):
if m.params[i].paramType != param.paramType:
found = False
found = True
m = None
if not self.primary:
self.ID.checkType()
m = getMethod(self.ID.prefixLink.values(), self.ID.methodName, self.args)
else:
self.primary.checkType()
m = getMethod(self.primary.myType.typePointer.methods, self.ID.methodName, self.args)
from pprint import pprint
if m:
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.methodName))
################# Helper #######################
def getMethod(methods, args):
def getMethod(methods, methodName, args):
# methodName = "" if it's constructor
for c in methods:
if len(args.exprs) == len(c.params):
if (methodName == "" or c.name == methodName) and len(args.exprs) == len(c.params):
found = True
for i, param in enumerate(args.exprs):
# TODO one of these myTypes has the possibility of being None ????
if c.params[i].paramType.myType and param.myType and c.params[i].paramType.myType != param.myType:
if c.params[i].paramType.myType != param.myType:
found = False
break
if found:
return c
return None
......@@ -121,7 +121,7 @@ class VarDclNode(ASTNode):
env.addtoEnv(self)
return self.env
def checkType1(self):
def checkType(self):
self.myType = self.dclType.myType
if self.variableInit:
self.variableInit.checkType()
......@@ -153,7 +153,7 @@ class IfNode(ASTNode):
self.children.append(self.ifBody)
self.children.append(self.elseBody)
def checkType1(self):
def checkType(self):
self.ifConditional.checkType()
if self.ifConditional.myType.name != 'boolean':
raise Exception("ERROR: Cannot use non-boolean type for ifConditional.")
......@@ -181,7 +181,7 @@ class WhileNode(ASTNode):
self.children.append(self.whileBound)
self.children.append(self.whileBody)
def checkType1(self):
def checkType(self):
self.whileBound.checkType()
if self.whileBound.myType.name != 'boolean':
raise Exception("ERROR: Cannot use non-boolean type for whileBound.")
......@@ -204,7 +204,7 @@ class ReturnNode(ASTNode):
self.children.append(self.expr)
def checkType1(self):
def checkType(self):
if self.expr:
self.expr.checkType()
self.myType = self.expr.myType
......@@ -266,7 +266,7 @@ class ForNode(ASTNode):
self.children.append(self.forUpdate)
self.children.append(self.bodyStatement)
def checkType1(self):
def checkType(self):
self.forInit.checkType()
self.forBound.checkType() # need resolving var declared in forInit to use it in forBound
if self.forBound.myType.name != 'boolean':
......
......@@ -12,7 +12,6 @@ class FieldNode(ASTNode):
def __init__(self, parseTree, typeName, order):
self.parseTree = parseTree
self.name = ''
self.fieldType = ''
self.variableDcl = None
self.mods = []
self.env = None
......@@ -20,15 +19,6 @@ class FieldNode(ASTNode):
self.typeName = typeName
self.order = order
# get field name
nameNodes = getParseTreeNodes(['ID'], parseTree, ['params', 'type', 'methodBody'])
for n in nameNodes:
self.name = n.lex
nameNodes = getParseTreeNodes(['type'], parseTree, ['methodMod', 'methodBody', 'variableInit'])
for n in nameNodes:
self.fieldType = TypeNode(n, self.typeName)
for node in parseTree.children:
if node.name == 'methodMod':
for m in node.children:
......@@ -37,11 +27,17 @@ class FieldNode(ASTNode):
elif node.name == 'variableDcl':
self.variableDcl = VarDclNode(node, self.typeName)
self.name = self.variableDcl.name
self.children.append(self.variableDcl)
def __eq__(self, other):
return self.name == other.name
def checkType(self):
self.variableDcl.checkType()
self.myType = self.variableDcl.myType
###########################################################
# method
......@@ -91,6 +87,7 @@ class MethodNode(ASTNode):
if self.body: self.children.append(self.body)
self.children.append(self.methodType)
self.children.extend(self.params)
def __eq__(self, other):
if self.name == other.name and len(self.params) == len(other.params):
......@@ -108,3 +105,11 @@ class MethodNode(ASTNode):
env.addtoEnv(p)
self.env = env
return env
def checkType(self):
if self.methodType: # constructor
self.myType = self.methodType.myType
for p in self.params:
p.checkType()
if self.body:
self.body.checkType()
......@@ -12,7 +12,7 @@ from Environment import Env
# 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
# 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]
......@@ -23,20 +23,21 @@ class NameNode(ASTNode):
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)
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.name = getParseTreeNodes(["ID", "COMPID"], parseTree)[0].lex
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):
......@@ -44,32 +45,32 @@ class NameNode(ASTNode):
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.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
# 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") # TODO localVarNode is for some reason returning a tuple: (boolean, node) for example (False, <UnitNodes.ParamNode object at 0x7fb353b1e978>). This will need to be fixed in order for NameNode:checkType to work
localVarNode = self.env.findNode(ID, "expr")
if localVarNode:
self.addToPrefix(localVarNode)
return True
......@@ -84,22 +85,23 @@ class NameNode(ASTNode):
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
# Finding a static field
def checkStatic(self):
if not self.IDs:
return True
currPrefix = ""
for index, ID in enumerate(self.IDs):
for index, ID in enumerate(self.IDs):
if currPrefix:
currPrefix += "."
currPrefix += ID
try:
typeNode = self.env.getNode(currPrefix, "type")
except Exception as e:
......@@ -108,17 +110,18 @@ class NameNode(ASTNode):
# 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
# 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
self.prefixLink = typeFieldNode.variableDcl.dclType.typePointer
self.prefix = currPrefix + "." + staticFieldName
self.IDs = self.IDs[index+2:]
return True
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?
......@@ -128,18 +131,18 @@ class NameNode(ASTNode):
return True
return False
# Finds the shortest prefix that either is a local variable, instance field or a static field.
# 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),
# 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():
return
return
# Checking if a1 is a local variable
if self.checkLocalVar():
......@@ -147,7 +150,7 @@ class NameNode(ASTNode):
# Checking if a1 is in contains set
if self.checkContains():
return
return
# Checking if the shortest prefix is a static field
......@@ -160,30 +163,47 @@ class NameNode(ASTNode):
# 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 (self.typeName == "Main"):
# print('\nCheck contents of NameNode')
# print('self.typeName ', self.typeName)
# print('self.name ', self.name)
# print('self.IDs ', self.IDs)
# print('self.prefix ', self.prefix)
# print('self.prefixLink ', self.prefixLink)
# print('self.methodInvoke ', self.methodInvoke)
if self.methodInvoke \
or not self.prefixLink \
or (type(self.prefixLink) is str and self.prefixLink == 'contain') \
or not isinstance(self.prefixLink, ASTNode): # this is regarding the localVarNode issue I pointed out above
return
curType = self.prefixLink
if self.IDs: # go through remaining ids
for ID in self.IDs: # curType is a type
curType = curType.env.getNode(ID, 'fieldDcl')
self.myType = curType.fieldType.myType
if not self.prefixLink or self.prefixLink == 'contain':
self.prefixLink = self
curType = self.prefixLink
else: # found the node for the whole call already
# find the return type for the whole thing
if isinstance(self.prefixLink, MemberNodes.FieldNode):
self.myType = self.prefixLink.fieldType.myType
if self.IDs:
while self.IDs:
if len(self.IDs) == 1:
if self.methodInvoke:
if curType.__class__.__name__ == 'ParamNode':
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:
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')
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))
from AST import ASTNode, getParseTreeNodes
from MemberNodes import FieldNode, MethodNode
from Environment import Env
from TheTypeNode import TypeStruct
# types: class, interface
......@@ -141,6 +142,12 @@ class ClassInterNode(ASTNode):
if isinstance(i, FieldNode):
print(i.name)
def checkType(self):
self.myType = TypeStruct(self.canonName, self)
for c in self.children:
if c and hasattr(c, 'checkType'):
c.checkType()
# class
class ClassNode(ClassInterNode):
# always list all fields in the init method to show the class structure
......
......@@ -46,3 +46,6 @@ class ParamNode(ASTNode):
self.env = None
self.children = [self.paramType]
self.typeName = typeName
def checkType(self):
self.myType = self.paramType.myType
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