diff --git a/AST.py b/AST.py index 885000ea05442e6e2712bfd6b9c618da066a1d69..fbb2702a388b4f85e59647e4c5ccf691f38ca527 100644 --- a/AST.py +++ b/AST.py @@ -14,6 +14,10 @@ class ASTNode(): self.children = [] self.myType = "" # either empty string or a TypeStruct + # reachability: None = not set, True = maybe, False = no + self.inMaybe = None # either None or True/False + self.outMaybe = None # either None or True/False + # Do certains actions on every node of the AST tree # call the same method in each class and its children recursively # the methods that represent an action would return arguments to be used in @@ -64,6 +68,29 @@ class ASTNode(): if c and hasattr(c, 'checkType'): c.checkType() + def staticAnalysis(self): + 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: + if c and hasattr(c, 'reachCheck'): + lastOut = c.reachCheck(lastOut) + self.outMaybe = self.outMaybe and lastOut + + return self.outMaybe + def printNodePretty(self, prefix=0): pp = pprint.PrettyPrinter(indent=prefix) pp.pprint(self.__class__.__name__) diff --git a/AstBuilding.py b/AstBuilding.py index 035a8f9c43a2f6db093c7f7adc1c148777b21b1a..5563a7ba29c93890efc6d715949c8bdeeabf3e41 100644 --- a/AstBuilding.py +++ b/AstBuilding.py @@ -9,8 +9,6 @@ def astBuild(trees): ASTs.append((n, CompNode(t))) return ASTs - - def buildEnvAndLink(ASTs): # build env globalEnv = GlobalEnv() @@ -51,4 +49,13 @@ def disamiguateAndTypeChecking(ASTs): for t in ASTs: t[1].checkType() - # resolving the rest of the name \ No newline at end of file + # resolving the rest of the name + +####################################################### + +def reachabilityChecking(ASTs): + for t in ASTs: + t[1].reachCheck() + + # for t in ASTs: + # t[1].staticAnalysis() diff --git a/ExprPrimaryNodes.py b/ExprPrimaryNodes.py index c1a0593225ea49303d8242f945afc73efd3d7b81..e6b44162c2c9bee4d2abc90f55de9dfc30708993 100644 --- a/ExprPrimaryNodes.py +++ b/ExprPrimaryNodes.py @@ -401,6 +401,59 @@ class ExprNode(ASTNode): 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 diff --git a/LineNodes.py b/LineNodes.py index 89cf11878b4004a351f427c9c23a48180e853d14..30058be18c264c95e1c898912f5d4f41fbe3b677 100644 --- a/LineNodes.py +++ b/LineNodes.py @@ -164,6 +164,27 @@ class IfNode(ASTNode): if self.elseBody: self.elseBody.checkType() + def reachCheck(self, inMaybe = True): + if inMaybe == False: + # error if in[s] = no for any s + raise Exception("in[s] = no for IfNode in class {}".format(self.typeName)) + + self.inMaybe = inMaybe + s1 = self.ifBody.reachCheck(self.inMaybe) + if not self.elseBody: + # L : if (E) S + # in[S] = in[L] + # out[L] = in[L] + self.outMaybe = self.inMaybe + else: + # L : if (E) S1 else S2 + # in[S1] = in[L] + # in[S2] = in[L] + # out[L] = out[S1] V out[S2] + s2 = self.elseBody.reachCheck(self.inMaybe) + self.outMaybe = s1 or s2 + return self.outMaybe + # whileStatement, whileStatementNoShortIf # Rules: # 1. whileStatement WHILE LPAREN expr RPAREN statement @@ -189,6 +210,26 @@ class WhileNode(ASTNode): raise Exception("ERROR: Cannot use non-boolean type for whileBound.") self.whileBody.checkType() + def staticAnalysis(self): + # check constant expr + if hasattr(self.whileBound, "getConstant"): + con = self.whileBound.getConstant() + + if con != None: + print(con, self.whileBound.typeName) + + def reachCheck(self, inMaybe = True): + if inMaybe == False: + # error if in[s] = no for any s + raise Exception("in[s] = no for WhileNode in class {}".format(self.typeName)) + + # L : while (E) S + # in[S] = in[L] + # out[L] = in[L] + self.inMaybe = inMaybe + self.whileBound.reachCheck(self.inMaybe) + self.outMaybe = self.inMaybe + # returnStatement # Rules: # 1. returnStatement RETURN expr SEMICO @@ -217,6 +258,17 @@ class ReturnNode(ASTNode): else: 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): + if inMaybe == False: + # error if in[s] = no for any s + raise Exception("in[s] = no for ReturnNode in class {}".format(self.typeName)) + + # L : return, L : return E + # out[L] = no + self.inMaybe = inMaybe + self.outMaybe = False + return self.outMaybe + # forStatement and forStatementNoShortIf # Rules: # 1. forStatement FOR LPAREN forInit SEMICO forExpr SEMICO forInit RPAREN statement @@ -279,3 +331,10 @@ class ForNode(ASTNode): raise Exception("ERROR: Cannot use non-boolean type for forBound.") self.forUpdate.checkType() self.bodyStatement.checkType() + + def staticAnalysis(self): + # check constant expr + if hasattr(self.forBound, "getConstant"): + con = self.forBound.getConstant() + if con != None: + print(con, self.whileBound.typeNam) diff --git a/MemberNodes.py b/MemberNodes.py index 7a0bb3e89844dd4766598e43684404054b52e6e2..cfafa1adfae0220f1dc632fdc6562fa7e0f38576 100644 --- a/MemberNodes.py +++ b/MemberNodes.py @@ -169,6 +169,20 @@ class MethodNode(ASTNode): raise Exception("ERROR: return type of function {} doesn't match with return statement.".format(self.name)) return + def reachCheck(self, inMaybe = True): + # self.inMaybe is always true for methods + self.inMaybe = True + self.outMaybe = False + + if self.body: + self.outMaybe = self.body.reachCheck() + + # error if out[(non-void) method body] = maybe + # self.methodType is an empty string if it's a constructor + if type(self.methodType) != str and self.methodType.myType.name != "void" and self.outMaybe == True: + raise Exception("Non-void method '{}' in class '{}' does not return".format(self.name, self.typeName)) + return self.outMaybe + ############# helper for forward ref checking ######## # Input: AST Node # Output: A list of names to be check diff --git a/Test.py b/Test.py index 0dec390f9fe1fd9dc118dd146583edfd000d44de..a08be2a913508ac004e0297f812f77447c24e606 100644 --- a/Test.py +++ b/Test.py @@ -6,7 +6,7 @@ import traceback from Scanning import scan from Parsing import parse -from AstBuilding import astBuild, buildEnvAndLink, disamiguateAndTypeChecking +from AstBuilding import astBuild, buildEnvAndLink, disamiguateAndTypeChecking, reachabilityChecking import Weeding @@ -38,7 +38,7 @@ def a2Multiple(): testCases = [f for f in sys.argv[1:]] else: # All files in the test directory - testDirectory = "./Tests/A3/" + testDirectory = "./Tests/A4/" testCases = [f.path for f in scandir(testDirectory) if f.is_dir()] testCases += [f.path for f in scandir(testDirectory) if not f.is_dir()] @@ -48,7 +48,7 @@ def a2Multiple(): for c in testCases: # get all files from stdlib folder - testFiles = [join(dp, f) for dp, dn, filenames in walk('stdlib/3.0/java/') for f in filenames] + testFiles = [join(dp, f) for dp, dn, filenames in walk('stdlib/4.0/java/') for f in filenames] if '.java' in c: # add this one file @@ -97,7 +97,7 @@ def run(testFiles): print("ERROR in Scanning: " + error) print("**********************************************************") return "ERROR in scanning" - + # s = "All Tokens: " # for token in tokens: # if (token.name and token.lex): @@ -155,6 +155,11 @@ def run(testFiles): disamiguateAndTypeChecking(ASTs) except Exception as e: return "disamiguateAndTypeChecking: " + e.args[0] + + try: + reachabilityChecking(ASTs) + except Exception as e: + return "reachabilityChecking: " + e.args[0] return "" diff --git a/TypeNodes.py b/TypeNodes.py index 20cd7a5a001fb394f250ce40be429ea9ecf03dbd..87c79ada6845fed783b92c5cf3153bfd5a7635a7 100644 --- a/TypeNodes.py +++ b/TypeNodes.py @@ -261,7 +261,6 @@ class ClassNode(ClassInterNode): raise Exception("ERROR: Constructor {0} doesn't have the same name as class {1}".format(constructor.name, self.name)) return - ##################################################################### # interface class InterNode(ClassInterNode): diff --git a/UnitNodes.py b/UnitNodes.py index 5a19d39828f153a77abe244e9a5ebb27a83f5a5a..c015457cf708cb5c1ba913c8b155b400d57db5af 100644 --- a/UnitNodes.py +++ b/UnitNodes.py @@ -34,12 +34,21 @@ class LiteralNode(ASTNode): self.value = False else: self.value = True - + def linkType(self): if self.name == "java.lang.String": node = self.env.getNode(self.name, "type") self.myType = TypeStruct(self.name, node) + def getConstant(self): + if self.name == 'int': + return int(self.value) + elif self.name == 'boolean': + if self.value == 'true': + return True + return False + return self.value + ################################################################################## # param type ID class ParamNode(ASTNode):