Example #1
0
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
Example #2
0
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))