Skip to content
Snippets Groups Projects
NameNode.py 5.67 KiB
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))