From 5095c3ae83f7589c6ea0eef2412f49d19c773433 Mon Sep 17 00:00:00 2001
From: Nicholas Robinson <nwrobins@edu.uwaterloo.ca>
Date: Fri, 6 Mar 2020 04:51:55 -0500
Subject: [PATCH] NameNode & disambigName & checkType stuff

- added fieldType to FieldNode
- added myType to NameNode
- fixed NameNode:checkStatic
- recursively calling disambigName exactly the same as how we do
checkType
- starting NameNode:checkType
- cleaned up Test.py commented code
---
 AST.py              |  4 ++-
 AstBuilding.py      |  5 ++-
 Environment.py      |  1 -
 ExprPrimaryNodes.py |  6 ++--
 MemberNodes.py      |  5 +++
 NameNode.py         | 88 ++++++++++++++++++++++++++++++++-------------
 Test.py             | 35 +++++-------------
 7 files changed, 86 insertions(+), 58 deletions(-)

diff --git a/AST.py b/AST.py
index 75aedd1..b7c55c7 100644
--- a/AST.py
+++ b/AST.py
@@ -54,7 +54,9 @@ class ASTNode():
         pass
     
     def disambigName(self):
-        pass
+        for c in self.children:
+            if c and hasattr(c, 'disambigName'):
+                c.disambigName()
 
     def checkType(self):
         # self is type correct if all its children are type correct (no exception raised)
diff --git a/AstBuilding.py b/AstBuilding.py
index 4ac9259..5a8bb39 100644
--- a/AstBuilding.py
+++ b/AstBuilding.py
@@ -42,9 +42,6 @@ def buildEnvAndLink(ASTs):
     for t in ASTs:
         t[1].recurseAction("checkHierarchy")
 
-    for t in ASTs:
-        t[1].checkType()
-
 #######################################################
 
 def disamiguateAndTypeChecking(ASTs):
@@ -58,5 +55,7 @@ def disamiguateAndTypeChecking(ASTs):
         t[1].disambigName()
     
     # type checking
+    for t in ASTs:
+        t[1].checkType()
 
     # resolving the rest of the name
\ No newline at end of file
diff --git a/Environment.py b/Environment.py
index c99dc59..41a0e32 100644
--- a/Environment.py
+++ b/Environment.py
@@ -170,7 +170,6 @@ class CompEnv(Env):
 
     def getNode(self, name, namespace):
         key = (name, namespace)
-
         if key in self.map:
             return self.map.get(key)
         elif self.parentEnv: # globalEnv
diff --git a/ExprPrimaryNodes.py b/ExprPrimaryNodes.py
index 2db92b0..54cf3bb 100644
--- a/ExprPrimaryNodes.py
+++ b/ExprPrimaryNodes.py
@@ -29,7 +29,7 @@ def makeNodeFromExpr(parseTree, typeName):
         if c.name == 'primaryAndArray':
             return makeNodeFromAllPrimary(c, typeName)
         elif c.name == 'ID' or c.name == 'COMPID':
-            return NameNode(c, typeName, typeName)
+            return NameNode(c, False, typeName) # TODO is this always False??
         elif c.name == 'assignment':
             return AssignNode(c, typeName)
         elif c.name == 'refType':
@@ -357,6 +357,7 @@ class FieldAccessNode(ASTNode):
         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
@@ -386,10 +387,11 @@ class MethodInvNode(ASTNode):
 
         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 '.' in self.ID:
+            if isinstance(self.ID, NameNode) and len(self.ID.IDs) > 1:
                 helperDisambigName(self.ID)
 
     def checkType1(self):
diff --git a/MemberNodes.py b/MemberNodes.py
index e3caccc..8b9a72c 100644
--- a/MemberNodes.py
+++ b/MemberNodes.py
@@ -12,6 +12,7 @@ class FieldNode(ASTNode):
     def __init__(self, parseTree, typeName, order):
         self.parseTree = parseTree
         self.name = ''
+        self.fieldType = ''
         self.variableDcl = None
         self.mods = []
         self.env = None
@@ -24,6 +25,10 @@ class FieldNode(ASTNode):
         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:
diff --git a/NameNode.py b/NameNode.py
index d2ce960..4654e2f 100644
--- a/NameNode.py
+++ b/NameNode.py
@@ -1,4 +1,7 @@
 from AST import ASTNode, getParseTreeNodes
+from TheTypeNode import TypeStruct
+import MemberNodes
+import TypeNodes
 from Environment import Env
 
 # name nodes: contains compID and IDs
