def convert_name_reference(self, variable): """ Converts a single variable to nest processable format. :param variable: a single variable. :type variable: ASTVariable :return: a nest processable format. :rtype: str """ from pynestml.codegeneration.nest_printer import NestPrinter assert (variable is not None and isinstance(variable, ASTVariable)), \ '(PyNestML.CodeGeneration.NestReferenceConverter) No or wrong type of uses-gsl provided (%s)!' % type( variable) variable_name = NestNamesConverter.convert_to_cpp_name( variable.get_complete_name()) if variable_name == PredefinedVariables.E_CONSTANT: return 'numerics::e' else: symbol = variable.get_scope().resolve_to_symbol( variable_name, SymbolKind.VARIABLE) if symbol is None: # test if variable name can be resolved to a type if PredefinedUnits.is_unit(variable.get_complete_name()): return str( UnitConverter.get_factor( PredefinedUnits.get_unit( variable.get_complete_name()).get_unit())) code, message = Messages.get_could_not_resolve(variable_name) Logger.log_message( log_level=LoggingLevel.ERROR, code=code, message=message, error_position=variable.get_source_position()) return '' else: if symbol.is_local(): return variable_name + ( '[i]' if symbol.has_vector_parameter() else '') elif symbol.is_buffer(): return NestPrinter.print_origin(symbol) + NestNamesConverter.buffer_value(symbol) \ + ('[i]' if symbol.has_vector_parameter() else '') else: if symbol.is_function: return 'get_' + variable_name + '()' + ( '[i]' if symbol.has_vector_parameter() else '') else: if symbol.is_init_values(): temp = NestPrinter.print_origin(symbol) if self.uses_gsl: temp += GSLNamesConverter.name(symbol) else: temp += NestNamesConverter.name(symbol) temp += ('[i]' if symbol.has_vector_parameter() else '') return temp else: return NestPrinter.print_origin(symbol) + \ NestNamesConverter.name(symbol) + \ ('[i]' if symbol.has_vector_parameter() else '')
def visit_simple_expression(self, node): """ Visits a single function call :param node: a simple expression """ assert isinstance(node, ASTSimpleExpression), \ '(PyNestML.Visitor.FunctionCallVisitor) No or wrong type of simple expression provided (%s)!' % tuple(node) assert (node.get_scope() is not None), \ "(PyNestML.Visitor.FunctionCallVisitor) No scope found, run symboltable creator!" if node.get_function_call() is None: return function_name = node.get_function_call().get_name() if function_name == PredefinedFunctions.TIME_RESOLUTION: _node = node while _node: _node = self.neuron.get_parent(_node) if isinstance(_node, ASTEquationsBlock) \ or isinstance(_node, ASTFunction): code, message = Messages.get_could_not_resolve( function_name) Logger.log_message( code=code, message=message, error_position=node.get_source_position(), log_level=LoggingLevel.ERROR)
def convert_name_reference(self, ast_variable: ASTVariable, prefix: str = ''): """ Converts a single name reference to a gsl processable format. :param ast_variable: a single variable :type ast_variable: ASTVariable :return: a gsl processable format of the variable :rtype: str """ variable_name = NestNamesConverter.convert_to_cpp_name( ast_variable.get_name()) if variable_name == PredefinedVariables.E_CONSTANT: return 'numerics::e' symbol = ast_variable.get_scope().resolve_to_symbol( ast_variable.get_complete_name(), SymbolKind.VARIABLE) if symbol is None: # test if variable name can be resolved to a type if PredefinedUnits.is_unit(ast_variable.get_complete_name()): return str( UnitConverter.get_factor( PredefinedUnits.get_unit( ast_variable.get_complete_name()).get_unit())) code, message = Messages.get_could_not_resolve(variable_name) Logger.log_message( log_level=LoggingLevel.ERROR, code=code, message=message, error_position=ast_variable.get_source_position()) return '' if symbol.is_init_values(): return GSLNamesConverter.name(symbol) if symbol.is_buffer(): if isinstance(symbol.get_type_symbol(), UnitTypeSymbol): units_conversion_factor = UnitConverter.get_factor( symbol.get_type_symbol().unit.unit) else: units_conversion_factor = 1 s = "" if not units_conversion_factor == 1: s += "(" + str(units_conversion_factor) + " * " s += prefix + 'B_.' + NestNamesConverter.buffer_value(symbol) if symbol.has_vector_parameter(): s += '[i]' if not units_conversion_factor == 1: s += ")" return s if symbol.is_local() or symbol.is_function: return variable_name if symbol.has_vector_parameter(): return prefix + 'get_' + variable_name + '()[i]' return prefix + 'get_' + variable_name + '()'
def get_variables(cls, ast_declaration): """ For a given meta_model declaration it returns a list of all corresponding variable symbols. :param ast_declaration: a single meta_model declaration. :type ast_declaration: ASTDeclaration :return: a list of all corresponding variable symbols. :rtype: list(VariableSymbol) """ assert isinstance(ast_declaration, ASTDeclaration), \ '(PyNestML.CodeGeneration.DeclarationsHelper) No or wrong type of declaration provided (%s)!' % type( ast_declaration) ret = list() for var in ast_declaration.get_variables(): symbol = ast_declaration.get_scope().resolve_to_symbol( var.get_complete_name(), SymbolKind.VARIABLE) if symbol is not None: ret.append(symbol) else: code, message = Messages.get_could_not_resolve( var.get_complete_name()) Logger.log_message( code=code, message=message, error_position=ast_declaration.get_source_position(), log_level=LoggingLevel.ERROR) return ret
def visit_simple_expression(self, node): """ Visits a single function call as stored in a simple expression and checks to see whether any calls are made to generate a random number. If so, set a flag so that the necessary initialisers can be called at the right time in the generated code. """ assert isinstance(node, ASTSimpleExpression), \ '(PyNestML.Visitor.FunctionCallVisitor) No or wrong type of simple expression provided (%s)!' % tuple(node) assert (node.get_scope() is not None), \ "(PyNestML.Visitor.FunctionCallVisitor) No scope found, run symboltable creator!" scope = node.get_scope() if node.get_function_call() is None: return function_name = node.get_function_call().get_name() method_symbol = scope.resolve_to_symbol(function_name, SymbolKind.FUNCTION) # check if this function exists if method_symbol is None: code, message = Messages.get_could_not_resolve(function_name) Logger.log_message(code=code, message=message, error_position=node.get_source_position(), log_level=LoggingLevel.ERROR) node.type = ErrorTypeSymbol() return if function_name == PredefinedFunctions.RANDOM_NORMAL: self._norm_rng_is_used = True return
def visit_simple_expression(self, node): """ Visits a single function call as stored in a simple expression and derives the correct type of all its parameters. :param node: a simple expression :type node: ASTSimpleExpression :rtype void """ assert isinstance(node, ASTSimpleExpression), \ '(PyNestML.Visitor.FunctionCallVisitor) No or wrong type of simple expression provided (%s)!' % tuple(node) assert (node.get_scope() is not None), \ "(PyNestML.Visitor.FunctionCallVisitor) No scope found, run symboltable creator!" scope = node.get_scope() function_name = node.get_function_call().get_name() method_symbol = scope.resolve_to_symbol(function_name, SymbolKind.FUNCTION) # check if this function exists if method_symbol is None: code, message = Messages.get_could_not_resolve(function_name) Logger.log_message(code=code, message=message, error_position=node.get_source_position(), log_level=LoggingLevel.ERROR) node.type = ErrorTypeSymbol() return return_type = method_symbol.get_return_type() return_type.referenced_object = node # convolve symbol does not have a return type set. # returns whatever type the second parameter is. if function_name == PredefinedFunctions.CONVOLVE: # Deviations from the assumptions made here are handled in the convolveCoco buffer_parameter = node.get_function_call().get_args()[1] if buffer_parameter.getVariable() is not None: buffer_name = buffer_parameter.getVariable().getName() buffer_symbol_resolve = scope.resolve_to_symbol( buffer_name, SymbolKind.VARIABLE) if buffer_symbol_resolve is not None: node.type = buffer_symbol_resolve.getTypeSymbol() return # getting here means there is an error with the parameters to convolve code, message = Messages.get_convolve_needs_buffer_parameter() Logger.log_message(code=code, message=message, error_position=node.get_source_position(), log_level=LoggingLevel.ERROR) node.type = ErrorTypeSymbol() return if isinstance(method_symbol.get_return_type(), VoidTypeSymbol): # todo by KP: the error message is not used here, @ptraeder fix this # error_msg = ErrorStrings.message_void_function_on_rhs(self, function_name, node.get_source_position()) node.type = ErrorTypeSymbol() return # if nothing special is handled, just get the expression type from the return type of the function node.type = return_type
def visit_simple_expression(self, node): """ Visits a single function call as stored in a simple expression and derives the correct type of all its parameters. :param node: a simple expression :type node: ASTSimpleExpression :rtype void """ assert isinstance(node, ASTSimpleExpression), \ '(PyNestML.Visitor.FunctionCallVisitor) No or wrong type of simple expression provided (%s)!' % tuple(node) assert (node.get_scope() is not None), \ "(PyNestML.Visitor.FunctionCallVisitor) No scope found, run symboltable creator!" scope = node.get_scope() function_name = node.get_function_call().get_name() method_symbol = scope.resolve_to_symbol(function_name, SymbolKind.FUNCTION) # check if this function exists if method_symbol is None: code, message = Messages.get_could_not_resolve(function_name) Logger.log_message(code=code, message=message, error_position=node.get_source_position(), log_level=LoggingLevel.ERROR) node.type = ErrorTypeSymbol() return return_type = method_symbol.get_return_type() return_type.referenced_object = node # convolve symbol does not have a return type set. # returns whatever type the second parameter is. if function_name == PredefinedFunctions.CONVOLVE: # Deviations from the assumptions made here are handled in the convolveCoco buffer_parameter = node.get_function_call().get_args()[1] if buffer_parameter.get_variable() is not None: buffer_name = buffer_parameter.get_variable().get_name() buffer_symbol_resolve = scope.resolve_to_symbol(buffer_name, SymbolKind.VARIABLE) if buffer_symbol_resolve is not None: node.type = buffer_symbol_resolve.get_type_symbol() return # getting here means there is an error with the parameters to convolve code, message = Messages.get_convolve_needs_buffer_parameter() Logger.log_message(code=code, message=message, error_position=node.get_source_position(), log_level=LoggingLevel.ERROR) node.type = ErrorTypeSymbol() return if isinstance(method_symbol.get_return_type(), VoidTypeSymbol): # todo by KP: the error message is not used here, @ptraeder fix this # error_msg = ErrorStrings.message_void_function_on_rhs(self, function_name, node.get_source_position()) node.type = ErrorTypeSymbol() return # if nothing special is handled, just get the expression type from the return type of the function node.type = return_type
def convert_name_reference(self, variable): """ Converts a single variable to nest processable format. :param variable: a single variable. :type variable: ASTVariable :return: a nest processable format. :rtype: str """ from pynestml.codegeneration.nest_printer import NestPrinter assert (variable is not None and isinstance(variable, ASTVariable)), \ '(PyNestML.CodeGeneration.NestReferenceConverter) No or wrong type of uses-gsl provided (%s)!' % type( variable) variable_name = NestNamesConverter.convert_to_cpp_name(variable.get_complete_name()) if PredefinedUnits.is_unit(variable.get_complete_name()): return str( UnitConverter.get_factor(PredefinedUnits.get_unit(variable.get_complete_name()).get_unit())) if variable_name == PredefinedVariables.E_CONSTANT: return 'numerics::e' else: symbol = variable.get_scope().resolve_to_symbol(variable_name, SymbolKind.VARIABLE) if symbol is None: # this should actually not happen, but an error message is better than an exception code, message = Messages.get_could_not_resolve(variable_name) Logger.log_message(log_level=LoggingLevel.ERROR, code=code, message=message, error_position=variable.get_source_position()) return '' else: if symbol.is_local(): return variable_name + ('[i]' if symbol.has_vector_parameter() else '') elif symbol.is_buffer(): return NestPrinter.print_origin(symbol) + NestNamesConverter.buffer_value(symbol) \ + ('[i]' if symbol.has_vector_parameter() else '') else: if symbol.is_function: return 'get_' + variable_name + '()' + ('[i]' if symbol.has_vector_parameter() else '') else: if symbol.is_init_values(): temp = NestPrinter.print_origin(symbol) if self.uses_gsl: temp += GSLNamesConverter.name(symbol) else: temp += NestNamesConverter.name(symbol) temp += ('[i]' if symbol.has_vector_parameter() else '') return temp else: return NestPrinter.print_origin(symbol) + \ NestNamesConverter.name(symbol) + \ ('[i]' if symbol.has_vector_parameter() else '')
def visit_simple_expression(self, node): """ Visits a single function call as stored in a simple expression and, if template types are used for function parameters, checks if all actual parameter types are mutually consistent. :param node: a simple expression :type node: ASTSimpleExpression :rtype None: """ assert isinstance(node, ASTSimpleExpression), \ '(PyNestML.Visitor.FunctionCallVisitor) No or wrong type of simple expression provided (%s)!' % tuple(node) assert (node.get_scope() is not None), \ "(PyNestML.Visitor.FunctionCallVisitor) No scope found, run symboltable creator!" scope = node.get_scope() if node.get_function_call() is None: return function_name = node.get_function_call().get_name() method_symbol = scope.resolve_to_symbol(function_name, SymbolKind.FUNCTION) # check if this function exists if method_symbol is None: code, message = Messages.get_could_not_resolve(function_name) Logger.log_message(code=code, message=message, error_position=node.get_source_position(), log_level=LoggingLevel.ERROR) self._failure_occurred = True return return_type = method_symbol.get_return_type() template_symbol_to_actual_symbol = {} template_symbol_to_parameter_indices = {} # loop over all function parameters for arg_idx, arg_type in enumerate(method_symbol.param_types): if isinstance(arg_type, TemplateTypeSymbol): actual_symbol = node.get_function_call().get_args()[arg_idx].type if arg_type._i in template_symbol_to_actual_symbol.keys(): if (not template_symbol_to_actual_symbol[arg_type._i].differs_only_in_magnitude(actual_symbol)) \ and (not template_symbol_to_actual_symbol[arg_type._i].is_castable_to(actual_symbol)): # if the one cannot be cast into the other code, message = Messages.templated_arg_types_inconsistent(function_name, arg_idx, template_symbol_to_parameter_indices[arg_type._i], failing_arg_type_str=actual_symbol.print_nestml_type( ), other_type_str=template_symbol_to_actual_symbol[arg_type._i].print_nestml_type()) Logger.log_message(code=code, message=message, error_position=node.get_source_position(), log_level=LoggingLevel.ERROR) self._failure_occurred = True return template_symbol_to_parameter_indices[arg_type._i] += [arg_idx] else: template_symbol_to_actual_symbol[arg_type._i] = actual_symbol template_symbol_to_parameter_indices[arg_type._i] = [arg_idx]
def get_multiple_receptors(self) -> List[VariableSymbol]: """ Returns a list of all spike input ports which are defined as both inhibitory *and* excitatory at the same time. :return: a list of spike input port variable symbols """ ret = list() for port in self.get_spike_input_ports(): if port.is_excitatory() and port.is_inhibitory(): if port is not None: ret.append(port) else: code, message = Messages.get_could_not_resolve(port.get_symbol_name()) Logger.log_message( message=message, code=code, error_position=port.get_source_position(), log_level=LoggingLevel.ERROR) return ret
def get_multiple_receptors(self): """ Returns a list of all spike buffers which are defined as inhibitory and excitatory. :return: a list of spike buffers variable symbols :rtype: list(VariableSymbol) """ ret = list() for iBuffer in self.get_spike_buffers(): if iBuffer.is_excitatory() and iBuffer.is_inhibitory(): if iBuffer is not None: ret.append(iBuffer) else: code, message = Messages.get_could_not_resolve(iBuffer.get_symbol_name()) Logger.log_message( message=message, code=code, error_position=iBuffer.get_source_position(), log_level=LoggingLevel.ERROR) return ret
def get_variables(cls, ast_declaration): """ For a given meta_model declaration it returns a list of all corresponding variable symbols. :param ast_declaration: a single meta_model declaration. :type ast_declaration: ASTDeclaration :return: a list of all corresponding variable symbols. :rtype: list(VariableSymbol) """ assert isinstance(ast_declaration, ASTDeclaration), \ '(PyNestML.CodeGeneration.DeclarationsHelper) No or wrong type of declaration provided (%s)!' % type( ast_declaration) ret = list() for var in ast_declaration.get_variables(): symbol = ast_declaration.get_scope().resolve_to_symbol(var.get_complete_name(), SymbolKind.VARIABLE) if symbol is not None: ret.append(symbol) else: code, message = Messages.get_could_not_resolve(var.get_complete_name()) Logger.log_message(code=code, message=message, error_position=ast_declaration.get_source_position(), log_level=LoggingLevel.ERROR) return ret
def convert_name_reference(self, variable: ASTVariable, prefix='') -> str: """ Converts a single variable to nest processable format. :param variable: a single variable. :type variable: ASTVariable :return: a nest processable format. """ from pynestml.codegeneration.nest_printer import NestPrinter if isinstance(variable, ASTExternalVariable): _name = str(variable) if variable.get_alternate_name(): # the disadvantage of this approach is that the time the value is to be obtained is not explicitly specified, so we will actually get the value at the end of the min_delay timestep return "((POST_NEURON_TYPE*)(__target))->get_" + variable.get_alternate_name( ) + "()" return "((POST_NEURON_TYPE*)(__target))->get_" + _name + "(_tr_t)" if variable.get_name() == PredefinedVariables.E_CONSTANT: return 'numerics::e' symbol = variable.get_scope().resolve_to_symbol( variable.get_complete_name(), SymbolKind.VARIABLE) if symbol is None: # test if variable name can be resolved to a type if PredefinedUnits.is_unit(variable.get_complete_name()): return str( UnitConverter.get_factor( PredefinedUnits.get_unit( variable.get_complete_name()).get_unit())) code, message = Messages.get_could_not_resolve(variable.get_name()) Logger.log_message(log_level=LoggingLevel.ERROR, code=code, message=message, error_position=variable.get_source_position()) return '' if symbol.is_local(): return variable.get_name() + ( '[' + variable.get_vector_parameter() + ']' if symbol.has_vector_parameter() else '') if symbol.is_buffer(): if isinstance(symbol.get_type_symbol(), UnitTypeSymbol): units_conversion_factor = UnitConverter.get_factor( symbol.get_type_symbol().unit.unit) else: units_conversion_factor = 1 s = "" if not units_conversion_factor == 1: s += "(" + str(units_conversion_factor) + " * " s += NestPrinter.print_origin( symbol, prefix=prefix) + NestNamesConverter.buffer_value(symbol) if symbol.has_vector_parameter(): s += '[' + variable.get_vector_parameter() + ']' if not units_conversion_factor == 1: s += ")" return s if symbol.is_inline_expression: return 'get_' + variable.get_name() + '()' + ( '[i]' if symbol.has_vector_parameter() else '') if symbol.is_kernel(): assert False, "NEST reference converter cannot print kernel; kernel should have been converted during code generation" if symbol.is_state(): temp = "" temp += NestNamesConverter.getter(symbol) + "()" temp += ('[' + variable.get_vector_parameter() + ']' if symbol.has_vector_parameter() else '') return temp variable_name = NestNamesConverter.convert_to_cpp_name( variable.get_complete_name()) if symbol.is_local(): return variable_name + ('[i]' if symbol.has_vector_parameter() else '') if symbol.is_inline_expression: return 'get_' + variable_name + '()' + ( '[i]' if symbol.has_vector_parameter() else '') return NestPrinter.print_origin(symbol, prefix=prefix) + \ NestNamesConverter.name(symbol) + \ ('[' + variable.get_vector_parameter() + ']' if symbol.has_vector_parameter() else '')
def convert_name_reference(self, variable, prefix='', with_origins = True): """ Converts a single variable to nest processable format. :param variable: a single variable. :type variable: ASTVariable :return: a nest processable format. :rtype: str """ from pynestml.codegeneration.nest_printer import NestPrinter assert (variable is not None and isinstance(variable, ASTVariable)), \ '(PyNestML.CodeGeneration.NestReferenceConverter) No or wrong type of uses-gsl provided (%s)!' % type( variable) variable_name = NestNamesConverter.convert_to_cpp_name(variable.get_complete_name()) if variable_name == PredefinedVariables.E_CONSTANT: return 'numerics::e' assert variable.get_scope() is not None, "Undeclared variable: " + variable.get_complete_name() symbol = variable.get_scope().resolve_to_symbol(variable_name, SymbolKind.VARIABLE) if symbol is None: # test if variable name can be resolved to a type if PredefinedUnits.is_unit(variable.get_complete_name()): return str(UnitConverter.get_factor(PredefinedUnits.get_unit(variable.get_complete_name()).get_unit())) code, message = Messages.get_could_not_resolve(variable_name) Logger.log_message(log_level=LoggingLevel.ERROR, code=code, message=message, error_position=variable.get_source_position()) return '' if symbol.is_local(): return variable_name + ('[i]' if symbol.has_vector_parameter() else '') if symbol.is_buffer(): if isinstance(symbol.get_type_symbol(), UnitTypeSymbol): units_conversion_factor = UnitConverter.get_factor(symbol.get_type_symbol().unit.unit) else: units_conversion_factor = 1 s = "" if not units_conversion_factor == 1: s += "(" + str(units_conversion_factor) + " * " s += NestPrinter.print_origin(symbol, prefix=prefix) if with_origins else '' s += NestNamesConverter.buffer_value(symbol) if symbol.has_vector_parameter(): s += '[i]' if not units_conversion_factor == 1: s += ")" return s if symbol.is_inline_expression: return 'get_' + variable_name + '()' + ('[i]' if symbol.has_vector_parameter() else '') if symbol.is_kernel(): assert False, "NEST reference converter cannot print kernel; kernel should have been converted during code generation" if symbol.is_state(): temp = NestPrinter.print_origin(symbol, prefix=prefix) if with_origins else '' if self.uses_gsl: temp += GSLNamesConverter.name(symbol) else: temp += NestNamesConverter.name(symbol) temp += ('[i]' if symbol.has_vector_parameter() else '') return temp return (NestPrinter.print_origin(symbol, prefix=prefix) if with_origins else '') + \ NestNamesConverter.name(symbol) + \ ('[i]' if symbol.has_vector_parameter() else '')
def visit_simple_expression(self, node): """ Visits a single function call as stored in a simple expression and derives the correct type of all its parameters. :param node: a simple expression :type node: ASTSimpleExpression :rtype void """ assert isinstance(node, ASTSimpleExpression), \ '(PyNestML.Visitor.FunctionCallVisitor) No or wrong type of simple expression provided (%s)!' % tuple(node) assert (node.get_scope() is not None), \ "(PyNestML.Visitor.FunctionCallVisitor) No scope found, run symboltable creator!" scope = node.get_scope() function_name = node.get_function_call().get_name() method_symbol = scope.resolve_to_symbol(function_name, SymbolKind.FUNCTION) # check if this function exists if method_symbol is None: code, message = Messages.get_could_not_resolve(function_name) Logger.log_message(code=code, message=message, error_position=node.get_source_position(), log_level=LoggingLevel.ERROR) node.type = ErrorTypeSymbol() return return_type = method_symbol.get_return_type() if isinstance(return_type, TemplateTypeSymbol): for i, arg_type in enumerate(method_symbol.param_types): if arg_type == return_type: return_type = node.get_function_call().get_args()[i].type break if isinstance(return_type, TemplateTypeSymbol): # error: return type template not found among parameter type templates assert(False) # check for consistency among actual derived types for template parameters from pynestml.cocos.co_co_function_argument_template_types_consistent import CorrectTemplatedArgumentTypesVisitor correctTemplatedArgumentTypesVisitor = CorrectTemplatedArgumentTypesVisitor() correctTemplatedArgumentTypesVisitor._failure_occurred = False node.accept(correctTemplatedArgumentTypesVisitor) if correctTemplatedArgumentTypesVisitor._failure_occurred: return_type = ErrorTypeSymbol() return_type.referenced_object = node # convolve symbol does not have a return type set. # returns whatever type the second parameter is. if function_name == PredefinedFunctions.CONVOLVE: # Deviations from the assumptions made here are handled in the convolveCoco buffer_parameter = node.get_function_call().get_args()[1] if buffer_parameter.get_variable() is not None: buffer_name = buffer_parameter.get_variable().get_name() buffer_symbol_resolve = scope.resolve_to_symbol(buffer_name, SymbolKind.VARIABLE) if buffer_symbol_resolve is not None: node.type = buffer_symbol_resolve.get_type_symbol() return # getting here means there is an error with the parameters to convolve code, message = Messages.get_convolve_needs_buffer_parameter() Logger.log_message(code=code, message=message, error_position=node.get_source_position(), log_level=LoggingLevel.ERROR) node.type = ErrorTypeSymbol() return if isinstance(method_symbol.get_return_type(), VoidTypeSymbol): # todo: the error message is not used here, fix this # error_msg = ErrorStrings.message_void_function_on_rhs(self, function_name, node.get_source_position()) node.type = ErrorTypeSymbol() return # if nothing special is handled, just get the expression type from the return type of the function node.type = return_type