Last active
October 19, 2022 07:29
-
-
Save hmenke/2d57a3aa1d3d59a265f935dd4b384c36 to your computer and use it in GitHub Desktop.
Call other scripting languages from LuaJIT via FFI
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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