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