from AST import ASTNode, getParseTreeNodes from TheTypeNode import TypeStruct import MemberNodes import TypeNodes from Environment import Env # name nodes: contains compID and IDs # TODO: # Optional stuff: # clean up findNode vs getNode (remove findNode if getNode works properly)[optional] # if sth is None in line Node, don't append to children # add comments for what is self.typeName at each class (the type name that this node is underneath) # Necessary: # 2) checking if remaining compid are instance fields # a) check if the last ID in the name is a method if methodInvoke == true (might not be necessary) [this is after type checking] class NameNode(ASTNode): def __init__(self, parseTree, methodInvoke, typeName): self.parseTree = parseTree self.name = "" # str: stores original lex of the name (ID/COMPID) self.IDs = [] # a queue of strings: each string is an ID in a COMPID/ID. self.prefix = "" # str: stores the currently resolved/identified prefix in the name self.prefixLink = None # 3 possible values: # 1) a pointer to an AST node (i.e. the prefix is resolved) # 2) "contain" (prefix isn't resolved but is identified) # 3) None: neither of 1 or 2 self.methodInvoke = methodInvoke # Bool: true if the last ID in the name is a method invokation self.methodName = "" 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 self.IDs = self.name.split(".") # Updates the resolved/identified prefix # 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 type(self.prefixLink) is str and self.prefixLink == "contain": self.prefixLink = node return # Otherwise, we update both self.prefix and self.prefixLink, since we have now resolved a new ID if self.prefix: self.prefix += "." self.prefix += self.IDs[0] self.prefixLink = node self.IDs.pop(0) # Checks and updates prefix if the next ID in self.IDs is a "this" pointer def checkThis(self): if not self.IDs: return True # The scanner makes sure that "this" can only be in the beginning of a compid (e.g. a.b.this.d, or a.b.this are both not allowed) if self.IDs[0] == "this": typeNode = self.env.getNode(self.typeName, "type") self.addToPrefix(typeNode) return True return False # Checks and updates prefix if the next ID in self.IDs is a local variable or a parameter def checkLocalVar(self): if not self.IDs: return True ID = self.IDs[0] localVarNode = self.env.findNode(ID, "expr") if localVarNode: self.addToPrefix(localVarNode) return True return False # Checks and updates prefix if the next ID in self.IDs is in the contains set # TODO: figure out how to do the resolution after type checking is done def checkContains(self, update=False): if not self.IDs: return True ID = self.IDs[0] if self.env.findNode(ID, "fieldDcl") or self.env.findNode(ID, "method"): self.prefixLink = "contain" self.addToPrefix("contain") return True return False # Finding a static field def checkStatic(self): if not self.IDs: return True currPrefix = "" for index, ID in enumerate(self.IDs): if currPrefix: currPrefix += "." currPrefix += ID 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.variableDcl # if it is primitive, then we leave it as a VarDclNode if not self.prefixLink.dclType.myType.isPrimitive: self.prefixLink = self.prefixLink.dclType.myType.typePointer 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 # Finds the shortest prefix that either is a local variable, instance field or a static field. # Raises error if no such prefix is found in the environment # ASSUMPTION: only the left most nodes in the AST # (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 # Checking if a1 is a local variable if self.checkLocalVar(): return # Checking if a1 is in contains set if self.checkContains(): return # 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 not self.prefixLink or self.prefixLink == 'contain': self.prefixLink = self curType = self.prefixLink if self.IDs: while self.IDs: if len(self.IDs) == 1: if self.methodInvoke: if curType.__class__.__name__ in ['ParamNode', 'VarDclNode']: curType = curType.myType.typePointer curType = curType.env.getNode(self.IDs[0], 'method') self.methodName = self.IDs[0] else: if curType.myType and curType.myType.isArray and self.IDs[0] == 'length': self.myType = TypeStruct("int", None) return else: if curType.__class__.__name__ in ['ParamNode', 'VarDclNode']: curType = curType.myType.typePointer curType = curType.env.getNode(self.IDs[0], 'fieldDcl') # for methods, we want to keep prefixLink pointing to the class for getting method later else: curType = curType.env.getNode(self.IDs[0], 'fieldDcl') self.prefix = self.prefix + "." + self.IDs[0] self.IDs.pop(0) self.prefixLink = curType # if self.prefixLink.__class__.__name__ == 'ParamNode': # from pprint import pprint # pprint(vars(self.prefixLink)) # self.prefixLink = self.prefixLink.myType.typePointer if self.methodInvoke: self.myType = 'TObeResolved' else: self.myType = self.prefixLink.myType if not self.myType: # from pprint import pprint # pprint(vars(self.prefixLink)) # pprint(vars(self)) raise Exception("ERROR: Cannot check type of name {}".format(self.name))