llvmpy/newbinding/binding.py
2013-02-05 13:47:34 -06:00

434 lines
13 KiB
Python

import functools
import codegen as cg
_rank = 0
namespaces = {}
class Namespace(object):
def __init__(self, name):
self.name = name
self.classes = []
self.functions = []
namespaces[name] = self
def Class(self, *bases):
cls = Class(self, *bases)
self.classes.append(cls)
return cls
def Function(self, *args):
fn = Function(self, *args)
self.functions.append(fn)
return fn
@property
def fullname(self):
return self.name
def __str__(self):
return self.name
class Type(object):
pass
class BuiltinTypes(Type):
def __init__(self, name):
self.name = name
@property
def fullname(self):
return self.name
def wrap(self, writer, var):
return var
Void = BuiltinTypes('void')
Unsigned = BuiltinTypes('unsigned')
Bool = BuiltinTypes('bool')
ConstStdString = BuiltinTypes('const std::string')
class Class(Type):
format = 'O'
def __init__(self, ns, *bases):
self.ns = ns
self.bases = bases
self._is_defined = False
self.methods = []
self.enums = []
self.includes = set()
def __call__(self, defn):
assert not self._is_defined
self.name = defn.__name__
for k, v in defn.__dict__.items():
if isinstance(v, Method):
self.methods.append(v)
if isinstance(v, Constructor):
for sig in v.signatures:
sig[0] = ptr(self)
v.name = k
v.parent = self
elif isinstance(v, Enum):
self.enums.append(v)
v.name = k
v.parent = self
elif k == '_include_':
if isinstance(v, str):
self.includes.add(v)
else:
for i in v:
self.includes.add(i)
return self
def compile_cpp(self, writer):
# generate methods
for meth in self.methods:
meth.compile_cpp(writer)
# generate method table
writer.println('static')
writer.println('PyMethodDef %s[] = {' % cg.mangle(self.fullname))
with writer.indent():
fmt = '{ "%(name)s", (PyCFunction)%(func)s, METH_VARARGS, NULL },'
for meth in self.methods:
name = meth.name
func = cg.mangle(meth.fullname)
writer.println(fmt % locals())
writer.println('{ NULL },')
writer.println('};')
writer.println()
def compile_py(self, writer):
clsname = self.name
bases = 'capsule.Wrapper'
if self.bases:
bases = ', '.join(x.name for x in self.bases)
writer.println('@capsule.register_class')
with writer.block('class %(clsname)s(%(bases)s):' % locals()):
for enum in self.enums:
enum.compile_py(writer)
for meth in self.methods:
meth.compile_py(writer)
if not self.enums and not self.methods:
writer.println('pass')
writer.println()
@property
def capsule_name(self):
if self.bases:
return self.bases[-1].capsule_name
else:
return self.fullname
@property
def fullname(self):
try:
name = self.realname
except AttributeError:
name = self.name
return '::'.join([self.ns.fullname, name])
def __str__(self):
return self.fullname
def unwrap(self, writer, val):
fmt = 'PyCapsule_GetPointer(%(val)s, "%(name)s")'
name = self.capsule_name
raw = writer.declare('void*', fmt % locals())
writer.die_if_false(raw)
ptrty = ptr(self).fullname
ty = self.fullname
fmt = 'typecast<%(ty)s>::from(%(raw)s)'
casted = writer.declare(ptrty, fmt % locals())
writer.die_if_false(casted)
return casted
class Enum(object):
def __init__(self, *value_names):
self.parent = None
self.value_names = value_names
@property
def fullname(self):
try:
name = self.realname
except AttributeError:
name = self.name
return '::'.join([self.parent.fullname, name])
def __str__(self):
return self.fullname
def wrap(self, writer, val):
ret = writer.declare('PyObject*', 'NULL')
with writer.block('switch(%s) ' % val):
for v in self.value_names:
writer.println('case %s::%s:' % (self.parent, v))
with writer.indent():
fmt = '%(ret)s = PyString_FromString("%(v)s");'
writer.println(fmt % locals())
writer.println('break;')
else:
writer.println('default:')
with writer.indent():
writer.raises(ValueError, 'Invalid enum %s' % v)
return ret
def compile_py(self, writer):
with writer.block('class %s:' % self.name):
for v in self.value_names:
writer.println('%(v)s = "%(v)s"' % locals())
writer.println()
class Method(object):
_kind_ = 'meth'
def __init__(self, return_type=Void, *args):
self.parent = None
self.signatures = []
self.includes = set()
self._add_signature(return_type, *args)
def _add_signature(self, return_type, *args):
prev_lens = set(map(len, self.signatures))
cur_len = len(args) + 1
if cur_len in prev_lens:
raise Exception('Only support overloading with different number'
' of arguments')
self.signatures.append([return_type] + list(args))
def __ior__(self, method):
assert type(self) is type(method)
for sig in method.signatures:
self._add_signature(sig[0], *sig[1:])
return self
@property
def fullname(self):
return '::'.join([self.parent.fullname, self.realname])
@property
def realname(self):
try:
return self.__realname
except AttributeError:
return self.name
@realname.setter
def realname(self, v):
self.__realname = v
def __str__(self):
return self.fullname
def compile_cpp(self, writer):
with writer.py_function(self.fullname):
if len(self.signatures) == 1:
sig = self.signatures[0]
retty = sig[0]
argtys = sig[1:]
self.compile_cpp_body(writer, retty, argtys)
else:
nargs = writer.declare('Py_ssize_t', 'PyTuple_Size(args)')
for sig in self.signatures:
retty = sig[0]
argtys = sig[1:]
expect = len(argtys)
if (not isinstance(self, StaticMethod) and
isinstance(self.parent, Class)):
# Is a instance method, add 1 for "this".
expect += 1
with writer.block('if (%(expect)d == %(nargs)s)' % locals()):
self.compile_cpp_body(writer, retty, argtys)
writer.raises(TypeError, 'Invalid number of args')
def compile_cpp_body(self, writer, retty, argtys):
if isinstance(self.parent, Class):
args = writer.parse_arguments('args', ptr(self.parent), *argtys)
ret = writer.method_call(self.realname, retty.fullname, *args)
else:
args = writer.parse_arguments('args', *argtys)
ret = writer.call(self.fullname, retty.fullname, *args)
writer.return_value(retty.wrap(writer, ret))
def compile_py(self, writer):
decl = writer.function(self.name, args=('self',), varargs='args')
with decl as (this, varargs):
unwrap_this = writer.unwrap(this)
unwrapped = writer.unwrap_many(varargs)
func = '.'.join([self.parent.name, self.name])
ret = writer.call('_api.%s' % func,
args=(unwrap_this,), varargs=unwrapped)
wrapped = writer.wrap(ret)
writer.return_value(wrapped)
writer.println()
class StaticMethod(Method):
def compile_cpp_body(self, writer, retty, argtys):
assert isinstance(self.parent, Class)
args = writer.parse_arguments('args', *argtys)
ret = self.compile_cpp_call(writer, retty, args)
writer.return_value(retty.wrap(writer, ret))
def compile_cpp_call(self, writer, retty, args):
ret = writer.call(self.fullname, retty.fullname, *args)
return ret
def compile_py(self, writer):
writer.println('@staticmethod')
decl = writer.function(self.name, varargs='args')
with decl as varargs:
unwrapped = writer.unwrap_many(varargs)
func = '.'.join([self.parent.name, self.name])
ret = writer.call('_api.%s' % func, varargs=unwrapped)
wrapped = writer.wrap(ret)
writer.return_value(wrapped)
writer.println()
class Function(Method):
_kind_ = 'func'
def __init__(self, parent, name, return_type=Void, *args):
super(Function, self).__init__(return_type, *args)
self.parent = parent
self.name = name
def compile_py(self, writer):
with writer.function(self.name, varargs='args') as varargs:
unwrapped = writer.unwrap_many(varargs)
func = self.fullname.split('::', 1)[1].replace('::', '.')
ret = writer.call('_api.%s' % func,
varargs=unwrapped)
wrapped = writer.wrap(ret)
writer.return_value(wrapped)
writer.println()
class Destructor(Method):
_kind_ = 'dtor'
def __init__(self):
super(Destructor, self).__init__()
def compile_cpp_body(self, writer, retty, argtys):
assert isinstance(self.parent, Class)
assert not argtys
args = writer.parse_arguments('args', ptr(self.parent), *argtys)
writer.println('delete %s;' % args[0])
writer.return_value(None)
def compile_py(self, writer):
func = '.'.join([self.parent.name, self.name])
writer.println('_delete_ = _api.%s' % func)
class Constructor(StaticMethod):
_kind_ = 'ctor'
def __init__(self, *args):
super(Constructor, self).__init__(Void, *args)
def compile_cpp_call(self, writer, retty, args):
alloctype = retty.fullname.rstrip(' *')
arglist = ', '.join(args)
stmt = 'new %(alloctype)s(%(arglist)s)' % locals()
ret = writer.declare(retty.fullname, stmt)
return ret
class ref(Type):
def __init__(self, element):
assert isinstance(element, Class), type(element)
self.element = element
def __str__(self):
return self.fullname
@property
def fullname(self):
return '%s&' % self.element.fullname
@property
def capsule_name(self):
return self.element.capsule_name
@property
def format(self):
return self.element.format
def wrap(self, writer, val):
p = writer.declare(ptr(self.element).fullname, '&%s' % val)
return writer.pycapsule_new(p, self.capsule_name, self.element.fullname)
def unwrap(self, writer, val):
p = self.element.unwrap(writer, val)
return writer.declare(self.fullname, '*%s' % p)
class ptr(Type):
def __init__(self, element):
assert isinstance(element, Class)
self.element = element
@property
def fullname(self):
return '%s*' % self.element
@property
def format(self):
return self.element.format
def unwrap(self, writer, val):
ret = writer.declare(self.fullname, 'NULL')
with writer.block('if (%(val)s != Py_None)' % locals()):
val = self.element.unwrap(writer, val)
writer.println('%(ret)s = %(val)s;' % locals())
return ret
def wrap(self, writer, val):
return writer.pycapsule_new(val, self.element.capsule_name,
self.element.fullname)
class cast(Type):
format = 'O'
def __init__(self, original, target):
self.original = original
self.target = target
@property
def fullname(self):
return self.binding_type.fullname
@property
def python_type(self):
if not isinstance(self.target, Type):
return self.target
else:
return self.original
@property
def binding_type(self):
if isinstance(self.target, Type):
return self.target
else:
return self.original
def wrap(self, writer, val):
dst = self.python_type.__name__
return writer.call('py_%(dst)s_from' % locals(), 'PyObject*', val)
def unwrap(self, writer, val):
src = self.python_type.__name__
dst = self.binding_type.fullname
ret = writer.declare(dst)
status = writer.call('py_%(src)s_to' % locals(), 'int', val, ret)
writer.die_if_false(status)
return ret