Module src.frontend.symbol_table
The module serves to create the symbol table
Expand source code
"""
The module serves to create the symbol table
"""
import os
import re
from importlib.machinery import SourceFileLoader
from collections import namedtuple
from inspect import getsourcefile
lex = SourceFileLoader("lexer", f"{os.path.dirname(os.path.abspath(getsourcefile(lambda:0)))}/lexer.py").load_module()
ast = SourceFileLoader("AST_builder", f"{os.path.dirname(os.path.abspath(getsourcefile(lambda:0)))}/AST_builder.py").load_module()
en_map = {
0 : "Variable",
1 : "Function",
2 : "Parameter",
3 : "Label",
}
class Entry():
"""
A class that represents a singular entry in the symbol table
"""
def __init__(self, en_typ, nam, typ, scop, modif):
"""
Args:
en_typ: Whether the entry is a variable, function, parameter, or label.
nam: Name of the entry.
typ: The associated type of the entry such as `int`, `float` ect. For functions this represents the return type.
scop: The scope where the entry exists.
modif: The modifiers associated with the entry such as `unsigned`, `const`, `static` ect.
"""
self.entry_type = en_typ
self.name = nam
self.type = typ
self.scope = scop
self.references = []
self.modifiers = list(sorted(set(modif)))
Node = namedtuple("Node", ["Node", "Scope"])
Node.__doc__ = """
A simple namedtuple to allow for better readability when performing the depth first search required for the generation of the symbol table.
"""
class symbol_table():
"""
A class that stores all the known and unknown symbols.
"""
def __init__(self,AST):
"""
Args:
AST: The head node of the abstract syntax tree.
"""
self.AST = AST
self.symbols = []
self.undefined = []
self.errors = []
def analyze(self):
"""
Analyses the abstract syntax tree to determine whether there are any undeclared references.
"""
ntv = [Node(self.AST, "/")]
scopenum = 0
typ = None
b = False
# Simple implementation of a DFS
while ntv != []:
# Grabs the first element which will be the residual left most child
cur = ntv[0]
# checks whether the current node is an operation that will need to access the symbol table
try:
index = ["func", "decl", "call", "var", "body"].index(cur.Node.name)
#Catches edge case where var or func is used an self_defined name
if cur.Node.children == []:
ntv = [Node(x, cur.Scope) for x in cur.Node.children if 'children' in x.__dict__] + ntv[1:]
continue
# Function Declaration
if index == 0:
# If a function is declared after main without a prototype
if [x for x in self.symbols if x.name == "main" and x.entry_type == {value: key for key, value in en_map.items()}["Function"]] != [] and [x for x in self.symbols if cur.Node.children[1].name == x.name] == []:
self.undefined.append(Entry({value: key for key, value in en_map.items()}["Function"], cur.Node.children[1].name, cur.Node.children[0].name, cur.Scope, [x.name for x in cur.Node.children[0].children]))
# If a function is declared befor main is declared without a prototype
elif [x for x in self.symbols if x.name == "main" and x.entry_type == {value: key for key, value in en_map.items()}["Function"]] == []:
self.symbols.append(Entry({value: key for key, value in en_map.items()}["Function"], cur.Node.children[1].name, cur.Node.children[0].name, cur.Scope, [x.name for x in cur.Node.children[0].children]))
cur = cur._replace(Scope = cur.Scope + cur.Node.children[1].name + "/")
else:
cur = cur._replace(Scope = cur.Scope + cur.Node.children[1].name + "/")
#Function Prototype Declaration
elif index == 1:
self.symbols.append(Entry({value: key for key, value in en_map.items()}["Function"], cur.Node.children[1].name, cur.Node.children[0].name, cur.Scope, [x.name for x in cur.Node.children[0].children]))
cur = cur._replace(Scope = cur.Scope + cur.Node.children[1].name + "/")
pass
# Function Call
elif index == 2:
if [x for x in self.symbols if x.entry_type == {value: key for key, value in en_map.items()}["Function"] and x.name == cur.Node.children[0].name] == []:
self.undefined.append(Entry({value: key for key, value in en_map.items()}["Function"],cur.Node.children[0].name, "None", cur.Scope, [x.name for x in cur.Node.children[0].children]))
else:
ref = [x for x in self.symbols if en_map[x.entry_type] == "Function" and x.name == cur.Node.children[0].name]
for i in ref:
i.references.append(i)
# Initialization and Usage
elif index == 3:
#declaration of variable
if len(cur.Node.children) > 1:
#add to symbol table this should also handle function param being that they are still within the same scope as there parent function
if([x for x in self.symbols if x.name == cur.Node.children[1].name and x.scope in cur.Scope] == []):
if cur.Node.parent.parent.name == "func" or cur.Node.parent.parent.name == "decl":
if cur.Node.parent.name == "param":
self.symbols.append(Entry({value: key for key, value in en_map.items()}["Parameter"],cur.Node.children[1].name, cur.Node.children[0].name, cur.Scope, [x.name for x in cur.Node.children[0].children]))
else:
self.symbols.append(Entry({value: key for key, value in en_map.items()}["Variable"],cur.Node.children[1].name, cur.Node.children[0].name, cur.Scope, [x.name for x in cur.Node.children[0].children]))
else:
self.symbols.append(Entry({value: key for key, value in en_map.items()}["Variable"],cur.Node.children[1].name, cur.Node.children[0].name, cur.Scope, [x.name for x in cur.Node.children[0].children]))
elif(cur.Node.parent.name != "param"):
print(f'Variable Already Declared {cur.Node.children[1].name} {cur.Node.children[0].name}')
self.undefined.append(Entry({value: key for key, value in en_map.items()}["Variable"], cur.Node.children[1].name, cur.Node.children[0].name, cur.Scope, [x.name for x in cur.Node.children[0].children]))
elif(cur.Node.parent.name == "param" and [x for x in self.symbols if cur.Node.children[1].name == x.name] == []):
print(f'Undeclared parameter{cur.Node.children[1].name} {cur.Node.children[0].name}')
self.undefined.append(Entry({value: key for key, value in en_map.items()}["Variable"], cur.Node.children[1].name, cur.Node.children[0].name, cur.Scope, [x.name for x in cur.Node.children[0].children]))
pass
#usage of varible
else:
if ([x for x in self.symbols if x.name == cur.Node.children[0].name and x.scope in cur.Scope] == []):
print(f'Variable Undeclared {cur.Node.children[0].name}')
self.undefined.append(Entry({value: key for key, value in en_map.items()}["Variable"], cur.Node.children[0].name, "None", cur.Scope, [x.name for x in cur.Node.children[0].children]))
else:
ref = [x for x in self.symbols if x.name == cur.Node.children[0].name and x.scope in cur.Scope]
for i in ref:
i.references.append(i)
elif index == 4:
cur = cur._replace(Scope = f"{cur.Scope}{scopenum}/")
scopenum += 1
#gets all labels declared in body of a function
labels = [x.children[0] for x in cur.Node.children if x.name == "label"]
if labels != []:
#for each label in the body
for i in labels:
#checks if label is already in the symbol table.
if [x for x in self.symbols if x.name == i.name] == []:
self.symbols.append(Entry({value: key for key, value in en_map.items()}["Label"], i.name, "None", cur.Scope, [x.name for x in cur.Node.children[0].children]))
else:
self.undefined.append(Entry({value: key for key, value in en_map.items()}["Label"], i.name, "None", cur.Scope, [x.name for x in cur.Node.children[0].children]))
pass
except ValueError:
# This means that the token is not in that list
pass
# fetches the relevant children of the current node and appends the already known children to the list of residual nodes
ntv = [Node(x, cur.Scope) for x in cur.Node.children if 'children' in x.__dict__] + ntv[1:]
def __str__(self):
li = []
known_len = [
max([len(x.name) for x in self.symbols] + [len("Name")]),
max([len(en_map[x.entry_type]) for x in self.symbols] + [len("Entry Type")]),
max([len(x.type) for x in self.symbols] + [len("Type")]),
max([len(x.scope) for x in self.symbols] + [len("Scope")]),
max([len(", ".join(x.modifiers)) for x in self.symbols] + [len("Modifiers")]),
]
unknown_len = [
max([len(x.name) for x in self.undefined] + [len("Name")]),
max([len(en_map[x.entry_type]) for x in self.undefined] + [len("Entry Type")]),
max([len(x.scope) for x in self.undefined] + [len("Scope")]),
]
li.append("Known Symbols")
li.append(f" {'Name':^{known_len[0]}} | {'Entry Type':^{known_len[1]}} | {'Type':^{known_len[2]}} | {'Scope':^{known_len[3]}} | {'Modifiers':^{known_len[4]}} ")
li.append(f"-{'-'*known_len[0]}-+-{'-'*known_len[1]}-+-{'-'*known_len[2]}-+-{'-'*known_len[3]}-+-{'-'*known_len[4]}-")
for x in self.symbols:
li.append(f" {x.name:>{known_len[0]}} | {en_map[x.entry_type]:>{known_len[1]}} | {x.type :>{known_len[2]}} | {x.scope :<{known_len[3]}} | {', '.join(x.modifiers) :>{known_len[4]}}")
li.append("")
li.append("Unknown Symbols")
li.append(f" {'Name':^{unknown_len[0]}} | {'Entry Type':^{unknown_len[1]}} | {'Type':^{unknown_len[2]}}")
li.append(f"-{'-'*unknown_len[0]}-+-{'-'*unknown_len[1]}-+-{'-'*unknown_len[2]}-")
for x in self.undefined:
li.append(f" {x.name:>{unknown_len[0]}} | {en_map[x.entry_type]:>{unknown_len[1]}} | {x.type :>{unknown_len[2]}}")
return "\n".join(li) + "\n"
def __repr__(self):
return "\n".join(self.errors)
def print_symbol_table(self):
"""
Prints the known symbols in the symbol table to stdout
"""
col_lengths = [
max([len(x.name) for x in self.symbols] + [len("Name")]),
max([len(en_map[x.entry_type]) for x in self.symbols] + [len("Entry Type")]),
max([len(x.type) for x in self.symbols] + [len("Type")]),
max([len(x.scope) for x in self.symbols] + [len("Scope")]),
max([len(", ".join(x.modifiers)) for x in self.symbols] + [len("Modifiers")]),
]
print ("Known Symbols")
print (f" {'Name':^{col_lengths[0]}} | {'Entry Type':^{col_lengths[1]}} | {'Type':^{col_lengths[2]}} | {'Scope':^{col_lengths[3]}} | {'Modifiers':^{col_lengths[4]}} ")
print (f"-{'-'*col_lengths[0]}-+-{'-'*col_lengths[1]}-+-{'-'*col_lengths[2]}-+-{'-'*col_lengths[3]}-+-{'-'*col_lengths[4]}-")
for x in self.symbols:
print(f" {x.name:>{col_lengths[0]}} | {en_map[x.entry_type]:>{col_lengths[1]}} | {x.type :>{col_lengths[2]}} | {x.scope :<{col_lengths[3]}} | {', '.join(x.modifiers) :>{col_lengths[4]}}")
def print_unknown_symbols(self):
"""
Prints the unknown symbols in the symbol table to stdout
"""
col_lengths = [
max([len(x.name) for x in self.undefined] + [len("Name")]),
max([len(en_map[x.entry_type]) for x in self.undefined] + [len("Entry Type")]),
max([len(x.scope) for x in self.undefined] + [len("Scope")]),
]
print ("Unknown Symbols")
print (f" {'Name':^{col_lengths[0]}} | {'Entry Type':^{col_lengths[1]}} | {'Type':^{col_lengths[2]}}")
print (f"-{'-'*col_lengths[0]}-+-{'-'*col_lengths[1]}-+-{'-'*col_lengths[2]}-")
for x in self.undefined:
print(f" {x.name:>{col_lengths[0]}} | {en_map[x.entry_type]:>{col_lengths[1]}} | {x.type :>{col_lengths[2]}}")
Classes
class Entry (en_typ, nam, typ, scop, modif)
-
A class that represents a singular entry in the symbol table
Args
en_typ
- Whether the entry is a variable, function, parameter, or label.
nam
- Name of the entry.
typ
- The associated type of the entry such as
int
,float
ect. For functions this represents the return type. scop
- The scope where the entry exists.
modif
- The modifiers associated with the entry such as
unsigned
,const
,static
ect.
Expand source code
class Entry(): """ A class that represents a singular entry in the symbol table """ def __init__(self, en_typ, nam, typ, scop, modif): """ Args: en_typ: Whether the entry is a variable, function, parameter, or label. nam: Name of the entry. typ: The associated type of the entry such as `int`, `float` ect. For functions this represents the return type. scop: The scope where the entry exists. modif: The modifiers associated with the entry such as `unsigned`, `const`, `static` ect. """ self.entry_type = en_typ self.name = nam self.type = typ self.scope = scop self.references = [] self.modifiers = list(sorted(set(modif)))
class Node (Node, Scope)
-
A simple namedtuple to allow for better readability when performing the depth first search required for the generation of the symbol table.
Ancestors
- builtins.tuple
Instance variables
var Node
-
Alias for field number 0
var Scope
-
Alias for field number 1
class symbol_table (AST)
-
A class that stores all the known and unknown symbols.
Args
AST
- The head node of the abstract syntax tree.
Expand source code
class symbol_table(): """ A class that stores all the known and unknown symbols. """ def __init__(self,AST): """ Args: AST: The head node of the abstract syntax tree. """ self.AST = AST self.symbols = [] self.undefined = [] self.errors = [] def analyze(self): """ Analyses the abstract syntax tree to determine whether there are any undeclared references. """ ntv = [Node(self.AST, "/")] scopenum = 0 typ = None b = False # Simple implementation of a DFS while ntv != []: # Grabs the first element which will be the residual left most child cur = ntv[0] # checks whether the current node is an operation that will need to access the symbol table try: index = ["func", "decl", "call", "var", "body"].index(cur.Node.name) #Catches edge case where var or func is used an self_defined name if cur.Node.children == []: ntv = [Node(x, cur.Scope) for x in cur.Node.children if 'children' in x.__dict__] + ntv[1:] continue # Function Declaration if index == 0: # If a function is declared after main without a prototype if [x for x in self.symbols if x.name == "main" and x.entry_type == {value: key for key, value in en_map.items()}["Function"]] != [] and [x for x in self.symbols if cur.Node.children[1].name == x.name] == []: self.undefined.append(Entry({value: key for key, value in en_map.items()}["Function"], cur.Node.children[1].name, cur.Node.children[0].name, cur.Scope, [x.name for x in cur.Node.children[0].children])) # If a function is declared befor main is declared without a prototype elif [x for x in self.symbols if x.name == "main" and x.entry_type == {value: key for key, value in en_map.items()}["Function"]] == []: self.symbols.append(Entry({value: key for key, value in en_map.items()}["Function"], cur.Node.children[1].name, cur.Node.children[0].name, cur.Scope, [x.name for x in cur.Node.children[0].children])) cur = cur._replace(Scope = cur.Scope + cur.Node.children[1].name + "/") else: cur = cur._replace(Scope = cur.Scope + cur.Node.children[1].name + "/") #Function Prototype Declaration elif index == 1: self.symbols.append(Entry({value: key for key, value in en_map.items()}["Function"], cur.Node.children[1].name, cur.Node.children[0].name, cur.Scope, [x.name for x in cur.Node.children[0].children])) cur = cur._replace(Scope = cur.Scope + cur.Node.children[1].name + "/") pass # Function Call elif index == 2: if [x for x in self.symbols if x.entry_type == {value: key for key, value in en_map.items()}["Function"] and x.name == cur.Node.children[0].name] == []: self.undefined.append(Entry({value: key for key, value in en_map.items()}["Function"],cur.Node.children[0].name, "None", cur.Scope, [x.name for x in cur.Node.children[0].children])) else: ref = [x for x in self.symbols if en_map[x.entry_type] == "Function" and x.name == cur.Node.children[0].name] for i in ref: i.references.append(i) # Initialization and Usage elif index == 3: #declaration of variable if len(cur.Node.children) > 1: #add to symbol table this should also handle function param being that they are still within the same scope as there parent function if([x for x in self.symbols if x.name == cur.Node.children[1].name and x.scope in cur.Scope] == []): if cur.Node.parent.parent.name == "func" or cur.Node.parent.parent.name == "decl": if cur.Node.parent.name == "param": self.symbols.append(Entry({value: key for key, value in en_map.items()}["Parameter"],cur.Node.children[1].name, cur.Node.children[0].name, cur.Scope, [x.name for x in cur.Node.children[0].children])) else: self.symbols.append(Entry({value: key for key, value in en_map.items()}["Variable"],cur.Node.children[1].name, cur.Node.children[0].name, cur.Scope, [x.name for x in cur.Node.children[0].children])) else: self.symbols.append(Entry({value: key for key, value in en_map.items()}["Variable"],cur.Node.children[1].name, cur.Node.children[0].name, cur.Scope, [x.name for x in cur.Node.children[0].children])) elif(cur.Node.parent.name != "param"): print(f'Variable Already Declared {cur.Node.children[1].name} {cur.Node.children[0].name}') self.undefined.append(Entry({value: key for key, value in en_map.items()}["Variable"], cur.Node.children[1].name, cur.Node.children[0].name, cur.Scope, [x.name for x in cur.Node.children[0].children])) elif(cur.Node.parent.name == "param" and [x for x in self.symbols if cur.Node.children[1].name == x.name] == []): print(f'Undeclared parameter{cur.Node.children[1].name} {cur.Node.children[0].name}') self.undefined.append(Entry({value: key for key, value in en_map.items()}["Variable"], cur.Node.children[1].name, cur.Node.children[0].name, cur.Scope, [x.name for x in cur.Node.children[0].children])) pass #usage of varible else: if ([x for x in self.symbols if x.name == cur.Node.children[0].name and x.scope in cur.Scope] == []): print(f'Variable Undeclared {cur.Node.children[0].name}') self.undefined.append(Entry({value: key for key, value in en_map.items()}["Variable"], cur.Node.children[0].name, "None", cur.Scope, [x.name for x in cur.Node.children[0].children])) else: ref = [x for x in self.symbols if x.name == cur.Node.children[0].name and x.scope in cur.Scope] for i in ref: i.references.append(i) elif index == 4: cur = cur._replace(Scope = f"{cur.Scope}{scopenum}/") scopenum += 1 #gets all labels declared in body of a function labels = [x.children[0] for x in cur.Node.children if x.name == "label"] if labels != []: #for each label in the body for i in labels: #checks if label is already in the symbol table. if [x for x in self.symbols if x.name == i.name] == []: self.symbols.append(Entry({value: key for key, value in en_map.items()}["Label"], i.name, "None", cur.Scope, [x.name for x in cur.Node.children[0].children])) else: self.undefined.append(Entry({value: key for key, value in en_map.items()}["Label"], i.name, "None", cur.Scope, [x.name for x in cur.Node.children[0].children])) pass except ValueError: # This means that the token is not in that list pass # fetches the relevant children of the current node and appends the already known children to the list of residual nodes ntv = [Node(x, cur.Scope) for x in cur.Node.children if 'children' in x.__dict__] + ntv[1:] def __str__(self): li = [] known_len = [ max([len(x.name) for x in self.symbols] + [len("Name")]), max([len(en_map[x.entry_type]) for x in self.symbols] + [len("Entry Type")]), max([len(x.type) for x in self.symbols] + [len("Type")]), max([len(x.scope) for x in self.symbols] + [len("Scope")]), max([len(", ".join(x.modifiers)) for x in self.symbols] + [len("Modifiers")]), ] unknown_len = [ max([len(x.name) for x in self.undefined] + [len("Name")]), max([len(en_map[x.entry_type]) for x in self.undefined] + [len("Entry Type")]), max([len(x.scope) for x in self.undefined] + [len("Scope")]), ] li.append("Known Symbols") li.append(f" {'Name':^{known_len[0]}} | {'Entry Type':^{known_len[1]}} | {'Type':^{known_len[2]}} | {'Scope':^{known_len[3]}} | {'Modifiers':^{known_len[4]}} ") li.append(f"-{'-'*known_len[0]}-+-{'-'*known_len[1]}-+-{'-'*known_len[2]}-+-{'-'*known_len[3]}-+-{'-'*known_len[4]}-") for x in self.symbols: li.append(f" {x.name:>{known_len[0]}} | {en_map[x.entry_type]:>{known_len[1]}} | {x.type :>{known_len[2]}} | {x.scope :<{known_len[3]}} | {', '.join(x.modifiers) :>{known_len[4]}}") li.append("") li.append("Unknown Symbols") li.append(f" {'Name':^{unknown_len[0]}} | {'Entry Type':^{unknown_len[1]}} | {'Type':^{unknown_len[2]}}") li.append(f"-{'-'*unknown_len[0]}-+-{'-'*unknown_len[1]}-+-{'-'*unknown_len[2]}-") for x in self.undefined: li.append(f" {x.name:>{unknown_len[0]}} | {en_map[x.entry_type]:>{unknown_len[1]}} | {x.type :>{unknown_len[2]}}") return "\n".join(li) + "\n" def __repr__(self): return "\n".join(self.errors) def print_symbol_table(self): """ Prints the known symbols in the symbol table to stdout """ col_lengths = [ max([len(x.name) for x in self.symbols] + [len("Name")]), max([len(en_map[x.entry_type]) for x in self.symbols] + [len("Entry Type")]), max([len(x.type) for x in self.symbols] + [len("Type")]), max([len(x.scope) for x in self.symbols] + [len("Scope")]), max([len(", ".join(x.modifiers)) for x in self.symbols] + [len("Modifiers")]), ] print ("Known Symbols") print (f" {'Name':^{col_lengths[0]}} | {'Entry Type':^{col_lengths[1]}} | {'Type':^{col_lengths[2]}} | {'Scope':^{col_lengths[3]}} | {'Modifiers':^{col_lengths[4]}} ") print (f"-{'-'*col_lengths[0]}-+-{'-'*col_lengths[1]}-+-{'-'*col_lengths[2]}-+-{'-'*col_lengths[3]}-+-{'-'*col_lengths[4]}-") for x in self.symbols: print(f" {x.name:>{col_lengths[0]}} | {en_map[x.entry_type]:>{col_lengths[1]}} | {x.type :>{col_lengths[2]}} | {x.scope :<{col_lengths[3]}} | {', '.join(x.modifiers) :>{col_lengths[4]}}") def print_unknown_symbols(self): """ Prints the unknown symbols in the symbol table to stdout """ col_lengths = [ max([len(x.name) for x in self.undefined] + [len("Name")]), max([len(en_map[x.entry_type]) for x in self.undefined] + [len("Entry Type")]), max([len(x.scope) for x in self.undefined] + [len("Scope")]), ] print ("Unknown Symbols") print (f" {'Name':^{col_lengths[0]}} | {'Entry Type':^{col_lengths[1]}} | {'Type':^{col_lengths[2]}}") print (f"-{'-'*col_lengths[0]}-+-{'-'*col_lengths[1]}-+-{'-'*col_lengths[2]}-") for x in self.undefined: print(f" {x.name:>{col_lengths[0]}} | {en_map[x.entry_type]:>{col_lengths[1]}} | {x.type :>{col_lengths[2]}}")
Methods
def analyze(self)
-
Analyses the abstract syntax tree to determine whether there are any undeclared references.
Expand source code
def analyze(self): """ Analyses the abstract syntax tree to determine whether there are any undeclared references. """ ntv = [Node(self.AST, "/")] scopenum = 0 typ = None b = False # Simple implementation of a DFS while ntv != []: # Grabs the first element which will be the residual left most child cur = ntv[0] # checks whether the current node is an operation that will need to access the symbol table try: index = ["func", "decl", "call", "var", "body"].index(cur.Node.name) #Catches edge case where var or func is used an self_defined name if cur.Node.children == []: ntv = [Node(x, cur.Scope) for x in cur.Node.children if 'children' in x.__dict__] + ntv[1:] continue # Function Declaration if index == 0: # If a function is declared after main without a prototype if [x for x in self.symbols if x.name == "main" and x.entry_type == {value: key for key, value in en_map.items()}["Function"]] != [] and [x for x in self.symbols if cur.Node.children[1].name == x.name] == []: self.undefined.append(Entry({value: key for key, value in en_map.items()}["Function"], cur.Node.children[1].name, cur.Node.children[0].name, cur.Scope, [x.name for x in cur.Node.children[0].children])) # If a function is declared befor main is declared without a prototype elif [x for x in self.symbols if x.name == "main" and x.entry_type == {value: key for key, value in en_map.items()}["Function"]] == []: self.symbols.append(Entry({value: key for key, value in en_map.items()}["Function"], cur.Node.children[1].name, cur.Node.children[0].name, cur.Scope, [x.name for x in cur.Node.children[0].children])) cur = cur._replace(Scope = cur.Scope + cur.Node.children[1].name + "/") else: cur = cur._replace(Scope = cur.Scope + cur.Node.children[1].name + "/") #Function Prototype Declaration elif index == 1: self.symbols.append(Entry({value: key for key, value in en_map.items()}["Function"], cur.Node.children[1].name, cur.Node.children[0].name, cur.Scope, [x.name for x in cur.Node.children[0].children])) cur = cur._replace(Scope = cur.Scope + cur.Node.children[1].name + "/") pass # Function Call elif index == 2: if [x for x in self.symbols if x.entry_type == {value: key for key, value in en_map.items()}["Function"] and x.name == cur.Node.children[0].name] == []: self.undefined.append(Entry({value: key for key, value in en_map.items()}["Function"],cur.Node.children[0].name, "None", cur.Scope, [x.name for x in cur.Node.children[0].children])) else: ref = [x for x in self.symbols if en_map[x.entry_type] == "Function" and x.name == cur.Node.children[0].name] for i in ref: i.references.append(i) # Initialization and Usage elif index == 3: #declaration of variable if len(cur.Node.children) > 1: #add to symbol table this should also handle function param being that they are still within the same scope as there parent function if([x for x in self.symbols if x.name == cur.Node.children[1].name and x.scope in cur.Scope] == []): if cur.Node.parent.parent.name == "func" or cur.Node.parent.parent.name == "decl": if cur.Node.parent.name == "param": self.symbols.append(Entry({value: key for key, value in en_map.items()}["Parameter"],cur.Node.children[1].name, cur.Node.children[0].name, cur.Scope, [x.name for x in cur.Node.children[0].children])) else: self.symbols.append(Entry({value: key for key, value in en_map.items()}["Variable"],cur.Node.children[1].name, cur.Node.children[0].name, cur.Scope, [x.name for x in cur.Node.children[0].children])) else: self.symbols.append(Entry({value: key for key, value in en_map.items()}["Variable"],cur.Node.children[1].name, cur.Node.children[0].name, cur.Scope, [x.name for x in cur.Node.children[0].children])) elif(cur.Node.parent.name != "param"): print(f'Variable Already Declared {cur.Node.children[1].name} {cur.Node.children[0].name}') self.undefined.append(Entry({value: key for key, value in en_map.items()}["Variable"], cur.Node.children[1].name, cur.Node.children[0].name, cur.Scope, [x.name for x in cur.Node.children[0].children])) elif(cur.Node.parent.name == "param" and [x for x in self.symbols if cur.Node.children[1].name == x.name] == []): print(f'Undeclared parameter{cur.Node.children[1].name} {cur.Node.children[0].name}') self.undefined.append(Entry({value: key for key, value in en_map.items()}["Variable"], cur.Node.children[1].name, cur.Node.children[0].name, cur.Scope, [x.name for x in cur.Node.children[0].children])) pass #usage of varible else: if ([x for x in self.symbols if x.name == cur.Node.children[0].name and x.scope in cur.Scope] == []): print(f'Variable Undeclared {cur.Node.children[0].name}') self.undefined.append(Entry({value: key for key, value in en_map.items()}["Variable"], cur.Node.children[0].name, "None", cur.Scope, [x.name for x in cur.Node.children[0].children])) else: ref = [x for x in self.symbols if x.name == cur.Node.children[0].name and x.scope in cur.Scope] for i in ref: i.references.append(i) elif index == 4: cur = cur._replace(Scope = f"{cur.Scope}{scopenum}/") scopenum += 1 #gets all labels declared in body of a function labels = [x.children[0] for x in cur.Node.children if x.name == "label"] if labels != []: #for each label in the body for i in labels: #checks if label is already in the symbol table. if [x for x in self.symbols if x.name == i.name] == []: self.symbols.append(Entry({value: key for key, value in en_map.items()}["Label"], i.name, "None", cur.Scope, [x.name for x in cur.Node.children[0].children])) else: self.undefined.append(Entry({value: key for key, value in en_map.items()}["Label"], i.name, "None", cur.Scope, [x.name for x in cur.Node.children[0].children])) pass except ValueError: # This means that the token is not in that list pass # fetches the relevant children of the current node and appends the already known children to the list of residual nodes ntv = [Node(x, cur.Scope) for x in cur.Node.children if 'children' in x.__dict__] + ntv[1:]
def print_symbol_table(self)
-
Prints the known symbols in the symbol table to stdout
Expand source code
def print_symbol_table(self): """ Prints the known symbols in the symbol table to stdout """ col_lengths = [ max([len(x.name) for x in self.symbols] + [len("Name")]), max([len(en_map[x.entry_type]) for x in self.symbols] + [len("Entry Type")]), max([len(x.type) for x in self.symbols] + [len("Type")]), max([len(x.scope) for x in self.symbols] + [len("Scope")]), max([len(", ".join(x.modifiers)) for x in self.symbols] + [len("Modifiers")]), ] print ("Known Symbols") print (f" {'Name':^{col_lengths[0]}} | {'Entry Type':^{col_lengths[1]}} | {'Type':^{col_lengths[2]}} | {'Scope':^{col_lengths[3]}} | {'Modifiers':^{col_lengths[4]}} ") print (f"-{'-'*col_lengths[0]}-+-{'-'*col_lengths[1]}-+-{'-'*col_lengths[2]}-+-{'-'*col_lengths[3]}-+-{'-'*col_lengths[4]}-") for x in self.symbols: print(f" {x.name:>{col_lengths[0]}} | {en_map[x.entry_type]:>{col_lengths[1]}} | {x.type :>{col_lengths[2]}} | {x.scope :<{col_lengths[3]}} | {', '.join(x.modifiers) :>{col_lengths[4]}}")
def print_unknown_symbols(self)
-
Prints the unknown symbols in the symbol table to stdout
Expand source code
def print_unknown_symbols(self): """ Prints the unknown symbols in the symbol table to stdout """ col_lengths = [ max([len(x.name) for x in self.undefined] + [len("Name")]), max([len(en_map[x.entry_type]) for x in self.undefined] + [len("Entry Type")]), max([len(x.scope) for x in self.undefined] + [len("Scope")]), ] print ("Unknown Symbols") print (f" {'Name':^{col_lengths[0]}} | {'Entry Type':^{col_lengths[1]}} | {'Type':^{col_lengths[2]}}") print (f"-{'-'*col_lengths[0]}-+-{'-'*col_lengths[1]}-+-{'-'*col_lengths[2]}-") for x in self.undefined: print(f" {x.name:>{col_lengths[0]}} | {en_map[x.entry_type]:>{col_lengths[1]}} | {x.type :>{col_lengths[2]}}")