Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions transpyle/cpp/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):

Expand Down
60 changes: 42 additions & 18 deletions transpyle/cpp/compiler_interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -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',)
}
Expand All @@ -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],
Expand All @@ -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
Expand All @@ -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],
Expand Down
141 changes: 116 additions & 25 deletions transpyle/cpp/unparser.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand All @@ -154,8 +155,27 @@ 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('<')
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
self._unsupported_syntax(type_hint)
if isinstance(type_hint, typed_ast3.Attribute):
if isinstance(type_hint.value, typed_ast3.Name):
Expand All @@ -169,12 +189,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')
Expand All @@ -183,14 +210,22 @@ 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':
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(" = ")
Expand All @@ -213,7 +248,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)
Expand All @@ -229,9 +267,18 @@ 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))
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'

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 ')
Expand All @@ -258,8 +305,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()
Expand Down Expand Up @@ -341,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('; ')
Expand Down Expand Up @@ -451,18 +496,22 @@ 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:
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('.')
self.write(t.attr)

def _Call(self, t):
func_name = horast.unparse(t.func).strip()

if func_name == 'np.zeros':
self._includes['valarray'] = True
self.write('std::valarray<')
Expand All @@ -478,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 << ')
Expand All @@ -494,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)
Expand All @@ -507,12 +566,44 @@ 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):
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):

Expand Down