From 101e58b8fe092d5c44cbca1fcfcd8413579f5118 Mon Sep 17 00:00:00 2001
From: Nicholas Robinson <nwrobins@edu.uwaterloo.ca>
Date: Wed, 11 Mar 2020 01:52:22 -0400
Subject: [PATCH] reachability basics

- change Test.py to run reachabilityChecking
- basic ASTNode reachCheck
- implemented reachability in: MethodNode, IfNode, WhileNode, ReturnNode
- tiny bit of cleanup (whitespace)
---
 AST.py         | 22 ++++++++++++++++++++++
 AstBuilding.py | 13 ++++++++++---
 LineNodes.py   | 43 +++++++++++++++++++++++++++++++++++++++++++
 MemberNodes.py | 14 ++++++++++++++
 Test.py        | 12 +++++++-----
 TypeNodes.py   |  1 -
 6 files changed, 96 insertions(+), 9 deletions(-)

diff --git a/AST.py b/AST.py
index 5e8ed05..fbb2702 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
@@ -69,6 +73,24 @@ class ASTNode():
             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 035a8f9..5563a7b 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/LineNodes.py b/LineNodes.py
index 8a0b2a4..30058be 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
@@ -197,6 +218,17 @@ class WhileNode(ASTNode):
             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:
@@ -226,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
diff --git a/MemberNodes.py b/MemberNodes.py
index 7a0bb3e..cfafa1a 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 de43b30..a08be2a 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
 
 
@@ -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
@@ -155,9 +155,11 @@ def run(testFiles):
         disamiguateAndTypeChecking(ASTs)
     except Exception as e:
         return "disamiguateAndTypeChecking: " + e.args[0]
-
-    for t in ASTs:
-        t[1].staticAnalysis()
+    
+    try:
+        reachabilityChecking(ASTs)
+    except Exception as e:
+        return "reachabilityChecking: " + e.args[0]
 
     return ""
 
diff --git a/TypeNodes.py b/TypeNodes.py
index 20cd7a5..87c79ad 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):
-- 
GitLab