alexgutteridge-rsruby 0.5

Sign up to get free protection for your applications and to get access to all the features.
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