From 4de40c17c77c98294b65daac68a65dd335e06745 Mon Sep 17 00:00:00 2001 From: Erwin de Haan Date: Thu, 6 Dec 2018 16:12:53 +0100 Subject: [PATCH 1/8] Added some hacky code to CppUnparser to make more unparing possible. --- transpyle/cpp/__init__.py | 3 +++ transpyle/cpp/unparser.py | 44 +++++++++++++++++++++++++++++++-------- 2 files changed, 38 insertions(+), 9 deletions(-) diff --git a/transpyle/cpp/__init__.py b/transpyle/cpp/__init__.py index d15eb09..0784089 100644 --- a/transpyle/cpp/__init__.py +++ b/transpyle/cpp/__init__.py @@ -8,6 +8,9 @@ __all__ = ['CppParser', 'CppAstGeneralizer', 'Cpp14Unparser', 'CppSwigCompiler'] +# Annotation to mark a class as a struct. (Python -> Cpp) +def struct(f): + return f class CppBinder(Binder): diff --git a/transpyle/cpp/unparser.py b/transpyle/cpp/unparser.py index 656804c..40c2170 100644 --- a/transpyle/cpp/unparser.py +++ b/transpyle/cpp/unparser.py @@ -133,6 +133,7 @@ def _unsupported_syntax(self, syntax, comment: str = None): self.fill('! TODO: unsupported syntax') unparsing_unsupported('C++', syntax, comment) + # Misusing ast.Index to signify reference types. Should probably fork typed-ast or something. The ast generalizer does something string based. def dispatch_type(self, type_hint): _LOG.debug('dispatching type hint %s', type_hint) if is_generic_type(type_hint): @@ -154,8 +155,24 @@ def dispatch_type(self, type_hint): self.write('*') return if isinstance(type_hint, typed_ast3.Subscript): - _LOG.error('encountered unsupported subscript form: %s', - horast.unparse(type_hint).strip()) + if isinstance(type_hint.value, typed_ast3.Attribute) \ + and isinstance(type_hint.value.value, typed_ast3.Name): + unparsed = horast.unparse(type_hint.value).strip() + self.write(PY_TO_CPP_TYPES[unparsed]) + if unparsed == 'st.ndarray': + self.write('<') + sli = type_hint.slice + self.write('>') + return + elif isinstance(type_hint.value, typed_ast3.Name): + unparsed = horast.unparse(type_hint.value).strip() + self.write(unparsed) + self.write('<') + self.write(horast.unparse(type_hint.slice).strip()) + self.write('>') + if isinstance(type_hint.slice, typed_ast3.Index): + self.write('&') + return self._unsupported_syntax(type_hint) if isinstance(type_hint, typed_ast3.Attribute): if isinstance(type_hint.value, typed_ast3.Name): @@ -169,12 +186,19 @@ def dispatch_type(self, type_hint): assert type_hint.value is None self.write('void') return + self.dispatch(type_hint) + if isinstance(type_hint, typed_ast3.Index): + if isinstance(type_hint.value, typed_ast3.Name): + self.write('&') def _Expr(self, tree): super()._Expr(tree) self.write(';') + def _Pass(self, tree): + self.fill('/* pass */') + def _Import(self, t): self.fill('/* Python import') # raise NotImplementedError('not supported yet') @@ -183,7 +207,11 @@ def _Import(self, t): # #include "boost/multi_array.hpp" def _ImportFrom(self, t): - raise NotImplementedError('not supported yet') + self.fill('/* Python import') + # raise NotImplementedError('not supported yet') + super()._ImportFrom(t) + self.fill('*/') + # #include "boost/multi_array.hpp" def _Assign(self, t): if self._context != 'for header': @@ -229,9 +257,10 @@ def _Pass(self, t): def _ClassDef(self, t): self.write('\n') - if t.decorator_list: - self._unsupported_syntax(t, 'with decorators') - self.fill('class {}'.format(t.name)) + is_struct = False + if len(t.decorator_list) == 1: + is_struct = t.decorator_list[0].id=='struct' + self.fill('{}} {}'.format('struct' if is_struct else 'class', t.name)) if t.bases: _LOG.warning('C++: assuming base classes are inherited as public') self.write(': public ') @@ -258,8 +287,6 @@ def _ClassDef(self, t): self.leave() self.write(';') - # raise NotImplementedError('not supported yet') - constructor_and_destructor_names = {'__init__', '__del__'} supported_special_method_names = set() @@ -513,7 +540,6 @@ def _Comment(self, node): self.fill('//') self.write(node.comment) - class Cpp14HeaderUnparserBackend(Cpp14UnparserBackend): def _FunctionDef(self, t): From 9cbb3a26cf54c1e97300b6874a9be3320c836920 Mon Sep 17 00:00:00 2001 From: Erwin de Haan Date: Sun, 24 Feb 2019 21:32:36 +0100 Subject: [PATCH 2/8] Unparser changes. Added more options to for mode unparsing. Added support for NameConstants. Added support for Byte strings (and also chars as single byte strings). --- transpyle/cpp/unparser.py | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/transpyle/cpp/unparser.py b/transpyle/cpp/unparser.py index 40c2170..2069d9a 100644 --- a/transpyle/cpp/unparser.py +++ b/transpyle/cpp/unparser.py @@ -101,7 +101,7 @@ def for_header_to_tuple(target, target_type, iter_) -> t.Tuple[ init = typed_ast3.Assign(targets=[target], value=begin, type_comment=None) else: init = typed_ast3.AnnAssign(target=target, annotation=target_type, value=begin, simple=True) - condition = typed_ast3.Compare(left=target, ops=[typed_ast3.Lt()], comparators=[end]) + condition = typed_ast3.Expr(typed_ast3.Compare(left=target, ops=[typed_ast3.Lt()], comparators=[end])) increment = typed_ast3.AugAssign(target=target, op=typed_ast3.Add(), value=step) return init, condition, increment @@ -540,6 +540,33 @@ def _Comment(self, node): self.fill('//') self.write(node.comment) + def _NameConstant(self, node): + if node.value == 'False': + self.write("false") + elif node.value == 'True': + self.write("true") + elif node.value == 'None': + self.write("null") + else: + self.write(node.value) + + def _Str(self, node): + self.write('"') + self.write(node.s.replace('"', '\\"').replace("\0", "\\0").replace("\n", "\\n").replace("\t", "\\t").replace("\r", "\\r")) + self.write('"') + + def _Bytes(self, node): + # Char + if len(node.s) == 1: + self.write("'") + self.write(node.s.replace("'", "\\'").replace("\0", "\\0").replace("\n", "\\n").replace("\t", "\\t").replace("\r", "\\r")) + self.write("'") + else: + self._Str(node) + + def _unsupported_syntax(self, tree, comment: str = ''): + raise SyntaxError('unparsing {}{} to C++ is not supported'.format(type(tree), comment)) + class Cpp14HeaderUnparserBackend(Cpp14UnparserBackend): def _FunctionDef(self, t): From 42f37526096f15c371e87292a93b04f0fae5d57a Mon Sep 17 00:00:00 2001 From: Erwin de Haan Date: Tue, 7 May 2019 14:32:29 +0200 Subject: [PATCH 3/8] Added pragma support. --- transpyle/cpp/unparser.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/transpyle/cpp/unparser.py b/transpyle/cpp/unparser.py index 2069d9a..7e627bb 100644 --- a/transpyle/cpp/unparser.py +++ b/transpyle/cpp/unparser.py @@ -534,10 +534,16 @@ def _arg(self, t): self.write(t.arg) def _Comment(self, node): - if node.eol: - self.write(' //') + if node.comment.startswith("pragma"): + if node.eol: + self.write('\n#') + else: + self.fill('#') else: - self.fill('//') + if node.eol: + self.write(' //') + else: + self.fill('//') self.write(node.comment) def _NameConstant(self, node): From 625b485a3ea955e88c5101265fd64b1ef6fcbc5f Mon Sep 17 00:00:00 2001 From: Erwin de Haan Date: Tue, 21 May 2019 15:00:12 +0200 Subject: [PATCH 4/8] Fixed some slided type output support. --- transpyle/cpp/unparser.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/transpyle/cpp/unparser.py b/transpyle/cpp/unparser.py index 7e627bb..7f3eea9 100644 --- a/transpyle/cpp/unparser.py +++ b/transpyle/cpp/unparser.py @@ -168,8 +168,11 @@ def dispatch_type(self, type_hint): unparsed = horast.unparse(type_hint.value).strip() self.write(unparsed) self.write('<') - self.write(horast.unparse(type_hint.slice).strip()) - self.write('>') + if isinstance(type_hint.slice, typed_ast3.Subscript): + self.dispatch_type(type_hint.slice) + else: + self.write(horast.unparse(type_hint.slice).strip()) + self.write(' >') if isinstance(type_hint.slice, typed_ast3.Index): self.write('&') return @@ -241,7 +244,10 @@ def _AnnAssign(self, t): self._unsupported_syntax(t, 'which is not simple') self.dispatch_type(t.annotation) self.write(' ') - self.dispatch(t.target) + try: + self.dispatch(t.target) + except AttributeError as e: + print(e) if t.value: self.write(' = ') self.dispatch(t.value) From cbf42ce5435b82f4372f110b63070c8e8be91572 Mon Sep 17 00:00:00 2001 From: Erwin de Haan Date: Thu, 13 Jun 2019 14:59:11 +0200 Subject: [PATCH 5/8] Oversights and other merge mistakes fixed. --- transpyle/cpp/unparser.py | 40 +++++++++++++++++++++++++++------------ 1 file changed, 28 insertions(+), 12 deletions(-) diff --git a/transpyle/cpp/unparser.py b/transpyle/cpp/unparser.py index 7f3eea9..50763e4 100644 --- a/transpyle/cpp/unparser.py +++ b/transpyle/cpp/unparser.py @@ -101,7 +101,7 @@ def for_header_to_tuple(target, target_type, iter_) -> t.Tuple[ init = typed_ast3.Assign(targets=[target], value=begin, type_comment=None) else: init = typed_ast3.AnnAssign(target=target, annotation=target_type, value=begin, simple=True) - condition = typed_ast3.Expr(typed_ast3.Compare(left=target, ops=[typed_ast3.Lt()], comparators=[end])) + condition = typed_ast3.Compare(left=target, ops=[typed_ast3.Lt()], comparators=[end]) increment = typed_ast3.AugAssign(target=target, op=typed_ast3.Add(), value=step) return init, condition, increment @@ -219,9 +219,13 @@ def _ImportFrom(self, t): def _Assign(self, t): if self._context != 'for header': self.fill() - if t.type_comment: - self.dispatch_type(t.resolved_type_comment) - self.write(' ') + try: # type_comment does not exist on my source AST + if t.type_comment: + self.dispatch_type(t.resolved_type_comment) + self.write(' ') + except AttributeError: + pass + for target in t.targets: self.dispatch(target) self.write(" = ") @@ -263,10 +267,18 @@ def _Pass(self, t): def _ClassDef(self, t): self.write('\n') + if len(t.decorator_list) > 1: + self._unsupported_syntax(t, 'with too many decorators') + is_struct = False if len(t.decorator_list) == 1: - is_struct = t.decorator_list[0].id=='struct' - self.fill('{}} {}'.format('struct' if is_struct else 'class', t.name)) + is_struct = t.decorator_list[0].id == 'struct' + + if is_struct: + self.fill('struct {}'.format(t.name)) + else: + self.fill('class {}'.format(t.name)) + if t.bases: _LOG.warning('C++: assuming base classes are inherited as public') self.write(': public ') @@ -374,7 +386,7 @@ def _AsyncFunctionDef(self, t): def _For(self, t): self.fill('for (') - init, cond, increment = for_header_to_tuple(t.target, t.resolved_type_comment, t.iter) + init, cond, increment = for_header_to_tuple(t.target, t.type_comment, t.iter) # resolved_type_comment does not exsit on my source AST self._context = 'for header' self.dispatch(init) self.write('; ') @@ -484,11 +496,15 @@ def _Attribute(self, t): self.write('->') self.write(t.attr) return - unparsed = PY_TUPLES_TO_CPP[t.value.id, t.attr] - if unparsed == 'sqrt': - self._includes['cmath'] = True - self.write(unparsed) - return + try: + unparsed = PY_TUPLES_TO_CPP[t.value.id, t.attr] + if unparsed == 'sqrt': + self._includes['cmath'] = True + self.write(unparsed) + return + except KeyError: + _LOG.warning('Could not find %s.%s attribute in standard tuples. Assuming normal object.', t.value.id, t.attr) + self.dispatch(t.value) self.write('.') self.write(t.attr) From 24f6d1ea82d0fd003790c3e09f36e3cb27d636a9 Mon Sep 17 00:00:00 2001 From: Erwin de Haan Date: Fri, 14 Jun 2019 11:32:04 +0200 Subject: [PATCH 6/8] Windows compat, at least for unparsing and actually loading the package. --- transpyle/cpp/compiler_interface.py | 60 ++++++++++++++++++++--------- 1 file changed, 42 insertions(+), 18 deletions(-) diff --git a/transpyle/cpp/compiler_interface.py b/transpyle/cpp/compiler_interface.py index 5801704..3757711 100644 --- a/transpyle/cpp/compiler_interface.py +++ b/transpyle/cpp/compiler_interface.py @@ -33,10 +33,19 @@ class GppInterface(CompilerInterface): 'MPI': pathlib.Path('mpic++') } + compile_flags = tuple() + + # Config does not always contain these items (eg. on Windows) + + if 'BASECFLAGS' in PYTHON_CONFIG: + compile_flags = tuple(split_and_strip('{} {}'.format(compile_flags,PYTHON_CONFIG['BASECFLAGS']))) + + if 'BASECPPFLAGS' in PYTHON_CONFIG: + compile_flags = tuple(split_and_strip('{} {}'.format(compile_flags,PYTHON_CONFIG['BASECPPFLAGS']))) + _flags = { '': ('-O3', '-fPIC', '-Wall', '-Wextra', '-Wpedantic', '-fdiagnostics-color=always'), - 'compile': tuple(split_and_strip('{} {}'.format( - PYTHON_CONFIG['BASECFLAGS'], PYTHON_CONFIG['BASECPPFLAGS']))), + 'compile': compile_flags, 'link': (), 'OpenMP': ('-fopenmp',) } @@ -46,18 +55,20 @@ class GppInterface(CompilerInterface): pathlib.Path(PYTHON_CONFIG['INCLUDEPY']), *[pathlib.Path(_, 'core', 'include') for _ in np.__path__]] - library_paths = [ - pathlib.Path(PYTHON_CONFIG['LIBDIR'])] + library_paths = [] + if 'LIBDIR' in PYTHON_CONFIG: + library_paths.append(pathlib.Path(PYTHON_CONFIG['LIBDIR'])) - if PYTHON_CONFIG['LDLIBRARY']: + if 'LDLIBRARY' in PYTHON_CONFIG: ldlibrary = pathlib.Path(PYTHON_CONFIG['LDLIBRARY'].lstrip('lib')).with_suffix('') else: - ldlibrary = pathlib.Path('not_implemented_yet.dll') + ldlibrary = None - # libraries = split_and_strip('-l{} {} {} {}'.format( - # ldlibrary, PYTHON_CONFIG['LIBS'], PYTHON_CONFIG['SYSLIBS'], PYTHON_CONFIG['LINKFORSHARED'])) - libraries = split_and_strip('-l{} {} {}'.format( - ldlibrary, PYTHON_CONFIG['SYSLIBS'], PYTHON_CONFIG['LINKFORSHARED'])) + if 'LIBS' in PYTHON_CONFIG and 'SYSLIBS' in PYTHON_CONFIG and 'LINKFORSHARED' in PYTHON_CONFIG: + libraries = split_and_strip('-l{} {} {} {}'.format( + ldlibrary, PYTHON_CONFIG['LIBS'], PYTHON_CONFIG['SYSLIBS'], PYTHON_CONFIG['LINKFORSHARED'])) + else: + libraries = [] _options = { 'compile': ['-I{}'.format(_) for _ in include_paths], @@ -73,10 +84,19 @@ class ClangppInterface(CompilerInterface): _executables = {'': pathlib.Path('clang++')} + compile_flags = tuple() + + # Config does not always contain these items (eg. on Windows) + + if 'BASECFLAGS' in PYTHON_CONFIG: + compile_flags = tuple(split_and_strip('{} {}'.format(compile_flags, PYTHON_CONFIG['BASECFLAGS']))) + + if 'BASECPPFLAGS' in PYTHON_CONFIG: + compile_flags = tuple(split_and_strip('{} {}'.format(compile_flags, PYTHON_CONFIG['BASECPPFLAGS']))) + _flags = { '': ('-O3', '-fPIC', '-Wall', '-Wextra', '-Wpedantic', '-fcolor-diagnostics'), - 'compile': tuple(split_and_strip('{} {}'.format( - PYTHON_CONFIG['BASECFLAGS'], PYTHON_CONFIG['BASECPPFLAGS']))), + 'compile': compile_flags, 'OpenMP': ('-fopenmp',) } # -Ofast @@ -85,16 +105,20 @@ class ClangppInterface(CompilerInterface): pathlib.Path(PYTHON_CONFIG['INCLUDEPY']), *[pathlib.Path(_, 'core', 'include') for _ in np.__path__]] - library_paths = [ - pathlib.Path(PYTHON_CONFIG['LIBDIR'])] + library_paths = [] + if 'LIBDIR' in PYTHON_CONFIG: + library_paths.append(pathlib.Path(PYTHON_CONFIG['LIBDIR'])) - if PYTHON_CONFIG['LDLIBRARY']: + if 'LDLIBRARY' in PYTHON_CONFIG: ldlibrary = pathlib.Path(PYTHON_CONFIG['LDLIBRARY'].lstrip('lib')).with_suffix('') else: - ldlibrary = pathlib.Path('not_implemented_yet.dll') + ldlibrary = None - libraries = split_and_strip('-l{} {} {} {}'.format( - ldlibrary, PYTHON_CONFIG['LIBS'], PYTHON_CONFIG['SYSLIBS'], PYTHON_CONFIG['LINKFORSHARED'])) + if 'LIBS' in PYTHON_CONFIG and 'SYSLIBS' in PYTHON_CONFIG and 'LINKFORSHARED' in PYTHON_CONFIG: + libraries = split_and_strip('-l{} {} {} {}'.format( + ldlibrary, PYTHON_CONFIG['LIBS'], PYTHON_CONFIG['SYSLIBS'], PYTHON_CONFIG['LINKFORSHARED'])) + else: + libraries = [] _options = { 'compile': ['-I{}'.format(_) for _ in include_paths], From e8df15a8ec238b89eaca9cd47c78297bd8ea289d Mon Sep 17 00:00:00 2001 From: Erwin de Haan Date: Tue, 18 Jun 2019 15:25:03 +0200 Subject: [PATCH 7/8] Fixed unparsing of function calls to subscripts like func(args) --- transpyle/cpp/unparser.py | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/transpyle/cpp/unparser.py b/transpyle/cpp/unparser.py index 50763e4..f47908f 100644 --- a/transpyle/cpp/unparser.py +++ b/transpyle/cpp/unparser.py @@ -511,7 +511,7 @@ def _Attribute(self, t): def _Call(self, t): func_name = horast.unparse(t.func).strip() - + if func_name == 'np.zeros': self._includes['valarray'] = True self.write('std::valarray<') @@ -527,10 +527,10 @@ def _Call(self, t): comma = True self.dispatch(arg) return - + if t.keywords: self._unsupported_syntax(t, 'with keyword arguments') - + if func_name == 'print': self._includes['iostream'] = True self.write('std::cout << ') @@ -543,7 +543,17 @@ def _Call(self, t): self.dispatch(arg) return - super()._Call(t) + self.dispatch_type(t.func) + self.write('(') + + comma = False + for arg in t.args: + if comma: + self.write(", ") + else: + comma = True + self.dispatch(arg) + self.write(')') # def _Subscript(self, t): # super()._Subscript(t) From 40a54e41ae73279e32c34b3e71e0973f2977e44e Mon Sep 17 00:00:00 2001 From: Erwin de Haan Date: Thu, 8 Aug 2019 15:13:45 +0200 Subject: [PATCH 8/8] Removed extra warning, it's superfluous, since it's basically the default action. --- transpyle/cpp/unparser.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/transpyle/cpp/unparser.py b/transpyle/cpp/unparser.py index f47908f..eb6c123 100644 --- a/transpyle/cpp/unparser.py +++ b/transpyle/cpp/unparser.py @@ -503,7 +503,7 @@ def _Attribute(self, t): self.write(unparsed) return except KeyError: - _LOG.warning('Could not find %s.%s attribute in standard tuples. Assuming normal object.', t.value.id, t.attr) + pass #_LOG.warning('Could not find %s.%s attribute in standard tuples. Assuming normal object.', t.value.id, t.attr) self.dispatch(t.value) self.write('.')