Exemple #1
0
 def Evaluate(self, SymbolTable):
     func_name = self.value
     func_node = SymbolTable.getSymbol(func_name, "func").getValue()
     funcSymbolTable = SymbolTableClass(SymbolTable)
     var_dec = func_node.children[0]
     args = [x.children[0] for x in var_dec.children]
     func_node.children[0].Evaluate(funcSymbolTable)
     if (len(args) != len(self.children)):
         raise ValueError("Number of arguments must \
                           be the same as declaration")
     for i in range(len(args)):
         symbol = args[i].Evaluate(funcSymbolTable).getValue()
         symbol_type = funcSymbolTable.getSymbol(symbol).getType()
         value_obj = self.children[i].Evaluate(SymbolTable)
         if (symbol_type != value_obj.getType()):
             raise ValueError("Function argument must be \
                                the same as declared")
         value = value_obj.getValue()
         funcSymbolTable.setSymbol(symbol, value)
     for i in range(1, len(func_node.children)):
         func_node.children[i].Evaluate(funcSymbolTable)
     result = funcSymbolTable.getSymbol(func_name)
     return result
Exemple #2
0
def translate(node, st=None, strings=None, funcName=False):
    if isinstance(node, oast.Add):
        left = translate(node.left, st, strings, funcName)
        right = translate(node.right, st, strings, funcName)

        return ast.Add(left, right)

    elif isinstance(node, oast.And):
        left = translate(node.nodes[0], st, strings, funcName)
        right = translate(node.nodes[1], st, strings, funcName)

        return ast.And(left, right)

    elif isinstance(node, oast.Assign):
        # Translate the right hand side first so it can use the older version
        # of the left hand side.
        exp = translate(node.expr, st, strings, funcName)
        var = node.nodes.pop()

        if isinstance(var, oast.AssAttr):
            string = strings.setdefault(var.attrname, ast.String(var.attrname))
            var = translate(var.expr, st, strings, funcName)

            return ast.SetAttr(var, string, exp)

        else:
            var = translate(var, st, strings, funcName)

            return ast.Assign(var, exp)

    elif isinstance(node, oast.AssName):
        return st.getSymbol(node.name, True)

    elif isinstance(node, oast.CallFunc):
        name = translate(node.node, st, strings, True)
        args = [translate(a, st, strings) for a in node.args]

        return ast.FunctionCall(name, *args)

    elif isinstance(node, oast.Class):
        bases = [translate(base, st, strings, funcName) for base in node.bases]

        body = translate(node.code, st, strings, funcName)
        body = ast.BasicBlock(body)

        sym = st.getSymbol(node.name, True)
        name = st.getName(node.name, True)

        # This is here temporarily.  It will be moved to the typify pass
        # later.
        sym['type'] = 'class'

        klass = ast.Class(name, bases, body)

        return ast.Assign(sym, klass)

    elif isinstance(node, oast.Compare):
        left = translate(node.expr, st, strings, funcName)

        op, right = node.ops[0]

        right = translate(right, st, strings, funcName)

        if op == '==':
            return ast.Eq(left, right)

        elif op == '!=':
            return ast.Ne(left, right)

        elif op == 'is':
            return ast.Is(left, right)

    elif isinstance(node, oast.Const):
        return ast.Integer(node.value)

    elif isinstance(node, oast.Dict):
        pairs = {}

        for pair in node.items:
            key, value = pair

            key = translate(key, st, strings, funcName)
            value = translate(value, st, strings, funcName)

            pairs[key] = value

        return ast.Dictionary(pairs)

    elif isinstance(node, oast.Discard):
        return translate(node.expr, st, strings, funcName)

    elif isinstance(node, oast.Div):
        left = translate(node.left, st, strings, funcName)
        right = translate(node.right, st, strings, funcName)

        return ast.Div(left, right)

    elif isinstance(node, oast.Function):
        sym = st.getSymbol(node.name, True)
        name = st.getName(node.name, True)

        sym['type'] = 'function'

        newST = SymbolTable(st)

        argSymbols = [
            newST.getSymbol(argName, True) for argName in node.argnames
        ]

        body = translate(node.code, newST, strings, funcName)
        body = ast.BasicBlock(body)

        fun = ast.Function(name, argSymbols, body, newST)
        fun['simplified'] = False

        st.update(newST)

        return ast.Assign(sym, fun)

    elif isinstance(node, oast.Getattr):
        exp = translate(node.expr, st, strings, funcName)
        name = strings.setdefault(node.attrname, ast.String(node.attrname))

        return ast.GetAttr(exp, name)

    elif isinstance(node, oast.If):
        tests = node.tests
        cond, then = tests.pop(0)

        # Translate the conditional expression.
        cond = translate(cond, st, strings)

        # Snapshot the SymbolTable
        st.snapshot()

        # Translate the 'then' clause.
        then = translate(then, st, strings, funcName)
        then = ast.BasicBlock(then)

        # Roll-back the SymbolTable for the 'else' clause.
        st.rollback()

        # Translate the 'else' clause.
        if len(tests) > 0:
            els = [translate(oast.If(tests, node.else_), st, funcName)]
        else:
            els = translate(node.else_, st, strings, funcName)

        els = ast.BasicBlock(els)

        return ast.If(cond, then, els, st)

    elif isinstance(node, oast.IfExp):
        cond = translate(node.test, st, strings, funcName)
        then = translate(node.then, st, strings, funcName)
        els = translate(node.else_, st, strings, funcName)

        return ast.IfExp(cond, then, els)

    elif isinstance(node, oast.Lambda):
        name = st.getName('lambda', True)

        newST = SymbolTable(st)

        argSymbols = map(lambda name: newST.getSymbol(name, True),
                         node.argnames)

        code = ast.Return(translate(node.code, newST, strings, funcName))
        block = ast.BasicBlock([code])
        fun = ast.Function(name, argSymbols, block, newST)
        fun['simplified'] = False

        st.update(newST)

        return fun

    elif isinstance(node, oast.List):
        elements = []

        for n in node.nodes:
            elements.append(translate(n, st, strings, funcName))

        return ast.List(elements)

    elif isinstance(node, oast.Module):
        # Create a new SymbolTable for this module.
        st = SymbolTable()
        strings = {}

        children = translate(node.node, st, strings, funcName)

        block = ast.BasicBlock(children)
        fun = ast.Function(st.getBIF('main'), [], block, st)

        # Mark the main function as migrated so that it doesn't get moved
        # later.
        fun['simplified'] = True

        return ast.Module([fun], strings)

    elif isinstance(node, oast.Mul):
        left = translate(node.left, st, strings, funcName)
        right = translate(node.right, st, strings, funcName)

        return ast.Mul(left, right)

    elif isinstance(node, oast.Name):
        ret = 'input_int' if node.name == 'input' else node.name

        if ret == 'input_int':
            ret = st.getBIF(ret)

        else:
            if ret == 'True':
                ret = ast.Tru()

            elif ret == 'False':
                ret = ast.Fals()

            else:
                ret = st.getSymbol(ret)

        return ret

    elif isinstance(node, oast.Not):
        operand = translate(node.expr, st, strings, funcName)

        return ast.Not(operand)

    elif isinstance(node, oast.Or):
        left = translate(node.nodes[0], st, strings, funcName)
        right = translate(node.nodes[1], st, strings, funcName)

        return ast.Or(left, right)

    elif isinstance(node, oast.Printnl):
        children = [
            translate(e, st, strings, funcName) for e in node.getChildNodes()
        ]
        children = util.flatten(children)

        return ast.FunctionCall(st.getBIF('print_any'), *children)

    elif isinstance(node, oast.Return):
        return ast.Return(translate(node.value, st, strings, funcName))

    elif isinstance(node, oast.Stmt):
        stmts = [
            translate(s, st, strings, funcName) for s in node.getChildNodes()
        ]

        return util.flatten(stmts)

    elif isinstance(node, oast.Sub):
        left = translate(node.left, st, strings, funcName)
        right = translate(node.right, st, strings, funcName)

        return ast.Sub(left, right)

    elif isinstance(node, oast.Subscript):
        sym = translate(node.expr, st, strings, funcName)
        sub = translate(node.subs[0], st, strings, funcName)

        return ast.Subscript(sym, sub)

    elif isinstance(node, oast.While):
        cond = translate(node.test, st, strings, funcName)

        body = translate(node.body, st, strings, funcName)
        body = ast.BasicBlock(body)

        return ast.While(cond, body, st)

    elif isinstance(node, oast.UnarySub):
        operand = translate(node.expr, st, strings, funcName)

        return ast.Negate(operand)

    else:
        raise Exception("Unsupported AST node encountered: {}".format(
            node.__class__.__name__))