@@ -28,6 +31,7 @@ class NameNode(ASTNode):
         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 
@@ -37,7 +41,7 @@ class NameNode(ASTNode):
     # 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 self.prefixLink == "contain":
+        if type(self.prefixLink) is str and self.prefixLink == "contain":
             self.prefixLink = node
             return
         
@@ -65,7 +69,7 @@ class NameNode(ASTNode):
         if not self.IDs:
             return True
         ID = self.IDs[0]
-        localVarNode = self.env.findNode(ID, "expr")
+        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
         if localVarNode:
             self.addToPrefix(localVarNode)
             return True
@@ -80,7 +84,7 @@ class NameNode(ASTNode):
 
         ID = self.IDs[0]
         if self.env.findNode(ID, "fieldDcl") or self.env.findNode(ID, "method"):
-            self.addToPrefix("contains")
+            self.addToPrefix("contain")
             return True
         return False
 
@@ -95,25 +99,33 @@ class NameNode(ASTNode):
             if currPrefix:
                 currPrefix += "."
             currPrefix += ID
-
-            typeNode = self.env.getNode(currPrefix, "type")
-            if typeNode:
-
-                # 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)):
-                    staticFieldName = self.IDs[index+1]
-                    typeFieldNode = typeNode.env.getNode(staticFieldName, "fieldDcl")
-                    if "static" in typeFieldNode.mods:
-                        self.prefixLink = typeFieldNode
-                        self.prefix = currPrefix + "." + staticFieldName
-                        self.IDs = self.IDs[index+2:]
-                        return True    
-                    return False
+            
+            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
+                    self.prefix = currPrefix + "." + staticFieldName
+                    self.IDs = self.IDs[index+2:]
+                    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
         
@@ -125,7 +137,6 @@ class NameNode(ASTNode):
     #             (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 
@@ -142,8 +153,37 @@ class NameNode(ASTNode):
         # Checking if the shortest prefix is a static field
         if self.checkStatic():
             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 (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
+
 
+        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
diff --git a/Test.py b/Test.py
index 6fa1c55..5e1195a 100644
--- a/Test.py
+++ b/Test.py
@@ -59,7 +59,6 @@ def a2Multiple():
         total += 1
 
         if ret == "":
-            # print(c)
             if 'Je_' in c:
                 print(c)
                 print("JE Passed without error")
@@ -82,7 +81,6 @@ def run(testFiles):
     parseTrees = []
 
     for f in testFiles:
-        # print(f)
         if f.split("/")[-1] == ".DS_Store":
             continue
         content = open(f, "r").read()
@@ -120,7 +118,6 @@ def run(testFiles):
             (tree, error) = parse(tokens)
         except:
             print("Exception in Parsing")
-        # print(tree)
 
         # Error in Parsing
         if tree is None:
@@ -134,15 +131,11 @@ def run(testFiles):
 
         parseTrees.append((f, tree))
 
-        # print("Scan and Parse Succeeded")
-        # print("**********************************************************")
+    # for (n, t) in parseTrees:
+    #     if n == "Tests/A3/J1_accessstaticfield/Main.java":
+    #         print(n)
+    #         print(t)
 
-    # try:
-    #     ASTs = astBuild(parseTrees)
-    # except Exception as e:
-    #     print("ERROR at AST building for file {}".format(f))
-    #     traceback.print_stack()
-    #     return "building AST: " + e.args[0]
     ASTs = astBuild(parseTrees)
 
     # for (n, t) in ASTs:
@@ -154,25 +147,13 @@ def run(testFiles):
     try:
         buildEnvAndLink(ASTs)
     except Exception as e:
-        # print(testFiles)
-        # print("error at environment building and linking")
-        # print(e)
         return "buildEnvAndLink: " + e.args[0]
 
-    # print("<<<<------- after buildEnvAndLink -------->>>>>>")
-    # for (n, t) in ASTs:
-    #     print(n)
-    #     print("--------------------")
-    #     t.printTree()
-    #     print("\n \n\n \n")
+    try:
+        disamiguateAndTypeChecking(ASTs)
+    except Exception as e:
+        return "disamiguateAndTypeChecking: " + e.args[0]
 
-    # try:
-    #     disamiguateAndTypeChecking(ASTs)
-    # except Exception as e:
-    #     return "Disambiguate and Type checking: " + e.args[0]
-    disamiguateAndTypeChecking(ASTs)
-    # print("Succeeded")
-    # print("**********************************************************")
     return ""
 
 main()
-- 
GitLab