r_bridge 0.5.0 → 0.5.4

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: cbeed05e728b4a713d765b889957fde99c7afa4a8379bbe5220ec39401c62de8
4
- data.tar.gz: 1fd7ebe821f80b039fd6ec84311fea02a4feebda0552c62ddb0673fb4999bbd3
3
+ metadata.gz: b2f3e3fc8242a4204e378f7f86bcc3ca120b163aca161e7b5f7bca12f66eecc3
4
+ data.tar.gz: 64d9be810cc38ed60bff9204c8dbdf5e1668d295f99d617f7bcfbcf91aea0041
5
5
  SHA512:
6
- metadata.gz: 22332956afff7dd86e4803b12912f5f349e04d390fb7d16b94f0bbefbd1fb74313d294bf9045c556ba6ee43c27d1e29a39c88c13877ac4a9d9cc60cec7ebf25a
7
- data.tar.gz: d9b515e48c8ee6df50b2f1d14d2f75623bac6b1f933e10e00f6ae1626788a7907d9d38093bda13bc8cfdb92ca34e58cd2bc7a8bf8945ad03deae3e7ad7c72ed7
6
+ metadata.gz: 3af47902e433e3d7be48d472a28d6d84d6cb684f09fa321db31cf8d2ad007af682d8ad401aba3544158fb9853262f224e531a9c0e769c22aa27957b307a4015a
7
+ data.tar.gz: a43acbb159c37d3ac14ca2580090c810081977d44786812349d901d8ba450173b21ee6ac412a1e9962378fa075030e9b56b52b53e786804969c2fe7406d52257
data/README.md CHANGED
@@ -41,12 +41,14 @@ Depending on installing via source or binary, necessary settings differ. If you
41
41
  + This path should have libR.pc file.
42
42
  5. For R to properly work, R_HOME variable is sometimes required. Set it to the root directory that contains R libraries.
43
43
  + (e.g.) export R_HOME=/usr/lib64/R
44
- 6. Install 'r_bridge' via gem
44
+ 6. Install 'r_bridge' gem
45
45
 
46
46
  ```
47
47
  gem install r_bridge
48
48
  ```
49
49
 
50
+
51
+
50
52
  * For Windows
51
53
 
52
54
  For windows, there is a known issue. Output from R contains unknown characters, which may relate to UTF16 conversion within Windows.
@@ -57,15 +59,13 @@ For windows, there is a known issue. Output from R contains unknown characters,
57
59
  2. Make sure that R.exe and R.dll can be seen from your system.
58
60
  + For Windows, add path for R.exe (e.g. C:\Program Files\R\R-4.0.2\bin ) to 'Path' system or user variable.
59
61
  + Also, create 'RUBY_DLL_PATH' system or user variable, which includes path for R.dll (e.g. C:\Program Files\R\R-4.0.2\bin\x64 ).
60
- 3. Install 'r_bridge' via gem
62
+ 3. Install 'r_bridge' gem
61
63
 
62
64
  ```
63
65
  gem install r_bridge
64
66
  ```
65
67
 
66
68
 
67
-
68
-
69
69
  ## Example
70
70
 
71
71
  * Example 1
