Skip to content

Instantly share code, notes, and snippets.

@hmenke
Last active October 19, 2022 07:29
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save hmenke/2d57a3aa1d3d59a265f935dd4b384c36 to your computer and use it in GitHub Desktop.
Save hmenke/2d57a3aa1d3d59a265f935dd4b384c36 to your computer and use it in GitHub Desktop.
Call other scripting languages from LuaJIT via FFI
local ffi = assert(require("ffi"))
local SCM = assert(ffi.load("libguile-2.0.so"))
ffi.cdef[[
typedef struct scm_unused_struct *SCM;
void scm_init_guile (void);
SCM scm_c_eval_string (const char *expr);
SCM scm_c_lookup (const char *name);
SCM scm_variable_ref (SCM var);
SCM scm_object_to_string (SCM obj, SCM printer);
char *scm_to_locale_string (SCM str);
void scm_dynwind_free (void *mem);
]]
local guile_initialized = false
local guile = {}
function guile.init()
if not guile_initialized then
guile_initialized = true
SCM.scm_init_guile()
end
end
function guile.run(expr)
assert(guile_initialized,"Guile not initialized")
return assert(SCM.scm_c_eval_string(expr))
end
function guile.eval(expr)
assert(guile_initialized,"Guile not initialized")
local object = assert(SCM.scm_c_eval_string(expr))
local display = assert(SCM.scm_variable_ref(assert(SCM.scm_c_lookup("display"))))
local result = assert(SCM.scm_object_to_string(object, display))
local c_str = assert(SCM.scm_to_locale_string(result))
local str = ffi.string(c_str)
SCM.scm_dynwind_free(c_str)
return str
end
function guile.close()
-- no op
end
guile.init()
guile.run("(define (fact n) (if (< n 1) 1 (* n (fact (- n 1)))))")
print(guile.eval("fact"))
print(guile.eval("(fact 5)"))
guile.close()
local ffi = require("ffi")
local JULIA = ffi.load("julia", true)
ffi.cdef [[
// Types
typedef struct _jl_value_t jl_value_t;
// Initialization
void jl_init__threading(void);
void jl_init_with_image__threading(const char *julia_bindir,
const char *image_relative_path);
// Execution and conversion
jl_value_t *jl_eval_string(const char*);
const char *jl_string_ptr(jl_value_t *);
const char *jl_typeof_str(jl_value_t *);
]]
local julia_initialized = false
local julia = {}
function julia.init(rpath)
-- Initialize the Julia interpreter (singleton)
if not julia_initialized then
if rpath then
JULIA.jl_init_with_image__threading(rpath, "sys.so")
else
JULIA.jl_init__threading()
end
julia_initialized = true
end
end
function julia.run(expr)
assert(julia_initialized)
JULIA.jl_eval_string(expr)
end
function julia.eval(expr)
assert(julia_initialized)
local jlval, cstr, str
jlval = JULIA.jl_eval_string(expr)
-- First check that the datatype
if jlval ~= ffi.NULL then
cstr = JULIA.jl_typeof_str(jlval)
else
return nil
end
if cstr ~= ffi.NULL then
str = ffi.string(cstr)
else
return nil
end
-- if the datatype is string, convert to Lua string
if str == "String" then
cstr = JULIA.jl_string_ptr(jlval)
else
return nil
end
if cstr ~= ffi.NULL then
str = ffi.string(cstr)
else
return nil
end
return str
end
return julia
local ffi = assert(require("ffi"))
local LUA = assert(ffi.load("liblua5.3.so", true))
ffi.cdef[[
typedef struct lua_State lua_State;
lua_State *luaL_newstate();
void luaL_openlibs(lua_State *L);
void lua_close (lua_State *L);
int luaL_loadstring (lua_State *L, const char *s);
int lua_pcallk (lua_State *L, int nargs, int nresults, int msgh, int ctx, void * k);
const char *luaL_tolstring (lua_State *L, int idx, size_t *len);
int lua_gettop (lua_State *L);
void lua_settop (lua_State *L, int index);
]]
local lua_initialized = false
local lua = {}
local L
function lua.init()
if not lua_initialized then
L = LUA.luaL_newstate()
LUA.luaL_openlibs(L)
lua_initialized = true
end
end
function lua.run(expr)
if (LUA.luaL_loadstring(L, expr) ~= 0 or
LUA.lua_pcallk(L, 0, -1, 0, 0, nil) ~= 0) then
local err = ffi.string(LUA.luaL_tolstring(L, -1, nil))
LUA.lua_settop(L, 0)
error(err)
end
end
function lua.eval(expr)
local top = LUA.lua_gettop(L)
lua.run(expr)
top = LUA.lua_gettop(L) - top
values = {}
for n = 1, top do
table.insert(values, ffi.string(LUA.luaL_tolstring(L, n, nil)))
LUA.lua_settop(L, -2)
end
LUA.lua_settop(L, 0)
return unpack(values)
end
function lua.close()
LUA.lua_close(L)
end
lua.init()
lua.run[[print("Hello World")]]
print(lua.eval("return 'x', 2, {}"))
lua.close()
local ffi = assert(require("ffi"))
local PYTHON = assert(ffi.load("libpython3.8.so", true))
ffi.cdef[[
// Py_ssize_t is a signed integral type such that sizeof(Py_ssize_t) == sizeof(size_t).
typedef size_t Py_ssize_t;
typedef struct _object PyObject;
// Method definitions
typedef PyObject *(*PyCFunction)(PyObject *, PyObject *);
struct PyMethodDef {
const char *ml_name;
PyCFunction ml_meth;
int ml_flags;
const char *ml_doc;
};
typedef struct PyMethodDef PyMethodDef;
// Module definitions
PyObject* PyModule_New(const char *name);
int PyModule_AddFunctions(PyObject *module, PyMethodDef *functions);
// Argument processing
int PyArg_ParseTuple(PyObject *args, const char *format, ...);
PyObject* Py_BuildValue(const char *format, ...);
// Initialization
void Py_Initialize();
void Py_Finalize();
void PyErr_Print();
PyObject * PyEval_GetBuiltins();
// Reference counting
void Py_IncRef(PyObject *o);
void Py_DecRef(PyObject *o);
// Dictionary functions
PyObject * PyDict_New();
PyObject * PyDict_GetItemString(PyObject *, const char *);
int PyDict_SetItemString(PyObject *, const char *, PyObject *);
// Execution and conversion
PyObject * PyRun_String(const char *, int, PyObject *, PyObject *);
PyObject * PyObject_Str(PyObject *);
char * PyUnicode_AsUTF8(PyObject *);
]]
local python_initialized = false
local python = {}
local locals
local globals
local None
local texModule
function python.init()
-- Initialize the Python interpreter (singleton)
if not python_initialized then
PYTHON.Py_Initialize()
python_initialized = true
end
-- Allocate a dictionary for locals and globals
locals = PYTHON.PyDict_New()
globals = PYTHON.PyDict_New()
-- Import builtins into global (otherwise things like "print" are missing)
if (PYTHON.PyDict_GetItemString(globals, "__builtins__") == ffi.NULL) then
if (PYTHON.PyDict_SetItemString(globals, "__builtins__",
PYTHON.PyEval_GetBuiltins()) ~= 0) then
error("Could not import builtins!")
end
end
-- HACK: get a reference to None
None = PYTHON.Py_BuildValue("s", ffi.NULL)
-- Register new module
texModule = PYTHON.PyModule_New("tex")
PYTHON.PyDict_SetItemString(globals, "tex", texModule)
end
-- Bind Lua functions to the Python interpreter
function python.bind_function(name, f, doc)
local texMethods = ffi.new("PyMethodDef[?]", 2)
local METH_VARARGS = 0x0001
local function wrapper(self, args)
local str = ffi.new("char*[1]", ffi.NULL)
if PYTHON.PyArg_ParseTuple(args, "|s", str) == 0 then
return None
end
local value
if str[0] ~= ffi.NULL then
value = ffi.string(str[0])
end
local result = f(value)
if result and type(result) ~= "string" then
error("Can only return string or nil")
end
return PYTHON.Py_BuildValue("s", result or ffi.NULL)
end
texMethods[0].ml_name = name
texMethods[0].ml_meth = wrapper
texMethods[0].ml_flags = METH_VARARGS
texMethods[0].ml_doc = doc or ffi.NULL
-- Sentinel
texMethods[1].ml_name = ffi.NULL
texMethods[1].ml_meth = ffi.NULL
texMethods[1].ml_flags = 0
texMethods[1].ml_doc = ffi.NULL
PYTHON.PyModule_AddFunctions(texModule, texMethods)
end
function python.run(expr)
local Py_file_input = 257
local r = PYTHON.PyRun_String(expr,Py_file_input,globals,locals)
if r == ffi.NULL then
PYTHON.PyErr_Print()
error("Python error!")
end
PYTHON.Py_DecRef(r)
end
-- Evaluate a single statement and get the result
function python.eval(expr)
local Py_eval_input = 258
local r = PYTHON.PyRun_String(expr,Py_eval_input,globals,locals)
local value = nil
if r == ffi.NULL then
-- Check for errors
PYTHON.PyErr_Print()
error("Python error!")
else
local pystr = PYTHON.PyObject_Str(r)
local str = PYTHON.PyUnicode_AsUTF8(pystr)
value = ffi.string(str)
PYTHON.Py_DecRef(pystr)
end
PYTHON.Py_DecRef(r)
return value
end
function python.close()
PYTHON.Py_DecRef(locals)
PYTHON.Py_DecRef(globals)
PYTHON.Py_Finalize()
end
python.init()
python.bind_function("sprint", print)
python.bind_function("foo", function() return "Hello from Lua" end)
python.run[[
tex.sprint("Hello for Python")
print(tex.foo())
]]
python.run[[
from scipy.integrate import quad
def integrand(x, a, b):
return a*x**2 + b
]]
a = 2
b = 1
print(python.eval("quad(integrand, 0, 1, args=(" .. a .. "," .. b .. "))[0]"))
python.close()
local ffi = assert(require("ffi"))
local libR = assert(ffi.load("libR.so"))
local c_str = function(text)
local str = ffi.new("char[?]", #text + 1)
ffi.copy(str, text)
return str
end
ffi.cdef[[
// libc
int setenv(const char *name, const char *value, int overwrite);
// Types
typedef struct SEXPREC *SEXP;
typedef int R_len_t;
typedef int R_xlen_t;
/* PARSE_NULL will not be returned by R_ParseVector */
typedef enum {
PARSE_NULL,
PARSE_OK,
PARSE_INCOMPLETE,
PARSE_ERROR,
PARSE_EOF
} ParseStatus;
typedef enum { FALSE = 0, TRUE } Rboolean;
// Values
extern SEXP R_NilValue;
extern SEXP R_GlobalEnv;
// Functions
int Rf_initEmbeddedR(int argc, char *argv[]);
void Rf_endEmbeddedR(int fatal);
SEXP Rf_protect(SEXP);
SEXP Rf_mkString(const char *);
SEXP R_ParseVector(SEXP, int, ParseStatus *, SEXP);
R_len_t Rf_length(SEXP);
SEXP R_tryEval(SEXP, SEXP, int *);
SEXP (VECTOR_ELT)(SEXP x, R_xlen_t i);
SEXP Rf_asChar(SEXP);
const char *(R_CHAR)(SEXP x);
]]
local R_initialized = false
local R = {}
function R.init()
if not R_initialized then
R_initialized = true
local argc = 3
local argv = ffi.new("char*[?]",argc)
argv[0] = c_str("R")
argv[1] = c_str("--no-save")
argv[2] = c_str("--quiet")
ffi.C.setenv("R_HOME", "/usr/lib/R", 1)
libR.Rf_initEmbeddedR(argc, argv)
end
end
function R.close()
if R_initialized then
libR.Rf_endEmbeddedR(0)
R_initialized = true
end
end
function R.eval(expr)
assert(R_initialized, "R interpreter is not initialized")
local cmdSexp = libR.Rf_protect(libR.Rf_mkString(c_str(expr)))
local status = ffi.new("ParseStatus[1]")
cmdexpr = libR.Rf_protect(libR.R_ParseVector(cmdSexp, -1, status, libR.R_NilValue));
local result = {}
if status[0] == libR.PARSE_OK then
for i = 0, libR.Rf_length(cmdexpr)-1 do
local errorOccurred = ffi.new("int[1]")
local ans = libR.R_tryEval(libR.VECTOR_ELT(cmdexpr, i), R_GlobalEnv, errorOccurred)
if errorOccurred[0] ~= 0 then
error("An error has occurred during evaluation")
else
result[#result+1] = ffi.string(libR.R_CHAR(libR.Rf_asChar(ans)))
end
end
else
error("An error has occurred during parsing!")
end
return result
end
-- Test
R.init()
local expr = arg[1] or error("No expression!")
local res = R.eval(expr)
for n, v in ipairs(res) do
print("Result[" .. n .. "]: " .. v)
end
R.close()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment