def get_collect_function_call(self, variable, collect_var): """ Represents a call to cast function responsible of collecting value from python object. Parameters: ---------- variable: variable the variable needed to collect collect_var : the pyobject variable """ if variable.rank > 0: return FunctionCall(numpy_get_data, [collect_var]) if isinstance(variable.dtype, NativeComplex): return self.get_cast_function_call('pycomplex_to_complex', collect_var) if isinstance(variable.dtype, NativeBool): return self.get_cast_function_call('pybool_to_bool', collect_var) try: collect_function = collect_function_registry[variable.dtype] except KeyError: errors.report(PYCCEL_RESTRICTION_TODO, symbol=variable.dtype, severity='fatal') return FunctionCall(collect_function, [collect_var])
def _visit_Call(self, stmt): args = [] if stmt.args: args += self._visit(stmt.args) if stmt.keywords: args += self._visit(stmt.keywords) if len(args) == 0: args = () func = self._visit(stmt.func) if isinstance(func, Symbol): f_name = str(func.name) if f_name == "print": func = PythonPrint(PythonTuple(*args)) else: func = FunctionCall(f_name, args) elif isinstance(func, DottedName): f_name = str(func.name[-1]) func_attr = FunctionCall(f_name, args) func = DottedName(*func.name[:-1], func_attr) else: raise NotImplementedError(' Unknown function type {}'.format( str(type(func)))) return func
def _create_collecting_value_body(self, variable, collect_var, tmp_variable = None): """ Create If block to differentiate between python and numpy data types when collecting value format : if (collect_var is numpy_scalar) collect_value from numpy type else collect value from python type Parameters: ---------- variable: variable the variable needed to collect collect_var : variable the pyobject variable tmp_variable : variable temporary variable to hold value default None Returns ------- body : If block """ var = tmp_variable if tmp_variable else variable python_type_collect_func_call = self.get_collect_function_call(variable, collect_var) numpy_type_collect_func_call = FunctionCall(PyArray_ScalarAsCtype, [collect_var, var]) check_scalar_type = FunctionCall(PyArray_CheckScalar, [collect_var]) body = If((check_scalar_type, [numpy_type_collect_func_call]), (LiteralTrue() , [Assign(var, python_type_collect_func_call)])) return body
def get_cast_function_call(self, cast_type, arg): """ Represents a call to cast function responsible of the conversion of one data type into another. Parameters: ---------- cast_type: string The type of cast function on format 'data type_to_data type' arg: variable the variable needed to cast """ if cast_type in self._cast_functions_dict: cast_function = self._cast_functions_dict[cast_type] else: cast_function_name = self.get_new_name(self._global_names, cast_type) try: cast_function = cast_function_registry[cast_type]( cast_function_name) except KeyError as e: raise NotImplementedError( "No conversion function : {}".format(cast_type)) from e self._cast_functions_dict[cast_type] = cast_function return FunctionCall(cast_function, [arg])
def _get_static_function(self, used_names, function, collect_dict): """ Create arguments and functioncall for arguments rank > 0 in fortran. Format : a is numpy array func(a) ==> static_func(a.DIM , a.DATA) where a.DATA = buffer holding data a.DIM = size of array """ additional_body = [] if self._target_language == 'fortran': static_args = [] for a in function.arguments: if isinstance(a, Variable) and a.rank > 0: # Add shape arguments for static function for i in range(collect_dict[a].rank): var = Variable(dtype=NativeInteger(), name=self.get_new_name( used_names, a.name + "_dim")) body = FunctionCall(numpy_get_dim, [collect_dict[a], i]) if a.is_optional: body = IfTernaryOperator( VariableAddress(collect_dict[a]), body, LiteralInteger(0)) body = Assign(var, body) additional_body.append(body) static_args.append(var) static_args.append(a) static_function = as_static_function_call(function, self._module_name, name=function.name) else: static_function = function static_args = function.arguments return static_function, static_args, additional_body
def as_static_function_call(func, mod_name, name=None): assert isinstance(func, FunctionDef) assert isinstance(mod_name, str) # create function alias by prepending 'mod_' to its name func_alias = func.clone('mod_' + str(func.name)) # from module import func as func_alias imports = [Import(target=AsName(func.name, func_alias.name), source=mod_name)] # function arguments args = sanitize_arguments(func.arguments) # function body call = FunctionCall(func_alias, args) results = func.results results = results[0] if len(results) == 1 else results stmt = call if len(func.results) == 0 else Assign(results, call) body = [stmt] # new function declaration new_func = FunctionDef(func.name, list(args), func.results, body, arguments_inout = func.arguments_inout, functions = func.functions, interfaces = func.interfaces, imports = imports, doc_string = func.doc_string, ) # make it compatible with c static_func = as_static_function(new_func, name) return static_func
def _visit_GeneratorExp(self, stmt): result = self._visit(stmt.elt) generators = self._visit(stmt.generators) parent = self._scope[-3] if not isinstance(parent, ast.Call): raise NotImplementedError( "GeneratorExp is not the argument of a function call") name = str(self._visit(parent.func)) grandparent = self._scope[-4] if isinstance(grandparent, ast.Assign): if len(grandparent.targets) != 1: raise NotImplementedError( "Cannot unpack function with generator expression argument" ) lhs = self._visit(grandparent.targets[0]) else: lhs = self.get_new_variable() body = result if name == 'sum': body = AugAssign(lhs, '+', body) else: body = FunctionCall(name, (lhs, body)) body = Assign(lhs, body) body.set_fst(parent) indices = [] generators = list(generators) while len(generators) > 0: indices.append(generators[-1].target) generators[-1].insert2body(body) body = generators.pop() indices = indices[::-1] body = [body] if name == 'sum': expr = FunctionalSum(body, result, lhs, indices, None) elif name == 'min': expr = FunctionalMin(body, result, lhs, indices, None) elif name == 'max': expr = FunctionalMax(body, result, lhs, indices, None) else: errors.report(PYCCEL_RESTRICTION_TODO, symbol=name, bounding_box=(stmt.lineno, stmt.col_offset), severity='fatal') expr.set_fst(stmt) return expr
def as_static_function_call(func): assert (isinstance(func, FunctionDef)) args = func.arguments args = sanitize_arguments(args) functions = func.functions body = [FunctionCall(func, args)] func = FunctionDef(func.name, list(args), [], body, arguments_inout=func.arguments_inout, functions=functions) static_func = as_static_function(func) return static_func
def _get_check_type_statement(self, variable, collect_var): if variable.rank > 0 : numpy_dtype = self.find_in_numpy_dtype_registry(variable) check = PyccelEq(FunctionCall(numpy_get_type, [collect_var]), numpy_dtype) else : python_check = PythonType_Check(variable, collect_var) numpy_check = NumpyType_Check(variable, collect_var) if variable.precision == default_precision[str_dtype(variable.dtype)] : check = PyccelOr(python_check, numpy_check) else : check = PyccelAssociativeParenthesis(PyccelAnd(PyccelNot(python_check), numpy_check)) if isinstance(variable, ValuedVariable): default = PyccelNot(VariableAddress(collect_var)) if variable.rank > 0 else PyccelEq(VariableAddress(collect_var), VariableAddress(Py_None)) check = PyccelAssociativeParenthesis(PyccelOr(default, check)) return check
def __call__(self, arguments): # ... if not isinstance(arguments, (list, tuple, Tuple)): arguments = [arguments] arguments = Tuple(*arguments) assert(len(self.target) == len(arguments)) assert(isinstance(self.funcdef, FunctionDef)) # ... # ... func = self.func target = self.target funcdef = self.funcdef func_args = funcdef.arguments # ... # ... target_arg_names = [x.name for x in list(target.keys())] args = [] current = 0 for x in func_args: if x.name in target_arg_names: arg = [a for k,a in target.items() if k.name == x.name] assert(len(arg) == 1) arg = arg[0] args += [arg] else: args += [arguments[current]] current += 1 # ... args = Tuple(*args) return FunctionCall(func.name, args)
def __call__(self, *args): args = Tuple(*args) return FunctionCall(self.name, args)
def _print_FunctionDef(self, expr): # Save all used names used_names = set([a.name for a in expr.arguments] + [r.name for r in expr.results] + [expr.name.name]) # Find a name for the wrapper function wrapper_name = self._get_wrapper_name(used_names, expr) used_names.add(wrapper_name) # Collect local variables wrapper_vars = {a.name: a for a in expr.arguments} wrapper_vars.update({r.name: r for r in expr.results}) python_func_args = self.get_new_PyObject("args", used_names) python_func_kwargs = self.get_new_PyObject("kwargs", used_names) python_func_selfarg = self.get_new_PyObject("self", used_names) # Collect arguments and results wrapper_args = [ python_func_selfarg, python_func_args, python_func_kwargs ] wrapper_results = [self.get_new_PyObject("result", used_names)] if expr.is_private: wrapper_func = FunctionDef( name=wrapper_name, arguments=wrapper_args, results=wrapper_results, body=[ PyErr_SetString( 'PyExc_NotImplementedError', '"Private functions are not accessible from python"'), AliasAssign(wrapper_results[0], Nil()), Return(wrapper_results) ]) return CCodePrinter._print_FunctionDef(self, wrapper_func) if any(isinstance(arg, FunctionAddress) for arg in expr.arguments): wrapper_func = FunctionDef( name=wrapper_name, arguments=wrapper_args, results=wrapper_results, body=[ PyErr_SetString('PyExc_NotImplementedError', '"Cannot pass a function as an argument"'), AliasAssign(wrapper_results[0], Nil()), Return(wrapper_results) ]) return CCodePrinter._print_FunctionDef(self, wrapper_func) # Collect argument names for PyArgParse arg_names = [a.name for a in expr.arguments] keyword_list_name = self.get_new_name(used_names, 'kwlist') keyword_list = PyArgKeywords(keyword_list_name, arg_names) wrapper_body = [keyword_list] wrapper_body_translations = [] parse_args = [] collect_vars = {} for arg in expr.arguments: collect_var, cast_func = self.get_PyArgParseType(used_names, arg) collect_vars[arg] = collect_var body, tmp_variable = self._body_management(used_names, arg, collect_var, cast_func, True) if tmp_variable: wrapper_vars[tmp_variable.name] = tmp_variable # If the variable cannot be collected from PyArgParse directly wrapper_vars[collect_var.name] = collect_var # Save cast to argument variable wrapper_body_translations.extend(body) parse_args.append(collect_var) # Write default values if isinstance(arg, ValuedVariable): wrapper_body.append( self.get_default_assign(parse_args[-1], arg)) # Parse arguments parse_node = PyArg_ParseTupleNode(python_func_args, python_func_kwargs, expr.arguments, parse_args, keyword_list) wrapper_body.append(If((PyccelNot(parse_node), [Return([Nil()])]))) wrapper_body.extend(wrapper_body_translations) # Call function static_function, static_args, additional_body = self._get_static_function( used_names, expr, collect_vars) wrapper_body.extend(additional_body) for var in static_args: wrapper_vars[var.name] = var if len(expr.results) == 0: func_call = FunctionCall(static_function, static_args) else: results = expr.results if len( expr.results) > 1 else expr.results[0] func_call = Assign(results, FunctionCall(static_function, static_args)) wrapper_body.append(func_call) # Loop over results to carry out necessary casts and collect Py_BuildValue type string res_args = [] for a in expr.results: collect_var, cast_func = self.get_PyBuildValue(used_names, a) if cast_func is not None: wrapper_vars[collect_var.name] = collect_var wrapper_body.append(AliasAssign(collect_var, cast_func)) res_args.append( VariableAddress(collect_var) if collect_var. is_pointer else collect_var) # Call PyBuildNode wrapper_body.append( AliasAssign(wrapper_results[0], PyBuildValueNode(res_args))) # Call free function for python type wrapper_body += [ FunctionCall(Py_DECREF, [i]) for i in self._to_free_PyObject_list ] self._to_free_PyObject_list.clear() #Return wrapper_body.append(Return(wrapper_results)) # Create FunctionDef and write using classic method wrapper_func = FunctionDef(name=wrapper_name, arguments=wrapper_args, results=wrapper_results, body=wrapper_body, local_vars=wrapper_vars.values()) return CCodePrinter._print_FunctionDef(self, wrapper_func)
def _print_Interface(self, expr): # Collecting all functions funcs = expr.functions # Save all used names used_names = set(n.name for n in funcs) # Find a name for the wrapper function wrapper_name = self._get_wrapper_name(used_names, expr) self._global_names.add(wrapper_name) # Collect local variables python_func_args = self.get_new_PyObject("args", used_names) python_func_kwargs = self.get_new_PyObject("kwargs", used_names) python_func_selfarg = self.get_new_PyObject("self", used_names) # Collect wrapper arguments and results wrapper_args = [ python_func_selfarg, python_func_args, python_func_kwargs ] wrapper_results = [self.get_new_PyObject("result", used_names)] # Collect parser arguments wrapper_vars = {} # Collect argument names for PyArgParse arg_names = [a.name for a in funcs[0].arguments] keyword_list_name = self.get_new_name(used_names, 'kwlist') keyword_list = PyArgKeywords(keyword_list_name, arg_names) wrapper_body = [keyword_list] wrapper_body_translations = [] body_tmp = [] # To store the mini function responsible of collecting value and calling interfaces functions and return the builded value funcs_def = [] default_value = { } # dict to collect all initialisation needed in the wrapper check_var = Variable(dtype=NativeInteger(), name=self.get_new_name(used_names, "check")) wrapper_vars[check_var.name] = check_var types_dict = OrderedDict( (a, set()) for a in funcs[0].arguments ) #dict to collect each variable possible type and the corresponding flags # collect parse arg parse_args = [ Variable(dtype=PyccelPyArrayObject(), is_pointer=True, rank=a.rank, order=a.order, name=self.get_new_name(used_names, a.name + "_tmp")) if a.rank > 0 else Variable(dtype=PyccelPyObject(), name=self.get_new_name(used_names, a.name + "_tmp"), is_pointer=True) for a in funcs[0].arguments ] # Managing the body of wrapper for func in funcs: mini_wrapper_func_body = [] res_args = [] mini_wrapper_func_vars = {a.name: a for a in func.arguments} flags = 0 collect_vars = {} # Loop for all args in every functions and create the corresponding condition and body for p_arg, f_arg in zip(parse_args, func.arguments): collect_vars[f_arg] = p_arg body, tmp_variable = self._body_management( used_names, f_arg, p_arg, None) if tmp_variable: mini_wrapper_func_vars[tmp_variable.name] = tmp_variable # get check type function check = self._get_check_type_statement(f_arg, p_arg) # If the variable cannot be collected from PyArgParse directly wrapper_vars[p_arg.name] = p_arg # Save the body wrapper_body_translations.extend(body) # Write default values if isinstance(f_arg, ValuedVariable): wrapper_body.append( self.get_default_assign(parse_args[-1], f_arg)) flag_value = flags_registry[(f_arg.dtype, f_arg.precision)] flags = (flags << 4) + flag_value # shift by 4 to the left types_dict[f_arg].add( (f_arg, check, flag_value)) # collect variable type for each arguments mini_wrapper_func_body += body # create the corresponding function call static_function, static_args, additional_body = self._get_static_function( used_names, func, collect_vars) mini_wrapper_func_body.extend(additional_body) for var in static_args: mini_wrapper_func_vars[var.name] = var if len(func.results) == 0: func_call = FunctionCall(static_function, static_args) else: results = func.results if len( func.results) > 1 else func.results[0] func_call = Assign(results, FunctionCall(static_function, static_args)) mini_wrapper_func_body.append(func_call) # Loop for all res in every functions and create the corresponding body and cast for r in func.results: collect_var, cast_func = self.get_PyBuildValue(used_names, r) mini_wrapper_func_vars[collect_var.name] = collect_var if cast_func is not None: mini_wrapper_func_vars[r.name] = r mini_wrapper_func_body.append( AliasAssign(collect_var, cast_func)) res_args.append( VariableAddress(collect_var) if collect_var. is_pointer else collect_var) # Building PybuildValue and freeing the allocated variable after. mini_wrapper_func_body.append( AliasAssign(wrapper_results[0], PyBuildValueNode(res_args))) mini_wrapper_func_body += [ FunctionCall(Py_DECREF, [i]) for i in self._to_free_PyObject_list ] mini_wrapper_func_body.append(Return(wrapper_results)) self._to_free_PyObject_list.clear() # Building Mini wrapper function mini_wrapper_func_name = self.get_new_name( used_names.union(self._global_names), func.name.name + '_mini_wrapper') self._global_names.add(mini_wrapper_func_name) mini_wrapper_func_def = FunctionDef( name=mini_wrapper_func_name, arguments=parse_args, results=wrapper_results, body=mini_wrapper_func_body, local_vars=mini_wrapper_func_vars.values()) funcs_def.append(mini_wrapper_func_def) # append check condition to the functioncall body_tmp.append((PyccelEq(check_var, LiteralInteger(flags)), [ AliasAssign(wrapper_results[0], FunctionCall(mini_wrapper_func_def, parse_args)) ])) # Errors / Types management # Creating check_type function check_func_def = self._create_wrapper_check(check_var, parse_args, types_dict, used_names, funcs[0].name.name) funcs_def.append(check_func_def) # Create the wrapper body with collected informations body_tmp = [((PyccelNot(check_var), [Return([Nil()])]))] + body_tmp body_tmp.append((LiteralTrue(), [ PyErr_SetString('PyExc_TypeError', '"Arguments combinations don\'t exist"'), Return([Nil()]) ])) wrapper_body_translations = [If(*body_tmp)] # Parsing Arguments parse_node = PyArg_ParseTupleNode(python_func_args, python_func_kwargs, funcs[0].arguments, parse_args, keyword_list, True) wrapper_body += list(default_value.values()) wrapper_body.append(If((PyccelNot(parse_node), [Return([Nil()])]))) #finishing the wrapper body wrapper_body.append( Assign(check_var, FunctionCall(check_func_def, parse_args))) wrapper_body.extend(wrapper_body_translations) wrapper_body.append(Return(wrapper_results)) # Return # Create FunctionDef funcs_def.append( FunctionDef(name=wrapper_name, arguments=wrapper_args, results=wrapper_results, body=wrapper_body, local_vars=wrapper_vars.values())) sep = self._print(SeparatorComment(40)) return sep + '\n'.join( CCodePrinter._print_FunctionDef(self, f) for f in funcs_def)
def _body_array(self, variable, collect_var, check_type=False): """ Responsible for collecting value and managing error and create the body of arguments with rank greater than 0 in format if (rank check == False){ print TypeError Wrong rank return Null }else if(Type Check == False){ Print TypeError Wrong type return Null }else if (order check == False){ #check for order for rank > 1 Print NotImplementedError Wrong Order return Null } collect the value from PyArrayObject Parameters: ---------- Variable : Variable The optional variable collect_var : variable the pyobject type variable holder of value check_type : Boolean True if the type is needed Returns ------- body : list A list of statements """ body = [] #TODO create and extern rank and order check function #check optional : if variable.is_optional: check = PyccelNot(VariableAddress(collect_var)) body += [(check, [Assign(VariableAddress(variable), Nil())])] #rank check : check = PyccelNe(FunctionCall(numpy_get_ndims, [collect_var]), LiteralInteger(collect_var.rank)) error = PyErr_SetString( 'PyExc_TypeError', '"{} must have rank {}"'.format(collect_var, str(collect_var.rank))) body += [(check, [error, Return([Nil()])])] if check_type: #Type check numpy_dtype = self.find_in_numpy_dtype_registry(variable) arg_dtype = self.find_in_dtype_registry( self._print(variable.dtype), variable.precision) check = PyccelNe(FunctionCall(numpy_get_type, [collect_var]), numpy_dtype) info_dump = PythonPrint( [FunctionCall(numpy_get_type, [collect_var]), numpy_dtype]) error = PyErr_SetString( 'PyExc_TypeError', '"{} must be {}"'.format(variable, arg_dtype)) body += [(check, [info_dump, error, Return([Nil()])])] if collect_var.rank > 1 and self._target_language == 'fortran': #Order check if collect_var.order == 'F': check = FunctionCall(numpy_check_flag, [collect_var, numpy_flag_f_contig]) else: check = FunctionCall(numpy_check_flag, [collect_var, numpy_flag_c_contig]) error = PyErr_SetString( 'PyExc_NotImplementedError', '"Argument does not have the expected ordering ({})"'. format(collect_var.order)) body += [(PyccelNot(check), [error, Return([Nil()])])] body += [(LiteralTrue(), [ Assign(VariableAddress(variable), self.get_collect_function_call(variable, collect_var)) ])] body = [If(*body)] return body
def __new__(cls, func, import_lambda): # ... m_results = func.m_results name = 'interface_{}'.format(func.name) args = [i for i in func.arguments if not i in m_results] s_results = func.results results = list(s_results) + list(m_results) # ... # ... imports = [import_lambda] stmts = [] # ... # ... out argument if len(results) == 1: outs = [Symbol('out')] else: outs = [Symbol('out_{}'.format(i)) for i in range(0, len(results))] # ... # ... generators = func.generators d_shapes = {} for i in m_results: d_shapes[i] = compute_shape(i, generators) # ... # ... TODO build statements if_cond = Is(Symbol('out'), Nil()) if_body = [] # TODO add imports from numpy if_body += [Import('zeros', 'numpy')] if_body += [Import('float64', 'numpy')] for i, var in enumerate(results): if var in m_results: shaping = d_shapes[var] if_body += shaping.stmts if_body += [Assign(outs[i], Zeros(shaping.var, var.dtype))] # update statements stmts = [If((if_cond, if_body))] # ... # ... add call to the python or pyccelized function stmts += [FunctionCall(func, args + outs)] # ... # ... add return out if len(outs) == 1: stmts += [Return(outs[0])] else: stmts += [Return(outs)] # ... # ... body = imports + stmts # ... # update arguments with optional args += [Assign(Symbol('out'), Nil())] return FunctionDef(name, args, results, body)
def _visit_FunctionDef(self, stmt): # TODO check all inputs and which ones should be treated in stage 1 or 2 name = self._visit(stmt.name) name = name.replace("'", '') arguments = self._visit(stmt.args) local_vars = [] global_vars = [] headers = [] templates = {} is_pure = False is_elemental = False is_private = False imports = [] doc_string = None def fill_types(ls): container = [] for arg in ls: if isinstance(arg, Symbol): arg = arg.name container.append(arg) elif isinstance(arg, LiteralString): arg = str(arg) arg = arg.strip("'").strip('"') container.append(arg) else: msg = 'Invalid argument of type {} passed to types decorator'.format( type(arg)) errors.report(msg, bounding_box=(stmt.lineno, stmt.col_offset), severity='error') return container decorators = {} # add the decorator @types if the arguments are annotated annotated_args = [] for a in arguments: if isinstance(a, Argument): annotated_args.append(a.annotation) elif isinstance(a, ValuedArgument): annotated_args.append(a.argument.annotation) if all(not isinstance(a, Nil) for a in annotated_args): if stmt.returns: returns = ValuedArgument(Symbol('results'), self._visit(stmt.returns)) annotated_args.append(returns) decorators['types'] = [FunctionCall('types', annotated_args)] for d in self._visit(stmt.decorator_list): tmp_var = str(d) if isinstance(d, Symbol) else str(d.funcdef) if tmp_var in decorators: decorators[tmp_var] += [d] else: decorators[tmp_var] = [d] if 'bypass' in decorators: return EmptyNode() if 'stack_array' in decorators: decorators['stack_array'] = tuple( str(b) for a in decorators['stack_array'] for b in a.args) if 'allow_negative_index' in decorators: decorators['allow_negative_index'] = tuple( str(b) for a in decorators['allow_negative_index'] for b in a.args) # extract the templates if 'template' in decorators: for comb_types in decorators['template']: cache.clear_cache() types = [] if len(comb_types.args) != 2: msg = 'Number of Arguments provided to the template decorator is not valid' errors.report(msg, symbol=comb_types, bounding_box=(stmt.lineno, stmt.col_offset), severity='error') for i in comb_types.args: if isinstance(i, ValuedArgument) and not i.name in ('name', 'types'): msg = 'Argument provided to the template decorator is not valid' errors.report(msg, symbol=comb_types, bounding_box=(stmt.lineno, stmt.col_offset), severity='error') if all(isinstance(i, ValuedArgument) for i in comb_types.args): tp_name, ls = (comb_types.args[0].value, comb_types.args[1].value) if\ comb_types.args[0].name == 'name' else\ (comb_types.args[1].value, comb_types.args[0].value) else: tp_name = comb_types.args[0] ls = comb_types.args[1] ls = ls.value if isinstance(ls, ValuedArgument) else ls try: tp_name = str(tp_name) ls = ls if isinstance(ls, PythonTuple) else list(ls) except TypeError: msg = 'Argument provided to the template decorator is not valid' errors.report(msg, symbol=comb_types, bounding_box=(stmt.lineno, stmt.col_offset), severity='fatal') types = fill_types(ls) txt = '#$ header template ' + str(tp_name) txt += '(' + '|'.join(types) + ')' if tp_name in templates: msg = 'The template "{}" is duplicated'.format(tp_name) errors.report(msg, bounding_box=(stmt.lineno, stmt.col_offset), severity='warning') templates[tp_name] = hdr_parse(stmts=txt) # extract the types to construct a header if 'types' in decorators: for comb_types in decorators['types']: cache.clear_cache() results = [] ls = comb_types.args if len(ls) > 0 and isinstance(ls[-1], ValuedArgument): arg_name = ls[-1].name if not arg_name == 'results': msg = 'Argument "{}" provided to the types decorator is not valid'.format( arg_name) errors.report(msg, symbol=comb_types, bounding_box=(stmt.lineno, stmt.col_offset), severity='error') else: container = ls[-1].value container = container if isinstance( container, PythonTuple) else [container] results = fill_types(container) types = fill_types(ls[:-1]) else: types = fill_types(ls) txt = '#$ header ' + name txt += '(' + ','.join(types) + ')' if results: txt += ' results(' + ','.join(results) + ')' header = hdr_parse(stmts=txt) if name in self.namespace.static_functions: header = header.to_static() headers += [header] body = stmt.body if 'sympy' in decorators.keys(): # TODO maybe we should run pylint here stmt.decorators.pop() func = SympyFunction(name, arguments, [], [stmt.__str__()]) func.set_fst(stmt) self.insert_function(func) return EmptyNode() elif 'python' in decorators.keys(): # TODO maybe we should run pylint here stmt.decorators.pop() func = PythonFunction(name, arguments, [], [stmt.__str__()]) func.set_fst(stmt) self.insert_function(func) return EmptyNode() else: body = self._visit(body) if len(body) > 0 and isinstance(body[0], CommentBlock): doc_string = body[0] doc_string.header = '' body = body[1:] if 'pure' in decorators.keys(): is_pure = True if 'elemental' in decorators.keys(): is_elemental = True if len(arguments) > 1: errors.report(FORTRAN_ELEMENTAL_SINGLE_ARGUMENT, symbol=decorators['elemental'], bounding_box=(stmt.lineno, stmt.col_offset), severity='error') if 'private' in decorators.keys(): is_private = True returns = [i.expr for i in _atomic(body, cls=Return)] assert all(len(i) == len(returns[0]) for i in returns) results = [] result_counter = 1 for i in zip(*returns): if not all(i[0] == j for j in i) or not isinstance(i[0], Symbol): result_name, result_counter = create_variable( self._used_names, prefix='Out', counter=result_counter) results.append(result_name) elif isinstance(i[0], Symbol) and any(i[0].name == x.name for x in arguments): result_name, result_counter = create_variable( self._used_names, prefix='Out', counter=result_counter) results.append(result_name) else: results.append(i[0]) func = FunctionDef(name, arguments, results, body, local_vars=local_vars, global_vars=global_vars, is_pure=is_pure, is_elemental=is_elemental, is_private=is_private, imports=imports, decorators=decorators, headers=headers, templates=templates, doc_string=doc_string) func.set_fst(stmt) return func