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 +4 -4
- data/README.md +42 -8
- data/Rakefile +34 -0
- data/example/example.rb +56 -0
- data/example/example_ptr_manager_block.rb +23 -0
- data/example/example_ptr_manager_explicit_use.rb +10 -0
- data/example/example_using_lcons.rb +14 -0
- data/ext/r_bridge/r_embed.c +34 -4
- data/ext/r_bridge/r_lang.c +24 -1
- data/lib/r_bridge/r_bridge_ffi.rb +85 -12
- data/lib/r_bridge/r_bridge_lazyfunc_ext.rb +90 -27
- data/lib/r_bridge/version.rb +1 -1
- data/r_bridge.gemspec +2 -2
- metadata +9 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b2f3e3fc8242a4204e378f7f86bcc3ca120b163aca161e7b5f7bca12f66eecc3
|
4
|
+
data.tar.gz: 64d9be810cc38ed60bff9204c8dbdf5e1668d295f99d617f7bcfbcf91aea0041
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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'
|
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'
|
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
|
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.
|
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(),
|
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
|
data/example/example.rb
ADDED
@@ -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()
|
data/ext/r_bridge/r_embed.c
CHANGED
@@ -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
|
-
|
11
|
-
|
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
|
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
|
|
data/ext/r_bridge/r_lang.c
CHANGED
@@ -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
|
-
|
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.
|
287
|
-
raise "
|
288
|
-
|
289
|
-
|
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(
|
292
|
-
tag =
|
293
|
-
val =
|
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
|
-
|
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.
|
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(
|
7
|
-
return LazyFunc.new(
|
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.
|
11
|
-
|
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
|
-
|
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
|
-
|
43
|
+
new_arg_assoc_array << [key, result_manager.get_last_for( val )]
|
21
44
|
when RParamName then
|
22
|
-
|
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
|
31
|
-
|
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
|
37
|
-
|
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
|
-
|
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
|
-
|
70
|
+
new_arg_assoc_array << [key, RBridge::r_nil()]
|
48
71
|
end
|
49
72
|
else # R object
|
50
|
-
|
73
|
+
new_arg_assoc_array << [key, val]
|
51
74
|
end
|
52
75
|
}
|
53
|
-
|
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 =
|
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
|
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
|
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
|
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
|
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
|
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
|
data/lib/r_bridge/version.rb
CHANGED
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
|
10
|
-
spec.description = %q{R (language) provides C interface. This package utilize the interface and allow Ruby to
|
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.
|
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:
|
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
|
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.
|
91
|
+
rubygems_version: 3.1.4
|
88
92
|
signing_key:
|
89
93
|
specification_version: 4
|
90
|
-
summary: Enables Ruby to
|
94
|
+
summary: Enables Ruby to construct and evaluate R internal objects
|
91
95
|
test_files: []
|