614 lines
17 KiB
Python
614 lines
17 KiB
Python
import logging
|
|
import re
|
|
from utils import *
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
_py2capi_fmtmap = {
|
|
str: 's#',
|
|
}
|
|
|
|
NULL = 'NULL'
|
|
|
|
_symbols = set()
|
|
|
|
def new_symbol(name):
|
|
if name in _symbols:
|
|
ct = 1
|
|
orig = name
|
|
while name in _symbols:
|
|
name = '%s%d' % (orig, ct)
|
|
ct += 1
|
|
_symbols.add(name)
|
|
return name
|
|
|
|
def parse_arguments(println, var, *args):
|
|
typecodes = []
|
|
holders = []
|
|
argvals = []
|
|
for arg in args:
|
|
typecodes.append(arg.format)
|
|
val = declare(println, 'PyObject*')
|
|
argvals.append(val)
|
|
holders.append('&' + val)
|
|
|
|
items = [var, '"%s"' % (''.join(typecodes))] + holders
|
|
println('if(!PyArg_ParseTuple(%s)) return NULL;' % ', '.join(items))
|
|
|
|
# unwrap
|
|
unwrapped = []
|
|
for arg, val in zip(args, argvals):
|
|
unwrapped.append(arg.unwrap(println, val))
|
|
|
|
return unwrapped
|
|
|
|
_re_mangle_pattern = re.compile(r'[ _<>\*&]')
|
|
|
|
def mangle(name):
|
|
def repl(m):
|
|
s = m.group(0)
|
|
if s in '<>*&':
|
|
return ''
|
|
elif s in ' ':
|
|
return '_'
|
|
elif s in '_':
|
|
return '__'
|
|
else:
|
|
assert False
|
|
name = _re_mangle_pattern.sub(repl, name)
|
|
return name.replace('::', '_')
|
|
|
|
def pycapsule_new(println, ptr, name, clsname):
|
|
# build capsule
|
|
name_soften = mangle(name)
|
|
var = new_symbol('pycap_%s' % name_soften)
|
|
fmt = 'PyObject* %(var)s = pycapsule_new(%(ptr)s, "%(name)s", "%(clsname)s");'
|
|
println(fmt % locals())
|
|
println('if (!%(var)s) return NULL;' % locals())
|
|
return var
|
|
|
|
|
|
def declare(println, typ, init=None):
|
|
typ_soften = mangle(typ)
|
|
var = new_symbol('var_%s' % typ_soften)
|
|
if init is None:
|
|
println('%(typ)s %(var)s;' % locals())
|
|
else:
|
|
println('%(typ)s %(var)s = %(init)s;' % locals())
|
|
return var
|
|
|
|
|
|
def return_value(println, var):
|
|
println('return %(var)s;' % locals())
|
|
|
|
|
|
def return_none(println):
|
|
println('Py_RETURN_NONE;')
|
|
|
|
|
|
def die_if_null(println, var):
|
|
println('if (!%(var)s) return NULL;' % locals())
|
|
|
|
|
|
class Binding(object):
|
|
__rank_global = 0
|
|
def __init__(self):
|
|
self.rank = Binding.__rank_global
|
|
Binding.__rank_global += 1
|
|
self.include = set()
|
|
|
|
def compile(self, name, println):
|
|
raise NotImplementedError(type(self))
|
|
|
|
class Ref(object):
|
|
def __init__(self, elem):
|
|
self.element = elem
|
|
|
|
@property
|
|
def fullname(self):
|
|
return '%s&' % self.element.fullname
|
|
|
|
@property
|
|
def capsule_name(self):
|
|
return self.element.capsule_name
|
|
|
|
@property
|
|
def pointer(self):
|
|
return self.element.pointer
|
|
|
|
def as_pointer(self, println, var):
|
|
init = '&%s' % (var)
|
|
casted = declare(println, self.pointer, init)
|
|
return casted
|
|
|
|
@property
|
|
def format(self):
|
|
return 'O'
|
|
|
|
def unwrap(self, println, var):
|
|
ptr = self.element.unwrap(println, var)
|
|
return declare(println, self.fullname, '*%s' % ptr)
|
|
|
|
def wrap(self, println, var):
|
|
return self.element.wrap(println, self.as_pointer(println, var))
|
|
|
|
|
|
class Pointer(object):
|
|
def __init__(self, elem):
|
|
self.element = elem
|
|
|
|
@property
|
|
def fullname(self):
|
|
return '%s*' % self.element.fullname
|
|
|
|
@property
|
|
def capsule_name(self):
|
|
return self.element.capsule_name
|
|
|
|
@property
|
|
def pointer(self):
|
|
return self.element.pointer
|
|
|
|
@property
|
|
def format(self):
|
|
return 'O'
|
|
|
|
def unwrap(self, println, var):
|
|
ret = declare(println, self.fullname)
|
|
println2 = indent_println(println)
|
|
println('if (%(var)s == Py_None) {' % locals())
|
|
println2('%(ret)s = NULL;' % locals())
|
|
println('} else {')
|
|
ptr = self.element.unwrap(println2, var)
|
|
println2('%(ret)s = %(ptr)s;' % locals())
|
|
println('}')
|
|
return ret
|
|
|
|
def wrap(self, println, var):
|
|
return self.element.wrap(println, var)
|
|
|
|
|
|
class BuiltinType(object):
|
|
def __init__(self, name):
|
|
self.name = name
|
|
self.Ref = Ref(self)
|
|
self.Pointer = Pointer(self)
|
|
|
|
@property
|
|
def fullname(self):
|
|
return self.name
|
|
|
|
@property
|
|
def capsule_name(self):
|
|
return self.fullname
|
|
|
|
def To(self, pytype):
|
|
return Wrapper(self, pytype)
|
|
|
|
def From(self, pytype):
|
|
return Unwrapper(self, pytype)
|
|
|
|
Void = BuiltinType('void')
|
|
Bool = BuiltinType('bool')
|
|
Unsigned = BuiltinType('unsigned')
|
|
ConstStdString = BuiltinType('const std::string')
|
|
|
|
class PyObjectImpl(object):
|
|
name = 'PyObject*'
|
|
fullname = name
|
|
format = 'O'
|
|
|
|
def unwrap(self, println, var):
|
|
return var
|
|
|
|
PyObject = PyObjectImpl()
|
|
|
|
class Unwrapper(object):
|
|
def __init__(self, cls, pytype):
|
|
self.cls = cls
|
|
self.pytype = pytype
|
|
|
|
@property
|
|
def fullname(self):
|
|
return str(self.pytype)
|
|
|
|
@property
|
|
def format(self):
|
|
return 'O'
|
|
|
|
def unwrap(self, println, var):
|
|
out = declare(println, self.cls.fullname)
|
|
conv = 'py_%s_to' % (self.pytype.__name__)
|
|
status = '%(conv)s(%(var)s, %(out)s)' % locals()
|
|
println('if (!%(status)s) return NULL;' % locals())
|
|
return out
|
|
|
|
|
|
class Wrapper(object):
|
|
def __init__(self, cls, pytype):
|
|
self.cls = cls
|
|
self.pytype = pytype
|
|
|
|
@property
|
|
def fullname(self):
|
|
return self.cls.fullname
|
|
|
|
def wrap(self, println, var):
|
|
conv = 'py_%s_from' % (self.pytype.__name__)
|
|
func = '%(conv)s(%(var)s)' % locals()
|
|
out = declare(println, 'PyObject*', func)
|
|
println('if (!%(out)s) return NULL;' % locals())
|
|
return out
|
|
|
|
class Enum(Binding):
|
|
def __init__(self, ns, *values):
|
|
super(Enum, self).__init__()
|
|
self.values = values
|
|
self.ns = ns
|
|
self.name = None
|
|
|
|
def compile(self, name, println):
|
|
self.name = self.name or name
|
|
|
|
@property
|
|
def fullname(self):
|
|
return '::'.join([self.ns, self.name])
|
|
|
|
class ClassEnum(Enum):
|
|
def __init__(self, cls, *values):
|
|
super(ClassEnum, self).__init__(None, *values)
|
|
self.cls = cls
|
|
self.cls.enums.append(self)
|
|
|
|
def compile(self, name, println):
|
|
self.ns = self.cls.fullname
|
|
super(ClassEnum, self).compile(name, println)
|
|
|
|
def wrap(self, println, var):
|
|
println2 = indent_println(println)
|
|
ret = declare(println, 'PyObject*', NULL)
|
|
println('switch(%s) { ' % var)
|
|
for v in self.values:
|
|
println('case %s::%s:' % (self.ns, v))
|
|
println2('%(ret)s = PyString_FromString("%(v)s");' % locals())
|
|
println2('break;')
|
|
else:
|
|
println('default:')
|
|
println2('PyErr_SetString(PyExc_TypeError, "Invalid enum: %s");' %
|
|
v)
|
|
println2('return NULL;')
|
|
println('}')
|
|
return ret
|
|
|
|
def unwrap(self, println, var):
|
|
pass
|
|
|
|
|
|
class Class(Binding):
|
|
def __init__(self, ns):
|
|
super(Class, self).__init__()
|
|
self.ctor = None
|
|
self.Ref = Ref(self)
|
|
self.Pointer = Pointer(self)
|
|
self.Subclass = lambda: Subclass(self)
|
|
self.Enum = lambda *v: ClassEnum(self, *v)
|
|
self.enums = []
|
|
self.ns = ns
|
|
self.methods = []
|
|
self.name = None
|
|
|
|
def To(self, pytype):
|
|
return Wrapper(self, pytype)
|
|
|
|
def From(self, pytype):
|
|
return Unwrapper(self, pytype)
|
|
|
|
def new(self, *args):
|
|
method = Constructor(self, self.Pointer, *args)
|
|
self.methods.append(method)
|
|
return method
|
|
|
|
def delete(self):
|
|
method = Destructor(self, Void, self.Pointer)
|
|
self.methods.append(method)
|
|
return method
|
|
|
|
def method(self, return_type, *args):
|
|
method = Method(self, return_type, self.Pointer, *args)
|
|
self.methods.append(method)
|
|
return method
|
|
|
|
def staticmethod(self, return_type, *args):
|
|
sm = StaticMethod(self, return_type, *args)
|
|
self.methods.append(sm)
|
|
return sm
|
|
|
|
def multimethod(self, *signatures):
|
|
mm = MultiMethod(self, signatures)
|
|
self.methods.append(mm)
|
|
return mm
|
|
|
|
def staticmultimethod(self, *signatures):
|
|
smm = StaticMultiMethod(self, signatures)
|
|
self.methods.append(smm)
|
|
return smm
|
|
|
|
def compile(self, name, println):
|
|
# set name
|
|
self.name = self.name or name
|
|
|
|
@property
|
|
def capsule_name(self):
|
|
return self.fullname
|
|
|
|
@property
|
|
def pointer(self):
|
|
return '%s*' % self.fullname
|
|
|
|
@property
|
|
def fullname(self):
|
|
return '::'.join([self.ns, self.name])
|
|
|
|
@property
|
|
def mangled_name(self):
|
|
return mangle(self.fullname)
|
|
|
|
@property
|
|
def format(self):
|
|
return 'O'
|
|
|
|
def unwrap(self, println, var):
|
|
typ = self.pointer
|
|
elty = self.fullname
|
|
cap = self.capsule_name
|
|
capptr = 'PyCapsule_GetPointer(%(var)s, "%(cap)s")' % locals()
|
|
ptr = declare(println, 'void*', capptr)
|
|
unwrapped = 'typecast<%(elty)s>::from(%(ptr)s)' % locals()
|
|
var = declare(println, typ, unwrapped)
|
|
println('if (!%(var)s) {' % locals())
|
|
println2 = indent_println(println)
|
|
println2('PyErr_SetString(PyExc_TypeError, "Invalid cast");')
|
|
println2('return NULL;')
|
|
println('}')
|
|
die_if_null(println, var)
|
|
return var
|
|
|
|
def wrap(self, println, var):
|
|
return pycapsule_new(println, var, self.capsule_name, self.fullname)
|
|
|
|
class Subclass(Class):
|
|
def __init__(self, parent):
|
|
super(Subclass, self).__init__(parent.ns)
|
|
self.parent = parent
|
|
self.ns = self.parent.ns
|
|
|
|
@property
|
|
def capsule_name(self):
|
|
return self.parent.capsule_name
|
|
|
|
class Function(Binding):
|
|
def __init__(self, ns, return_type, *args):
|
|
super(Function, self).__init__()
|
|
self.return_type = return_type
|
|
self.args = args
|
|
self.ns = ns
|
|
self.name = None
|
|
|
|
def compile(self, name, println):
|
|
# set name
|
|
self.name = self.name or name
|
|
# generate wrapper
|
|
println('static')
|
|
println('PyObject*')
|
|
println('%(name)s(PyObject* self, PyObject* args)' % locals())
|
|
println('{')
|
|
self.compile_body(indent_println(println))
|
|
println('}')
|
|
|
|
def compile_body(self, println):
|
|
args = parse_arguments(println, 'args', *self.args)
|
|
call = '%s(%s)' % (self.fullname, ', '.join(args))
|
|
if self.return_type is not Void:
|
|
callres = declare(println, self.return_type.fullname, call)
|
|
pycap = self.return_type.wrap(println, callres)
|
|
return_value(println, pycap)
|
|
else:
|
|
println('%s;' % call)
|
|
return_none(println)
|
|
|
|
|
|
@property
|
|
def fullname(self):
|
|
return '::'.join([self.ns, self.name])
|
|
|
|
class Method(Binding):
|
|
def __init__(self, cls, return_type, *args):
|
|
super(Method, self).__init__()
|
|
self.cls = cls
|
|
self.return_type = return_type
|
|
self.args = args
|
|
self.name = None
|
|
self._realname = None
|
|
|
|
def compile(self, name, println):
|
|
# set name
|
|
self.name = self.name or name
|
|
# generate wrapper
|
|
println('static')
|
|
println('PyObject*')
|
|
mangled = self.mangled_name
|
|
println('%(mangled)s(PyObject* self, PyObject* args)' % locals())
|
|
println('{')
|
|
self.compile_body(indent_println(println))
|
|
println('}')
|
|
|
|
def compile_body(self, println):
|
|
args = parse_arguments(println, 'args', *self.args)
|
|
this = args[0]
|
|
args = ', '.join(args[1:])
|
|
name = self.realname
|
|
call = '%(this)s->%(name)s(%(args)s)' % locals()
|
|
if self.return_type is not Void:
|
|
obj = declare(println, self.return_type.fullname, call)
|
|
ret = self.return_type.wrap(println, obj)
|
|
return_value(println, ret)
|
|
else:
|
|
println('%s;' % call)
|
|
return_none(println)
|
|
|
|
@property
|
|
def fullname(self):
|
|
return '::'.join([self.cls.fullname, self.name])
|
|
|
|
@property
|
|
def realname(self):
|
|
if not self._realname:
|
|
return self.name
|
|
else:
|
|
return self._realname
|
|
|
|
@realname.setter
|
|
def realname(self, v):
|
|
self._realname = v
|
|
|
|
|
|
@property
|
|
def mangled_name(self):
|
|
return mangle(self.fullname)
|
|
|
|
class MultiMethod(Binding):
|
|
'''Can only differs by the number of arguments.
|
|
'''
|
|
def __init__(self, cls, signatures):
|
|
super(MultiMethod, self).__init__()
|
|
nargs = set()
|
|
for sig in signatures:
|
|
n = len(sig)
|
|
if n in nargs:
|
|
raise TypeError("MultiMethod only supports overloaded version"
|
|
"with different number of arguments")
|
|
nargs.add(n)
|
|
self.cls = cls
|
|
self.signatures = signatures
|
|
self.name = None
|
|
|
|
def compile(self, name, println):
|
|
# set name
|
|
self.name = self.name or name
|
|
# generate wrapper
|
|
println('static')
|
|
println('PyObject*')
|
|
mangled = self.mangled_name
|
|
println('%(mangled)s(PyObject* self, PyObject* args)' % locals())
|
|
println('{')
|
|
println2 = indent_println(println)
|
|
nargs = declare(println2, 'Py_ssize_t', 'PyTuple_Size(args)')
|
|
for sig in self.signatures:
|
|
expect = len(sig)
|
|
println2('if (%(nargs)s == %(expect)d) {' % locals())
|
|
method = Method(self.cls, sig[0], self.cls.Pointer, *sig[1:])
|
|
method.name = self.name
|
|
method.compile_body(indent_println(println2))
|
|
println2('}')
|
|
println2('PyErr_SetString(PyExc_TypeError, "Wrong # of args");')
|
|
println2('return NULL;')
|
|
println('}')
|
|
|
|
@property
|
|
def fullname(self):
|
|
return '::'.join([self.cls.fullname, self.name])
|
|
|
|
@property
|
|
def mangled_name(self):
|
|
return mangle(self.fullname)
|
|
|
|
class StaticMethod(Method):
|
|
def compile_body(self, println):
|
|
args = parse_arguments(println, 'args', *self.args)
|
|
args = ', '.join(args)
|
|
fullname = self.fullname
|
|
call = '%(fullname)s(%(args)s)' % locals()
|
|
if self.return_type is not Void:
|
|
obj = declare(println, self.return_type.fullname, call)
|
|
ret = self.return_type.wrap(println, obj)
|
|
return_value(println, ret)
|
|
else:
|
|
println('%s;' % call)
|
|
return_none(println)
|
|
|
|
class StaticMultiMethod(Binding):
|
|
'''Can only differs by the number of arguments.
|
|
'''
|
|
def __init__(self, cls, signatures):
|
|
super(StaticMultiMethod, self).__init__()
|
|
nargs = set()
|
|
for sig in signatures:
|
|
n = len(sig)
|
|
if n in nargs:
|
|
raise TypeError("StaticMultiMethod only supports overloaded "
|
|
"version with different number of arguments")
|
|
nargs.add(n)
|
|
self.cls = cls
|
|
self.signatures = signatures
|
|
self.name = None
|
|
|
|
|
|
def compile(self, name, println):
|
|
# set name
|
|
self.name = self.name or name
|
|
# generate wrapper
|
|
println('static')
|
|
println('PyObject*')
|
|
mangled = self.mangled_name
|
|
println('%(mangled)s(PyObject* self, PyObject* args)' % locals())
|
|
println('{')
|
|
println2 = indent_println(println)
|
|
nargs = declare(println2, 'Py_ssize_t', 'PyTuple_Size(args)')
|
|
for sig in self.signatures:
|
|
expect = len(sig) - 1
|
|
println2('if (%(nargs)s == %(expect)d) {' % locals())
|
|
method = StaticMethod(self.cls, sig[0], *sig[1:])
|
|
method.name = self.name
|
|
method.compile_body(indent_println(println2))
|
|
println2('}')
|
|
println2('PyErr_SetString(PyExc_TypeError, "Wrong # of args");')
|
|
println2('return NULL;')
|
|
println('}')
|
|
|
|
@property
|
|
def fullname(self):
|
|
return '::'.join([self.cls.fullname, self.name])
|
|
|
|
@property
|
|
def mangled_name(self):
|
|
return mangle(self.fullname)
|
|
|
|
class Constructor(StaticMethod):
|
|
def compile_body(self, println):
|
|
args = parse_arguments(println, 'args', *self.args)
|
|
args = ', '.join(args)
|
|
name = self.cls.fullname
|
|
ctor = 'new %(name)s(%(args)s)' % locals()
|
|
obj = declare(println, self.cls.pointer, ctor)
|
|
ret = self.return_type.wrap(println, obj)
|
|
return_value(println, ret)
|
|
|
|
class Destructor(Method):
|
|
def compile_body(self, println):
|
|
args = parse_arguments(println, 'args', *self.args)
|
|
assert len(args) == 1
|
|
dtor = 'delete %s;' % args[0]
|
|
println(dtor)
|
|
return_none(println)
|
|
|
|
|
|
class Namespace(object):
|
|
def __init__(self, name):
|
|
self.name = name
|
|
|
|
def Class(self, *args, **kwargs):
|
|
return Class(self.name, *args, **kwargs)
|
|
|
|
def Function(self, *args, **kwargs):
|
|
return Function(self.name, *args, **kwargs)
|
|
|