/**************************************************************************** * * $Id: pl2sl.c,v 1.4 2003/08/24 00:21:31 dburke Exp $ * * pl2sl.c * Conversion routines between Perl and S-Lang data types. * ****************************************************************************/ #include "util.h" #include "pdl.h" #include "pl2sl.h" static void pl2sl_type( SV *item, SLtype item_type, int item_flag ); /* * Usage: * SLtype = pltype( SV *val, int *flag ) * * Aim: * Given a Perl object (as a SV *), return the approproiate * S-Lang type (as a SLtype value) for it. flag is an output * variable - * if 1 then the SLtype should be considered to mean just that, * if 0 then it indicates a "special" meaning * (used by assoc/array types) * * Notes: * Initial version - needs thinking/work * * - probably important to do integer/double/string check in * that order due to Perl's DWIM-ery wrt types * * used by _guess_type in SLang.xs so can't be static */ SLtype pltype( SV *plval, int *flag ) { *flag = 1; /* before we do any checks ensure we have something to check */ if ( !SvOK(plval) ) return SLANG_NULL_TYPE; if ( SvROK(plval) ) { /* * assume that if an object we either know what to do * or it can't be converted */ if ( sv_isobject(plval) ) { if ( sv_derived_from(plval,"Math::Complex") ) return SLANG_COMPLEX_TYPE; if ( sv_derived_from(plval,"DataType_Type") ) return SLANG_DATATYPE_TYPE; if ( sv_derived_from(plval,"Struct_Type") ) return SLANG_STRUCT_TYPE; if ( sv_derived_from(plval,"Assoc_Type") ) return SLANG_ASSOC_TYPE; if ( sv_derived_from(plval,"Array_Type") ) return SLANG_ARRAY_TYPE; /* need to extend the meaning of the flag field */ if ( sv_derived_from(plval,"PDL") ) { *flag = 2; return SLANG_ARRAY_TYPE; } /* * run out of specific types * - indicate this by returning SLANG_UNDEFINED_TYPE * but with a flag of 0 */ if ( sv_derived_from(plval,"Inline::SLang::_Type") ) { *flag = 0; return SLANG_UNDEFINED_TYPE; } } else { SV *ref = SvRV(plval); if ( SvTYPE(ref) == SVt_PVHV ) { *flag = 0; return SLANG_ASSOC_TYPE; } if ( SvTYPE(ref) == SVt_PVAV ) { *flag = 0; return SLANG_ARRAY_TYPE; } } } else { /* not a reference */ if ( SvIOK(plval) ) return SLANG_INT_TYPE; if ( SvNOK(plval) ) return SLANG_DOUBLE_TYPE; if ( SvPOK(plval) ) return SLANG_STRING_TYPE; } croak( "Sent a perl type that can not be converted to S-Lang." ); } /* pltype() */ /* * pl2sl_assoc_intenal( HV *hash ) * - called by pl2sl_assoc() * - this does the actual conversion */ static void pl2sl_assoc_internal( HV *hash ) { I32 nfields, i; /* * loop through the keys in the Perl hash and set the corresponding * value in the S-Lang Assoc_Type array */ nfields = hv_iterinit( hash ); Printf( (" hash ref contains %d fields\n",nfields) ); for ( i = 0; i < nfields; i++ ) { SV *value; char *fieldname; I32 ignore; /* get the next key/value pair from the hash */ value = hv_iternextsv( hash, &fieldname, &ignore ); Printf( (" - field %d/%d name=[%s]\n",i,nfields-1,fieldname) ); /* * push $1 [in case pl2sl() trashes it], the field name, * and then the Perl value (converted to S-Lang) onto the * S-Lang stack * * TODO: [low priority enhancement] * we know the type of the variable we are converting to * so we could save some time by calling the correct part * of pl2sl(). Although not sure about Any_Type arrays * in this scheme. */ (void) SLang_load_string( "$1;" ); UTIL_SLERR( SLang_push_string( fieldname ), "Unable to push a string onto the stack" ); pl2sl( value ); /* * this sort of a call can leak mem prior to S-Lang < 1.4.9 but I think we're * okay with this version. Any mem leaks in the struct code should first * check that S-Lang lib >= 1.4.9 */ (void) SLang_load_string( "$3=(); $2=(); $1=(); $1[$2] = $3;" ); } SL_PUSH_ELEM1_ONTO_STACK(3); return; } /* pl2sl_assoc_internal() */ /* * pl2sl_assoc( SV *item, int item_flag ) */ static void pl2sl_assoc( SV *item, int item_flag ) { HV *hash; if ( item_flag ) { SV *typename; SV *object; Printf( ("*** converting Perl Assoc_Type object to S-Lang\n") ); /* * create the array with the correct type * * TODO: [low priority] * Newz() to create a char * large enough to contain * '$1 = Assoc_Type[%s];', typename * and then SLang_load_string() that */ CALL_METHOD_SCALAR_SV( item, "_private_get_typeof", , typename ); Printf( (" assoc type = [%s]\n", SvPV_nolen(typename)) ); (void) SLang_load_string( SvPV_nolen(typename) ); (void) SLang_load_string( "$2=(); $1 = Assoc_Type [$2];" ); SvREFCNT_dec( typename ); /* * get the hash used to store the actual data */ CALL_METHOD_SCALAR_SV( item, "_private_get_hashref", , object ); object = sv_2mortal( object ); hash = (HV *) SvRV( object ); } else { /* * hash ref: follow Assoc_Type object handling above - we convert * to an 'Assoc_Type [Any_Type];' array since we can't be sure * about the type without looping through all the keys */ Printf( ("*** converting Perl {...} object to S-Lang\n") ); /* create the assoc array in $1 */ (void) SLang_load_string( "$1 = Assoc_Type [Any_Type];" ); /* iterate through the hash, filling in the values */ hash = (HV*) SvRV( item ); // sv_2mortal ??? } /* and delegate all the complicated stuff */ pl2sl_assoc_internal( hash ); } /* pl2sl_assoc() */ /* * pl2sl_struct() */ static void pl2sl_struct( SV *item ) { SV *dstruct; SV *object; HV *hash; I32 nfields, i; Printf( ("*** converting Perl struct to S-Lang\n") ); /* * create a structure in $1 with the correct fields * - once the string has been used we can decrease the * reference count to ensure it is freed */ CALL_METHOD_SCALAR_SV( item, "_define_struct", , dstruct ); Printf( ("struct definition =\n[%s]\n", SvPV_nolen(dstruct)) ); (void) SLang_load_string( SvPV_nolen(dstruct) ); SvREFCNT_dec( dstruct ); /* * get the hash used to store the actual data */ CALL_METHOD_SCALAR_SV( item, "_private_get_hashref", , object ); object = sv_2mortal( object ); hash = (HV *) SvRV( object ); /* * loop through the keys in the Perl hash and set the corresponding * value in the S-Lang struct */ nfields = hv_iterinit( hash ); Printf( (" struct contains %d fields\n",nfields) ); for ( i = 0; i < nfields; i++ ) { SV *value; char *fieldname; I32 ignore; /* get the next key/value pair from the hash */ value = hv_iternextsv( hash, &fieldname, &ignore ); Printf( (" - field %d/%d name=[%s]\n",i,nfields-1,fieldname) ); /* * push $1 [in case pl2sl() trashes it], the field name, * and then the Perl value (converted to S-Lang) onto the * S-Lang stack */ (void) SLang_load_string( "$1;" ); UTIL_SLERR( SLang_push_string( fieldname ), "Unable to push a string onto the stack" ); pl2sl( value ); /* * this sort of a call can leak mem prior to S-Lang < 1.4.9 but I think we're * okay with this version. Any mem leaks in the struct code should first * check that S-Lang lib >= 1.4.9 */ (void) SLang_load_string( "$3=(); $2=(); $1=(); set_struct_field( $1, $2, $3 );" ); } SL_PUSH_ELEM1_ONTO_STACK(3); return; } /* pl2sl_struct() */ /* * pl2sl_array_internal() * must be called with the S-Lang array in $1 * - originally had hard-coded 1/2D routines and a generic * support system for up to 7D data structure. * Have moved to just using the generic system. * The plan is to add support for arrays of particular * types - ie those with a C API - and it's easier if * we only have to code them once. * * Note: * this is being written in such a way as to force users to use * piddles for arrays wherever possible! * */ static void pl2sl_array_internal( AV *array, AV *dims ) { long dimsize[SLARRAY_MAX_DIMS], coord[SLARRAY_MAX_DIMS]; AV *aref[SLARRAY_MAX_DIMS]; SV *set_array_elem_sv; char *set_array_elem_str; SV **dval; long nelem; I32 maxdim, i, j; SLtype sl_type; int sl_flag; maxdim = av_len( dims ); /* count from 0 */ /* * I think S-Lang arrays are limited to <= 7 [SLARRAY_MAX_DIMS] * - left check in in case this changes (the reason why we are * limited to 7 is that we need to use 2 $x (ie temp) vars * for the array and the value, which leaves a max of 7 for * coordinates */ Printf( (" * converting %dD Array_Type array to S_Lang\n",maxdim+1) ); if ( maxdim > 6 ) croak( "Error: unable to convert an array of dimensionality %d\n", maxdim+1 ); /* not a very useful array */ if ( -1 == maxdim ) { SL_PUSH_ELEM1_ONTO_STACK(2); return; } /* * set up arrays for looping through the array */ nelem = 1; for ( i = 0; i <= maxdim; i++ ) { SV **numsv = av_fetch( dims, i, 0 ); long num = SvIV( *numsv ); Printf( (" *** dimension %d has size %d\n",i,num) ); nelem *= num; dimsize[i] = num-1; /* want to start counting at 0 */ coord[i] = 0; if ( 0 == i ) aref[i] = array; else aref[i] = (AV *) SvRV( *av_fetch( aref[i-1], 0, 0 ) ); } /* * this is truly not wonderful: set up the string that * pops the array, coordinates, and data value off the * S-Lang stack and fills in the array element * - *and* I'm too lazy to do this in C! */ Printf( ("Calling Array_Type::_private_get_assign_string(%d)\n",maxdim) ); { int count; dSP; ENTER; SAVETMPS; PUSHMARK(SP); XPUSHs( sv_2mortal(newSViv(maxdim)) ); PUTBACK; count = call_pv( "Array_Type::_private_get_assign_string", G_SCALAR ); SPAGAIN; if ( 1 != count ) croak( "Internal error: unable to call _private_get_assign_string()\n" ); set_array_elem_sv = SvREFCNT_inc( POPs ); PUTBACK; FREETMPS; LEAVE; } set_array_elem_str = SvPV_nolen(set_array_elem_sv); Printf( ("set str = [%s]\n",set_array_elem_str) ); /* * loop i=1 to nelem * - from coord/aref arrays can get the data value from Perl * and set the S-Lang value * - increase coord/aref arrays to point to the next value * [a recursive loop * if last elem of coord array < ndims[last element] * add 1 to it; update aref[last element] * else * reset last element to 0, repeat with previous * coord element [possibly repeat] * update the necessary aref elements * */ dval = av_fetch( aref[maxdim], coord[maxdim], 0 ); sl_type = pltype( *dval, &sl_flag ); for ( i = 1; i < nelem; i++ ) { Printf( (" **** Setting %dD array elem %d coord=[",maxdim+1,i) ); /* * since we are about to call pl2l() we push $1 onto the stack * to protect it. Then we push the coordinates, and then the * current data value */ (void) SLang_load_string( "$1;" ); for( j = 0; j <= maxdim; j++ ) { Printf( (" %d",coord[j]) ); UTIL_SLERR( SLang_push_integer(coord[j]), "Internal error: unable to push onto the stack" ); } Printf( (" ] and coord[maxdim] = %d\n",coord[maxdim]) ); dval = av_fetch( aref[maxdim], coord[maxdim], 0 ); pl2sl_type( *dval, sl_type, sl_flag ); /* now set the value (also resets $1 to be the array) */ (void) SLang_load_string( set_array_elem_str ); /* update the pointer */ if ( coord[maxdim] < dimsize[maxdim] ) coord[maxdim]++; else { Printf( ("+++ start: loop to upate coords/array refs\n") ); /* * loop through each previous coord until we find * one with 'coord[j] < dimsize[j]', increase it * and then reset the 'higher dim' coord/aref values */ j = maxdim - 1; while ( coord[j] == dimsize[j] ) { j--; } Printf( ("++++++++ got to dim #%d with coord=[%d]\n",j,coord[j]) ); coord[j]++; if ( j ) aref[j] = (AV *) SvRV( *av_fetch( aref[j-1], coord[j-1], 0 ) ); j++; while ( j <= maxdim ) { Printf( ("++++++ resetting dim #%d to 0\n",j) ); coord[j] = 0; aref[j] = (AV *) SvRV( *av_fetch( aref[j-1], coord[j-1], 0 ) ); j++; } Printf( ("+++ finished coords/array refs update\n") ); } /* if: coord[maxdim] == dimsize[maxdim] */ } /* for: i=1 .. nelem-1 */ /* handle the last element */ Printf( (" **** Setting %dD array elem %d coord=[",maxdim+1,nelem) ); (void) SLang_load_string( "$1;" ); for( j = 0; j <= maxdim; j++ ) { Printf( (" %d",coord[j]) ); UTIL_SLERR( SLang_push_integer(coord[j]), "Internal error: unable to push onto the stack" ); } Printf( (" ] [[last element]]\n") ); dval = av_fetch( aref[maxdim], coord[maxdim], 0 ); pl2sl_type( *dval, sl_type, sl_flag ); (void) SLang_load_string( set_array_elem_str ); SL_PUSH_ELEM1_ONTO_STACK(maxdim+3); SvREFCNT_dec( set_array_elem_sv ); /* free up mem */ return; } /* pl2sl_array_internal() */ static void pl2sl_array_atype( SV *item ) { SV *arraystr; SV *arrayref, *dimsref; AV *array, *dims; Printf( ("*** converting Perl Array_Type object to S-Lang\n") ); /* * create the array with the correct type & dims in $1 */ CALL_METHOD_SCALAR_SV( item, "_private_define_array", , arraystr ); Printf( (" array definition = [%s]\n", SvPV_nolen(arraystr)) ); (void) SLang_load_string( SvPV_nolen(arraystr) ); SvREFCNT_dec( arraystr ); /* * get the array reference used to store the actual data * and the array dimensions [could do in one call] */ CALL_METHOD_SCALAR_SV( item, "_private_get_arrayref", , arrayref ); arrayref = sv_2mortal( arrayref ); array = (AV *) SvRV( arrayref ); CALL_METHOD_SCALAR_SV( item, "_private_get_dims", , dimsref ); dimsref = sv_2mortal( dimsref ); dims = (AV *) SvRV( dimsref ); /* * and delegate all the complicated stuff, including pushing * the array back onto the S-Lang stack and clearing $1..$n */ pl2sl_array_internal( array, dims ); return; } /* pl2sl_array_atype() */ /* * an array reference * - we have to guess the array dimensions and data type * the current algorithm is LESS THAN OPTIMAL * eg given [ [ 1, 2 ], "foo" ] it should return Any_Type [1] * but it will assume Integer_Type [2] * also something like [ 1, 2.3, "foo" ] is prob best * converted as a String_Type array - this code selects Integer_Type * >>>> will change at some point but not a high priority just now <<<< * * - see Array_Type * */ static void pl2sl_array_aref( SV *item ) { int dimsize[SLARRAY_MAX_DIMS]; AV *array = (AV*) SvRV(item); AV *temp; AV *dims; SLang_Array_Type *sl_dims; SLtype dtype; int i, ndims, nelem, dtype_flag; for ( i = 0; i < SLARRAY_MAX_DIMS; i++ ) dimsize[i] = 0; array = (AV*) SvRV(item); Printf( ("*** converting Perl array ref to ") ); /* * what is the data type and array size? * ALGORITHM SHOULD BE MORE CLEVERERER */ ndims = 0; dimsize[ndims] = av_len(array) + 1; nelem = dimsize[ndims]; temp = array; Printf( ("[%d]",dimsize[ndims]) ); ndims++; fixme( "think dimension handling is wrong" ); while ( 1 ) { SV *val = *av_fetch( temp, 0, 0 ); if ( SvROK(val) && SVt_PVAV == SvTYPE(SvRV(val)) ) { if ( SLARRAY_MAX_DIMS == ndims ) croak( "Error: Max array dimension for S-Lang is %d.\n", SLARRAY_MAX_DIMS ); temp = (AV *) SvRV(val); dimsize[ndims] = av_len(temp) + 1; nelem *= dimsize[ndims]; Printf( ("[%d]", dimsize[ndims]) ); ndims++; } else { /* found a non-array element: guess its data type */ dtype = pltype( val, &dtype_flag ); break; } } /* * create a Perl array containing the array dimensions * - I think I need to re-work pl2sl_array_internal()! */ dims = (AV *) sv_2mortal( (SV *) newAV() ); av_extend( dims, ndims ); for ( i=0; i_store[]) * * Perhaps we need to add a routine to the _Type class to indicate * this condition? */ Printf( ("*** converting Perl _Type object to S-Lang\n") ); pl2sl( SvRV(item) ); (void) SLang_load_string( "$1 = (); _inline->_push_data( $1 );" ); _clean_slang_vars(1); } /* switch: item_type */ } /* pl2sl_type() */ /* * convert perl variables to S-Lang variables * * note: we automatically push each variable onto the S-Lang stack * - this will probably turn out to be a bad idea; for instance it * means it can't be called recursively when converting * array/associative arrays. * * - we croak for those types we do not recognise [in pltype] */ void pl2sl( SV *item ) { SLtype item_type; int item_flag; item_type = pltype( item, &item_flag ); pl2sl_type( item, item_type, item_flag ); } /* end of pl2sl.c */