Module: R::Support

Defined in:
lib/R_interface/rsupport.rb,
lib/R_interface/r_methods.rb,
lib/R_interface/rsupport_scope.rb

Overview


This is a support module for evaluating R functions


Constant Summary collapse

@@exec_counter =
0
@@exec_from_ruby =

Using this method gives us more control over what happens when calling do.call and allows for debugging. Use it in exec_function when debugging is needed.

Polyglot.eval("R", <<-R)
  function(build_method, ...) {
    # function 'invoke' from package rlang should do a cleaner than
    # do.call (this is what the documentation says).
    # res = do.call(...);
    res = invoke(...);
    res2 = build_method(res);
    res2
  }
R

Class Method Summary collapse

Class Method Details

.captureObject


Captures the R output to a variable and returns it.




32
33
34
35
36
37
38
39
40
41
42
# File 'lib/R_interface/r_methods.rb', line 32

def self.capture
  Polyglot.eval("R", <<-R)
    function(obj, ...) {
      sink(tt <- textConnection("results","w"), split=FALSE, type = c("output", "message"));
      print(obj, ...);
      sink();
      close(tt);
      results
    }
  R
end

.capture2Object



44
45
46
47
48
49
50
51
52
53
54
55
56
57
# File 'lib/R_interface/r_methods.rb', line 44

def self.capture2
  Polyglot.eval("R", <<-R)
    function(obj, ...) {
      tryCatch({
        sink(tt <- textConnection("results","w"), split=FALSE, type = c("output", "message"));
        print(obj, ...);
        sink();
        results
      }, finally = {
        close(tt);
      })
    }
  R
end

.convert_symbol2r(symbol) ⇒ Object


Converts a Ruby symbol onto an R symbol. Converts ‘__’ to ‘.’ and ‘rclass’ to class


Parameters:

  • symbol (Symbol)

    A Ruby symbol to convert to R



200
201
202
203
204
205
206
207
208
# File 'lib/R_interface/rsupport.rb', line 200

def self.convert_symbol2r(symbol)
  name = symbol.to_s
  # convert '__' to '.'
  name.gsub!(/__/,".")
  # Method 'rclass' is a substitute for R method 'class'.  Needed, as 'class' is also
  # a Ruby method on an object
  name.gsub!("rclass", "class")
  name
end

.create_bin_expr(operator) ⇒ Object





152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
# File 'lib/R_interface/r_methods.rb', line 152

