alexgutteridge-rsruby 0.5
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +11 -0
- data/License.txt +504 -0
- data/Manifest.txt +39 -0
- data/README.txt +96 -0
- data/Rakefile.rb +153 -0
- data/examples/arrayfields.rb +52 -0
- data/examples/bioc.rb +35 -0
- data/examples/dataframe.rb +35 -0
- data/examples/erobj.rb +30 -0
- data/ext/Converters.c +667 -0
- data/ext/Converters.h +77 -0
- data/ext/R_eval.c +144 -0
- data/ext/R_eval.h +40 -0
- data/ext/extconf.rb +13 -0
- data/ext/robj.c +205 -0
- data/ext/rsruby.c +182 -0
- data/ext/rsruby.h +87 -0
- data/lib/rsruby.rb +302 -0
- data/lib/rsruby/dataframe.rb +107 -0
- data/lib/rsruby/erobj.rb +105 -0
- data/lib/rsruby/robj.rb +67 -0
- data/test/table.txt +4 -0
- data/test/tc_array.rb +59 -0
- data/test/tc_boolean.rb +30 -0
- data/test/tc_cleanup.rb +22 -0
- data/test/tc_eval.rb +21 -0
- data/test/tc_extensions.rb +43 -0
- data/test/tc_init.rb +11 -0
- data/test/tc_io.rb +57 -0
- data/test/tc_library.rb +20 -0
- data/test/tc_matrix.rb +23 -0
- data/test/tc_modes.rb +264 -0
- data/test/tc_robj.rb +84 -0
- data/test/tc_sigint.rb +10 -0
- data/test/tc_to_r.rb +145 -0
- data/test/tc_to_ruby.rb +153 -0
- data/test/tc_util.rb +19 -0
- data/test/tc_vars.rb +28 -0
- data/test/test_all.rb +22 -0
- metadata +103 -0
data/ext/Converters.c
ADDED
@@ -0,0 +1,667 @@
|
|
1
|
+
/*
|
2
|
+
* == Author
|
3
|
+
* Alex Gutteridge
|
4
|
+
*
|
5
|
+
* == Copyright
|
6
|
+
*Copyright (C) 2006 Alex Gutteridge
|
7
|
+
*
|
8
|
+
* The Original Code is the RPy python module.
|
9
|
+
*
|
10
|
+
* The Initial Developer of the Original Code is Walter Moreira.
|
11
|
+
* Portions created by the Initial Developer are Copyright (C) 2002
|
12
|
+
* the Initial Developer. All Rights Reserved.
|
13
|
+
*
|
14
|
+
* Contributor(s):
|
15
|
+
* Gregory R. Warnes <greg@warnes.net> (RPy Maintainer)
|
16
|
+
*
|
17
|
+
*This library is free software; you can redistribute it and/or
|
18
|
+
*modify it under the terms of the GNU Lesser General Public
|
19
|
+
*License as published by the Free Software Foundation; either
|
20
|
+
*version 2.1 of the License, or (at your option) any later version.
|
21
|
+
*
|
22
|
+
*This library is distributed in the hope that it will be useful,
|
23
|
+
*but WITHOUT ANY WARRANTY; without even the implied warranty of
|
24
|
+
*MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
25
|
+
*Lesser General Public License for more details.
|
26
|
+
*
|
27
|
+
*You should have received a copy of the GNU Lesser General Public
|
28
|
+
*License along with this library; if not, write to the Free Software
|
29
|
+
*Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
30
|
+
*/
|
31
|
+
|
32
|
+
#include <rsruby.h>
|
33
|
+
#include "Converters.h"
|
34
|
+
|
35
|
+
// ************** Converters from Ruby to R *********//
|
36
|
+
|
37
|
+
|
38
|
+
SEXP ruby_to_R(VALUE obj)
|
39
|
+
{
|
40
|
+
SEXP robj;
|
41
|
+
VALUE str;
|
42
|
+
char buf [100];
|
43
|
+
|
44
|
+
//Return nil if object is nil
|
45
|
+
if (obj == Qnil) {
|
46
|
+
return R_NilValue;
|
47
|
+
}
|
48
|
+
|
49
|
+
//If object has 'as_r' then call it and use
|
50
|
+
//returned value subsequently
|
51
|
+
if (rb_respond_to(obj, rb_intern("as_r"))){
|
52
|
+
obj = rb_funcall(obj,rb_intern("as_r"),0);
|
53
|
+
if (!obj)
|
54
|
+
return NULL;
|
55
|
+
}
|
56
|
+
|
57
|
+
if (Robj_Check(obj))
|
58
|
+
{
|
59
|
+
Data_Get_Struct(obj, struct SEXPREC, robj);
|
60
|
+
PROTECT(robj);
|
61
|
+
}
|
62
|
+
else if (obj == Qtrue || obj == Qfalse)
|
63
|
+
{
|
64
|
+
PROTECT(robj = NEW_LOGICAL(1));
|
65
|
+
if (obj == Qtrue){
|
66
|
+
LOGICAL_DATA(robj)[0] = TRUE;
|
67
|
+
} else {
|
68
|
+
LOGICAL_DATA(robj)[0] = FALSE;
|
69
|
+
}
|
70
|
+
|
71
|
+
}
|
72
|
+
else if (TYPE(obj) == T_FIXNUM ||
|
73
|
+
TYPE(obj) == T_BIGNUM)
|
74
|
+
{
|
75
|
+
PROTECT(robj = NEW_INTEGER(1));
|
76
|
+
INTEGER_DATA(robj)[0] = NUM2LONG(obj);
|
77
|
+
}
|
78
|
+
else if (TYPE(obj) == T_FLOAT)
|
79
|
+
{
|
80
|
+
PROTECT(robj = NEW_NUMERIC(1));
|
81
|
+
NUMERIC_DATA(robj)[0] = NUM2DBL(obj);
|
82
|
+
}
|
83
|
+
else if (RubyComplex_Check(obj))
|
84
|
+
{
|
85
|
+
PROTECT(robj = NEW_COMPLEX(1));
|
86
|
+
COMPLEX_DATA(robj)[0].r = NUM2DBL(rb_funcall(obj,rb_intern("real"),0));
|
87
|
+
COMPLEX_DATA(robj)[0].i = NUM2DBL(rb_funcall(obj,rb_intern("image"),0));
|
88
|
+
}
|
89
|
+
else if (!NIL_P(rb_check_string_type(obj)))
|
90
|
+
{
|
91
|
+
PROTECT(robj = NEW_STRING(1));
|
92
|
+
SET_STRING_ELT(robj, 0, COPY_TO_USER_STRING(RSTRING(obj)->ptr));
|
93
|
+
}
|
94
|
+
else if (!NIL_P(rb_check_array_type(obj)))
|
95
|
+
{
|
96
|
+
PROTECT(robj = array_to_R(obj));
|
97
|
+
}
|
98
|
+
else if (TYPE(obj) == T_HASH)
|
99
|
+
{
|
100
|
+
PROTECT(robj = hash_to_R(obj));
|
101
|
+
}
|
102
|
+
else
|
103
|
+
{
|
104
|
+
str = rb_funcall(obj,rb_intern("inspect"),0);
|
105
|
+
str = rb_funcall(str,rb_intern("slice"),2,INT2NUM(0),INT2NUM(60));
|
106
|
+
sprintf(buf,"Unsupported object '%s' passed to R.\n",RSTRING(str)->ptr);
|
107
|
+
rb_raise(rb_eArgError,buf);
|
108
|
+
PROTECT(robj = NULL); /* Protected to avoid stack inbalance */
|
109
|
+
}
|
110
|
+
|
111
|
+
UNPROTECT(1);
|
112
|
+
return robj;
|
113
|
+
}
|
114
|
+
|
115
|
+
/* Make a R list or vector from a Ruby array */
|
116
|
+
SEXP array_to_R(VALUE obj)
|
117
|
+
{
|
118
|
+
VALUE it;
|
119
|
+
SEXP robj, rit;
|
120
|
+
int i, state;
|
121
|
+
|
122
|
+
/* This matrix defines what mode a vector should take given what
|
123
|
+
it already contains and a new item
|
124
|
+
|
125
|
+
E.g. Row 0 indicates that if we've seen an any, the vector will
|
126
|
+
always remain an any. Row 3 indicates that if we've seen a
|
127
|
+
float, then seeing an boolean, integer, or float will preserve
|
128
|
+
the vector as a float vector, while seeing a string or an Robj will
|
129
|
+
convert it into an any vector.
|
130
|
+
*/
|
131
|
+
int fsm[7][7] = {
|
132
|
+
{0, 0, 0, 0, 0, 0, 0}, // any
|
133
|
+
{0, 1, 2, 3, 4, 0, 0}, // bool
|
134
|
+
{0, 2, 2, 3, 4, 0, 0}, // int
|
135
|
+
{0, 3, 3, 3, 4, 0, 0}, // float
|
136
|
+
{0, 4, 4, 4, 4, 0, 0}, // complex
|
137
|
+
{0, 0, 0, 0, 0, 5, 0}, // string
|
138
|
+
{0, 0, 0, 0, 0, 0, 6} // RObj
|
139
|
+
};
|
140
|
+
|
141
|
+
//Probably unnessecary but just in case
|
142
|
+
obj = rb_check_array_type(obj);
|
143
|
+
|
144
|
+
if (RARRAY(obj)->len == 0)
|
145
|
+
return R_NilValue;
|
146
|
+
|
147
|
+
PROTECT(robj = NEW_LIST(RARRAY(obj)->len));
|
148
|
+
|
149
|
+
state = -1;
|
150
|
+
for (i=0; i<RARRAY(obj)->len; i++) {
|
151
|
+
|
152
|
+
it = rb_ary_entry(obj, i);
|
153
|
+
|
154
|
+
if (state < 0)
|
155
|
+
state = type_to_int(it);
|
156
|
+
else
|
157
|
+
state = fsm[state][type_to_int(it)];
|
158
|
+
|
159
|
+
if (!(rit = ruby_to_R(it)))
|
160
|
+
goto exception;
|
161
|
+
|
162
|
+
SET_VECTOR_ELT(robj, i, rit);
|
163
|
+
}
|
164
|
+
|
165
|
+
switch(state)
|
166
|
+
{
|
167
|
+
case INT_T:
|
168
|
+
robj = AS_INTEGER(robj);
|
169
|
+
break;
|
170
|
+
case BOOL_T:
|
171
|
+
robj = AS_LOGICAL(robj);
|
172
|
+
break;
|
173
|
+
case FLOAT_T:
|
174
|
+
robj = AS_NUMERIC(robj);
|
175
|
+
break;
|
176
|
+
case COMPLEX_T:
|
177
|
+
robj = AS_COMPLEX(robj);
|
178
|
+
break;
|
179
|
+
case STRING_T:
|
180
|
+
robj = AS_CHARACTER(robj);
|
181
|
+
break;
|
182
|
+
default:;
|
183
|
+
/* Otherwise, it's either an ANY_T or ROBJ_T - we want ANY */
|
184
|
+
}
|
185
|
+
|
186
|
+
UNPROTECT(1);
|
187
|
+
return robj;
|
188
|
+
|
189
|
+
exception:
|
190
|
+
UNPROTECT(1);
|
191
|
+
rb_raise(rb_eArgError,"Error converting Array to R\n");
|
192
|
+
return NULL;
|
193
|
+
}
|
194
|
+
|
195
|
+
/* Make a R named list or vector from a Ruby Hash */
|
196
|
+
SEXP
|
197
|
+
hash_to_R(VALUE obj)
|
198
|
+
{
|
199
|
+
VALUE keys, values;
|
200
|
+
SEXP robj, names;
|
201
|
+
|
202
|
+
//TODO - Baffling. Not sure what's wrong with these functions?
|
203
|
+
//rb_hash_keys(proc_table);
|
204
|
+
//rb_hash_values(proc_table);
|
205
|
+
//rb_hash_size(proc_table);
|
206
|
+
//compiles, but complains they are undefined symbols when run...
|
207
|
+
|
208
|
+
if (FIX2INT(rb_funcall(obj,rb_intern("size"),0)) == 0)
|
209
|
+
return R_NilValue;
|
210
|
+
|
211
|
+
/* If 'keys' succeed and 'values' fails this leaks */
|
212
|
+
if (!(keys = rb_funcall(obj,rb_intern("keys"),0)))
|
213
|
+
return NULL;
|
214
|
+
if (!(values = rb_funcall(obj,rb_intern("values"),0)))
|
215
|
+
return NULL;
|
216
|
+
|
217
|
+
if (!(robj = array_to_R(values)))
|
218
|
+
goto fail;
|
219
|
+
if (!(names = array_to_R(keys)))
|
220
|
+
goto fail;
|
221
|
+
|
222
|
+
PROTECT(robj);
|
223
|
+
SET_NAMES(robj, names);
|
224
|
+
UNPROTECT(1);
|
225
|
+
|
226
|
+
return robj;
|
227
|
+
|
228
|
+
fail:
|
229
|
+
return NULL;
|
230
|
+
}
|
231
|
+
|
232
|
+
int
|
233
|
+
type_to_int(VALUE obj)
|
234
|
+
{
|
235
|
+
if (obj == Qtrue || obj == Qfalse)
|
236
|
+
return BOOL_T;
|
237
|
+
else if (TYPE(obj) == T_FIXNUM ||
|
238
|
+
TYPE(obj) == T_BIGNUM)
|
239
|
+
return INT_T;
|
240
|
+
else if (TYPE(obj) == T_FLOAT)
|
241
|
+
return FLOAT_T;
|
242
|
+
else if (RubyComplex_Check(obj))
|
243
|
+
return COMPLEX_T;
|
244
|
+
//NB (TODO): This line means that objects are coerced into
|
245
|
+
//String form if possible rather than leaving them as RObj
|
246
|
+
else if (!NIL_P(rb_check_string_type(obj)))
|
247
|
+
return STRING_T;
|
248
|
+
else if (Robj_Check(obj))
|
249
|
+
return ROBJ_T;
|
250
|
+
else
|
251
|
+
return ANY_T;
|
252
|
+
}
|
253
|
+
|
254
|
+
// ************** Converters from R to Ruby *********//
|
255
|
+
|
256
|
+
VALUE to_ruby_with_mode(SEXP robj, int mode)
|
257
|
+
{
|
258
|
+
VALUE obj;
|
259
|
+
int i;
|
260
|
+
|
261
|
+
switch (mode)
|
262
|
+
{
|
263
|
+
case PROC_CONVERSION:
|
264
|
+
i = to_ruby_proc(robj, &obj);
|
265
|
+
if (i<0) return Qnil;
|
266
|
+
if (i==1) break;
|
267
|
+
case CLASS_CONVERSION:
|
268
|
+
i = to_ruby_class(robj, &obj);
|
269
|
+
if (i<0) return Qnil;
|
270
|
+
if (i==1) break;
|
271
|
+
case BASIC_CONVERSION:
|
272
|
+
i = to_ruby_basic(robj, &obj);
|
273
|
+
if (i<0) return Qnil;
|
274
|
+
if (i==1) break;
|
275
|
+
case VECTOR_CONVERSION:
|
276
|
+
i = to_ruby_vector(robj, &obj, mode=VECTOR_CONVERSION);
|
277
|
+
if (i<0) return Qnil;
|
278
|
+
if (i==1) break;
|
279
|
+
default:
|
280
|
+
obj = Data_Wrap_Struct(rb_const_get(rb_cObject,
|
281
|
+
rb_intern("RObj")), 0, 0, robj);
|
282
|
+
rb_iv_set(obj,"@conversion",INT2FIX(TOP_MODE));
|
283
|
+
rb_iv_set(obj,"@wrap",Qfalse);
|
284
|
+
}
|
285
|
+
|
286
|
+
return obj;
|
287
|
+
}
|
288
|
+
|
289
|
+
/* Convert an R object to a 'basic' Ruby object (mode 2) */
|
290
|
+
/* NOTE: R vectors of length 1 will yield a Ruby scalar */
|
291
|
+
int
|
292
|
+
to_ruby_basic(SEXP robj, VALUE *obj)
|
293
|
+
{
|
294
|
+
int status;
|
295
|
+
VALUE tmp;
|
296
|
+
|
297
|
+
status = to_ruby_vector(robj, &tmp, BASIC_CONVERSION);
|
298
|
+
|
299
|
+
if(status==1 && TYPE(tmp) == T_ARRAY && RARRAY(tmp)->len == 1)
|
300
|
+
{
|
301
|
+
*obj = rb_ary_entry(tmp, 0);
|
302
|
+
}
|
303
|
+
else
|
304
|
+
*obj = tmp;
|
305
|
+
|
306
|
+
return status;
|
307
|
+
}
|
308
|
+
|
309
|
+
|
310
|
+
/* Convert an R object to a 'vector' Ruby object (mode 1) */
|
311
|
+
/* NOTE: R vectors of length 1 will yield a Ruby array of length 1*/
|
312
|
+
int
|
313
|
+
to_ruby_vector(SEXP robj, VALUE *obj, int mode)
|
314
|
+
{
|
315
|
+
VALUE it, tmp;
|
316
|
+
VALUE params[2];
|
317
|
+
SEXP names, dim;
|
318
|
+
int len, *integers, i, type;
|
319
|
+
char *strings, *thislevel;
|
320
|
+
double *reals;
|
321
|
+
Rcomplex *complexes;
|
322
|
+
|
323
|
+
if (!robj)
|
324
|
+
return -1; /* error */
|
325
|
+
|
326
|
+
if (robj == R_NilValue) {
|
327
|
+
*obj = Qnil;
|
328
|
+
return 1; /* succeed */
|
329
|
+
}
|
330
|
+
|
331
|
+
len = GET_LENGTH(robj);
|
332
|
+
tmp = rb_ary_new2(len);
|
333
|
+
type = TYPEOF(robj);
|
334
|
+
|
335
|
+
for (i=0; i<len; i++) {
|
336
|
+
switch (type)
|
337
|
+
{
|
338
|
+
case LGLSXP:
|
339
|
+
integers = INTEGER(robj);
|
340
|
+
if(integers[i]==NA_INTEGER) /* watch out for NA's */
|
341
|
+
{
|
342
|
+
if (!(it = INT2NUM(integers[i])))
|
343
|
+
return -1;
|
344
|
+
}
|
345
|
+
//TODO - not sure of the conversion here.
|
346
|
+
else if (integers[i] != 0){
|
347
|
+
it = Qtrue;
|
348
|
+
} else if (integers[i] == 0){
|
349
|
+
it = Qfalse;
|
350
|
+
} else {
|
351
|
+
return -1;
|
352
|
+
}
|
353
|
+
break;
|
354
|
+
case INTSXP:
|
355
|
+
integers = INTEGER(robj);
|
356
|
+
if(isFactor(robj)) {
|
357
|
+
/* Watch for NA's! */
|
358
|
+
if(integers[i]==NA_INTEGER)
|
359
|
+
it = rb_str_new2(CHAR(NA_STRING));
|
360
|
+
else
|
361
|
+
{
|
362
|
+
thislevel = CHAR(STRING_ELT(GET_LEVELS(robj), integers[i]-1));
|
363
|
+
if (!(it = rb_str_new2(thislevel)))
|
364
|
+
return -1;
|
365
|
+
}
|
366
|
+
}
|
367
|
+
else {
|
368
|
+
if (!(it = LONG2NUM(integers[i])))
|
369
|
+
return -1;
|
370
|
+
}
|
371
|
+
break;
|
372
|
+
case REALSXP:
|
373
|
+
reals = REAL(robj);
|
374
|
+
if (!(it = rb_float_new(reals[i])))
|
375
|
+
return -1;
|
376
|
+
break;
|
377
|
+
case CPLXSXP:
|
378
|
+
complexes = COMPLEX(robj);
|
379
|
+
|
380
|
+
params[0] = rb_float_new(complexes[i].r);
|
381
|
+
params[1] = rb_float_new(complexes[i].i);
|
382
|
+
|
383
|
+
if (!(it = rb_class_new_instance(2, params, rb_const_get(rb_cObject, rb_intern("Complex")))))
|
384
|
+
|
385
|
+
return -1;
|
386
|
+
break;
|
387
|
+
case STRSXP:
|
388
|
+
if(STRING_ELT(robj, i)==R_NaString)
|
389
|
+
it = rb_str_new2(CHAR(NA_STRING));
|
390
|
+
else
|
391
|
+
{
|
392
|
+
strings = CHAR(STRING_ELT(robj, i));
|
393
|
+
if (!(it = rb_str_new2(strings)))
|
394
|
+
return -1;
|
395
|
+
}
|
396
|
+
break;
|
397
|
+
case LISTSXP:
|
398
|
+
if (!(it = to_ruby_with_mode(elt(robj, i), mode)))
|
399
|
+
return -1;
|
400
|
+
break;
|
401
|
+
case VECSXP:
|
402
|
+
if (!(it = to_ruby_with_mode(VECTOR_ELT(robj, i), mode)))
|
403
|
+
return -1;
|
404
|
+
break;
|
405
|
+
default:
|
406
|
+
return 0; /* failed */
|
407
|
+
}
|
408
|
+
rb_ary_store(tmp, i, it);
|
409
|
+
}
|
410
|
+
|
411
|
+
dim = GET_DIM(robj);
|
412
|
+
if (dim != R_NilValue) {
|
413
|
+
len = GET_LENGTH(dim);
|
414
|
+
*obj = to_ruby_array(tmp, INTEGER(dim), len);
|
415
|
+
return 1;
|
416
|
+
}
|
417
|
+
|
418
|
+
names = GET_NAMES(robj);
|
419
|
+
if (names == R_NilValue)
|
420
|
+
*obj = tmp;
|
421
|
+
else {
|
422
|
+
*obj = to_ruby_hash(tmp, names);
|
423
|
+
}
|
424
|
+
|
425
|
+
return 1;
|
426
|
+
}
|
427
|
+
|
428
|
+
/* Search a conversion procedure from the proc table */
|
429
|
+
int
|
430
|
+
from_proc_table(SEXP robj, VALUE *fun)
|
431
|
+
{
|
432
|
+
VALUE proc_table, procs, proc, funs, res, obj, mode;
|
433
|
+
VALUE args[2];
|
434
|
+
int i, l, error;
|
435
|
+
|
436
|
+
proc_table = rb_iv_get(RSRUBY,"@proc_table");
|
437
|
+
|
438
|
+
proc = Qnil;
|
439
|
+
|
440
|
+
//TODO - Baffling. Not sure what's wrong with these functions?
|
441
|
+
//procs = rb_hash_keys(proc_table);
|
442
|
+
//funs = rb_hash_values(proc_table);
|
443
|
+
//l = FIX2INT(rb_hash_size(proc_table));
|
444
|
+
|
445
|
+
procs = rb_funcall(proc_table,rb_intern("keys"),0);
|
446
|
+
funs = rb_funcall(proc_table,rb_intern("values"),0);
|
447
|
+
l = FIX2INT(rb_funcall(proc_table,rb_intern("size"),0));
|
448
|
+
|
449
|
+
obj = Data_Wrap_Struct(rb_const_get(rb_cObject,
|
450
|
+
rb_intern("RObj")), 0, 0, robj);
|
451
|
+
rb_iv_set(obj,"@conversion",INT2FIX(TOP_MODE));
|
452
|
+
rb_iv_set(obj,"@wrap",Qfalse);
|
453
|
+
|
454
|
+
error = 0;
|
455
|
+
for (i=0; i<l; i++) {
|
456
|
+
proc = rb_ary_entry(procs, i);
|
457
|
+
|
458
|
+
mode = rb_iv_get(RSRUBY,"@default_mode");
|
459
|
+
rb_iv_set(RSRUBY,
|
460
|
+
"@default_mode",
|
461
|
+
INT2FIX(BASIC_CONVERSION));
|
462
|
+
|
463
|
+
//New safe code
|
464
|
+
args[0] = proc;
|
465
|
+
args[1] = obj;
|
466
|
+
res = rb_ensure(call_proc,(VALUE) &args[0],reset_mode,mode);
|
467
|
+
|
468
|
+
if (RTEST(res)) {
|
469
|
+
*fun = rb_ary_entry(funs, i);
|
470
|
+
break;
|
471
|
+
}
|
472
|
+
}
|
473
|
+
|
474
|
+
return error;
|
475
|
+
}
|
476
|
+
|
477
|
+
VALUE call_proc(VALUE data){
|
478
|
+
VALUE *args = (VALUE *) data;
|
479
|
+
return rb_funcall(args[0], rb_intern("call"), 1, args[1]);
|
480
|
+
}
|
481
|
+
|
482
|
+
VALUE reset_mode(VALUE mode){
|
483
|
+
|
484
|
+
rb_iv_set(RSRUBY,
|
485
|
+
"@default_mode",
|
486
|
+
mode);
|
487
|
+
|
488
|
+
return Qnil;
|
489
|
+
|
490
|
+
}
|
491
|
+
|
492
|
+
int
|
493
|
+
to_ruby_proc(SEXP robj, VALUE *obj)
|
494
|
+
{
|
495
|
+
VALUE fun=Qnil, tmp, mode;
|
496
|
+
VALUE args[2];
|
497
|
+
int i;
|
498
|
+
|
499
|
+
//Find function from proc table. integer is returned
|
500
|
+
//to indicate success/failure
|
501
|
+
|
502
|
+
i = from_proc_table(robj, &fun);
|
503
|
+
|
504
|
+
if (i < 0)
|
505
|
+
return -1; /* an error occurred */
|
506
|
+
|
507
|
+
if (fun==Qnil)
|
508
|
+
return 0; /* conversion failed */
|
509
|
+
|
510
|
+
//Create new object based on robj and call the function
|
511
|
+
//found above with it as argument
|
512
|
+
tmp = Data_Wrap_Struct(rb_const_get(rb_cObject,
|
513
|
+
rb_intern("RObj")), 0, 0, robj);
|
514
|
+
rb_iv_set(tmp,"@conversion",INT2FIX(TOP_MODE));
|
515
|
+
rb_iv_set(tmp,"@wrap",Qfalse);
|
516
|
+
|
517
|
+
//Again set conversion mode to basic to prevent recursion
|
518
|
+
mode = rb_iv_get(RSRUBY,"@default_mode");
|
519
|
+
rb_iv_set(RSRUBY, "@default_mode", INT2FIX(BASIC_CONVERSION));
|
520
|
+
|
521
|
+
//New safe code
|
522
|
+
args[0] = fun;
|
523
|
+
args[1] = tmp;
|
524
|
+
*obj = rb_ensure(call_proc,(VALUE) &args[0],reset_mode,mode);
|
525
|
+
|
526
|
+
return 1; /* conversion succeed */
|
527
|
+
}
|
528
|
+
|
529
|
+
/* Search a conversion procedure from the class attribute */
|
530
|
+
VALUE from_class_table(SEXP robj)
|
531
|
+
{
|
532
|
+
SEXP rclass;
|
533
|
+
VALUE key, fun, class_table;
|
534
|
+
int i;
|
535
|
+
|
536
|
+
class_table = rb_iv_get(RSRUBY, "@class_table");
|
537
|
+
|
538
|
+
PROTECT(rclass = GET_CLASS(robj));
|
539
|
+
|
540
|
+
fun = Qnil;
|
541
|
+
if (rclass != R_NilValue) {
|
542
|
+
|
543
|
+
//key may be an array or string depending on
|
544
|
+
//the class specification
|
545
|
+
key = to_ruby_with_mode(rclass, BASIC_CONVERSION);
|
546
|
+
fun = rb_hash_aref(class_table, key);
|
547
|
+
|
548
|
+
//If we haven't found a function then go through
|
549
|
+
//each class in rclass and look for a match
|
550
|
+
if (fun==Qnil) {
|
551
|
+
|
552
|
+
for (i=0; i<GET_LENGTH(rclass); i++){
|
553
|
+
fun = rb_hash_aref(class_table,
|
554
|
+
rb_str_new2(CHAR(STRING_ELT(rclass, i))));
|
555
|
+
if (fun != Qnil){
|
556
|
+
break;
|
557
|
+
}
|
558
|
+
}
|
559
|
+
}
|
560
|
+
}
|
561
|
+
UNPROTECT(1);
|
562
|
+
return fun;
|
563
|
+
}
|
564
|
+
|
565
|
+
/* Convert a Robj to a Ruby object via the class table (mode 3) */
|
566
|
+
/* See the docs for conversion rules */
|
567
|
+
int
|
568
|
+
to_ruby_class(SEXP robj, VALUE *obj)
|
569
|
+
{
|
570
|
+
VALUE fun, tmp, mode;
|
571
|
+
VALUE args[2];
|
572
|
+
|
573
|
+
fun = from_class_table(robj);
|
574
|
+
|
575
|
+
if (fun==Qnil)
|
576
|
+
return 0; /* conversion failed */
|
577
|
+
|
578
|
+
tmp = Data_Wrap_Struct(rb_const_get(rb_cObject,
|
579
|
+
rb_intern("RObj")), 0, 0, robj);
|
580
|
+
rb_iv_set(tmp,"@conversion",INT2FIX(TOP_MODE));
|
581
|
+
rb_iv_set(tmp,"@wrap",Qfalse);
|
582
|
+
|
583
|
+
//Again set conversion mode to basic to prevent recursion
|
584
|
+
mode = rb_iv_get(RSRUBY, "@default_mode");
|
585
|
+
rb_iv_set(RSRUBY, "@default_mode", INT2FIX(BASIC_CONVERSION));
|
586
|
+
|
587
|
+
//New safe code
|
588
|
+
args[0] = fun;
|
589
|
+
args[1] = tmp;
|
590
|
+
*obj = rb_ensure(call_proc,(VALUE) &args[0],reset_mode,mode);
|
591
|
+
//*obj = rb_funcall(fun, rb_intern("call"), 1, tmp);
|
592
|
+
|
593
|
+
return 1; /* conversion succeed */
|
594
|
+
}
|
595
|
+
|
596
|
+
/* Convert a R named vector or list to a Ruby Hash */
|
597
|
+
VALUE to_ruby_hash(VALUE obj, SEXP names)
|
598
|
+
{
|
599
|
+
int len, i;
|
600
|
+
VALUE it, hash;
|
601
|
+
char *name;
|
602
|
+
|
603
|
+
if ((len = RARRAY(obj)->len) < 0)
|
604
|
+
return Qnil;
|
605
|
+
|
606
|
+
hash = rb_hash_new();
|
607
|
+
for (i=0; i<len; i++) {
|
608
|
+
it = rb_ary_entry(obj, i);
|
609
|
+
name = CHAR(STRING_ELT(names, i));
|
610
|
+
rb_hash_aset(hash, rb_str_new2(name), it);
|
611
|
+
}
|
612
|
+
|
613
|
+
return hash;
|
614
|
+
}
|
615
|
+
|
616
|
+
/* We need to transpose the list because R makes array by the
|
617
|
+
* fastest index */
|
618
|
+
VALUE ltranspose(VALUE list, int *dims, int *strides,
|
619
|
+
int pos, int shift, int len)
|
620
|
+
{
|
621
|
+
VALUE nl, it;
|
622
|
+
int i;
|
623
|
+
|
624
|
+
if (!(nl = rb_ary_new2(dims[pos])))
|
625
|
+
return Qnil;
|
626
|
+
|
627
|
+
if (pos == len-1) {
|
628
|
+
for (i=0; i<dims[pos]; i++) {
|
629
|
+
if (!(it = rb_ary_entry(list, i*strides[pos]+shift)))
|
630
|
+
return Qnil;
|
631
|
+
rb_ary_store(nl, i, it);
|
632
|
+
}
|
633
|
+
return nl;
|
634
|
+
}
|
635
|
+
|
636
|
+
for (i=0; i<dims[pos]; i++) {
|
637
|
+
if (!(it = ltranspose(list, dims, strides, pos+1, shift, len)))
|
638
|
+
return Qnil;
|
639
|
+
rb_ary_store(nl, i, it);
|
640
|
+
shift += strides[pos];
|
641
|
+
}
|
642
|
+
|
643
|
+
return nl;
|
644
|
+
}
|
645
|
+
|
646
|
+
/* Convert a R Array to a Ruby Array (in the form of
|
647
|
+
* array of arrays of ...) */
|
648
|
+
VALUE to_ruby_array(VALUE obj, int *dims, int l)
|
649
|
+
{
|
650
|
+
VALUE list;
|
651
|
+
int i, c, *strides;
|
652
|
+
|
653
|
+
strides = (int *)ALLOC_N(int,l);
|
654
|
+
if (!strides)
|
655
|
+
rb_raise(rb_eRuntimeError,"Could not allocate memory for array\n");
|
656
|
+
|
657
|
+
c = 1;
|
658
|
+
for (i=0; i<l; i++) {
|
659
|
+
strides[i] = c;
|
660
|
+
c *= dims[i];
|
661
|
+
}
|
662
|
+
|
663
|
+
list = ltranspose(obj, dims, strides, 0, 0, l);
|
664
|
+
free(strides);
|
665
|
+
|
666
|
+
return list;
|
667
|
+
}
|