Skip to content

Instantly share code, notes, and snippets.

@Youka
Created December 30, 2014 11:25
Show Gist options
  • Save Youka/41f2be9ce51c9703b845 to your computer and use it in GitHub Desktop.
Save Youka/41f2be9ce51c9703b845 to your computer and use it in GitHub Desktop.
Execute & bind Ruby by LuaJIT
-- Load foreign functions interface
local ffi = require("ffi")
-- Load Ruby library & define function headers
local ruby = ffi.load("msvcrt-ruby210")
ffi.cdef([[
typedef uintptr_t RUBY_VALUE;
enum{
RUBY_Qfalse = 0,
RUBY_Qtrue = 2,
RUBY_Qnil = 4
};
enum{
RUBY_T_STRING = 0x05,
RUBY_T_MASK = 0x1f,
RUBY_IMMEDIATE_MASK = 0x03,
RUBY_RSTRING_NOEMBED = 1 << 13
};
typedef struct{
RUBY_VALUE flags;
const RUBY_VALUE klass;
}RBasic;
typedef struct{
RBasic basic;
union{
struct{
long len;
char *ptr;
}heap;
char ary[sizeof(RUBY_VALUE)*3/sizeof(char)];
}as;
}RString;
int ruby_setup(void);
RUBY_VALUE rb_define_module(const char*);
void rb_define_module_function(RUBY_VALUE, const char*, void*, int);
void* ruby_options(int, char**);
int ruby_run_node(void*);
]])
-- Test data
local args = {"-v", "test.rb"}
-- Lua load in Ruby
local function eval_str(mod, x)
local band = bit.band
if x == ffi.C.RUBY_Qnil or x == ffi.C.RUBY_Qfalse or
band(x, ffi.C.RUBY_IMMEDIATE_MASK) ~= 0 or
band(ffi.cast("RBasic*", x)[0].flags, ffi.C.RUBY_T_MASK) ~= ffi.C.RUBY_T_STRING then
error("String expected", 1)
end
local code = band(ffi.cast("RBasic*", x)[0].flags, ffi.C.RUBY_RSTRING_NOEMBED) ~= 0 and
load(ffi.string(ffi.cast("RString*", x)[0].as.heap.ptr)) or
load(ffi.string(ffi.cast("RString*", x)[0].as.ary))
return code and (code() and ffi.C.RUBY_Qtrue or ffi.C.RUBY_Qfalse) or ffi.C.RUBY_Qnil
end
-- Initialize Ruby
if ruby.ruby_setup() == 0 then
-- Extend Ruby environment
local ruby_module_lua = ruby.rb_define_module("Lua")
ruby.rb_define_module_function(ruby_module_lua, "eval_str", ffi.cast("RUBY_VALUE(*)(RUBY_VALUE, RUBY_VALUE)", eval_str), 1)
-- Build Ruby options
local args_n = #args
local options = ffi.new("char*[?]", 1 + args_n)
options[0] = ffi.cast("char*", "rubylua")
for i=1, args_n do
options[i] = ffi.cast("char*", args[i])
end
-- Run Ruby with options
local status = ruby.ruby_run_node(ruby.ruby_options(1 + args_n, options))
if status ~= 0 then
error("Ruby error code: " .. status)
end
else
error("Couldn't setup Ruby!")
end
puts Lua::eval_str "print(\"Hello from Lua in Ruby!\")"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment