From c19c45bed64c3f2cf7326f64d39d04b02d41faf8 Mon Sep 17 00:00:00 2001
From: Nicholas Robinson <nwrobins@edu.uwaterloo.ca>
Date: Fri, 28 Feb 2020 15:42:44 -0500
Subject: [PATCH] centralize class & interface logic

---
 TypeNodes.py | 132 +++++++++++++++++++++++++++------------------------
 1 file changed, 69 insertions(+), 63 deletions(-)

diff --git a/TypeNodes.py b/TypeNodes.py
index 744c1e2..05bb041 100644
--- a/TypeNodes.py
+++ b/TypeNodes.py
@@ -4,8 +4,61 @@ from Environment import Env
 
 # types: class , interface
 
+class ClassInterNode(ASTNode):    
+    def checkHierarchy(self):
+        # 3. An interface must not be repeated in an implements clause
+        if self.superInter:
+            unique = []
+            for inter in self.superInter:
+                if inter.name in unique:
+                    raise Exception("ERROR: Class/Interface '{}' implements duplicate interfaces '{}'".format(self.name, inter.name))
+                unique.append(inter.name)
+        
+        # 7. A class or interface must not declare two methods with the same signature (name and parameter types).
+        unique = []
+        for method in self.methods:
+            key = (method.name, method.paramTypes)
+            if key in unique:
+                raise Exception("ERROR: Class/Interface '{}' declares 2 methods with the same signature '{}'".format(self.name, key[0]))
+            unique.append(key)
+        
+        # 6. The hierarchy must be acyclic
+        # 9. A class or interface must not contain (declare or inherit) two methods with the same signature but different return types
+        # 11. A nonstatic method must not replace a static method
+        # 13. A protected method must not replace a public method
+        # 14. A method must not replace a final method
+        return self.getContains([])
+
+    def getContains(self, hierarchy):        
+        # check if not acyclic
+        if self.name in hierarchy:
+            raise Exception("ERROR: The hierarchy is not acyclic '{}', saw '{}'".format(hierarchy, self.name))
+        hierarchy.append(self.name)
+
+        # get contains
+        contains = self.methods
+        
+        for inter in self.superInter:
+            superContains = inter.getContains(hierarchy)
+            for con in superContains:
+                conOverwritten = False
+                for method in self.methods:
+                    if (method == con):
+                        # cannot have same signiture but different return types
+                        if (method.methodType != con.methodType):
+                            raise Exception("ERROR: Class '{}' contains 2 methods '{}' with the same signature but different return types".format(self.name, method.name))
+                        # protected must not replace public
+                        if 'protected' in method.mods and 'public' in con.mods:
+                            raise Exception("ERROR: Protected method '{}' in class '{}' replaces public method '{}' in class {}".format(method.name, self.name, con.name, inter.name))
+                        conOverwritten = True
+                        break
+                if not conOverwritten:
+                    contains.append(con)
+        
+        return contains
+
 # class
-class ClassNode(ASTNode):
+class ClassNode(ClassInterNode):
     # always list all fields in the init method to show the class structure
     def __init__(self, parseTree):
         self.parseTree = parseTree
@@ -88,17 +141,7 @@ class ClassNode(ASTNode):
             for inter in self.superInter:
                 if not isinstance(inter, InterNode):
                     raise Exception("ERROR: Class '{}' implements non-interface '{}'".format(self.name, inter.name))
-                if inter.name in unique:
-                    raise Exception("ERROR: Class '{}' implements duplicate interfaces '{}'".format(self.name, inter.name))
                 unique.append(inter.name)
-        
-        # 7. A class or interface must not declare two methods with the same signature (name and parameter types).
-        unique = []
-        for method in self.methods:
-            key = (method.name, method.paramTypes)
-            if key in unique:
-                raise Exception("ERROR: Class '{}' declares 2 methods with the same signature '{}'".format(self.name, key[0]))
-            unique.append(key)
 
         # 8. A class must not declare two constructors with the same parameter types
         unique = []
@@ -108,12 +151,9 @@ class ClassNode(ASTNode):
                 raise Exception("ERROR: Class '{}' declares 2 constructors with the same parameter types".format(self.name))
             unique.append(key)
         
