llvmpy/newbinding/codegen.py

281 lines
7.8 KiB
Python

import re, contextlib
NULL = 'NULL'
_symbols = set()
def indent(println):
def _println(s=''):
println("%s%s" % (' '* 4, s))
return _println
def quote(txt):
return '"%s"' % txt
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 CodeWriterBase(object):
def __init__(self, println):
self.println = println
self.used_symbols = set()
@contextlib.contextmanager
def indent(self):
old = self.println
self.println = indent(self.println)
yield
self.println = old
@contextlib.contextmanager
def py_function(self, name):
self.println('static')
self.println('PyObject*')
with self.block('%(name)s(PyObject* self, PyObject* args)' % locals()):
self.used_symbols.add('self')
self.used_symbols.add('args')
yield
self.println()
def new_symbol(self, name):
if name in self.used_symbols:
ct = 1
orig = name
while name in self.used_symbols:
name = '%s%d' % (orig, ct)
ct += 1
self.used_symbols.add(name)
return name
class CppCodeWriter(CodeWriterBase):
@contextlib.contextmanager
def block(self, lead):
self.println(lead)
self.println('{')
with self.indent():
yield
self.println('}')
def declare(self, typ, init=None):
typ_soften = mangle(typ)
var = self.new_symbol('var_%s' % typ_soften)
if init is None:
self.println('%(typ)s %(var)s;' % locals())
else:
self.println('%(typ)s %(var)s = %(init)s;' % locals())
return var
def return_value(self, val):
if val is None:
self.println('Py_RETURN_NONE;')
else:
self.println('return %s;' % val)
def return_null(self):
self.return_value(NULL)
def parse_arguments(self, var, *args):
typecodes = []
holders = []
argvals = []
for arg in args:
typecodes.append(arg.format)
val = self.declare('PyObject*')
argvals.append(val)
holders.append('&' + val)
items = [var, '"%s"' % (''.join(typecodes))] + holders
with self.block('if(!PyArg_ParseTuple(%s))' % ', '.join(items)):
self.return_null()
# unwrap
unwrapped = []
for arg, val in zip(args, argvals):
unwrapped.append(arg.unwrap(self, val))
return unwrapped
def call(self, func, retty, *args):
arglist = ', '.join(args)
stmt = '%(func)s(%(arglist)s)' % locals()
if retty == 'void':
self.println(stmt + ';')
else:
return self.declare(retty, stmt)
def method_call(self, func, retty, *args):
this = args[0]
arglist = ', '.join(args[1:])
if func == 'delete':
assert not arglist
stmt = 'delete %(this)s' % locals()
elif func == 'new':
alloctype = retty.rstrip(' *')
stmt = 'new %(alloctype)s(%(arglist)s)' % locals()
else:
stmt = '%(this)s->%(func)s(%(arglist)s)' % locals()
if retty == 'void':
self.println('%s;' % stmt)
else:
return self.declare(retty, stmt)
def pycapsule_new(self, ptr, name, clsname):
name_soften = mangle(name)
ret = self.call('pycapsule_new', 'PyObject*', ptr, quote(name),
quote(clsname))
with self.block('if (!%(ret)s)' % locals()):
self.return_null()
return ret
def die_if_false(self, val):
with self.block('if(!%(val)s)' % locals()):
self.return_null()
def raises(self, exccls, msg):
exc = 'PyExc_%s' % exccls.__name__
self.println('PyErr_SetString(%s, "%s");' % (exc, msg))
self.return_null()
class PyCodeWriter(CodeWriterBase):
@contextlib.contextmanager
def block(self, lead):
self.println(lead)
with self.indent():
yield
@contextlib.contextmanager
def function(self, func, args=(), varargs=None):
with self.scope():
arguments = []
for arg in args:
arguments.append(self.new_symbol(arg))
if varargs:
varargs = self.new_symbol(varargs)
arguments.append('*%s' % varargs)
arglist = ', '.join(arguments)
with self.block('def %(func)s(%(arglist)s):' % locals()):
if arguments:
arguments[-1] = arguments[-1].lstrip('*')
if len(arguments) > 1:
yield arguments
else:
yield arguments[0]
else:
yield
@contextlib.contextmanager
def scope(self):
self.old = self.used_symbols
self.used_symbols = set()
yield
self.used_symbols = self.old
def release_ownership(self, val):
self.println('capsule.release_ownership(%(val)s)' % locals())
def unwrap_many(self, args):
unwrapped = self.new_symbol('unwrapped')
self.println('%(unwrapped)s = map(capsule.unwrap, %(args)s)' % locals())
return unwrapped
def unwrap(self, val):
return self.call('capsule.unwrap', args=(val,), ret='unwrapped')
def wrap(self, val, owned):
wrapped = self.new_symbol('wrapped')
self.println('%(wrapped)s = capsule.wrap(%(val)s, %(owned)s)' % locals())
return wrapped
def call(self, func, args=(), varargs=None, ret='ret'):
arguments = []
for arg in args:
arguments.append(arg)
if varargs:
arguments.append('*%s' % varargs)
arglist = ', '.join(arguments)
ret = self.new_symbol(ret)
self.println('%(ret)s = %(func)s(%(arglist)s)' % locals())
return ret
def return_value(self, val=None):
if val is None:
val = ''
self.println('return %s' % val)