import sys """This program generates multiple c source code files It handles the tedium of all the repetitive code dealing with types and particularly combinations of types with types and types with functions. $Id: codegenerator.py,v 1.4 2002/03/05 20:59:35 jaytmiller Exp $ """ #*********************************************************************# # # # DATA SECTION # # # #*********************************************************************# # Of course, the warning below does not apply to this file! _HEADER = \ ''' /* W W AAA RRRR N N III N N GGG !!! ** W W A A R R NN N I NN N G G !!! ** W W W AAAAA RRRR N N N I N N N G ! ** W W W A A R R N NN I N NN G GG ** W W A A R R N N III N N GGG !!! ** ** WARNING: This file is program generated by codegenerator.py. ** ** DO NOT EDIT THIS FILE! Any changes made to this file will be lost! */ #include #include #include "libnumarray.h" static PyObject *_Error; ''' _TYPE_SEPARATOR = \ ''' /****************** *******************/ ''' AS_PyVALUE_TEMPLATE = \ ''' static PyObject *asPyValue(void *data) { return Py_BuildValue(, *(( *) data)); } SELF_CHECKED_CFUNC_DESCR(asPyValue, CFUNC_AS_PY_VALUE); ''' # From_PyVALUE uses a (relatively) backwards convention for return status. # 0 --> failure. non-zero --> success. This is actually my preferred # style as well, but *not* what the rest of numarray does. # Most ufuncs use the convention -1 for error and 0 for success. FROM_PyVALUE_TEMPLATE = \ ''' static int fromPyValue(PyObject *value, void *dataptr) { long lval; double dval; *data = ( *) dataptr; if (!PyNumber_Check(value)) return 0; else { if (PyLong_Check(value)) { lval = PyLong_AsLong(value); *data = (lval); } else if (PyInt_Check(value)) { lval = PyInt_AsLong(value); *data = (lval); } else if (PyFloat_Check(value)) { dval = PyFloat_AsDouble(value); *data = (dval); } else if (PyComplex_Check(value)) { return 0; } return 1; } } SELF_CHECKED_CFUNC_DESCR(fromPyValue, CFUNC_FROM_PY_VALUE); ''' CONVERSION_TEMPLATE = \ ''' static int as(long niter, long ninargs, long noutargs, void **buffers, long *bsizes) { long i; *tin = ( *) buffers[0]; *tout = ( *) buffers[1]; for (i=0; i (*tin++); } return 0; } UFUNC_DESCR2(as, sizeof(), sizeof()); ''' SORT_HEADER = _HEADER + \ ''' #define swap(i, j) { temp=v[i]; v[i]=v[j]; v[j]=temp; } #define wswap(i, j) { temp=v[i]; v[i]=v[j]; v[j]=temp; \ wtemp=w[i]; w[i]=w[j]; w[j]=wtemp; } ''' SORT_TEMPLATE = \ ''' void sort0( *v, int left, int right) { temp; int i, last; int diff = right - left; if (diff <= 0) return; i = (left+right)/2; swap(left, i); last = left; for(i=left+1; i<=right; i++) if (v[i] < v[left]) { ++ last; swap(last, i); } swap(left, last); /* Do short partition first to limit recursion storage to O(log(n)) */ if ((last-1-left) < (right-last+1)) { sort0(v, left, last-1); sort0(v, last+1, right); } else { sort0(v, last+1, right); sort0(v, left, last-1); } } static int sort(long niter, long ninargs, long noutargs, void **buffers, long *bsizes) { sort0( ( *) buffers[0], 0, niter-1); return 0; } UFUNC_DESCR1(sort, sizeof()); void asort0( *v, Int32 *w, int left, int right) { temp; Int32 wtemp; int i, last; int diff = right - left; if (diff <= 0) return; i = (left+right)/2; wswap(left, i); last = left; for(i=left+1; i<=right; i++) if (v[i] < v[left]) { ++ last; wswap(last, i); } wswap(left, last); /* Do short partition first to limit recursion storage to O(log(n)) */ if ((last-1-left) < (right-last+1)) { asort0(v, w, left, last-1); asort0(v, w, last+1, right); } else { asort0(v, w, last+1, right); asort0(v, w, left, last-1); } } static int asort(long niter, long ninargs, long noutargs, void **buffers, long *bsizes) { asort0( ( *) buffers[0], (Int32 *) buffers[1], 0, niter-1); return 0; } CFUNC_DESCR(asort, CFUNC_UFUNC, CHECK_ALIGN, 0, 2, sizeof(), sizeof(Int32), 0, 0, 0, 0); static Int32 search(Int32 na, *a, v) { Int32 i = 0; *b; while(na > 10) { Int32 mid = na>>1; if (v <= a[mid]) na = mid; else { i += mid + 1; a += mid + 1; na = mid - 1 + (na&1); } } b = a; while((*b < v) && na--) ++ b; return i+(b-a); } static int searchsorted(int niter, int ninargs, int noutargs, void **buffers, long *bsizes) { Int32 nbins; *bins; *values; Int32 *indices; int i; if (NA_checkOneCBuffer("searchsorted", 1, buffers[0], bsizes[0], sizeof(Int32))) return -1; nbins = *(Int32 *) buffers[0]; if (NA_checkOneCBuffer("searchsorted", nbins, buffers[1], bsizes[1], sizeof())) return -1; bins = ( *) buffers[1]; if (NA_checkOneCBuffer("searchsorted", niter, buffers[2], bsizes[2], sizeof())) return -1; values = ( *) buffers[2]; if (NA_checkOneCBuffer("searchsorted", niter, buffers[3], bsizes[3], sizeof(Int32))) return -1; indices = (Int32 *) buffers[3]; if (NA_checkIo("searchsorted", 3, 1, ninargs, noutargs)) return -1; for(i=0; isearch(nbins, bins, values[i]); return 0; } SELF_CHECKED_CFUNC_DESCR(searchsorted, CFUNC_UFUNC); ''' BYTES_HEADER = _HEADER + \ ''' /* These macros do byteswapping from an input buffer to an output buffer. The output buffer may or may not be the same as the input buffer, hence a temporary copy of the input is required. */ /* unaligned copy functions */ #define copy1(i, o) *(o) = *(i); #define copy2(i, o) copy1(i, o); copy1(i+1, o+1); #define copy4(i, o) copy2(i, o); copy2(i+2, o+2); #define copy8(i, o) copy4(i, o); copy4(i+4, o+4); #define swap1(i, o) copy1(i, o); #define swap2(i, o) swap1(i, o+1); swap1(i+1, o); #define swap4(i, o) swap2(i, o+2); swap2(i+2, o); #define swap8(i, o) swap4(i, o+4); swap4(i+4, o); /* aligned copy functions */ #define acopy1(i, o) ((Int8 *)o)[0] = ((Int8 *)i)[0]; #define acopy2(i, o) ((Int16 *)o)[0] = ((Int16 *)i)[0]; #define acopy4(i, o) ((Int32 *)o)[0] = ((Int32 *)i)[0]; #define acopy8(i, o) ((Float64 *)o)[0] = ((Float64 *)i)[0]; #define acopyN(i, o) memcpy(o, i, N); #define MIN(x,y) (((x)<=(y)) ? (x) : (y)) #define MAX(x,y) (((x)>=(y)) ? (x) : (y)) /* The following is used to copy nbytes of data for each element. ** ** As such it can be used to align any sort of data provided the ** ** output pointers used are aligned */ static int copyNbytes(long dim, long nbytes, long *niters, void *input, long inboffset, long *inbstrides, void *output, long outboffset, long *outbstrides) { long i, j; char *tin = (char *) input + inboffset; char *tout = (char *) output + outboffset; if (dim == 0) { for (i=0; i=0; i--) { otemp = PySequence_GetItem(nitersObj, i); niters[nniters-i-1] = PyInt_AsLong(otemp); Py_DECREF(otemp); otemp = PySequence_GetItem(inbstridesObj, i); inbstrides[nniters-i-1] = PyInt_AsLong(otemp); Py_DECREF(otemp); } outbstrides[0] = nbytes; for (i=1; i= max) \ i -= max; static int chooseNbytes(long niter, long ninargs, long noutargs, void **buffers, long *bsizes) { Int32 i, cMode, maxP, N, *selector; char **population, *output; int outi = ninargs + noutargs - 1; if (NA_checkIo("chooseNbytes", 2, 1, MIN(ninargs,2), noutargs)) return -1; if (NA_checkOneCBuffer("chooseNbytes", 2, buffers[0], bsizes[0], sizeof(Int32))) return -1; else { cMode = ((Int32 *) buffers[0])[0]; N = ((Int32 *) buffers[0])[1]; } if (NA_checkOneCBuffer("chooseNbytes", niter, buffers[1], bsizes[1], sizeof(Int32))) return -1; else selector = (Int32 *) buffers[1]; if (ninargs-2 == 0) return 0; else maxP = ninargs-2; for(i=2; i= maxP) j = maxP-1; memcpy( &output[i*N], &population[j][i*N], N); } break; } return 0; } SELF_CHECKED_CFUNC_DESCR(chooseNbytes, CFUNC_UFUNC); static int takeNbytes(long niter, long ninargs, long noutargs, void **buffers, long *bsizes) { Int32 i, cMode, N, *scatteredstrides, *scatteredshape, **indices; char *gathered, *scattered; int nindices = ninargs-4, outi = ninargs+noutargs-1; if (NA_checkIo("takeNbytes", 4, 1, MIN(ninargs, 4), noutargs)) return -1; if (nindices == 0) return 0; if (NA_checkOneCBuffer("takeNbytes", 2, buffers[0], bsizes[0], sizeof(Int32))) return -1; else { cMode = ((Int32 *) buffers[0])[0]; N = ((Int32 *) buffers[0])[1]; } if (NA_checkOneCBuffer("takeNbytes", niter*N, buffers[1], bsizes[1], 1)) return -1; else scattered = (char *) buffers[1]; if (NA_checkOneCBuffer("takeNbytes", nindices, buffers[2], bsizes[2], sizeof(Int32))) return -1; else { scatteredstrides = (Int32 *) buffers[2]; } if (NA_checkOneCBuffer("takeNbytes", nindices, buffers[3], bsizes[3], sizeof(Int32))) return -1; else { scatteredshape = (Int32 *) buffers[3]; } for(i=4; i= scatteredshape[j]) k = scatteredshape[j]-1; index += scatteredstrides[j]*k; } memcpy( &gathered[i*N], scattered+index, N); } break; } return 0; } SELF_CHECKED_CFUNC_DESCR(takeNbytes, CFUNC_UFUNC); static int putNbytes(long niter, long ninargs, long noutargs, void **buffers, long *bsizes) { Int32 i, cMode, N, *scatteredstrides, *scatteredshape, **indices; char *gathered, *scattered; int nindices = ninargs-4, outi = ninargs+noutargs-1; if (nindices == 0) return 0; if (NA_checkIo("putNbytes", 4, 1, MIN(ninargs, 4), noutargs)) return -1; if (NA_checkOneCBuffer("putNbytes", 2, buffers[0], bsizes[0], sizeof(Int32))) return -1; else { cMode = ((Int32 *) buffers[0])[0]; N = ((Int32 *) buffers[0])[1]; } if (NA_checkOneCBuffer("putNbytes", niter*N, buffers[1], bsizes[1], 1)) return -1; else gathered = (char *) buffers[1]; if (NA_checkOneCBuffer("putNbytes", nindices, buffers[2], bsizes[2], sizeof(Int32))) return -1; else { scatteredstrides = (Int32 *) buffers[2]; } if (NA_checkOneCBuffer("putNbytes", nindices, buffers[3], bsizes[3], sizeof(Int32))) return -1; else { scatteredshape = (Int32 *) buffers[3]; } for(i=4; i= scatteredshape[j]) k = scatteredshape[j]-1; index += scatteredstrides[j]*k; } memcpy( scattered+index, &gathered[i*N], N); } break; } return 0; } SELF_CHECKED_CFUNC_DESCR(putNbytes, CFUNC_UFUNC); ''' BYTES_TEMPLATE = \ ''' /******************************************* * * * These copy data to a contiguous buffer. * * They do not handle non-aligned data. * * Offsets and Strides are in byte units * * * *******************************************/ static int copybytes(long dim, long dummy, long *niters, void *input, long inboffset, long *inbstrides, void *output, long outboffset, long *outbstrides) { long i; char *tin = (char *) input + inboffset; char *tout = (char *) output + outboffset; if (dim == 0) { for (i=0; i(tin, tout); tin += inbstrides[dim]; tout += outbstrides[dim]; } } else { for (i=0; ibytes(dim-1, dummy, niters, input, inboffset + i*inbstrides[dim], inbstrides, output, outboffset + i*outbstrides[dim], outbstrides); } } return 0; } STRIDING_DESCR2(copybytes, CHECK_ALIGN, , ); static int alignbytes(long dim, long dummy, long *niters, void *input, long inboffset, long *inbstrides, void *output, long outboffset, long *outbstrides) { return copyNbytes(dim, , niters, input, inboffset, inbstrides, output, outboffset, outbstrides); } STRIDING_DESCR2(alignbytes, !CHECK_ALIGN, , ); /******* byteswap *****/ static int byteswapbytes(long dim, long dummy, long *niters, void *input, long inboffset, long *inbstrides, void *output, long outboffset, long *outbstrides) { long i; char *tin = (char *) input + inboffset; char *tout = (char *) output + outboffset; if (dim == 0) { for (i=0; i]; copy(tin, t); swap(t, tout); tin += inbstrides[dim]; tout += outbstrides[dim]; } } else { for (i=0; ibytes(dim-1, dummy, niters, input, inboffset + i*inbstrides[dim], inbstrides, output, outboffset + i*outbstrides[dim], outbstrides); } } return 0; } STRIDING_DESCR2(byteswapbytes, !CHECK_ALIGN, , ); static int choosebytes(long niter, long ninargs, long noutargs, void **buffers, long *bsizes) { Int32 i, cMode, maxP, N, *selector; **population, *output; int outi = ninargs + noutargs - 1; if (NA_checkIo("choosebytes", 2, 1, MIN(ninargs,2), noutargs)) return -1; if (NA_checkOneCBuffer("choosebytes", 2, buffers[0], bsizes[0], sizeof(Int32))) return -1; else { cMode = ((Int32 *) buffers[0])[0]; N = ((Int32 *) buffers[0])[1]; } if (NA_checkOneCBuffer("choosebytes", niter, buffers[1], bsizes[1], sizeof(Int32))) return -1; else selector = (Int32 *) buffers[1]; if (ninargs-2 == 0) return 0; else maxP = ninargs-2; for(i=2; ibytes", niter, buffers[i], bsizes[i], )) return -1; population = ( **) &buffers[2]; if (NA_checkOneCBuffer("choosebytes", niter, buffers[outi], bsizes[outi], )) return -1; else output = ( *) buffers[outi]; if (maxP == 0) return 0; switch(cMode) { case WRAPPED: for(i=0; i= maxP) j = maxP-1; output[i] = population[j][i]; } break; } return 0; } SELF_CHECKED_CFUNC_DESCR(choosebytes, CFUNC_UFUNC); ''' _METHODS_DEF = \ ''' static PyMethodDef _Methods[] = { \t{NULL, NULL} /* Sentinel */ }; ''' ADDMETHOD_TEMPLATE = \ '''\t{ "%s", %s, METH_VARARGS}, ''' _TAIL = \ ''' /* platform independent*/ #ifdef MS_WIN32 __declspec(dllexport) #endif void init_(void) { PyObject *m, *d; m = Py_InitModule("_", _Methods); d = PyModule_GetDict(m); _Error = PyErr_NewException("_.error", NULL, NULL); PyDict_SetItemString(d, "error", _Error); import_libnumarray(); PyDict_SetItemString(d, "functionDict", init_funcDict()); } ''' #*********************************************************************# # data for _ufuncmodule.c # #*********************************************************************# UFUNC_HEADER = \ ''' #include #include #include #include "libnumarray.h" #ifdef MS_WIN32 #pragma warning(once : 4244) #endif #define logical_and(arg1, arg2) (arg1 != 0) & (arg2 != 0) #define logical_or(arg1, arg2) (arg1 != 0) | (arg2 != 0) #define logical_xor(arg1, arg2) ((arg1 != 0) ^ (arg2 != 0)) & 1 #define ufmaximum(arg1, arg2) (((temp1=arg1) > (temp2=arg2)) ? temp1 : temp2) #define ufminimum(arg1, arg2) (((temp1=arg1) < (temp2=arg2)) ? temp1 : temp2) double numarray_zero = 0.0; static double raiseDivByZero(void) { return 1.0/numarray_zero; } static double num_log(double x) { if (x == 0.0) return raiseDivByZero(); else return log(x); } static double num_log10(double x) { if (x == 0.0) return raiseDivByZero(); else return log10(x); } static double num_pow(double x, double y) { int z = (int) y; if ((x < 0.0) && (y != z)) return raiseDivByZero(); else return pow(x, y); } static PyObject *_Error; /* The following routine is used in the event of a detected integer * ** divide by zero so that a floating divide by zero is generated. * ** This is done since numarray uses the floating point exception * ** sticky bits to detect errors. The last bit is an attempt to * ** prevent optimization of the divide by zero away, the input value * ** should always be 0 * */ int int_dividebyzero_error(long value, long unused) { double dummy; dummy = 1./numarray_zero; if (dummy) /* to prevent optimizer from eliminating expression */ return 0; else return 1; } /* Likewise for Integer overflows */ Int32 int_overflow_error(Float64 value) { double dummy; dummy = pow(1.e10, fabs(value/2)); if (dummy) /* to prevent optimizer from eliminating expression */ return (int) value; else return 1; } ''' UFUNC_UNARY_TEMPLATE = \ ''' static int __vector(long niter, long ninargs, long noutargs, void **buffers, long *bsizes) { long i; *tin = ( *) buffers[0]; *tout = ( *) buffers[1]; for (i=0; i } return 0; } UFUNC_DESCR2(__vector, sizeof(), sizeof()); ''' UFUNC_BINARY_TEMPLATE = \ ''' static int __vector_vector(long niter, long ninargs, long noutargs, void **buffers, long *bsizes) { long i; *tin1 = ( *) buffers[0]; *tin2 = ( *) buffers[1]; *tout = ( *) buffers[2]; for (i=0; i } return 0; } UFUNC_DESCR3(__vector_vector, sizeof(), sizeof(), sizeof()); static int __vector_scalar(long niter, long ninargs, long noutargs, void **buffers, long *bsizes) { long i; *tin1 = ( *) buffers[0]; tscalar = *( *) buffers[1]; *tout = ( *) buffers[2]; for (i=0; i } return 0; } UFUNC_DESCR3vs(__vector_scalar, sizeof(), sizeof(), sizeof()); static int __scalar_vector(long niter, long ninargs, long noutargs, void **buffers, long *bsizes) { long i; tscalar = *( *) buffers[0]; *tin2 = ( *) buffers[1]; *tout = ( *) buffers[2]; for (i=0; i } return 0; } UFUNC_DESCR3sv(__scalar_vector, sizeof(), sizeof(), sizeof()); static int __reduce(long dim, long dummy, long *niters, void *input, long inboffset, long *inbstrides, void *output, long outboffset, long *outbstrides) { long i; *tin = ( *) ((char *) input + inboffset); *tout = ( *) ((char *) output + outboffset); net; if (dim == 0) { net = *tout; for (i=1; i *) ((char *) tin + inbstrides[dim]); } *tout = net; } else { for (i=0; i__reduce(dim-1, dummy, niters, input, inboffset + i*inbstrides[dim], inbstrides, output, outboffset + i*outbstrides[dim], outbstrides); } } return 0; } STRIDING_DESCR2(__reduce, CHECK_ALIGN, sizeof(), sizeof()); static int __accumulate( long dim, long dummy, long *niters, void *input, long inboffset, long *inbstrides, void *output, long outboffset, long *outbstrides) { long i; *tin = ( *) ((char *) input + inboffset); *tout = ( *) ((char *) output + outboffset); lastval; if (dim == 0) { for (i=1; i *) ((char *) tin + inbstrides[dim]); tout = ( *) ((char *) tout + outbstrides[dim]); } } else { for (i=0; i__accumulate(dim-1, dummy, niters, input, inboffset + i*inbstrides[dim], inbstrides, output, outboffset + i*outbstrides[dim], outbstrides); } } return 0; } STRIDING_DESCR2(__accumulate, CHECK_ALIGN, sizeof(), sizeof()); ''' #*********************************************************************# # data for filling function table # #*********************************************************************# _CFUNCDICT_HEADER = \ ''' static PyObject *init_funcDict(void) { PyObject *dict; dict = PyDict_New(); ''' _ADDCFUNC_TEMPLATE = \ ''' NA_add_cfunc(dict, "%s", (void *) &%s_descr); ''' _CFUNCDICT_TAIL = \ ''' return dict; } ''' # The following templates are the most complex. There are a number of # different forms of templates, for unary and binary functions, and then # different forms for the computation line (operator, function plus # special versions for integer multiply and divide for overflow and # divide by zero checking. More may be necesary in the future. These # are defined here since most are used multiple times in the ufunc # definition table. # td stands for 'template dict' function_td = { "operator1":"*tout++ = (*tin++);", "operator_vv":"*tout++ = (*tin1++, *tin2++);", "operator_vs":"*tout++ = (*tin1++, tscalar);", "operator_sv":"*tout++ = (tscalar, *tin2++);", "operator_reduce":"net = (net, *tin);", "operator_accumulate":"*tout = (lastval ,*tin);" } operator_td = { "operator1":"*tout++ = *tin++;", "operator_vv":"*tout++ = *tin1++ *tin2++;", "operator_vs":"*tout++ = *tin1++ tscalar;", "operator_sv":"*tout++ = tscalar *tin2++;", "operator_reduce":"net = net *tin;", "operator_accumulate":"*tout = lastval *tin;" } int_divide_td = { "operator_vv":"*tout++ = ((*tin2==0) ? int_dividebyzero_error(*tin2++, *tin1++) : *tin1++ *tin2++);", "operator_vs":"*tout++ = ((tscalar==0) ? int_dividebyzero_error(tscalar, *tin1++) : *tin1++ tscalar);", "operator_sv":"*tout++ = ((*tin2==0) ? int_dividebyzero_error(*tin2++, 0) : tscalar *tin2++);", "operator_reduce":"net = ((*tin==0) ? int_dividebyzero_error(*tin, 0) : net *tin);", "operator_accumulate":"*tout = ((*tin==0) ? int_dividebyzero_error(*tin, 0) : lastval *tin);" } int8_check = \ """if (temp > 127) temp = int_overflow_error(127.); if (temp < -128) temp = int_overflow_error(-128.);""" uint8_check = \ """if (temp > 255) temp = int_overflow_error(255.);""" int16_check = \ """if (temp > 32767) temp = int_overflow_error(32767.); if (temp < -32768) temp = int_overflow_error(-32768);""" uint16_check = \ """if (temp > 65535) temp = int_overflow_error(65535.);""" int32_check = \ """if (temp > 2147483647) temp = (Float64) int_overflow_error(2147483647.); if (temp < (-2147483648.)) temp = (Float64) int_overflow_error(-2147483648.);""" # A template used below to generate multiple templates! (since they # are all so similar. These are used by make_int_template_dict just below. intX_td = { "operator_vv": """temp = (() *tin1++) % (() *tin2++); *tout++ = () temp;""", "operator_vs": """temp = (() *tin1++) % (() tscalar); *tout++ = () temp;""", "operator_sv": """temp = (() tscalar) % (() *tin2++); *tout++ = () temp;""", "operator_reduce": """temp = (() net) % (() *tin); net = () temp;""", "operator_accumulate": """temp = (() lastval) % (() *tin); *tout = () temp;""" } def make_int_template_dict(uptype, sizestr, checkstr): td = {} for key in intX_td.keys(): td[key] = intX_td[key] % \ {"type": sizestr,"checkstr": checkstr, "uptype":uptype} return td #*********************************************************************# # data lists for type conversion # #*********************************************************************# # These are used by the code to generate dictionaries with the keys # defined in tdictfields and the corresponding values in typeconfig # list items. There will be one dictionary for each element in # typeconfig. It is done this way to make the table more readable # and easier to edit. typeconfig = [ ["Bool", "(Bool) isNonZERO", "PY_BOOL_CHAR", ""], ["Int8", "(Int8)", "PY_INT8_CHAR", ""], ["Int16", "(Int16)", "PY_INT16_CHAR", ""], ["Int32", "(Int32)", "PY_INT32_CHAR", ""], ["UInt8", "(UInt8)", "PY_UINT8_CHAR", "(Int16)"], ["UInt16", "(UInt16)", "PY_UINT16_CHAR", "(Int32)"], ["Float32","(Float32)", "PY_FLOAT32_CHAR",""], ["Float64","(Float64)", "PY_FLOAT64_CHAR",""] ] class ConvParams: def __init__(self, typename, typecast, pybuildchar, pycast): self.typename = typename self.typecast = typecast self.pybuildchar = pybuildchar self.pycast = pycast bytesconfig = [ ["1", "Int8"], ["2", "Int16"], ["4", "Int32"], ["8", "Float64"] ]; class BytesParams: def __init__(self, size, type): self.size = size self.typename = type #*********************************************************************# # data lists for ufunc generation # #*********************************************************************# # Some implicit rules: # Bool never returned by math functions # Bool always returned by comparison functions # Math operators (+-*/**) generally return all types, double math functions # only return float types, special purpose functions (floor, ceil, etc) # only return one kind (int or float) # Define various forms for signatures. New ufuncs may require new # signatures lists. These are used in the ufunc definition table # repeatedly. generic_operator_sigs = [ ('Bool', 'Int8'), ('Int8', 'Int8'), ('Int16', 'Int16'), ('Int32', 'Int32'), ('UInt8', 'UInt8'), ('UInt16', 'UInt16'), ('Float32', 'Float32'), ('Float64', 'Float64') ] generic_mathfunction_sigs = [ ('Bool', 'Float32'), ('Int8', 'Float32'), ('Int16', 'Float32'), ('Int32', 'Float32'), ('UInt8', 'Float32'), ('UInt16', 'Float32'), ('Float32', 'Float32'), ('Float64', 'Float64') ] generic_comparisonfunction_sigs = [ ('Bool', 'Bool'), ('Int8', 'Bool'), ('Int16', 'Bool'), ('Int32', 'Bool'), ('UInt8', 'Bool'), ('UInt16', 'Bool'), ('Float32', 'Bool'), ('Float64', 'Bool') ] generic_int_sigs = [ ('Bool', 'Bool'), ('Int8', 'Int8'), ('Int16', 'Int16'), ('Int32', 'Int32'), ('UInt8', 'UInt8'), ('UInt16', 'UInt16') ] generic_float_sigs = [ ('Float32', 'Float32'), ('Float64', 'Float64') ] opt_mult_decl = "Int32 temp;" opt_mult32_decl = "Float64 temp;" opt_minmax_decl = " temp1, temp2;" # ============================================================================ # IMPORTANT: no <>-sugared strings below this point import re # The following functions "sugar" the templates by doing regular expression # based string substitution. Each instance of is repaced by # %(identifier)s in the call to "autoclutter" below. def clean(f): """replaces instances of %(var)s with """ s = open(f).read() return re.sub(r"%\((\w+)\)s", r"<\1>", s) def dirty(s): """replaces instances of with %(var)s""" return re.sub(r"<(\w+)>", r"%(\1)s", s) def autoclutter(d): for k in d.keys(): if type(d[k]) == type(""): # handle strings d[k] = dirty(d[k]) elif type(d[k]) == type(d): # handle dicts autoclutter(d[k]) # translate --> %(var)s in templates seen *so far* autoclutter(globals()) # ============================================================================ # Use function to define all variants of integer multiply. multiply_int8_td = make_int_template_dict("Int32", "Int8", int8_check) multiply_uint8_td = make_int_template_dict("Int32", "UInt8", uint8_check) multiply_int16_td = make_int_template_dict("Int32", "Int16", int16_check) multiply_uint16_td = make_int_template_dict("Int32", "UInt16", uint16_check) multiply_int32_td = make_int_template_dict("Float64", "Int32", int32_check) # These are used by the code to generate dictionaries with the keys # defined in ufdictfields and the corresponding values in ufuncconfigs # list items. There will be one dictionary for each element in # ufuncconfigs. It is done this way to make the table more readable # and easier to edit. # Multiple entries for function names are permitted as long as the # signatures have no overlaps. # pyname ins outs form sigs optional_decls ufuncconfigs = [ ["minus", 1, 1, (operator_td, "-"), generic_operator_sigs, ""], ["add", 2, 1, (operator_td, "+"), generic_operator_sigs,""], ["subtract", 2, 1, (operator_td, "-"), generic_operator_sigs, ""], ["multiply", 2, 1, (multiply_int8_td, "*"), [('Int8', 'Int8')], opt_mult_decl], ["multiply", 2, 1, (multiply_uint8_td, "*"), [('UInt8', 'UInt8')], opt_mult_decl], ["multiply", 2, 1, (multiply_int16_td, "*"), [('Int16', 'Int16')], opt_mult_decl], ["multiply", 2, 1, (multiply_uint16_td, "*"), [('UInt16', 'UInt16')], opt_mult_decl], ["multiply", 2, 1, (multiply_int32_td, "*"), [('Int32', 'Int32')], opt_mult32_decl], ["multiply", 2, 1, (operator_td, "*"), generic_float_sigs, ""], ["divide", 2, 1, (int_divide_td,"/"), generic_int_sigs, ""], ["divide", 2, 1, (operator_td, "/"), generic_float_sigs, ""], ["remainder",2, 1, (int_divide_td, " % "), generic_int_sigs, ""], ["remainder",2, 1, (function_td, "fmod"), generic_float_sigs, ""], ["power", 2, 1, (function_td, "num_pow"), generic_operator_sigs, ""], ["abs", 1, 1, (function_td, "fabs"), generic_operator_sigs, ""], ["sin", 1, 1, (function_td, "sin"), generic_mathfunction_sigs, ""], ["cos", 1, 1, (function_td, "cos"), generic_mathfunction_sigs, ""], ["tan", 1, 1, (function_td, "tan"), generic_mathfunction_sigs, ""], ["arcsin", 1, 1, (function_td, "asin"), generic_mathfunction_sigs, ""], ["arccos", 1, 1, (function_td, "acos"), generic_mathfunction_sigs, ""], ["arctan", 1, 1, (function_td, "atan"), generic_mathfunction_sigs, ""], ["arctan2",2, 1, (function_td, "atan2"), generic_mathfunction_sigs, ""], ["log", 1, 1, (function_td, "num_log"), generic_mathfunction_sigs, ""], ["log10", 1, 1, (function_td, "num_log10"),generic_mathfunction_sigs, ""], ["exp", 1, 1, (function_td, "exp"), generic_mathfunction_sigs, ""], ["sinh", 1, 1, (function_td, "sinh"), generic_mathfunction_sigs, ""], ["cosh", 1, 1, (function_td, "cosh"), generic_mathfunction_sigs, ""], ["tanh", 1, 1, (function_td, "tanh"), generic_mathfunction_sigs, ""], ["sqrt", 1, 1, (function_td, "sqrt"), generic_mathfunction_sigs, ""], ["equal", 2, 1, (operator_td, "=="), generic_comparisonfunction_sigs, ""], ["not_equal", 2, 1, (operator_td, "!="), generic_comparisonfunction_sigs, ""], ["greater", 2, 1, (operator_td, ">"), generic_comparisonfunction_sigs, ""], ["greater_equal",2, 1, (operator_td, ">="), generic_comparisonfunction_sigs, ""], ["less", 2, 1, (operator_td, "<"), generic_comparisonfunction_sigs, ""], ["less_equal", 2, 1, (operator_td, "<="), generic_comparisonfunction_sigs, ""], ["logical_and", 2, 1, (function_td, "logical_and"), generic_comparisonfunction_sigs, ""], ["logical_or", 2, 1, (function_td, "logical_or"), generic_comparisonfunction_sigs, ""], ["logical_xor", 2, 1, (function_td, "logical_xor"), generic_comparisonfunction_sigs, ""], ["logical_not", 1, 1, (operator_td, "!"), generic_comparisonfunction_sigs, ""], ["bitwise_and", 2, 1, (operator_td, "&"), generic_int_sigs, ""], ["bitwise_or", 2, 1, (operator_td, "|"), generic_int_sigs, ""], ["bitwise_xor", 2, 1, (operator_td, "^"), generic_int_sigs, ""], ["bitwise_not", 1, 1, (operator_td, "~"), generic_int_sigs, ""], ["rshift", 2, 1, (operator_td, ">>"), generic_int_sigs, ""], ["lshift", 2, 1, (operator_td, "<<"), generic_int_sigs, ""], ["floor", 1, 1, (function_td, ""), generic_int_sigs, ""], ["floor", 1, 1, (function_td, "floor"), generic_float_sigs, ""], ["ceil", 1, 1, (function_td, ""), generic_int_sigs, ""], ["ceil", 1, 1, (function_td, "ceil"), generic_float_sigs, ""], ["maximum", 2, 1, (function_td, "ufmaximum"), generic_operator_sigs, opt_minmax_decl], ["minimum", 2, 1, (function_td, "ufminimum"), generic_operator_sigs, opt_minmax_decl] ] class UFuncParams: def __init__(self, pyname, ninputs, noutputs, form, sigs, opt_declarations): self.pyname = pyname self.ninputs = ninputs self.noutputs = noutputs self.form = form self.sigs = sigs self.optional_declarations = opt_declarations #*********************************************************************# # # # PROGRAM SECTION # # # #*********************************************************************# """ The classes below assemble the various elements defined in the previous part of the module into the final C modules. Each subclass of CodeGenerator works basically the same way: It is constructed by supplying optional module unique header, tail, and function separator. When called, a CodeGenerator builds up two lists of strings by calling its "bodygenerator()" method. Looping over a parameter table, the bodygenerator builds: 1. A code list containing the instantiated header and emitted function bodies. This is the bulk of the code. 2. A function list containing the C struct initializers which are used to register the function bodies with python. These are necessarily in a seperate textual segment so that they may be joined in an array and looped over at initialization time. In general, templates are instantiated by formatting them either using a CodeGenerator instance dict, or a Params instance dict. After generating its two lists, a CodeGenerator combines them into the complete C code for a python module and writes the code out to the specified file. CodeGenerator subclasses customize behavior by overriding the "bodygenerator" and "addcfunc" methods. Each "bodygenerator" tends to be tailored to the parameter table used to specify the cfuncs for the module. Several of the codegenerators (cconv, ufunc, bytes) are supported by a "Params" class, instances of which act as table records. As the body generator loops over the parameter table, it creates a Params instance from each record. The Params instance dictionary then provides a source of keyword values to instantiate the code template. See ConvParams, ByteParams, and UfuncParams. Each is essentially a struct. In general, template instantiation is performed by string substitution using the python %(identifier)s syntax. The %(indentifer)s syntax permits a string to be formatted using a dictionary to supply identifier values. """ class CodeGenerator: """Generates source code using supplied functions and code templates""" def __init__(self, header=_HEADER, tail=_TAIL, separator=_TYPE_SEPARATOR): self.header = header self.tail = tail self.separator = separator self.module_methods = "" def __call__(self, file=None, Type=None): self.codelist = [ self.header % self.__dict__] self.funclist = [ _CFUNCDICT_HEADER ] if Type is None: self.bodygenerator() else: self.bodygenerator(Type) self.funclist.append(_CFUNCDICT_TAIL) self.codelist.append(_METHODS_DEF % self.__dict__) self.codelist.extend(self.funclist) self.codelist.append(self.tail % self.__dict__) sourcecode = "".join(self.codelist) if file: f = open(file,"w") f.write(sourcecode) f.close() else: return sourcecode def addcfunc(self, name, key=None): if key is None: key = name self.funclist.append(_ADDCFUNC_TEMPLATE % (key, name)) def addmethod(self, name, key=None): if key is None: key = name self.module_methods += (ADDMETHOD_TEMPLATE % (key, name)) class ConvCodeGenerator(CodeGenerator): def __init__(self, *components): CodeGenerator.__init__(self, *components) self.module = "conv" def addcfunc(self, type, name): CodeGenerator.addcfunc(self, type+name, key=repr((type, name))) def bodygenerator(self): """Generates the repetitive sections of code for conversions""" # Iterate over the "from" datatype. for cfg1 in typeconfig: t1 = apply(ConvParams, cfg1) self.codelist.append((self.separator + AS_PyVALUE_TEMPLATE + FROM_PyVALUE_TEMPLATE) % t1.__dict__) self.addcfunc(t1.typename, "asPyValue") self.addcfunc(t1.typename, "fromPyValue") # Iterate over the "to" datatype for cfg2 in typeconfig: t2 = apply(ConvParams, cfg2) if cfg1 == cfg2: continue t1.desttypename = t2.typename t1.desttypecast = t2.typecast self.codelist.append(CONVERSION_TEMPLATE % t1.__dict__) typetup = repr((t1.typename, t2.typename)) name = t1.typename + "as" + t2.typename self.funclist.append(_ADDCFUNC_TEMPLATE % (typetup, name)) class SortCodeGenerator(CodeGenerator): def __init__(self, *components): CodeGenerator.__init__(self, *components) self.module = "sort" def addcfunc(self, type, name): CodeGenerator.addcfunc(self, type+name, key=repr((type, name))) def bodygenerator(self): for cfg in typeconfig: t = apply(ConvParams, cfg) self.codelist.append((self.separator + SORT_TEMPLATE) % t.__dict__) self.addcfunc(t.typename, "sort") self.addcfunc(t.typename, "asort") self.addcfunc(t.typename, "searchsorted") class BytesCodeGenerator(CodeGenerator): def __init__(self, *components): CodeGenerator.__init__(self, *components) self.module = "bytes" def bodygenerator(self): for cfg in bytesconfig: t = apply(BytesParams, cfg) self.codelist.append((self.separator + BYTES_TEMPLATE) % t.__dict__) tail = t.size+"bytes" self.addcfunc("copy"+ tail) self.addcfunc("byteswap"+tail) self.addcfunc("align"+tail) self.addcfunc("choose"+tail) self.addcfunc("copyNbytes") self.addcfunc("chooseNbytes") self.addcfunc("putNbytes") self.addcfunc("takeNbytes") self.addmethod("copyToString") class UFuncCodeGenerator(CodeGenerator): def __init__(self, *components): CodeGenerator.__init__(self, *components) def bodygenerator(self, Type): """Generates the repetitive sections of code.""" self.module = "ufunc" + Type # Loop over ufunc table for cfg in ufuncconfigs: ufo = apply(UFuncParams, cfg) self.codelist.append(self.separator % ufo.pyname) ufo.opt_decls = ufo.optional_declarations # Select appropriate base template form. if ufo.ninputs == 1: template = UFUNC_UNARY_TEMPLATE vslist = ['vector'] elif ufo.ninputs == 2: template = UFUNC_BINARY_TEMPLATE vslist = ['vector_vector','vector_scalar','scalar_vector', 'reduce', 'accumulate'] else: raise ValueError("Can't handle ufuncs with > 2 inputs") # select appropriate computation form (operator vs function) # opdict = ufo.form[0].copy() ufo.operator = ufo.form[1] # for operator1, operator_sv, operator_vs, operator_vv...: # Substitute actual operator/function for for key in ufo.form[0].keys(): ufo.__dict__[key] = ufo.form[0][key] % ufo.__dict__ # Loop over signatures for given ufunc for intypename, outtypename in ufo.sigs: if intypename != Type: continue # Substitute for input and output types in template ufo.intypename = intypename ufo.outtypename = outtypename # Expand opt_decls in terms of ufo vars ufo.optional_declarations = ufo.opt_decls % ufo.__dict__ # Expand template in terms of ufo vars self.codelist.append(template % ufo.__dict__) # for each form, add for form in vslist: if form in ["accumulate","reduce"]: sig = ((intypename,),(outtypename,)) else: sig = (ufo.ninputs*(intypename,), ufo.noutputs*(outtypename,)) self.addcfunc(name=ufo.pyname+"_"+intypename+"_"+form, key=repr((ufo.pyname, form, sig))) #*********************************************************************# generate_conv_code = ConvCodeGenerator() generate_sort_code = SortCodeGenerator(SORT_HEADER) generate_bytes_code = BytesCodeGenerator(BYTES_HEADER) generate_ufunc_code = UFuncCodeGenerator(UFUNC_HEADER, _TAIL, "/********************* %s *********************/\n") def generate(Name, Type=None): if Type is None: s = "generate_%s_code('Src/_%smodule.c')" % (Name, Name) else: s = "generate_%s_code('Src/_%smodule.c', '%s')" % \ (Name, Name+Type, Type) eval(s) if __name__ == "__main__": generate("conv") generate("sort") generate("bytes") for Type in typeconfig: generate("ufunc", Type[0])