alexgutteridge-rsruby 0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/ext/rsruby.c ADDED
@@ -0,0 +1,182 @@
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
+
34
+ /* Global list to protect R objects from garbage collection */
35
+ /* This is inspired in $R_SRC/src/main/memory.c */
36
+ //static SEXP R_References;
37
+
38
+ SEXP
39
+ RecursiveRelease(SEXP obj, SEXP list)
40
+ {
41
+ if (!isNull(list)) {
42
+ if (obj == CAR(list))
43
+ return CDR(list);
44
+ else
45
+ SETCDR(list, RecursiveRelease(obj, CDR(list)));
46
+ }
47
+ return list;
48
+ }
49
+
50
+ /* TODO: This needs implementing as a Ruby destructor for each RObj */
51
+ /*static void
52
+ Robj_dealloc(VALUE self)
53
+ {
54
+ SEXP robj;
55
+
56
+ Data_Get_Struct(self, struct SEXPREC, robj);
57
+
58
+ R_References = RecursiveRelease(robj, R_References);
59
+ SET_SYMVALUE(install("R.References"), R_References);
60
+
61
+ return;
62
+ }*/
63
+
64
+
65
+ /* Obtain an R object via its name.
66
+ * This is only used to get the 'get' function.
67
+ * All subsequent calls go via the 'get' function itself
68
+ */
69
+ VALUE get_fun(VALUE self, VALUE name){
70
+
71
+ VALUE str;
72
+ int conversion=TOP_MODE;
73
+ SEXP robj;
74
+ VALUE rubyobj;
75
+ char* cstr_name;
76
+
77
+ str = StringValue(name);
78
+
79
+ cstr_name = RSTRING(str)->ptr;
80
+
81
+ robj = (SEXP)get_fun_from_name(cstr_name);
82
+ if (!robj)
83
+ return Qnil;
84
+
85
+ /* Wrap the returned R object as a ruby Object */
86
+ rubyobj = Data_Wrap_Struct(rb_const_get(rb_cObject,
87
+ rb_intern("RObj")), 0, 0, robj);
88
+ rb_iv_set(rubyobj,"@conversion",INT2FIX(conversion));
89
+ rb_iv_set(rubyobj,"@wrap",Qfalse);
90
+
91
+ return rubyobj;
92
+
93
+ }
94
+
95
+ //TODO - This function does not appear to be working correctly
96
+ void r_finalize(void)
97
+ {
98
+ unsigned char buf[1024];
99
+ char * tmpdir;
100
+
101
+ R_dot_Last();
102
+ R_RunExitFinalizers();
103
+ CleanEd();
104
+ KillAllDevices();
105
+
106
+ if((tmpdir = getenv("R_SESSION_TMPDIR"))) {
107
+ snprintf((char *)buf, 1024, "rm -rf %s", tmpdir);
108
+ R_system((char *)buf);
109
+ }
110
+
111
+ PrintWarnings(); /* from device close and .Last */
112
+ R_gc(); /* Remove any remaining R objects from memory */
113
+ }
114
+
115
+ /*
116
+ * Shutdown the R interpreter
117
+ */
118
+ VALUE rs_shutdown(VALUE self){
119
+
120
+ r_finalize();
121
+ Rf_endEmbeddedR(0);
122
+ return Qtrue;
123
+
124
+ }
125
+
126
+ /*
127
+ * Starts the R interpreter.
128
+ */
129
+ VALUE rr_init(VALUE self){
130
+
131
+ SEXP R_References;
132
+
133
+ init_R(0,NULL);
134
+ // Initialize the list of protected objects
135
+ R_References = R_NilValue;
136
+ SET_SYMVALUE(install("R.References"), R_References);
137
+
138
+ return self;
139
+
140
+ }
141
+
142
+ /*
143
+ * Initialises the R interpreter.
144
+ */
145
+ void init_R(int argc, char **argv){
146
+
147
+ char *defaultArgv[] = {"rsruby","-q","--vanilla"};
148
+
149
+ Rf_initEmbeddedR(sizeof(defaultArgv) / sizeof(defaultArgv[0]), defaultArgv);
150
+ R_Interactive = FALSE; //Remove crash menu (and other interactive R features)
151
+ }
152
+
153
+ /* This method is for testing catching of segfaults */
154
+ VALUE crash(){
155
+ int* ptr = (int*)0;
156
+ *ptr = 1;
157
+ return Qtrue;
158
+ }
159
+
160
+
161
+ /* Ruby code */
162
+
163
+ VALUE cRRuby;
164
+ VALUE cRObj;
165
+
166
+ void Init_rsruby_c(){
167
+
168
+ cRRuby = rb_define_class("RSRuby",rb_cObject);
169
+
170
+ rb_define_method(cRRuby, "r_init", rr_init, 0);
171
+ rb_define_method(cRRuby, "get_fun", get_fun, 1);
172
+ rb_define_method(cRRuby, "shutdown", rs_shutdown, 0);
173
+
174
+ rb_define_method(cRRuby, "crash", crash, 0);
175
+
176
+ //Add the lcall method to RObj
177
+ cRObj = rb_const_get(rb_cObject,rb_intern("RObj"));
178
+ rb_define_method(cRObj, "lcall", RObj_lcall, 1);
179
+ rb_define_method(cRObj, "__init_lcall__", RObj_init_lcall, 1);
180
+ rb_define_method(cRObj, "to_ruby", RObj_to_ruby, -2);
181
+
182
+ }
data/ext/rsruby.h ADDED
@@ -0,0 +1,87 @@
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
+ #ifndef R_RUBY_MAIN
33
+ #define R_RUBY_MAIN
34
+
35
+ #include "ruby.h"
36
+
37
+ #include "R.h"
38
+ #include "Rdefines.h"
39
+ #include "Rinternals.h"
40
+ #include "Rdefines.h"
41
+ #include "Rdevices.h"
42
+
43
+ #include "signal.h"
44
+
45
+ #include "R_eval.h"
46
+ #include "Converters.h"
47
+
48
+ #define MAXIDSIZE 256
49
+
50
+ #define NO_CONVERSION 0
51
+ #define VECTOR_CONVERSION 1
52
+ #define BASIC_CONVERSION 2
53
+ #define CLASS_CONVERSION 3
54
+ #define PROC_CONVERSION 4
55
+
56
+ #define TOP_MODE 4
57
+
58
+ #define RSRUBY rb_funcall(rb_const_get(rb_cObject,rb_intern("RSRuby")),rb_intern("instance"),0)
59
+
60
+ /* Missing definitions from Rinterface.h or RStartup.h */
61
+ # define CleanEd Rf_CleanEd
62
+ extern int Rf_initEmbeddedR(int argc, char **argv);
63
+ extern int R_Interactive;
64
+ extern void CleanEd(void);
65
+ extern int R_CollectWarnings;
66
+ # define PrintWarnings Rf_PrintWarnings
67
+ extern void PrintWarnings(void);
68
+
69
+ void Init_rsruby();
70
+
71
+ void init_R(int argc, char *argv[0]);
72
+ void r_finalize(void);
73
+
74
+ SEXP RecursiveRelease(SEXP obj, SEXP list);
75
+ //static void Robj_dealloc(VALUE self);
76
+
77
+ VALUE rs_shutdown(VALUE self);
78
+ VALUE get_fun(VALUE self, VALUE name);
79
+ VALUE rr_init(VALUE self);
80
+
81
+ VALUE crash(void);
82
+
83
+ VALUE RObj_lcall(VALUE self, VALUE args);
84
+ VALUE RObj_init_lcall(VALUE self, VALUE args);
85
+ VALUE RObj_to_ruby(VALUE self, VALUE args);
86
+ int make_argl(VALUE args, SEXP *e);
87
+ #endif
data/lib/rsruby.rb ADDED
@@ -0,0 +1,302 @@
1
+ require 'rsruby/robj'
2
+ require 'rsruby_c'
3
+ require 'singleton'
4
+
5
+ require 'complex'
6
+
7
+ #== Synopsis
8
+ #
9
+ #This class represents the embedded R interpreter. The Singleton module is
10
+ #mixed in to ensure that only one R interpreter is running in a script at
11
+ #any one time and that the interpreter can always be easily accessed without
12
+ #using a global variable.
13
+ #
14
+ #The R interpreter is started by calling RSRuby.instance. The returned
15
+ #object represents the R interpreter and R functions are called by
16
+ #calling methods on this object:
17
+ #
18
+ # r = RSRuby.instance
19
+ # r.sum(1,2,3)
20
+ # puts r.t_test(1,2,3)['p-value']
21
+ #
22
+ #See the manual[http://web.kuicr.kyoto-u.ac.jp/~alexg/rsruby/manual.pdf] for
23
+ #more details on calling functions and the conversion system for passing data
24
+ #between Ruby and R. If no suitable conversion from R to Ruby is found, an RObj
25
+ #is returned (all R functions are returned as instances of RObj).
26
+ #--
27
+ #== Copyright
28
+ #Copyright (C) 2006 Alex Gutteridge
29
+ #
30
+ #The Original Code is the RPy python module.
31
+ #
32
+ #The Initial Developer of the Original Code is Walter Moreira.
33
+ #Portions created by the Initial Developer are Copyright (C) 2002
34
+ #the Initial Developer. All Rights Reserved.
35
+ #
36
+ #Contributor(s):
37
+ #Gregory R. Warnes <greg@warnes.net> (RPy Maintainer)
38
+ #
39
+ #This library is free software; you can redistribute it and/or
40
+ #modify it under the terms of the GNU Lesser General Public
41
+ #License as published by the Free Software Foundation; either
42
+ #version 2.1 of the License, or (at your option) any later version.
43
+ #
44
+ #This library is distributed in the hope that it will be useful,
45
+ #but WITHOUT ANY WARRANTY; without even the implied warranty of
46
+ #MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
47
+ #Lesser General Public License for more details.
48
+ #
49
+ #You should have received a copy of the GNU Lesser General Public
50
+ #License along with this library; if not, write to the Free Software
51
+ #Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
52
+ #++
53
+
54
+ class RSRuby
55
+
56
+ VERSION = '0.5'
57
+
58
+ include Singleton
59
+
60
+ #Constants for conversion modes
61
+ TOP_CONVERSION = 4
62
+ PROC_CONVERSION = 4
63
+ CLASS_CONVERSION = 3
64
+ BASIC_CONVERSION = 2
65
+ VECTOR_CONVERSION = 1
66
+ NO_CONVERSION = 0
67
+ NO_DEFAULT = -1
68
+
69
+ attr_accessor :proc_table, :class_table, :default_mode, :caching
70
+
71
+ #Create a new RSRuby interpreter instance. The Singleton design pattern
72
+ #ensures that only one instance can be running in a script. Further
73
+ #calls to RSRuby.instance will return the original instance.
74
+ def initialize()
75
+
76
+ #Initialize R
77
+ r_init
78
+
79
+ @default_mode = NO_DEFAULT
80
+
81
+ @class_table = {}
82
+ @proc_table = {}
83
+
84
+ @caching = true
85
+ reset_cache
86
+
87
+ #Catch errors
88
+ self.__init_eval_R__("options(error=expression(NULL))")
89
+ #disable errors
90
+ self.__init_eval_R__("options(show.error.messages=F)")
91
+
92
+ end
93
+
94
+ def reset_cache
95
+ #Setup R object cache
96
+ @cache = {}
97
+ @cache['get'] = self.get_fun('get')
98
+
99
+ #Get constants
100
+ @cache['TRUE'] = self.__getitem__('T',true)
101
+ @cache['FALSE'] = self.__getitem__('F',true)
102
+
103
+ @cache['parse'] = self.__getitem__('parse',true)
104
+ @cache['eval'] = self.__getitem__('eval',true)
105
+
106
+ @cache['NA'] = self.__init_eval_R__('NA')
107
+ @cache['NaN'] = self.__init_eval_R__('NaN')
108
+ # @cache['NAN'] = self.eval_R('as.double(NA)')
109
+
110
+ #help!
111
+ @cache['helpfun'] = self.with_mode(NO_CONVERSION, self.__getitem__('help',true))
112
+ end
113
+
114
+ #Delete an R object from the cache. Use R-style function naming, not ruby style.
115
+ def delete_from_cache(x)
116
+ @cache.delete(x)
117
+ end
118
+
119
+ def self.img(filename,args={})
120
+ format = File.extname(filename).gsub(".","").to_sym
121
+ r = RSRuby.instance
122
+ raise ArgumentError, "Format #{format.to_s} is not supported" unless [:pdf].include? format
123
+ r.pdf(filename,args)
124
+ yield(r)
125
+ r.dev_off.call
126
+ end
127
+
128
+ #Handles method name conversion and calling of R functions
129
+ #If called without args the R function/varialbe is returned rather
130
+ #than called.
131
+ def method_missing(r_id,*args)
132
+
133
+ #Translate Ruby method call to R
134
+ robj_name = RSRuby.convert_method_name(r_id.to_s)
135
+
136
+ #Retrieve it
137
+ robj = self.__getitem__(robj_name)
138
+
139
+ #TODO perhaps this is not neccessary - always call these methods
140
+ #use the [] syntax for variables etc...
141
+ if args.length > 0
142
+
143
+ #convert arguments to lcall format
144
+ lcall_args = RSRuby.convert_args_to_lcall(args)
145
+
146
+ #Return result of calling object with lcall
147
+ #formatted args
148
+ return robj.lcall(lcall_args)
149
+
150
+ end
151
+
152
+ return robj
153
+
154
+ end
155
+
156
+ #The same as method_missing, but only returns the R function/object,
157
+ #does not call it.
158
+ def [](r_id)
159
+
160
+ #Translate Ruby method call to R
161
+ robj_name = RSRuby.convert_method_name(r_id.to_s)
162
+
163
+ #Retrieve it
164
+ robj = self.__getitem__(robj_name)
165
+
166
+ #And return it
167
+ return robj
168
+
169
+ end
170
+
171
+ #Takes an #RObj representing an R function and sets the 'wrapping'
172
+ #mode for that function. Implemented for compatibility with RPy.
173
+ def with_mode(mode,func)
174
+ func.wrap = mode
175
+ return func
176
+ end
177
+
178
+ #Converts a String representing a 'Ruby-style' R function name into a
179
+ #String with the real R name according to the rules given in the manual.
180
+ def RSRuby.convert_method_name(name)
181
+ if name.length > 1 and name[-1].chr == '_' and name[-2].chr != '_'
182
+ name = name[0..-2]
183
+ end
184
+ name.gsub!(/__/,'<-')
185
+ name.gsub!(/_/, '.')
186
+ return name
187
+ end
188
+
189
+ #Converts an Array of function arguments into lcall format. If the last
190
+ #element of the array is a Hash then the contents of the Hash are
191
+ #interpreted as named arguments.
192
+ #
193
+ #The returned value is an Array of tuples (Arrays of length two). Each
194
+ #tupple corresponds to a name/argument pair.
195
+ #
196
+ #For example:
197
+ # convert_args_to_lcall([1,2,3,{:a=>4,:b=>5})
198
+ # => [['',1],['',2],['',3],['a',4],['b',5]]
199
+ def RSRuby.convert_args_to_lcall(args)
200
+
201
+ lcall_args = []
202
+
203
+ args.each_with_index do |arg,i|
204
+ unless arg.kind_of?(Hash) and i == args.length-1
205
+ lcall_args.push(['',arg])
206
+ else
207
+ arg.each do |k,v|
208
+ lcall_args.push([k.to_s,v])
209
+ end
210
+ end
211
+ end
212
+
213
+ return lcall_args
214
+
215
+ end
216
+
217
+ #Sets the default conversion mode for RSRuby. The constants defined
218
+ #in #RSRuby should be used
219
+ #DEPRECATED: Use the accessor instead
220
+ def RSRuby.set_default_mode(m)
221
+ if m < -1 or m > TOP_CONVERSION
222
+ raise ArgumentError, "Invalid mode requested"
223
+ end
224
+ RSRuby.instance.default_mode = m
225
+ end
226
+ #Returns the current default conversion mode as an Integer.
227
+ #DEPRECATED: Use the accessor on the RSRuby instance isntead
228
+ def RSRuby.get_default_mode
229
+ RSRuby.instance.default_mode
230
+ end
231
+
232
+ #TODO - not implemented
233
+ def RSRuby.set_rsruby_input(m)
234
+ @@rsruby_input = m
235
+ end
236
+
237
+ #TODO - not implemented
238
+ def RSRuby.get_rsruby_input
239
+ @@rsruby_input
240
+ end
241
+
242
+ #TODO - not implemented
243
+ def RSRuby.set_rsruby_output(m)
244
+ @@rsruby_output = m
245
+ end
246
+
247
+ #TODO - not implemented
248
+ def RSRuby.get_rsruby_output
249
+ @@rsruby_output
250
+ end
251
+
252
+ #TODO - not implemented
253
+ def RSRuby.set_rsruby_showfiles(m)
254
+ @@rsruby_showfiles = m
255
+ end
256
+
257
+ #TODO - not implemented
258
+ def RSRuby.get_rsruby_showfiles
259
+ @@rsruby_showfiles
260
+ end
261
+
262
+ #Evaluates the given string in R. Returns the result of the evaluation.
263
+ def eval_R(s)
264
+ self.eval(self.parse(:text => s))
265
+ end
266
+
267
+
268
+ #Wraps the R help function.
269
+ def help(*args)
270
+ helpobj = @cache['helpfun'].call(args)
271
+ self.print(helpobj)
272
+ end
273
+
274
+
275
+ def __init_eval_R__(s)
276
+ parsed = self.parse.__init_lcall__([['text',s]])
277
+ self.eval.__init_lcall__([['',parsed]])
278
+ end
279
+
280
+ def __getitem__(name,init=false)
281
+
282
+ #Find the identifier and cache (unless already cached)
283
+ unless @cache.has_key?(name) && @caching
284
+ if init
285
+ robj = @cache['get'].__init_lcall__([['',name]])
286
+ else
287
+ robj = @cache['get'].lcall([['',name]])
288
+ end
289
+ @cache[name] = robj if @caching
290
+ end
291
+
292
+ #Retrieve object from cache
293
+ robj ||= @cache[name]
294
+
295
+ return robj
296
+
297
+ end
298
+
299
+ end
300
+
301
+ class RException < RuntimeError
302
+ end