class MethodScope: def __init__(self, name: str, access_modifier: str, parent: Optional[SymbolTable] = None): """The MethodScope object is responsible for keeping track of the information of a method. Arguments: - name [str]: The name of the method. - access_modifier [str]: Whether the method is public of private. - parent [SymbolTable]: The method's parent class attribute_directory, only Optional for the Global Scope. - parent_memory [Memory]: The memory of the parent class, only Optional for the Global Scope. """ self._name = name self._access_modifier = access_modifier self._parent = parent self._local_memory = Memory(Scopes.LOCAL, ScopeRanges.LOCAL) self._instruction_pointer = None if parent is not None: logger.debug( f"Created MethodScope {access_modifier} {name}, parent {parent.name}") # These are added later, as there could be multiple arguments, # so we parse them one by one, and finally add the return type # which goes after the arguments self._arguments = SymbolTable(f'{name} Arguments', parent) self._ordered_arguments = [] self._return_type = None self._return_memory_address = -1 self._variables_directory = SymbolTable(name, self._arguments) @property def access_modifier(self): return self._access_modifier @property def name(self) -> str: """The name of the class. Returns: - The name [str] of the method. """ return self._name @property def local_memory(self) -> Memory: return self._local_memory @property def variables_directory(self) -> SymbolTable: """The SymbolTable which keeps track of the variables in the method. Returns: - The SymbolTable instance. """ return self._variables_directory @property def return_type(self) -> str: """The return type of the method. Returns: - The return type [str] of the method. """ return self._return_type @property def return_memory_address(self) -> int: """Where the return value of the method will point to in memory. Returns: - The address [int] where the value of the return will be stored. """ return self._return_memory_address @property def ordered_arguments(self): return self._ordered_arguments def add_return_type(self, return_type: str) -> None: """Adds the return type to the method after parsing it. Arguments: - return_type [str]: The return type of the method. """ self._return_type = return_type if return_type != "void": address = CompilationMemory.next_global_memory_space(return_type) self._return_memory_address = Variable(address, return_type, address) def add_argument(self, name: str, arg_type: str) -> None: """Adds an argument to the method. Arguments: - name [str]: The name of the argument. - arg_type [str]: The type of the argument. """ memory_space = self._local_memory.next_memory_space(arg_type) variable = Variable(name, arg_type, memory_space) self._arguments.add_symbol(variable) self._ordered_arguments.append(variable) def add_variable(self, name: str, var_type: str) -> None: """Adds a variable to the method after parsing it. Arguments: - name [str]: The name of the variable. - var_type [str]: The type of the variable. """ # It belongs to global memory if it has no parent. if self._parent is not None: memory_space = self._local_memory.next_memory_space(var_type) else: memory_space = CompilationMemory.next_global_memory_space(var_type) self._variables_directory.add_symbol( Variable(name, var_type, memory_space)) def allocate_memory_chunk(self, variable) -> None: if variable.is_global(): CompilationMemory.next_global_memory_chunk(variable.var_type, variable.size - 1) elif variable.is_local(): self._local_memory.next_memory_chunk(variable.var_type, variable.size - 1) @property def instruction_pointer(self): return self._instruction_pointer @instruction_pointer.setter def instruction_pointer(self, ip): self._instruction_pointer = ip
class ClassScope: # Recursive typing, see: https://stackoverflow.com/a/38341145 def __init__(self, name: str, inherits: Optional["ClassScope"] = None, global_scope: Optional["SymbolTable"] = None): """ClassScope is responsible for keeping track of a classes methods, attributes, and local memory. Arguments: - name [str]: The name of the class. - inherits [ClassScope]: The class scope of the parent class if there is one. """ self._name = name if inherits is None: self._method_directory = SymbolTable(name) self._attribute_directory = SymbolTable(name, global_scope) self._instance_memory = Memory(Scopes.INSTANCE, ScopeRanges.INSTANCE) else: self._method_directory = SymbolTable(name, inherits.method_directory) self._attribute_directory = SymbolTable( name, inherits.attribute_directory) self._instance_memory = inherits.instance_memory @property def name(self) -> str: """The name of the class. Returns: - The name [str] of the class. """ return self._name @property def method_directory(self) -> SymbolTable: """The SymbolTable which keeps track of the methods in the class. Returns: - The SymbolTable instance. """ return self._method_directory @property def attribute_directory(self) -> SymbolTable: """The SymbolTable which keeps track of the attributes in the class. Returns: - The SymbolTable instance. """ return self._attribute_directory @property def instance_memory(self) -> Memory: return self._instance_memory def add_method(self, name: str, access_modifier: str) -> MethodScope: """Adds a method to the class. Arguments: - name [str]: The name of the method. - access_modifier [str]: Whether the method is public or private. Returns: - The MethodScope object created for this method. """ method_scope = MethodScope(name, access_modifier, self._attribute_directory) self._method_directory.add_symbol(method_scope) return method_scope def add_attribute(self, name: str, var_type: str, access_modifier: str) -> None: """Adds an attribute to the class. Arguments: - name [str]: The name of the attribute. - var_type [str]: The type of the attribute. - access_modifier [str]: Whether the attribute is public or private. """ memory_address = self._instance_memory.next_memory_space(var_type) self._attribute_directory.add_symbol( Variable(f"@{name}", var_type, memory_address, access_modifier=access_modifier))