View Source Lua (Lua v0.0.8)
Lua is an ergonomic interface to Luerl, aiming to be the best way to use Luerl from Elixir.
Features
- Ergonomic API for Elixir <> Lua FFI
- Improved error messages
- Deep-setting variables and state
- Excellent documentation and guides for working with Luerl
Executing Lua
Lua can be run using the eval!/2
function
iex> {[4], _} =
...> Lua.eval!("""
...> return 2 + 2
...> """)
Exposing Elixir functions to Lua
Lua
provides the deflua
macro for exposing Elixir functions to Lua
defmodule MyAPI do
use Lua.API
deflua double(v), do: 2 * v
end
lua = Lua.new() |> Lua.load_api(MyAPI)
{[10], _} =
Lua.eval!(lua, """
return double(5)
""")
Calling Lua functions from Elixir
Lua can be used to expose complex functions written in Elixir. In some cases, you may want to call Lua functions from Elixir. This can
be achieved with the Lua.call_function!/3
function
defmodule MyAPI do
use Lua.API, scope: "example"
deflua foo(value), state do
Lua.call_function!(state, [:string, :lower], [value])
end
end
lua = Lua.new() |> Lua.load_api(MyAPI)
{["wow"], _} = Lua.eval!(lua, "return example.foo(\"WOW\")")
Modify Lua state from Elixir
You can also use Lua
to modify the state of the lua environment inside your Elixir code. Imagine you have a queue module that you
want to implement in Elixir, with the queue stored in a global variable
defmodule Queue do
use Lua.API, scope: "q"
deflua push(v), state do
# Pull out the global variable "my_queue" from lua
queue = Lua.get!(state, [:my_queue])
{[], state} = Lua.call_function!(state, [:table, :insert], [queue, v])
# Return the modified lua state with no return values
{[], state}
end
end
lua = Lua.new() |> Lua.load_api(Queue)
{[queue], _} =
Lua.eval!(lua, """
my_queue = {}
q.push("first")
q.push("second")
return my_queue
""")
["first", "second"] = Lua.Table.as_list(queue)
Credits
Lua piggy-backs off of Robert Virding's Luerl project, which implements an Lua lexer, parser, and full-blown lua virtual machine that runs inside the BEAM.
Summary
Functions
Calls a function in Lua's state
Encodes a Lua value into its internal form
Evalutes the script or chunk, returning the result and discarding side effects in the state
Gets a table value in Lua
Inject functions written with the deflua
macro into the Lua
runtime
Loads a lua file into the environment. Any values returned in the globa scope are thrown away.
Initializes a Lua VM sandbox. All library functions are stubbed out, so no access to the filesystem or the execution environment is exposed.
Sandboxes the given path, swapping out the implementation with a function that raises when called
Sets a table value in Lua. Nested keys will create intermediate tables
Sets the path patterns that Lua will look in when requiring Lua scripts.
Functions
Calls a function in Lua's state
iex> {[ret], _lua} = Lua.call_function!(Lua.new(), [:string, :lower], ["HELLO ROBERT"])
iex> ret
"hello robert"
References to functions can also be passed
iex> {[ref], lua} = Lua.eval!("return string.lower")
iex> {[ret], _lua} = Lua.call_function!(lua, ref, ["FUNCTION REF"])
iex> ret
"function ref"
This is also useful for executing Lua function's inside of Elixir APIs
defmodule MyAPI do
use Lua.API, scope: "example"
deflua foo(value), state do
Lua.call_function!(state, [:string, :lower], [value])
end
end
lua = Lua.new() |> Lua.load_api(MyAPI)
{["wow"], _} = Lua.eval!(lua, "return example.foo("WOW")")
Encodes a Lua value into its internal form
Evalutes the script or chunk, returning the result and discarding side effects in the state
Gets a table value in Lua
iex> state = Lua.set!(Lua.new(), [:hello], "world")
iex> Lua.get!(state, [:hello])
"world"
When a value doesn't exist, it returns nil
iex> Lua.get!(Lua.new(), [:nope])
nil
It can also get nested values
iex> state = Lua.set!(Lua.new(), [:a, :b, :c], "nested")
iex> Lua.get!(state, [:a, :b, :c])
"nested"
Inject functions written with the deflua
macro into the Lua
runtime
Loads a lua file into the environment. Any values returned in the globa scope are thrown away.
Mimics the functionality of lua's dofile
Initializes a Lua VM sandbox. All library functions are stubbed out, so no access to the filesystem or the execution environment is exposed.
Options
:sandboxed
- list of paths to be sandboxed, e.g.sandboxed: [[:require], [:os, :exit]]
Sandboxes the given path, swapping out the implementation with a function that raises when called
iex> lua = Lua.new(sandboxed: [])
iex> Lua.sandbox(lua, [:os, :exit])
Sets a table value in Lua. Nested keys will create intermediate tables
iex> Lua.set!(Lua.new(), [:hello], "World")
It can also set nested values
iex> Lua.set!(Lua.new(), [:a, :b, :c], [])
These table values are availble in lua scripts
iex> lua = Lua.set!(Lua.new(), [:a, :b, :c], "nested!")
iex> {result, _} = Lua.eval!(lua, "return a.b.c")
iex> result
["nested!"]
Sets the path patterns that Lua will look in when requiring Lua scripts.