from AST import ASTNode, getParseTreeNodes 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.env = None self.typeName = typeName # the name of the class or interface that this node belongs under self.children = [] 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 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.addToPrefix("contains") 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 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 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))