-        # 6. The hierarchy must be acyclic
-        # 9. A class or interface must not contain (declare or inherit) two methods with the same signature but different return types
-        # 11. A nonstatic method must not replace a static method
-        # 13. A protected method must not replace a public method
-        # 14. A method must not replace a final method
-        contains = self.getContains([])
+        # centralized point for overlapping class & interface logic
+        contains = super().checkHierarchy()
+
         # 10. A class that contains (declares or inherits) any abstract methods must be abstract.
         for con in contains:
             if 'abstract' in con.mods and 'abstract' not in self.mods:
@@ -121,20 +161,16 @@ class ClassNode(ASTNode):
 
     # hierarchy: string[]
     def getContains(self, hierarchy):
-        # TODO: move this to a more centralized spot for both ClassNode and InterNode?
-        # TODO: need to check all of the implemented interfaces as well ***
+        # centralized logic
+        contains = super().getContains(hierarchy)
         
-        # check if not acyclic
-        if self.name in hierarchy:
-            raise Exception("ERROR: The hierarchy is not acyclic '{}', saw '{}'".format(hierarchy, self.name))
-        hierarchy.append(self.name)
-        # get contains
-        contains = self.methods
+        # get contains from extends class
         if self.superClass:
+            addToContains = []
             superContains = self.superClass.getContains(hierarchy)
             for con in superContains:
                 conOverwritten = False
-                for method in self.methods:
+                for method in contains:
                     if (method == con):
                         # cannot have same signiture but different return types
                         if (method.methodType != con.methodType):
@@ -151,7 +187,8 @@ class ClassNode(ASTNode):
                         conOverwritten = True
                         break
                 if not conOverwritten:
-                    contains.append(con)
+                    addToContains.append(con)
+            contains += addToContains
         return contains
 
     def getConstructor(self, argTypes):
@@ -161,7 +198,7 @@ class ClassNode(ASTNode):
 
 #####################################################################
 # interface
-class InterNode(ASTNode):
+class InterNode(ClassInterNode):
     # always list all fields in the init method to show the class structure
     def __init__(self, parseTree):
         self.parseTree = parseTree
@@ -214,41 +251,10 @@ class InterNode(ASTNode):
                     raise Exception("ERROR: Interface '{}' extends duplicate interfaces '{}'".format(self.name, inter.name))
                 unique.append(inter.name)
 
-        # 7. A class or interface must not declare two methods with the same signature (name and parameter types).
-        unique = []
-        for method in self.methods:
-            key = (method.name, method.paramTypes)
-            if key in unique:
-                raise Exception("ERROR: Interface '{}' declares 2 methods with the same signature '{}'".format(self.name, key[0]))
-            unique.append(key)
-
-        # 6. The hierarchy must be acyclic
-        # 9. A class or interface must not contain (declare or inherit) two methods with the same signature but different return types
-        # 13. A protected method must not replace a public method
-        contains = self.getContains([])
+        # centralized point for overlapping class & interface logic
+        contains = super().checkHierarchy()
 
     # hierarchy: string[]
     def getContains(self, hierarchy):
-        # check if not acyclic
-        if self.name in hierarchy:
-            raise Exception("ERROR: The hierarchy is not acyclic '{}'".format(hierarchy))
-        hierarchy.append(self.name)
-        # get contains
-        contains = self.methods
-        for inter in self.superInter:
-            superContains = inter.getContains(hierarchy)
-            for con in superContains:
-                conOverwritten = False
-                for method in self.methods:
-                    if (method == con):
-                        # cannot have same signiture but different return types
-                        if (method.methodType != con.methodType):
-                            raise Exception("ERROR: Interface '{}' contains 2 methods '{}' with the same signature but different return types".format(self.name, method.name))
-                        # protected must not replace public
-                        if 'protected' in method.mods and 'public' in con.mods:
-                            raise Exception("ERROR: Protected method '{}' in class '{}' replaces public method '{}' in class {}".format(method.name, self.name, con.name, inter.name))
-                        conOverwritten = True
-                        break
-                if not conOverwritten:
-                    contains.append(con)
-        return contains
\ No newline at end of file
+        # centralized logic
+        return super().getContains(hierarchy)
-- 
GitLab