def visit_Name(self, node): if node.id in self.local_names: return cxxid(node.id) elif node.id in self.global_declarations: return "{0}()".format(cxxid(node.id)) else: return cxxid(node.id)
def visit_FunctionDef(self, node): self.fname = cxxid(node.name) tmp = self.prepare_functiondef_context(node) operator_body, formal_types, formal_args = tmp tmp = self.prepare_types(node) dflt_argv, dflt_argt, result_type, callable_type, pure_type = tmp # a function has a call operator to be called # and a default constructor to create instances fscope = "type{0}::".format( "<{0}>".format(", ".join(formal_types)) if formal_types else "") ffscope = "{0}::{1}".format(self.fname, fscope) operator_declaration = [ templatize( make_const_function_declaration( self, node, "typename {0}result_type".format(fscope), "operator()", formal_types, formal_args, dflt_argv), formal_types, dflt_argt), EmptyStatement() ] operator_signature = make_const_function_declaration( self, node, "typename {0}result_type".format(ffscope), "{0}::operator()".format(self.fname), formal_types, formal_args) ctx = CachedTypeVisitor(self.lctx) operator_local_declarations = ([ Statement("{0} {1}".format( self.types[self.local_names[k]].generate(ctx), cxxid(k))) for k in self.ldecls ]) dependent_typedefs = ctx.typedefs() operator_definition = FunctionBody( templatize(operator_signature, formal_types), Block(dependent_typedefs + operator_local_declarations + operator_body)) ctx = CachedTypeVisitor() extra_typedefs = ([ Typedef(Value(t.generate(ctx), t.name)) for t in self.types[node][1] ] + [Typedef(Value(result_type.generate(ctx), "result_type"))]) extra_typedefs = ctx.typedefs() + extra_typedefs return_declaration = [ templatize(Struct("type", extra_typedefs), formal_types, dflt_argt) ] topstruct = Struct(self.fname, [callable_type, pure_type] + return_declaration + operator_declaration) return [topstruct], [operator_definition]
def visit_Module(self, node): """ Build a compilation unit. """ # build all types header_deps = sorted(self.dependencies) headers = [Include('/'.join(["pythonic", "include"] + [cxxid(x) for x in t]) + ".hpp") for t in header_deps] headers += [Include('/'.join(["pythonic"] + [cxxid(x) for x in t]) + ".hpp") for t in header_deps] decls_n_defns = list(filter(None, (self.visit(stmt) for stmt in node.body))) decls, defns = zip(*decls_n_defns) if decls_n_defns else ([], []) nsbody = [s for ls in decls + defns for s in ls] ns = Namespace(pythran_ward + self.passmanager.module_name, nsbody) self.result = CompilationUnit(headers + [ns])
def prepare_functiondef_context(self, node): # prepare context and visit function body fargs = node.args.args formal_args = [cxxid(arg.id) for arg in fargs] formal_types = ["argument_type" + str(i) for i in range(len(fargs))] local_decls = set(self.gather(LocalNodeDeclarations, node)) self.local_names = {sym.id: sym for sym in local_decls} self.local_names.update({arg.id: arg for arg in fargs}) self.lctx = CachedTypeVisitor() self.ldecls = {n.id for n in local_decls} body = [self.visit(stmt) for stmt in node.body] return body, formal_types, formal_args
def has_argument(module, fname): '''Checks if a given function has arguments''' for n in module.body: if isinstance(n, ast.FunctionDef) and n.name == fname: return [cxxid(arg.id) for arg in n.args.args] return []
def generate_cxx(module_name, code, specs=None, optimizations=None, module_dir=None, report_times=False): '''python + pythran spec -> c++ code returns a PythonModule object and an error checker the error checker can be used to print more detailed info on the origin of a compile error (e.g. due to bad typing) ''' if specs: entry_points = set(specs.keys()) else: entry_points = None pm, ir, docstrings = front_middle_end(module_name, code, optimizations, module_dir, report_times=report_times, entry_points=entry_points) # back-end content = pm.dump(Cxx, ir) # instantiate the meta program if specs is None: class Generable(object): def __init__(self, content): self.content = content def __str__(self): return str(self.content) generate = __str__ mod = Generable(content) def error_checker(): tog.typecheck(ir) else: # uniform typing if isinstance(specs, dict): specs = Spec(specs, {}) def error_checker(): types = tog.typecheck(ir) check_specs(specs, types) specs.to_docstrings(docstrings) check_exports(pm, ir, specs) if isinstance(code, bytes): code_bytes = code else: code_bytes = code.encode('ascii', 'ignore') metainfo = { 'hash': hashlib.sha256(code_bytes).hexdigest(), 'version': __version__, 'date': datetime.now() } mod = PythonModule(module_name, docstrings, metainfo) mod.add_to_includes( Include("pythonic/core.hpp"), Include("pythonic/python/core.hpp"), # FIXME: only include these when needed Include("pythonic/types/bool.hpp"), Include("pythonic/types/int.hpp"), Line("#ifdef _OPENMP\n#include <omp.h>\n#endif")) mod.add_to_includes( *[Include(inc) for inc in _extract_specs_dependencies(specs)]) mod.add_to_includes(*content.body) mod.add_to_includes(Include("pythonic/python/exception_handler.hpp"), ) def warded(module_name, internal_name): return pythran_ward + '{0}::{1}'.format(module_name, internal_name) for function_name, signatures in specs.functions.items(): internal_func_name = cxxid(function_name) # global variables are functions with no signatures :-) if not signatures: mod.add_global_var( function_name, "{}()()".format(warded(module_name, internal_func_name))) for sigid, signature in enumerate(signatures): numbered_function_name = "{0}{1}".format( internal_func_name, sigid) arguments_types = [pytype_to_ctype(t) for t in signature] arguments_names = has_argument(ir, function_name) arguments = [ n for n, _ in zip(arguments_names, arguments_types) ] name_fmt = pythran_ward + "{0}::{1}::type{2}" args_list = ", ".join(arguments_types) specialized_fname = name_fmt.format( module_name, internal_func_name, "<{0}>".format(args_list) if arguments_names else "") result_type = "typename %s::result_type" % specialized_fname mod.add_pyfunction( FunctionBody( FunctionDeclaration( Value(result_type, numbered_function_name), [ Value(t + '&&', a) for t, a in zip(arguments_types, arguments) ]), Block([ Statement(""" PyThreadState *_save = PyEval_SaveThread(); try {{ auto res = {0}()({1}); PyEval_RestoreThread(_save); return res; }} catch(...) {{ PyEval_RestoreThread(_save); throw; }} """.format(warded(module_name, internal_func_name), ', '.join(arguments))) ])), function_name, arguments_types, signature) for function_name, signature in specs.capsules.items(): internal_func_name = cxxid(function_name) arguments_types = [pytype_to_ctype(t) for t in signature] arguments_names = has_argument(ir, function_name) arguments = [n for n, _ in zip(arguments_names, arguments_types)] name_fmt = pythran_ward + "{0}::{1}::type{2}" args_list = ", ".join(arguments_types) specialized_fname = name_fmt.format( module_name, internal_func_name, "<{0}>".format(args_list) if arguments_names else "") result_type = "typename %s::result_type" % specialized_fname docstring = spec_to_string(function_name, signature) mod.add_capsule( FunctionBody( FunctionDeclaration(Value(result_type, function_name), [ Value(t, a) for t, a in zip(arguments_types, arguments) ]), Block([ ReturnStatement("{0}()({1})".format( warded(module_name, internal_func_name), ', '.join(arguments))) ])), function_name, docstring) return mod, error_checker
def visit_FunctionDef(self, node): tmp = self.prepare_functiondef_context(node) operator_body, formal_types, formal_args = tmp tmp = self.prepare_types(node) dflt_argv, dflt_argt, result_type, callable_type, pure_type = tmp # a generator has a call operator that returns the iterator next_name = "__generator__{0}".format(cxxid(node.name)) instanciated_next_name = "{0}{1}".format( next_name, "<{0}>".format(", ".join(formal_types)) if formal_types else "") operator_body.append( Statement("{}: return result_type()".format( CxxGenerator.FinalStatement))) next_declaration = [ FunctionDeclaration(Value("result_type", "next"), []), EmptyStatement() ] # empty statement to force a comma ... # the constructors next_constructors = [ FunctionBody(FunctionDeclaration(Value("", next_name), []), Line(': pythonic::yielder() {}')) ] if formal_types: # If all parameters have a default value, we don't need default # constructor if dflt_argv and all(dflt_argv): next_constructors = list() next_constructors.append( FunctionBody( make_function_declaration(self, node, "", next_name, formal_types, formal_args, dflt_argv), Line(": {0} {{ }}".format(", ".join( ["pythonic::yielder()"] + ["{0}({0})".format(arg) for arg in formal_args]))))) next_iterator = [ FunctionBody(FunctionDeclaration(Value("void", "operator++"), []), Block([Statement("next()")])), FunctionBody( FunctionDeclaration( Value( "typename {0}::result_type".format( instanciated_next_name), "operator*"), [], "const"), Block([ReturnStatement(CxxGenerator.StateValue)])), FunctionBody( FunctionDeclaration( Value( "pythonic::types::generator_iterator<{0}>".format( next_name), "begin"), []), Block([ Statement("next()"), ReturnStatement("pythonic::types::generator_iterator<{0}>" "(*this)".format(next_name)) ])), FunctionBody( FunctionDeclaration( Value( "pythonic::types::generator_iterator<{0}>".format( next_name), "end"), []), Block([ ReturnStatement( "pythonic::types::generator_iterator<{0}>()".format( next_name)) ])) ] next_signature = templatize( FunctionDeclaration( Value( "typename {0}::result_type".format(instanciated_next_name), "{0}::next".format(instanciated_next_name)), []), formal_types) next_body = operator_body # the dispatch table at the entry point next_body.insert( 0, Statement("switch({0}) {{ {1} }}".format( CxxGenerator.StateHolder, " ".join("case {0}: goto {1};".format(num, where) for (num, where) in sorted(self.yields.values(), key=lambda x: x[0]))))) ctx = CachedTypeVisitor(self.lctx) next_members = ([ Statement("{0} {1}".format(ft, fa)) for (ft, fa) in zip(formal_types, formal_args) ] + [ Statement("{0} {1}".format( self.types[self.local_names[k]].generate(ctx), k)) for k in self.ldecls ] + [ Statement("{0} {1}".format(v, k)) for k, v in self.extra_declarations ] + [ Statement("typename {0}::result_type {1}".format( instanciated_next_name, CxxGenerator.StateValue)) ]) extern_typedefs = [ Typedef(Value(t.generate(ctx), t.name)) for t in self.types[node][1] ] iterator_typedef = [ Typedef( Value( "pythonic::types::generator_iterator<{0}>".format( "{0}<{1}>".format(next_name, ", ".join(formal_types) ) if formal_types else next_name), "iterator")), Typedef(Value(result_type.generate(ctx), "value_type")) ] result_typedef = [ Typedef(Value(result_type.generate(ctx), "result_type")) ] extra_typedefs = (ctx.typedefs() + extern_typedefs + iterator_typedef + result_typedef) next_struct = templatize( Struct( next_name, extra_typedefs + next_members + next_constructors + next_iterator + next_declaration, "pythonic::yielder"), formal_types) next_definition = FunctionBody(next_signature, Block(next_body)) operator_declaration = [ templatize( make_const_function_declaration(self, node, instanciated_next_name, "operator()", formal_types, formal_args, dflt_argv), formal_types, dflt_argt), EmptyStatement() ] operator_signature = make_const_function_declaration( self, node, instanciated_next_name, "{0}::operator()".format(cxxid(node.name)), formal_types, formal_args) operator_definition = FunctionBody( templatize(operator_signature, formal_types), Block([ ReturnStatement("{0}({1})".format(instanciated_next_name, ", ".join(formal_args))) ])) topstruct_type = templatize(Struct("type", extra_typedefs), formal_types) topstruct = Struct(cxxid(node.name), [topstruct_type, callable_type, pure_type] + operator_declaration) return [next_struct, topstruct], [next_definition, operator_definition]
def visit_Module(self, node): for n in node.body: if isinstance(n, ast.FunctionDef) and n.name == self.fname: return [cxxid(arg.id) for arg in n.args.args] return []