Exemple #3
0
def translate(node, st = None, strings = None, funcName = False):
	if isinstance(node, oast.Add):
		left  = translate(node.left,  st, strings, funcName)
		right = translate(node.right, st, strings, funcName)
		
		return ast.Add(left, right)
	
	elif isinstance(node, oast.And):
		left	 = translate(node.nodes[0], st, strings, funcName)
		right = translate(node.nodes[1], st, strings, funcName)
		
		return ast.And(left, right)
	
	elif isinstance(node, oast.Assign):
		# Translate the right hand side first so it can use the older version
		# of the left hand side.
		exp = translate(node.expr, st, strings, funcName)
		var = node.nodes.pop()
		
		if isinstance(var, oast.AssAttr):
			string = strings.setdefault(var.attrname, ast.String(var.attrname))
			var = translate(var.expr, st, strings, funcName)
			
			return ast.SetAttr(var, string, exp)
		
		else:
			var = translate(var, st, strings, funcName)
			
			return ast.Assign(var, exp)
	
	elif isinstance(node, oast.AssName):
		return st.getSymbol(node.name, True)
	
	elif isinstance(node, oast.CallFunc):
		name = translate(node.node, st, strings, True)
		args = [translate(a, st, strings) for a in node.args]
		
		return ast.FunctionCall(name, *args)
	
	elif isinstance(node, oast.Class):
		bases = [translate(base, st, strings, funcName) for base in node.bases]
		
		body = translate(node.code, st, strings, funcName)
		body = ast.BasicBlock(body)
		
		sym	= st.getSymbol(node.name, True)
		name	= st.getName(node.name, True)
		
		# This is here temporarily.  It will be moved to the typify pass
		# later.
		sym['type'] = 'class'
		
		klass = ast.Class(name, bases, body)
		
		return ast.Assign(sym, klass)
	
	elif isinstance(node, oast.Compare):
		left = translate(node.expr, st, strings, funcName)
		
		op, right = node.ops[0]
		
		right = translate(right, st, strings, funcName)
		
		if op == '==':
			return ast.Eq(left, right)
		
		elif op == '!=':
			return ast.Ne(left, right)
		
		elif op == 'is':
			return ast.Is(left, right)
	
	elif isinstance(node, oast.Const):
		return ast.Integer(node.value)
	
	elif isinstance(node, oast.Dict):
		pairs = {}
		
		for pair in node.items:
			key, value = pair
			
			key = translate(key, st, strings, funcName)
			value = translate(value, st, strings, funcName)
			
			pairs[key] = value
		
		return ast.Dictionary(pairs)
	
	elif isinstance(node, oast.Discard):
		return translate(node.expr, st, strings, funcName)
	
	elif isinstance(node, oast.Div):
		left  = translate(node.left,  st, strings, funcName)
		right = translate(node.right, st, strings, funcName)
		
		return ast.Div(left, right)
	
	elif isinstance(node, oast.Function):
		sym	= st.getSymbol(node.name, True)
		name	= st.getName(node.name, True)
		
		sym['type'] = 'function'
		
		newST = SymbolTable(st)
		
		argSymbols = [newST.getSymbol(argName, True) for argName in node.argnames]
		
		body = translate(node.code, newST, strings, funcName)
		body = ast.BasicBlock(body)
		
		fun = ast.Function(name, argSymbols, body, newST)
		fun['simplified'] = False
		
		st.update(newST)
		
		return ast.Assign(sym, fun)
	
	elif isinstance(node, oast.Getattr):
		exp	= translate(node.expr, st, strings, funcName)
		name	= strings.setdefault(node.attrname, ast.String(node.attrname))
		
		return ast.GetAttr(exp, name)
	
	elif isinstance(node, oast.If):
		tests = node.tests
		cond, then = tests.pop(0)
		
		# Translate the conditional expression.
		cond = translate(cond, st, strings)
		
		# Snapshot the SymbolTable
		st.snapshot()
		
		# Translate the 'then' clause.
		then = translate(then, st, strings, funcName)
		then = ast.BasicBlock(then)
		
		# Roll-back the SymbolTable for the 'else' clause.
		st.rollback()
		
		# Translate the 'else' clause.
		if len(tests) > 0:
			els = [translate(oast.If(tests, node.else_), st, funcName)]
		else:
			els = translate(node.else_, st, strings, funcName)
		
		els = ast.BasicBlock(els)
		
		return ast.If(cond, then, els, st)
	
	elif isinstance(node, oast.IfExp):
		cond = translate(node.test, st, strings, funcName)
		then = translate(node.then, st, strings, funcName)
		els = translate(node.else_, st, strings, funcName)
		
		return ast.IfExp(cond, then, els)
	
	elif isinstance(node, oast.Lambda):
		name = st.getName('lambda', True)
		
		newST = SymbolTable(st)
		
		argSymbols = map(lambda name: newST.getSymbol(name, True), node.argnames)
		
		code  = ast.Return(translate(node.code, newST, strings, funcName))
		block = ast.BasicBlock([code])
		fun   = ast.Function(name, argSymbols, block, newST)
		fun['simplified'] = False
		
		st.update(newST)
		
		return fun
	
	elif isinstance(node, oast.List):
		elements = []
		
		for n in node.nodes:
			elements.append(translate(n, st, strings, funcName))
		
		return ast.List(elements)
		
	elif isinstance(node, oast.Module):
		# Create a new SymbolTable for this module.
		st = SymbolTable()
		strings = {}
		
		children = translate(node.node, st, strings, funcName)
		
		block = ast.BasicBlock(children)
		fun = ast.Function(st.getBIF('main'), [], block, st)
		
		# Mark the main function as migrated so that it doesn't get moved
		# later.
		fun['simplified'] = True
		
		return ast.Module([fun], strings)
	
	elif isinstance(node, oast.Mul):
		left  = translate(node.left,  st, strings, funcName)
		right = translate(node.right, st, strings, funcName)
		
		return ast.Mul(left, right)
	
	elif isinstance(node, oast.Name):
		ret = 'input_int' if node.name == 'input' else node.name
		
		if ret == 'input_int':
			ret = st.getBIF(ret)
		
		else:
			if ret == 'True':
				ret = ast.Tru()
			
			elif ret == 'False':
				ret = ast.Fals()
			
			else:
				ret = st.getSymbol(ret)
		
		return ret
	
	elif isinstance(node, oast.Not):
		operand = translate(node.expr, st, strings, funcName)
		
		return ast.Not(operand)
	
	elif isinstance(node, oast.Or):
		left  = translate(node.nodes[0], st, strings, funcName)
		right = translate(node.nodes[1], st, strings, funcName)
		
		return ast.Or(left, right)
		
	elif isinstance(node, oast.Printnl):
		children = [translate(e, st, strings, funcName) for e in node.getChildNodes()]
		children = util.flatten(children)
		
		return ast.FunctionCall(st.getBIF('print_any'), *children)
	
	elif isinstance(node, oast.Return):
		return ast.Return(translate(node.value, st, strings, funcName))
		
	elif isinstance(node, oast.Stmt):
		stmts = [translate(s, st, strings, funcName) for s in node.getChildNodes()]
		
		return util.flatten(stmts)
	
	elif isinstance(node, oast.Sub):
		left  = translate(node.left,  st, strings, funcName)
		right = translate(node.right, st, strings, funcName)
		
		return ast.Sub(left, right)
	
	elif isinstance(node, oast.Subscript):
		sym = translate(node.expr, st, strings, funcName)
		sub = translate(node.subs[0], st, strings, funcName)
		
		return ast.Subscript(sym, sub)
	
	elif isinstance(node, oast.While):
		cond = translate(node.test, st, strings, funcName)
		
		body = translate(node.body, st, strings, funcName)
		body = ast.BasicBlock(body)
		
		return ast.While(cond, body, st)
	
	elif isinstance(node, oast.UnarySub):
		operand = translate(node.expr, st, strings, funcName)
		
		return ast.Negate(operand)
	
	else:
		raise Exception("Unsupported AST node encountered: {}".format(node.__class__.__name__))
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
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)