Skip to content
Snippets Groups Projects
Commit cd7407ce authored by Pui Yan Chloe Sham's avatar Pui Yan Chloe Sham
Browse files

Merge branch 'reachabilityCheck' into 'master'

implemented reachability checking. passing 56 test cases locally

See merge request !8
parents 84766895 de31ad20
No related branches found
No related tags found
1 merge request!8implemented reachability checking. passing 56 test cases locally
...@@ -68,28 +68,10 @@ class ASTNode(): ...@@ -68,28 +68,10 @@ class ASTNode():
if c and hasattr(c, 'checkType'): if c and hasattr(c, 'checkType'):
c.checkType() c.checkType()
def staticAnalysis(self): def reachCheck(self, inMaybe=True):
for c in self.children:
if c and hasattr(c, 'staticAnalysis'):
c.staticAnalysis()
# return outMaybe
def reachCheck(self, inMaybe = True):
if inMaybe == False:
# error if in[s] = no for any s
# I don't think it should get to here... but maybe?
raise Exception("in[s] = no for a certain {}".format(type(self)))
self.inMaybe = inMaybe
self.outMaybe = self.inMaybe
lastOut = self.inMaybe
for c in self.children: for c in self.children:
if c and hasattr(c, 'reachCheck'): if c and hasattr(c, 'reachCheck'):
lastOut = c.reachCheck(lastOut) c.reachCheck(inMaybe)
self.outMaybe = self.outMaybe and lastOut
return self.outMaybe
def printNodePretty(self, prefix=0): def printNodePretty(self, prefix=0):
pp = pprint.PrettyPrinter(indent=prefix) pp = pprint.PrettyPrinter(indent=prefix)
......
...@@ -49,13 +49,8 @@ def disamiguateAndTypeChecking(ASTs): ...@@ -49,13 +49,8 @@ def disamiguateAndTypeChecking(ASTs):
for t in ASTs: for t in ASTs:
t[1].checkType() t[1].checkType()
# resolving the rest of the name
####################################################### #######################################################
def reachabilityChecking(ASTs): def reachabilityChecking(ASTs):
for t in ASTs: for t in ASTs:
t[1].reachCheck() t[1].reachCheck()
\ No newline at end of file
# for t in ASTs:
# t[1].staticAnalysis()
...@@ -224,6 +224,11 @@ class AssignNode(ASTNode): ...@@ -224,6 +224,11 @@ class AssignNode(ASTNode):
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)) 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 # cast: castExpr LPAREN castType RPAREN unaryNotPlusMinus
...@@ -574,6 +579,11 @@ class MethodInvNode(ASTNode): ...@@ -574,6 +579,11 @@ class MethodInvNode(ASTNode):
else: else:
raise Exception("ERROR: Class {} doesn't have a method {} with given argument types.".format(self.typeName, self.ID.name)) 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 ####################### ################# Helper #######################
......
...@@ -88,6 +88,23 @@ class BlockNode(ASTNode): ...@@ -88,6 +88,23 @@ class BlockNode(ASTNode):
self.env = env self.env = env
return self.env return self.env
def reachCheck(self, inMaybe):
self.inMaybe = inMaybe
if not self.inMaybe:
raise Exception("ERROR: cannot reach block node in class {}".format(self.typeName))
# Checking reachability of each statement
prevOut = self.inMaybe # Note: in[S1] = in[L]
for statement in self.statements:
if statement:
statement.reachCheck(prevOut)
prevOut = statement.outMaybe
else: # checking for empty statements
if not prevOut:
raise Exception("ERROR: empty statement is unreachable at block node for class {}".format(self.typeName))
self.outMaybe = prevOut
return
# variableDcl # variableDcl
# Rules: # Rules:
...@@ -113,12 +130,12 @@ class VarDclNode(ASTNode): ...@@ -113,12 +130,12 @@ class VarDclNode(ASTNode):
# Checking for definite assignment # Checking for definite assignment
if checkAssign: if checkAssign:
if not self.variableInit: if not self.variableInit:
raise Exception("ERROR: local variable declaration {} is not assigned".format(self.name)) raise Exception("ERROR: local variable declaration {} is not assigned in class {}".format(self.name, self.typeName))
# Checking if the local variable appears in it's own intializer # Checking if the local variable appears in it's own intializer
nameNodes = getASTNode(["NameNode"], self.variableInit) nameNodes = getASTNode(["NameNode"], self.variableInit)
for node in nameNodes: for node in nameNodes:
if self.name in node.IDs: if self.name in node.IDs:
raise Exception("ERROR: local variable {} appears in it's own intialization".format(self.name)) raise Exception("ERROR: local variable {} appears in it's own intialization in class {}".format(self.name, self.typeName))
self.myType = self.dclType.myType self.myType = self.dclType.myType
self.children.append(self.dclType) self.children.append(self.dclType)
...@@ -140,6 +157,10 @@ class VarDclNode(ASTNode): ...@@ -140,6 +157,10 @@ class VarDclNode(ASTNode):
if not self.myType.assignable(self.variableInit.myType): if not self.myType.assignable(self.variableInit.myType):
raise Exception("ERROR: Cannot initialize variable of type {} with type {}".format(self.myType.name, self.variableInit.myType.name)) raise Exception("ERROR: Cannot initialize variable of type {} with type {}".format(self.myType.name, self.variableInit.myType.name))
def reachCheck(self, inMaybe):
if not inMaybe:
raise Exception("ERROR: not reaching a variable declaration statement for var {} in class {}".format(self.name, self.typeName))
self.outMaybe = inMaybe
# ifStatement, ifElseStatement, ifElseStatementNoShortIf # ifStatement, ifElseStatement, ifElseStatementNoShortIf
# Rules: # Rules:
...@@ -174,26 +195,31 @@ class IfNode(ASTNode): ...@@ -174,26 +195,31 @@ class IfNode(ASTNode):
if self.elseBody: if self.elseBody:
self.elseBody.checkType() self.elseBody.checkType()
def reachCheck(self, inMaybe = True): def reachCheck(self, inMaybe):
if inMaybe == False: if not inMaybe:
# error if in[s] = no for any s
raise Exception("in[s] = no for IfNode in class {}".format(self.typeName)) raise Exception("in[s] = no for IfNode in class {}".format(self.typeName))
self.inMaybe = inMaybe # No need to check for empty statement, since in[ifBody] = inMaybe
s1 = self.ifBody.reachCheck(self.inMaybe) if self.ifBody:
self.ifBody.reachCheck(inMaybe)
if not self.elseBody: if not self.elseBody:
# L : if (E) S # L : if (E) S
# in[S] = in[L] # in[S] = in[L]
# out[L] = in[L] # out[L] = in[L]
self.outMaybe = self.inMaybe self.outMaybe = inMaybe
else: else:
# L : if (E) S1 else S2 # L : if (E) S1 else S2
# in[S1] = in[L] # in[S1] = in[L]
# in[S2] = in[L] # in[S2] = in[L]
# out[L] = out[S1] V out[S2] # out[L] = out[S1] V out[S2]
s2 = self.elseBody.reachCheck(self.inMaybe) if self.elseBody:
self.outMaybe = s1 or s2 self.elseBody.reachCheck(inMaybe)
return self.outMaybe self.outMaybe = (self.ifBody.outMaybe or self.elseBody.outMaybe)
else:
# no need to check reachability for empty elseBody, since in[elseBody] = in[L]
self.outMaybe = self.ifBody.outMaybe
# whileStatement, whileStatementNoShortIf # whileStatement, whileStatementNoShortIf
# Rules: # Rules:
...@@ -215,30 +241,42 @@ class WhileNode(ASTNode): ...@@ -215,30 +241,42 @@ class WhileNode(ASTNode):
self.children.append(self.whileBody) self.children.append(self.whileBody)
def checkType(self): def checkType(self):
self.whileBound.checkType() if self.whileBound:
if self.whileBound.myType.name != 'boolean': self.whileBound.checkType()
raise Exception("ERROR: Cannot use non-boolean type for whileBound.") if self.whileBound.myType.name != 'boolean':
self.whileBody.checkType() raise Exception("ERROR: Cannot use non-boolean type for whileBound.")
if self.whileBody:
def staticAnalysis(self): self.whileBody.checkType()
# check constant expr
def reachCheck(self, inMaybe):
if not inMaybe:
raise Exception("in[s] = no for WhileNode in class {}".format(self.typeName))
# Checking constant expression in whileBound
con = None # default to None: i.e. not a constant expression
if hasattr(self.whileBound, "getConstant"): if hasattr(self.whileBound, "getConstant"):
con = self.whileBound.getConstant() con = self.whileBound.getConstant()
if con != None: # Setting self.outMaybe
print(con, self.whileBound.typeName) inMaybeWhileBody = inMaybe # the input to reachCheck on whileBody
# General case: while(E) S
def reachCheck(self, inMaybe = True): if con == None:
if inMaybe == False: self.outMaybe = inMaybe
# error if in[s] = no for any s # while(false) S
raise Exception("in[s] = no for WhileNode in class {}".format(self.typeName)) elif con == False or con == 0:
self.outMaybe = inMaybe
# L : while (E) S inMaybeWhileBody = False
# in[S] = in[L] else: # either an integer that's not zero or True
# out[L] = in[L] self.outMaybe = False
self.inMaybe = inMaybe
self.whileBound.reachCheck(self.inMaybe) # Checking reachability on whileBody
self.outMaybe = self.inMaybe if self.whileBody:
self.whileBody.reachCheck(inMaybeWhileBody)
elif not inMaybeWhileBody: # empty block/empty statement that's unreachable
raise Exception("ERROR: unreachable empty statment/block at while node for class {}".format(self.typeName))
return
# returnStatement # returnStatement
# Rules: # Rules:
...@@ -268,16 +306,10 @@ class ReturnNode(ASTNode): ...@@ -268,16 +306,10 @@ class ReturnNode(ASTNode):
else: else:
self.myType = None # this is None as returning a value of type Void is invalid even in a function with type Void self.myType = None # this is None as returning a value of type Void is invalid even in a function with type Void
def reachCheck(self, inMaybe = True): def reachCheck(self, inMaybe):
if inMaybe == False: if not inMaybe:
# error if in[s] = no for any s raise Exception("ERROR: return statement unreachable at class {}".format(self.typeName))
raise Exception("in[s] = no for ReturnNode in class {}".format(self.typeName)) self.outMaybe = False # out[L] = no
# L : return, L : return E
# out[L] = no
self.inMaybe = inMaybe
self.outMaybe = False
return self.outMaybe
# forStatement and forStatementNoShortIf # forStatement and forStatementNoShortIf
# Rules: # Rules:
...@@ -342,9 +374,30 @@ class ForNode(ASTNode): ...@@ -342,9 +374,30 @@ class ForNode(ASTNode):
self.forUpdate.checkType() self.forUpdate.checkType()
self.bodyStatement.checkType() self.bodyStatement.checkType()
def staticAnalysis(self): def reachCheck(self, inMaybe):
# check constant expr if not inMaybe:
raise Exception("in[s] = no for ForNode in class {}".format(self.typeName))
# Checking constant expression in whileBound
con = None # default to None: i.e. not a constant expression
if hasattr(self.forBound, "getConstant"): if hasattr(self.forBound, "getConstant"):
con = self.forBound.getConstant() con = self.forBound.getConstant()
if con != None:
print(con, self.whileBound.typeNam) # Setting self.outMaybe
inMaybeForBody = inMaybe # the input to reachCheck on bodyStatement
# General case
if con == None:
self.outMaybe = inMaybe
# for(false) S
elif con == False or con == 0:
self.outMaybe = inMaybe
inMaybeForBody = False
else: # either an integer that's not zero or True
self.outMaybe = False
# Checking reachability on whileBody
if self.bodyStatement:
self.bodyStatement.reachCheck(inMaybeForBody)
elif inMaybeForBody: # checking if the empty forBody can be reached
raise Exception("ERROR: unreachable empty statement/block at for node at class {}".format(self.typeName))
return
...@@ -169,19 +169,22 @@ class MethodNode(ASTNode): ...@@ -169,19 +169,22 @@ class MethodNode(ASTNode):
raise Exception("ERROR: return type of function {} doesn't match with return statement.".format(self.name)) raise Exception("ERROR: return type of function {} doesn't match with return statement.".format(self.name))
return return
def reachCheck(self, inMaybe = True): def reachCheck(self, inMaybe=True):
# self.inMaybe is always true for methods
self.inMaybe = True
self.outMaybe = False self.outMaybe = False
# Check reachability of method body
# No need to check for empty function body, since the check is done in checkType
if self.body: if self.body:
self.outMaybe = self.body.reachCheck() self.body.reachCheck(True) # For method bodies, in[L] = maybe by default
self.outMaybe = self.body.outMaybe
# error if out[(non-void) method body] = maybe # Check if out[method body] is a maybe for non-void methods
# self.methodType is an empty string if it's a constructor if not self.methodType or self.myType.name == "void": # Omitting the check for constructors and void functions
if type(self.methodType) != str and self.methodType.myType.name != "void" and self.outMaybe == True: return
if self.outMaybe:
raise Exception("Non-void method '{}' in class '{}' does not return".format(self.name, self.typeName)) raise Exception("Non-void method '{}' in class '{}' does not return".format(self.name, self.typeName))
return self.outMaybe return
############# helper for forward ref checking ######## ############# helper for forward ref checking ########
# Input: AST Node # Input: AST Node
......
...@@ -158,7 +158,6 @@ def run(testFiles): ...@@ -158,7 +158,6 @@ def run(testFiles):
disamiguateAndTypeChecking(ASTs) disamiguateAndTypeChecking(ASTs)
except Exception as e: except Exception as e:
return "disamiguateAndTypeChecking: " + e.args[0] return "disamiguateAndTypeChecking: " + e.args[0]
try: try:
reachabilityChecking(ASTs) reachabilityChecking(ASTs)
except Exception as e: except Exception as e:
......
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