def self.create_bin_expr(operator)
  is_formula = (operator == "`~`")? 'TRUE' : 'FALSE'
  Polyglot.eval("R", <<-R)
    function(op1, op2) {
      o1 = enexpr(op1)
      o2 = enexpr(op2)
      if (typeof(o1) == 'symbol' && o1 == expr(`__`)) {
        o1 = expr(.)
      }
      if (typeof(o2) == 'symbol' && o2 == expr(`__`)) {
        o2 = expr(.)
      }
      exp = expr(#{operator}(!!o1, !!o2))
      if (#{is_formula}) {
        as.formula(exp)
      } else {
        exp
      }
    }
    R
end

.dbk_indexObject





81
82
83
84
85
86
87
# File 'lib/R_interface/r_methods.rb', line 81

def self.dbk_index
  Polyglot.eval("R", <<-R)
    function(obj, ...) {
      obj[[...]]
    }
  R
end

.enquoObject





122
123
124
125
126
127
128
# File 'lib/R_interface/r_methods.rb', line 122

def self.enquo
  Polyglot.eval("R", <<-R)
    function(x, ...) {
      enquo(x)          
    } 
  R
end

.eval(string) ⇒ Object


@TODO: Fix the ‘rescue’ clause and open issue with fastR Evaluates an R code


Parameters:

  • string (String)

    A string of R code that can be correctly parsed by R



86
87
88
89
90
91
92
# File 'lib/R_interface/rsupport.rb', line 86

def self.eval(string)
  begin
    Polyglot.eval("R", string)
  rescue RuntimeError
    raise NoMethodError.new("Method #{string} not found in R environment")
  end
end

.exec_function(function, *args) ⇒ Object



Parameters:

  • function (R function (Interop))

    R function to execute



215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
# File 'lib/R_interface/rsupport.rb', line 215

def self.exec_function(function, *args)

  begin
    
    # If the execution counter is 0, function was not recursively called
    # Starts capturing output
    if (@@exec_counter == 0)
      R::Support.eval("unlockBinding('r_capture', globalenv())")
      @@con = R::Support.start_capture.call("r_capture")
    end
    
    @@exec_counter = @@exec_counter + 1
    
    # function has no arguments, call it directly
    if (args.length == 0)
      res = R::Object.build(function.call)
    else
      pl = R::Support.parse2list(*args)
      res = @@exec_from_ruby.call(R::Object.method(:build), function, pl)
      # R::Object.build(R::Support.eval("do.call").call(function, pl))
    end

    @@exec_counter = @@exec_counter - 1
    
    # When execution counter back to 0, print the captured output if the length
    # of the output is greater than 0
    if (@@exec_counter == 0)
      R::Support.stop_capture.call(@@con)
      if (R::Support.eval("length(r_capture) > 0")[0])
        cap = R::Object.build(R::Support.eval("r_capture"))
        (0...cap.size).each do |i|
          puts cap >> i
        end
      end
    end

  rescue StandardError => e
    R::Support.stop_capture.call(@@con) if (@@exec_counter == 0)
    raise e
  end

  res
  
end

.exec_function_i(function, *args) ⇒ Object


Executes the given R function with the given arguments.




276
277
278
279
# File 'lib/R_interface/rsupport.rb', line 276

def self.exec_function_i(function, *args)
  pl = R::Support.parse2list(*args)
  R::Support.eval("invoke").call(function, pl)
end

.exec_function_name(function_name, *args) ⇒ Object



Parameters:

  • function_name (String)

    Name of the R function to execute



265
266
267
268
269
270
# File 'lib/R_interface/rsupport.rb', line 265

def self.exec_function_name(function_name, *args)
  # @TODO: should check all that can go wrong when calling eval(function_name) to
  # raise the proper exception      
  f = R::Support.eval(function_name)
  R::Support.exec_function(f, *args)
end

.interop(object) ⇒ Object



Parameters:

  • object (Object)

    Any Ruby object

Returns:

  • boolean if the object is an interop or not



99
100
101
# File 'lib/R_interface/rsupport.rb', line 99

def self.interop(object)
  Truffle::Interop.foreign?(object)
end

.md_indexObject


multi-dimensional indexing




93
94
95
96
97
98
99
# File 'lib/R_interface/r_methods.rb', line 93

def self.md_index
  Polyglot.eval("R", <<-R)
    function(mdobject, ...) { 
      mdobject[...];
    }
  R
end

.new_scope(symbol, *args, &block) ⇒ Object



68
69
70
71
72
73
# File 'lib/R_interface/rsupport_scope.rb', line 68

def self.new_scope(symbol, *args, &block)
  executionScope = Scope.with(symbol, *args)
  scope = executionScope.new
  scope.instance_eval(&block)
  # scope
end

.parse2list(*args) ⇒ Object


Parses the Ruby arguments into a R list of R objects


Parameters:

  • args (Ruby Parameter list)


171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
# File 'lib/R_interface/rsupport.rb', line 171

def self.parse2list(*args)
  
  params = Polyglot.eval("R", "list()")
  
  args.each_with_index do |arg, i|
    if (arg.is_a? Hash)
      params = parse_hash(arg, params)
    elsif (arg.is_a? NotAvailable)
      params = R::Support.eval("c").call(params, arg.r_interop)
    elsif (arg.is_a? NilClass)
      params = R::Support.eval("`length<-`").call(params, i+1)
    else
      params = R::Support.eval("`[[<-`").
                 call(params, i+1, R::Support.parse_arg(arg))
    end
  end
  
  # if the parameter is an empty list, then add the element NULL to the list
  (Polyglot.eval("R", "length").call(params) == 0) ?
    Polyglot.eval("R", "list").call(nil) : params
  
end

.parse_arg(arg) ⇒ Object


whatever needs it


Parameters:

  • arg (Object)

    A Ruby object to be converted to R to be used by an R function, or

Returns:

  • Object that can be used in R



109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
# File 'lib/R_interface/rsupport.rb', line 109

def self.parse_arg(arg)

  case(arg)
  when -> (arg) { interop(arg) }
    arg
  when R::Object
    arg.r_interop
  when Numeric
    R::Support.eval('c').call(arg)
  when NegRange
    final_value = (arg.exclude_end?)? (arg.last - 1) : arg.last
    R::Support.eval('seq').call(arg.first, final_value)
  when Range
    final_value = (arg.exclude_end?)? (arg.last - 1) : arg.last
    R::Support.eval('seq').call(arg.first, final_value)
  when :all
    R.empty_symbol
  when Symbol
    arg = R::Support.eval('as.name').call(arg.to_s.gsub(/__/,"."))
  when Proc, Method
    R::RubyCallback.build(arg)
  # This is a Ruby argument that will be automatically converted
  # by the low level Polyglot interface to R
  else 
    arg
  end

end

.parse_hash(hsh, lst) ⇒ Object


Given a hash, convert it to an R argument list




142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
# File 'lib/R_interface/rsupport.rb', line 142

def self.parse_hash(hsh, lst)

  hsh.each_pair do |key, value|
    k = key.to_s.gsub(/__/,".")
    
    case 
    when (value.is_a? NotAvailable)
      na_named = R::Support.eval("`names<-`").call(value.r_interop, k)
      lst = R::Support.eval("c").call(lst, na_named)
    when (k == 'all')
      lst = R::Support.eval("`[[<-`").
              call(lst, R::Support.eval('`+`').
                          call(R::Support.eval('length').call(lst), 1),
                   R::Support.parse_arg(value))
    else
      lst = R::Support.eval("`[[<-`").
              call(lst, k, R::Support.parse_arg(value))
    end
  end
  
  lst
  
end

Prints a foreign R interop pointer. Used for debug.




352
353
354
355
356
# File 'lib/R_interface/rsupport.rb', line 352

def self.print_foreign(interop)
  puts "===External value==="
  R::Support.eval("print").call(interop)
  puts "===end external value==="
end




337
338
339
340
341
342
343
344
345
346
# File 'lib/R_interface/rsupport.rb', line 337

def self.print_str(obj)
  
  lst = obj.as__list
  puts lst
=begin      
  (1..lst.length >> 0).each do |i|
    puts lst[[i]]
  end
=end      
end

.process_missing(symbol, internal, *args) ⇒ Object


Process the missing method it should not wrap the return value inside an R::Object it is not applied to an object


Parameters:

  • symbol (Symbol)
  • internal (Boolean)

    true if the method will return to an internal method, i.e.,

  • object (Ruby Object)

    the ruby object to which the method is applied, false if



314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
# File 'lib/R_interface/rsupport.rb', line 314

def self.process_missing(symbol, internal, *args)

  name = R::Support.convert_symbol2r(symbol)
  
  case name
  # when missing method has an '=' sign in it...
  when ->(x) { x =~ /(.*)=$/ }
    R::Support.set_symbol($1, *args)
  # when missing method is 'eval'... needs special treatment
  when "eval"
    R::Support.r_evaluate(*args)
  else
    function = R::Support.eval(name)
    internal ? R::Support.exec_function_i(function, *args) :
      R::Support.exec_function(function, *args)
  end

end

.r_evaluate(*args) ⇒ Object


Calls the R ‘eval’ function. This requires some special semantics R function ‘eval’ needs to be called in a special way, since it expects the second argument to be an environment. If the arguments are packed into a list, then there is no second argument and the function fails to use the second argument as environment




300
301
302
303
# File 'lib/R_interface/rsupport.rb', line 300

def self.r_evaluate(*args)
  r_args = args.map { |arg| R::Support.parse_arg(arg) }
  R::Object.build(R::Support.eval("eval").call(*r_args))
end

.rangeObject





134
135
136
137
138
139
140
141
142
143
144
145
146
# File 'lib/R_interface/r_methods.rb', line 134

def self.range
  Polyglot.eval("R", <<-R)
    function(x, y, neg = FALSE) {
      e1 = enexpr(x)
      e2 = enexpr(y)
      if (neg) {
        expr(-(!!e1:!!e2))
      } else {
        expr(!!e1:!!e2)
      }
    }
  R
end

.ruby_callback_methodObject


R function that returns another R function that calls back a Ruby Method or Proc Some R functions that receive a function as argument will test to see if their parameters is a function or a symbol, so the Ruby method needs to be wrapped inside an R function in order for it to pass this test.




108
109
110
111
112
113
114
115
116
# File 'lib/R_interface/r_methods.rb', line 108

def self.ruby_callback_method
  Polyglot.eval("R", <<-R)
    function(rb_method) {
      function(...) {
      rb_method(...)
      }
    }
  R
end

.set_symbol(name, *args) ⇒ Object


sets the R symbol <name> to the given value


Parameters:

  • name (String)

    String representation of the R symbol that needs to be assigned

  • args (Array)

    Array with one object to be set to the symbol



287
288
289
290
# File 'lib/R_interface/rsupport.rb', line 287

def self.set_symbol(name, *args)
  args << R::Support.eval("globalenv").call()
  R::Support.exec_function_name("assign", name, *args)
end

.start_captureObject



59
60
61
62
63
64
65
66
# File 'lib/R_interface/r_methods.rb', line 59

def self.start_capture
  Polyglot.eval("R", <<-R)
    function(cap_variable) {
      sink(con <- textConnection(cap_variable,"w"), split=FALSE, type = c("output"));
      con
    }
  R
end

.stop_captureObject



68
69
70
71
72
73
74
75
# File 'lib/R_interface/r_methods.rb', line 68

def self.stop_capture
  Polyglot.eval("R", <<-R)
    function(con) {
      sink();
      close(con);
    }
  R
end