def check_co_co(cls, _neuron=None): """ Checks the coco for the handed over neuron. :param _neuron: a single neuron instance. :type _neuron: ASTNeuron """ assert (_neuron is not None and isinstance(_neuron, ASTNeuron)), \ '(PyNestML.CoCo.FunctionCallsConsistent) No or wrong type of neuron provided (%s)!' % type(_neuron) cls.__neuronName = _neuron.get_name() for userDefinedFunction in _neuron.get_functions(): cls.processed_function = userDefinedFunction symbol = userDefinedFunction.get_scope().resolve_to_symbol(userDefinedFunction.get_name(), SymbolKind.FUNCTION) # first ensure that the block contains at least one statement if symbol is not None and len(userDefinedFunction.get_block().get_stmts()) > 0: # now check that the last statement is a return cls.__check_return_recursively(symbol.get_return_type(), userDefinedFunction.get_block().get_stmts(), False) # now if it does not have a statement, but uses a return type, it is an error elif symbol is not None and userDefinedFunction.has_return_type() and \ not symbol.get_return_type().equals(PredefinedTypes.get_void_type()): code, message = Messages.get_no_return() Logger.log_message(neuron=_neuron, code=code, message=message, error_position=userDefinedFunction.get_source_position(), log_level=LoggingLevel.ERROR) return
def check_co_co(cls, _neuron=None): """ Checks the coco for the handed over neuron. :param _neuron: a single neuron instance. :type _neuron: ASTNeuron """ assert (_neuron is not None and isinstance(_neuron, ASTNeuron)), \ '(PyNestML.CoCo.FunctionCallsConsistent) No or wrong type of neuron provided (%s)!' % type(_neuron) cls.__neuronName = _neuron.get_name() for userDefinedFunction in _neuron.get_functions(): cls.processed_function = userDefinedFunction symbol = userDefinedFunction.get_scope().resolve_to_symbol(userDefinedFunction.get_name(), SymbolKind.FUNCTION) # first ensure that the block contains at least one statement if symbol is not None and len(userDefinedFunction.get_block().get_stmts()) > 0: # now check that the last statement is a return cls.__check_return_recursively(symbol.get_return_type(), userDefinedFunction.get_block().get_stmts(), False) # now if it does not have a statement, but uses a return type, it is an error elif symbol is not None and userDefinedFunction.has_return_type() and \ not symbol.get_return_type().equals(PredefinedTypes.get_void_type()): code, message = Messages.get_no_return() Logger.log_message(node=_neuron, code=code, message=message, error_position=userDefinedFunction.get_source_position(), log_level=LoggingLevel.ERROR) return
def __check_return_recursively(cls, type_symbol=None, stmts=None, ret_defined=False): """ For a handed over statement, it checks if the statement is a return statement and if it is typed according to the handed over type symbol. :param type_symbol: a single type symbol :type type_symbol: type_symbol :param stmts: a list of statements, either simple or compound :type stmts: list(ASTSmallStmt,ASTCompoundStmt) :param ret_defined: indicates whether a ret has already beef defined after this block of stmt, thus is not necessary. Implies that the return has been defined in the higher level block :type ret_defined: bool """ # in order to ensure that in the sub-blocks, a return is not necessary, we check if the last one in this # block is a return statement, thus it is not required to have a return in the sub-blocks, but optional last_statement = stmts[len(stmts) - 1] ret_defined = False or ret_defined if (len(stmts) > 0 and isinstance(last_statement, ASTStmt) and last_statement.is_small_stmt() and last_statement.small_stmt.is_return_stmt()): ret_defined = True # now check that returns are there if necessary and correctly typed for c_stmt in stmts: if c_stmt.is_small_stmt(): stmt = c_stmt.small_stmt else: stmt = c_stmt.compound_stmt # if it is a small statement, check if it is a return statement if isinstance(stmt, ASTSmallStmt) and stmt.is_return_stmt(): # first check if the return is the last one in this block of statements if stmts.index(c_stmt) != (len(stmts) - 1): code, message = Messages.get_not_last_statement('Return') Logger.log_message(error_position=stmt.get_source_position(), code=code, message=message, log_level=LoggingLevel.WARNING) # now check that it corresponds to the declared type if stmt.get_return_stmt().has_expression() and type_symbol is PredefinedTypes.get_void_type(): code, message = Messages.get_type_different_from_expected(PredefinedTypes.get_void_type(), stmt.get_return_stmt().get_expression().type) Logger.log_message(error_position=stmt.get_source_position(), message=message, code=code, log_level=LoggingLevel.ERROR) # if it is not void check if the type corresponds to the one stated if not stmt.get_return_stmt().has_expression() and \ not type_symbol.equals(PredefinedTypes.get_void_type()): code, message = Messages.get_type_different_from_expected(PredefinedTypes.get_void_type(), type_symbol) Logger.log_message(error_position=stmt.get_source_position(), message=message, code=code, log_level=LoggingLevel.ERROR) if stmt.get_return_stmt().has_expression(): type_of_return = stmt.get_return_stmt().get_expression().type if isinstance(type_of_return, ErrorTypeSymbol): code, message = Messages.get_type_could_not_be_derived(cls.processed_function.get_name()) Logger.log_message(error_position=stmt.get_source_position(), code=code, message=message, log_level=LoggingLevel.ERROR) elif not type_of_return.equals(type_symbol): TypeCaster.try_to_recover_or_error(type_symbol, type_of_return, stmt.get_return_stmt().get_expression()) elif isinstance(stmt, ASTCompoundStmt): # otherwise it is a compound stmt, thus check recursively if stmt.is_if_stmt(): cls.__check_return_recursively(type_symbol, stmt.get_if_stmt().get_if_clause().get_block().get_stmts(), ret_defined) for else_ifs in stmt.get_if_stmt().get_elif_clauses(): cls.__check_return_recursively(type_symbol, else_ifs.get_block().get_stmt(), ret_defined) if stmt.get_if_stmt().has_else_clause(): cls.__check_return_recursively(type_symbol, stmt.get_if_stmt().get_else_clause().get_block().get_stmts(), ret_defined) elif stmt.is_while_stmt(): cls.__check_return_recursively(type_symbol, stmt.get_while_stmt().get_block().get_stmts(), ret_defined) elif stmt.is_for_stmt(): cls.__check_return_recursively(type_symbol, stmt.get_for_stmt().get_block().get_stmts(), ret_defined) # now, if a return statement has not been defined in the corresponding higher level block, we have # to ensure that it is defined here elif not ret_defined and stmts.index(c_stmt) == (len(stmts) - 1): if not (isinstance(stmt, ASTSmallStmt) and stmt.is_return_stmt()): code, message = Messages.get_no_return() Logger.log_message(error_position=stmt.get_source_position(), log_level=LoggingLevel.ERROR, code=code, message=message) return
def __check_return_recursively(cls, type_symbol=None, stmts=None, ret_defined=False): """ For a handed over statement, it checks if the statement is a return statement and if it is typed according to the handed over type symbol. :param type_symbol: a single type symbol :type type_symbol: type_symbol :param stmts: a list of statements, either simple or compound :type stmts: list(ASTSmallStmt,ASTCompoundStmt) :param ret_defined: indicates whether a ret has already beef defined after this block of stmt, thus is not necessary. Implies that the return has been defined in the higher level block :type ret_defined: bool """ # in order to ensure that in the sub-blocks, a return is not necessary, we check if the last one in this # block is a return statement, thus it is not required to have a return in the sub-blocks, but optional last_statement = stmts[len(stmts) - 1] ret_defined = False or ret_defined if (len(stmts) > 0 and isinstance(last_statement, ASTStmt) and last_statement.is_small_stmt() and last_statement.small_stmt.is_return_stmt()): ret_defined = True # now check that returns are there if necessary and correctly typed for c_stmt in stmts: if c_stmt.is_small_stmt(): stmt = c_stmt.small_stmt else: stmt = c_stmt.compound_stmt # if it is a small statement, check if it is a return statement if isinstance(stmt, ASTSmallStmt) and stmt.is_return_stmt(): # first check if the return is the last one in this block of statements if stmts.index(c_stmt) != (len(stmts) - 1): code, message = Messages.get_not_last_statement('Return') Logger.log_message(error_position=stmt.get_source_position(), code=code, message=message, log_level=LoggingLevel.WARNING) # now check that it corresponds to the declared type if stmt.get_return_stmt().has_expression() and type_symbol is PredefinedTypes.get_void_type(): code, message = Messages.get_type_different_from_expected(PredefinedTypes.get_void_type(), stmt.get_return_stmt().get_expression().type) Logger.log_message(error_position=stmt.get_source_position(), message=message, code=code, log_level=LoggingLevel.ERROR) # if it is not void check if the type corresponds to the one stated if not stmt.get_return_stmt().has_expression() and \ not type_symbol.equals(PredefinedTypes.get_void_type()): code, message = Messages.get_type_different_from_expected(PredefinedTypes.get_void_type(), type_symbol) Logger.log_message(error_position=stmt.get_source_position(), message=message, code=code, log_level=LoggingLevel.ERROR) if stmt.get_return_stmt().has_expression(): type_of_return = stmt.get_return_stmt().get_expression().type if isinstance(type_of_return, ErrorTypeSymbol): code, message = Messages.get_type_could_not_be_derived(cls.processed_function.get_name()) Logger.log_message(error_position=stmt.get_source_position(), code=code, message=message, log_level=LoggingLevel.ERROR) elif not type_of_return.equals(type_symbol): TypeCaster.try_to_recover_or_error(type_symbol, type_of_return, stmt.get_return_stmt().get_expression()) elif isinstance(stmt, ASTCompoundStmt): # otherwise it is a compound stmt, thus check recursively if stmt.is_if_stmt(): cls.__check_return_recursively(type_symbol, stmt.get_if_stmt().get_if_clause().get_block().get_stmts(), ret_defined) for else_ifs in stmt.get_if_stmt().get_elif_clauses(): cls.__check_return_recursively(type_symbol, else_ifs.get_block().get_stmt(), ret_defined) if stmt.get_if_stmt().has_else_clause(): cls.__check_return_recursively(type_symbol, stmt.get_if_stmt().get_else_clause().get_block().get_stmts(), ret_defined) elif stmt.is_while_stmt(): cls.__check_return_recursively(type_symbol, stmt.get_while_stmt().get_block().get_stmts(), ret_defined) elif stmt.is_for_stmt(): cls.__check_return_recursively(type_symbol, stmt.get_for_stmt().get_block().get_stmts(), ret_defined) # now, if a return statement has not been defined in the corresponding higher level block, we have # to ensure that it is defined here elif not ret_defined and stmts.index(c_stmt) == (len(stmts) - 1): if not (isinstance(stmt, ASTSmallStmt) and stmt.is_return_stmt()): code, message = Messages.get_no_return() Logger.log_message(error_position=stmt.get_source_position(), log_level=LoggingLevel.ERROR, code=code, message=message) return