class DefinitionsTreeListener(VYPListener):
    def __init__(self):
        self.functionTable = SymbolTable()
        self.classTable = SymbolTable()
        self.currentFunctionId = ''
        self.currentClass: ClassSymbol = None
        # can be function table or method table of current class
        self.currentFunctionTable = self.functionTable
        self.__defineBuiltInFunctions()
        self.__defineBuiltInClasses()

    def getFunctionTable(self):
        return self.functionTable

    def getClassTable(self):
        return self.classTable

    def __defineBuiltInClasses(self):

        objectSymbol = ClassSymbol('Object', StubParentSymbol(),
                                   StaticPartialSymbolTable())
        toStringSymbol = FunctionSymbol('toString', 'string', 'Object')
        getClassSymbol = FunctionSymbol('getClass', 'string', 'Object')
        objectSymbol.methodTable.addSymbol('toString', toStringSymbol)
        objectSymbol.methodTable.addSymbol('getClass', getClassSymbol)
        self.classTable.addSymbol('Object', objectSymbol)

    def defineMethod(self, identifier, dataType):
        definitionSymbol = FunctionSymbol(identifier, dataType,
                                          self.currentClass.id)
        self.functionTable.addSymbol(identifier, definitionSymbol)
        self.currentFunctionId = identifier

    def __defineBuiltInFunctions(self):
        self.defineFunction('print', 'void')

        self.defineFunction('readInt', 'int')

        self.defineFunction('readString', 'string')

        functionSymbol = self.defineFunction('length', 'int')
        definitionSymbol = GeneralSymbol('s', SymbolType.VARIABLE, 'string')
        definitionSymbol.setAsDefined()
        functionSymbol.appendParameter(definitionSymbol)

        functionSymbol = self.defineFunction('subStr', 'string')
        definitionSymbol = GeneralSymbol('s', SymbolType.VARIABLE, 'string')
        definitionSymbol.setAsDefined()
        functionSymbol.appendParameter(definitionSymbol)

        definitionSymbol = GeneralSymbol('i', SymbolType.VARIABLE, 'int')
        definitionSymbol.setAsDefined()
        functionSymbol.appendParameter(definitionSymbol)

        definitionSymbol = GeneralSymbol('n', SymbolType.VARIABLE, 'int')
        definitionSymbol.setAsDefined()
        functionSymbol.appendParameter(definitionSymbol)

    def defineFunction(self, identifier, dataType):
        ownerClass = ""
        if self.currentClass is not None:
            ownerClass = self.currentClass.id
        definitionSymbol = FunctionSymbol(identifier, dataType, ownerClass)
        self.currentFunctionTable.addSymbol(identifier, definitionSymbol)
        self.currentFunctionId = identifier
        return definitionSymbol

    ''' Enter function symbol to global function definitions '''

    def enterFunction_header(self, ctx: VYPParser.Function_headerContext):
        self.defineFunction(ctx.ID().getText(), ctx.variable_type().getText())

    ''' Function parameters need to be inserted into symbol table. If 'void' is 
        used as parameter, no action is needed. This rule is not used anywhere
        else, so this rule is entered only during function definition. '''

    def enterFunction_parameter_definition(
            self, ctx: VYPParser.Function_parametersContext):
        definitionSymbol = GeneralSymbol(ctx.ID().getText(),
                                         SymbolType.VARIABLE,
                                         ctx.variable_type().getText())
        definitionSymbol.setAsDefined()
        self.defineFunctionParameter(definitionSymbol)

    def enterClass_header(self, ctx: VYPParser.Class_headerContext):
        parentClass = self.classTable.getSymbol(ctx.parent_id.text)
        self.defineClass(ctx.class_id.text, parentClass)

    def exitClass_body(self, ctx: VYPParser.Class_bodyContext):
        self.currentClass = None
        self.localSymbolTable = SymbolTable()

    def exitClass_definition(self, ctx: VYPParser.Class_definitionContext):
        self.currentFunctionTable = self.functionTable

    def enterField_definition(self, ctx: VYPParser.Field_definitionContext):
        self.defineField(ctx.ID().getText(), ctx.variable_type().getText())

    def enterMultiple_field_definition(
            self, ctx: VYPParser.Multiple_field_definitionContext):
        self.defineField(ctx.ID().getText(),
                         ctx.parentCtx.variable_type().getText())

    def exitProgram(self, ctx):
        mainSymbol = self.functionTable.getSymbol('main')
        if mainSymbol.dataType != 'void' or len(mainSymbol.parameterList) != 0:
            raise SemanticGeneralError("Wrong definition of 'main' function")
        self.updateFunctionTypes()

    def defineField(self, fieldId, dataType):
        fieldSymbol = GeneralSymbol(fieldId, SymbolType.VARIABLE, dataType)
        self.currentClass.defineField(fieldSymbol)

    def defineClass(self, classId, parentId):
        classSymbol = ClassSymbol(classId, parentId)
        self.classTable.addSymbol(classId, classSymbol)
        self.currentClass = classSymbol
        self.currentFunctionTable = classSymbol.methodTable

    def initializeFunctionSymbolTable(self, identifier):
        self.currentFunctionId = identifier

    def defineFunctionParameter(self, symbol: GeneralSymbol):
        self.currentFunctionTable.getSymbol(
            self.currentFunctionId).appendParameter(symbol)

    def updateFunctionTypes(self):
        self.updateFuntionTypesHelper(self.functionTable.getAllSymbols())
        for classSymbol in self.classTable.getAllSymbols():
            self.updateFuntionTypesHelper(
                classSymbol.methodTable.getAllCurrentSymbols())
            classSymbol.dataType = self.classTable.getSymbol(
                classSymbol.dataType)
            for fieldSymbol in classSymbol.fieldTable.getAllCurrentSymbols():
                if fieldSymbol.dataType not in ['int', 'string'
                                                ] and isinstance(
                                                    fieldSymbol.dataType, str):
                    fieldSymbol.dataType = self.classTable.getSymbol(
                        fieldSymbol.dataType)

    def updateFuntionTypesHelper(self, functionList):
        for function in functionList:
            if function.ownerClass == function.id:
                if function.dataType != 'void' or len(
                        function.parameterList) != 0:
                    raise SemanticGeneralError("Wrong constructor definition")
            if function.dataType not in ['int', 'void', 'string']:
                classSymbol = self.classTable.getSymbol(function.dataType)
                function.dataType = classSymbol
            self.updateFunctionParameterList(function.parameterList)

    def updateFunctionParameterList(self, parameterList):
        for parameter in parameterList.parameters:
            if parameter.dataType in ['int', 'void', 'string']:
                continue
            classSymbol = self.classTable.getSymbol(parameter.dataType)
            parameter.dataType = classSymbol
