%PDF- %PDF-
Direktori : /lib/python3/dist-packages/pythran/ |
Current File : //lib/python3/dist-packages/pythran/cxxgen.py |
""" Generator for C/C++. """ # Serge Guelton: The licensing terms are not set in the source package, but # pypi[1] says the software is under the MIT license, so I reproduce it here # [1] http://pypi.python.org/pypi/cgen # # Copyright (C) 2008 Andreas Kloeckner # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. # from textwrap import dedent from pythran.tables import pythran_ward from pythran.spec import signatures_to_string __copyright__ = "Copyright (C) 2008 Andreas Kloeckner" class Nop(object): def generate(self, with_semicolon=True): yield '' class Declarator(object): def generate(self, with_semicolon=True): tp_lines, tp_decl = self.get_decl_pair() tp_lines = list(tp_lines) for line in tp_lines[:-1]: yield line sc = ";" if with_semicolon else "" if tp_decl is None: yield "%s%s" % (tp_lines[-1], sc) else: yield "%s %s%s" % (tp_lines[-1], tp_decl, sc) def get_decl_pair(self): """Return a tuple ``(type_lines, rhs)``. *type_lines* is a non-empty list of lines (most often just a single one) describing the type of this declarator. *rhs* is the right- hand side that actually contains the function/array/constness notation making up the bulk of the declarator syntax. """ def inline(self): """Return the declarator as a single line.""" tp_lines, tp_decl = self.get_decl_pair() tp_lines = " ".join(tp_lines) if tp_decl is None: return tp_lines else: return "%s %s" % (tp_lines, tp_decl) class Value(Declarator): """A simple declarator: *typename* and *name* are given as strings.""" def __init__(self, typename, name): self.typename = typename self.name = name def get_decl_pair(self): return [self.typename], self.name class NestedDeclarator(Declarator): def __init__(self, subdecl): self.subdecl = subdecl @property def name(self): return self.subdecl.name def get_decl_pair(self): return self.subdecl.get_decl_pair() class DeclSpecifier(NestedDeclarator): def __init__(self, subdecl, spec, sep=' '): NestedDeclarator.__init__(self, subdecl) self.spec = spec self.sep = sep def get_decl_pair(self): def add_spec(sub_it): it = iter(sub_it) try: yield "%s%s%s" % (self.spec, self.sep, next(it)) except StopIteration: pass for line in it: yield line sub_tp, sub_decl = self.subdecl.get_decl_pair() return add_spec(sub_tp), sub_decl class Typedef(DeclSpecifier): def __init__(self, subdecl): DeclSpecifier.__init__(self, subdecl, "typedef") class FunctionDeclaration(NestedDeclarator): def __init__(self, subdecl, arg_decls, *attributes): NestedDeclarator.__init__(self, subdecl) self.inline = True self.arg_decls = arg_decls self.attributes = attributes def get_decl_pair(self): sub_tp, sub_decl = self.subdecl.get_decl_pair() if self.inline: sub_tp = ['inline'] + sub_tp return sub_tp, ("%s(%s) %s" % ( sub_decl, ", ".join(ad.inline() for ad in self.arg_decls), " ".join(self.attributes))) class Struct(Declarator): """ A structure declarator. Attributes ---------- tpname : str Name of the structure. (None for unnamed struct) fields : [Declarator] Content of the structure. inherit : str Parent class of current structure. """ def __init__(self, tpname, fields, inherit=None): """Initialize the structure declarator. """ self.tpname = tpname self.fields = fields self.inherit = inherit def get_decl_pair(self): """ See Declarator.get_decl_pair.""" def get_tp(): """ Iterator generating lines for struct definition. """ decl = "struct " if self.tpname is not None: decl += self.tpname if self.inherit is not None: decl += " : " + self.inherit yield decl yield "{" for f in self.fields: for f_line in f.generate(): yield " " + f_line yield "} " return get_tp(), "" # template -------------------------------------------------------------------- class Template(NestedDeclarator): def __init__(self, template_spec, subdecl): super(Template, self).__init__(subdecl) self.template_spec = template_spec def generate(self, with_semicolon=False): yield "template <%s>" % ", ".join(self.template_spec) for i in self.subdecl.generate(with_semicolon): yield i if not isinstance(self.subdecl, (Template, FunctionDeclaration)): yield ";" # control flow/statement stuff ------------------------------------------------ class ExceptHandler(object): def __init__(self, name, body, alias=None): self.name = name self.body = body self.alias = alias def generate(self): if self.name is None: yield "catch(...)" else: yield "catch (pythonic::types::%s const& %s)" % (self.name, self.alias or '') for line in self.body.generate(): yield line class TryExcept(object): def __init__(self, try_, except_): self.try_ = try_ self.except_ = except_ def generate(self): yield "try" for line in self.try_.generate(): yield line for exception in self.except_: for line in exception.generate(): yield " " + line class If(object): def __init__(self, condition, then_, else_=None): self.condition = condition self.then_ = then_ self.else_ = else_ def generate(self): yield "if (%s)" % self.condition for line in self.then_.generate(): yield line if self.else_ is not None: yield "else" for line in self.else_.generate(): yield line class Loop(object): def __init__(self, body): self.body = body def generate(self): yield self.intro_line() for line in self.body.generate(): yield line class While(Loop): def __init__(self, condition, body): super(While, self).__init__(body) self.condition = condition def intro_line(self): return "while (%s)" % self.condition class For(Loop): def __init__(self, start, condition, update, body): super(For, self).__init__(body) self.start = start self.condition = condition self.update = update def intro_line(self): return "for (%s; %s; %s)" % (self.start, self.condition, self.update) class AutoFor(Loop): def __init__(self, target, iter_, body): super(AutoFor, self).__init__(body) self.target = target self.iter = iter_ def intro_line(self): if self.target == '_': return "for (PYTHRAN_UNUSED auto&& {0}: {1})".format(self.target, self.iter) else: return "for (auto&& {0}: {1})".format(self.target, self.iter) # simple statements ----------------------------------------------------------- class Define(object): def __init__(self, symbol, value): self.symbol = symbol self.value = value def generate(self): yield "#define %s %s" % (self.symbol, self.value) class Include(object): def __init__(self, filename, system=True): self.filename = filename self.system = system def generate(self): if self.system: yield "#include <%s>" % self.filename else: yield "#include \"%s\"" % self.filename class Label(object): def __init__(self, label): self.label = label def generate(self): yield self.label + ':;' class Statement(object): def __init__(self, text): self.text = text def generate(self): yield self.text + ";" class AnnotatedStatement(object): def __init__(self, stmt, annotations): self.stmt = stmt self.annotations = annotations def generate(self): for directive in self.annotations: pragma = "#pragma " + directive.s yield pragma.format(*directive.deps) for s in self.stmt.generate(): yield s class ReturnStatement(Statement): def generate(self): yield "return " + self.text + ";" class EmptyStatement(Statement): def __init__(self): Statement.__init__(self, "") class Assign(object): def __init__(self, lvalue, rvalue): self.lvalue = lvalue self.rvalue = rvalue def generate(self): yield "%s = %s;" % (self.lvalue, self.rvalue) class Line(object): def __init__(self, text=""): self.text = text def generate(self): yield self.text # initializers ---------------------------------------------------------------- class FunctionBody(object): def __init__(self, fdecl, body): """Initialize a function definition. *fdecl* is expected to be a :class:`FunctionDeclaration` instance, while *body* is a :class:`Block`. """ self.fdecl = fdecl self.body = body def generate(self): for f_line in self.fdecl.generate(with_semicolon=False): yield f_line for b_line in self.body.generate(): yield b_line # block ----------------------------------------------------------------------- class Block(object): def __init__(self, contents=None): if contents is None: contents = [] self.contents = contents def generate(self): yield "{" for item in self.contents: for item_line in item.generate(): yield " " + item_line yield "}" class Module(Block): def generate(self): for c in self.contents: for line in c.generate(): yield line class Namespace(Block): def __init__(self, name, contents=None): Block.__init__(self, contents) self.name = name def generate(self): yield "namespace " + self.name yield "{" for item in self.contents: for item_line in item.generate(): yield " " + item_line yield "}" # copy-pasted from codepy.bpl, which is a real mess... # the original code was under MIT License # cf. http://pypi.python.org/pypi/codepy # so I reproduce it here # # Copyright (C) 2008 Andreas Kloeckner # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. # class PythonModule(object): ''' Wraps the creation of a Pythran module wrapped a Python native Module ''' def __init__(self, name, docstrings, metadata): ''' Builds an empty PythonModule ''' self.name = name self.preamble = [] self.includes = [] self.functions = {} self.global_vars = [] self.implems = [] self.capsules = [] self.python_implems = [] self.wrappers = [] self.docstrings = docstrings self.metadata = metadata moduledoc = self.docstring(self.docstrings.get(None, "")) self.metadata['moduledoc'] = moduledoc def docstring(self, doc): return self.splitstring(dedent(doc).replace('"', '\\"') .replace('\n', '\\n') .replace('\r', '\\r')) def splitstring(self, doc): return '"{}"'.format('\\n""'.join(doc.split('\\n'))) def add_to_preamble(self, *pa): self.preamble.extend(pa) def add_to_includes(self, *incl): self.includes.extend(incl) def add_pyfunction(self, func, name, types, signature): self.add_function_to(self.python_implems, func, name, types, signature) def add_capsule(self, func, ptrname, sig): self.capsules.append((ptrname, sig)) self.implems.append(func) def add_function(self, func, name, types, signature): self.add_function_to(self.implems, func, name, types, signature) def add_function_to(self, to, func, name, ctypes, signature): """ Add a function to be exposed. *func* is expected to be a :class:`cgen.FunctionBody`. Because a function can have several signatures exported, this method actually creates a wrapper for each specialization and a global wrapper that checks the argument types and runs the correct candidate, if any """ to.append(func) args_unboxing = [] # turns PyObject to c++ object args_checks = [] # check if the above conversion is valid wrapper_name = pythran_ward + 'wrap_' + func.fdecl.name for i, t in enumerate(ctypes): args_unboxing.append('from_python<{}>(args_obj[{}])'.format(t, i)) args_checks.append('is_convertible<{}>(args_obj[{}])'.format(t, i)) arg_decls = func.fdecl.arg_decls[:len(ctypes)] keywords = "".join('"{}", '.format(arg.name) for arg in arg_decls) wrapper = dedent(''' static PyObject * {wname}(PyObject *self, PyObject *args, PyObject *kw) {{ PyObject* args_obj[{size}+1]; {silent_warning} char const* keywords[] = {{{keywords} nullptr}}; if(! PyArg_ParseTupleAndKeywords(args, kw, "{fmt}", (char**)keywords {objs})) return nullptr; if({checks}) return to_python({name}({args})); else {{ return nullptr; }} }}''') self.wrappers.append( wrapper.format(name=func.fdecl.name, silent_warning= '' if ctypes else '(void)args_obj;', size=len(ctypes), fmt="O" * len(ctypes), objs=''.join(', &args_obj[%d]' % i for i in range(len(ctypes))), args=', '.join(args_unboxing), checks=' && '.join(args_checks) or '1', wname=wrapper_name, keywords=keywords, ) ) func_descriptor = wrapper_name, ctypes, signature self.functions.setdefault(name, []).append(func_descriptor) def add_global_var(self, name, init): self.global_vars.append(name) self.python_implems.append(Assign('static PyObject* ' + name, 'to_python({})'.format(init))) def __str__(self): """Generate (i.e. yield) the source code of the module line-by-line. """ themethods = [] theextraobjects = [] theoverloads = [] for vname in self.global_vars: theextraobjects.append( 'PyModule_AddObject(theModule, "{0}", {0});'.format(vname)) for fname, overloads in self.functions.items(): tryall = [] signatures = [] for overload, ctypes, signature in overloads: try_ = dedent(""" if(PyObject* obj = {name}(self, args, kw)) return obj; PyErr_Clear(); """.format(name=overload)) tryall.append(try_) signatures.append(signature) candidates = signatures_to_string(fname, signatures) wrapper_name = pythran_ward + 'wrapall_' + fname candidate = dedent(''' static PyObject * {wname}(PyObject *self, PyObject *args, PyObject *kw) {{ return pythonic::handle_python_exception([self, args, kw]() -> PyObject* {{ {tryall} return pythonic::python::raise_invalid_argument( "{name}", {candidates}, args, kw); }}); }} '''.format(name=fname, tryall="\n".join(tryall), candidates=self.splitstring( candidates.replace('\n', '\\n') ), wname=wrapper_name)) fdoc = self.docstring(self.docstrings.get(fname, '')) themethod = dedent('''{{ "{name}", (PyCFunction){wname}, METH_VARARGS | METH_KEYWORDS, {doc}}}'''.format(name=fname, wname=wrapper_name, doc=fdoc)) themethods.append(themethod) theoverloads.append(candidate) for ptrname, sig in self.capsules: capsule = ''' PyModule_AddObject(theModule, "{ptrname}", PyCapsule_New((void*)&{ptrname}, "{sig}", NULL) );'''.format(ptrname=ptrname, sig=sig) theextraobjects.append(capsule) methods = dedent(''' static PyMethodDef Methods[] = {{ {methods} {{NULL, NULL, 0, NULL}} }}; '''.format(methods="".join(m + "," for m in themethods))) module = dedent(''' #if PY_MAJOR_VERSION >= 3 static struct PyModuleDef moduledef = {{ PyModuleDef_HEAD_INIT, "{name}", /* m_name */ {moduledoc}, /* m_doc */ -1, /* m_size */ Methods, /* m_methods */ NULL, /* m_reload */ NULL, /* m_traverse */ NULL, /* m_clear */ NULL, /* m_free */ }}; #define PYTHRAN_RETURN return theModule #define PYTHRAN_MODULE_INIT(s) PyInit_##s #else #define PYTHRAN_RETURN return #define PYTHRAN_MODULE_INIT(s) init##s #endif PyMODINIT_FUNC PYTHRAN_MODULE_INIT({name})(void) #ifndef _WIN32 __attribute__ ((visibility("default"))) #if defined(GNUC) && !defined(__clang__) __attribute__ ((externally_visible)) #endif #endif ; PyMODINIT_FUNC PYTHRAN_MODULE_INIT({name})(void) {{ import_array() #if PY_MAJOR_VERSION >= 3 PyObject* theModule = PyModule_Create(&moduledef); #else PyObject* theModule = Py_InitModule3("{name}", Methods, {moduledoc} ); #endif if(! theModule) PYTHRAN_RETURN; PyObject * theDoc = Py_BuildValue("(sss)", "{version}", "{date}", "{hash}"); if(! theDoc) PYTHRAN_RETURN; PyModule_AddObject(theModule, "__pythran__", theDoc); {extraobjects} PYTHRAN_RETURN; }} '''.format(name=self.name, extraobjects='\n'.join(theextraobjects), **self.metadata)) body = (self.preamble + self.includes + self.implems + [Line('#ifdef ENABLE_PYTHON_MODULE')] + self.python_implems + [Line(code) for code in self.wrappers + theoverloads] + [Line(methods), Line(module), Line('#endif')]) return "\n".join(Module(body).generate()) class CompilationUnit(object): def __init__(self, body): self.body = body def __str__(self): return '\n'.join('\n'.join(s.generate()) for s in self.body)