# Copyright, 1999, Regents of the University of California
# Please see file Legal.htm
import string
import sys, re, os
from semantics import *
sizere = re.compile(r'(size[ \t]*\([ \t]*)([a-zA-Z_][a-zA-Z_0-9]*)', re.IGNORECASE)
class PyFortError (Exception):
def __init__ (self, *args):
self.args=args
def __str__ (self):
return str(self.args)
class Generator:
def __init__(self, module_name, routine_list, common_list):
self.module_name = module_name
self.routine_list = routine_list
self.common_list = common_list
if common_list:
raise PyFortError, "Data blocks not supported."
class Document_Generator (Generator):
def __init__(self, module_name, outdir, routine_list, common_list):
Generator.__init__ (self, module_name, routine_list, common_list)
self.filename = os.path.join(outdir, module_name + ".txt")
def generate(self):
dout = open(self.filename, "w")
dout.write("Procedures in module " + self.module_name + '\n')
for proc in self.routine_list:
dout.write("\n----------- " + proc.routine_name() + " ------------\n")
s = proc.head_comments()
if s:
dout.write(s)
dout.write('\nCalling sequence: \n ' + self.generate_routine_interface (proc)+ '\n')
dout.write('Fortran declarations for these variables are:\n')
for x in proc.declarations():
dout.write(' ')
y = x.intent
if y == 'valued': x.set_intent('in')
dout.write(repr(x))
x.set_intent(y)
dout.write('\n')
def generate_routine_interface (self, proc):
"Generate the calling sequence for proc in Python"
t = proc.python_outputs()
if t:
if t[0] == proc.routine_name():
t[0] = 'function_result'
s = string.join(t, ', ') + ' = '
else:
s = ''
s = s + proc.routine_name()
s = s + '('
s = s + string.join(proc.python_inputs(), ', ')
s = s + ')'
return s
_nlre = re.compile(r'$', re.MULTILINE)
_begre = re.compile(r'^', re.MULTILINE)
def fixup (acomment):
"Fix the comment so it can be a C string literal."
out = string.replace(acomment, '"', r'\"')
out = _nlre.sub(r'\\n"', out)
out = _begre.sub(r'"', out)
return out
class C_Module_Generator (Generator):
def __init__(self,
compiler,
module_name,
outdir,
routine_list,
common_list,
):
Generator.__init__ (self, module_name, routine_list, common_list)
self.compiler = compiler
self.filename = os.path.join(outdir, module_name + "module.c")
def generate(self):
"Generate the C module wrapper."
self.module_file = open (self.filename, 'w')
try:
self.write_header ()
self.write_array_argument_handler ()
self.write_methods()
self.write_method_table ()
self.write_init_function ()
self.write_trailer()
self.module_file.close()
except PyFortError, msg:
print msg
self.module_file.close()
self.module_file.unlink()
raise SystemExit, 1
##################################################
# writing the module header
##################################################
def write_header (self):
"""Write the beginning of the module file."""
text = \
"""\
#ifdef __CPLUSPLUS__
extern "C" {
#endif
#include "Python.h"
#include "Numeric/arrayobject.h"
static PyObject *ErrorObject;
static int array_really_contiguous(PyArrayObject *ap) {
int sd;
int i;
sd = ap->descr->elsize;
for (i = ap->nd-1; i >= 0; --i) {
if (ap->dimensions[i] == 0) return 1; /* contiguous by definition */
if (ap->strides[i] != sd) return 0;
sd *= ap->dimensions[i];
}
return 1;
}
struct fcomplex {
float r;
float i;
};
typedef struct fcomplex Py_complex_float;
"""
self.put(text)
text = \
"""\
#define TRANSPOSE_OPTION %(transpose_option)d
#define MIRROR_OPTION %(mirror_option)d
#define get_fortran_dim(v,n) v->dimensions[(keyoption & MIRROR_OPTION) ? v->nd - (n) : (n)-1]"""
D = {'transpose_option': self.compiler.transpose_option(),
'mirror_option': self.compiler.mirror_option()}
self.put(text, D)
self.put(self.compiler.header())
##### Write the array argument acquisition #####
def write_array_argument_handler (self):
text = \
"""
static int default_option = TRANSPOSE_OPTION;
static PyObject*
set_pyfort_option (PyObject* unused, PyObject* args) {
if(!PyArg_ParseTuple(args, "i", &default_option)) return NULL;
Py_INCREF(Py_None);
return Py_None;
}
static void
set_pyfort_error (char* routine, char* var, char* problem) {
char buf[512];
sprintf(buf, "%%s, argument %%s: %%s", routine, var, problem);
PyErr_SetString (ErrorObject, buf);
}
static void set_transposed_strides (PyArrayObject* ar)
{
int m1, n1, itmp;
n1 = ar->nd;
if (n1 < 2) return;
m1 = ar->strides[n1-1]; /* stride for one element */
for(itmp=0; itmp < n1 ; itmp++) {
ar->strides[itmp] = m1;
m1 *= ar->dimensions[itmp];
}
ar->flags &= ~CONTIGUOUS;
}
static PyArrayObject*
transpose_array (char* rname, char* vname, PyArrayObject* ap) {
/* return transpose of ap, possibly not contiguous so as to avoid copy if we
are transposing a previously transposed contiguous array
This means with the transpose option on the output of one call might
not need any copying if used as input to another call. I.e., Fortran
arrays stay in row-major order.
*/
int i, n;
PyArrayObject *ret;
n = ap->nd;
/* this allocates memory for dimensions and strides (but fills them
incorrectly), sets up descr, and points data at ap->data. */
ret = (PyArrayObject *)PyArray_FromDimsAndData(n, ap->dimensions,
ap->descr->type_num,
ap->data);
if (!ret) {
set_pyfort_error (rname, vname, "Could not create descriptors for transpose.");
return NULL;
}
/* point at true owner of memory: */
ret->base = (PyObject *)ap;
Py_INCREF(ap);
for(i=0; i<n; i++) {
ret->dimensions[i] = ap->dimensions[n - i - 1];
ret->strides[i] = ap->strides[n - i - 1];
}
if (array_really_contiguous(ret)) {
ret->flags |= CONTIGUOUS;
} else {
ret->flags &= ~CONTIGUOUS;
}
return ret;
}
static PyArrayObject* make_contiguous(char* rname, char* vname, PyArrayObject* ap)
{
/* return an owned ref to a contiguous version of ap */
PyArrayObject* result;
if (ap->flags & CONTIGUOUS) {
Py_INCREF (ap);
return ap;
} else {
result = (PyArrayObject *) PyArray_ContiguousFromObject(
(PyObject*) ap, ap->descr->type_num, 0, 0);
if(!result) set_pyfort_error(rname, vname, "Failed making object contiguous.");
return result;
}
}
static int do_size_check (char* rname, char* vname, PyArrayObject *av, int rank, int extents[], int mirror)
{
int size1;
int i, j;
char buf[512];
size1 = av->nd;
if( size1 == rank) {
for(i=0; i < rank; ++i) {
/* no checking on last dimension of expected size 1 */
if (i == size1-1) {
if (extents[i] == 1) break;
}
j = mirror ? size1 - 1 - i : i;
if(av->dimensions[j] != extents[i])
{
sprintf(buf, "Incorrect extent in dimension %%d (%%d expected %%d)",
i+1, av->dimensions[j], extents[i]);
set_pyfort_error(rname, vname, buf);
return 0;
}
}
} else {
if (rank != 1 ||
size1 > 0 ||
extents[0] != 1)
{
sprintf(buf, "Incorrect rank (%%d expected %%d)",
size1, rank);
set_pyfort_error(rname, vname, buf);
return 0;
}
}
return 1; /* size ok */
}
static PyArrayObject*
do_array_in (char* rname, char* vname, PyObject *v,
enum PyArray_TYPES python_array_type)
{
PyArrayObject* av;
PyArrayObject* t;
if(!PyArray_Check (v)) {
t = (PyArrayObject *) PyArray_ContiguousFromObject(v, PyArray_NOTYPE, 0, 0);
if (!t) {
set_pyfort_error(rname, vname, "Argument cannot be converted to needed array.");
goto err;
}
} else {
t = (PyArrayObject*) v;
Py_INCREF((PyObject*) t);
}
if (t->descr->type_num != python_array_type) {
av = (PyArrayObject*) PyArray_Cast (t, python_array_type);
Py_DECREF((PyObject*) t);
t = av;
if (!t) {
set_pyfort_error(rname, vname, "Argument cannot be cast to needed type.");
goto err;
}
}
return t;
err:
return (PyArrayObject*) 0;
}
static PyArrayObject*
do_array_inout (char* rname, char* vname, PyObject *v,
enum PyArray_TYPES python_array_type)
{
PyArrayObject* av;
if (!PyArray_Check (v)) {
set_pyfort_error(rname, vname, "Argument intent(inout) must be an array.");
goto err;
}
av = (PyArrayObject*) v;
if (av->descr->type_num != python_array_type) {
set_pyfort_error(rname, vname, "Argument intent(inout) must be of correct type.");
goto err;
}
if (!(av->flags & CONTIGUOUS)) {
set_pyfort_error(rname, vname, "Argument intent(inout) must be contiguous.");
goto err;
}
Py_INCREF(av);
return av;
err:
return (PyArrayObject*) 0;
}
static PyArrayObject*
do_array_create (char* rname, char* vname, enum PyArray_TYPES python_array_type,
int rank, int extents[], int mirror)
{
PyArrayObject* av;
int i, dims[7];
if (rank > 7) {
set_pyfort_error(rname, vname, "Too many dimensions -- limit is 7.");
goto err;
}
if(mirror) {
for(i=0; i < rank; ++i) dims[i] = extents[rank-1-i];
} else {
for(i=0; i < rank; ++i) dims[i] = extents[i];
}
av = (PyArrayObject*) PyArray_FromDims(rank, dims, python_array_type);
if (!av) {
set_pyfort_error(rname, vname, "Could not create array -- too big?");
goto err;
}
return av;
err:
return (PyArrayObject*) 0;
}
"""
self.put(text);
##################################################
# writing the trailer
##################################################
def write_trailer(self):
text = \
"""
/* C++ trailer */
#ifdef __CPLUSCPLUS__
}
#endif
"""
self.put(text);
##################################################
# writing the method table
##################################################
def write_method_table (self):
"Write the method table."
D = {'module_name': self.module_name}
# Declare the method table
text = \
"""
static struct PyMethodDef %(module_name)s_methods[] = {
"""
self.put(text, D)
# Put the wrapper functions of routines in the method table
text = \
"""\
{"%(routine_name)s", (PyCFunction) %(module_name)s_%(routine_name)s, METH_VARARGS, %(module_name)s_%(routine_name)s__doc__},
"""
for routine in self.routine_list:
D['routine_name'] = routine.routine_name()
self.module_file.write(text % D)
text = \
"""\
{"set_pyfort_option", (PyCFunction) set_pyfort_option, METH_VARARGS,
"set_pyfort_option (value) sets default value of option keyword."},
{NULL, NULL, 0, NULL}/* sentinel */
};
"""
self.put(text)
##################################################
# writing the initialization function for Python
##################################################
def write_init_function (self):
D = {'module_name': self.module_name}
text = \
"""
static char %(module_name)s_module_documentation[] =
"Fortran interface module %(module_name)s";
void init%(module_name)s(void)
{
PyObject *m, *d, *j;
import_array ();
m = Py_InitModule4("%(module_name)s", %(module_name)s_methods,
%(module_name)s_module_documentation,
(PyObject*)NULL,PYTHON_API_VERSION);
d = PyModule_GetDict(m);
ErrorObject = PyString_FromString("%(module_name)s.error");
PyDict_SetItemString(d, "error", ErrorObject);
j = PyInt_FromLong((long) TRANSPOSE_OPTION);
PyDict_SetItemString(d, "TRANSPOSE", j);
Py_XDECREF(j);
j = PyInt_FromLong((long) MIRROR_OPTION);
PyDict_SetItemString(d, "MIRROR", j);
Py_XDECREF(j);
j = PyInt_FromLong(0L);
PyDict_SetItemString(d, "NONE", j);
Py_XDECREF(j);
if (PyErr_Occurred()) {
Py_FatalError("can't initialize module %(module_name)s");
}
}
"""
self.put(text, D)
##################################################
# writing the method wrappers
##################################################
def write_methods (self):
self.put("/* Methods */\n")
for routine in self.routine_list:
self.write_a_routine_wrapper (routine)
##################################################
# writing the wrapper for one routine
##################################################
def write_a_routine_wrapper (self, routine):
"Write out one routine's wrapper."
self.array_result_flag = 0
self.compiler.modify_routine (routine)
self.write_docstring (routine)
self.write_prototype(routine)
self.write_method_header(routine)
self.write_general_locals(routine)
self.write_fortran_argument_locals(routine)
self.write_initialization(routine)
self.write_argument_parsing(routine)
self.write_preprocessing(routine)
self.write_fortran_call(routine)
self.write_postprocessing(routine)
self.write_fortran_argument_cleanup(routine, 0)
self.write_return_value_construction(routine)
self.write_fortran_argument_cleanup(routine, 1)
self.write_method_finish(routine)
def write_docstring (self, routine):
"Write out one routine's doc string."
h = routine.head_comments()
if h:
c = fixup(h)
else:
c = '"' + routine.routine_name() + '(' + \
string.join(routine.python_inputs(), ', ') + ')' + '"'
D = {'module_name': self.module_name,
'routine_name': routine.routine_name(),
'comment': c \
}
text = \
"""
/* %(routine_name)s */
static char %(module_name)s_%(routine_name)s__doc__[] =
%(comment)s;
"""
self.put(text, D)
def set_variable (self, name, routine, D):
"Set things in D"
if name == routine.routine_name():
n = 'fortran_result'
else:
n = string.upper(name)
d = routine.declaration(name)
c = self.compiler.c_type(d)
D['routine_name'] = routine.routine_name()
D['c_type'] = c
D['variable_name'] = string.upper(name)
if d.rank():
D['d_type'] = 'PyObject*'
else:
D['d_type'] = c
def write_prototype(self, routine):
"""Write the prototype for the Fortran call"""
self.put(self.compiler.prototype(routine))
def write_method_header(self, routine):
D = {'module_name': self.module_name,
'routine_name': routine.routine_name() \
}
text = \
"""
static PyObject*
%(module_name)s_%(routine_name)s (PyObject* unused, PyObject* args) {
"""
self.put(text, D)
def write_general_locals(self, routine):
text = \
"""
PyObject *pyfort_result;
int keyoption;
"""
self.put (text)
if routine.is_function():
text = \
"""\
%(result_type)s fortran_result;
"""
D = {'result_type': self.compiler.c_type(routine.declaration(routine.routine_name())) }
self.put (text, D)
if D['result_type'] == 'Py_complex' or D['result_type'] == 'Py_complex_float':
text = \
"""\
PyArrayObject* fortran_result_array;
PyObject* rfortran_result_array;
int fortran_result_array_length;
"""
self.put(text)
self.array_result_flag = 1
def write_fortran_argument_locals(self, routine):
text = \
"""\
%(d_type)s %(prefix)s%(variable_name)s;
"""
texte = \
"""\
int e%(variable_name)s[%(rank)d];
"""
formal_argument_list = routine.arguments()
D={}
apv = 0 # have we declared pyarray_value yet?
for a in formal_argument_list:
self.set_variable(a, routine, D)
d = routine.declaration(a)
if d.intent == 'in':
if d.rank():
if not apv:
self.put(" PyArrayObject* pyarray_value;\n")
apv = 1
D['prefix'] = ''
self.put(text, D)
D['prefix'] = 'a'
D['d_type'] = 'PyArrayObject*'
self.put(text, D)
D['rank'] = d.rank()
self.put(texte, D)
if D['c_type'] == 'char*':
raise PyFortError, \
"Character arrays not supported."
else:
D['prefix'] = ''
self.put(text, D)
if D['c_type'] == 'char*':
D['prefix'] = 'n'
D['d_type'] = 'int'
self.put(text, D)
if d.intent == 'valued':
if d.rank():
raise PyFortError, "Initial value cannot be specified for array."
else:
D['prefix'] = ''
self.put(text, D)
if D['c_type'] == 'char*':
D['prefix'] = 'n'
D['d_type'] = 'int'
self.put(text, D)
elif d.intent == 'inout':
if d.rank():
D['prefix'] = ''
self.put(text, D)
D['prefix'] = 'a'
D['d_type'] = 'PyArrayObject*'
self.put(text, D)
D['rank'] = d.rank()
self.put(texte, D)
if D['c_type'] == 'char*':
raise PyFortError, \
"Character arrays not supported."
else:
raise PyFortError, \
"Intent inout must be an array."
elif d.intent == "temporary":
if D['c_type'] == 'char*':
raise PyFortError, \
"Character temporaries not supported."
if d.rank():
if not apv:
self.put(" PyArrayObject* pyarray_value;\n")
apv = 1
D['prefix'] = 'a'
D['d_type'] = 'PyArrayObject*'
self.put(text, D)
D['rank'] = d.rank()
self.put(texte, D)
else:
D['prefix'] = ''
self.put(text, D)
elif d.intent == "out":
if D['c_type'] == 'char*':
raise PyFortError, \
"Character outputs not yet supported."
if d.rank():
if not apv:
self.put(" PyArrayObject* pyarray_value;\n")
apv = 1
D['prefix'] = 'a'
D['d_type'] = 'PyArrayObject*'
self.put(text, D)
D['prefix'] = 'r'
D['d_type'] = 'PyObject*'
self.put(text, D)
D['rank'] = d.rank()
self.put(texte, D)
else:
D['prefix'] = ''
self.put(text, D)
elif d.intent == "hidden":
D['prefix'] = ''
self.put(text, D)
# Check if we need to set pointers for allocatable arrays
allocatable_array_used = 0
for a in formal_argument_list:
d = routine.declaration(a)
if d.rank() > 1 and d.allocatable:
allocatable_array_used = 1
if allocatable_array_used:
text = \
"""\
int ii;
"""
self.put(text)
# Declare the pointer arrays needed for allocatable arrays
text = \
"""\
%(c_type)s%(pointers)s %(prefix)s%(variable_name)s;
"""
for a in formal_argument_list:
d = routine.declaration(a)
if d.rank() > 1 and d.allocatable:
self.set_variable(a, routine, D)
for dimension in range(d.rank()):
D['prefix'] = (dimension+1)*'p' + 'a'
D['pointers'] = (dimension+1)*'*'
self.put(text,D)
def write_initialization(self, routine):
if self.array_result_flag:
text = \
"""\
fortran_result_array = (PyArrayObject*) 0;
fortran_result_array_length = 1;
"""
self.put(text)
text = \
"""\
%(prefix)s%(variable_name)s = (%(d_type)s) 0;
"""
formal_argument_list = routine.arguments()
D={}
for a in formal_argument_list:
d = routine.declaration(a)
if d.rank():
self.set_variable(a, routine, D)
D['prefix'] = 'a'
D['d_type'] = 'PyArrayObject*'
self.put(text, D)
text = \
"""\
keyoption = default_option;
"""
self.put(text)
def write_argument_parsing(self, routine):
"Make the call to PyArg_ParseTuple"
paps=''
D = {}
input_arguments = routine.python_inputs()
for a in input_arguments:
self.set_variable(a, routine, D)
d = routine.declaration(a)
if d.rank():
paps = paps + 'O'
else:
paps = paps + self.compiler.python_code(d)
papt = self.compiler.parser_argument_list (routine)
if papt:
D['list'] = papt + ', &keyoption'
else:
D['list'] = '&keyoption'
D['paps'] = paps
text = \
"""
if(!PyArg_ParseTuple(args, "%(paps)s|i", %(list)s)) {
return NULL;
}
"""
self.put(text, D)
def write_preprocessing(self, routine):
"The part between getting the arguments and making the call."
D={}
D['routine_name'] = routine.routine_name()
# Get the intent in arrays
textin = \
"""\
if (!(a%(variable_name)s = do_array_in ("%(routine_name)s", "%(variable_name)s", %(variable_name)s, %(python_array_type)s))) goto err;
"""
for a in routine.python_inputs():
d = routine.declaration(a)
if d.intent == 'in':
if d.rank():
self.set_variable(a, routine, D)
D['python_array_type'] = self.compiler.python_array_type(d)
self.put(textin, D)
# Get the intent inout arguments
textinout = \
"""\
if (!(a%(variable_name)s = do_array_inout ("%(routine_name)s", "%(variable_name)s", %(variable_name)s, %(python_array_type)s))) goto err;
"""
for a in routine.python_inputs():
d = routine.declaration(a)
if d.intent == 'inout':
if d.rank():
self.set_variable(a, routine, D)
D['python_array_type'] = self.compiler.python_array_type(d)
self.put(textinout, D)
# Set the sizes of valued integers
text = \
"""\
%(variable_name)s = %(value)s;
"""
for a in routine.valued():
self.set_variable(a, routine, D)
d = routine.declaration(a)
s = string.upper(d.value)
f = sizere.match (s)
while f:
v = f.group(2)
replacement = 'get_fortran_dim(a' + string.upper(v)
s = s[0:f.start(1)] + replacement + s[f.end(2):]
f = sizere.match (s)
D['value'] = s
self.put (text, D);
# do size checking on inputs, then transposes if requested.
text = \
"""\
e%(variable_name)s[%(j)d] = %(n)s;
"""
for a in routine.arguments():
d = routine.declaration(a)
if d.rank():
self.set_variable(a, routine, D)
for j in range(d.rank()):
D['j'] = j
D['n'] = string.upper(d.dimension(j))
self.put(text, D);
text = \
"""\
if (!do_size_check ("%(routine_name)s", "%(variable_name)s", a%(variable_name)s, %(rank)s, e%(variable_name)s, keyoption & MIRROR_OPTION)) goto err;
"""
textin = \
"""\
if ((keyoption & TRANSPOSE_OPTION) && (a%(variable_name)s->nd > 1)) {
pyarray_value = a%(variable_name)s;
a%(variable_name)s = transpose_array ("%(routine_name)s", "%(variable_name)s", pyarray_value);
Py_DECREF(pyarray_value);
if(!a%(variable_name)s) goto err;
}
pyarray_value = a%(variable_name)s;
a%(variable_name)s = make_contiguous ("%(routine_name)s", "%(variable_name)s", pyarray_value);
Py_DECREF(pyarray_value);
if(!a%(variable_name)s) goto err;
"""
for a in routine.python_inputs():
d = routine.declaration(a)
if d.rank():
self.set_variable(a, routine, D)
D['rank'] = d.rank()
D['variable'] = D['variable_name']
D['python_array_type'] = self.compiler.python_array_type(d)
if d.intent == 'in':
self.put(text, D)
self.put(textin, D)
if d.intent == 'inout':
self.put(text, D)
# create any desired temporaries and output arrays
textarray = \
"""\
if (!(a%(variable_name)s = do_array_create ("%(routine_name)s", "%(variable_name)s", %(python_array_type)s, %(rank)s, e%(variable_name)s, keyoption&MIRROR_OPTION))) goto err;
"""
for a in routine.temporaries():
self.set_variable(a, routine, D)
d = routine.declaration(a)
if d.rank():
D['rank'] = d.rank()
D['python_array_type'] = self.compiler.python_array_type(d)
self.put(textarray, D)
else:
raise routine.routine_name() + ": Argument " + a + "declared temporary but is not dimensioned."
for a in routine.python_outputs():
self.set_variable(a, routine, D)
d = routine.declaration(a)
if d.rank():
D['rank'] = d.rank()
D['python_array_type'] = self.compiler.python_array_type(d)
self.put(textarray, D)
# Allocate memory for the pointer arrays needed for ansi-style arrays
text = \
"""\
%(prefix)s%(variable_name)s = (%(c_type)s%(pointers)s)malloc((size_t)%(arraysize)ssizeof(%(element)s));
"""
for a in routine.arguments():
d = routine.declaration(a)
if d.rank() > 1 and d.allocatable:
self.set_variable(a, routine, D)
for dimension in range(1,d.rank()):
D['prefix'] = (dimension+1)*'p' + 'a'
D['pointers'] = (dimension+1)*'*'
arraysize = ''
for j in range(d.rank()-dimension):
arraysize = arraysize + string.upper(d.dimension(j))+'*'
D['arraysize'] = arraysize
D['element'] = D['c_type'] + dimension*'*'
self.put(text,D)
# Fill the pointer arrays needed for allocatable arrays
text = \
"""\
pa%(variable_name)s = (%(c_type)s*) (a%(variable_name)s->data);
"""
for a in routine.arguments():
d = routine.declaration(a)
if d.rank() > 1 and d.allocatable:
self.set_variable(a, routine, D)
self.put(text,D)
text = \
"""\
for (ii=0; ii<%(arraysize)s; ii++) %(prefix1)sa%(variable_name)s[ii]=&(%(prefix2)sa%(variable_name)s[ii*%(stride)s]);
"""
for a in routine.arguments():
d = routine.declaration(a)
if d.rank() > 1 and d.allocatable:
self.set_variable(a, routine, D)
for dimension in range(1,d.rank()):
arraysize = string.upper(d.dimension(0))
for j in range(1,d.rank()-dimension):
arraysize = arraysize+'*'+string.upper(d.dimension(j))
D['arraysize'] = arraysize
D['prefix1'] = (dimension+1)*'p'
D['prefix2'] = dimension*'p'
D['stride'] = string.upper(d.dimension(d.rank()-dimension))
self.put(text,D)
def write_fortran_call(self, routine):
self.put(" ")
if routine.is_function():
self.put("fortran_result = ")
self.put(self.fortran_link_name(routine.routine_name()))
self.put("(")
actual_argument_list = self.compiler.actual_argument_list(routine)
self.put(string.join(actual_argument_list, ", \n "))
self.put(");\n")
def write_postprocessing(self, routine):
text = \
"""\
fortran_result_array = (PyArrayObject*) PyArray_FromDims(
1,
&fortran_result_array_length,
%(python_array_type)s
);
if (!fortran_result_array) {
PyErr_SetString(ErrorObject,
"%(routine_name)s: failed creating space for fortran result.");
goto err;
}
*((%(d_type)s *)fortran_result_array->data) = fortran_result;
/* if fortran_result ever gets to be a non-scalar would need work here */
rfortran_result_array = PyArray_Return(fortran_result_array);
"""
if self.array_result_flag:
d = routine.declaration(routine.routine_name())
D = {'result_type': self.compiler.c_type(d)}
self.set_variable (routine.routine_name(), routine, D)
D['python_array_type'] = self.compiler.python_array_type(d)
self.put (text, D)
# We created the outputs at the right size
# so we just have to make them look right to Python
text = \
"""\
r%(variable_name)s = PyArray_Return(a%(variable_name)s);
"""
if self.compiler.executable_name() != 'cc':
text = \
"""\
if(!(keyoption&MIRROR_OPTION)) set_transposed_strides(a%(variable_name)s);
""" + text
output_list = routine.python_outputs()
if routine.is_function():
del output_list[0]
D = {}
for a in output_list:
self.set_variable (a, routine, D)
d = routine.declaration (a)
if d.rank():
self.put (text, D)
def write_return_value_construction(self, routine):
"Make the call to Py_BuildValue"
#note Py_BuildValue works with an empty format string, returns Py_None
paps=''
D = {}
output_list = routine.python_outputs()
if routine.is_function():
del output_list[0]
d = routine.declaration(routine.routine_name())
if self.array_result_flag:
paps = 'O'
else:
paps = self.compiler.python_code(d)
for a in output_list:
self.set_variable(a, routine, D)
d = routine.declaration(a)
ct = self.compiler.c_type(d)
if d.rank():
paps = paps + 'O'
else:
paps = paps + self.compiler.python_code(d)
D['papt'] = self.compiler.build_argument_list(routine, self.array_result_flag)
D['paps'] = paps
text = \
"""
pyfort_result = Py_BuildValue("%(paps)s"%(papt)s);
"""
self.put(text, D)
text = \
"""\
Py_XDECREF(r%(variable_name)s);
"""
if self.array_result_flag:
D['variable_name'] = 'fortran_result_array'
self.put(text, D)
for a in output_list:
d = routine.declaration(a)
self.set_variable(a, routine, D)
if d.rank():
self.put(text, D)
self.put(\
"""\
return pyfort_result;
""")
def write_fortran_argument_cleanup(self, routine, error_flag):
text = \
"""\
Py_XDECREF((PyObject*) a%(name)s);
"""
D={}
if(error_flag):
err_label_needed = 0
for a in routine.arguments():
d = routine.declaration(a)
if d.rank():
err_label_needed = 1
if self.array_result_flag:
err_label_needed = 1
if err_label_needed:
self.put("err:\n")
if self.array_result_flag:
self.put(\
"""\
Py_XDECREF((PyObject*) fortran_result_array);
""")
for a in routine.arguments():
d = routine.declaration(a)
D['name'] = string.upper(a)
if d.rank():
if d.intent != "inout":
self.put(text, D)
self.put(" return NULL;\n")
else:
for a in routine.python_inputs() + routine.temporaries():
d = routine.declaration(a)
D['name'] = string.upper(a)
if d.rank():
if d.intent != "inout":
self.put(text, D)
text = \
"""\
free (%(prefix)s%(variable_name)s);
"""
for a in routine.arguments():
d = routine.declaration(a)
if d.rank() > 1 and d.allocatable:
self.set_variable(a, routine, D)
for dimension in range(1,d.rank()):
D['prefix'] = (dimension+1)*'p' + 'a'
self.put(text,D)
def write_method_finish(self, routine):
"Write the end of the method"
D={'routine_name': routine.routine_name()}
text = \
"""\
}
/* end of wrapper for %(routine_name)s */
"""
self.put(text, D)
# helpers
def put(self, text, dictionary={}):
"Put out the formatted text using the dictionary"
self.module_file.write(text % dictionary)
def fortran_link_name (self, name):
"Return the Fortran link name."
return self.compiler.link_name(name)
|