Exemple #2
0
class SyntacticAnalyzer(object):
    def __init__(self, list_tokens):
        self.list_tokens = list_tokens
        self.index = -1
        self.symbolTable = SymbolTable()  # Semantico
        self.pct = Pct()
        self.scope = 0  # Semantico

    # name_func' == name_func__

    def program(self):

        if self.nextToken().token == 'program':
            self.symbolTable.beginScope()  # Semantico
            if self.nextToken().tokenType == 'Identificador':
                self.symbolTable.addSymbol(self.getCurrentToken().token,
                                           'program')  # Semantico
                if self.nextToken().token == ';':
                    self.var_declarations()
                    self.subprograms_declarations()
                    self.composed_commands()
                    if self.nextToken().token == '.':
                        self.symbolTable.endScope()  # Semantico
                        print('Sucesso')
                    else:
                        self.getCurrentToken()
                        self.syntaxError('.')
                else:
                    self.syntaxError(";")
            else:
                self.syntaxError("Identificador")
        else:
            self.syntaxError("program")

    def var_declarations(self):
        if self.nextToken().token == 'var':
            self.list_var_declarations()
        else:
            self.index -= 1

    def subprograms_declarations(self):
        self.subprograms_declarations__()

    def subprograms_declarations__(self):
        if self.subprogram_declaration():
            if self.nextToken().token == ';':
                self.subprograms_declarations__()
            else:
                self.syntaxError(';')
        else:
            pass

    def subprogram_declaration(self):
        if self.nextToken().token == 'procedure':
            if self.nextToken().tokenType == 'Identificador':
                self.symbolTable.addSymbol(self.getCurrentToken().token,
                                           'procedure')  # Semantico
                self.symbolTable.beginScope()  # Semantico
                self.arguments()
                if self.nextToken().token == ';':
                    self.var_declarations()
                    self.subprograms_declarations()
                    self.composed_commands()
                    return True
                else:
                    self.syntaxError(';')
            else:
                self.syntaxError('Identificador')
        else:
            self.index -= 1
            return False

    def arguments(self):
        if self.nextToken().token == '(':
            self.list_parameters()
            if self.nextToken().token == ')':
                pass
            else:
                self.syntaxError(')')
        else:
            self.index -= 1

    def list_parameters(self):
        self.list_identifiers()  # pode ser nulo
        if self.nextToken().token == ':':
            self._type()
            self.list_parameters__()
        else:
            self.syntaxError(':')

    def list_parameters__(self):
        if self.nextToken().token == ';':
            self.list_identifiers()
            if self.nextToken().token == ':':
                self._type()
                self.list_parameters__()
            else:
                self.syntaxError(':')
        else:
            self.index -= 1

    ## return
    def composed_commands(self):
        if self.nextToken().token == 'begin':
            self.scope += 1  # Semantico
            self.options_commands()
            if self.nextToken().token == 'end':
                self.scope -= 1  # Semantico
                if not self.scope:  # Semantico
                    self.symbolTable.endScope()
                return True
            else:
                self.syntaxError('end')
        else:
            self.index -= 1
            return False

    def options_commands(self):
        self.list_commands()

    def list_commands(self):
        self.command()
        self.list_commands__()

    def list_commands__(self):
        if self.nextToken().token == ';':
            self.command()
            self.list_commands__()
        else:
            self.index -= 1

    ## return
    def command(self):
        if self.variable():
            # Recupera o tipo da variavel que recebe
            type_id = self.symbolTable.searchSymbol(self.getCurrentToken(
            ).token).type  # Semantico [Verificacao de Tipos]
            if type_id == 'program':
                sys.exit(
                    'ERRO! O nome do programa nao pode ser usado em comandos e expressoes'
                )

            if self.nextToken().token == ':=':
                self.expression()
                # Verifica compatibilidade de tipos do resultado com o identificador
                self.verifyTypesId(type_id, self.pct.top())
                return
            else:
                self.syntaxError(':=')

        elif self.activation_procedure():
            pass
        elif self.composed_commands():
            pass
        else:
            aux = self.nextToken()

            if aux.token == 'if':
                self.expression()
                # Verifica o tipo resultado
                if self.pct.top(
                ) == 'boolean':  # Semantico [Verificacao de Tipos]
                    self.pct.pop()  # Ok!
                else:
                    sys.exit(
                        'ERROR linha {}! Incompatibilidade de tipos: tipo do if nao eh booleano'
                        .format(self.getCurrentToken().line))
                if self.nextToken().token == 'then':
                    self.command()
                    self.part_else()
                    return
                else:
                    self.syntaxError('then')
            elif aux.token == 'while':
                self.expression()
                if self.pct.top() == 'boolean':  # Semantico
                    self.pct.pop()  # Ok!
                else:
                    sys.exit(
                        'ERROR linha {}! Incompatibilidade de tipos: tipo do while nao eh booleano'
                        .format(self.getCurrentToken().line))
                if self.nextToken().token == 'do':
                    self.command()
                    return
                else:
                    self.syntaxError('do')
            else:
                self.index -= 1
                return False

    ## return
    def variable(self):
        if self.nextToken().tokenType == 'Identificador':
            self.verifyScope(
                self.getCurrentToken())  # Semantico [Tabela De Simbolos]
            return True
        else:
            self.index -= 1
            return False

    ## return
    def activation_procedure(self):
        if self.nextToken().tokenType == 'Identificador':
            self.verifyScope(
                self.getCurrentToken())  # Semantico [Tabela de Simbolos]
            if self.nextToken().token == '(':
                self.list_expressions()
                if self.nextToken().token == ')':
                    return True
                else:
                    self.syntaxError(')')
            else:
                self.index -= 1
                return True
        else:
            self.index -= 1
            return False

    def expression(self):
        if self.simple_expression():
            if self.op_relational():
                self.simple_expression()
                # Confere os tipos # Semantico
                if not self.pct.reduce_pct_relational():
                    sys.exit(
                        'ERROR linha {}! Incompatibilidade de tipos: Comparando variaveis com tipos diferentes de integer ou real'
                        .format(self.getCurrentToken().line))
        else:
            self.syntaxError('Expressao')

    def simple_expression(self):
        if self.term():
            self.simple_expression__()  #
            return True
        elif self.signal():
            self.term()
            self.simple_expression__()
            return True
        else:
            return False

    def simple_expression__(self):
        if self.op_aditive():
            op = self.getCurrentToken().token
            self.term()
            self.simple_expression__()
            # Semantico [Verificacao de Tipos]
            if op == 'or':
                if not self.pct.reduce_pct_logical():
                    sys.exit(
                        'ERROR linha {}! Incompatibilidade de tipos: Op logicas com tipos diferentes de boolean'
                        .format(self.getCurrentToken().line))
            else:
                if not self.pct.reduce_pct_arithmetic():
                    sys.exit(
                        'ERROR linha {}! Incompatibilidade de tipos: Op aritimeticas com tipos diferentes de integer ou real'
                        .format(self.getCurrentToken().line))

    ## return
    def op_relational(self):
        if self.nextToken().tokenType == 'Operador Relacional':
            return True
        else:
            self.index -= 1
            return False

    ## return
    def op_aditive(self):
        if self.nextToken().tokenType == 'Operador Aditivo':
            return True
        else:
            self.index -= 1
            return False

    ## return
    def op_multiplicative(self):
        if self.nextToken().tokenType == 'Operador Multiplicativo':
            return True
        else:
            self.index -= 1
            return False

    ## return
    def term(self):
        if self.factor():
            self.term__()
            return True
        else:
            return False

    def term__(self):
        if self.op_multiplicative():
            op = self.getCurrentToken().token
            self.factor()
            self.term__()
            # Confere os tipos # Semantico
            if op == 'and':
                if not self.pct.reduce_pct_logical():
                    sys.exit(
                        'ERROR linha {}! Incompatibilidade de tipos: Op logicas com tipos diferentes de boolean'
                        .format(self.getCurrentToken().line))
            else:
                if not self.pct.reduce_pct_arithmetic():
                    sys.exit(
                        'ERROR linha {}! Incompatibilidade de tipos: Op aritimeticas com tipos diferentes de integer ou real'
                        .format(self.getCurrentToken().line))

    ## return
    def signal(self):
        if self.nextToken().token in "+-":
            return True
        else:
            self.index -= 1
            return False

    ## return
    def factor(self):
        aux = self.nextToken()
        if aux.tokenType == 'Identificador':
            self.verifyScope(
                self.getCurrentToken())  # Semantico [Tabela de Simbolos]
            # Recupera o simbolo corrente dentro da tabela de simbolos e empilha seu tipo na pct
            self.pct.push(
                self.symbolTable.searchSymbol(self.getCurrentToken().token).
                type)  # Semantico [Verificacao de Tipos]
            if self.nextToken().token == '(':
                self.list_expressions()
                if self.nextToken().token == ')':
                    return True
                else:
                    self.syntaxError(')')
            else:
                self.index -= 1
                return True
        elif aux.tokenType == 'Numero Inteiro':
            self.pct.push('integer')  # Semantico [Verificacao de Tipos]
            return True
        elif aux.tokenType == 'Numero Real':
            self.pct.push('real')  # Semantico [Verificacao de Tipos]
            return True
        elif aux.token == 'true':
            self.pct.push('boolean')  # Semantico [Verificacao de Tipos]
            return True
        elif aux.token == 'false':
            self.pct.push('boolean')  # Semantico [Verificacao de Tipos]
            return True
        elif aux.token == '(':
            self.expression()
            if self.nextToken().token == ')':
                return True
            else:
                self.syntaxError(')')
        elif aux.token == 'not':
            self.factor()
            return True
        else:
            self.index -= 1
            return False

    def list_expressions(self):
        self.expression()
        self.list_expressions__()

    def list_expressions__(self):
        if self.nextToken().token == ',':
            self.expression()
            self.list_expressions__()
        else:
            self.index -= 1

    def part_else(self):
        if self.nextToken().token == 'else':
            self.command()
        else:
            self.index -= 1

    def list_var_declarations(self):
        if self.list_identifiers():
            if self.nextToken().token == ':':
                self._type()
                if self.nextToken().token == ';':
                    self.list_var_declarations__()
                else:
                    self.syntaxError(';')
            else:
                self.syntaxError(':')
        else:
            self.syntaxError('Indentificador')

    def list_var_declarations__(self):
        if self.list_identifiers():
            if self.nextToken().token == ':':
                self._type()
                if self.nextToken().token == ';':
                    self.list_var_declarations__()
                else:
                    self.syntaxError(';')
            else:
                self.syntaxError(':')

    def list_identifiers(self):
        if self.nextToken().tokenType == 'Identificador':
            self.verifyScope(self.getCurrentToken())  # Semantico
            self.list_identifiers__()
            return True
        else:
            self.index -= 1
            return False

    def list_identifiers__(self):
        if self.nextToken().token == ',':
            if self.nextToken().tokenType == 'Identificador':
                self.verifyScope(self.getCurrentToken())  # Semantico
                self.list_identifiers__()
            else:
                self.syntaxError('Identificador')
        else:
            self.index -= 1

    def _type(self):
        if self.nextToken().token in ['integer', 'boolean', 'real']:
            self.symbolTable.setType(self.getCurrentToken().token)  # Semantico
        else:
            self.syntaxError('Tipo')

    # Help Functions
    def verifyTypesId(self, type_id, pct_top):
        line = self.getCurrentToken().line
        if type_id == "integer" and pct_top == "real":
            sys.exit(
                'ERRO linha {}! Incompatibilidade de tipos: Adicionando real em uma variavel inteira'
                .format(line))
        elif type_id in ['integer', 'real'] and pct_top == "boolean":
            sys.exit(
                'ERRO linha {}! Incompatibilidade de tipos: Adicionando boolean em uma variavel {}'
                .format(line, type_id))
        elif pct_top in ['integer', 'real'] and type_id == "boolean":
            sys.exit(
                'ERRO linha {}! Incompatibilidade de tipos: Adicionando {} em uma variavel boolean'
                .format(line, pct_top))

        self.pct.pop(
        )  # Apos verificar id, retira da pilha, zerando assim a pilha

    def verifyScope(self, symbol):
        if self.scope:
            if not self.symbolTable.searchSymbol(symbol.token):
                sys.exit('Erro linha {}! Simbolo {} nao declarado'.format(
                    symbol.line, symbol.token))
        else:
            if not self.symbolTable.addSymbol(symbol.token, '?'):
                sys.exit(
                    'Erro linha {}! Simbolo {} ja foi declarado no mesmo escopo'
                    .format(symbol.line, symbol.token))

    def nextToken(self):
        if (self.index + 1) < len(self.list_tokens):
            self.index += 1
            #print(self.list_tokens[self.index])
            #self.showStack()
            return self.list_tokens[self.index]
        else:
            sys.exit("out range")

    def syntaxError(self, expected):
        ct = self.getCurrentToken()
        sys.exit(
            'Syntax error, "{}" expected but "{}" found in line {}'.format(
                expected, ct.token, ct.line))

    def getCurrentToken(self):
        #print '[getCurrentToken]: {}'.format(self.list_tokens[self.index])
        return self.list_tokens[self.index]

    def showStack(self):
        print("########")
        for line in traceback.format_stack():
            print(line)
        print("########")
