Current section
Files
Jump to
Current section
Files
src/glua.erl
-module(glua).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/glua.gleam").
-export([nil/1, table_decoder/2, list/3, string/2, bool/2, int/2, float/2, table/3, new/0, function/2, get/3, get_private/3, ref_get/2, set/3, set_api/3, set_lua_paths/2, set_private/3, delete_private/2, load/2, load_file/2, eval/3, ref_eval/2, eval_chunk/3, ref_eval_chunk/2, eval_file/3, ref_eval_file/2, call_function/4, ref_call_function/3, call_function_by_name/4, ref_call_function_by_name/3]).
-export_type([lua/0, lua_error/0, lua_runtime_exception_kind/0, chunk/0, value/0, value_ref/0]).
-if(?OTP_RELEASE >= 27).
-define(MODULEDOC(Str), -moduledoc(Str)).
-define(DOC(Str), -doc(Str)).
-else.
-define(MODULEDOC(Str), -compile([])).
-define(DOC(Str), -compile([])).
-endif.
?MODULEDOC(
" A library to embed Lua in Gleam programs.\n"
"\n"
" Gleam wrapper around [Luerl](https://github.com/rvirding/luerl).\n"
).
-type lua() :: any().
-type lua_error() :: {lua_compiler_exception, list(binary())} |
{lua_runtime_exception, lua_runtime_exception_kind(), lua()} |
key_not_found |
{unexpected_result_type, list(gleam@dynamic@decode:decode_error())} |
unknown_error.
-type lua_runtime_exception_kind() :: {illegal_index, binary(), binary()} |
{error_call, list(binary())} |
{undefined_function, binary()} |
{bad_arith, binary(), list(binary())} |
{assert_error, binary()} |
unknown_exception.
-type chunk() :: any().
-type value() :: any().
-type value_ref() :: any().
-file("src/glua.gleam", 59).
-spec nil(lua()) -> {lua(), value()}.
nil(Lua) ->
glua_ffi:lua_nil(Lua).
-file("src/glua.gleam", 93).
-spec table_decoder(
gleam@dynamic@decode:decoder(DQQ),
gleam@dynamic@decode:decoder(DQS)
) -> gleam@dynamic@decode:decoder(list({DQQ, DQS})).
table_decoder(Keys_decoder, Values_decoder) ->
Inner = begin
gleam@dynamic@decode:field(
0,
Keys_decoder,
fun(Key) ->
gleam@dynamic@decode:field(
1,
Values_decoder,
fun(Val) -> gleam@dynamic@decode:success({Key, Val}) end
)
end
)
end,
gleam@dynamic@decode:list(Inner).
-file("src/glua.gleam", 116).
-spec list(lua(), fun((lua(), DQY) -> {lua(), value()}), list(DQY)) -> {lua(),
list(value())}.
list(Lua, Encoder, Values) ->
gleam@list:map_fold(Values, Lua, Encoder).
-file("src/glua.gleam", 61).
-spec string(lua(), binary()) -> {lua(), value()}.
string(Lua, V) ->
glua_ffi:encode(Lua, V).
-file("src/glua.gleam", 65).
-spec bool(lua(), boolean()) -> {lua(), value()}.
bool(Lua, V) ->
glua_ffi:encode(Lua, V).
-file("src/glua.gleam", 69).
-spec int(lua(), integer()) -> {lua(), value()}.
int(Lua, V) ->
glua_ffi:encode(Lua, V).
-file("src/glua.gleam", 73).
-spec float(lua(), float()) -> {lua(), value()}.
float(Lua, V) ->
glua_ffi:encode(Lua, V).
-file("src/glua.gleam", 77).
-spec table(
lua(),
{fun((lua(), DQN) -> {lua(), value()}),
fun((lua(), DQO) -> {lua(), value()})},
list({DQN, DQO})
) -> {lua(), value()}.
table(Lua, Encoders, Values) ->
{Key_encoder, Value_encoder} = Encoders,
{Lua@4, Values@1} = gleam@list:map_fold(
Values,
Lua,
fun(Lua@1, Pair) ->
{Lua@2, K} = Key_encoder(Lua@1, erlang:element(1, Pair)),
{Lua@3, V} = Value_encoder(Lua@2, erlang:element(2, Pair)),
{Lua@3, {K, V}}
end
),
glua_ffi:encode(Lua@4, Values@1).
-file("src/glua.gleam", 129).
?DOC(" Creates a new Lua VM instance\n").
-spec new() -> lua().
new() ->
luerl:init().
-file("src/glua.gleam", 106).
-spec function(
lua(),
fun((lua(), list(gleam@dynamic:dynamic_())) -> {lua(), list(value())})
) -> {lua(), value()}.
function(Lua, F) ->
Fun = fun(Args, Lua@1) -> _pipe = F(Lua@1, luerl:decode_list(Args, Lua@1)),
gleam@pair:swap(_pipe) end,
glua_ffi:encode(Lua, Fun).
-file("src/glua.gleam", 160).
?DOC(
" Gets a value in the Lua environment.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" glua.get(state: glua.new(), keys: [\"_VERSION\"], using: decode.string)\n"
" // -> Ok(\"Lua 5.3\")\n"
" ```\n"
"\n"
" ```gleam\n"
" let #(lua, encoded) = glua.new() |> glua.bool(True)\n"
" let assert Ok(lua) = glua.set(\n"
" state: lua,\n"
" keys: [\"my_table\", \"my_value\"],\n"
" value: encoded\n"
" )\n"
"\n"
" glua.get(\n"
" state: lua,\n"
" keys: [\"my_table\", \"my_value\"],\n"
" using: decode.bool\n"
" )\n"
" // -> Ok(True)\n"
" ```\n"
"\n"
" ```gleam\n"
" glua.get(state: glua.new(), keys: [\"non_existent\"], using: decode.string)\n"
" // -> Error(glua.KeyNotFound)\n"
" ```\n"
).
-spec get(lua(), list(binary()), gleam@dynamic@decode:decoder(DRD)) -> {ok, DRD} |
{error, lua_error()}.
get(Lua, Keys, Decoder) ->
gleam@result:'try'(
glua_ffi:get_table_keys_dec(Lua, Keys),
fun(Value) ->
gleam@result:'try'(
begin
_pipe = gleam@dynamic@decode:run(Value, Decoder),
gleam@result:map_error(
_pipe,
fun(Field@0) -> {unexpected_result_type, Field@0} end
)
end,
fun(Decoded) -> {ok, Decoded} end
)
end
).
-file("src/glua.gleam", 184).
?DOC(
" Gets a private value that is not exposed to the Lua runtime.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" assert glua.new()\n"
" |> glua.set_private(\"private_value\", \"secret_value\")\n"
" |> glua.get_private(\"private_value\", decode.string)\n"
" == Ok(\"secret_value\")\n"
" ```\n"
).
-spec get_private(lua(), binary(), gleam@dynamic@decode:decoder(DRH)) -> {ok,
DRH} |
{error, lua_error()}.
get_private(Lua, Key, Decoder) ->
gleam@result:'try'(
glua_ffi:get_private(Lua, Key),
fun(Value) ->
gleam@result:'try'(
begin
_pipe = gleam@dynamic@decode:run(Value, Decoder),
gleam@result:map_error(
_pipe,
fun(Field@0) -> {unexpected_result_type, Field@0} end
)
end,
fun(Decoded) -> {ok, Decoded} end
)
end
).
-file("src/glua.gleam", 198).
?DOC(" Same as `glua.get`, but returns a reference to the value instead of decoding it\n").
-spec ref_get(lua(), list(binary())) -> {ok, value_ref()} | {error, lua_error()}.
ref_get(Lua, Keys) ->
glua_ffi:get_table_keys(Lua, Keys).
-file("src/glua.gleam", 243).
?DOC(
" Sets a value in the Lua environment.\n"
"\n"
" All nested keys will be created as intermediate tables.\n"
"\n"
" If successfull, this function will return the updated Lua state\n"
" and the setted value will be available in Lua scripts.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" let #(lua, encoded) = glua.new() |> glua.int(10)\n"
" let assert Ok(lua) = glua.set(\n"
" state: lua,\n"
" keys: [\"my_number\"],\n"
" value: encoded\n"
" )\n"
"\n"
" glua.get(state: lua, keys: [\"my_number\"], using: decode.int)\n"
" // -> Ok(10)\n"
" ```\n"
"\n"
" ```gleam\n"
" let emails = [\"jhondoe@example.com\", \"lucy@example.com\"]\n"
" let #(lua, encoded) = glua.new() |> glua.list(glua.string, emails)\n"
" let assert Ok(lua) = glua.set(\n"
" state: lua,\n"
" keys: [\"info\", \"emails\"],\n"
" value: encoded\n"
" )\n"
"\n"
" let assert Ok(#(_, results)) = glua.eval(\n"
" state: lua,\n"
" code: \"return info.emails\",\n"
" using: decode.string\n"
" )\n"
"\n"
" assert results == emails\n"
" ```\n"
).
-spec set(lua(), list(binary()), value()) -> {ok, lua()} | {error, lua_error()}.
set(Lua, Keys, Val) ->
State = begin
gleam@list:try_fold(
Keys,
{[], Lua},
fun(Acc, Key) ->
{Keys@1, Lua@1} = Acc,
Keys@2 = lists:append(Keys@1, [Key]),
case glua_ffi:get_table_keys(Lua@1, Keys@2) of
{ok, _} ->
{ok, {Keys@2, Lua@1}};
{error, key_not_found} ->
{Tbl, Lua@2} = luerl_emul:alloc_table([], Lua@1),
_pipe = glua_ffi:set_table_keys(Lua@2, Keys@2, Tbl),
gleam@result:map(
_pipe,
fun(Lua@3) -> {Keys@2, Lua@3} end
);
{error, E} ->
{error, E}
end
end
)
end,
gleam@result:'try'(
State,
fun(_use0) ->
{Keys@3, Lua@4} = _use0,
glua_ffi:set_table_keys(Lua@4, Keys@3, Val)
end
).
-file("src/glua.gleam", 283).
?DOC(" Sets a group of values under a particular table in the Lua environment.\n").
-spec set_api(lua(), list(binary()), list({binary(), value()})) -> {ok, lua()} |
{error, lua_error()}.
set_api(Lua, Keys, Values) ->
gleam@list:try_fold(
Values,
Lua,
fun(State, _use1) ->
{Key, Val} = _use1,
set(State, lists:append(Keys, [Key]), Val)
end
).
-file("src/glua.gleam", 311).
?DOC(
" Sets the paths where the Lua runtime will look when requiring other Lua files.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" let my_scripts_paths = [\"app/scripts/lua/?.lua\"]\n"
" let assert Ok(state) = glua.set_lua_paths(\n"
" state: glua.new(),\n"
" paths: my_scripts_paths\n"
" )\n"
"\n"
" let assert Ok(#(_, [result])) = glua.eval(\n"
" state:,\n"
" code: \"local my_math = require 'my_script'; return my_math.square(3)\"\n"
" using: decode.int\n"
" )\n"
"\n"
" assert result = 9\n"
" ```\n"
).
-spec set_lua_paths(lua(), list(binary())) -> {ok, lua()} | {error, lua_error()}.
set_lua_paths(Lua, Paths) ->
{Lua@1, Paths@1} = string(Lua, gleam@string:join(Paths, <<";"/utf8>>)),
set(Lua@1, [<<"package"/utf8>>, <<"path"/utf8>>], Paths@1).
-file("src/glua.gleam", 278).
?DOC(
" Sets a value that is not exposed to the Lua runtime and can only be accessed from Gleam.\n"
"\n"
" ## Examples\n"
" ```gleam\n"
" assert glua.new()\n"
" |> glua.set(\"secret_value\", \"private_value\")\n"
" |> glua.get(\"secret_value\")\n"
" == Ok(\"secret_value\")\n"
" ```\n"
).
-spec set_private(lua(), binary(), any()) -> lua().
set_private(Lua, Key, Value) ->
luerl:put_private(Key, Value, Lua).
-file("src/glua.gleam", 352).
?DOC(
" Remove a private value that is not exposed to the Lua runtime. \n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" let lua = glua.set_private(glua.new(), \"my_value\", \"will_be_removed\"\n"
" assert glua.get(lua, \"my_value\", decode.string) == Ok(\"will_be_removed\")\n"
"\n"
" assert glua.delete_private(lua, \"my_value\")\n"
" |> glua.get(\"my_value\", decode.string)\n"
" == Error(glua.KeyNotFound)\n"
" ```\n"
).
-spec delete_private(lua(), binary()) -> lua().
delete_private(Lua, Key) ->
luerl:delete_private(Key, Lua).
-file("src/glua.gleam", 362).
?DOC(
" Parses a string of Lua code and returns it as a compiled chunk.\n"
"\n"
" To eval the returned chunk, use `glua.eval_chunk` or `glua.ref_eval_chunk`.\n"
).
-spec load(lua(), binary()) -> {ok, {lua(), chunk()}} | {error, lua_error()}.
load(Lua, Code) ->
glua_ffi:load(Lua, Code).
-file("src/glua.gleam", 375).
?DOC(
" Parses a Lua source file and returns it as a compiled chunk.\n"
"\n"
" To eval the returned chunk, use `glua.eval_chunk` or `glua.ref_eval_chunk`.\n"
).
-spec load_file(lua(), binary()) -> {ok, {lua(), chunk()}} |
{error, lua_error()}.
load_file(Lua, Path) ->
glua_ffi:load_file(Lua, Path).
-file("src/glua.gleam", 429).
?DOC(
" Evaluates a string of Lua code.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" let assert Ok(#(_, results)) = glua.eval(\n"
" state: glua.new(),\n"
" code: \"return 1 + 2\",\n"
" using: decode.int\n"
" )\n"
" assert results == [3]\n"
" ```\n"
"\n"
" ```gleam\n"
" let my_decoder = decode.one_of(decode.string, or: [\n"
" decode.int |> decode.map(int.to_string)\n"
" ])\n"
"\n"
" let assert Ok(#(_, results)) = glua.eval(\n"
" state: glua.new(),\n"
" code: \"return 'hello, world!', 10\",\n"
" using: my_decoder\n"
" )\n"
" assert results == [\"hello, world!\", \"10\"]\n"
" ```\n"
"\n"
" ```gleam\n"
" glua.eval(state: glua.new(), code: \"return 1 * \", using: decode.int)\n"
" // -> Error(glua.LuaCompilerException(\n"
" messages: [\"syntax error before: \", \"1\"]\n"
" ))\n"
" ```\n"
"\n"
" ```gleam\n"
" glua.eval(state: glua.new(), code: \"return 'Hello, world!'\", using: decode.int)\n"
" // -> Error(glua.UnexpectedResultType(\n"
" [decode.DecodeError(\"Int\", \"String\", [])]\n"
" ))\n"
" ```\n"
"\n"
" > **Note**: If you are evaluating the same piece of code multiple times,\n"
" > instead of calling `glua.eval` repeatly it is recommended to first convert\n"
" > the code to a chunk by passing it to `glua.load`, and then\n"
" > evaluate that chunk using `glua.eval_chunk` or `glua.ref_eval_chunk`.\n"
).
-spec eval(lua(), binary(), gleam@dynamic@decode:decoder(DSZ)) -> {ok,
{lua(), list(DSZ)}} |
{error, lua_error()}.
eval(Lua, Code, Decoder) ->
gleam@result:'try'(
glua_ffi:eval_dec(Lua, Code),
fun(_use0) ->
{Lua@1, Ret} = _use0,
gleam@result:'try'(
begin
_pipe = gleam@list:try_map(
Ret,
fun(_capture) ->
gleam@dynamic@decode:run(_capture, Decoder)
end
),
gleam@result:map_error(
_pipe,
fun(Field@0) -> {unexpected_result_type, Field@0} end
)
end,
fun(Decoded) -> {ok, {Lua@1, Decoded}} end
)
end
).
-file("src/glua.gleam", 450).
?DOC(" Same as `glua.eval`, but returns references to the values instead of decode them\n").
-spec ref_eval(lua(), binary()) -> {ok, {lua(), list(value_ref())}} |
{error, lua_error()}.
ref_eval(Lua, Code) ->
glua_ffi:eval(Lua, Code).
-file("src/glua.gleam", 480).
?DOC(
" Evaluates a compiled chunk of Lua code.\n"
"\n"
" ## Examples\n"
" ```gleam\n"
" let assert Ok(#(lua, chunk)) = glua.load(\n"
" state: glua.new(),\n"
" code: \"return 'hello, world!'\"\n"
" )\n"
"\n"
" let assert Ok(#(_, results)) = glua.eval_chunk(\n"
" state: lua,\n"
" chunk:,\n"
" using: decode.string\n"
" )\n"
"\n"
" assert results == [\"hello, world!\"]\n"
" ```\n"
).
-spec eval_chunk(lua(), chunk(), gleam@dynamic@decode:decoder(DTN)) -> {ok,
{lua(), list(DTN)}} |
{error, lua_error()}.
eval_chunk(Lua, Chunk, Decoder) ->
gleam@result:'try'(
glua_ffi:eval_chunk_dec(Lua, Chunk),
fun(_use0) ->
{Lua@1, Ret} = _use0,
gleam@result:'try'(
begin
_pipe = gleam@list:try_map(
Ret,
fun(_capture) ->
gleam@dynamic@decode:run(_capture, Decoder)
end
),
gleam@result:map_error(
_pipe,
fun(Field@0) -> {unexpected_result_type, Field@0} end
)
end,
fun(Decoded) -> {ok, {Lua@1, Decoded}} end
)
end
).
-file("src/glua.gleam", 501).
?DOC(" Same as `glua.eval_chunk`, but returns references to the values instead of decode them\n").
-spec ref_eval_chunk(lua(), chunk()) -> {ok, {lua(), list(value_ref())}} |
{error, lua_error()}.
ref_eval_chunk(Lua, Chunk) ->
glua_ffi:eval_chunk(Lua, Chunk).
-file("src/glua.gleam", 526).
?DOC(
" Evaluates a Lua source file.\n"
"\n"
" ## Examples\n"
" ```gleam\n"
" let assert Ok(#(_, results)) = glua.eval_file(\n"
" state: glua.new(),\n"
" path: \"path/to/hello.lua\",\n"
" using: decode.string\n"
" )\n"
"\n"
" assert results == [\"hello, world!\"]\n"
" ```\n"
).
-spec eval_file(lua(), binary(), gleam@dynamic@decode:decoder(DUB)) -> {ok,
{lua(), list(DUB)}} |
{error, lua_error()}.
eval_file(Lua, Path, Decoder) ->
gleam@result:'try'(
glua_ffi:eval_file_dec(Lua, Path),
fun(_use0) ->
{Lua@1, Ret} = _use0,
gleam@result:'try'(
begin
_pipe = gleam@list:try_map(
Ret,
fun(_capture) ->
gleam@dynamic@decode:run(_capture, Decoder)
end
),
gleam@result:map_error(
_pipe,
fun(Field@0) -> {unexpected_result_type, Field@0} end
)
end,
fun(Decoded) -> {ok, {Lua@1, Decoded}} end
)
end
).
-file("src/glua.gleam", 547).
?DOC(" Same as `glua.eval_file`, but returns references to the values instead of decode them.\n").
-spec ref_eval_file(lua(), binary()) -> {ok, {lua(), list(value_ref())}} |
{error, lua_error()}.
ref_eval_file(Lua, Path) ->
glua_ffi:eval_file(Lua, Path).
-file("src/glua.gleam", 600).
?DOC(
" Calls a Lua function by reference.\n"
"\n"
" ## Examples\n"
" ```gleam\n"
" let assert Ok(#(lua, fun)) = glua.ref_eval(state: glua.new(), code: \"return math.sqrt\")\n"
"\n"
" let #(lua, encoded) = glua.int(lua, 81)\n"
" let assert Ok(#(_, [result])) = glua.call_function(\n"
" state: lua,\n"
" ref: fun,\n"
" args: [encoded],\n"
" using: decode.int\n"
" )\n"
"\n"
" assert result == 9\n"
" ```\n"
"\n"
" ```gleam\n"
" let code = \"function fib(n)\n"
" if n <= 1 then\n"
" return n\n"
" else\n"
" return fib(n - 1) + fib(n - 2)\n"
" end\n"
" end\n"
"\n"
" return fib\n"
" \"\n"
" let assert Ok(#(lua, fun)) = glua.ref_eval(state: glua.new(), code:)\n"
"\n"
" let #(lua, encoded) = glua.int(lua, 10)\n"
" let assert Ok(#(_, [result])) = glua.call_function(\n"
" state: lua,\n"
" ref: fun,\n"
" args: [encoded],\n"
" using: decode.int\n"
" )\n"
"\n"
" assert result == 55 \n"
" ```\n"
).
-spec call_function(
lua(),
value_ref(),
list(value()),
gleam@dynamic@decode:decoder(DUQ)
) -> {ok, {lua(), list(DUQ)}} | {error, lua_error()}.
call_function(Lua, Fun, Args, Decoder) ->
gleam@result:'try'(
glua_ffi:call_function_dec(Lua, Fun, Args),
fun(_use0) ->
{Lua@1, Ret} = _use0,
gleam@result:'try'(
begin
_pipe = gleam@list:try_map(
Ret,
fun(_capture) ->
gleam@dynamic@decode:run(_capture, Decoder)
end
),
gleam@result:map_error(
_pipe,
fun(Field@0) -> {unexpected_result_type, Field@0} end
)
end,
fun(Decoded) -> {ok, {Lua@1, Decoded}} end
)
end
).
-file("src/glua.gleam", 623).
?DOC(" Same as `glua.call_function`, but returns references to the values instead of decode them.\n").
-spec ref_call_function(lua(), value_ref(), list(value())) -> {ok,
{lua(), list(value_ref())}} |
{error, lua_error()}.
ref_call_function(Lua, Fun, Args) ->
glua_ffi:call_function(Lua, Fun, Args).
-file("src/glua.gleam", 655).
?DOC(
" Gets a reference to the function at `keys`, then inmediatly calls it with the provided `args`.\n"
"\n"
" This is a shorthand for `glua.ref_get` followed by `glua.call_function`.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" let #(lua, encoded) = glua.new() |> glua.string(\"hello from gleam!\")\n"
" let assert Ok(#(_, [s])) = glua.call_function_by_name(\n"
" state: lua,\n"
" keys: [\"string\", \"upper\"],\n"
" args: [encoded],\n"
" using: decode.string\n"
" )\n"
"\n"
" assert s == \"HELLO FROM GLEAM!\" \n"
" ```\n"
).
-spec call_function_by_name(
lua(),
list(binary()),
list(value()),
gleam@dynamic@decode:decoder(DVJ)
) -> {ok, {lua(), list(DVJ)}} | {error, lua_error()}.
call_function_by_name(Lua, Keys, Args, Decoder) ->
gleam@result:'try'(
ref_get(Lua, Keys),
fun(Fun) -> call_function(Lua, Fun, Args, Decoder) end
).
-file("src/glua.gleam", 666).
?DOC(" Same as `glua.call_function_by_name`, but it chains `glua.ref_get` with `glua.ref_call_function` instead of `glua.call_function`\n").
-spec ref_call_function_by_name(lua(), list(binary()), list(value())) -> {ok,
{lua(), list(value_ref())}} |
{error, lua_error()}.
ref_call_function_by_name(Lua, Keys, Args) ->
gleam@result:'try'(
ref_get(Lua, Keys),
fun(Fun) -> ref_call_function(Lua, Fun, Args) end
).