Skip to content

Instantly share code, notes, and snippets.

@LincolnBryant
Last active November 10, 2020 02:04
Show Gist options
  • Save LincolnBryant/c3c2734d928f0c657a7d15d81a268149 to your computer and use it in GitHub Desktop.
Save LincolnBryant/c3c2734d928f0c657a7d15d81a268149 to your computer and use it in GitHub Desktop.
Creating Erlang interface functions for Lua via Luerl

Creating Erlang interface functions for Lua

It is possible to create Erlang interface functions to expose an API for your application to Lua.

In this example we will create an Erlang function to calculate the factorial of a number, and then call it from our Lua code.

Using rebar3 we will first create a new application

$ rebar3 new app factorialua
===> Writing factorialua/src/factorialua_app.erl
===> Writing factorialua/src/factorialua_sup.erl
===> Writing factorialua/src/factorialua.app.src
===> Writing factorialua/rebar.config
===> Writing factorialua/.gitignore
===> Writing factorialua/LICENSE
===> Writing factorialua/README.md

In the factorialua directory, you can modify rebar.config with your favorite editor and add Luerl as a dependency. Here we use the develop branch, but you may use other branches at your discretion.

$ cat rebar.config
{erl_opts, [debug_info]}.
{deps, [{luerl, {git, "https://github.com/rvirding/luerl.git", {branch, "develop"}}}]}.

{shell, [
  % {config, "config/sys.config"},
    {apps, [factorialua]}
]}.

Running rebar3 get-deps ought to fetch the dependency and configure your Erlang paths appropriately.

Next we will create a module with our factorial function, and export an install function for Luerl. This module should live in the application's src directory, e.g. factorialua/src/fac.erl

-module(fac).

-include_lib("luerl/src/luerl.hrl").

-import(luerl_lib, [lua_error/2,badarg_error/3]).

-export([install/1]).

install(St0) ->
    luerl_heap:alloc_table(table(), St0).

table() ->
    [{<<"fac">>,#erl_func{code=fun fac/2}}].

fac(As, St) ->
    case get_number_args(As) of
        [N|_] when is_number(N) -> {[fac(N)], St};
        _ -> badarg_error(fac, As, St)
    end.

get_number_args(As) ->
    lists:map(fun luerl_lib:arg_to_number/1, As).

fac(0) -> 1;
fac(N) -> N * fac(N-1).

Now that the fac module is in place, we will create another module that loads the factorial function into Lua and executes the function from Lua.

-module(run_fac).
-export([run/0]).

run() ->
    St0 = luerl:init(),
    St1 = load_lib(<<"factorialua">>,fac,St0),

    % Calculate the factorial of 10
    {[Return], St2} = luerl:do("return factorialua.fac(10)", St1),
    Return.

load_lib(Key, Mod, St0) ->
    {Tab,St1} = Mod:install(St0),
    %% Add key to global and to package.loaded.
    St2 = luerl_emul:set_global_key(Key, Tab, St1),
    luerl_emul:set_table_keys([<<"package">>,<<"loaded">>,Key], Tab, St2).

Finally start an Erlang shell and see the code in action:

1> run_fac:run().
3628800
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment