From ea1020292db7a8653d736ec2c4c91bee1caa45ca Mon Sep 17 00:00:00 2001
From: Nicholas Robinson <nwrobins@edu.uwaterloo.ca>
Date: Thu, 27 Feb 2020 23:22:30 -0500
Subject: [PATCH] Hierarchy stuff

- some more class stuff
---
 MemberNodes.py |  2 ++
 TypeNodes.py   | 57 ++++++++++++++++++++++++++++++++++++++++++--------
 UnitNodes.py   |  3 +++
 3 files changed, 53 insertions(+), 9 deletions(-)

diff --git a/MemberNodes.py b/MemberNodes.py
index 5571bbc..759f721 100644
--- a/MemberNodes.py
+++ b/MemberNodes.py
@@ -77,6 +77,8 @@ class MethodNode(ASTNode):
 
         if self.body: self.children.append(self.body)
 
+    def __eq__(self, other):
+        return self.name == other.name and self.paramTypes == other.paramTypes
 
     def buildEnv(self, parentEnv):
         env = Env(parentEnv)
diff --git a/TypeNodes.py b/TypeNodes.py
index 2af75cf..fb22369 100644
--- a/TypeNodes.py
+++ b/TypeNodes.py
@@ -63,8 +63,6 @@ class ClassNode(ASTNode):
 
     def linkType(self):
         # link types to the actual nodes fom the environment (envs already created)
-        # to use: self.env.getNode(name, namespace)
-        # print('hello here we go we are in classNode {}'.format(self.name))
         if self.env is not None:
             if self.superClass:
                 newSuperClass = self.env.getNode(self.superClass, 'type')
@@ -77,28 +75,66 @@ class ClassNode(ASTNode):
     def checkHierarchy(self):
         if self.superClass:
             # A class must not extend an interface.
-            if isinstance(self.superClass, InterNode):
+            if not isinstance(self.superClass, ClassNode):
                 raise Exception("ERROR: Class {} extends interface {}".format(self.name, self.superClass.name))
             # A class must not extend a final class.
             if 'final' in self.superClass.mods:
                 raise Exception("ERROR: Class {} extends final class {}".format(self.name, self.superClass.name))
-        # A class must not implement a class.
+        # A class must not implement a class. No duplicate interfaces.
         if self.superInter:
+            unique = []
             for inter in self.superInter:
-                if isinstance(inter, ClassNode):
+                if not isinstance(inter, InterNode):
                     raise Exception("ERROR: Class {} implements class {}".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)
         
         # A class or interface must not declare two methods with the same signature (name and parameter types).
-        uniqueMethods = []
+        unique = []
         for method in self.methods:
             key = (method.name, method.paramTypes)
-            if key in uniqueMethods:
+            if key in unique:
                 raise Exception("ERROR: Class {} declares 2 methods with the same signature {}".format(self.name, key[0]))
-            uniqueMethods.append(key)
-        # A class or interface must not contain (declare or inherit) two methods with the same signature but different return types
+            unique.append(key)
+
         # A class must not declare two constructors with the same parameter types
+        unique = []
+        for cons in self.constructors:
+            key = (cons.name, cons.paramTypes)
+            if key in unique:
+                raise Exception("ERROR: Class {} declares 2 constructors with the same parameter types".format(self.name))
+            unique.append(key)
         
+        # A class or interface must not contain (declare or inherit) two methods with the same signature but different return types
+        # The hierarchy must be acyclic
+        contains = self.getContains([])
         # 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:
+                raise Exception("ERROR: Non-abstract Class {} contains an abstract method".format(self.name))
+
+    # 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
+        if self.superClass:
+            superContains = self.superClass.getContains(hierarchy)
+            for con in superContains:
+                conOverwritten = False
+                for method in self.methods:
+                    if (method == con):
+                        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))
+                        conOverwritten = True
+                        break
+                if not conOverwritten:
+                    contains.append(con)
+        return contains
 
     def getConstructor(self, argTypes):
         for c in self.constructors:
@@ -147,3 +183,6 @@ class InterNode(ASTNode):
                 for (index, inter) in enumerate(self.superInter):
                     newSuperInter = self.env.getNode(inter, 'type')
                     self.superInter[index] = newSuperInter
+
+    def checkHierarchy(self):
+        pass
diff --git a/UnitNodes.py b/UnitNodes.py
index 65bd3c6..4764238 100644
--- a/UnitNodes.py
+++ b/UnitNodes.py
@@ -69,3 +69,6 @@ class TypeNode(ASTNode):
             nameNodes = getParseTreeNodes(['LSQRBRACK'], parseTree)
             if nameNodes:
                 self.isArray = True
+
+    def __eq__(self, other):
+        return self.name == other.name
\ No newline at end of file
-- 
GitLab