class CustomParseTreeListener(VYPListener):
    def __init__(self, functionDefinitionTable, classTable):
        self.localSymbolTable = SymbolTable()
        self.functionTable: SymbolTable() = functionDefinitionTable
        self.preemptiveFunctionCallTable = SymbolTable()
        self.semanticsChecker = SemanticsChecker()
        self.codeGenerator = CodeGenerator()
        self.expressionStack = deque()
        self.currentFunctionId = ''
        self.currentFunction = None
        self.classTable = classTable
        self.checkClassDefinitionsSemantics()
        self.currentClass = None
        self.currentFunctionReturn = False
        self.functionCallStack = list()

    ''' Reset symbol table since symbol table is valid only inside of function/method
        definition'''

    def exitFunction_definition(self,
                                ctx: VYPParser.Function_definitionContext):
        if self.currentClass is None:
            self.localSymbolTable.resetToDefaultState()

    def enterFunction_header(self, ctx: VYPParser.Function_headerContext):
        self.currentFunctionId = ctx.ID().getText()

        if self.currentClass == None:
            self.currentFunction = self.functionTable.getSymbol(
                self.currentFunctionId)
            functionParameterNames = list(
                map(
                    lambda x: x.id,
                    self.functionTable.getSymbol(
                        ctx.ID().getText()).parameterList.parameters))
        else:
            self.currentFunction = self.currentClass.methodTable.getSymbol(
                self.currentFunctionId)
            functionParameterNames = list(
                map(
                    lambda x: x.id,
                    self.currentClass.methodTable.getSymbol(
                        ctx.ID().getText()).parameterList.parameters))

        self.codeGenerator.generateFunctionHeader(self.currentFunction,
                                                  functionParameterNames)

    ''' Function parameters need to be inserted into symbol table. If 'void' is 
        used as parameter, no action is needed. This rule is not used anywhere
        else, so this rule is entered only during function definition. '''

    def enterFunction_parameter_definition(
            self, ctx: VYPParser.Function_parametersContext):
        definitionSymbol = GeneralSymbol(ctx.ID().getText(),
                                         SymbolType.VARIABLE,
                                         ctx.variable_type().getText())
        definitionSymbol.setAsDefined()
        self.defineFunctionParameter(definitionSymbol)

    def enterVariable_definition(self,
                                 ctx: VYPParser.Variable_definitionContext):
        variableType = ctx.variable_type().getText() if ctx.variable_type(
        ).getText() in ['int', 'string'] else self.classTable.getSymbol(
            ctx.variable_type().getText())
        definitionSymbol = GeneralSymbol(ctx.ID().getText(),
                                         SymbolType.VARIABLE, variableType,
                                         ctx.start.line, ctx.start.column)
        self.localSymbolTable.addSymbol(ctx.ID().getText(), definitionSymbol)
        if self.functionTable.isSymbolDefined(
                ctx.ID().getText()) or self.classTable.isSymbolDefined(
                    ctx.ID().getText()):
            raise SemanticGeneralError(
                f"Symbol with id: {ctx.ID().getText()} is already defined")
        self.codeGenerator.defineVariable(definitionSymbol.codeName,
                                          self.currentFunction, variableType)

    ''' Data type of variable must be taken from parent context'''

    def enterMultiple_variable_definition(
            self, ctx: VYPParser.Multiple_variable_definitionContext):
        definitionSymbol = GeneralSymbol(
            ctx.ID().getText(), SymbolType.VARIABLE,
            ctx.parentCtx.variable_type().getText(), ctx.start.line,
            ctx.start.column)
        self.localSymbolTable.addSymbol(ctx.ID().getText(), definitionSymbol)
        if self.functionTable.isSymbolDefined(
                ctx.ID().getText()) or self.classTable.isSymbolDefined(
                    ctx.ID().getText()):
            raise SemanticGeneralError(
                f"Symbol with id: {ctx.ID().getText()} is already defined")
        self.codeGenerator.defineVariable(definitionSymbol.codeName,
                                          self.currentFunction,
                                          definitionSymbol.dataType)

    def enterField_definition(self, ctx: VYPParser.Field_definitionContext):
        if self.currentClass.methodTable.isSymbolDefined(ctx.ID().getText()):
            raise SemanticGeneralError(
                f"There is already method with id: {ctx.ID().getText()} defined"
            )

    def enterMultiple_field_definition(
            self, ctx: VYPParser.Multiple_field_definitionContext):
        if self.currentClass.methodTable.isSymbolDefined(ctx.ID().getText()):
            raise SemanticGeneralError(
                f"There is already method with id: {ctx.ID().getText()} defined"
            )

    def enterCode_block(self, ctx: VYPParser.Code_blockContext):
        self.localSymbolTable.addClosure()

    def exitCode_block(self, ctx: VYPParser.Code_blockContext):
        self.localSymbolTable.removeClosure()

    def enterVariable_assignment(self,
                                 ctx: VYPParser.Variable_assignmentContext):
        symbol = self.localSymbolTable.getSymbol(ctx.ID().getText())
        symbol.setAsDefined()

    def exitVariable_assignment(self,
                                ctx: VYPParser.Variable_assignmentContext):
        symbol = self.localSymbolTable.getSymbol(ctx.ID().getText())
        expression = self.expressionStack.pop()
        self.semanticsChecker.checkVariableAssignment(symbol.dataType,
                                                      expression.dataType)
        self.codeGenerator.assignValueToVariable(self.currentFunction,
                                                 symbol.codeName)

    def enterStatement(self, ctx: VYPParser.StatementContext):
        self.expressionStack.clear()
        pass

    def enterClass_definition(self, ctx: VYPParser.Class_definitionContext):
        self.currentClass = self.classTable.getSymbol(
            ctx.class_header().class_id.text)
        self.localSymbolTable = self.currentClass.fieldTable

    def exitClass_definition(self, ctx: VYPParser.Class_definitionContext):
        self.currentClass = None
        self.localSymbolTable = SymbolTable()

    def enterMethod_definition(self, ctx: VYPParser.Method_definitionContext):
        self.localSymbolTable = SymbolTable()

    def defineFunctionParameter(self, symbol: GeneralSymbol):
        self.localSymbolTable.addSymbol(symbol.id, symbol)

    def checkClassDefinitionsSemantics(self):
        self.semanticsChecker.checkMethodOverrideTypes(self.classTable)

    def enterProgram(self, ctx: VYPParser.ProgramContext):
        self.generateMethodVirtualTables()

    def exitProgram(self, ctx: VYPParser.ProgramContext):

        self.codeGenerator.generateCode()

    def exitFunction_body(self, ctx: VYPParser.Function_bodyContext):
        if self.currentClass == None:
            currentFunction = self.functionTable.getSymbol(
                self.currentFunctionId)
        else:
            currentFunction = self.currentClass.methodTable.getSymbol(
                self.currentFunctionId)

        if currentFunction.dataType != 'void':
            if self.currentFunctionReturn == False:
                pass
                #raise SemanticGeneralError("No return value specified")
        self.currentFunctionReturn = False
        setReturnValue = currentFunction.dataType != 'void'

        if setReturnValue:
            if currentFunction.dataType == 'string':
                self.codeGenerator.generateLiteralExpression(
                    self.currentFunction, '""', 'string')
            else:
                self.codeGenerator.generateLiteralExpression(
                    self.currentFunction, 0, 'int')
        if self.currentFunctionId != 'main':
            self.codeGenerator.generateReturnValue(self.currentFunction,
                                                   setReturnValue)
        else:
            self.codeGenerator.returnFromFunction(self.currentFunction)

    def exitIf_part(self, ctx: VYPParser.If_partContext):
        self.codeGenerator.generateIfEnd(self.currentFunction, ctx.start.line,
                                         ctx.start.column)

    def exitElse_part(self, ctx: VYPParser.Else_partContext):
        self.codeGenerator.generateElseEnd(self.currentFunction,
                                           ctx.parentCtx.start.line,
                                           ctx.parentCtx.start.column)

    def enterWhile_block(self, ctx: VYPParser.While_blockContext):
        self.codeGenerator.generateWhileStart(self.currentFunction,
                                              ctx.start.line, ctx.start.column)

    def exitWhile_block(self, ctx: VYPParser.While_blockContext):
        self.codeGenerator.generateWhileEnd(self.currentFunction,
                                            ctx.start.line, ctx.start.column)

    def exitWhile_expression(self, ctx: VYPParser.While_expressionContext):
        expression = self.expressionStack.pop()
        if expression.dataType != 'int':
            raise SemanticTypeIncompatibilityError(
                f"WHILE expected data type 'int', got '{expression.dataType}' instead."
            )
        self.codeGenerator.generateEvaluateWhile(self.currentFunction,
                                                 ctx.parentCtx.start.line,
                                                 ctx.parentCtx.start.column)

    def exitIf_expression(self, ctx: VYPParser.If_expressionContext):
        expression = self.expressionStack.pop()
        if expression.dataType != 'int':
            raise SemanticTypeIncompatibilityError(
                f"IF expected data type 'int', got '{expression.dataType}' instead."
            )
        self.codeGenerator.generateIfStart(self.currentFunction,
                                           ctx.start.line, ctx.start.column)

    def generateMethodVirtualTables(self):
        for classSymbol in self.classTable.getAllSymbols():
            allMethods = classSymbol.getAllAvailableMethods()
            self.codeGenerator.generateVirtualMethodTable(
                classSymbol.id, allMethods)