@@ -203,15 +203,30 @@ ary = [ RBridge::SymbolR.new("y"),
203
203
  formula = RBridge::create_formula_from_syms( ary ) # y ~ x
204
204
  ```
205
205
 
206
+ * Create EXTPTRSXP type object
207
+
208
+ If you need to deal with external pointers in R, you can hold them with EXTPTRSXP type objects.
209
+
210
+ create_extptr takes FFI::Pointer as its argument, and creates EXTPTRSXP object. Internally R_MakeExternalPtr() C function is used.
211
+
212
+ ```
213
+ RBridge.create_extptr( ffi_ptr )
214
+ ```
215
+
206
216
 
207
217
  * Create R function call
208
218
 
209
- create_function_call creates R's internal S expresion structure for function call.
219
+ create_ns_function_call, create_env_function_call and create_function_call create R's internal S expresion structures for function calls. create_ns_function_call can specify (package) namespace. create_env_function_call can specify environment name.
220
+
221
+ Arguments are passed as Ruby's Hash like objects, Hash or assocation Array, and their each element's value needs to be R's object. For these R objects You can assign not only R vectors but also other function calls.
222
+
223
+ Assocation Arrays are useful when you have more than one elements with the same key name, which Hash does not allow. This happens when you do not specify parameter name and you use empty Strings, "", as parameter names.
210
224
 
211
- Arguments are passed to the second argument as Ruby Hash, and each Hash's value needs to point to R's object. You usually pass R vectors, but you can also pass another function call as an argument.
212
225
 
213
226
  ```
214
- RBridge.create_function_call( fname, hash )
227
+ RBridge.create_ns_function_call( ns, fname, hash_like )
228
+ RBridge.create_env_function_call( env, fname, hash_like )
229
+ RBridge.create_function_call( fname, hash_like )
215
230
 
216
231
  (e.g.)
217
232
  # pass vector to argument
@@ -221,6 +236,25 @@ RBridge.exec_function_no_return(hellowrold)
221
236
  # pass another function call to argument
222
237
  getwd = RBridge.create_function_call( "print", { "x" => RBridge.create_function_call( "getwd", {} ) } )
223
238
  RBridge.exec_function_no_return(getwd)
239
+
240
+ # call sqrt function in base package.
241
+ ivec = RBridge.create_intvec([1,2,3])
242
+ calc = RBridge.create_ns_function_call( "base","sqrt", {"x" => ivec} )
243
+ RBridge.exec_function_no_return( RBridge.create_function_call( "print", {"" => calc} )) # execute & print out
244
+
245
+ # Call function in environment
246
+ #
247
+ # (e.g.) source the following newenv.R and call the newenv$hello function.
248
+ # # newenv.R
249
+ # newenv = new.env()
250
+ # newenv$hello = function(){ print("Hello!") }
251
+ #
252
+ RBridge.exec_function_no_return( RBridge.create_function_call("source", { "" => RBridge.create_strvec(["newenv.R"]) }))
253
+ RBridge.exec_function_no_return( RBridge.create_env_function_call("newenv", "hello", { } ))
254
+
255
+ # pass association Array to argument
256
+ add = RBridge.create_function_call( "+", [["" , RBridge.create_intvec( [1,2,3] )],["", RBridge.create_intvec( [10,20,30] ) ]] )
257
+ RBridge.exec_function( RBridge.create_function_call("print", [["" , add ]]))
224
258
  ```
225
259
 
226
260
  The followings are utility functions. assign() and library() are frequently used in R, and easy ways to create them are provided.
@@ -346,7 +380,7 @@ RBridge.end_embedded_r()
346
380
 
347
381
  ## Additional Features
348
382
 
349
- * LazyFunc + RParamManager (+ RResultManager): In contrast to create_function_call(), create_lazy_funcion(), which create LazyFunc object, does not need to take existent R objects. It can take RParamName, RResultName and so on, which are not associated with R objects until the function is evaluated.
383
+ * LazyFunc + RParamManager (+ RResultManager): In contrast to create_function_call(), create_lazy_function(), which creates LazyFunc object, does not need to take existent R objects. It can take RParamName, RResultName and so on, which are not associated with R objects until the function is evaluated. create_ns_lazy_function(), create_env_lazy_function() are lazy evaluation version of create_ns_function_call() and create_env_function_call(), which respectively allows specifying namespaces and environments.
350
384
 
351
385
  These features are being developed for StatSailr, and the API is not stable. They can be accessed but they are not recommended for general usage.
352
386
 
data/Rakefile CHANGED
@@ -1,10 +1,44 @@
1
1
  require "bundler/gem_tasks"
2
2
  require "rake/testtask"
3
+ require 'rake/clean'
3
4
 
5
+ message_for_rake_compiler = "This gem requires native extension compilation. rake-compiler gem should be installed and 'rake compile' before 'rake test'."
6
+
7
+ begin
8
+ require "rake/extensiontask"
9
+ rescue LoadError => e
10
+ puts "ERROR:" + message_for_rake_compiler
11
+ raise
12
+ end
13
+ raise ("ERROR:" + message_for_rake_compiler) unless defined? Rake::ExtensionTask
14
+
15
+ # rake compile
16
+ Rake::ExtensionTask.new do |ext|
17
+ ext.name = "librbridge" # This should be same as shared library name specified in create_makefile of extconf.rb
18
+ ext.ext_dir = "ext/r_bridge"
19
+ ext.lib_dir = "lib/r_bridge"
20
+ end
21
+
22
+ # rake test
4
23
  Rake::TestTask.new(:test) do |t|
5
24
  t.libs << "test"
6
25
  t.libs << "lib"
7
26
  t.test_files = FileList["test/**/*_test.rb"]
8
27
  end
9
28
 
29
+ # rake example
30
+ task :example do
31
+ file_list = Dir.glob("example/example*.rb")
32
+ file_list.each(){|path|
33
+ puts "###### Running script #{path} #####"
34
+ ruby path
35
+ puts ""
36
+ }
37
+ end
38
+
39
+ # rake clean
40
+ CLEAN.include(["tmp/", "lib/r_bridge/librbridge.so"])
41
+
42
+
10
43
  task :default => :test
44
+ task :test => :compile
@@ -0,0 +1,56 @@
1
+ require("r_bridge")
2
+
3
+ RBridge.init_embedded_r()
4
+
5
+ fun = RBridge.create_function_call("getwd", {} )
6
+ path = RBridge.exec_function(fun)
7
+ RBridge.exec_function( RBridge.create_function_call( "print", { "x" => path } ))
8
+
9
+ RBridge.exec_function( RBridge.create_function_call( "print", { "x" => RBridge.create_function_call("getwd", {} ) } ))
10
+
11
+ RBridge.ptr_manager_open ("mean_function") {
12
+ i_v = RBridge.create_vec([0,1,2,3,4,5,6,7,8,9,10,50])
13
+ r_v = RBridge.create_vec( [0.2] )
14
+ func = RBridge.create_function_call( "mean", {"trim" => r_v , "x" => i_v })
15
+ result = RBridge.exec_function(func)
16
+
17
+ # print RBridge.confirm_type( result, :REALSXP )
18
+ func2 = RBridge.create_function_call( "print", { "x" => result } )
19
+ RBridge.exec_function(func2)
20
+ }
21
+
22
+ RBridge.ptr_manager_open("cat hello world"){
23
+ s_v = RBridge.create_vec( ["Hello", "World", "from", "Ruby+R", "\n"] )
24
+ func3 = RBridge.create_function_call( "cat", { "" => s_v } )
25
+ RBridge.exec_function_no_return(func3) # Here exec_function() raises error b/c return value is nil.
26
+ }
27
+
28
+ RBridge.ptr_manager_open("prepare regression analysis"){
29
+ ary = [ RBridge::SymbolR.new("y"),
30
+ RBridge::SymbolR.new("~"),
31
+ RBridge::SymbolR.new("x")]
32
+ formula = RBridge::create_formula_from_syms( ary )
33
+
34
+ df = RBridge::create_dataframe( { "y" => [12, 13, 14, 18 , 17, 20, 20 ] , "x" => [ 1, 2, 3, 4, 5, 6, 7]} )
35
+
36
+ # assign to R variable
37
+ RBridge::exec_function( RBridge::create_assign_function( "formula" , formula ) )
38
+ RBridge::exec_function( RBridge::create_assign_function( "df" , df ))
39
+ }
40
+
41
+ RBridge.ptr_manager_open("run regression analysis. lm(formula, data) "){
42
+ # obtain from R variable
43
+ formula = RBridge::SymbolR.new("formula").to_r_symbol
44
+ df = RBridge::SymbolR.new("df").to_r_symbol
45
+
46
+ func4 = RBridge.create_function_call( "lm", { "formula" => formula , "data" => df } )
47
+ model = RBridge.exec_function(func4)
48
+ func5 = RBridge.create_function_call( "summary", { "object" => model })
49
+ summary = RBridge.exec_function(func5)
50
+ func6 = RBridge.create_function_call( "print", { "x" => summary })
51
+ RBridge.exec_function(func6)
52
+ }
53
+
54
+ RBridge.gc_all()
55
+ RBridge.end_embedded_r()
56
+
@@ -0,0 +1,23 @@
1
+ require "r_bridge"
2
+ RBridge.init_embedded_r()
3
+
4
+ RBridge.ptr_manager_open("prepare regression analysis"){
5
+ ary = [ RBridge::SymbolR.new("y"),
6
+ RBridge::SymbolR.new("~"),
7
+ RBridge::SymbolR.new("x")]
8
+ formula = RBridge::create_formula_from_syms( ary )
9
+
10
+ df = RBridge::create_dataframe( { "y" => [12, 13, 14, 18 , 17, 20, 20 ] , "x" => [ 1, 2, 3, 4, 5, 6, 7]} )
11
+
12
+ RBridge.ptr_manager_open("a little break"){
13
+ RBridge::exec_function_no_return(RBridge::create_function_call("print", {"x" => RBridge::create_vec( ["(^^)" , "<" , "Hello"] ) }))
14
+ }
15
+
16
+ # assign to R variable
17
+ reg = RBridge::exec_function( RBridge::create_function_call( "lm" , {"data" => df , "formula" => formula } ) )
18
+ summary = RBridge::exec_function( RBridge::create_function_call( "summary" , {"object" => reg } ) )
19
+ RBridge::exec_function_no_return(RBridge::create_function_call( "print" , {"x" => summary }))
20
+ }
21
+
22
+ RBridge.gc_all() # In this case, not necesary. ( When there are objects that are not managed by ptr_manager, this is necessary.)
23
+ RBridge.end_embedded_r()
@@ -0,0 +1,10 @@
1
+ require "r_bridge"
2
+ RBridge.init_embedded_r()
3
+
4
+ RBridge.ptr_manager_open("pm1")
5
+ new_vec = RBridge.create_vec(["Hello", "World"])
6
+ RBridge.exec_function_no_return( RBridge.create_function_call("print", {"x" => new_vec }))
7
+ RBridge.ptr_manager_close("pm1")
8
+
9
+ RBridge.gc_all() # In this case, not necesary. ( When there are objects that are not managed by ptr_manager, this is necessary.)
10
+ RBridge.end_embedded_r()
@@ -0,0 +1,14 @@
1
+ require "r_bridge"
2
+
3
+ RBridge.init_embedded_r()
4
+
5
+ getwd = RBridge.create_function_call( "print", { "x" => RBridge.create_function_call( "getwd", {} ) } )
6
+ RBridge.exec_function_no_return(getwd)
7
+
8
+ lcons1 = RBridge.lcons_gen( RBridge.create_vec(["Hello", "World"]) )
9
+ RBridge.set_tag_to_lcons( lcons1, "x")
10
+ lcons2 = RBridge.lcons(RBridge.r_lang_symbol("print") , lcons1)
11
+ RBridge.exec_function_no_return(lcons2)
12
+
13
+ RBridge.gc_all()
14
+ RBridge.end_embedded_r()
@@ -2,25 +2,55 @@
2
2
  #include <Rembedded.h>
3
3
  #include <Rdefines.h>
4
4
 
5
+ #include <stdbool.h>
6
+
7
+ #include <stdint.h>
8
+ #define CSTACK_DEFNS
9
+ #include <Rinterface.h>
10
+
5
11
  #include "win_compat.h"
6
12
 
13
+ #ifdef __FreeBSD__
14
+ #include <ieeefp.h>
15
+ fp_rnd_t fpmask_preset;
16
+ #endif
17
+
7
18
  EXPORT void
8
- r_embedded_init()
19
+ r_embedded_init( bool unlimited_stack_size )
9
20
  {
10
- size_t localArgc = 2;
11
- char localArgs[][50] = {"R", "--silent"};
21
+
22
+ #ifdef __FreeBSD__
23
+ fpmask_preset = fpsetmask(0);
24
+ #endif
25
+
26
+ size_t localArgc = 3;
27
+ char localArgs[][50] = {"R", "--silent", "--vanilla"};
12
28
 
13
29
  char *args[ localArgc ];
14
30
  for (size_t i = 0; i < localArgc; ++i){
15
31
  args[i] = localArgs[i];
16
32
  }
17
33
 
18
- Rf_initEmbeddedR( localArgc , args );
34
+ // Rf_initEmbeddedR raises C stack usage limit error on multithreading environment.
35
+ // Rf_initEmbeddedR( localArgc , args ); is replaced with the following code.
36
+
37
+ if( unlimited_stack_size == false ){ // default stack size
38
+ Rf_initialize_R( localArgc , args );
39
+ setup_Rmainloop();
40
+ }else{
41
+ Rf_initialize_R( localArgc , args );
42
+ R_CStackLimit = (uintptr_t) -1 ; // Set -1 for unlimited C stack size.
43
+ setup_Rmainloop();
44
+ }
19
45
  }
20
46
 
21
47
  EXPORT void
22
48
  r_embedded_end()
23
49
  {
24
50
  Rf_endEmbeddedR(0);
51
+
52
+ #ifdef __FreeBSD__
53
+ fpsetmask(fpmask_preset);
54
+ #endif
25
55
  }
26
56
 
@@ -4,6 +4,22 @@
4
4
 
5
5
  #include "win_compat.h"
6
6
 
7
+ EXPORT SEXP
8
+ r_lang_create_ns_fcall( const char* ns, const char* fname, SEXP args)
9
+ {
10
+ SEXP func;
11
+ PROTECT( func = LCONS( LCONS( Rf_install("::"), LCONS( Rf_install(ns), LCONS( Rf_install(fname), R_NilValue))) , args ));
12
+ return func;
13
+ }
14
+
15
+ EXPORT SEXP
16
+ r_lang_create_env_fcall( const char* env, const char* fname, SEXP args)
17
+ {
18
+ SEXP func;
19
+ PROTECT( func = LCONS( LCONS( Rf_install("$"), LCONS( Rf_install(env), LCONS( Rf_install(fname), R_NilValue))) , args ));
20
+ return func;
21
+ }
22
+
7
23
  EXPORT SEXP
8
24
  r_lang_create_fcall( const char* fname, SEXP args)
9
25
  {
@@ -12,7 +28,6 @@ r_lang_create_fcall( const char* fname, SEXP args)
12
28
  return func;
13
29
  }
14
30
 
15
-
16
31
  EXPORT SEXP
17
32
  r_lang_cons( SEXP car, SEXP cdr)
18
33
  {
@@ -43,6 +58,14 @@ r_lang_symbol( const char* symbol_name)
43
58
  return r_symbol;
44
59
  }
45
60
 
61
+ EXPORT SEXP
62
+ r_lang_create_extptr( void* ptr )
63
+ {
64
+ SEXP extptr;
65
+ PROTECT( extptr = R_MakeExternalPtr( ptr, R_NilValue, R_NilValue ));
66
+ return extptr;
67
+ }
68
+
46
69
  EXPORT SEXP
47
70
  r_lang_nil()
48
71
  {
@@ -7,7 +7,7 @@ module RBridge
7
7
  lib_name = "librbridge" + "." + RbConfig::CONFIG['DLEXT']
8
8
  ffi_lib File.expand_path( lib_name, __dir__ )
9
9
 
10
- attach_function :r_embedded_init, [], :void
10
+ attach_function :r_embedded_init, [:bool], :void
11
11
  attach_function :r_embedded_end, [], :void
12
12
 
13
13
  attach_function :r_vec_create_str, [:int], :pointer
@@ -24,12 +24,16 @@ module RBridge
24
24
  attach_function :r_list_to_dataframe, [:pointer ], :pointer
25
25
  attach_function :r_dataframe_set_rownames, [:pointer, :pointer], :void
26
26
 
27
+ attach_function :r_lang_create_ns_fcall, [:string, :string, :pointer], :pointer
28
+ attach_function :r_lang_create_env_fcall, [:string, :string, :pointer], :pointer
27
29
  attach_function :r_lang_create_fcall, [:string, :pointer], :pointer
28
30
  attach_function :r_lang_cons, [:pointer, :pointer], :pointer
29
31
  attach_function :r_lang_cons_gen, [:pointer], :pointer
30
32
  attach_function :r_lang_set_tag, [:pointer, :string], :void
31
33
  attach_function :r_lang_symbol, [:string], :pointer
32
34
 
35
+ attach_function :r_lang_create_extptr, [:pointer], :pointer
36
+
33
37
  attach_function :r_eval, [:pointer], :pointer
34
38
  attach_function :r_eval_no_return, [:pointer], :pointer
35
39
 
@@ -45,8 +49,11 @@ module RBridge
45
49
 
46
50
  # From here, Ruby interface
47
51
 
48
- def self.init_embedded_r()
49
- r_embedded_init()
52
+ def self.init_embedded_r( unlimited_cstack: false )
53
+ if(unlimited_cstack.class != TrueClass && unlimited_cstack.class != FalseClass)
54
+ raise "init_embedded_r() can take true or false for unlimited C stack size. true: unlimited, false: default."
55
+ end
56
+ r_embedded_init( unlimited_cstack )
50
57
  end
51
58
 
52
59
  def self.end_embedded_r()
@@ -185,7 +192,6 @@ module RBridge
185
192
  when FalseClass
186
193
  1
187
194
  else
188
- nil
189
195
  raise " The current elemnt is not supported to convert to R vector : " + elem
190
196
  end
191
197
  }
@@ -208,6 +214,7 @@ module RBridge
208
214
  when 1
209
215
  raise "All the elements shold be true/false. Current value: " + ary[idx]
210
216
  end
217
+ converted
211
218
  else
212
219
  ary[idx]
213
220
  end
@@ -270,6 +277,10 @@ module RBridge
270
277
  return new_lcons
271
278
  end
272
279
 
280
+ def self.r_nil()
281
+ return r_lang_nil()
282
+ end
283
+
273
284
  def self.is_r_nil?( obj )
274
285
  result = r_is_nil( obj )
275
286
  if(result == 1)
@@ -283,21 +294,53 @@ module RBridge
283
294
  r_lang_set_tag( lcons, tag_name )
284
295
  end
285
296
 
286
- def self.create_function_call( fname, hash )
287
- raise "create_function_call should take String for function name" if(fname.class != String)
288
- raise "create_function_call should take Hash for function arguments" if(hash.class != Hash)
289
- if(hash.size == 0)
297
+ def self.create_extptr( ffi_pointer )
298
+ raise "create_extptr should take a FFI::Pointer argument" if(ffi_pointer.class != FFI::Pointer)
299
+
300
+ extptr = r_lang_create_extptr(ffi_pointer)
301
+
302
+ ptr_manager_add_ptr_to_current( extptr )
303
+ return extptr
304
+ end
305
+
306
+ def self.array_depth (ary)
307
+ return 0 unless ary.is_a?(Array)
308
+ return 1 + ary.map(){|elem| array_depth(elem) }.max()
309
+ end
310
+
311
+ def self.assoc_array?(ary)
312
+ if ary.map(){|elem| elem.size }.uniq == [2]
313
+ true
314
+ else
315
+ false
316
+ end
317
+ end
318
+
319
+ def self.hash_to_lcons_args( hash_like )
320
+ if hash_like.class == Hash
321
+ # hash
322
+ elsif hash_like.class == Array
323
+ if array_depth(hash_like) == 2 && assoc_array?(hash_like)
324
+ # association array
325
+ else
326
+ raise "hash_to_lcons_args should take Hash argument (or Association Array argument)"
327
+ end
328
+ else
329
+ raise "hash_to_lcons_args should take Hash argument (or Association Array argument)"
330
+ end
331
+
332
+ if(hash_like.size == 0)
290
333
  lcons_args = r_lang_nil()
291
- elsif(hash.size == 1)
292
- tag = hash.first[0]
293
- val = hash.first[1]
334
+ elsif(hash_like.size == 1)
335
+ tag = hash_like.first[0]
336
+ val = hash_like.first[1]
294
337
  lcons_args = lcons_gen( val )
295
338
  if( tag != "" )
296
339
  set_tag_to_lcons( lcons_args, tag )
297
340
  end
298
341
  else
299
342
  idx = 0
300
- hash.reverse_each(){|arg|
343
+ hash_like.reverse_each(){|arg|
301
344
  tag = arg[0]
302
345
  val = arg[1]
303
346
  if(idx == 0 )
@@ -311,6 +354,36 @@ module RBridge
311
354
  idx = idx + 1
312
355
  }
313
356
  end
357
+ return lcons_args
358
+ end
359
+
360
+ def self.create_ns_function_call( ns, fname, hash_like )
361
+ raise "create_ns_function_call should take String for namespace" if(ns.class != String)
362
+ raise "create_ns_function_call should take String for function name" if(fname.class != String)
363
+ raise "create_ns_function_call should take Hash like structure for function arguments" if(hash_like.class != Hash && hash_like.class != Array)
364
+ lcons_args = hash_to_lcons_args( hash_like )
365
+
366
+ new_function_call = r_lang_create_ns_fcall(ns, fname, lcons_args)
367
+ ptr_manager_add_ptr_to_current( new_function_call )
368
+ return new_function_call
369
+ end
370
+
371
+ def self.create_env_function_call( env, fname, hash_like )
372
+ raise "create_env_function_call should take String for env" if(env.class != String)
373
+ raise "create_env_function_call should take String for function name" if(fname.class != String)
374
+ raise "create_env_function_call should take Hash like structure for function arguments" if(hash_like.class != Hash && hash_like.class != Array)
375
+ lcons_args = hash_to_lcons_args( hash_like )
376
+
377
+ new_function_call = r_lang_create_env_fcall(env, fname, lcons_args)
378
+ ptr_manager_add_ptr_to_current( new_function_call )
379
+ return new_function_call
380
+ end
381
+
382
+ def self.create_function_call( fname, hash_like )
383
+ raise "create_function_call should take String for function name" if(fname.class != String)
384
+ raise "create_function_call should take Hash like structure for function arguments" if(hash_like.class != Hash && hash_like.class != Array)
385
+ lcons_args = hash_to_lcons_args( hash_like )
386
+
314
387
  new_function_call = r_lang_create_fcall(fname, lcons_args)
315
388
  ptr_manager_add_ptr_to_current( new_function_call )
316
389
  return new_function_call
@@ -1,25 +1,48 @@
1
1
  require "r_bridge/r_bridge_ffi"
2
2
 
3
3
  module RBridge
4
- def self.create_lazy_function( fname, hash , param_manager)
4
+ def self.create_ns_lazy_function( ns, fname, hash_like , param_manager)
5
+ raise "create_ns_lazy_function should take String for namespace" if(ns.class != String)
6
+ raise "create_ns_lazy_function should take String for function name" if(fname.class != String)
7
+ raise "create_ns_lazy_function should take Hash like structure for function arguments" if(hash_like.class != Hash && hash_like.class != Array)
8
+ return LazyFunc.new( ns, nil, fname, hash_like, param_manager)
9
+ end
10
+
11
+ def self.create_env_lazy_function( env, fname, hash_like , param_manager)
12
+ raise "create_env_lazy_function should take String for environment" if(env.class != String)
13
+ raise "create_env_lazy_function should take String for function name" if(fname.class != String)
14
+ raise "create_env_lazy_function should take Hash like structure for function arguments" if(hash_like.class != Hash && hash_like.class != Array)
15
+ return LazyFunc.new( nil, env, fname, hash_like, param_manager)
16
+ end
17
+
18
+ def self.create_lazy_function( fname, hash_like , param_manager)
5
19
  raise "create_lazy_function should take String for function name" if(fname.class != String)
6
- raise "create_lazy_function should take Hash for function arguments" if(hash.class != Hash)
7
- return LazyFunc.new( fname, hash , param_manager)
20
+ raise "create_lazy_function should take Hash like structure for function arguments" if(hash_like.class != Hash && hash_like.class != Array)
21
+ return LazyFunc.new( nil, nil, fname, hash_like, param_manager)
8
22
  end
9
23
 
10
- def self.create_function_call_from_lazy_function ( fname, fargs, param_manager, result_manager)
11
- farg_keys = fargs.keys
24
+ def self.create_function_call_from_lazy_function_attrs( ns, env, fname, fargs, param_manager, result_manager)
25
+
26
+ new_arg_assoc_array = []
27
+ fargs.each(){|key, val|
28
+
29
+ if val.is_a? RResultPrevious
30
+ r_previous = result_manager.get_previous() # if r_nil (i.e. 1st instruction or no previous result-store instructions) we need to use default one.
31
+ if ! RBridge::is_r_nil?(r_previous) # When previous result exists
32
+ new_arg_assoc_array << [key, r_previous]
33
+ next
34
+ else # When previous result does not exist
35
+ val = val.default
36
+ end
37
+ end
12
38
 
13
- new_arg_hash = {}
14
- farg_keys.each(){|key|
15
- val = fargs[key]
16
39
  case val
17
40
  when LazyFunc then
18
- new_arg_hash[key] = create_function_call_from_lazy_function( val.fname, val.args , param_manager, result_manager )
41
+ new_arg_assoc_array << [key, create_function_call_from_lazy_function_attrs( val.ns, val.env, val.fname, val.args , param_manager, result_manager )]
19
42
  when RResultName , RResultNameArray then
20
- new_arg_hash[key] = result_manager.get_last_for( val )
43
+ new_arg_assoc_array << [key, result_manager.get_last_for( val )]
21
44
  when RParamName then
22
- new_arg_hash[key] = param_manager.get_r_object( val )
45
+ new_arg_assoc_array << [key, param_manager.get_r_object( val )]
23
46
  when RNameContainer then
24
47
  idx = 0
25
48
  while idx < val.elems.size do
@@ -27,51 +50,69 @@ module RBridge
27
50
  case elem
28
51
  when RResultName, RResultNameArray then
29
52
  result = result_manager.get_last_for( elem )
30
- if( ! result.nil? )
31
- new_arg_hash[key] = result
53
+ if( ! RBridge::is_r_nil?(result) )
54
+ new_arg_assoc_array << [key, result]
32
55
  break
33
56
  end
34
57
  when RParamName then
35
58
  result = param_manager.get_r_object( elem )
36
- if( ! result.nil? )
37
- new_arg_hash[key] = result
59
+ if( ! RBridge::is_r_nil?(result) )
60
+ new_arg_assoc_array << [key, result]
38
61
  break
39
62
  end
40
63
  else # R object
41
- new_arg_hash[key] = val
64
+ new_arg_assoc_array << [key, val]
42
65
  break
43
66
  end
44
67
  idx = idx + 1
45
68
  end
46
69
  if(idx == val.elems.size ) # Not found
47
- new_arg_hash[key] = nil
70
+ new_arg_assoc_array << [key, RBridge::r_nil()]
48
71
  end
49
72
  else # R object
50
- new_arg_hash[key] = val
73
+ new_arg_assoc_array << [key, val]
51
74
  end
52
75
  }
53
- return create_function_call( fname, new_arg_hash )
76
+ if( ns.nil? )
77
+ if( env.nil? )
78
+ return create_function_call( fname, new_arg_assoc_array )
79
+ else
80
+ return create_env_function_call( env, fname, new_arg_assoc_array )
81
+ end
82
+ else
83
+ if( env.nil? )
84
+ return create_ns_function_call( ns, fname, new_arg_assoc_array )
85
+ else
86
+ raise "namespace and environment are not allowed to be specified at the same time."
87
+ end
88
+ end
54
89
  end
55
90
 
56
91
  def self.exec_lazy_function( lazy_func , result_manager , allow_nil_result: false )
57
92
  raise "exec_lazy_function should take LazyFunc object" if(lazy_func.class != LazyFunc)
58
93
  raise "exec_lazy_function should take RResultManager or Nil for 2nd argment: " + result_manager.class.to_s if(! [RResultManager, NilClass].include?(result_manager.class) )
94
+ ns = lazy_func.ns
95
+ env = lazy_func.env
59
96
  fname = lazy_func.fname
60
97
  arg_hash = lazy_func.args
61
98
  param_manager = lazy_func.param_manager
62
99
 
63
- func = create_function_call_from_lazy_function(fname, arg_hash, param_manager, result_manager)
100
+ func = create_function_call_from_lazy_function_attrs(ns, env, fname, arg_hash, param_manager, result_manager)
64
101
  result = exec_function( func , allow_nil_result: allow_nil_result )
65
102
  return result
66
103
  end
67
104
 
68
105
  class LazyFunc
106
+ attr :ns
107
+ attr :env
69
108
  attr :fname
70
109
  attr :args
71
110
  attr :param_manager
72
111
 
73
- def initialize( fname, arg_hash, param_manager)
112
+ def initialize( ns, env, fname, arg_hash, param_manager)
74
113
  raise "LazyFunc requires RParamManager object for param_manager argument " if ! param_manager.is_a?(RParamManager)
114
+ @ns = ns # When namespace does not need to be specified, set nil.
115
+ @env = env # When environment does not need to be specified, set nil.
75
116
  @fname = fname
76
117
  @args = arg_hash
77
118
  @param_manager = param_manager
@@ -86,6 +127,7 @@ module RBridge
86
127
  end
87
128
 
88
129
  class RParamManager
130
+ attr :param_hash , true
89
131
  def initialize(hash)
90
132
  @param_hash = hash
91
133
  end
@@ -112,6 +154,18 @@ module RBridge
112
154
  end
113
155
  end
114
156
 
157
+ class RResultPrevious
158
+ # RResultPrevious is used for result from the previous instruction.
159
+ # If the instruction is the 1st one, there are no previous ones. At this time, default one is used.
160
+ attr :default
161
+ def initialize(val)
162
+ if ! ( val.is_a?(RNameContainer) || val.is_a?(RResultName) || val.is_a?(RResultNameArray) || val.is_a?(RParamName) || ::RBridge.is_pointer?( val ) )
163
+ raise "RResultPrevious.new requires RNameContainer, RResultName, RResultNameArray, RParamName or R object as default"
164
+ end
165
+ @default = val
166
+ end
167
+ end
168
+
115
169
  class RResultManager
116
170
  def initialize
117
171
  @results = []
@@ -125,7 +179,7 @@ module RBridge
125
179
  @results << [inst_name, r_obj ]
126
180
  end
127
181
 
128
- def get_last_index_for( result_name )
182
+ def get_last_index_for( result_name ) # From this method, if result name is not found return (Ruby) nil.
129
183
  name = result_name.name
130
184
 
131
185
  idx = @results.size - 1
@@ -143,18 +197,18 @@ module RBridge
143
197
  end
144
198
  end
145
199
 
146
- def get_last_for( r_result ) # If corresponding result name is not found, return nil.
200
+ def get_last_for( r_result ) # If corresponding result name is not found, return r_nil().
147
201
  raise "get_last_for method requires RResultName or RResultNameArray for its argument." if ! ( r_result.is_a?(RResultName) || r_result.is_a?(RResultNameArray) )
148
202
  if( r_result.is_a? RResultName)
149
203
  inst_name = r_result.name
150
204
 
151
205
  elem_to_match = @results.reverse.find{|elem| elem[0] == inst_name }
152
206
  if elem_to_match.nil?
153
- return nil
207
+ return RBridge::r_nil()
154
208
  else
155
209
  r_obj = elem_to_match[1]
156
210
  if RBridge::is_r_nil?( r_obj )
157
- return nil
211
+ return RBridge::r_nil()
158
212
  else
159
213
  return r_obj
160
214
  end
@@ -170,7 +224,7 @@ module RBridge
170
224
  }
171
225
 
172
226
  if( index_array.all?(nil) )
173
- return nil
227
+ return RBridge::r_nil()
174
228
  else
175
229
  index_array.delete(nil)
176
230
  if ! index_array.empty?
@@ -178,13 +232,22 @@ module RBridge
178
232
  r_obj = @results[last_idx][1]
179
233
  return r_obj
180
234
  else
181
- return nil
235
+ return RBridge::r_nil()
182
236
  end
183
237
  end
184
238
  else
185
239
  raise "get_last_for() takes unexpected object"
186
240
  end
187
241
  end
242
+
243
+ def get_previous()
244
+ if @results.size > 0
245
+ r_obj = @results.last[1]
246
+ return r_obj
247
+ else
248
+ return RBridge::r_nil()
249
+ end
250
+ end
188
251
  end
189
252
 
190
253
  class RNameContainer
@@ -1,3 +1,3 @@
1
1
  module RBridge
2
- VERSION = "0.5.0"
2
+ VERSION = "0.5.4"
3
3
  end
data/r_bridge.gemspec CHANGED
@@ -6,8 +6,8 @@ Gem::Specification.new do |spec|
6
6
  spec.authors = ["Toshihiro Umehara"]
7
7
  spec.email = ["toshi@niceume.com"]
8
8
 
9
- spec.summary = %q{Enables Ruby to access and construct R internal objects}
10
- spec.description = %q{R (language) provides C interface. This package utilize the interface and allow Ruby to manipulate and evaluate R's internal S expressions }
9
+ spec.summary = %q{Enables Ruby to construct and evaluate R internal objects}
10
+ spec.description = %q{R (language) provides C interface. This package utilize the interface and allow Ruby to construct and evaluate R's internal S expressions }
11
11
  spec.homepage = "https://github.com/niceume/r_bridge"
12
12
  spec.license = "GPL-3.0"
13
13
  spec.required_ruby_version = Gem::Requirement.new(">= 2.3.0")
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: r_bridge
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.0
4
+ version: 0.5.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Toshihiro Umehara
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-10-28 00:00:00.000000000 Z
11
+ date: 2021-11-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: ffi
@@ -31,7 +31,7 @@ dependencies:
31
31
  - !ruby/object:Gem::Version
32
32
  version: 1.13.0
33
33
  description: 'R (language) provides C interface. This package utilize the interface
34
- and allow Ruby to manipulate and evaluate R''s internal S expressions '
34
+ and allow Ruby to construct and evaluate R''s internal S expressions '
35
35
  email:
36
36
  - toshi@niceume.com
37
37
  executables: []
@@ -47,6 +47,10 @@ files:
47
47
  - Rakefile
48
48
  - bin/console
49
49
  - bin/setup
50
+ - example/example.rb
51
+ - example/example_ptr_manager_block.rb
52
+ - example/example_ptr_manager_explicit_use.rb
53
+ - example/example_using_lcons.rb
50
54
  - ext/r_bridge/extconf.rb
51
55
  - ext/r_bridge/r_bridge.c
52
56
  - ext/r_bridge/r_embed.c
@@ -84,8 +88,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
84
88
  - !ruby/object:Gem::Version
85
89
  version: '0'
86
90
  requirements: []
87
- rubygems_version: 3.1.2
91
+ rubygems_version: 3.1.4
88
92
  signing_key:
89
93
  specification_version: 4
90
- summary: Enables Ruby to access and construct R internal objects
94
+ summary: Enables Ruby to construct and evaluate R internal objects
91
95
  test_files: []