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)