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))