Skip to contents

luajr allows you to run Lua code from R.

Lua is a lightweight, simple, and fast scripting language that is used in a variety of settings. The standard Lua interpreter is already reasonably fast, but there is also a just-in-time compiler for Lua called LuaJIT that is even faster. luajr uses LuaJIT.

This is not a guide to Lua or LuaJIT; it is a quick-start guide to luajr for people who already know how to program in Lua. See the Lua web site for resources related to coding in Lua, and the LuaJIT web site for resources related to LuaJIT itself.

Running Lua code: lua() and lua_shell()

To get a feel for luajr or to run “one-off” Lua code from your R project, use lua() and lua_shell().

When you pass a character string to lua(), it is run as Lua code:

lua("return 'Hello ' .. 'world!'")
#> [1] "Hello world!"

Assignments to global variables will persist between calls to lua():

lua("my_animal = 'walrus'")
lua("return my_animal")
#> [1] "walrus"

This is because luajr maintains a “default Lua state” which holds all global variables. This default Lua state is opened the first time a package function is used. You can create your own, separate Lua states, or reset the default Lua state (see Lua states, below).

Assignments to local variables will not persist between calls to lua():

lua("local my_animal = 'donkey'")
lua("return my_animal")
#> [1] "walrus"

In this case, the second line returns "walrus" because the local variable my_animal goes out of scope after the first call to lua() ends, so the second call to lua() is referring back to the global variable my_animal from before.

You can include more than one statement in the code run by lua():

lua("local my_veg = 'potato'; local my_dish = my_veg .. ' pie'; return my_dish")
#> [1] "potato pie"

You can also use the filename argument to lua() to load and run a Lua source file, instead of running the contents of a string.

Call lua_shell() to open an interactive Lua shell at the R prompt. This can be helpful for debugging or for testing Lua statements.

Calling Lua functions from R: lua_func()

The key piece of functionality for luajr is probably lua_func(). This allows you to call Lua functions from R.

The first argument to lua_func(), func, is a string that should evaluate to a Lua function. lua_func() then returns an R function that can be used to call that Lua function from R. For example, you can use lua_func() to access an existing Lua function from R:

luaprint = lua_func("print")
luaprint("Hello, world")
#> NULL

Here, "print" is just referring to the built-in Lua function print which prints a Lua value to the console. You can also use lua_func() to refer to a previously defined function in the default Lua state:

lua("function excited_print(x) print(x .. '!') end")
lua("return excited_print('Hello, world')")

xp = lua_func("excited_print", "native")
xp("Wow")
#> NULL

Or you can use lua_func() to define an anonymous Lua function:

timestwo = lua_func("function(x) return x*2 end", "native")
timestwo(123)
#> [1] 246

Under the hood, lua_func() just takes its first parameter (a string), adds "return " to the front of it, executes it as Lua code, and registers the result as the function.

The second argument to lua_func(), argcode, is also very important. argcode determines how the arguments passed to the function from R are translated into Lua variables for use inside the function.

The most important argcodes are "auto" and "native".

The argcode "auto" means that the parameter should be passed as a luajr type providing direct access to the R variable passed in. For example, with the argcode "auto", an R numeric vector is passed as a luajr.numeric type, an R character vector is passed as a luajr.character type, and an R function is passed as a luajr.rfunction type. For a guide to luajr types, see the vignette The luajr.lua module

The argcode "native" means that the R value passed in should be converted to a native Lua type. For example, with the argcode "native", a single number in R like 2.5 is passed as a Lua number, a single string like "foo" is passed as a Lua string, and a list like list(a = 1, b = 2) or a vector like 10:12 is passed as a Lua table.

For example, suppose you want to pass the R numeric vector c(1.1, 3.3, 2.2) into a Lua function from which you will return the biggest number in the vector. If you want the numeric vector to come into your function as a luajr.numeric type, you could write:

x = c(1.1, 3.3, 2.2)
my_max = lua_func("
function(x)
    local max = nil
    for i = 1,#x do
        if max == nil or x[i] > max then
            max = x[i]
        end
    end
    return max
end", "auto")
my_max(x)

If you want the numeric vector to come into your function as a Lua table, you could write:

x = c(1.1, 3.3, 2.2)
my_max = lua_func("
function(x)
    return math.max(unpack(x))
end", "native")
my_max(x)

where the above example takes advantage of Lua’s built-in math.max function that returns the biggest element from among all the arguments passed in.

The permissible arg codes are:

Arg code Short form Accepted R types Resulting Lua type
auto . any
sexp S any R.sexp
rfunction F function luajr.rfunction
environment E environment luajr.environment
logical L logical luajr.logical
integer I integer, numeric (coerced) luajr.integer
numeric N numeric, integer (coerced) luajr.numeric
character C character luajr.character
vector, list V list luajr.list
pointer P external pointer light userdata
native $. any native-compatible type
function $F function or external pointer to Lua function function
boolean $L length-1 logical boolean
number $N length-1 integer or numeric number
string $C length-1 character string
table $V list or atomic vector table

Arg codes should be separated from each other by a comma, semicolon, or space within the string. “Short form” arg codes do not need to be separated. For example, "S$L" is equivalent to "sexp, boolean".

Above, the $ prefix specifies “native type”.

There is also the ! prefix, which can be used to specify “strict” mode. In “strict” mode, there is no coercion between integer and numeric types, and NULL yields an error instead of nil when a Lua native type is requested.

The & prefix can be used to specify that an R vector type should be passed by reference. When a vector (logical vector, integer vector, numeric vector, character vector, or list) is passed from R to Lua by reference, modifications made to the elements of that passed-in vector persist back in the R calling frame. For example:

values = c(1.0, 2.0, 3.0)
keep = lua_func("function(x) x[1] = 999 end", ".") # passed by value
keep(values)
#> NULL
print(values)
#> [1] 1 2 3

change = lua_func("function(x) x[1] = 999 end", "&.") # passed by reference
change(values)
#> NULL
print(values)
#> [1] 999   2   3

Vectors can never be resized by reference; only their already-existing elements can be changed by reference.

Working with Lua States: lua_open(), lua_reset()

All the functions mentioned above (lua(), lua_shell(), and lua_func()) can also take an argument L that specifies a particular Lua state that the function operates in.

When L = NULL (the default) the functions operate on the default Lua state.

But you can also open alternative Lua states using lua_open(), and then by passing the result as the parameter L, specify that the function operates in that specific state. For example:

L1 = lua_open()
lua("a = 2")
lua("a = 4", L = L1)
lua("return a")
#> [1] 2
lua("return a", L = L1)
#> [1] 4

There is no lua_close in luajr because Lua states are closed automatically when they are garbage collected in R.

lua_reset() resets the default Lua state:

lua("a = 2")
lua("return a")
#> [1] 2
lua_reset()
lua("return a")
#> NULL

To reset a non-default Lua state L returned by lua_open(), just do L = lua_open() again. The memory previously used by L will be cleaned up at the next garbage collection.

Further reading

For notes on how to create and manipulate R objects – such as vectors, lists, and data.frames – in your Lua code, see the luajr.lua module vignette.